core: inline anonymous classes with arguments
This commit is contained in:
@@ -29,6 +29,7 @@ import jadx.core.dex.instructions.args.InsnWrapArg;
|
||||
import jadx.core.dex.instructions.args.LiteralArg;
|
||||
import jadx.core.dex.instructions.args.Named;
|
||||
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.ClassNode;
|
||||
@@ -123,6 +124,9 @@ public class InsnGen {
|
||||
}
|
||||
|
||||
public void declareVar(CodeWriter code, RegisterArg arg) {
|
||||
if (arg.getSVar().contains(AFlag.FINAL)) {
|
||||
code.add("final ");
|
||||
}
|
||||
useType(code, arg.getType());
|
||||
code.add(' ');
|
||||
code.add(mgen.getNameGen().assignArg(arg));
|
||||
@@ -144,16 +148,19 @@ public class InsnGen {
|
||||
if (fieldNode != null) {
|
||||
FieldReplaceAttr replace = fieldNode.get(AType.FIELD_REPLACE);
|
||||
if (replace != null) {
|
||||
FieldInfo info = replace.getFieldInfo();
|
||||
if (replace.isOuterClass()) {
|
||||
useClass(code, info.getDeclClass());
|
||||
code.add(".this");
|
||||
switch (replace.getReplaceType()) {
|
||||
case CLASS_INSTANCE:
|
||||
useClass(code, replace.getClsRef());
|
||||
code.add(".this");
|
||||
break;
|
||||
case VAR:
|
||||
addArg(code, replace.getVarRef());
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
addArgDot(code, arg);
|
||||
fieldNode = mth.dex().resolveField(field);
|
||||
if (fieldNode != null) {
|
||||
code.attachAnnotation(fieldNode);
|
||||
}
|
||||
@@ -531,30 +538,7 @@ public class InsnGen {
|
||||
throws CodegenException {
|
||||
ClassNode cls = mth.dex().resolveClass(insn.getClassType());
|
||||
if (cls != null && cls.contains(AFlag.ANONYMOUS_CLASS) && !fallback) {
|
||||
// anonymous class construction
|
||||
ArgType parent;
|
||||
if (cls.getInterfaces().size() == 1) {
|
||||
parent = cls.getInterfaces().get(0);
|
||||
} else {
|
||||
parent = cls.getSuperClass();
|
||||
}
|
||||
cls.add(AFlag.DONT_GENERATE);
|
||||
MethodNode defCtr = cls.getDefaultConstructor();
|
||||
if (defCtr != null) {
|
||||
if (RegionUtils.notEmpty(defCtr.getRegion())) {
|
||||
defCtr.add(AFlag.ANONYMOUS_CONSTRUCTOR);
|
||||
} else {
|
||||
defCtr.add(AFlag.DONT_GENERATE);
|
||||
}
|
||||
}
|
||||
code.add("new ");
|
||||
if (parent == null) {
|
||||
code.add("Object");
|
||||
} else {
|
||||
useClass(code, parent);
|
||||
}
|
||||
code.add("() ");
|
||||
new ClassGen(cls, mgen.getClassGen().getParentGen()).addClassBody(code);
|
||||
inlineAnonymousConstr(code, cls, insn);
|
||||
return;
|
||||
}
|
||||
if (insn.isSelf()) {
|
||||
@@ -568,7 +552,37 @@ public class InsnGen {
|
||||
code.add("new ");
|
||||
useClass(code, insn.getClassType());
|
||||
}
|
||||
generateMethodArguments(code, insn, 0, mth.dex().resolveMethod(insn.getCallMth()));
|
||||
MethodNode callMth = mth.dex().resolveMethod(insn.getCallMth());
|
||||
generateMethodArguments(code, insn, 0, callMth);
|
||||
}
|
||||
|
||||
private void inlineAnonymousConstr(CodeWriter code, ClassNode cls, ConstructorInsn insn) throws CodegenException {
|
||||
// anonymous class construction
|
||||
ArgType parent;
|
||||
if (cls.getInterfaces().size() == 1) {
|
||||
parent = cls.getInterfaces().get(0);
|
||||
} else {
|
||||
parent = cls.getSuperClass();
|
||||
}
|
||||
cls.add(AFlag.DONT_GENERATE);
|
||||
MethodNode defCtr = cls.getDefaultConstructor();
|
||||
if (defCtr != null) {
|
||||
if (RegionUtils.notEmpty(defCtr.getRegion())) {
|
||||
defCtr.add(AFlag.ANONYMOUS_CONSTRUCTOR);
|
||||
} else {
|
||||
defCtr.add(AFlag.DONT_GENERATE);
|
||||
}
|
||||
}
|
||||
code.add("new ");
|
||||
if (parent == null) {
|
||||
code.add("Object");
|
||||
} else {
|
||||
useClass(code, parent);
|
||||
}
|
||||
MethodNode callMth = mth.dex().resolveMethod(insn.getCallMth());
|
||||
generateMethodArguments(code, insn, 0, callMth);
|
||||
code.add(' ');
|
||||
new ClassGen(cls, mgen.getClassGen().getParentGen()).addClassBody(code);
|
||||
}
|
||||
|
||||
private void makeInvoke(InvokeNode insn, CodeWriter code) throws CodegenException {
|
||||
@@ -631,6 +645,12 @@ public class InsnGen {
|
||||
boolean overloaded = callMth != null && callMth.isArgsOverload();
|
||||
for (int i = k; i < argsCount; i++) {
|
||||
InsnArg arg = insn.getArg(i);
|
||||
if (arg.isRegister()) {
|
||||
SSAVar sVar = ((RegisterArg) arg).getSVar();
|
||||
if (sVar != null && sVar.contains(AFlag.SKIP_ARG)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
boolean cast = overloaded && processOverloadedArg(code, callMth, arg, i - startArgNum);
|
||||
if (!cast && i == argsCount - 1 && processVarArg(code, callMth, arg)) {
|
||||
continue;
|
||||
|
||||
@@ -8,6 +8,7 @@ import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.instructions.args.SSAVar;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.trycatch.CatchAttr;
|
||||
@@ -126,6 +127,10 @@ public class MethodGen {
|
||||
if (paramsAnnotation != null) {
|
||||
annotationGen.addForParameter(argsCode, paramsAnnotation, i);
|
||||
}
|
||||
SSAVar argSVar = arg.getSVar();
|
||||
if (argSVar!= null && argSVar.contains(AFlag.FINAL)) {
|
||||
argsCode.add("final ");
|
||||
}
|
||||
if (!it.hasNext() && mth.getAccessFlags().isVarArgs()) {
|
||||
// change last array argument to varargs
|
||||
ArgType type = arg.getType();
|
||||
|
||||
@@ -8,6 +8,7 @@ public enum AFlag {
|
||||
LOOP_END,
|
||||
|
||||
SYNTHETIC,
|
||||
FINAL, // SSAVar attribute for make var final
|
||||
|
||||
RETURN, // block contains only return instruction
|
||||
ORIG_RETURN,
|
||||
@@ -22,6 +23,7 @@ public enum AFlag {
|
||||
REMOVE,
|
||||
|
||||
SKIP_FIRST_ARG,
|
||||
SKIP_ARG, // skip argument in invoke call
|
||||
ANONYMOUS_CONSTRUCTOR,
|
||||
ANONYMOUS_CLASS,
|
||||
|
||||
|
||||
@@ -2,24 +2,39 @@ package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
|
||||
public class FieldReplaceAttr implements IAttribute {
|
||||
|
||||
private final FieldInfo fieldInfo;
|
||||
private final boolean isOuterClass;
|
||||
|
||||
public FieldReplaceAttr(FieldInfo fieldInfo, boolean isOuterClass) {
|
||||
this.fieldInfo = fieldInfo;
|
||||
this.isOuterClass = isOuterClass;
|
||||
public enum ReplaceWith {
|
||||
CLASS_INSTANCE,
|
||||
VAR
|
||||
}
|
||||
|
||||
public FieldInfo getFieldInfo() {
|
||||
return fieldInfo;
|
||||
private final ReplaceWith replaceType;
|
||||
private final Object replaceObj;
|
||||
|
||||
public FieldReplaceAttr(ClassInfo cls) {
|
||||
this.replaceType = ReplaceWith.CLASS_INSTANCE;
|
||||
this.replaceObj = cls;
|
||||
}
|
||||
|
||||
public boolean isOuterClass() {
|
||||
return isOuterClass;
|
||||
public FieldReplaceAttr(InsnArg reg) {
|
||||
this.replaceType = ReplaceWith.VAR;
|
||||
this.replaceObj = reg;
|
||||
}
|
||||
|
||||
public ReplaceWith getReplaceType() {
|
||||
return replaceType;
|
||||
}
|
||||
|
||||
public ClassInfo getClsRef() {
|
||||
return (ClassInfo) replaceObj;
|
||||
}
|
||||
|
||||
public InsnArg getVarRef() {
|
||||
return (InsnArg) replaceObj;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -29,6 +44,6 @@ public class FieldReplaceAttr implements IAttribute {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "REPLACE: " + fieldInfo;
|
||||
return "REPLACE: " + replaceType + " " + replaceObj;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package jadx.core.dex.instructions.args;
|
||||
|
||||
import jadx.core.dex.attributes.AttrNode;
|
||||
import jadx.core.dex.instructions.PhiInsn;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -8,7 +9,7 @@ import java.util.List;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class SSAVar {
|
||||
public class SSAVar extends AttrNode {
|
||||
|
||||
private final int regNum;
|
||||
private final int version;
|
||||
|
||||
@@ -76,8 +76,7 @@ public class ClassModifier extends AbstractVisitor {
|
||||
}
|
||||
}
|
||||
if (found != 0) {
|
||||
FieldInfo replace = FieldInfo.from(cls.dex(), parentClass, "this", parentClass.getType());
|
||||
field.addAttr(new FieldReplaceAttr(replace, true));
|
||||
field.addAttr(new FieldReplaceAttr(parentClass));
|
||||
field.add(AFlag.DONT_GENERATE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,7 +210,9 @@ public class CodeShrinker extends AbstractVisitor {
|
||||
// }
|
||||
SSAVar sVar = arg.getSVar();
|
||||
// allow inline only one use arg or 'this'
|
||||
if (sVar == null || sVar.getVariableUseCount() != 1 && !arg.isThis()) {
|
||||
if (sVar == null
|
||||
|| sVar.getVariableUseCount() != 1 && !arg.isThis()
|
||||
|| sVar.contains(AFlag.DONT_INLINE)) {
|
||||
continue;
|
||||
}
|
||||
InsnNode assignInsn = sVar.getAssign().getParentInsn();
|
||||
|
||||
@@ -18,6 +18,7 @@ public class DebugInfoVisitor extends AbstractVisitor {
|
||||
DebugInfoParser debugInfoParser = new DebugInfoParser(mth, debugOffset, insnArr);
|
||||
debugInfoParser.process();
|
||||
|
||||
// set method source line from first instruction
|
||||
if (insnArr.length != 0) {
|
||||
for (InsnNode insn : insnArr) {
|
||||
if (insn != null) {
|
||||
@@ -30,7 +31,7 @@ public class DebugInfoVisitor extends AbstractVisitor {
|
||||
}
|
||||
}
|
||||
if (!mth.getReturnType().equals(ArgType.VOID)) {
|
||||
// fix debug for splitter 'return' instructions
|
||||
// fix debug info for splitter 'return' instructions
|
||||
for (BlockNode exit : mth.getExitBlocks()) {
|
||||
InsnNode ret = BlockUtils.getLastInsn(exit);
|
||||
if (ret == null) {
|
||||
|
||||
@@ -23,8 +23,13 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class DotGraphVisitor extends AbstractVisitor {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DotGraphVisitor.class);
|
||||
|
||||
private static final String NL = "\\l";
|
||||
private static final boolean PRINT_DOMINATORS = false;
|
||||
|
||||
@@ -52,6 +57,8 @@ public class DotGraphVisitor extends AbstractVisitor {
|
||||
this.dir = outDir;
|
||||
this.useRegions = useRegions;
|
||||
this.rawInsn = rawInsn;
|
||||
LOG.debug("DOT {}{}graph dump dir: {}",
|
||||
useRegions ? "regions " : "", rawInsn ? "raw " : "", outDir.getAbsolutePath());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,7 +2,11 @@ package jadx.core.dex.visitors;
|
||||
|
||||
import jadx.core.codegen.TypeGen;
|
||||
import jadx.core.deobf.NameMapper;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.FieldReplaceAttr;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.instructions.ArithNode;
|
||||
import jadx.core.dex.instructions.ConstClassNode;
|
||||
@@ -34,7 +38,10 @@ import jadx.core.utils.InstructionRemover;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -43,6 +50,11 @@ import org.slf4j.LoggerFactory;
|
||||
* Visitor for modify method instructions
|
||||
* (remove, replace, process exception handlers)
|
||||
*/
|
||||
@JadxVisitor(
|
||||
name = "ModVisitor",
|
||||
desc = "Modify method instructions",
|
||||
runBefore = CodeShrinker.class
|
||||
)
|
||||
public class ModVisitor extends AbstractVisitor {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ModVisitor.class);
|
||||
|
||||
@@ -197,7 +209,6 @@ public class ModVisitor extends AbstractVisitor {
|
||||
remover.add(insn);
|
||||
return;
|
||||
}
|
||||
replaceInsn(block, insnNumber, co);
|
||||
if (co.isNewInstance()) {
|
||||
InsnNode newInstInsn = removeAssignChain(instArgAssignInsn, remover, InsnType.NEW_INSTANCE);
|
||||
if (newInstInsn != null) {
|
||||
@@ -217,8 +228,107 @@ public class ModVisitor extends AbstractVisitor {
|
||||
}
|
||||
ConstructorInsn replace = processConstructor(mth, co);
|
||||
if (replace != null) {
|
||||
replaceInsn(block, insnNumber, replace);
|
||||
co = replace;
|
||||
}
|
||||
replaceInsn(block, insnNumber, co);
|
||||
|
||||
processAnonymousConstructor(mth, co);
|
||||
}
|
||||
|
||||
private static void processAnonymousConstructor(MethodNode mth, ConstructorInsn co) {
|
||||
MethodInfo callMth = co.getCallMth();
|
||||
MethodNode callMthNode = mth.dex().resolveMethod(callMth);
|
||||
if (callMthNode == null) {
|
||||
return;
|
||||
}
|
||||
ClassNode classNode = callMthNode.getParentClass();
|
||||
ClassInfo classInfo = classNode.getClassInfo();
|
||||
ClassNode parentClass = mth.getParentClass();
|
||||
if (!classInfo.isInner()
|
||||
|| !Character.isDigit(classInfo.getShortName().charAt(0))
|
||||
|| !parentClass.getInnerClasses().contains(classNode)) {
|
||||
return;
|
||||
}
|
||||
if (!classNode.getAccessFlags().isStatic()
|
||||
&& (callMth.getArgsCount() == 0
|
||||
|| !callMth.getArgumentsTypes().get(0).equals(parentClass.getClassInfo().getType()))) {
|
||||
return;
|
||||
}
|
||||
// TODO: calculate this constructor and other constructor usage
|
||||
Map<InsnArg, FieldNode> argsMap = getArgsToFieldsMapping(callMthNode, co);
|
||||
if (argsMap.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// all checks passed
|
||||
classNode.add(AFlag.ANONYMOUS_CLASS);
|
||||
callMthNode.add(AFlag.DONT_GENERATE);
|
||||
for (Map.Entry<InsnArg, FieldNode> entry : argsMap.entrySet()) {
|
||||
FieldNode field = entry.getValue();
|
||||
if (field == null) {
|
||||
continue;
|
||||
}
|
||||
InsnArg arg = entry.getKey();
|
||||
field.addAttr(new FieldReplaceAttr(arg));
|
||||
field.add(AFlag.DONT_GENERATE);
|
||||
if (arg.isRegister()) {
|
||||
RegisterArg reg = (RegisterArg) arg;
|
||||
SSAVar sVar = reg.getSVar();
|
||||
if (sVar != null) {
|
||||
sVar.add(AFlag.FINAL);
|
||||
sVar.add(AFlag.DONT_INLINE);
|
||||
sVar.add(AFlag.SKIP_ARG);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<InsnArg, FieldNode> getArgsToFieldsMapping(MethodNode callMthNode, ConstructorInsn co) {
|
||||
Map<InsnArg, FieldNode> map = new LinkedHashMap<InsnArg, FieldNode>();
|
||||
ClassNode parentClass = callMthNode.getParentClass();
|
||||
List<RegisterArg> argList = callMthNode.getArguments(false);
|
||||
int startArg = parentClass.getAccessFlags().isStatic() ? 0 : 1;
|
||||
int argsCount = argList.size();
|
||||
for (int i = startArg; i < argsCount; i++) {
|
||||
RegisterArg arg = argList.get(i);
|
||||
InsnNode useInsn = getParentInsnSkipMove(arg);
|
||||
if (useInsn == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
FieldNode fieldNode = null;
|
||||
if (useInsn.getType() == InsnType.IPUT) {
|
||||
FieldInfo field = (FieldInfo) ((IndexInsnNode) useInsn).getIndex();
|
||||
fieldNode = parentClass.searchField(field);
|
||||
if (fieldNode == null || !fieldNode.getAccessFlags().isSynthetic()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
} else if (useInsn.getType() == InsnType.CONSTRUCTOR) {
|
||||
ConstructorInsn superConstr = (ConstructorInsn) useInsn;
|
||||
if (!superConstr.isSuper()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
} else {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
map.put(co.getArg(i), fieldNode);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private static InsnNode getParentInsnSkipMove(RegisterArg arg) {
|
||||
SSAVar sVar = arg.getSVar();
|
||||
if (sVar.getUseCount() != 1) {
|
||||
return null;
|
||||
}
|
||||
RegisterArg useArg = sVar.getUseList().get(0);
|
||||
InsnNode parentInsn = useArg.getParentInsn();
|
||||
if (parentInsn == null) {
|
||||
return null;
|
||||
}
|
||||
if (parentInsn.getType() == InsnType.MOVE) {
|
||||
return getParentInsnSkipMove(parentInsn.getResult());
|
||||
}
|
||||
return parentInsn;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,6 +21,11 @@ import java.util.List;
|
||||
* most of this modification breaks register dependencies,
|
||||
* so this pass must be just before CodeGen.
|
||||
*/
|
||||
@JadxVisitor(
|
||||
name = "PrepareForCodeGen",
|
||||
desc = "Prepare instructions for code generation pass",
|
||||
runAfter = {CodeShrinker.class, ClassModifier.class}
|
||||
)
|
||||
public class PrepareForCodeGen extends AbstractVisitor {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -77,25 +77,7 @@ public class SimplifyVisitor extends AbstractVisitor {
|
||||
return convertFieldArith(mth, insn);
|
||||
|
||||
case CHECK_CAST:
|
||||
InsnArg castArg = insn.getArg(0);
|
||||
ArgType argType = castArg.getType();
|
||||
|
||||
// Don't removes CHECK_CAST for wrapped INVOKE if invoked method returns different type
|
||||
if (castArg.isInsnWrap()) {
|
||||
InsnNode wrapInsn = ((InsnWrapArg) castArg).getWrapInsn();
|
||||
if (wrapInsn.getType() == InsnType.INVOKE) {
|
||||
argType = ((InvokeNode) wrapInsn).getCallMth().getReturnType();
|
||||
}
|
||||
}
|
||||
ArgType castToType = (ArgType) ((IndexInsnNode) insn).getIndex();
|
||||
if (!ArgType.isCastNeeded(mth.dex(), argType, castToType)) {
|
||||
InsnNode insnNode = new InsnNode(InsnType.MOVE, 1);
|
||||
insnNode.setOffset(insn.getOffset());
|
||||
insnNode.setResult(insn.getResult());
|
||||
insnNode.addArg(castArg);
|
||||
return insnNode;
|
||||
}
|
||||
break;
|
||||
return processCast(mth, insn);
|
||||
|
||||
case MOVE:
|
||||
InsnArg firstArg = insn.getArg(0);
|
||||
@@ -114,6 +96,28 @@ public class SimplifyVisitor extends AbstractVisitor {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static InsnNode processCast(MethodNode mth, InsnNode insn) {
|
||||
InsnArg castArg = insn.getArg(0);
|
||||
ArgType argType = castArg.getType();
|
||||
|
||||
// Don't removes CHECK_CAST for wrapped INVOKE if invoked method returns different type
|
||||
if (castArg.isInsnWrap()) {
|
||||
InsnNode wrapInsn = ((InsnWrapArg) castArg).getWrapInsn();
|
||||
if (wrapInsn.getType() == InsnType.INVOKE) {
|
||||
argType = ((InvokeNode) wrapInsn).getCallMth().getReturnType();
|
||||
}
|
||||
}
|
||||
ArgType castToType = (ArgType) ((IndexInsnNode) insn).getIndex();
|
||||
if (ArgType.isCastNeeded(mth.dex(), argType, castToType)) {
|
||||
return null;
|
||||
}
|
||||
InsnNode insnNode = new InsnNode(InsnType.MOVE, 1);
|
||||
insnNode.setOffset(insn.getOffset());
|
||||
insnNode.setResult(insn.getResult());
|
||||
insnNode.addArg(castArg);
|
||||
return insnNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplify 'cmp' instruction in if condition
|
||||
*/
|
||||
|
||||
@@ -55,7 +55,7 @@ public class ProcessTryCatchRegions extends AbstractRegionVisitor {
|
||||
}
|
||||
|
||||
private static void searchTryCatchDominators(MethodNode mth, Map<BlockNode, TryCatchBlock> tryBlocksMap) {
|
||||
final Set<TryCatchBlock> tryBlocks = new HashSet<TryCatchBlock>();
|
||||
Set<TryCatchBlock> tryBlocks = new HashSet<TryCatchBlock>();
|
||||
// collect all try/catch blocks
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
CatchAttr c = block.get(AType.CATCH_BLOCK);
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package jadx.tests.integration.inner;
|
||||
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.tests.api.IntegrationTest;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static jadx.tests.api.utils.JadxMatchers.containsOne;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class TestAnonymousClass10 extends IntegrationTest {
|
||||
|
||||
public static class TestCls {
|
||||
|
||||
public A test() {
|
||||
Random random = new Random();
|
||||
int a2 = random.nextInt();
|
||||
int a3 = a2 + 3;
|
||||
return new A(this, a2, a3, 4, 5, random.nextDouble()) {
|
||||
@Override
|
||||
public void m() {
|
||||
System.out.println(1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public abstract class A {
|
||||
public A(TestCls a1, int a2, int a3, int a4, int a5, double a6) {
|
||||
}
|
||||
|
||||
public abstract void m();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
ClassNode cls = getClassNode(TestCls.class);
|
||||
String code = cls.getCode().toString();
|
||||
|
||||
assertThat(code, containsOne("return new A(this, a2, a2 + 3, 4, 5, random.nextDouble()) {"));
|
||||
assertThat(code, not(containsString("synthetic")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package jadx.tests.integration.inner;
|
||||
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.tests.api.IntegrationTest;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static jadx.tests.api.utils.JadxMatchers.containsOne;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class TestAnonymousClass6 extends IntegrationTest {
|
||||
|
||||
public static class TestCls {
|
||||
public Runnable test(final double d) {
|
||||
return new Runnable() {
|
||||
public void run() {
|
||||
System.out.println(d);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
ClassNode cls = getClassNode(TestCls.class);
|
||||
String code = cls.getCode().toString();
|
||||
|
||||
assertThat(code, containsOne("public Runnable test(final double d) {"));
|
||||
assertThat(code, containsOne("return new Runnable() {"));
|
||||
assertThat(code, containsOne("public void run() {"));
|
||||
assertThat(code, containsOne("System.out.println(d);"));
|
||||
assertThat(code, not(containsString("synthetic")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package jadx.tests.integration.inner;
|
||||
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.tests.api.IntegrationTest;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static jadx.tests.api.utils.JadxMatchers.containsOne;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class TestAnonymousClass7 extends IntegrationTest {
|
||||
|
||||
public static class TestCls {
|
||||
public static Runnable test(final double d) {
|
||||
return new Runnable() {
|
||||
public void run() {
|
||||
System.out.println(d);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
ClassNode cls = getClassNode(TestCls.class);
|
||||
String code = cls.getCode().toString();
|
||||
|
||||
assertThat(code, containsOne("public static Runnable test(final double d) {"));
|
||||
assertThat(code, containsOne("return new Runnable() {"));
|
||||
assertThat(code, containsOne("public void run() {"));
|
||||
assertThat(code, containsOne("System.out.println(d);"));
|
||||
assertThat(code, not(containsString("synthetic")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package jadx.tests.integration.inner;
|
||||
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.tests.api.IntegrationTest;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static jadx.tests.api.utils.JadxMatchers.containsOne;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class TestAnonymousClass8 extends IntegrationTest {
|
||||
|
||||
public static class TestCls {
|
||||
|
||||
public final double d = Math.abs(4);
|
||||
|
||||
public Runnable test() {
|
||||
return new Runnable() {
|
||||
public void run() {
|
||||
System.out.println(d);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
ClassNode cls = getClassNode(TestCls.class);
|
||||
String code = cls.getCode().toString();
|
||||
|
||||
assertThat(code, containsOne("public Runnable test() {"));
|
||||
assertThat(code, containsOne("return new Runnable() {"));
|
||||
assertThat(code, containsOne("public void run() {"));
|
||||
assertThat(code, containsOne("this.d);"));
|
||||
assertThat(code, not(containsString("synthetic")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package jadx.tests.integration.inner;
|
||||
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.tests.api.IntegrationTest;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static jadx.tests.api.utils.JadxMatchers.containsOne;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class TestAnonymousClass9 extends IntegrationTest {
|
||||
|
||||
public static class TestCls {
|
||||
|
||||
public Callable<String> c = new Callable<String>() {
|
||||
@Override
|
||||
public String call() throws Exception {
|
||||
return "str";
|
||||
}
|
||||
};
|
||||
|
||||
public Runnable test() {
|
||||
return new FutureTask<String>(this.c) {
|
||||
public void run() {
|
||||
System.out.println(6);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
ClassNode cls = getClassNode(TestCls.class);
|
||||
String code = cls.getCode().toString();
|
||||
|
||||
assertThat(code, containsOne("c = new Callable<String>() {"));
|
||||
assertThat(code, containsOne("return new FutureTask<String>(this.c) {"));
|
||||
assertThat(code, not(containsString("synthetic")));
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@ public class TestConstructor extends SmaliTest {
|
||||
disableCompilation();
|
||||
ClassNode cls = getClassNodeFromSmali("TestConstructor");
|
||||
String code = cls.getCode().toString();
|
||||
System.out.println(code);
|
||||
|
||||
assertThat(code, containsOne("new SomeObject(arg3);"));
|
||||
assertThat(code, not(containsString("= someObject")));
|
||||
|
||||
Reference in New Issue
Block a user