Inline 'access' synthetic methods
This commit is contained in:
@@ -12,6 +12,7 @@ import jadx.dex.visitors.DotGraphVisitor;
|
||||
import jadx.dex.visitors.EnumVisitor;
|
||||
import jadx.dex.visitors.FallbackModeVisitor;
|
||||
import jadx.dex.visitors.IDexTreeVisitor;
|
||||
import jadx.dex.visitors.MethodInlinerVisitor;
|
||||
import jadx.dex.visitors.ModVisitor;
|
||||
import jadx.dex.visitors.regions.CheckRegions;
|
||||
import jadx.dex.visitors.regions.CleanRegions;
|
||||
@@ -113,6 +114,7 @@ public class Main {
|
||||
if (args.isCFGOutput())
|
||||
passes.add(new DotGraphVisitor(args.getOutDir(), true));
|
||||
|
||||
passes.add(new MethodInlinerVisitor());
|
||||
passes.add(new ClassModifier());
|
||||
passes.add(new CleanRegions());
|
||||
}
|
||||
|
||||
@@ -194,6 +194,9 @@ public class ClassGen {
|
||||
CodeWriter code = new CodeWriter(clsCode.getIndent() + 1);
|
||||
for (Iterator<MethodNode> it = mthList.iterator(); it.hasNext();) {
|
||||
MethodNode mth = it.next();
|
||||
if (mth.getAttributes().contains(AttributeFlag.DONT_GENERATE))
|
||||
continue;
|
||||
|
||||
try {
|
||||
if (mth.getAccessFlags().isAbstract() || mth.getAccessFlags().isNative()) {
|
||||
MethodGen mthGen = new MethodGen(this, mth);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package jadx.codegen;
|
||||
|
||||
import jadx.dex.attributes.AttributeType;
|
||||
import jadx.dex.attributes.IAttribute;
|
||||
import jadx.dex.attributes.MethodInlineAttr;
|
||||
import jadx.dex.info.ClassInfo;
|
||||
import jadx.dex.info.FieldInfo;
|
||||
import jadx.dex.info.MethodInfo;
|
||||
@@ -27,7 +29,12 @@ import jadx.dex.nodes.RootNode;
|
||||
import jadx.utils.StringUtils;
|
||||
import jadx.utils.exceptions.CodegenException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -324,6 +331,10 @@ public class InsnGen {
|
||||
case TERNARY:
|
||||
break;
|
||||
|
||||
case ARGS:
|
||||
code.add(arg(insn.getArg(0)));
|
||||
break;
|
||||
|
||||
/* fallback mode instructions */
|
||||
case NOP:
|
||||
state.add(InsnGenState.SKIP);
|
||||
@@ -462,6 +473,14 @@ public class InsnGen {
|
||||
private void makeInvoke(InvokeNode insn, CodeWriter code) throws CodegenException {
|
||||
MethodInfo callMth = insn.getCallMth();
|
||||
|
||||
// inline method if METHOD_INLINE attribute is attached
|
||||
MethodNode callMthNode = mth.dex().resolveMethod(callMth);
|
||||
if (callMthNode != null
|
||||
&& callMthNode.getAttributes().contains(AttributeType.METHOD_INLINE)) {
|
||||
inlineMethod(callMthNode, insn, code);
|
||||
return;
|
||||
}
|
||||
|
||||
int k = 0;
|
||||
InvokeType type = insn.getInvokeType();
|
||||
switch (type) {
|
||||
@@ -488,6 +507,45 @@ public class InsnGen {
|
||||
addArgs(code, insn, k);
|
||||
}
|
||||
|
||||
private void inlineMethod(MethodNode callMthNode, InvokeNode insn, CodeWriter code) throws CodegenException {
|
||||
IAttribute mia = callMthNode.getAttributes().get(AttributeType.METHOD_INLINE);
|
||||
InsnNode inl = ((MethodInlineAttr) mia).getInsn();
|
||||
if (callMthNode.getMethodInfo().getArgumentsTypes().isEmpty()) {
|
||||
makeInsn(inl, code, true);
|
||||
} else {
|
||||
// remap args
|
||||
InsnArg[] regs = new InsnArg[callMthNode.getRegsCount()];
|
||||
List<RegisterArg> callArgs = callMthNode.getArguments(true);
|
||||
for (int i = 0; i < callArgs.size(); i++) {
|
||||
InsnArg arg = insn.getArg(i);
|
||||
RegisterArg callArg = callArgs.get(i);
|
||||
regs[callArg.getRegNum()] = arg;
|
||||
}
|
||||
// replace args
|
||||
List<RegisterArg> inlArgs = new ArrayList<RegisterArg>();
|
||||
inl.getRegisterArgs(inlArgs);
|
||||
Map<RegisterArg, InsnArg> toRevert = new HashMap<RegisterArg, InsnArg>();
|
||||
for (RegisterArg r : inlArgs) {
|
||||
if (r.getRegNum() >= regs.length) {
|
||||
LOG.warn("Unknown register number {} in method call: {}, {}", r, callMthNode, mth);
|
||||
} else {
|
||||
InsnArg repl = regs[r.getRegNum()];
|
||||
if (repl == null) {
|
||||
LOG.warn("Not passed register {} in method call: {}, {}", r, callMthNode, mth);
|
||||
} else {
|
||||
inl.replaceArg(r, repl);
|
||||
toRevert.put(r, repl);
|
||||
}
|
||||
}
|
||||
}
|
||||
makeInsn(inl, code, true);
|
||||
// revert changes
|
||||
for (Entry<RegisterArg, InsnArg> e : toRevert.entrySet()) {
|
||||
inl.replaceArg(e.getValue(), e.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addArgs(CodeWriter code, InsnNode insn, int k) throws CodegenException {
|
||||
code.add('(');
|
||||
for (int i = k; i < insn.getArgsCount(); i++) {
|
||||
@@ -495,7 +553,7 @@ public class InsnGen {
|
||||
if (i < insn.getArgsCount() - 1)
|
||||
code.add(", ");
|
||||
}
|
||||
code.add(")");
|
||||
code.add(')');
|
||||
}
|
||||
|
||||
private void makeArith(ArithNode insn, CodeWriter code, EnumSet<InsnGenState> state) throws CodegenException {
|
||||
|
||||
@@ -21,6 +21,7 @@ public enum AttributeType {
|
||||
|
||||
// methods
|
||||
JADX_ERROR(true),
|
||||
METHOD_INLINE(true),
|
||||
|
||||
// classes
|
||||
ENUM_CLASS(true),
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package jadx.dex.attributes;
|
||||
|
||||
import jadx.dex.nodes.InsnNode;
|
||||
|
||||
public class MethodInlineAttr implements IAttribute {
|
||||
|
||||
private final InsnNode insn;
|
||||
|
||||
public MethodInlineAttr(InsnNode insn) {
|
||||
this.insn = insn;
|
||||
}
|
||||
|
||||
public InsnNode getInsn() {
|
||||
return insn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributeType getType() {
|
||||
return AttributeType.METHOD_INLINE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "INLINE: " + insn;
|
||||
}
|
||||
}
|
||||
@@ -76,6 +76,28 @@ public final class MethodInfo {
|
||||
return name.equals("<clinit>");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + declClass.hashCode();
|
||||
result = prime * result + retType.hashCode();
|
||||
result = prime * result + shortId.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (getClass() != obj.getClass()) return false;
|
||||
MethodInfo other = (MethodInfo) obj;
|
||||
if (!shortId.equals(other.shortId)) return false;
|
||||
if (!retType.equals(other.retType)) return false;
|
||||
if (!declClass.equals(other.declClass)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return retType + " " + declClass.getFullName() + "." + name
|
||||
|
||||
@@ -50,6 +50,9 @@ public enum InsnType {
|
||||
CONSTRUCTOR,
|
||||
BREAK,
|
||||
CONTINUE,
|
||||
|
||||
TERNARY,
|
||||
ARGS, // just generate arguments
|
||||
|
||||
NEW_MULTIDIM_ARRAY // TODO: now multidimensional arrays created using Array.newInstance function
|
||||
}
|
||||
|
||||
@@ -239,7 +239,15 @@ public class ClassNode extends AttrNode implements ILoadable {
|
||||
return null;
|
||||
}
|
||||
|
||||
public MethodNode searchMethodById(String shortId) {
|
||||
public MethodNode searchMethod(MethodInfo mth) {
|
||||
for (MethodNode m : methods) {
|
||||
if (m.getMethodInfo().equals(mth))
|
||||
return m;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public MethodNode searchMethodByName(String shortId) {
|
||||
for (MethodNode m : methods) {
|
||||
if (m.getMethodInfo().getShortId().equals(shortId))
|
||||
return m;
|
||||
@@ -248,7 +256,7 @@ public class ClassNode extends AttrNode implements ILoadable {
|
||||
}
|
||||
|
||||
public MethodNode searchMethodById(int id) {
|
||||
return searchMethodById(MethodInfo.fromDex(dex, id).getShortId());
|
||||
return searchMethodByName(MethodInfo.fromDex(dex, id).getShortId());
|
||||
}
|
||||
|
||||
public List<ClassNode> getInnerClasses() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package jadx.dex.nodes;
|
||||
|
||||
import jadx.dex.info.ClassInfo;
|
||||
import jadx.dex.info.MethodInfo;
|
||||
import jadx.dex.instructions.args.ArgType;
|
||||
import jadx.utils.exceptions.DecodeException;
|
||||
import jadx.utils.files.InputFile;
|
||||
@@ -51,6 +52,14 @@ public class DexNode {
|
||||
return root.resolveClass(clsInfo);
|
||||
}
|
||||
|
||||
public MethodNode resolveMethod(MethodInfo mth) {
|
||||
ClassNode cls = resolveClass(mth.getDeclClass());
|
||||
if (cls != null) {
|
||||
return cls.searchMethod(mth);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// DexBuffer wrappers
|
||||
|
||||
public String getString(int index) {
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package jadx.dex.visitors;
|
||||
|
||||
import jadx.dex.attributes.AttributeFlag;
|
||||
import jadx.dex.attributes.IAttribute;
|
||||
import jadx.dex.attributes.MethodInlineAttr;
|
||||
import jadx.dex.instructions.InsnType;
|
||||
import jadx.dex.nodes.BlockNode;
|
||||
import jadx.dex.nodes.InsnNode;
|
||||
import jadx.dex.nodes.MethodNode;
|
||||
import jadx.utils.exceptions.JadxException;
|
||||
|
||||
public class MethodInlinerVisitor extends AbstractVisitor {
|
||||
|
||||
@Override
|
||||
public void visit(MethodNode mth) throws JadxException {
|
||||
if (mth.getAccessFlags().isSynthetic() && mth.getAccessFlags().isStatic()) {
|
||||
if (mth.getBasicBlocks().size() == 1) {
|
||||
BlockNode block = mth.getBasicBlocks().get(0);
|
||||
// synthetic field getter
|
||||
if (block.getInstructions().size() == 1) {
|
||||
InsnNode insn = block.getInstructions().get(0);
|
||||
if (insn.getType() == InsnType.RETURN) {
|
||||
InsnNode inl = new InsnNode(InsnType.ARGS, 1);
|
||||
inl.addArg(insn.getArg(0));
|
||||
addInlineAttr(mth, inl);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// synthetic field setter
|
||||
if (block.getInstructions().size() == 2) {
|
||||
if (block.getInstructions().get(1).getType() == InsnType.RETURN) {
|
||||
InsnNode insn = block.getInstructions().get(0);
|
||||
addInlineAttr(mth, insn);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// synthetic method invoke
|
||||
if (block.getInstructions().size() == 1) {
|
||||
InsnNode insn = block.getInstructions().get(0);
|
||||
addInlineAttr(mth, insn);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addInlineAttr(MethodNode mth, InsnNode insn) {
|
||||
IAttribute attr = new MethodInlineAttr(insn);
|
||||
mth.getAttributes().add(attr);
|
||||
mth.getAttributes().add(AttributeFlag.DONT_GENERATE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package jadx.samples;
|
||||
|
||||
public class TestInner2 extends AbstractTest {
|
||||
|
||||
private String a;
|
||||
|
||||
public class A {
|
||||
public A() {
|
||||
a = "a";
|
||||
}
|
||||
|
||||
public String a() {
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
private static String b;
|
||||
|
||||
public static class B {
|
||||
public B() {
|
||||
b = "b";
|
||||
}
|
||||
|
||||
public String b() {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean testRun() throws Exception {
|
||||
assertTrue((new A()).a().equals("a"));
|
||||
assertTrue(a.equals("a"));
|
||||
assertTrue((new B()).b().equals("b"));
|
||||
assertTrue(b.equals("b"));
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
new TestInner2().testRun();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user