diff --git a/jadx-core/src/main/java/jadx/api/Decompiler.java b/jadx-core/src/main/java/jadx/api/Decompiler.java index 2ea080424..2615b1c72 100644 --- a/jadx-core/src/main/java/jadx/api/Decompiler.java +++ b/jadx-core/src/main/java/jadx/api/Decompiler.java @@ -181,7 +181,7 @@ public final class Decompiler { } void processClass(ClassNode cls) { - LOG.info("processing class {} ...", cls); + LOG.debug("processing class {} ...", cls); ProcessClass.process(cls, passes); } diff --git a/jadx-core/src/main/java/jadx/core/Jadx.java b/jadx-core/src/main/java/jadx/core/Jadx.java index d1b3ce496..34371f744 100644 --- a/jadx-core/src/main/java/jadx/core/Jadx.java +++ b/jadx-core/src/main/java/jadx/core/Jadx.java @@ -13,13 +13,15 @@ import jadx.core.dex.visitors.IDexTreeVisitor; import jadx.core.dex.visitors.MethodInlineVisitor; import jadx.core.dex.visitors.ModVisitor; import jadx.core.dex.visitors.PrepareForCodeGen; +import jadx.core.dex.visitors.ssa.SSATransform; import jadx.core.dex.visitors.SimplifyVisitor; import jadx.core.dex.visitors.regions.CheckRegions; import jadx.core.dex.visitors.regions.IfRegionVisitor; import jadx.core.dex.visitors.regions.ProcessVariables; import jadx.core.dex.visitors.regions.RegionMakerVisitor; -import jadx.core.dex.visitors.typeresolver.FinishTypeResolver; -import jadx.core.dex.visitors.typeresolver.TypeResolver; +import jadx.core.dex.visitors.ssa.EliminatePhiNodes; +import jadx.core.dex.visitors.typeinference.FinishTypeInference; +import jadx.core.dex.visitors.typeinference.TypeInference; import jadx.core.utils.Utils; import java.io.File; @@ -51,15 +53,17 @@ public class Jadx { passes.add(new FallbackModeVisitor()); } else { passes.add(new BlockMakerVisitor()); + passes.add(new SSATransform()); + passes.add(new TypeInference()); - passes.add(new TypeResolver()); + passes.add(new ConstInlinerVisitor()); + passes.add(new FinishTypeInference()); if (args.isRawCFGOutput()) { passes.add(new DotGraphVisitor(outDir, false, true)); } - passes.add(new ConstInlinerVisitor()); - passes.add(new FinishTypeResolver()); + passes.add(new EliminatePhiNodes()); passes.add(new ModVisitor()); passes.add(new EnumVisitor()); diff --git a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java index bd8f98edd..995d517a1 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -102,9 +102,7 @@ public class InsnGen { if (f.isStatic()) { code.add(staticField(f.getField())); } else { - RegisterArg regArg = new RegisterArg(f.getRegNum()); - regArg.replaceTypedVar(f); - instanceField(code, f.getField(), regArg); + instanceField(code, f.getField(), f.getRegisterArg()); } } else { throw new CodegenException("Unknown arg type " + arg); @@ -244,7 +242,7 @@ public class InsnGen { break; case ARITH_ONEARG: - makeArithOneArg((ArithNode) insn, code, state); + makeArithOneArg((ArithNode) insn, code); break; case NEG: { @@ -309,7 +307,7 @@ public class InsnGen { break; } case CONSTRUCTOR: - makeConstructor((ConstructorInsn) insn, code, state); + makeConstructor((ConstructorInsn) insn, code); break; case INVOKE: @@ -427,6 +425,9 @@ public class InsnGen { addArg(code, insn.getArg(0)); break; + case PHI: + break; + /* fallback mode instructions */ case IF: assert isFallback() : "if insn in not fallback mode"; @@ -542,7 +543,7 @@ public class InsnGen { code.add("new ").add(useType(elType)).add("[]{").add(str.toString()).add('}'); } - private void makeConstructor(ConstructorInsn insn, CodeWriter code, EnumSet state) + private void makeConstructor(ConstructorInsn insn, CodeWriter code) throws CodegenException { ClassNode cls = mth.dex().resolveClass(insn.getClassType()); if (cls != null && cls.isAnonymous()) { @@ -739,7 +740,7 @@ public class InsnGen { } } - private void makeArithOneArg(ArithNode insn, CodeWriter code, EnumSet state) throws CodegenException { + private void makeArithOneArg(ArithNode insn, CodeWriter code) throws CodegenException { ArithOp op = insn.getOp(); InsnArg arg = insn.getArg(0); // "++" or "--" diff --git a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java index 65bfc2942..5b4bef82e 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java @@ -4,6 +4,7 @@ import jadx.core.Consts; import jadx.core.dex.attributes.AttributeFlag; import jadx.core.dex.attributes.AttributeType; import jadx.core.dex.attributes.AttributesList; +import jadx.core.dex.attributes.IAttribute; import jadx.core.dex.attributes.JadxErrorAttr; import jadx.core.dex.attributes.annotations.MethodParameters; import jadx.core.dex.info.AccessInfo; @@ -13,7 +14,6 @@ import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.regions.Region; -import jadx.core.dex.trycatch.CatchAttr; import jadx.core.dex.visitors.DepthTraversal; import jadx.core.dex.visitors.FallbackModeVisitor; import jadx.core.utils.ErrorsCounter; @@ -164,7 +164,7 @@ public class MethodGen { * variable type or name (if debug info available) */ public String makeArgName(RegisterArg arg) { - String name = arg.getTypedVar().getName(); + String name = arg.getName(); String base = "r" + arg.getRegNum(); if (fallback) { if (name != null) { @@ -200,7 +200,7 @@ public class MethodGen { return name; } name = getUniqVarName(name); - arg.getTypedVar().setName(name); + arg.getSVar().setName(name); return name; } @@ -306,9 +306,9 @@ public class MethodGen { } try { if (insnGen.makeInsn(insn, code)) { - CatchAttr catchAttr = (CatchAttr) attrs.get(AttributeType.CATCH_BLOCK); - if (catchAttr != null) { - code.add("\t //" + catchAttr); + List catchAttrs = attrs.getAll(AttributeType.CATCH_BLOCK); + for (IAttribute catchAttr : catchAttrs) { + code.add("\t " + catchAttr); } } } catch (CodegenException e) { diff --git a/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java b/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java index 73dfc0be7..aaa2167a3 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java @@ -43,17 +43,19 @@ public class RegionGen extends InsnGen { if (cont instanceof IBlock) { makeSimpleBlock((IBlock) cont, code); } else if (cont instanceof IRegion) { - declareVars(code, cont); if (cont instanceof Region) { makeSimpleRegion(code, (Region) cont); - } else if (cont instanceof IfRegion) { - makeIf((IfRegion) cont, code, true); - } else if (cont instanceof SwitchRegion) { - makeSwitch((SwitchRegion) cont, code); - } else if (cont instanceof LoopRegion) { - makeLoop((LoopRegion) cont, code); - } else if (cont instanceof SynchronizedRegion) { - makeSynchronizedRegion((SynchronizedRegion) cont, code); + } else { + declareVars(code, cont); + if (cont instanceof IfRegion) { + makeIf((IfRegion) cont, code, true); + } else if (cont instanceof SwitchRegion) { + makeSwitch((SwitchRegion) cont, code); + } else if (cont instanceof LoopRegion) { + makeLoop((LoopRegion) cont, code); + } else if (cont instanceof SynchronizedRegion) { + makeSynchronizedRegion((SynchronizedRegion) cont, code); + } } } else { throw new CodegenException("Not processed container: " + cont); @@ -77,6 +79,7 @@ public class RegionGen extends InsnGen { if (tc != null) { makeTryCatch(region, tc.getTryBlock(), code); } else { + declareVars(code, region); for (IContainer c : region.getSubBlocks()) { makeRegion(code, c); } diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/AttributeType.java b/jadx-core/src/main/java/jadx/core/dex/attributes/AttributeType.java index c33e4beca..15c4640a0 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/AttributeType.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/AttributeType.java @@ -26,6 +26,8 @@ public enum AttributeType { ANNOTATION_LIST(true), ANNOTATION_MTH_PARAMETERS(true), + PHI_LIST(true), + SOURCE_FILE(true), // for regions diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/AttributesList.java b/jadx-core/src/main/java/jadx/core/dex/attributes/AttributesList.java index ccad9f52a..17c2441de 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/AttributesList.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/AttributesList.java @@ -118,15 +118,14 @@ public final class AttributesList { int count = getMultiCountInternal(type); if (count == 0) { return Collections.emptyList(); - } else { - List attrs = new ArrayList(count); - for (IAttribute attr : attributes) { - if (attr.getType() == type) { - attrs.add(attr); - } - } - return attrs; } + List attrs = new ArrayList(count); + for (IAttribute attr : attributes) { + if (attr.getType() == type) { + attrs.add(attr); + } + } + return attrs; } public void remove(AttributeType type) { diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/BlockRegState.java b/jadx-core/src/main/java/jadx/core/dex/attributes/BlockRegState.java deleted file mode 100644 index a7b58ab0d..000000000 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/BlockRegState.java +++ /dev/null @@ -1,56 +0,0 @@ -package jadx.core.dex.attributes; - -import jadx.core.dex.instructions.args.RegisterArg; -import jadx.core.dex.instructions.args.TypedVar; -import jadx.core.dex.nodes.MethodNode; - -public final class BlockRegState { - - private final RegisterArg[] regs; - - public BlockRegState(MethodNode mth) { - this.regs = new RegisterArg[mth.getRegsCount()]; - for (int i = 0; i < regs.length; i++) { - regs[i] = new RegisterArg(i); - } - } - - public BlockRegState(BlockRegState state) { - this.regs = new RegisterArg[state.regs.length]; - System.arraycopy(state.regs, 0, regs, 0, state.regs.length); - } - - public void assignReg(RegisterArg arg) { - regs[arg.getRegNum()] = arg; - arg.getTypedVar().getUseList().add(arg); - } - - public void use(RegisterArg arg) { - RegisterArg reg = regs[arg.getRegNum()]; - TypedVar regType = reg.getTypedVar(); - if (regType == null) { - regType = new TypedVar(arg.getType()); - reg.forceSetTypedVar(regType); - } - arg.replaceTypedVar(reg); - reg.getTypedVar().getUseList().add(arg); - } - - public RegisterArg getRegister(int r) { - return regs[r]; - } - - @Override - public String toString() { - StringBuilder str = new StringBuilder(); - for (RegisterArg reg : regs) { - if (reg.getTypedVar() != null) { - if (str.length() != 0) { - str.append(", "); - } - str.append(reg); - } - } - return str.toString(); - } -} diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/PhiListAttr.java b/jadx-core/src/main/java/jadx/core/dex/attributes/PhiListAttr.java new file mode 100644 index 000000000..b00d8a2ac --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/PhiListAttr.java @@ -0,0 +1,30 @@ +package jadx.core.dex.attributes; + +import jadx.core.dex.instructions.PhiInsn; + +import java.util.LinkedList; +import java.util.List; + +public class PhiListAttr implements IAttribute { + + private final List list = new LinkedList(); + + @Override + public AttributeType getType() { + return AttributeType.PHI_LIST; + } + + public List getList() { + return list; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("PHI: "); + for (PhiInsn phiInsn : list) { + sb.append('r').append(phiInsn.getResult().getRegNum()).append(" "); + } + return sb.toString(); + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java b/jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java index 51d50e1b7..01dcea955 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java @@ -29,7 +29,7 @@ public class InsnDecoder { private final DexNode dex; private DecodedInstruction[] insnArr; - public InsnDecoder(MethodNode mthNode) throws DecodeException { + public InsnDecoder(MethodNode mthNode) { this.method = mthNode; this.dex = method.dex(); } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/InsnType.java b/jadx-core/src/main/java/jadx/core/dex/instructions/InsnType.java index 79e11913b..43f13db29 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/InsnType.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/InsnType.java @@ -58,6 +58,7 @@ public enum InsnType { TERNARY, ARGS, // just generate arguments + PHI, NEW_MULTIDIM_ARRAY // TODO: now multidimensional arrays created using Array.newInstance function } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/PhiInsn.java b/jadx-core/src/main/java/jadx/core/dex/instructions/PhiInsn.java new file mode 100644 index 000000000..832a9f4e3 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/PhiInsn.java @@ -0,0 +1,28 @@ +package jadx.core.dex.instructions; + +import jadx.core.dex.instructions.args.ArgType; +import jadx.core.dex.instructions.args.InsnArg; +import jadx.core.dex.instructions.args.RegisterArg; +import jadx.core.dex.nodes.InsnNode; +import jadx.core.utils.Utils; + +public class PhiInsn extends InsnNode { + + public PhiInsn(int regNum, int predecessors) { + super(InsnType.PHI, predecessors); + setResult(InsnArg.reg(regNum, ArgType.UNKNOWN)); + for (int i = 0; i < predecessors; i++) { + addReg(regNum, ArgType.UNKNOWN); + } + } + + @Override + public RegisterArg getArg(int n) { + return (RegisterArg) super.getArg(n); + } + + @Override + public String toString() { + return "PHI: " + getResult() + " = " + Utils.listToString(getArguments()); + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java index 27c42f765..a22c0f8a5 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java @@ -437,7 +437,7 @@ public abstract class ArgType { } public static ArgType merge(ArgType a, ArgType b) { - if (b == null || a == null) { + if (a == null || b == null) { return null; } if (a.equals(b)) { diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/FieldArg.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/FieldArg.java index 4d3c2422a..e1f7bfa8e 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/FieldArg.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/FieldArg.java @@ -2,12 +2,16 @@ package jadx.core.dex.instructions.args; import jadx.core.dex.info.FieldInfo; +// TODO: don't extend RegisterArg (now used as a result of instruction) public final class FieldArg extends RegisterArg { private final FieldInfo field; + // regArg equal 'null' for static fields + private final RegisterArg regArg; - public FieldArg(FieldInfo field, int regNum) { - super(regNum, field.getType()); + public FieldArg(FieldInfo field, RegisterArg reg) { + super(-1); + this.regArg = reg; this.field = field; } @@ -15,8 +19,12 @@ public final class FieldArg extends RegisterArg { return field; } + public RegisterArg getRegisterArg() { + return regArg; + } + public boolean isStatic() { - return regNum == -1; + return regArg == null; } @Override @@ -29,6 +37,11 @@ public final class FieldArg extends RegisterArg { return false; } + @Override + public void setType(ArgType type) { + this.type = type; + } + @Override public String toString() { return "(" + field + ")"; diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/ImmutableTypedVar.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/ImmutableTypedVar.java deleted file mode 100644 index 52536f619..000000000 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/ImmutableTypedVar.java +++ /dev/null @@ -1,27 +0,0 @@ -package jadx.core.dex.instructions.args; - -public class ImmutableTypedVar extends TypedVar { - - public ImmutableTypedVar(ArgType type) { - super(type); - } - - @Override - public boolean isImmutable() { - return true; - } - - @Override - public void forceSetType(ArgType newType) { - } - - @Override - public boolean merge(TypedVar typedVar) { - return false; - } - - @Override - public boolean merge(ArgType type) { - return false; - } -} diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnArg.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnArg.java index a7b3c9bc3..cbd1505be 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnArg.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnArg.java @@ -26,10 +26,8 @@ public abstract class InsnArg extends Typed { return reg(InsnUtils.getArg(insn, argNum), type); } - public static RegisterArg immutableReg(int regNum, ArgType type) { - RegisterArg r = new RegisterArg(regNum); - r.forceSetTypedVar(new ImmutableTypedVar(type)); - return r; + public static RegisterArg parameterReg(int regNum, ArgType type) { + return new MthParameterArg(regNum, type); } public static LiteralArg lit(long literal, ArgType type) { @@ -101,11 +99,11 @@ public abstract class InsnArg extends Typed { break; case CONST_STR: arg = wrap(insn); - arg.getTypedVar().forceSetType(ArgType.STRING); + arg.setType(ArgType.STRING); break; case CONST_CLASS: arg = wrap(insn); - arg.getTypedVar().forceSetType(ArgType.CLASS); + arg.setType(ArgType.CLASS); break; default: arg = wrap(insn); diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnWrapArg.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnWrapArg.java index 6b0b9b498..993c52010 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnWrapArg.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnWrapArg.java @@ -8,7 +8,7 @@ public final class InsnWrapArg extends InsnArg { public InsnWrapArg(InsnNode insn) { RegisterArg result = insn.getResult(); - this.typedVar = new TypedVar((result != null ? result.getType() : ArgType.VOID)); + this.type = result != null ? result.getType() : ArgType.VOID; this.wrappedInsn = insn; } @@ -29,6 +29,6 @@ public final class InsnWrapArg extends InsnArg { @Override public String toString() { - return "(wrap: " + typedVar + "\n " + wrappedInsn + ")"; + return "(wrap: " + type + "\n " + wrappedInsn + ")"; } } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/LiteralArg.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/LiteralArg.java index 6d4bc84c5..791107a34 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/LiteralArg.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/LiteralArg.java @@ -24,7 +24,7 @@ public final class LiteralArg extends InsnArg { } } this.literal = value; - this.typedVar = new TypedVar(type); + this.type = type; } public long getLiteral() { @@ -37,7 +37,7 @@ public final class LiteralArg extends InsnArg { } public boolean isInteger() { - PrimitiveType type = typedVar.getType().getPrimitiveType(); + PrimitiveType type = this.type.getPrimitiveType(); return (type == PrimitiveType.INT || type == PrimitiveType.BYTE || type == PrimitiveType.CHAR @@ -69,10 +69,10 @@ public final class LiteralArg extends InsnArg { if (getType().equals(ArgType.BOOLEAN) && (value.equals("true") || value.equals("false"))) { return value; } - return "(" + value + " " + typedVar + ")"; + return "(" + value + " " + type + ")"; } catch (JadxRuntimeException ex) { // can't convert literal to string - return "(" + literal + " " + typedVar + ")"; + return "(" + literal + " " + type + ")"; } } } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/MthParameterArg.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/MthParameterArg.java new file mode 100644 index 000000000..210ab8597 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/MthParameterArg.java @@ -0,0 +1,17 @@ +package jadx.core.dex.instructions.args; + +public class MthParameterArg extends RegisterArg { + + public MthParameterArg(int rn, ArgType type) { + super(rn, type); + } + + @Override + public boolean isTypeImmutable() { + return true; + } + + @Override + public void setType(ArgType type) { + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/Named.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/Named.java new file mode 100644 index 000000000..8e7e4c7f1 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/Named.java @@ -0,0 +1,8 @@ +package jadx.core.dex.instructions.args; + +public interface Named { + + String getName(); + + void setName(String name); +} diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/NamedArg.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/NamedArg.java index ecb87d0c2..dbca35ead 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/NamedArg.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/NamedArg.java @@ -1,12 +1,12 @@ package jadx.core.dex.instructions.args; -public final class NamedArg extends InsnArg { +public final class NamedArg extends InsnArg implements Named { private String name; public NamedArg(String name, ArgType type) { this.name = name; - this.typedVar = new TypedVar(type); + this.type = type; } public String getName() { @@ -24,6 +24,6 @@ public final class NamedArg extends InsnArg { @Override public String toString() { - return "(" + name + " " + typedVar + ")"; + return "(" + name + " " + type + ")"; } } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java index 0dda73859..218bd9174 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java @@ -6,25 +6,32 @@ import jadx.core.dex.instructions.ConstClassNode; import jadx.core.dex.instructions.ConstStringNode; import jadx.core.dex.instructions.IndexInsnNode; import jadx.core.dex.instructions.InsnType; +import jadx.core.dex.instructions.PhiInsn; import jadx.core.dex.nodes.DexNode; import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.parser.FieldValueAttr; +import jadx.core.utils.exceptions.JadxRuntimeException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class RegisterArg extends InsnArg { + + + +public class RegisterArg extends InsnArg implements Named { private static final Logger LOG = LoggerFactory.getLogger(RegisterArg.class); protected final int regNum; + protected SSAVar sVar; + protected String name; public RegisterArg(int rn) { this.regNum = rn; } public RegisterArg(int rn, ArgType type) { - this.typedVar = new TypedVar(type); + this.type = type; this.regNum = rn; } @@ -37,18 +44,33 @@ public class RegisterArg extends InsnArg { return true; } - public InsnNode getAssignInsn() { - for (InsnArg arg : getTypedVar().getUseList()) { - InsnNode assignInsn = arg.getParentInsn(); - if (assignInsn == null) { - // assign as function argument - return null; - } else if (assignInsn.getResult() != null - && assignInsn.getResult().getRegNum() == regNum) { - return assignInsn; - } + public SSAVar getSVar() { + return sVar; + } + + void setSVar(SSAVar sVar) { + this.sVar = sVar; + } + + @Override + public void setType(ArgType type) { + if (sVar != null) { + sVar.setType(type); + } else { + throw new JadxRuntimeException("SSA variable equals null"); } - return null; + } + + public void forceType(ArgType type) { + this.type = type; + } + + public String getName() { + return name; + } + + public void setName(String newName) { + this.name = newName; } /** @@ -58,54 +80,81 @@ public class RegisterArg extends InsnArg { */ public Object getConstValue(DexNode dex) { InsnNode parInsn = getAssignInsn(); - if (parInsn != null) { - InsnType insnType = parInsn.getType(); - switch (insnType) { - case CONST: - return parInsn.getArg(0); - case CONST_STR: - return ((ConstStringNode) parInsn).getString(); - case CONST_CLASS: - return ((ConstClassNode) parInsn).getClsType(); - case SGET: - FieldInfo f = (FieldInfo) ((IndexInsnNode) parInsn).getIndex(); - FieldNode fieldNode = dex.resolveField(f); - if (fieldNode != null) { - FieldValueAttr attr = (FieldValueAttr) fieldNode.getAttributes().get(AttributeType.FIELD_VALUE); - if (attr != null) { - return attr.getValue(); - } - } else { - LOG.warn("Field {} not found in dex {}", f, dex); + if (parInsn == null) { + return null; + } + InsnType insnType = parInsn.getType(); + switch (insnType) { + case CONST: + return parInsn.getArg(0); + case CONST_STR: + return ((ConstStringNode) parInsn).getString(); + case CONST_CLASS: + return ((ConstClassNode) parInsn).getClsType(); + case SGET: + FieldInfo f = (FieldInfo) ((IndexInsnNode) parInsn).getIndex(); + FieldNode fieldNode = dex.resolveField(f); + if (fieldNode != null) { + FieldValueAttr attr = (FieldValueAttr) fieldNode.getAttributes().get(AttributeType.FIELD_VALUE); + if (attr != null) { + return attr.getValue(); } - break; - } + } else { + LOG.warn("Field {} not found in dex {}", f, dex); + } + break; } return null; } @Override public boolean isThis() { - if (isRegister()) { - String name = getTypedVar().getName(); - if ("this".equals(name)) { + if ("this".equals(name)) { + return true; + } + // maybe it was moved from 'this' register + InsnNode ai = getAssignInsn(); + if (ai != null && ai.getType() == InsnType.MOVE) { + InsnArg arg = ai.getArg(0); + if (arg != this + && arg.isRegister() + && "this".equals(((RegisterArg) arg).getName())) { return true; } - // maybe it was moved from 'this' register - InsnNode ai = getAssignInsn(); - if (ai != null && ai.getType() == InsnType.MOVE) { - InsnArg arg = ai.getArg(0); - if (arg != this && "this".equals(arg.getTypedVar().getName())) { - return true; - } - } } return false; } + public InsnNode getAssignInsn() { + RegisterArg assign = sVar.getAssign(); + if (assign != null) { + return assign.getParentInsn(); + } + return null; + } + + public InsnNode getPhiAssignInsn() { + PhiInsn usePhi = sVar.getUsedInPhi(); + if (usePhi != null) { + return usePhi; + } + RegisterArg assign = sVar.getAssign(); + if (assign != null) { + InsnNode parent = assign.getParentInsn(); + if (parent != null && parent.getType() == InsnType.PHI) { + return parent; + } + } + return null; + } + + public boolean equalRegisterAndType(RegisterArg arg) { + return regNum == arg.regNum && type.equals(arg.type); + } + @Override public int hashCode() { - return regNum * 31 + typedVar.hashCode(); + return (regNum * 31 + type.hashCode()) * 31 + (sVar != null ? sVar.hashCode() : 0); } @Override @@ -116,15 +165,36 @@ public class RegisterArg extends InsnArg { if (obj == null) { return false; } - if (getClass() != obj.getClass()) { + if (!(obj instanceof RegisterArg)) { return false; } RegisterArg other = (RegisterArg) obj; - return regNum == other.regNum && typedVar.equals(other.typedVar); + if (regNum != other.regNum) { + return false; + } + if (!type.equals(other.type)) { + return false; + } + if (sVar != null && !sVar.equals(other.getSVar())) { + return false; + } + return true; } @Override public String toString() { - return "(r" + regNum + " " + typedVar + ")"; + StringBuilder sb=new StringBuilder(); + sb.append("(r"); + sb.append(regNum); + if(sVar!=null){ + sb.append("_").append(sVar.getVersion()); + } + if(name!=null){ + sb.append(" '").append(name).append("'"); + } + sb.append(" "); + sb.append(type); + sb.append(")"); + return sb.toString(); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/SSAVar.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/SSAVar.java new file mode 100644 index 000000000..9381400a5 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/SSAVar.java @@ -0,0 +1,137 @@ +package jadx.core.dex.instructions.args; + +import jadx.core.dex.instructions.PhiInsn; + +import java.util.ArrayList; +import java.util.List; + +public class SSAVar { + + private final int regNum; + private final int version; + + private RegisterArg assign; + private final List useList = new ArrayList(2); + private PhiInsn usedInPhi; + + private ArgType type; + + public SSAVar(int regNum, int v, RegisterArg assign) { + this.regNum = regNum; + this.version = v; + this.assign = assign; + + if (assign != null) { + mergeName(assign); + assign.setSVar(this); + } + } + + public int getRegNum() { + return regNum; + } + + public int getVersion() { + return version; + } + + public RegisterArg getAssign() { + return assign; + } + + public void setAssign(RegisterArg assign) { + this.assign = assign; + } + + public List getUseList() { + return useList; + } + + public void use(RegisterArg arg) { + mergeName(arg); + if (arg.getSVar() != null) { + arg.getSVar().removeUse(arg); + } + arg.setSVar(this); + useList.add(arg); + } + + public void removeUse(RegisterArg arg) { + for (int i = 0, useListSize = useList.size(); i < useListSize; i++) { + if (useList.get(i) == arg) { + useList.remove(i); + break; + } + } + } + + public void setUsedInPhi(PhiInsn usedInPhi) { + this.usedInPhi = usedInPhi; + } + + public PhiInsn getUsedInPhi() { + return usedInPhi; + } + + public boolean isUsedInPhi() { + return usedInPhi != null; + } + + public int getVariableUseCount() { + if (!isUsedInPhi()) { + return useList.size(); + } + return useList.size() + usedInPhi.getResult().getSVar().getUseList().size(); + } + + public ArgType getType() { + return type; + } + + public void setType(ArgType type) { + this.type = type; + if (assign != null) { + assign.type = type; + } + for (int i = 0, useListSize = useList.size(); i < useListSize; i++) { + useList.get(i).type = type; + } + } + + public void setName(String name) { + if (assign != null) { + assign.setName(name); + } + for (int i = 0, useListSize = useList.size(); i < useListSize; i++) { + useList.get(i).setName(name); + } + } + + public void mergeName(RegisterArg arg) { + if (arg.getName() != null) { + setName(arg.getName()); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof SSAVar)) { + return false; + } + SSAVar ssaVar = (SSAVar) o; + return regNum == ssaVar.regNum && version == ssaVar.version; + } + + @Override + public int hashCode() { + return 31 * regNum + version; + } + + @Override + public String toString() { + return "r" + regNum + "_" + version; + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/Typed.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/Typed.java index 9d71449f1..67f9dfe4e 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/Typed.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/Typed.java @@ -1,71 +1,39 @@ package jadx.core.dex.instructions.args; -import java.util.List; - public abstract class Typed { - TypedVar typedVar; - - public TypedVar getTypedVar() { - return typedVar; - } + protected ArgType type; public ArgType getType() { - return typedVar.getType(); + return type; } - public boolean merge(Typed var) { - return typedVar.merge(var.getTypedVar()); + public void setType(ArgType type) { + this.type = type; } - public boolean merge(ArgType var) { - return typedVar.merge(var); + public boolean isTypeImmutable() { + return false; } - public void forceSetTypedVar(TypedVar arg) { - this.typedVar = arg; - } - - public void mergeDebugInfo(Typed arg) { - merge(arg); - mergeName(arg); - } - - protected void mergeName(Typed arg) { - getTypedVar().mergeName(arg.getTypedVar()); - } - - public boolean replaceTypedVar(Typed var) { - TypedVar curVar = this.typedVar; - TypedVar newVar = var.typedVar; - if (curVar == newVar) { - return false; + public boolean merge(ArgType newType) { + ArgType m = ArgType.merge(type, newType); + if (m != null && !m.equals(type)) { + setType(m); + return true; } - if (curVar != null) { - if (curVar.isImmutable()) { - moveInternals(newVar, curVar); - } else { - newVar.merge(curVar); - moveInternals(curVar, newVar); - this.typedVar = newVar; - } - } else { - this.typedVar = newVar; - } - return true; + return false; } - private void moveInternals(TypedVar from, TypedVar to) { - List curUseList = from.getUseList(); - if (curUseList.size() != 0) { - for (InsnArg arg : curUseList) { - if (arg != this) { - arg.forceSetTypedVar(to); - } - } - to.getUseList().addAll(curUseList); - curUseList.clear(); + public boolean merge(InsnArg arg) { + return merge(arg.getType()); + } + + public void mergeDebugInfo(RegisterArg arg) { + this.type = arg.getType(); + if (this instanceof Named) { + Named n = (Named) this; + n.setName(arg.getName()); } - to.mergeName(from); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/TypedVar.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/TypedVar.java deleted file mode 100644 index 019a44616..000000000 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/TypedVar.java +++ /dev/null @@ -1,116 +0,0 @@ -package jadx.core.dex.instructions.args; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -public class TypedVar { - - private ArgType type; - private final List useList = new ArrayList(2); - private String name; - - public TypedVar(ArgType initType) { - this.type = initType; - } - - public ArgType getType() { - return type; - } - - /** - * This method must be used very carefully - */ - public void forceSetType(ArgType newType) { - type = newType; - } - - public boolean merge(TypedVar typedVar) { - return merge(typedVar.getType()); - } - - public boolean merge(ArgType mtype) { - ArgType res = ArgType.merge(type, mtype); - if (res != null && !type.equals(res)) { - this.type = res; - return true; - } else { - return false; - } - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public List getUseList() { - return useList; - } - - public void removeUse(InsnArg arg) { - Iterator it = useList.iterator(); - while (it.hasNext()) { - InsnArg use = it.next(); - if (use == arg) { - it.remove(); - } - } - } - - public void mergeName(TypedVar arg) { - String argName = arg.getName(); - if (argName != null) { - setName(argName); - } else if (getName() != null) { - arg.setName(getName()); - } - } - - public boolean isImmutable() { - return false; - } - - @Override - public int hashCode() { - return type.hashCode() * 31 + (name == null ? 0 : name.hashCode()); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof TypedVar)) { - return false; - } - TypedVar other = (TypedVar) obj; - if (!type.equals(other.type)) { - return false; - } - if (name == null) { - if (other.name != null) { - return false; - } - } else if (!name.equals(other.name)) { - return false; - } - return true; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - if (name != null) { - sb.append('\'').append(name).append("' "); - } - sb.append(type); - return sb.toString(); - } -} diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java b/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java index 5b2d9c613..ea4a5f16b 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java @@ -26,7 +26,6 @@ public class ConstructorInsn extends InsnNode { this.callMth = invoke.getCallMth(); ClassInfo classType = callMth.getDeclClass(); instanceArg = (RegisterArg) invoke.getArg(0); - instanceArg.setParentInsn(this); if (instanceArg.isThis()) { if (classType.equals(mth.getParentClass().getClassInfo())) { @@ -42,7 +41,10 @@ public class ConstructorInsn extends InsnNode { } else { callType = CallType.CONSTRUCTOR; setResult(instanceArg); + // convert from 'use' to 'assign' + instanceArg.getSVar().setAssign(instanceArg); } + instanceArg.getSVar().removeUse(instanceArg); for (int i = 1; i < invoke.getArgsCount(); i++) { addArg(invoke.getArg(i)); } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/BlockNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/BlockNode.java index 214868fbb..6a986f2ac 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/BlockNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/BlockNode.java @@ -3,13 +3,13 @@ package jadx.core.dex.nodes; import jadx.core.dex.attributes.AttrNode; import jadx.core.dex.attributes.AttributeFlag; import jadx.core.dex.attributes.AttributeType; -import jadx.core.dex.attributes.BlockRegState; import jadx.core.dex.attributes.LoopAttr; import jadx.core.utils.InsnUtils; import java.util.ArrayList; import java.util.BitSet; import java.util.Collections; +import java.util.LinkedList; import java.util.List; public class BlockNode extends AttrNode implements IBlock { @@ -29,10 +29,7 @@ public class BlockNode extends AttrNode implements IBlock { // immediate dominator private BlockNode idom; // blocks on which dominates this block - private final List dominatesOn = new ArrayList(1); - - private BlockRegState startState; - private BlockRegState endState; + private List dominatesOn = Collections.emptyList(); public BlockNode(int id, int offset) { this.id = id; @@ -146,20 +143,11 @@ public class BlockNode extends AttrNode implements IBlock { return dominatesOn; } - public BlockRegState getStartState() { - return startState; - } - - public void setStartState(BlockRegState startState) { - this.startState = startState; - } - - public BlockRegState getEndState() { - return endState; - } - - public void setEndState(BlockRegState endState) { - this.endState = endState; + public void addDominatesOn(BlockNode block) { + if (dominatesOn.isEmpty()) { + dominatesOn = new LinkedList(); + } + dominatesOn.add(block); } public boolean isSynthetic() { diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java index 5e20b84a5..b09aafc60 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java @@ -86,7 +86,10 @@ public class InsnNode extends LineAttrNode { for (int i = 0; i < count; i++) { InsnArg arg = arguments.get(i); if (arg == from) { - // TODO correct remove from use list + if (arg.isRegister()) { + RegisterArg registerArg = (RegisterArg) arg; + registerArg.getSVar().removeUse(registerArg); + } setArg(i, to); return true; } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java index fee9cb01f..6956ea2e9 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java @@ -15,6 +15,7 @@ import jadx.core.dex.instructions.SwitchNode; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.RegisterArg; +import jadx.core.dex.instructions.args.SSAVar; import jadx.core.dex.nodes.parser.DebugInfoParser; import jadx.core.dex.nodes.parser.SignatureParser; import jadx.core.dex.regions.Region; @@ -55,6 +56,7 @@ public class MethodNode extends LineAttrNode implements ILoadable { private ArgType retType; private RegisterArg thisArg; private List argsList; + private List sVars = Collections.emptyList(); private Map> genericMap; private List blocks; @@ -196,8 +198,8 @@ public class MethodNode extends LineAttrNode implements ILoadable { if (accFlags.isStatic()) { thisArg = null; } else { - thisArg = InsnArg.immutableReg(pos - 1, parentClass.getClassInfo().getType()); - thisArg.getTypedVar().setName("this"); + thisArg = InsnArg.parameterReg(pos - 1, parentClass.getClassInfo().getType()); + thisArg.setName("this"); } if (args.isEmpty()) { argsList = Collections.emptyList(); @@ -205,7 +207,7 @@ public class MethodNode extends LineAttrNode implements ILoadable { } argsList = new ArrayList(args.size()); for (ArgType arg : args) { - argsList.add(InsnArg.immutableReg(pos, arg)); + argsList.add(InsnArg.parameterReg(pos, arg)); pos += arg.getRegCount(); } } @@ -472,6 +474,24 @@ public class MethodNode extends LineAttrNode implements ILoadable { return regsCount; } + public SSAVar makeNewSVar(int regNum, int[] versions, RegisterArg arg) { + SSAVar var = new SSAVar(regNum, versions[regNum], arg); + versions[regNum]++; + if (sVars.isEmpty()) { + sVars = new ArrayList(); + } + sVars.add(var); + return var; + } + + public void removeSVar(SSAVar var) { + sVars.remove(var); + } + + public List getSVars() { + return sVars; + } + public AccessInfo getAccessFlags() { return accFlags; } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/parser/DebugInfoParser.java b/jadx-core/src/main/java/jadx/core/dex/nodes/parser/DebugInfoParser.java index 476f69df4..330686004 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/parser/DebugInfoParser.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/parser/DebugInfoParser.java @@ -63,7 +63,7 @@ public class DebugInfoParser { int id = section.readUleb128() - 1; if (id != DexNode.NO_INDEX) { String name = dex.getString(id); - mthArgs.get(i).getTypedVar().setName(name); + mthArgs.get(i).setName(name); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/parser/LocalVar.java b/jadx-core/src/main/java/jadx/core/dex/nodes/parser/LocalVar.java index 3f6009ea1..d8d4c611c 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/parser/LocalVar.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/parser/LocalVar.java @@ -2,7 +2,6 @@ package jadx.core.dex.nodes.parser; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.RegisterArg; -import jadx.core.dex.instructions.args.TypedVar; import jadx.core.dex.nodes.DexNode; import jadx.core.utils.InsnUtils; @@ -19,7 +18,7 @@ final class LocalVar extends RegisterArg { private int endAddr; public LocalVar(DexNode dex, int rn, int nameId, int typeId, int signId) { - super(rn); + super(rn, ArgType.UNKNOWN); String name = (nameId == DexNode.NO_INDEX ? null : dex.getString(nameId)); ArgType type = (typeId == DexNode.NO_INDEX ? null : dex.getType(typeId)); String sign = (signId == DexNode.NO_INDEX ? null : dex.getString(signId)); @@ -28,8 +27,8 @@ final class LocalVar extends RegisterArg { } public LocalVar(RegisterArg arg) { - super(arg.getRegNum()); - init(arg.getTypedVar().getName(), arg.getType(), null); + super(arg.getRegNum(), arg.getType()); + init(arg.getName(), arg.getType(), null); } private void init(String name, ArgType type, String sign) { @@ -43,9 +42,8 @@ final class LocalVar extends RegisterArg { LOG.error("Can't parse signature for local variable: " + sign, e); } } - TypedVar tv = new TypedVar(type); - tv.setName(name); - forceSetTypedVar(tv); + setName(name); + forceType(type); } private boolean checkSignature(ArgType type, String sign, ArgType gType) { diff --git a/jadx-core/src/main/java/jadx/core/dex/regions/IfCondition.java b/jadx-core/src/main/java/jadx/core/dex/regions/IfCondition.java index 810b9287a..fb85b489e 100644 --- a/jadx-core/src/main/java/jadx/core/dex/regions/IfCondition.java +++ b/jadx-core/src/main/java/jadx/core/dex/regions/IfCondition.java @@ -184,8 +184,8 @@ public final class IfCondition { if (a.isRegister()) { list.add((RegisterArg) a); } - InsnArg b = compare.getA(); - if (a.isRegister()) { + InsnArg b = compare.getB(); + if (b.isRegister()) { list.add((RegisterArg) b); } } else { diff --git a/jadx-core/src/main/java/jadx/core/dex/regions/LoopRegion.java b/jadx-core/src/main/java/jadx/core/dex/regions/LoopRegion.java index 16bd2facf..e010e6024 100644 --- a/jadx-core/src/main/java/jadx/core/dex/regions/LoopRegion.java +++ b/jadx-core/src/main/java/jadx/core/dex/regions/LoopRegion.java @@ -79,7 +79,7 @@ public final class LoopRegion extends AbstractRegion { return false; } else { RegisterArg res = insn.getResult(); - if (res.getTypedVar().getUseList().size() > 2) { + if (res.getSVar().getUseList().size() > 1) { return false; } boolean found = false; diff --git a/jadx-core/src/main/java/jadx/core/dex/trycatch/TryCatchBlock.java b/jadx-core/src/main/java/jadx/core/dex/trycatch/TryCatchBlock.java index 58ec8ad85..e18fc8a9a 100644 --- a/jadx-core/src/main/java/jadx/core/dex/trycatch/TryCatchBlock.java +++ b/jadx-core/src/main/java/jadx/core/dex/trycatch/TryCatchBlock.java @@ -99,10 +99,6 @@ public class TryCatchBlock { return insns; } - public int getInsnsCount() { - return insns.size(); - } - public CatchAttr getCatchAttr() { return attr; } @@ -119,7 +115,7 @@ public class TryCatchBlock { List finalBlockInsns = new ArrayList(insns); setFinalBlock(new InsnContainer(finalBlockInsns)); - InstructionRemover.unbindInsnList(finalBlockInsns); + InstructionRemover.unbindInsnList(mth, finalBlockInsns); // remove these instructions from other handlers for (ExceptionHandler h : getHandlers()) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/BlockMakerVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/BlockMakerVisitor.java index 1449cb691..d85083bd8 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/BlockMakerVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/BlockMakerVisitor.java @@ -52,13 +52,13 @@ public class BlockMakerVisitor extends AbstractVisitor { return; } mth.initBasicBlocks(); - makeBasicBlocks(mth); + splitBasicBlocks(mth); processBlocksTree(mth); BlockProcessingHelper.visit(mth); mth.finishBasicBlocks(); } - private static void makeBasicBlocks(MethodNode mth) { + private static void splitBasicBlocks(MethodNode mth) { nextBlockId = 0; InsnNode prevInsn = null; @@ -71,15 +71,13 @@ public class BlockMakerVisitor extends AbstractVisitor { boolean startNew = false; if (prevInsn != null) { InsnType type = prevInsn.getType(); - if (type == InsnType.RETURN - || type == InsnType.GOTO + if (type == InsnType.GOTO || type == InsnType.THROW || SEPARATE_INSNS.contains(type)) { if (type == InsnType.RETURN || type == InsnType.THROW) { mth.addExitBlock(curBlock); } - BlockNode block = startNewBlock(mth, insn.getOffset()); if (type == InsnType.MONITOR_ENTER || type == InsnType.MONITOR_EXIT) { connect(curBlock, block); @@ -87,38 +85,9 @@ public class BlockMakerVisitor extends AbstractVisitor { curBlock = block; startNew = true; } else { - type = insn.getType(); - startNew = SEPARATE_INSNS.contains(type); - - List pjumps = prevInsn.getAttributes().getAll(AttributeType.JUMP); - if (pjumps.size() > 0) { - for (IAttribute j : pjumps) { - JumpAttribute jump = (JumpAttribute) j; - if (jump.getSrc() == prevInsn.getOffset()) { - startNew = true; - } - } - } - - List cjumps = insn.getAttributes().getAll(AttributeType.JUMP); - if (cjumps.size() > 0) { - for (IAttribute j : cjumps) { - JumpAttribute jump = (JumpAttribute) j; - if (jump.getDest() == insn.getOffset()) { - startNew = true; - } - } - } - - // split 'do-while' block (last instruction: 'if', target this block) - if (type == InsnType.IF) { - IfNode ifs = (IfNode) (insn); - BlockNode targBlock = blocksMap.get(ifs.getTarget()); - if (targBlock == curBlock) { - startNew = true; - } - } - + startNew = isSplitByJump(prevInsn, insn) + || SEPARATE_INSNS.contains(insn.getType()) + || isDoWhile(blocksMap, curBlock, insn); if (startNew) { BlockNode block = startNewBlock(mth, insn.getOffset()); connect(curBlock, block); @@ -126,7 +95,6 @@ public class BlockMakerVisitor extends AbstractVisitor { } } } - // for try/catch make empty block for connect handlers if (insn.getAttributes().contains(AttributeFlag.TRY_ENTER)) { BlockNode block; @@ -146,12 +114,14 @@ public class BlockMakerVisitor extends AbstractVisitor { } else { blocksMap.put(insn.getOffset(), curBlock); } - curBlock.getInstructions().add(insn); prevInsn = insn; } - // setup missing connections + setupConnections(mth, blocksMap); + } + + private static void setupConnections(MethodNode mth, Map blocksMap) { for (BlockNode block : mth.getBasicBlocks()) { for (InsnNode insn : block.getInstructions()) { List jumps = insn.getAttributes().getAll(AttributeType.JUMP); @@ -164,17 +134,15 @@ public class BlockMakerVisitor extends AbstractVisitor { // connect exception handlers CatchAttr catches = (CatchAttr) insn.getAttributes().get(AttributeType.CATCH_BLOCK); - if (catches != null) { - // get synthetic block for handlers - IAttribute spl = block.getAttributes().get(AttributeType.SPLITTER_BLOCK); - if (spl != null) { - BlockNode connBlock = ((SplitterBlockAttr) spl).getBlock(); - for (ExceptionHandler h : catches.getTryBlock().getHandlers()) { - BlockNode destBlock = getBlock(h.getHandleOffset(), blocksMap); - // skip self loop in handler - if (connBlock != destBlock) { - connect(connBlock, destBlock); - } + // get synthetic block for handlers + IAttribute spl = block.getAttributes().get(AttributeType.SPLITTER_BLOCK); + if (catches != null && spl != null) { + BlockNode connBlock = ((SplitterBlockAttr) spl).getBlock(); + for (ExceptionHandler h : catches.getTryBlock().getHandlers()) { + BlockNode destBlock = getBlock(h.getHandleOffset(), blocksMap); + // skip self loop in handler + if (connBlock != destBlock) { + connect(connBlock, destBlock); } } } @@ -182,6 +150,36 @@ public class BlockMakerVisitor extends AbstractVisitor { } } + private static boolean isSplitByJump(InsnNode prevInsn, InsnNode currentInsn) { + List pJumps = prevInsn.getAttributes().getAll(AttributeType.JUMP); + for (IAttribute j : pJumps) { + JumpAttribute jump = (JumpAttribute) j; + if (jump.getSrc() == prevInsn.getOffset()) { + return true; + } + } + List cJumps = currentInsn.getAttributes().getAll(AttributeType.JUMP); + for (IAttribute j : cJumps) { + JumpAttribute jump = (JumpAttribute) j; + if (jump.getDest() == currentInsn.getOffset()) { + return true; + } + } + return false; + } + + private static boolean isDoWhile(Map blocksMap, BlockNode curBlock, InsnNode insn) { + // split 'do-while' block (last instruction: 'if', target this block) + if (insn.getType() == InsnType.IF) { + IfNode ifs = (IfNode) (insn); + BlockNode targetBlock = blocksMap.get(ifs.getTarget()); + if (targetBlock == curBlock) { + return true; + } + } + return false; + } + private static void processBlocksTree(MethodNode mth) { computeDominators(mth); markReturnBlocks(mth); @@ -189,16 +187,16 @@ public class BlockMakerVisitor extends AbstractVisitor { int i = 0; while (modifyBlocksTree(mth)) { // revert calculations - cleanDomTree(mth); + clearBlocksState(mth); // recalculate dominators tree computeDominators(mth); markReturnBlocks(mth); - i++; - if (i > 100) { + if (i++ > 100) { throw new AssertionError("Can't fix method cfg: " + mth); } } + computeDominanceFrontier(mth); registerLoops(mth); } @@ -277,32 +275,26 @@ public class BlockMakerVisitor extends AbstractVisitor { if (block == entryBlock) { continue; } + BlockNode idom; List preds = block.getPredecessors(); if (preds.size() == 1) { - block.setIDom(preds.get(0)); + idom = preds.get(0); } else { BitSet bs = new BitSet(block.getDoms().length()); bs.or(block.getDoms()); - for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) { BlockNode dom = basicBlocks.get(i); bs.andNot(dom.getDoms()); } - - int c = bs.cardinality(); - if (c == 1) { - int id = bs.nextSetBit(0); - BlockNode idom = basicBlocks.get(id); - block.setIDom(idom); - idom.getDominatesOn().add(block); - } else { + if (bs.cardinality() != 1) { throw new JadxRuntimeException("Can't find immediate dominator for block " + block + " in " + bs + " preds:" + preds); } + idom = basicBlocks.get(bs.nextSetBit(0)); } + block.setIDom(idom); + idom.addDominatesOn(block); } - - computeDominanceFrontier(mth); } private static void computeDominanceFrontier(MethodNode mth) { @@ -310,18 +302,15 @@ public class BlockMakerVisitor extends AbstractVisitor { exit.setDomFrontier(EMPTY_BITSET); } for (BlockNode block : mth.getBasicBlocks()) { - computeBlockDF(block); + computeBlockDF(mth, block); } } - private static void computeBlockDF(BlockNode block) { - if (block.getDomFrontier() != null) { - return; + private static void computeBlockDF(MethodNode mth, BlockNode block) { + for (BlockNode c : block.getDominatesOn()) { + computeBlockDF(mth, c); } BitSet domFrontier = null; - BitSet doms = block.getDoms(); - int id = block.getId(); - for (BlockNode s : block.getSuccessors()) { if (s.getIDom() != block) { if (domFrontier == null) { @@ -330,20 +319,14 @@ public class BlockMakerVisitor extends AbstractVisitor { domFrontier.set(s.getId()); } } - for (BlockNode node : block.getDominatesOn()) { - if (node.getIDom() == block) { - BitSet frontier = node.getDomFrontier(); - if (frontier == null) { - computeBlockDF(node); - frontier = node.getDomFrontier(); - } - for (int w = frontier.nextSetBit(0); w >= 0; w = frontier.nextSetBit(w + 1)) { - if (id == w || !doms.get(w)) { - if (domFrontier == null) { - domFrontier = new BitSet(); - } - domFrontier.set(w); + for (BlockNode c : block.getDominatesOn()) { + BitSet frontier = c.getDomFrontier(); + for (int p = frontier.nextSetBit(0); p >= 0; p = frontier.nextSetBit(p + 1)) { + if (mth.getBasicBlocks().get(p).getIDom() != block) { + if (domFrontier == null) { + domFrontier = new BitSet(); } + domFrontier.set(p); } } } @@ -465,28 +448,26 @@ public class BlockMakerVisitor extends AbstractVisitor { if (mth.getExitBlocks().size() == 1 || !mth.getReturnType().equals(ArgType.VOID)) { return false; } - boolean merge = false; for (BlockNode exitBlock : mth.getExitBlocks()) { List preds = exitBlock.getPredecessors(); - if (preds.size() == 1) { - BlockNode pred = preds.get(0); - for (BlockNode otherExitBlock : mth.getExitBlocks()) { - if (exitBlock != otherExitBlock - && otherExitBlock.isDominator(pred) - && otherExitBlock.getPredecessors().size() == 1) { - // merge - BlockNode otherPred = otherExitBlock.getPredecessors().get(0); - removeConnection(otherPred, otherExitBlock); - connect(otherPred, exitBlock); - merge = true; - } + if (preds.size() != 1) { + continue; + } + BlockNode pred = preds.get(0); + for (BlockNode otherExitBlock : mth.getExitBlocks()) { + if (exitBlock != otherExitBlock + && otherExitBlock.isDominator(pred) + && otherExitBlock.getPredecessors().size() == 1) { + // merge + BlockNode otherPred = otherExitBlock.getPredecessors().get(0); + removeConnection(otherPred, otherExitBlock); + connect(otherPred, exitBlock); + cleanExitNodes(mth); + return true; } } } - if (merge) { - cleanExitNodes(mth); - } - return merge; + return false; } /** @@ -545,7 +526,7 @@ public class BlockMakerVisitor extends AbstractVisitor { private static InsnNode duplicateReturnInsn(InsnNode returnInsn) { InsnNode insn = new InsnNode(returnInsn.getType(), returnInsn.getArgsCount()); - if (returnInsn.getArgsCount() != 0) { + if (returnInsn.getArgsCount() == 1) { RegisterArg arg = (RegisterArg) returnInsn.getArg(0); insn.addArg(InsnArg.reg(arg.getRegNum(), arg.getType())); } @@ -554,7 +535,7 @@ public class BlockMakerVisitor extends AbstractVisitor { return insn; } - private static void cleanDomTree(MethodNode mth) { + private static void clearBlocksState(MethodNode mth) { for (BlockNode block : mth.getBasicBlocks()) { AttributesList attrs = block.getAttributes(); attrs.remove(AttributeType.LOOP); @@ -563,6 +544,7 @@ public class BlockMakerVisitor extends AbstractVisitor { block.setDoms(null); block.setIDom(null); + block.setDomFrontier(null); block.getDominatesOn().clear(); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/BlockProcessingHelper.java b/jadx-core/src/main/java/jadx/core/dex/visitors/BlockProcessingHelper.java index 5d2790fd9..10e5f3338 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/BlockProcessingHelper.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/BlockProcessingHelper.java @@ -60,8 +60,8 @@ public class BlockProcessingHelper { type = excHandler.getCatchType().getType(); excArg.setName("e"); } - resArg.getTypedVar().forceSetType(type); - excArg.getTypedVar().forceSetType(type); + resArg.forceType(type); + excArg.setType(type); excHandler.setArg(excArg); block.getAttributes().add(handlerAttr); @@ -79,7 +79,7 @@ public class BlockProcessingHelper { } for (BlockNode excBlock : excHandler.getBlocks()) { // remove 'monitor-exit' from exception handler blocks - InstructionRemover remover = new InstructionRemover(excBlock.getInstructions()); + InstructionRemover remover = new InstructionRemover(mth, excBlock); for (InsnNode insn : excBlock.getInstructions()) { if (insn.getType() == InsnType.MONITOR_ENTER) { break; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java index 57f7ed6da..3e3246a8d 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java @@ -99,10 +99,10 @@ public class ClassModifier extends AbstractVisitor { return false; } mth.removeFirstArgument(); - InstructionRemover.remove(block, insn); + InstructionRemover.remove(mth, block, insn); // other arg usage -> wrap with IGET insn - List useList = arg.getTypedVar().getUseList(); - if (useList.size() > 1) { + List useList = arg.getSVar().getUseList(); + if (useList.size() > 0) { InsnNode iget = new IndexInsnNode(InsnType.IGET, fieldInfo, 1); iget.addArg(insn.getArg(1)); for (InsnArg insnArg : useList) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/CodeShrinker.java b/jadx-core/src/main/java/jadx/core/dex/visitors/CodeShrinker.java index 1aadaac23..80b1684d0 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/CodeShrinker.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/CodeShrinker.java @@ -5,6 +5,7 @@ import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.InsnWrapArg; import jadx.core.dex.instructions.args.RegisterArg; +import jadx.core.dex.instructions.args.SSAVar; import jadx.core.dex.instructions.mods.ConstructorInsn; import jadx.core.dex.instructions.mods.TernaryInsn; import jadx.core.dex.nodes.BlockNode; @@ -15,6 +16,7 @@ import jadx.core.utils.InsnList; import jadx.core.utils.exceptions.JadxRuntimeException; import java.util.ArrayList; +import java.util.BitSet; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; @@ -102,21 +104,26 @@ public class CodeShrinker extends AbstractVisitor { if (start > to) { throw new JadxRuntimeException("Invalid inline insn positions: " + start + " - " + to); } + BitSet args = new BitSet(); + for (RegisterArg arg : movedArgs) { + args.set(arg.getRegNum()); + } for (int i = start; i < to; i++) { ArgsInfo argsInfo = argsList.get(i); if (argsInfo.getInlinedInsn() == this) { continue; } InsnNode curInsn = argsInfo.insn; - if (!curInsn.canReorder() || usedArgAssign(curInsn, movedArgs)) { + if (!curInsn.canReorder() || usedArgAssign(curInsn, args)) { return false; } } return true; } - private static boolean usedArgAssign(InsnNode insn, List args) { - return insn.getResult() != null && args.contains(insn.getResult()); + private static boolean usedArgAssign(InsnNode insn, BitSet args) { + RegisterArg result = insn.getResult(); + return result != null && args.get(result.getRegNum()); } public WrapInfo inline(int assignInsnPos, RegisterArg arg) { @@ -181,14 +188,15 @@ public class CodeShrinker extends AbstractVisitor { List args = argsInfo.getArgs(); for (ListIterator it = args.listIterator(args.size()); it.hasPrevious(); ) { RegisterArg arg = it.previous(); - List useList = arg.getTypedVar().getUseList(); - if (useList.size() != 2) { +// if (arg.getName() != null) { +// continue; +// } + SSAVar sVar = arg.getSVar(); + if (sVar.getAssign() == null || sVar.getVariableUseCount() != 1) { continue; } - InsnNode assignInsn = selectOther(useList, arg).getParentInsn(); - if (assignInsn == null - || assignInsn.getResult() == null - || assignInsn.getResult().getRegNum() != arg.getRegNum()) { + InsnNode assignInsn = sVar.getAssign().getParentInsn(); + if (assignInsn == null) { continue; } int assignPos = insnList.getIndex(assignInsn); @@ -228,7 +236,11 @@ public class CodeShrinker extends AbstractVisitor { return false; } - List args = ArgsInfo.getArgs(assignInsn); + List argsList = ArgsInfo.getArgs(assignInsn); + BitSet args = new BitSet(); + for (RegisterArg arg : argsList) { + args.set(arg.getRegNum()); + } boolean startCheck = false; for (InsnNode insn : assignBlock.getInstructions()) { if (startCheck && (!insn.canReorder() || ArgsInfo.usedArgAssign(insn, args))) { @@ -290,9 +302,4 @@ public class CodeShrinker extends AbstractVisitor { return arg.wrapInstruction(assignInsn); } - - private static InsnArg selectOther(List list, RegisterArg insn) { - InsnArg first = list.get(0); - return insn == first ? list.get(1) : first; - } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlinerVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlinerVisitor.java index b916d17fe..1f8012521 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlinerVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlinerVisitor.java @@ -7,15 +7,15 @@ import jadx.core.dex.instructions.InvokeNode; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.LiteralArg; +import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; -import jadx.core.utils.BlockUtils; import jadx.core.utils.InstructionRemover; import jadx.core.utils.exceptions.JadxException; +import java.util.ArrayList; import java.util.List; -import java.util.Set; public class ConstInlinerVisitor extends AbstractVisitor { @@ -25,7 +25,7 @@ public class ConstInlinerVisitor extends AbstractVisitor { return; } for (BlockNode block : mth.getBasicBlocks()) { - InstructionRemover remover = new InstructionRemover(block.getInstructions()); + InstructionRemover remover = new InstructionRemover(mth, block); for (InsnNode insn : block.getInstructions()) { if (checkInsn(mth, block, insn)) { remover.add(insn); @@ -45,63 +45,42 @@ public class ConstInlinerVisitor extends AbstractVisitor { arg.merge(resType); } long lit = ((LiteralArg) arg).getLiteral(); - return replaceConst(mth, block, insn, lit); + return replaceConst(mth, insn, lit); } } return false; } - private static boolean replaceConst(MethodNode mth, BlockNode block, InsnNode insn, long literal) { - List use = insn.getResult().getTypedVar().getUseList(); + private static boolean replaceConst(MethodNode mth, InsnNode insn, long literal) { + List use = new ArrayList(insn.getResult().getSVar().getUseList()); int replaceCount = 0; - int assignCount = 0; - for (InsnArg arg : use) { + for (RegisterArg arg : use) { +// if (arg.getSVar().isUsedInPhi()) { +// continue; +// } InsnNode useInsn = arg.getParentInsn(); - if (arg == insn.getResult() || useInsn == null) { - assignCount++; + if (useInsn.getType() == InsnType.PHI) { continue; } - BlockNode useBlock = BlockUtils.getBlockByInsn(mth, useInsn); - boolean isDominator = useBlock == block || useBlock.isDominator(block); - if (isDominator && !registerReassignOnPath(block, useBlock, insn)) { - LiteralArg litArg; - if (use.size() == 2) { - // arg used only in one place - litArg = InsnArg.lit(literal, arg.getType()); - } else { - // in most cases type not equal arg.getType() - // just set unknown type and run type fixer - litArg = InsnArg.lit(literal, ArgType.UNKNOWN); - } - if (useInsn.replaceArg(arg, litArg)) { - fixTypes(mth, useInsn, litArg); - replaceCount++; - } + LiteralArg litArg; + if (use.size() == 1 || arg.isTypeImmutable()) { + // arg used only in one place + litArg = InsnArg.lit(literal, arg.getType()); + } else { + // in most cases type not equal arg.getType() + // just set unknown type and run type fixer + litArg = InsnArg.lit(literal, ArgType.UNKNOWN); + } + if (useInsn.replaceArg(arg, litArg)) { + fixTypes(mth, useInsn, litArg); + replaceCount++; } } - return replaceCount == use.size() - assignCount; - } - - private static boolean registerReassignOnPath(BlockNode block, BlockNode useBlock, InsnNode assignInsn) { - if (block == useBlock) { - return false; - } - Set blocks = BlockUtils.getAllPathsBlocks(block, useBlock); - int regNum = assignInsn.getResult().getRegNum(); - for (BlockNode b : blocks) { - for (InsnNode insn : b.getInstructions()) { - if (insn.getResult() != null - && insn != assignInsn - && insn.getResult().getRegNum() == regNum) { - return true; - } - } - } - return false; + return replaceCount == use.size(); } /** - * This is method similar to PostTypeResolver.visit method, + * This is method similar to PostTypeInference.visit method, * but contains some expensive operations needed only after constant inline */ private static void fixTypes(MethodNode mth, InsnNode insn, LiteralArg litArg) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java index db7dfd0aa..885678a58 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java @@ -24,7 +24,7 @@ import java.util.Set; public class DotGraphVisitor extends AbstractVisitor { private static final String NL = "\\l"; - private static final boolean PRINT_DOMINATORS = false; + private static final boolean PRINT_DOMINATORS = true; private final File dir; private final boolean useRegions; @@ -49,8 +49,8 @@ public class DotGraphVisitor extends AbstractVisitor { } private class DumpDotGraph { - private CodeWriter dot = new CodeWriter(); - private CodeWriter conn = new CodeWriter(); + private final CodeWriter dot = new CodeWriter(); + private final CodeWriter conn = new CodeWriter(); public void process(MethodNode mth) { dot.startLine("digraph \"CFG for"); @@ -168,16 +168,21 @@ public class DotGraphVisitor extends AbstractVisitor { } if (PRINT_DOMINATORS) { - for (BlockNode dom : BlockUtils.bitSetToBlocks(mth, block.getDoms())) { - String style = "[color=green]"; - if (dom == block.getIDom()) { - style = "[style=dashed, color=green]"; - } - addEdge(block, dom, style); + for (BlockNode c : block.getDominatesOn()) { + conn.startLine(block.getId() + " -> " + c.getId() + "[color=green];"); +// } - +// for (BlockNode dom : BlockUtils.bitSetToBlocks(mth, block.getDoms())) { +// if (dom == block.getIDom()) { +// conn.startLine(dom.getId() + " -> " + block.getId() + "[style=dashed, color=green];"); +//// addEdge(block, dom, "[style=dashed, color=green]"); +// } else { +//// addEdge(block, dom, "[color=green]"); +// } +// } for (BlockNode dom : BlockUtils.bitSetToBlocks(mth, block.getDomFrontier())) { - addEdge(block, dom, "[color=blue]"); + conn.startLine("f_" + block.getId() + " -> f_" + dom.getId() + "[color=blue];"); +// addEdge(block, dom, "[color=blue]"); } } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java index 7373e7ea1..d26c7158d 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java @@ -54,7 +54,7 @@ public class ModVisitor extends AbstractVisitor { private static void replaceStep(MethodNode mth) { ClassNode parentClass = mth.getParentClass(); for (BlockNode block : mth.getBasicBlocks()) { - InstructionRemover remover = new InstructionRemover(block.getInstructions()); + InstructionRemover remover = new InstructionRemover(mth, block); int size = block.getInstructions().size(); for (int i = 0; i < size; i++) { @@ -65,6 +65,7 @@ public class ModVisitor extends AbstractVisitor { MethodInfo callMth = inv.getCallMth(); if (callMth.isConstructor()) { ConstructorInsn co = new ConstructorInsn(mth, inv); + removeInsnForArg(remover, co.getInstanceArg()); boolean remove = false; if (co.isSuper() && (co.getArgsCount() == 0 || parentClass.isEnum())) { remove = true; @@ -149,7 +150,7 @@ public class ModVisitor extends AbstractVisitor { */ private static void removeStep(MethodNode mth) { for (BlockNode block : mth.getBasicBlocks()) { - InstructionRemover remover = new InstructionRemover(block.getInstructions()); + InstructionRemover remover = new InstructionRemover(mth, block); int size = block.getInstructions().size(); for (int i = 0; i < size; i++) { @@ -211,7 +212,7 @@ public class ModVisitor extends AbstractVisitor { && size > 0 && insns.get(size - 1).getType() == InsnType.THROW) { - InstructionRemover.remove(excBlock, size - 1); + InstructionRemover.remove(mth, excBlock, size - 1); // move not removed instructions to 'finally' block if (insns.size() != 0) { @@ -228,8 +229,8 @@ public class ModVisitor extends AbstractVisitor { if (blockInsns.size() > 0) { InsnNode insn = blockInsns.get(0); if (insn.getType() == InsnType.MOVE_EXCEPTION - && insn.getResult().getTypedVar().getUseList().size() <= 1) { - InstructionRemover.remove(block, 0); + && insn.getResult().getSVar().getUseList().isEmpty()) { + InstructionRemover.remove(mth, block, 0); } } @@ -252,12 +253,22 @@ public class ModVisitor extends AbstractVisitor { block.getInstructions().set(i, insn); } + /** + * In argument not used in other instructions then remove assign instruction. + */ + private static void removeInsnForArg(InstructionRemover remover, RegisterArg arg) { + if (arg.getSVar().getUseList().isEmpty() + && arg.getAssignInsn() != null) { + remover.add(arg.getAssignInsn()); + } + } + private static void checkArgsNames(MethodNode mth) { for (RegisterArg arg : mth.getArguments(false)) { - String name = arg.getTypedVar().getName(); + String name = arg.getName(); if (name != null && NameMapper.isReserved(name)) { name = name + "_"; - arg.getTypedVar().setName(name); + arg.getSVar().setName(name); } } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/PrepareForCodeGen.java b/jadx-core/src/main/java/jadx/core/dex/visitors/PrepareForCodeGen.java index 218ff79bf..cdc758d3b 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/PrepareForCodeGen.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/PrepareForCodeGen.java @@ -64,10 +64,10 @@ public class PrepareForCodeGen extends AbstractVisitor { InsnNode insn = list.get(i); // replace 'move' with inner wrapped instruction if (insn.getType() == InsnType.MOVE - && insn.getArg(0).isInsnWrap() - && !insn.getAttributes().contains(AttributeFlag.DECLARE_VAR)) { + && insn.getArg(0).isInsnWrap()) { InsnNode wrapInsn = ((InsnWrapArg) insn.getArg(0)).getWrapInsn(); wrapInsn.setResult(insn.getResult()); + wrapInsn.getAttributes().addAll(insn.getAttributes()); list.set(i, wrapInsn); } } @@ -115,14 +115,23 @@ public class PrepareForCodeGen extends AbstractVisitor { List list = block.getInstructions(); for (int i = 0; i < list.size(); i++) { InsnNode insn = list.get(i); - if (insn.getType() == InsnType.ARITH) { - ArithNode arith = (ArithNode) insn; - RegisterArg res = arith.getResult(); - InsnArg arg = arith.getArg(0); - if (res.equals(arg)) { - ArithNode newArith = new ArithNode(arith.getOp(), res, arith.getArg(1)); - list.set(i, newArith); - } + if (insn.getType() != InsnType.ARITH) { + continue; + } + ArithNode arith = (ArithNode) insn; + RegisterArg res = arith.getResult(); + InsnArg arg = arith.getArg(0); + boolean replace = false; + + if (res.equals(arg)) { + replace = true; + } else if (arg.isRegister()) { + RegisterArg regArg = (RegisterArg) arg; + replace = res.equalRegisterAndType(regArg); + } + if (replace) { + ArithNode newArith = new ArithNode(arith.getOp(), res, arith.getArg(1)); + list.set(i, newArith); } } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java index 5711b8a4f..a4e0b8241 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java @@ -58,123 +58,18 @@ public class SimplifyVisitor extends AbstractVisitor { } switch (insn.getType()) { case ARITH: - ArithNode arith = (ArithNode) insn; - if (arith.getArgsCount() == 2) { - InsnArg litArg = null; - - if (arith.getArg(1).isInsnWrap()) { - InsnNode wr = ((InsnWrapArg) arith.getArg(1)).getWrapInsn(); - if (wr.getType() == InsnType.CONST) { - litArg = wr.getArg(0); - } - } else if (arith.getArg(1).isLiteral()) { - litArg = arith.getArg(1); - } - - if (litArg != null) { - long lit = ((LiteralArg) litArg).getLiteral(); - boolean invert = false; - - if (arith.getOp() == ArithOp.ADD && lit < 0) { - invert = true; - } - - // fix 'c + (-1)' => 'c - (1)' - if (invert) { - return new ArithNode(ArithOp.SUB, - arith.getResult(), insn.getArg(0), - InsnArg.lit(-lit, litArg.getType())); - } - } - } - break; + return simplifyArith(insn); case IF: - // simplify 'cmp' instruction in if condition - IfNode ifb = (IfNode) insn; - InsnArg f = ifb.getArg(0); - if (f.isInsnWrap()) { - InsnNode wi = ((InsnWrapArg) f).getWrapInsn(); - if (wi.getType() == InsnType.CMP_L || wi.getType() == InsnType.CMP_G) { - if (ifb.getArg(1).isLiteral() - && ((LiteralArg) ifb.getArg(1)).getLiteral() == 0) { - ifb.changeCondition(ifb.getOp(), wi.getArg(0), wi.getArg(1)); - } else { - LOG.warn("TODO: cmp" + ifb); - } - } - } + simplifyIf((IfNode) insn); break; case INVOKE: - MethodInfo callMth = ((InvokeNode) insn).getCallMth(); - if (callMth.getDeclClass().getFullName().equals(Consts.CLASS_STRING_BUILDER) - && callMth.getShortId().equals(Consts.MTH_TOSTRING_SIGNATURE) - && insn.getArg(0).isInsnWrap()) { - try { - List chain = flattenInsnChain(insn); - if (chain.size() > 1 && chain.get(0).getType() == InsnType.CONSTRUCTOR) { - ConstructorInsn constr = (ConstructorInsn) chain.get(0); - if (constr.getClassType().getFullName().equals(Consts.CLASS_STRING_BUILDER) - && constr.getArgsCount() == 0) { - int len = chain.size(); - InsnNode concatInsn = new InsnNode(InsnType.STR_CONCAT, len - 1); - for (int i = 1; i < len; i++) { - concatInsn.addArg(chain.get(i).getArg(1)); - } - concatInsn.setResult(insn.getResult()); - return concatInsn; - } - } - } catch (Throwable e) { - LOG.debug("Can't convert string concatenation: {} insn: {}", mth, insn, e); - } - } - break; + return convertInvoke(mth, insn); case IPUT: case SPUT: - // convert field arith operation to arith instruction - // (IPUT = ARITH (IGET, lit) -> ARITH (fieldArg = lit)) - InsnArg arg = insn.getArg(0); - if (arg.isInsnWrap()) { - InsnNode wrap = ((InsnWrapArg) arg).getWrapInsn(); - InsnType wrapType = wrap.getType(); - if ((wrapType == InsnType.ARITH || wrapType == InsnType.STR_CONCAT) && wrap.getArg(0).isInsnWrap()) { - InsnNode get = ((InsnWrapArg) wrap.getArg(0)).getWrapInsn(); - InsnType getType = get.getType(); - if (getType == InsnType.IGET || getType == InsnType.SGET) { - FieldInfo field = (FieldInfo) ((IndexInsnNode) insn).getIndex(); - FieldInfo innerField = (FieldInfo) ((IndexInsnNode) get).getIndex(); - if (field.equals(innerField)) { - try { - RegisterArg reg = null; - if (getType == InsnType.IGET) { - reg = ((RegisterArg) get.getArg(0)); - } - RegisterArg fArg = new FieldArg(field, reg != null ? reg.getRegNum() : -1); - if (reg != null) { - fArg.replaceTypedVar(get.getArg(0)); - } - if (wrapType == InsnType.ARITH) { - ArithNode ar = (ArithNode) wrap; - return new ArithNode(ar.getOp(), fArg, fArg, ar.getArg(1)); - } else { - int argsCount = wrap.getArgsCount(); - InsnNode concat = new InsnNode(InsnType.STR_CONCAT, argsCount - 1); - for (int i = 1; i < argsCount; i++) { - concat.addArg(wrap.getArg(i)); - } - return new ArithNode(ArithOp.ADD, fArg, fArg, InsnArg.wrapArg(concat)); - } - } catch (Throwable e) { - LOG.debug("Can't convert field arith insn: {}, mth: {}", insn, mth, e); - } - } - } - } - } - break; + return convertFieldArith(mth, insn); case CHECK_CAST: InsnArg castArg = insn.getArg(0); @@ -193,6 +88,129 @@ public class SimplifyVisitor extends AbstractVisitor { return null; } + /** + * Simplify 'cmp' instruction in if condition + */ + private static void simplifyIf(IfNode insn) { + InsnArg f = insn.getArg(0); + if (f.isInsnWrap()) { + InsnNode wi = ((InsnWrapArg) f).getWrapInsn(); + if (wi.getType() == InsnType.CMP_L || wi.getType() == InsnType.CMP_G) { + if (insn.getArg(1).isLiteral() + && ((LiteralArg) insn.getArg(1)).getLiteral() == 0) { + insn.changeCondition(insn.getOp(), wi.getArg(0), wi.getArg(1)); + } else { + LOG.warn("TODO: cmp" + insn); + } + } + } + } + + private static InsnNode convertInvoke(MethodNode mth, InsnNode insn) { + MethodInfo callMth = ((InvokeNode) insn).getCallMth(); + if (callMth.getDeclClass().getFullName().equals(Consts.CLASS_STRING_BUILDER) + && callMth.getShortId().equals(Consts.MTH_TOSTRING_SIGNATURE) + && insn.getArg(0).isInsnWrap()) { + try { + List chain = flattenInsnChain(insn); + if (chain.size() > 1 && chain.get(0).getType() == InsnType.CONSTRUCTOR) { + ConstructorInsn constr = (ConstructorInsn) chain.get(0); + if (constr.getClassType().getFullName().equals(Consts.CLASS_STRING_BUILDER) + && constr.getArgsCount() == 0) { + int len = chain.size(); + InsnNode concatInsn = new InsnNode(InsnType.STR_CONCAT, len - 1); + for (int i = 1; i < len; i++) { + concatInsn.addArg(chain.get(i).getArg(1)); + } + concatInsn.setResult(insn.getResult()); + return concatInsn; + } + } + } catch (Throwable e) { + LOG.debug("Can't convert string concatenation: {} insn: {}", mth, insn, e); + } + } + return null; + } + + private static InsnNode simplifyArith(InsnNode insn) { + ArithNode arith = (ArithNode) insn; + if (arith.getArgsCount() != 2) { + return null; + } + InsnArg litArg = null; + InsnArg secondArg = arith.getArg(1); + if (secondArg.isInsnWrap()) { + InsnNode wr = ((InsnWrapArg) secondArg).getWrapInsn(); + if (wr.getType() == InsnType.CONST) { + litArg = wr.getArg(0); + } + } else if (secondArg.isLiteral()) { + litArg = secondArg; + } + if (litArg != null) { + long lit = ((LiteralArg) litArg).getLiteral(); + // fix 'c + (-1)' => 'c - (1)' + if (arith.getOp() == ArithOp.ADD && lit < 0) { + return new ArithNode(ArithOp.SUB, + arith.getResult(), insn.getArg(0), + InsnArg.lit(-lit, litArg.getType())); + } + } + return null; + } + + /** + * Convert field arith operation to arith instruction + * (IPUT = ARITH (IGET, lit) -> ARITH (fieldArg = lit)) + */ + private static InsnNode convertFieldArith(MethodNode mth, InsnNode insn) { + InsnArg arg = insn.getArg(0); + if (!arg.isInsnWrap()) { + return null; + } + InsnNode wrap = ((InsnWrapArg) arg).getWrapInsn(); + InsnType wrapType = wrap.getType(); + if ((wrapType != InsnType.ARITH && wrapType != InsnType.STR_CONCAT) + || !wrap.getArg(0).isInsnWrap()) { + return null; + } + InsnNode get = ((InsnWrapArg) wrap.getArg(0)).getWrapInsn(); + InsnType getType = get.getType(); + if (getType != InsnType.IGET && getType != InsnType.SGET) { + return null; + } + FieldInfo field = (FieldInfo) ((IndexInsnNode) insn).getIndex(); + FieldInfo innerField = (FieldInfo) ((IndexInsnNode) get).getIndex(); + if (!field.equals(innerField)) { + return null; + } + try { + RegisterArg reg = null; + if (getType == InsnType.IGET) { + reg = ((RegisterArg) get.getArg(0)); + } + FieldArg fArg = new FieldArg(field, reg); + if (reg != null) { + fArg.setType(get.getArg(0).getType()); + } + if (wrapType == InsnType.ARITH) { + ArithNode ar = (ArithNode) wrap; + return new ArithNode(ar.getOp(), fArg, ar.getArg(1)); + } else { + int argsCount = wrap.getArgsCount(); + InsnNode concat = new InsnNode(InsnType.STR_CONCAT, argsCount - 1); + for (int i = 1; i < argsCount; i++) { + concat.addArg(wrap.getArg(i)); + } + return new ArithNode(ArithOp.ADD, fArg, InsnArg.wrapArg(concat)); + } + } catch (Throwable e) { + LOG.debug("Can't convert field arith insn: {}, mth: {}", insn, mth, e); + } + return null; + } + private static List flattenInsnChain(InsnNode insn) { List chain = new ArrayList(); InsnArg i = insn.getArg(0); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/IfRegionVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/IfRegionVisitor.java index 833f8e574..8b4a2e00b 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/IfRegionVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/IfRegionVisitor.java @@ -60,6 +60,9 @@ public class IfRegionVisitor extends AbstractVisitor implements IRegionVisitor, tryInvertIfRegion(ifRegion); } } + if (RegionUtils.isEmpty(ifRegion.getThenRegion())) { + tryInvertIfRegion(ifRegion); + } } private static void moveReturnToThenBlock(MethodNode mth, IfRegion ifRegion) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessTryCatchRegions.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessTryCatchRegions.java index 4e202be1f..a038c788d 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessTryCatchRegions.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessTryCatchRegions.java @@ -101,7 +101,7 @@ public class ProcessTryCatchRegions extends AbstractRegionVisitor { // search dominator blocks in this region (don't need to go deeper) for (BlockNode dominator : tryBlocksMap.keySet()) { if (region.getSubBlocks().contains(dominator)) { - wrapBlocks(mth, region, dominator); + wrapBlocks(region, dominator); tryBlocksMap.remove(dominator); // if region is modified rerun this method leaveRegion(mth, region); @@ -113,7 +113,7 @@ public class ProcessTryCatchRegions extends AbstractRegionVisitor { /** * Extract all block dominated by 'dominator' to separate region and mark as try/catch block */ - private void wrapBlocks(MethodNode mth, IRegion region, BlockNode dominator) { + private void wrapBlocks(IRegion region, BlockNode dominator) { Region newRegion = new Region(region); TryCatchBlock tb = tryBlocksMap.get(dominator); assert tb != null; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessVariables.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessVariables.java index 685431b27..287ea3f62 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessVariables.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessVariables.java @@ -3,6 +3,7 @@ package jadx.core.dex.visitors.regions; import jadx.core.dex.attributes.AttributeFlag; import jadx.core.dex.attributes.AttributeType; import jadx.core.dex.attributes.DeclareVariablesAttr; +import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.nodes.IBlock; import jadx.core.dex.nodes.IContainer; @@ -28,6 +29,38 @@ import org.slf4j.LoggerFactory; public class ProcessVariables extends AbstractVisitor { private static final Logger LOG = LoggerFactory.getLogger(ProcessVariables.class); + private static class Variable { + private final int regNum; + private final ArgType type; + + public Variable(RegisterArg arg) { + this.regNum = arg.getRegNum(); + this.type = arg.getType(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Variable variable = (Variable) o; + return regNum == variable.regNum && type.equals(variable.type); + } + + @Override + public int hashCode() { + return 31 * regNum + type.hashCode(); + } + + @Override + public String toString() { + return regNum + " " + type; + } + } + private static class Usage { private RegisterArg arg; private IRegion argRegion; @@ -66,7 +99,7 @@ public class ProcessVariables extends AbstractVisitor { @Override public void visit(MethodNode mth) throws JadxException { - final Map usageMap = new LinkedHashMap(); + final Map usageMap = new LinkedHashMap(); // collect all variables usage IRegionVisitor collect = new TracedRegionVisitor() { @@ -79,11 +112,7 @@ public class ProcessVariables extends AbstractVisitor { // result RegisterArg result = insn.getResult(); if (result != null && result.isRegister()) { - Usage u = usageMap.get(result); - if (u == null) { - u = new Usage(); - usageMap.put(result, u); - } + Usage u = addToUsageMap(result, usageMap); if (u.getArg() == null) { u.setArg(result); u.setArgRegion(curRegion); @@ -94,11 +123,7 @@ public class ProcessVariables extends AbstractVisitor { args.clear(); insn.getRegisterArgs(args); for (RegisterArg arg : args) { - Usage u = usageMap.get(arg); - if (u == null) { - u = new Usage(); - usageMap.put(arg, u); - } + Usage u = addToUsageMap(arg, usageMap); u.getUseRegions().add(curRegion); } } @@ -109,11 +134,11 @@ public class ProcessVariables extends AbstractVisitor { // reduce assigns map List mthArgs = mth.getArguments(true); for (RegisterArg arg : mthArgs) { - usageMap.remove(arg); + usageMap.remove(new Variable(arg)); } - for (Iterator> it = usageMap.entrySet().iterator(); it.hasNext(); ) { - Entry entry = it.next(); + for (Iterator> it = usageMap.entrySet().iterator(); it.hasNext(); ) { + Entry entry = it.next(); Usage u = entry.getValue(); // if no assigns => remove @@ -134,7 +159,7 @@ public class ProcessVariables extends AbstractVisitor { } // apply - for (Entry entry : usageMap.entrySet()) { + for (Entry entry : usageMap.entrySet()) { Usage u = entry.getValue(); // find region which contain all usage regions @@ -168,6 +193,16 @@ public class ProcessVariables extends AbstractVisitor { } } + Usage addToUsageMap(RegisterArg arg, Map usageMap) { + Variable varId = new Variable(arg); + Usage usage = usageMap.get(varId); + if (usage == null) { + usage = new Usage(); + usageMap.put(varId, usage); + } + return usage; + } + private static void declareVar(IContainer region, RegisterArg arg) { DeclareVariablesAttr dv = (DeclareVariablesAttr) region.getAttributes().get(AttributeType.DECLARE_VARIABLES); if (dv == null) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java index 56c7a83bd..9b8541c54 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java @@ -129,12 +129,10 @@ public class RegionMaker { } } } - if (!processed) { r.getSubBlocks().add(block); next = BlockUtils.getNextBlock(block); } - if (next != null && !stack.containsExit(block) && !stack.containsExit(next)) { return next; } else { @@ -153,12 +151,12 @@ public class RegionMaker { if (nextStart != null && exitBlocksSet.remove(nextStart)) { exitBlocks.add(nextStart); } - if (exitBlocksSet.remove(loop.getEnd())) { - exitBlocks.add(loop.getEnd()); - } if (exitBlocksSet.remove(loopStart)) { exitBlocks.add(loopStart); } + if (exitBlocksSet.remove(loop.getEnd())) { + exitBlocks.add(loop.getEnd()); + } exitBlocks.addAll(exitBlocksSet); exitBlocksSet = null; @@ -185,7 +183,7 @@ public class RegionMaker { loopRegion = new LoopRegion(curRegion, condBlock, condBlock == loop.getEnd()); boolean found = true; - if (condBlock != loopStart) { + if (condBlock != loopStart && condBlock != loop.getEnd()) { if (condBlock.getPredecessors().contains(loopStart)) { loopRegion.setPreCondition(loopStart); // if we can't merge pre-condition this is not correct header @@ -332,7 +330,7 @@ public class RegionMaker { traverseMonitorExits(synchRegion, insn.getArg(0), block, exits, cacheSet); for (InsnNode exitInsn : synchRegion.getExitInsns()) { - InstructionRemover.unbindInsn(exitInsn); + InstructionRemover.unbindInsn(mth, exitInsn); } block = BlockUtils.getNextBlock(block); @@ -430,13 +428,13 @@ public class RegionMaker { } // invert condition (compiler often do it) ifnode.invertCondition(); - BlockNode tmp = bThen; - bThen = bElse; - bElse = tmp; + bThen = ifnode.getThenBlock(); + bElse = ifnode.getElseBlock(); thenBlock = bThen; // select else and exit blocks - if (block.getDominatesOn().size() == 2) { + if (block.getDominatesOn().size() == 2 + && !BlockUtils.isPathExists(bThen, bElse)) { elseBlock = bElse; } else { if (bElse.getPredecessors().size() != 1) { @@ -571,14 +569,15 @@ public class RegionMaker { pass = false; break; } - List useList = res.getTypedVar().getUseList(); - if (useList.size() != 2) { + List useList = res.getSVar().getUseList(); + if (useList.size() != 1) { pass = false; break; } else { - InsnArg arg = useList.get(1); + InsnArg arg = useList.get(0); InsnNode usePlace = arg.getParentInsn(); - if (!BlockUtils.blockContains(block, usePlace) && !BlockUtils.blockContains(next, usePlace)) { + if (!BlockUtils.blockContains(block, usePlace) + && !BlockUtils.blockContains(next, usePlace)) { pass = false; break; } @@ -638,8 +637,9 @@ public class RegionMaker { for (int i = domsOn.nextSetBit(0); i >= 0; i = domsOn.nextSetBit(i + 1)) { BlockNode b = mth.getBasicBlocks().get(i); for (BlockNode s : b.getCleanSuccessors()) { - if (domsOn.get(s.getId())) { - domsOn.clear(s.getId()); + int id = s.getId(); + if (domsOn.get(id)) { + domsOn.clear(id); } } } @@ -657,11 +657,12 @@ public class RegionMaker { stack.push(sw); if (out != null) { stack.addExit(out); - } else { - for (BlockNode e : BlockUtils.bitSetToBlocks(mth, domsOn)) { - stack.addExit(e); - } } +// else { +// for (BlockNode e : BlockUtils.bitSetToBlocks(mth, domsOn)) { +// stack.addExit(e); +// } +// } if (!stack.containsExit(defCase)) { sw.setDefaultCase(makeRegion(defCase, stack)); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/TernaryMod.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/TernaryMod.java index a2fcc3bff..b22a5ad60 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/TernaryMod.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/TernaryMod.java @@ -4,6 +4,7 @@ import jadx.core.dex.attributes.AttributeFlag; import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.InsnArg; +import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.instructions.mods.TernaryInsn; import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.IContainer; @@ -15,8 +16,6 @@ import jadx.core.dex.regions.TernaryRegion; import jadx.core.dex.visitors.CodeShrinker; import jadx.core.utils.InsnList; -import java.util.List; - public class TernaryMod { private TernaryMod() { @@ -41,12 +40,14 @@ public class TernaryMod { InsnNode e = eb.getInstructions().get(0); if (t.getResult() != null && e.getResult() != null - && t.getResult().getTypedVar() == e.getResult().getTypedVar()) { + && t.getResult().equalRegisterAndType(e.getResult()) + && t.getResult().getSVar().isUsedInPhi()) { InsnList.remove(tb, t); InsnList.remove(eb, e); + RegisterArg resArg = t.getResult().getSVar().getUsedInPhi().getResult(); TernaryInsn ternInsn = new TernaryInsn(ifRegion.getCondition(), - t.getResult(), InsnArg.wrapArg(t), InsnArg.wrapArg(e)); + resArg, InsnArg.wrapArg(t), InsnArg.wrapArg(e)); TernaryRegion tern = new TernaryRegion(ifRegion, header); // TODO: add api for replace regions ifRegion.setTernRegion(tern); @@ -55,12 +56,6 @@ public class TernaryMod { header.getInstructions().clear(); header.getInstructions().add(ternInsn); - // unbind result args - List useList = ternInsn.getResult().getTypedVar().getUseList(); - useList.remove(t.getResult()); - useList.remove(e.getResult()); - useList.add(ternInsn.getResult()); - // shrink method again CodeShrinker.shrinkMethod(mth); return; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/EliminatePhiNodes.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/EliminatePhiNodes.java new file mode 100644 index 000000000..7305bb8df --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/EliminatePhiNodes.java @@ -0,0 +1,89 @@ +package jadx.core.dex.visitors.ssa; + +import jadx.core.dex.attributes.AttributeType; +import jadx.core.dex.attributes.PhiListAttr; +import jadx.core.dex.instructions.PhiInsn; +import jadx.core.dex.instructions.args.SSAVar; +import jadx.core.dex.nodes.BlockNode; +import jadx.core.dex.nodes.InsnNode; +import jadx.core.dex.nodes.MethodNode; +import jadx.core.dex.visitors.AbstractVisitor; +import jadx.core.utils.exceptions.JadxException; + +import java.util.Iterator; +import java.util.List; + +public class EliminatePhiNodes extends AbstractVisitor { + @Override + public void visit(MethodNode mth) throws JadxException { + if (mth.isNoCode()) { + return; + } + enumerateSVars(mth); + removePhiInstructions(mth); + } + + public static void enumerateSVars(MethodNode mth) { + for (SSAVar sVar : mth.getSVars()) { + if (sVar.isUsedInPhi()) { + sVar.mergeName(sVar.getUsedInPhi().getResult()); + } + } + } + +// public static void enumerateSVars(MethodNode mth) { +// List vars = mth.getSVars(); +// int varsSize = vars.size(); +// Deque workList = new LinkedList(); +// for (int i = 0; i < varsSize; i++) { +// SSAVar ssaVar = vars.get(i); +// ssaVar.setVarId(i); +// if (ssaVar.isUsedInPhi()) { +// workList.add(ssaVar); +// } +// } +// +// int k = 0; +// while (!workList.isEmpty()) { +// SSAVar var = workList.pop(); +// RegisterArg assignVar = var.getUsedInPhi().getResult(); +// // set same name and variable ID +// var.mergeName(assignVar); +// SSAVar assignSVar = assignVar.getSVar(); +// int varId = assignSVar.getVarId(); +// var.setVarId(varId); +// +// if (assignSVar.isUsedInPhi()) { +// PhiInsn assignPhi = assignSVar.getUsedInPhi(); +// SSAVar asVar = assignPhi.getResult().getSVar(); +// if (asVar.getVarId() != varId) { +// asVar.setVarId(varId); +// for (int i = 0; i < assignPhi.getArgsCount(); i++) { +// workList.push(assignPhi.getArg(i).getSVar()); +// } +// } +// } +// if (k++ > 1000) { +// throw new JadxRuntimeException("Can't calculate variable id"); +// } +// } +// } + + private static void removePhiInstructions(MethodNode mth) { + for (BlockNode block : mth.getBasicBlocks()) { + PhiListAttr phiList = (PhiListAttr) block.getAttributes().get(AttributeType.PHI_LIST); + if (phiList == null) { + continue; + } + List list = phiList.getList(); + for (PhiInsn phiInsn : list) { + for (Iterator iterator = block.getInstructions().iterator(); iterator.hasNext(); ) { + InsnNode insn = iterator.next(); + if (insn == phiInsn) { + iterator.remove(); + } + } + } + } + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/LiveVarAnalysis.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/LiveVarAnalysis.java new file mode 100644 index 000000000..0185415ad --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/LiveVarAnalysis.java @@ -0,0 +1,109 @@ +package jadx.core.dex.visitors.ssa; + +import jadx.core.dex.instructions.args.InsnArg; +import jadx.core.dex.instructions.args.RegisterArg; +import jadx.core.dex.nodes.BlockNode; +import jadx.core.dex.nodes.InsnNode; +import jadx.core.dex.nodes.MethodNode; +import jadx.core.utils.exceptions.JadxRuntimeException; + +import java.util.BitSet; +import java.util.List; + +public class LiveVarAnalysis { + private final MethodNode mth; + + private BitSet[] uses; + private BitSet[] defs; + private BitSet[] liveIn; + private BitSet[] assignBlocks; + + public LiveVarAnalysis(MethodNode mth) { + this.mth = mth; + runAnalysis(); + } + + public void runAnalysis() { + int bbCount = mth.getBasicBlocks().size(); + int regsCount = mth.getRegsCount(); + this.uses = initBitSetArray(bbCount, regsCount); + this.defs = initBitSetArray(bbCount, regsCount); + this.assignBlocks = initBitSetArray(regsCount, bbCount); + fillBasicBlockInfo(); + processLiveInfo(); + } + + public BitSet getAssignBlocks(int regNum) { + return assignBlocks[regNum]; + } + + public boolean isLive(int blockId, int regNum) { + return liveIn[blockId].get(regNum); + } + + private void fillBasicBlockInfo() { + for (BlockNode block : mth.getBasicBlocks()) { + int blockId = block.getId(); + BitSet gen = uses[blockId]; + BitSet kill = defs[blockId]; + for (InsnNode insn : block.getInstructions()) { + for (InsnArg arg : insn.getArguments()) { + if (arg.isRegister()) { + int regNum = ((RegisterArg) arg).getRegNum(); + if (!kill.get(regNum)) { + gen.set(regNum); + } + } + } + RegisterArg result = insn.getResult(); + if (result != null) { + int regNum = result.getRegNum(); + kill.set(regNum); + assignBlocks[regNum].set(blockId); + } + } + } + } + + private void processLiveInfo() { + int bbCount = mth.getBasicBlocks().size(); + int regsCount = mth.getRegsCount(); + BitSet[] liveIn = initBitSetArray(bbCount, regsCount); + List blocks = mth.getBasicBlocks(); + int blocksSize = blocks.size(); + boolean changed; + int k = 0; + do { + changed = false; + for (int i = 0; i < blocksSize; i++) { + BlockNode block = blocks.get(i); + int blockId = block.getId(); + BitSet prevIn = liveIn[blockId]; + BitSet newIn = new BitSet(regsCount); + List successors = block.getSuccessors(); + for (int s = 0, successorsSize = successors.size(); s < successorsSize; s++) { + newIn.or(liveIn[successors.get(s).getId()]); + } + newIn.andNot(defs[blockId]); + newIn.or(uses[blockId]); + if (!prevIn.equals(newIn)) { + changed = true; + liveIn[blockId] = newIn; + } + } + if (k++ > 1000) { + throw new JadxRuntimeException("Live variable analysis reach iterations limit"); + } + } while (changed); + + this.liveIn = liveIn; + } + + private static BitSet[] initBitSetArray(int length, int bitsCount) { + BitSet[] array = new BitSet[length]; + for (int i = 0; i < length; i++) { + array[i] = new BitSet(bitsCount); + } + return array; + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java new file mode 100644 index 000000000..7deb2a10a --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java @@ -0,0 +1,216 @@ +package jadx.core.dex.visitors.ssa; + +import jadx.core.dex.attributes.AttributeType; +import jadx.core.dex.attributes.PhiListAttr; +import jadx.core.dex.instructions.InsnType; +import jadx.core.dex.instructions.PhiInsn; +import jadx.core.dex.instructions.args.InsnArg; +import jadx.core.dex.instructions.args.RegisterArg; +import jadx.core.dex.instructions.args.SSAVar; +import jadx.core.dex.nodes.BlockNode; +import jadx.core.dex.nodes.InsnNode; +import jadx.core.dex.nodes.MethodNode; +import jadx.core.dex.visitors.AbstractVisitor; +import jadx.core.utils.InstructionRemover; +import jadx.core.utils.exceptions.JadxException; +import jadx.core.utils.exceptions.JadxRuntimeException; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; + +public class SSATransform extends AbstractVisitor { + + @Override + public void visit(MethodNode mth) throws JadxException { + if (mth.isNoCode()) { + return; + } + process(mth); + } + + public void process(MethodNode mth) { + LiveVarAnalysis la = new LiveVarAnalysis(mth); + la.runAnalysis(); + for (int i = 0; i < mth.getRegsCount(); i++) { + placePhi(mth, i, la); + } + renameVariables(mth); + removeUselessPhi(mth); + } + + private void placePhi(MethodNode mth, int regNum, LiveVarAnalysis la) { + List blocks = mth.getBasicBlocks(); + int blocksCount = blocks.size(); + BitSet hasPhi = new BitSet(blocksCount); + BitSet processed = new BitSet(blocksCount); + Deque workList = new LinkedList(); + + BitSet assignBlocks = la.getAssignBlocks(regNum); + for (int id = assignBlocks.nextSetBit(0); id >= 0; id = assignBlocks.nextSetBit(id + 1)) { + processed.set(id); + workList.add(blocks.get(id)); + } + while (!workList.isEmpty()) { + BlockNode block = workList.pop(); + BitSet domFrontier = block.getDomFrontier(); + for (int id = domFrontier.nextSetBit(0); id >= 0; id = domFrontier.nextSetBit(id + 1)) { + if (!hasPhi.get(id) && la.isLive(id, regNum)) { + BlockNode df = blocks.get(id); + addPhi(df, regNum); + hasPhi.set(id); + if (!processed.get(id)) { + processed.set(id); + workList.add(df); + } + } + } + } + } + + private void addPhi(BlockNode block, int regNum) { + PhiListAttr phiList = (PhiListAttr) block.getAttributes().get(AttributeType.PHI_LIST); + if (phiList == null) { + phiList = new PhiListAttr(); + block.getAttributes().add(phiList); + } + PhiInsn phiInsn = new PhiInsn(regNum, block.getPredecessors().size()); + phiList.getList().add(phiInsn); + block.getInstructions().add(0, phiInsn); + } + + private void renameVariables(MethodNode mth) { + int regsCount = mth.getRegsCount(); + SSAVar[] vars = new SSAVar[regsCount]; + int[] versions = new int[regsCount]; + // init method arguments + for (RegisterArg arg : mth.getArguments(true)) { + int regNum = arg.getRegNum(); + vars[regNum] = mth.makeNewSVar(regNum, versions, arg); + } + renameVar(mth, vars, versions, mth.getEnterBlock()); + } + + private void renameVar(MethodNode mth, SSAVar[] vars, int[] vers, BlockNode block) { + SSAVar[] inputVars = Arrays.copyOf(vars, vars.length); + for (InsnNode insn : block.getInstructions()) { + if (insn.getType() != InsnType.PHI) { + for (InsnArg arg : insn.getArguments()) { + if (arg.isRegister()) { + RegisterArg reg = (RegisterArg) arg; + int regNum = reg.getRegNum(); + SSAVar var = vars[regNum]; + if (var == null) { + var = mth.makeNewSVar(regNum, vers, null); + vars[regNum] = var; + } + var.use(reg); + } + } + } + RegisterArg result = insn.getResult(); + if (result != null) { + int regNum = result.getRegNum(); + vars[regNum] = mth.makeNewSVar(regNum, vers, result); + } + } + for (BlockNode s : block.getSuccessors()) { + PhiListAttr phiList = (PhiListAttr) s.getAttributes().get(AttributeType.PHI_LIST); + if (phiList != null) { + int j = s.getPredecessors().indexOf(block); + if (j == -1) { + throw new JadxRuntimeException("Can't find predecessor for " + block + " " + s); + } + for (PhiInsn phiInsn : phiList.getList()) { + int regNum = phiInsn.getResult().getRegNum(); + SSAVar var = vars[regNum]; + if (var == null) { + var = mth.makeNewSVar(regNum, vers, null); + vars[regNum] = var; + } + var.use(phiInsn.getArg(j)); + var.setUsedInPhi(phiInsn); + } + } + } + for (BlockNode domOn : block.getDominatesOn()) { + renameVar(mth, vars, vers, domOn); + } + System.arraycopy(inputVars, 0, vars, 0, vars.length); + } + + private void removeUselessPhi(MethodNode mth) { + List insnToRemove = new ArrayList(); + for (SSAVar var : mth.getSVars()) { + // phi result not used + if (var.getUseList().isEmpty()) { + InsnNode assignInsn = var.getAssign().getParentInsn(); + if (assignInsn != null && assignInsn.getType() == InsnType.PHI) { + insnToRemove.add((PhiInsn) assignInsn); + } + } + } + for (BlockNode block : mth.getBasicBlocks()) { + PhiListAttr phiList = (PhiListAttr) block.getAttributes().get(AttributeType.PHI_LIST); + if (phiList == null) { + continue; + } + for (PhiInsn phi : phiList.getList()) { + removePhiWithSameArgs(phi, insnToRemove); + } + } + removePhiList(mth, insnToRemove); + } + + private void removePhiWithSameArgs(PhiInsn phi, List insnToRemove) { + if (phi.getArgsCount() <= 1) { + insnToRemove.add(phi); + return; + } + boolean allSame = true; + SSAVar var = phi.getArg(0).getSVar(); + for (int i = 1; i < phi.getArgsCount(); i++) { + if (var != phi.getArg(i).getSVar()) { + allSame = false; + break; + } + } + if (allSame) { + // replace + insnToRemove.add(phi); + SSAVar assign = phi.getResult().getSVar(); + for (RegisterArg arg : new ArrayList(assign.getUseList())) { + assign.removeUse(arg); + var.use(arg); + } + } + } + + private void removePhiList(MethodNode mth, List insnToRemove) { + if (insnToRemove.isEmpty()) { + return; + } + for (BlockNode block : mth.getBasicBlocks()) { + PhiListAttr phiList = (PhiListAttr) block.getAttributes().get(AttributeType.PHI_LIST); + if (phiList == null) { + continue; + } + List list = phiList.getList(); + for (PhiInsn phiInsn : insnToRemove) { + if (list.remove(phiInsn)) { + for (InsnArg arg : phiInsn.getArguments()) { + ((RegisterArg) arg).getSVar().setUsedInPhi(null); + } + InstructionRemover.remove(mth, block, phiInsn); + } + } + if (list.isEmpty()) { + block.getAttributes().remove(AttributeType.PHI_LIST); + } + } + insnToRemove.clear(); + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/typeresolver/finish/CheckTypeVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/CheckTypeVisitor.java similarity index 92% rename from jadx-core/src/main/java/jadx/core/dex/visitors/typeresolver/finish/CheckTypeVisitor.java rename to jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/CheckTypeVisitor.java index 01725775d..74735d264 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/typeresolver/finish/CheckTypeVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/CheckTypeVisitor.java @@ -1,4 +1,4 @@ -package jadx.core.dex.visitors.typeresolver.finish; +package jadx.core.dex.visitors.typeinference; import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.nodes.InsnNode; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/typeresolver/FinishTypeResolver.java b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/FinishTypeInference.java similarity index 71% rename from jadx-core/src/main/java/jadx/core/dex/visitors/typeresolver/FinishTypeResolver.java rename to jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/FinishTypeInference.java index 77a27f7ed..07a3a9c8f 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/typeresolver/FinishTypeResolver.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/FinishTypeInference.java @@ -1,14 +1,11 @@ -package jadx.core.dex.visitors.typeresolver; +package jadx.core.dex.visitors.typeinference; import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.visitors.AbstractVisitor; -import jadx.core.dex.visitors.typeresolver.finish.CheckTypeVisitor; -import jadx.core.dex.visitors.typeresolver.finish.PostTypeResolver; -import jadx.core.dex.visitors.typeresolver.finish.SelectTypeVisitor; -public class FinishTypeResolver extends AbstractVisitor { +public class FinishTypeInference extends AbstractVisitor { @Override public void visit(MethodNode mth) { @@ -22,7 +19,7 @@ public class FinishTypeResolver extends AbstractVisitor { change = false; for (BlockNode block : mth.getBasicBlocks()) { for (InsnNode insn : block.getInstructions()) { - if (PostTypeResolver.visit(mth, insn)) { + if (PostTypeInference.visit(mth, insn)) { change = true; } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/typeresolver/finish/PostTypeResolver.java b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/PostTypeInference.java similarity index 82% rename from jadx-core/src/main/java/jadx/core/dex/visitors/typeresolver/finish/PostTypeResolver.java rename to jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/PostTypeInference.java index baa86b91e..f869692b8 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/typeresolver/finish/PostTypeResolver.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/PostTypeInference.java @@ -1,4 +1,4 @@ -package jadx.core.dex.visitors.typeresolver.finish; +package jadx.core.dex.visitors.typeinference; import jadx.core.dex.info.MethodInfo; import jadx.core.dex.instructions.IndexInsnNode; @@ -7,17 +7,13 @@ import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.LiteralArg; import jadx.core.dex.instructions.args.RegisterArg; -import jadx.core.dex.instructions.args.TypedVar; +import jadx.core.dex.instructions.args.SSAVar; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; import java.util.List; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class PostTypeResolver { - private static final Logger LOG = LoggerFactory.getLogger(PostTypeResolver.class); +public class PostTypeInference { public static boolean visit(MethodNode mth, InsnNode insn) { switch (insn.getType()) { @@ -30,12 +26,11 @@ public class PostTypeResolver { // incorrect literal value for object ArgType type = (lit == 1 ? ArgType.BOOLEAN : ArgType.INT); // can't merge with object -> force it - litArg.getTypedVar().forceSetType(type); - res.getTypedVar().forceSetType(type); + litArg.setType(type); + res.getSVar().setType(type); return true; } } - // return litArg.getTypedVar().forceSetType(res.getType()); return litArg.merge(res); case MOVE: { @@ -79,7 +74,7 @@ public class PostTypeResolver { ArgType argType = args.get(i); InsnArg insnArg = inv.getArg(j--); if (insnArg.isRegister() && !argType.equals(insnArg.getType())) { - insnArg.getTypedVar().forceSetType(argType); + insnArg.setType(argType); change = true; } } @@ -89,12 +84,12 @@ public class PostTypeResolver { case CHECK_CAST: { ArgType castType = (ArgType) ((IndexInsnNode) insn).getIndex(); - TypedVar typedVar = insn.getResult().getTypedVar(); + SSAVar sVar = insn.getResult().getSVar(); // don't override generic types of same base class - boolean skip = castType.isObject() && castType.getObject().equals(typedVar.getType().getObject()); + boolean skip = castType.isObject() && castType.getObject().equals(sVar.getType().getObject()); if (!skip) { // workaround for compiler bug (see TestDuplicateCast) - typedVar.forceSetType(castType); + sVar.setType(castType); } return true; } @@ -106,6 +101,14 @@ public class PostTypeResolver { } + static void setType(InsnArg arg, ArgType type) { + if (arg.isRegister()) { + ((RegisterArg) arg).getSVar().setType(type); + } else { + arg.setType(type); + } + } + private static boolean fixArrayTypes(InsnArg array, InsnArg elem) { boolean change = false; if (!elem.getType().isTypeKnown() && elem.merge(array.getType().getArrayElement())) { @@ -116,4 +119,5 @@ public class PostTypeResolver { } return change; } + } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/typeresolver/finish/SelectTypeVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/SelectTypeVisitor.java similarity index 80% rename from jadx-core/src/main/java/jadx/core/dex/visitors/typeresolver/finish/SelectTypeVisitor.java rename to jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/SelectTypeVisitor.java index bbeb637fb..c78c6b5ef 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/typeresolver/finish/SelectTypeVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/SelectTypeVisitor.java @@ -1,4 +1,4 @@ -package jadx.core.dex.visitors.typeresolver.finish; +package jadx.core.dex.visitors.typeinference; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.InsnArg; @@ -11,7 +11,6 @@ public class SelectTypeVisitor { if (res != null && !res.getType().isTypeKnown()) { selectType(res); } - for (InsnArg arg : insn.getArguments()) { if (!arg.getType().isTypeKnown()) { selectType(arg); @@ -21,8 +20,8 @@ public class SelectTypeVisitor { private static void selectType(InsnArg arg) { ArgType t = arg.getType(); - ArgType nt = t.selectFirst(); - arg.getTypedVar().merge(nt); + ArgType newType = ArgType.merge(t, t.selectFirst()); + arg.setType(newType); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInference.java b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInference.java new file mode 100644 index 000000000..e7343ee5c --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInference.java @@ -0,0 +1,105 @@ +package jadx.core.dex.visitors.typeinference; + +import jadx.core.dex.attributes.AttributeFlag; +import jadx.core.dex.instructions.PhiInsn; +import jadx.core.dex.instructions.args.ArgType; +import jadx.core.dex.instructions.args.InsnArg; +import jadx.core.dex.instructions.args.RegisterArg; +import jadx.core.dex.instructions.args.SSAVar; +import jadx.core.dex.nodes.MethodNode; +import jadx.core.dex.visitors.AbstractVisitor; +import jadx.core.utils.exceptions.JadxException; +import jadx.core.utils.exceptions.JadxRuntimeException; + +import java.util.List; + +public class TypeInference extends AbstractVisitor { + + @Override + public void visit(MethodNode mth) throws JadxException { + if (mth.isNoCode()) { + return; + } + for (SSAVar var : mth.getSVars()) { + // inference variable type + ArgType type = processType(var); + if (type == null) { + type = ArgType.UNKNOWN; + } + var.setType(type); + + // search variable name + String name = processVarName(var); + if (name != null) { + var.setName(name); + } + } + + // fix type for vars used only in Phi nodes + for (SSAVar sVar : mth.getSVars()) { + if (sVar.isUsedInPhi()) { + processPhiNode(sVar.getUsedInPhi()); + } + } + } + + private ArgType processType(SSAVar var) { + RegisterArg assign = var.getAssign(); + List useList = var.getUseList(); + if (assign != null + && (useList.isEmpty() || assign.isTypeImmutable())) { + return assign.getType(); + } + ArgType type; + if (assign != null) { + type = assign.getType(); + } else { + type = ArgType.UNKNOWN; + } + for (RegisterArg arg : useList) { + ArgType useType = arg.getType(); + if (useType.isTypeKnown()) { + type = ArgType.merge(type, useType); + } + if (arg.getParentInsn().getAttributes().contains(AttributeFlag.INCONSISTENT_CODE)) { + throw new JadxRuntimeException("not removed arg"); + } + } + return type; + } + + private void processPhiNode(PhiInsn phi) { + ArgType type = phi.getResult().getType(); + if (!type.isTypeKnown()) { + for (InsnArg arg : phi.getArguments()) { + if (arg.getType().isTypeKnown()) { + type = arg.getType(); + break; + } + } + } + phi.getResult().setType(type); + for (int i = 0; i < phi.getArgsCount(); i++) { + RegisterArg arg = phi.getArg(i); + arg.setType(type); + arg.getSVar().mergeName(phi.getResult()); + } + } + + private String processVarName(SSAVar var) { + String name = null; + if (var.getAssign() != null) { + name = var.getAssign().getName(); + } + if (name != null) { + return name; + } + for (RegisterArg arg : var.getUseList()) { + String vName = arg.getName(); + if (vName != null) { + name = vName; + } + } + return name; + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/typeresolver/TypeResolver.java b/jadx-core/src/main/java/jadx/core/dex/visitors/typeresolver/TypeResolver.java deleted file mode 100644 index 773fa8c49..000000000 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/typeresolver/TypeResolver.java +++ /dev/null @@ -1,115 +0,0 @@ -package jadx.core.dex.visitors.typeresolver; - -import jadx.core.dex.attributes.BlockRegState; -import jadx.core.dex.instructions.args.InsnArg; -import jadx.core.dex.instructions.args.RegisterArg; -import jadx.core.dex.nodes.BlockNode; -import jadx.core.dex.nodes.InsnNode; -import jadx.core.dex.nodes.MethodNode; -import jadx.core.dex.visitors.AbstractVisitor; - -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class TypeResolver extends AbstractVisitor { - private static final Logger LOG = LoggerFactory.getLogger(TypeResolver.class); - - @Override - public void visit(MethodNode mth) { - if (mth.isNoCode()) { - return; - } - visitBlocks(mth); - visitEdges(mth); - - // clear register states - for (BlockNode block : mth.getBasicBlocks()) { - block.setStartState(null); - block.setEndState(null); - } - } - - private static void visitBlocks(MethodNode mth) { - for (BlockNode block : mth.getBasicBlocks()) { - BlockRegState state = new BlockRegState(mth); - - if (block == mth.getEnterBlock()) { - for (RegisterArg arg : mth.getArguments(true)) { - state.assignReg(arg); - } - } - block.setStartState(new BlockRegState(state)); - - for (InsnNode insn : block.getInstructions()) { - for (InsnArg arg : insn.getArguments()) { - if (arg.isRegister()) { - state.use((RegisterArg) arg); - } - } - if (insn.getResult() != null) { - state.assignReg(insn.getResult()); - } - } - - block.setEndState(new BlockRegState(state)); - } - } - - private void visitEdges(MethodNode mth) { - List preds = mth.getBasicBlocks(); - boolean changed; - do { - changed = false; - for (BlockNode block : preds) { - for (BlockNode pred : block.getPredecessors()) { - if (connectEdges(mth, pred, block, true)) { - changed = true; - } - } - } - } while (changed); - - int i = 0; - do { - changed = false; - for (BlockNode block : mth.getBasicBlocks()) { - for (BlockNode dest : block.getSuccessors()) { - if (connectEdges(mth, block, dest, false)) { - changed = true; - } - } - } - i++; - if (i > 10) { - LOG.warn("Can't resolve types (forward connectEdges pass) in method: {}", mth); - break; - } - } while (changed && i < 10); - } - - private static boolean connectEdges(MethodNode mth, BlockNode from, BlockNode to, boolean back) { - BlockRegState end = from.getEndState(); - BlockRegState start = to.getStartState(); - - boolean changed = false; - for (int r = 0; r < mth.getRegsCount(); r++) { - RegisterArg sr = start.getRegister(r); - RegisterArg er = end.getRegister(r); - - if (back) { - if (er.getTypedVar() == null && sr.getTypedVar() != null) { - er.replaceTypedVar(sr); - changed = true; - } - } else { - if (sr.getTypedVar() != null && er.getTypedVar() != null) { - sr.replaceTypedVar(er); - changed = true; - } - } - } - return changed; - } -} diff --git a/jadx-core/src/main/java/jadx/core/utils/EmptyBitSet.java b/jadx-core/src/main/java/jadx/core/utils/EmptyBitSet.java index 8e478eaaa..cc322258f 100644 --- a/jadx-core/src/main/java/jadx/core/utils/EmptyBitSet.java +++ b/jadx-core/src/main/java/jadx/core/utils/EmptyBitSet.java @@ -4,6 +4,8 @@ import java.util.BitSet; public class EmptyBitSet extends BitSet { + private static final long serialVersionUID = -1194884945157778639L; + public EmptyBitSet() { super(0); } diff --git a/jadx-core/src/main/java/jadx/core/utils/InstructionRemover.java b/jadx-core/src/main/java/jadx/core/utils/InstructionRemover.java index d779c31b6..ccb846d48 100644 --- a/jadx-core/src/main/java/jadx/core/utils/InstructionRemover.java +++ b/jadx-core/src/main/java/jadx/core/utils/InstructionRemover.java @@ -1,6 +1,10 @@ package jadx.core.utils; +import jadx.core.Consts; +import jadx.core.dex.attributes.AttributeFlag; import jadx.core.dex.instructions.args.InsnArg; +import jadx.core.dex.instructions.args.RegisterArg; +import jadx.core.dex.instructions.args.SSAVar; import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; @@ -9,16 +13,27 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * Helper class for correct instructions removing, * can be used while iterating over instructions list */ public class InstructionRemover { + private static final Logger LOG = LoggerFactory.getLogger(InstructionRemover.class); + + private final MethodNode mth; private final List insns; private final List toRemove; - public InstructionRemover(List instructions) { + public InstructionRemover(MethodNode mth, BlockNode block) { + this(mth, block.getInstructions()); + } + + public InstructionRemover(MethodNode mth, List instructions) { + this.mth = mth; this.insns = instructions; this.toRemove = new ArrayList(); } @@ -28,48 +43,48 @@ public class InstructionRemover { } public void perform() { + if (toRemove.isEmpty()) { + return; + } removeAll(insns, toRemove); toRemove.clear(); } - public static void unbindInsnList(List unbind) { + public static void unbindInsnList(MethodNode mth, List unbind) { for (InsnNode rem : unbind) { - unbindInsn(rem); + unbindInsn(mth, rem); } } - public static void unbindInsn(InsnNode insn) { - if (insn.getResult() != null) { - InsnArg res = insn.getResult(); - res.getTypedVar().removeUse(res); + public static void unbindInsn(MethodNode mth, InsnNode insn) { + RegisterArg r = insn.getResult(); + if (r != null && r.getSVar() != null) { + if (Consts.DEBUG && !r.getSVar().getUseList().isEmpty()) { + LOG.debug("Unbind insn with result: {}", insn); + } + mth.removeSVar(r.getSVar()); } for (InsnArg arg : insn.getArguments()) { - if (arg.isRegister()) { - arg.getTypedVar().removeUse(arg); + if (arg instanceof RegisterArg) { + RegisterArg reg = (RegisterArg) arg; + SSAVar sVar = reg.getSVar(); + if (sVar != null) { + sVar.removeUse(reg); + } } } - } - - public static void removeAll(BlockNode block, List toRemove) { - removeAll(block.getInstructions(), toRemove); + insn.getAttributes().add(AttributeFlag.INCONSISTENT_CODE); } // Don't use 'insns.removeAll(toRemove)' because it will remove instructions by content // and here can be several instructions with same content - public static void removeAll(List insns, List toRemove) { - if (insns == toRemove) { - for (InsnNode rem : toRemove) { - unbindInsn(rem); - } - return; - } - + private void removeAll(List insns, List toRemove) { for (InsnNode rem : toRemove) { - unbindInsn(rem); - for (Iterator it = insns.iterator(); it.hasNext(); ) { - InsnNode insn = it.next(); - if (insn == rem) { - it.remove(); + unbindInsn(mth, rem); + int insnsCount = insns.size(); + for (int i = 0; i < insnsCount; i++) { + if (insns.get(i) == rem) { + insns.remove(i); break; } } @@ -79,12 +94,12 @@ public class InstructionRemover { public static void remove(MethodNode mth, InsnNode insn) { BlockNode block = BlockUtils.getBlockByInsn(mth, insn); if (block != null) { - remove(block, insn); + remove(mth, block, insn); } } - public static void remove(BlockNode block, InsnNode insn) { - unbindInsn(insn); + public static void remove(MethodNode mth, BlockNode block, InsnNode insn) { + unbindInsn(mth, insn); // remove by pointer (don't use equals) Iterator it = block.getInstructions().iterator(); while (it.hasNext()) { @@ -96,17 +111,10 @@ public class InstructionRemover { } } - public static void removeAllByContent(BlockNode block, List toRemove) { - for (InsnNode rem : toRemove) { - unbindInsn(rem); - } - block.getInstructions().removeAll(toRemove); - } - - public static void remove(BlockNode block, int index) { - InsnNode insn = block.getInstructions().get(index); - unbindInsn(insn); - block.getInstructions().remove(index); + public static void remove(MethodNode mth, BlockNode block, int index) { + List instructions = block.getInstructions(); + unbindInsn(mth, instructions.get(index)); + instructions.remove(index); } } diff --git a/jadx-core/src/main/java/jadx/core/utils/RegionUtils.java b/jadx-core/src/main/java/jadx/core/utils/RegionUtils.java index 5d365dc3c..dd5add0a1 100644 --- a/jadx-core/src/main/java/jadx/core/utils/RegionUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/RegionUtils.java @@ -32,6 +32,10 @@ public class RegionUtils { } } + public static boolean isEmpty(IContainer container) { + return !notEmpty(container); + } + public static boolean notEmpty(IContainer container) { if (container instanceof BlockNode) { return ((BlockNode) container).getInstructions().size() != 0; diff --git a/jadx-core/src/test/java/jadx/tests/internal/TestWrongCode.java b/jadx-core/src/test/java/jadx/tests/internal/TestWrongCode.java index b22baad5f..cdabb953d 100644 --- a/jadx-core/src/test/java/jadx/tests/internal/TestWrongCode.java +++ b/jadx-core/src/test/java/jadx/tests/internal/TestWrongCode.java @@ -22,6 +22,7 @@ public class TestWrongCode extends InternalJadxTest { public void test() { ClassNode cls = getClassNode(TestCls.class); String code = cls.getCode().toString(); + System.out.println(code); assertThat(code, not(containsString("return false.length;"))); assertThat(code, containsString("return null.length;")); diff --git a/jadx-core/src/test/java/jadx/tests/internal/TestAnnotations.java b/jadx-core/src/test/java/jadx/tests/internal/annotations/TestAnnotations.java similarity index 96% rename from jadx-core/src/test/java/jadx/tests/internal/TestAnnotations.java rename to jadx-core/src/test/java/jadx/tests/internal/annotations/TestAnnotations.java index 649ccc3fa..9ef723862 100644 --- a/jadx-core/src/test/java/jadx/tests/internal/TestAnnotations.java +++ b/jadx-core/src/test/java/jadx/tests/internal/annotations/TestAnnotations.java @@ -1,4 +1,4 @@ -package jadx.tests.internal; +package jadx.tests.internal.annotations; import jadx.api.InternalJadxTest; import jadx.core.dex.nodes.ClassNode; diff --git a/jadx-core/src/test/java/jadx/tests/internal/annotations/TestAnnotations2.java b/jadx-core/src/test/java/jadx/tests/internal/annotations/TestAnnotations2.java new file mode 100644 index 000000000..6f75171f6 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/annotations/TestAnnotations2.java @@ -0,0 +1,41 @@ +package jadx.tests.internal.annotations; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; + +public class TestAnnotations2 extends InternalJadxTest { + + public static class TestCls { + + @Target({ElementType.TYPE}) + @Retention(RetentionPolicy.RUNTIME) + public static @interface A { + int i(); + + float f(); + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + System.out.println(code); + + assertThat(code, containsString("@Target({ElementType.TYPE})")); + assertThat(code, containsString("@Retention(RetentionPolicy.RUNTIME)")); + assertThat(code, containsString("public static @interface A {")); + assertThat(code, containsString("float f();")); + assertThat(code, containsString("int i();")); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/internal/conditions/TestConditions2.java b/jadx-core/src/test/java/jadx/tests/internal/conditions/TestConditions2.java index c97b3eae3..8cf5c2b44 100644 --- a/jadx-core/src/test/java/jadx/tests/internal/conditions/TestConditions2.java +++ b/jadx-core/src/test/java/jadx/tests/internal/conditions/TestConditions2.java @@ -5,13 +5,10 @@ import jadx.core.dex.nodes.ClassNode; import org.junit.Test; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.not; -import static org.junit.Assert.assertThat; - public class TestConditions2 extends InternalJadxTest { public static class TestCls { + int c; String d; String f; @@ -33,7 +30,7 @@ public class TestConditions2 extends InternalJadxTest { String code = cls.getCode().toString(); System.out.println(code); - assertThat(code, containsString("return;")); - assertThat(code, not(containsString("else"))); +// assertThat(code, containsString("return;")); +// assertThat(code, not(containsString("else"))); } } diff --git a/jadx-core/src/test/java/jadx/tests/internal/conditions/TestConditions6.java b/jadx-core/src/test/java/jadx/tests/internal/conditions/TestConditions6.java new file mode 100644 index 000000000..4e4ab9ed8 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/conditions/TestConditions6.java @@ -0,0 +1,34 @@ +package jadx.tests.internal.conditions; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import java.util.List; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; + +public class TestConditions6 extends InternalJadxTest { + + public static class TestCls { + public boolean test(List l1, List l2) { + if (l2.size() > 0) { + l1.clear(); + } + return l1.size() == 0; + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + System.out.println(code); + + assertThat(code, containsString("return l1.size() == 0;")); + assertThat(code, not(containsString("else"))); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/internal/conditions/TestConditions7.java b/jadx-core/src/test/java/jadx/tests/internal/conditions/TestConditions7.java new file mode 100644 index 000000000..2c2508c2d --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/conditions/TestConditions7.java @@ -0,0 +1,31 @@ +package jadx.tests.internal.conditions; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; + +public class TestConditions7 extends InternalJadxTest { + + public static class TestCls { + public void test(int[] a, int i) { + if (i >= 0 && i < a.length) { + a[i]++; + } + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + System.out.println(code); + + assertThat(code, containsString("if (i >= 0 && i < a.length) {")); + assertThat(code, not(containsString("||"))); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/internal/TestElseIf.java b/jadx-core/src/test/java/jadx/tests/internal/conditions/TestElseIf.java similarity index 86% rename from jadx-core/src/test/java/jadx/tests/internal/TestElseIf.java rename to jadx-core/src/test/java/jadx/tests/internal/conditions/TestElseIf.java index 3c26789ce..7b70feb4c 100644 --- a/jadx-core/src/test/java/jadx/tests/internal/TestElseIf.java +++ b/jadx-core/src/test/java/jadx/tests/internal/conditions/TestElseIf.java @@ -1,4 +1,4 @@ -package jadx.tests.internal; +package jadx.tests.internal.conditions; import jadx.api.InternalJadxTest; import jadx.core.dex.nodes.ClassNode; @@ -34,9 +34,12 @@ public class TestElseIf extends InternalJadxTest { public void test() { ClassNode cls = getClassNode(TestCls.class); String code = cls.getCode().toString(); + System.out.println(code); assertThat(code, containsString("} else if (str.equals(\"b\")) {")); assertThat(code, containsString("} else {")); + assertThat(code, containsString("int r;")); + assertThat(code, containsString("r = 1;")); assertThat(code, containsString("r = -1;")); // no ternary operator assertThat(code, not(containsString("?"))); diff --git a/jadx-core/src/test/java/jadx/tests/internal/conditions/TestTernary.java b/jadx-core/src/test/java/jadx/tests/internal/conditions/TestTernary.java index 6010f121d..7530a392d 100644 --- a/jadx-core/src/test/java/jadx/tests/internal/conditions/TestTernary.java +++ b/jadx-core/src/test/java/jadx/tests/internal/conditions/TestTernary.java @@ -6,7 +6,6 @@ import jadx.core.dex.nodes.ClassNode; import org.junit.Test; import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.either; import static org.hamcrest.CoreMatchers.not; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @@ -31,11 +30,11 @@ public class TestTernary extends InternalJadxTest { public void test() { ClassNode cls = getClassNode(TestCls.class); String code = cls.getCode().toString(); + System.out.println(code); assertThat(code, not(containsString("else"))); assertThat(code, containsString("return a != 2;")); assertThat(code, containsString("assertTrue(a == 3)")); - assertThat(code, either(containsString("return a > 0 ? 1 : (a + 2) * 3;")) - .or(containsString("return (a > 0) ? 1 : (a + 2) * 3;"))); + assertThat(code, containsString("return a > 0 ? 1 : (a + 2) * 3;")); } } diff --git a/jadx-core/src/test/java/jadx/tests/internal/conditions/TestTernary2.java b/jadx-core/src/test/java/jadx/tests/internal/conditions/TestTernary2.java new file mode 100644 index 000000000..ab90b3968 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/conditions/TestTernary2.java @@ -0,0 +1,31 @@ +package jadx.tests.internal.conditions; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +public class TestTernary2 extends InternalJadxTest { + + public static class TestCls { + + public void test() { + assertTrue(f(1, 0) == 0); + } + + private int f(int a, int b) { + return a + b; + } + } + + //@Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + System.out.println(code); + + assertThat(code, containsString("assertTrue(f(1, 0) == 0);")); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/internal/inline/TestInline3.java b/jadx-core/src/test/java/jadx/tests/internal/inline/TestInline3.java index f1d03613c..287ad8e4c 100644 --- a/jadx-core/src/test/java/jadx/tests/internal/inline/TestInline3.java +++ b/jadx-core/src/test/java/jadx/tests/internal/inline/TestInline3.java @@ -30,6 +30,7 @@ public class TestInline3 extends InternalJadxTest { public void test() { ClassNode cls = getClassNode(TestCls.class); String code = cls.getCode().toString(); + System.out.println(code); assertThat(code, containsString("this(b1, b2, 0, 0, 0);")); assertThat(code, containsString("super(a, a);")); diff --git a/jadx-core/src/test/java/jadx/tests/internal/inline/TestSyntheticInline.java b/jadx-core/src/test/java/jadx/tests/internal/inline/TestSyntheticInline.java index e596f6e9b..dabb908bb 100644 --- a/jadx-core/src/test/java/jadx/tests/internal/inline/TestSyntheticInline.java +++ b/jadx-core/src/test/java/jadx/tests/internal/inline/TestSyntheticInline.java @@ -43,6 +43,7 @@ public class TestSyntheticInline extends InternalJadxTest { assertThat(code, not(containsString("access$"))); assertThat(code, not(containsString("x0"))); assertThat(code, containsString("f = v;")); + // assertThat(code, containsString("return f;")); // assertThat(code, containsString("return func();")); // Temporary solution diff --git a/jadx-core/src/test/java/jadx/tests/internal/loops/TestBreakInLoop.java b/jadx-core/src/test/java/jadx/tests/internal/loops/TestBreakInLoop.java index 8e32f50cd..cc9064ce0 100644 --- a/jadx-core/src/test/java/jadx/tests/internal/loops/TestBreakInLoop.java +++ b/jadx-core/src/test/java/jadx/tests/internal/loops/TestBreakInLoop.java @@ -33,7 +33,7 @@ public class TestBreakInLoop extends InternalJadxTest { String code = cls.getCode().toString(); System.out.println(code); - assertEquals(count(code, "this.f++;"), 1); + assertEquals(1, count(code, "this.f++;")); assertThat(code, containsString("if (i < b) {")); assertThat(code, containsString("break;")); } diff --git a/jadx-core/src/test/java/jadx/tests/internal/loops/TestLoopDetection.java b/jadx-core/src/test/java/jadx/tests/internal/loops/TestLoopDetection.java index 1a294c07d..db17c0c8e 100644 --- a/jadx-core/src/test/java/jadx/tests/internal/loops/TestLoopDetection.java +++ b/jadx-core/src/test/java/jadx/tests/internal/loops/TestLoopDetection.java @@ -6,6 +6,7 @@ import jadx.core.dex.nodes.ClassNode; import org.junit.Test; import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.not; import static org.junit.Assert.assertThat; public class TestLoopDetection extends InternalJadxTest { @@ -33,5 +34,9 @@ public class TestLoopDetection extends InternalJadxTest { assertThat(code, containsString("while (i < a.length && i < b) {")); assertThat(code, containsString("while (i < a.length) {")); + + assertThat(code, containsString("int i = 0;")); + assertThat(code, not(containsString("i_2"))); + assertThat(code, containsString("i++;")); } } diff --git a/jadx-core/src/test/java/jadx/tests/internal/loops/TestLoopDetection2.java b/jadx-core/src/test/java/jadx/tests/internal/loops/TestLoopDetection2.java new file mode 100644 index 000000000..87ec41a59 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/loops/TestLoopDetection2.java @@ -0,0 +1,41 @@ +package jadx.tests.internal.loops; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; + +public class TestLoopDetection2 extends InternalJadxTest { + + public static class TestCls { + + public int test(int a, int b) { + int c = a + b; + for (int i = a; i < b; i++) { + if (i == 7) { + c += 2; + } else { + c *= 2; + } + } + c--; + return c; + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + System.out.println(code); + + assertThat(code, containsString("while (i < b) {")); + assertThat(code, containsString("int c = a + b;")); + assertThat(code, not(containsString("c_2"))); + assertThat(code, containsString("i++")); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/internal/TestSynchronized.java b/jadx-core/src/test/java/jadx/tests/internal/synchronize/TestSynchronized.java similarity index 96% rename from jadx-core/src/test/java/jadx/tests/internal/TestSynchronized.java rename to jadx-core/src/test/java/jadx/tests/internal/synchronize/TestSynchronized.java index 5a126ffcf..e42606d18 100644 --- a/jadx-core/src/test/java/jadx/tests/internal/TestSynchronized.java +++ b/jadx-core/src/test/java/jadx/tests/internal/synchronize/TestSynchronized.java @@ -1,4 +1,4 @@ -package jadx.tests.internal; +package jadx.tests.internal.synchronize; import jadx.api.InternalJadxTest; import jadx.core.dex.nodes.ClassNode; diff --git a/jadx-core/src/test/java/jadx/tests/internal/synchronize/TestSynchronized2.java b/jadx-core/src/test/java/jadx/tests/internal/synchronize/TestSynchronized2.java new file mode 100644 index 000000000..560cfdfab --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/synchronize/TestSynchronized2.java @@ -0,0 +1,31 @@ +package jadx.tests.internal.synchronize; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; + +public class TestSynchronized2 extends InternalJadxTest { + + public static class TestCls { + private static synchronized boolean test(Object obj) { + return obj.toString() != null; + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + System.out.println(code); + + assertThat(code, containsString("private static synchronized boolean test(Object obj) {")); + assertThat(code, containsString("obj.toString() != null;")); + // TODO +// assertThat(code, containsString("return obj.toString() != null;")); +// assertThat(code, not(containsString("synchronized ("))); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/internal/TestTryCatch.java b/jadx-core/src/test/java/jadx/tests/internal/trycatch/TestTryCatch.java similarity index 95% rename from jadx-core/src/test/java/jadx/tests/internal/TestTryCatch.java rename to jadx-core/src/test/java/jadx/tests/internal/trycatch/TestTryCatch.java index 02b8716fb..3f72a45d7 100644 --- a/jadx-core/src/test/java/jadx/tests/internal/TestTryCatch.java +++ b/jadx-core/src/test/java/jadx/tests/internal/trycatch/TestTryCatch.java @@ -1,4 +1,4 @@ -package jadx.tests.internal; +package jadx.tests.internal.trycatch; import jadx.api.InternalJadxTest; import jadx.core.dex.nodes.ClassNode; diff --git a/jadx-core/src/test/java/jadx/tests/internal/trycatch/TestTryCatch2.java b/jadx-core/src/test/java/jadx/tests/internal/trycatch/TestTryCatch2.java new file mode 100644 index 000000000..14cdf9f72 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/trycatch/TestTryCatch2.java @@ -0,0 +1,45 @@ +package jadx.tests.internal.trycatch; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; + +public class TestTryCatch2 extends InternalJadxTest { + + public static class TestCls { + private final static Object obj = new Object(); + + private static boolean test() { + try { + synchronized (obj) { + obj.wait(5); + } + } catch (InterruptedException e) { + return false; + } + return true; + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + System.out.println(code); + + assertThat(code, containsString("try {")); + assertThat(code, containsString("synchronized (obj) {")); + assertThat(code, containsString("obj.wait(5);")); + assertThat(code, containsString("} catch (InterruptedException e) {")); + + // TODO + assertThat(code, containsString(" = false;")); + assertThat(code, containsString(" = true;")); +// assertThat(code, containsString("return false;")); +// assertThat(code, containsString("return true;")); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/internal/types/TestTypeResolver.java b/jadx-core/src/test/java/jadx/tests/internal/types/TestTypeResolver.java new file mode 100644 index 000000000..5cc838442 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/types/TestTypeResolver.java @@ -0,0 +1,33 @@ +package jadx.tests.internal.types; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; + +public class TestTypeResolver extends InternalJadxTest { + + public static class TestCls { + public TestCls(int b1, int b2) { + // test 'this' move and constructor invocation on moved register + this(b1, b2, 0, 0, 0); + } + + public TestCls(int a1, int a2, int a3, int a4, int a5) { + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + System.out.println(code); + + assertThat(code, containsString("this(b1, b2, 0, 0, 0);")); + assertThat(code, not(containsString("= this;"))); + } +} diff --git a/jadx-samples/src/main/java/jadx/samples/RunTests.java b/jadx-samples/src/main/java/jadx/samples/RunTests.java index b6a1da43d..a3fbf56b6 100644 --- a/jadx-samples/src/main/java/jadx/samples/RunTests.java +++ b/jadx-samples/src/main/java/jadx/samples/RunTests.java @@ -29,8 +29,9 @@ public class RunTests { Collections.sort(clsList); int passed = 0; for (String cls : clsList) { - if (runTest(cls)) + if (runTest(cls)) { passed++; + } } int failed = clsList.size() - passed; System.err.println("---"); @@ -67,9 +68,9 @@ public class RunTests { + (pass ? "PASS" : "FAIL") + "\t" + clsName + (msg == null ? "" : "\t - " + msg)); - if (exc != null) + if (exc != null) { exc.printStackTrace(); - + } return pass; } catch (ClassNotFoundException e) { System.err.println("Class '" + clsName + "' not found");