core: use SSA representation for instruction arguments
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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<Flags> 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<Flags> state) throws CodegenException {
|
||||
private void makeArithOneArg(ArithNode insn, CodeWriter code) throws CodegenException {
|
||||
ArithOp op = insn.getOp();
|
||||
InsnArg arg = insn.getArg(0);
|
||||
// "++" or "--"
|
||||
|
||||
@@ -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<IAttribute> catchAttrs = attrs.getAll(AttributeType.CATCH_BLOCK);
|
||||
for (IAttribute catchAttr : catchAttrs) {
|
||||
code.add("\t " + catchAttr);
|
||||
}
|
||||
}
|
||||
} catch (CodegenException e) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ public enum AttributeType {
|
||||
ANNOTATION_LIST(true),
|
||||
ANNOTATION_MTH_PARAMETERS(true),
|
||||
|
||||
PHI_LIST(true),
|
||||
|
||||
SOURCE_FILE(true),
|
||||
|
||||
// for regions
|
||||
|
||||
@@ -118,15 +118,14 @@ public final class AttributesList {
|
||||
int count = getMultiCountInternal(type);
|
||||
if (count == 0) {
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
List<IAttribute> attrs = new ArrayList<IAttribute>(count);
|
||||
for (IAttribute attr : attributes) {
|
||||
if (attr.getType() == type) {
|
||||
attrs.add(attr);
|
||||
}
|
||||
}
|
||||
return attrs;
|
||||
}
|
||||
List<IAttribute> attrs = new ArrayList<IAttribute>(count);
|
||||
for (IAttribute attr : attributes) {
|
||||
if (attr.getType() == type) {
|
||||
attrs.add(attr);
|
||||
}
|
||||
}
|
||||
return attrs;
|
||||
}
|
||||
|
||||
public void remove(AttributeType type) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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<PhiInsn> list = new LinkedList<PhiInsn>();
|
||||
|
||||
@Override
|
||||
public AttributeType getType() {
|
||||
return AttributeType.PHI_LIST;
|
||||
}
|
||||
|
||||
public List<PhiInsn> 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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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 + ")";
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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 + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package jadx.core.dex.instructions.args;
|
||||
|
||||
public interface Named {
|
||||
|
||||
String getName();
|
||||
|
||||
void setName(String name);
|
||||
}
|
||||
@@ -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 + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<RegisterArg> useList = new ArrayList<RegisterArg>(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<RegisterArg> 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;
|
||||
}
|
||||
}
|
||||
@@ -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<InsnArg> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<InsnArg> useList = new ArrayList<InsnArg>(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<InsnArg> getUseList() {
|
||||
return useList;
|
||||
}
|
||||
|
||||
public void removeUse(InsnArg arg) {
|
||||
Iterator<InsnArg> 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();
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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<BlockNode> dominatesOn = new ArrayList<BlockNode>(1);
|
||||
|
||||
private BlockRegState startState;
|
||||
private BlockRegState endState;
|
||||
private List<BlockNode> 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<BlockNode>();
|
||||
}
|
||||
dominatesOn.add(block);
|
||||
}
|
||||
|
||||
public boolean isSynthetic() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<RegisterArg> argsList;
|
||||
private List<SSAVar> sVars = Collections.emptyList();
|
||||
private Map<ArgType, List<ArgType>> genericMap;
|
||||
|
||||
private List<BlockNode> 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<RegisterArg>(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<SSAVar>();
|
||||
}
|
||||
sVars.add(var);
|
||||
return var;
|
||||
}
|
||||
|
||||
public void removeSVar(SSAVar var) {
|
||||
sVars.remove(var);
|
||||
}
|
||||
|
||||
public List<SSAVar> getSVars() {
|
||||
return sVars;
|
||||
}
|
||||
|
||||
public AccessInfo getAccessFlags() {
|
||||
return accFlags;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<InsnNode> finalBlockInsns = new ArrayList<InsnNode>(insns);
|
||||
setFinalBlock(new InsnContainer(finalBlockInsns));
|
||||
|
||||
InstructionRemover.unbindInsnList(finalBlockInsns);
|
||||
InstructionRemover.unbindInsnList(mth, finalBlockInsns);
|
||||
|
||||
// remove these instructions from other handlers
|
||||
for (ExceptionHandler h : getHandlers()) {
|
||||
|
||||
@@ -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<IAttribute> 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<IAttribute> 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<Integer, BlockNode> blocksMap) {
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
for (InsnNode insn : block.getInstructions()) {
|
||||
List<IAttribute> 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<IAttribute> pJumps = prevInsn.getAttributes().getAll(AttributeType.JUMP);
|
||||
for (IAttribute j : pJumps) {
|
||||
JumpAttribute jump = (JumpAttribute) j;
|
||||
if (jump.getSrc() == prevInsn.getOffset()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
List<IAttribute> 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<Integer, BlockNode> 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<BlockNode> 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<BlockNode> 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<InsnArg> useList = arg.getTypedVar().getUseList();
|
||||
if (useList.size() > 1) {
|
||||
List<RegisterArg> 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) {
|
||||
|
||||
@@ -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<RegisterArg> 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<RegisterArg> args = argsInfo.getArgs();
|
||||
for (ListIterator<RegisterArg> it = args.listIterator(args.size()); it.hasPrevious(); ) {
|
||||
RegisterArg arg = it.previous();
|
||||
List<InsnArg> 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<RegisterArg> args = ArgsInfo.getArgs(assignInsn);
|
||||
List<RegisterArg> 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<InsnArg> list, RegisterArg insn) {
|
||||
InsnArg first = list.get(0);
|
||||
return insn == first ? list.get(1) : first;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<InsnArg> use = insn.getResult().getTypedVar().getUseList();
|
||||
private static boolean replaceConst(MethodNode mth, InsnNode insn, long literal) {
|
||||
List<RegisterArg> use = new ArrayList<RegisterArg>(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<BlockNode> 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) {
|
||||
|
||||
@@ -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]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<InsnNode> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<InsnNode> 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 <op>= 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<InsnNode> 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 <op>= 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<InsnNode> flattenInsnChain(InsnNode insn) {
|
||||
List<InsnNode> chain = new ArrayList<InsnNode>();
|
||||
InsnArg i = insn.getArg(0);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<RegisterArg, Usage> usageMap = new LinkedHashMap<RegisterArg, Usage>();
|
||||
final Map<Variable, Usage> usageMap = new LinkedHashMap<Variable, Usage>();
|
||||
|
||||
// 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<RegisterArg> mthArgs = mth.getArguments(true);
|
||||
for (RegisterArg arg : mthArgs) {
|
||||
usageMap.remove(arg);
|
||||
usageMap.remove(new Variable(arg));
|
||||
}
|
||||
|
||||
for (Iterator<Entry<RegisterArg, Usage>> it = usageMap.entrySet().iterator(); it.hasNext(); ) {
|
||||
Entry<RegisterArg, Usage> entry = it.next();
|
||||
for (Iterator<Entry<Variable, Usage>> it = usageMap.entrySet().iterator(); it.hasNext(); ) {
|
||||
Entry<Variable, Usage> entry = it.next();
|
||||
Usage u = entry.getValue();
|
||||
|
||||
// if no assigns => remove
|
||||
@@ -134,7 +159,7 @@ public class ProcessVariables extends AbstractVisitor {
|
||||
}
|
||||
|
||||
// apply
|
||||
for (Entry<RegisterArg, Usage> entry : usageMap.entrySet()) {
|
||||
for (Entry<Variable, Usage> 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<Variable, Usage> 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) {
|
||||
|
||||
@@ -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<InsnArg> useList = res.getTypedVar().getUseList();
|
||||
if (useList.size() != 2) {
|
||||
List<RegisterArg> 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));
|
||||
|
||||
@@ -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<InsnArg> useList = ternInsn.getResult().getTypedVar().getUseList();
|
||||
useList.remove(t.getResult());
|
||||
useList.remove(e.getResult());
|
||||
useList.add(ternInsn.getResult());
|
||||
|
||||
// shrink method again
|
||||
CodeShrinker.shrinkMethod(mth);
|
||||
return;
|
||||
|
||||
@@ -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<SSAVar> vars = mth.getSVars();
|
||||
// int varsSize = vars.size();
|
||||
// Deque<SSAVar> workList = new LinkedList<SSAVar>();
|
||||
// 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<PhiInsn> list = phiList.getList();
|
||||
for (PhiInsn phiInsn : list) {
|
||||
for (Iterator<InsnNode> iterator = block.getInstructions().iterator(); iterator.hasNext(); ) {
|
||||
InsnNode insn = iterator.next();
|
||||
if (insn == phiInsn) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<BlockNode> 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<BlockNode> 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;
|
||||
}
|
||||
}
|
||||
@@ -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<BlockNode> blocks = mth.getBasicBlocks();
|
||||
int blocksCount = blocks.size();
|
||||
BitSet hasPhi = new BitSet(blocksCount);
|
||||
BitSet processed = new BitSet(blocksCount);
|
||||
Deque<BlockNode> workList = new LinkedList<BlockNode>();
|
||||
|
||||
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<PhiInsn> insnToRemove = new ArrayList<PhiInsn>();
|
||||
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<PhiInsn> 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<RegisterArg>(assign.getUseList())) {
|
||||
assign.removeUse(arg);
|
||||
var.use(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removePhiList(MethodNode mth, List<PhiInsn> insnToRemove) {
|
||||
if (insnToRemove.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
PhiListAttr phiList = (PhiListAttr) block.getAttributes().get(AttributeType.PHI_LIST);
|
||||
if (phiList == null) {
|
||||
continue;
|
||||
}
|
||||
List<PhiInsn> 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();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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;
|
||||
+3
-6
@@ -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;
|
||||
}
|
||||
}
|
||||
+18
-14
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+3
-4
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<RegisterArg> 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;
|
||||
}
|
||||
}
|
||||
@@ -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<BlockNode> 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;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import java.util.BitSet;
|
||||
|
||||
public class EmptyBitSet extends BitSet {
|
||||
|
||||
private static final long serialVersionUID = -1194884945157778639L;
|
||||
|
||||
public EmptyBitSet() {
|
||||
super(0);
|
||||
}
|
||||
|
||||
@@ -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<InsnNode> insns;
|
||||
private final List<InsnNode> toRemove;
|
||||
|
||||
public InstructionRemover(List<InsnNode> instructions) {
|
||||
public InstructionRemover(MethodNode mth, BlockNode block) {
|
||||
this(mth, block.getInstructions());
|
||||
}
|
||||
|
||||
public InstructionRemover(MethodNode mth, List<InsnNode> instructions) {
|
||||
this.mth = mth;
|
||||
this.insns = instructions;
|
||||
this.toRemove = new ArrayList<InsnNode>();
|
||||
}
|
||||
@@ -28,48 +43,48 @@ public class InstructionRemover {
|
||||
}
|
||||
|
||||
public void perform() {
|
||||
if (toRemove.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
removeAll(insns, toRemove);
|
||||
toRemove.clear();
|
||||
}
|
||||
|
||||
public static void unbindInsnList(List<InsnNode> unbind) {
|
||||
public static void unbindInsnList(MethodNode mth, List<InsnNode> 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<InsnNode> 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<InsnNode> insns, List<InsnNode> toRemove) {
|
||||
if (insns == toRemove) {
|
||||
for (InsnNode rem : toRemove) {
|
||||
unbindInsn(rem);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
private void removeAll(List<InsnNode> insns, List<InsnNode> toRemove) {
|
||||
for (InsnNode rem : toRemove) {
|
||||
unbindInsn(rem);
|
||||
for (Iterator<InsnNode> 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<InsnNode> it = block.getInstructions().iterator();
|
||||
while (it.hasNext()) {
|
||||
@@ -96,17 +111,10 @@ public class InstructionRemover {
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeAllByContent(BlockNode block, List<InsnNode> 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<InsnNode> instructions = block.getInstructions();
|
||||
unbindInsn(mth, instructions.get(index));
|
||||
instructions.remove(index);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;"));
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package jadx.tests.internal;
|
||||
package jadx.tests.internal.annotations;
|
||||
|
||||
import jadx.api.InternalJadxTest;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
@@ -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();"));
|
||||
}
|
||||
}
|
||||
@@ -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")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> l1, List<String> 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")));
|
||||
}
|
||||
}
|
||||
@@ -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("||")));
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -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("?")));
|
||||
@@ -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;"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);"));
|
||||
}
|
||||
}
|
||||
@@ -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);"));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;"));
|
||||
}
|
||||
|
||||
@@ -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++;"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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++"));
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package jadx.tests.internal;
|
||||
package jadx.tests.internal.synchronize;
|
||||
|
||||
import jadx.api.InternalJadxTest;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
@@ -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 (")));
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package jadx.tests.internal;
|
||||
package jadx.tests.internal.trycatch;
|
||||
|
||||
import jadx.api.InternalJadxTest;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
@@ -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;"));
|
||||
}
|
||||
}
|
||||
@@ -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;")));
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user