diff --git a/jadx-core/src/main/java/jadx/core/Jadx.java b/jadx-core/src/main/java/jadx/core/Jadx.java index fd8162f90..1a05c5986 100644 --- a/jadx-core/src/main/java/jadx/core/Jadx.java +++ b/jadx-core/src/main/java/jadx/core/Jadx.java @@ -71,6 +71,7 @@ public class Jadx { passes.add(new DebugInfoApplyVisitor()); } + passes.add(new GenericTypesVisitor()); passes.add(new DeboxingVisitor()); passes.add(new ModVisitor()); passes.add(new CodeShrinkVisitor()); diff --git a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java index 1238d1a75..2a5b3012a 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -15,6 +15,7 @@ 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.attributes.nodes.GenericInfoAttr; import jadx.core.dex.attributes.nodes.LoopLabelAttr; import jadx.core.dex.attributes.nodes.MethodInlineAttr; import jadx.core.dex.attributes.nodes.SkipMethodArgsAttr; @@ -603,8 +604,7 @@ public class InsnGen { code.add('}'); } - private void makeConstructor(ConstructorInsn insn, CodeWriter code) - throws CodegenException { + private void makeConstructor(ConstructorInsn insn, CodeWriter code) throws CodegenException { ClassNode cls = mth.dex().resolveClass(insn.getClassType()); if (cls != null && cls.isAnonymous() && !fallback) { cls.ensureProcessed(); @@ -622,20 +622,18 @@ public class InsnGen { } else { code.add("new "); useClass(code, insn.getClassType()); - ArgType argType = insn.getResult().getSVar().getCodeVar().getType(); - boolean genericCls = cls == null || !cls.getGenericTypeParameters().isEmpty(); - if (argType != null - && argType.getGenericTypes() != null - && genericCls) { + GenericInfoAttr genericInfoAttr = insn.get(AType.GENERIC_INFO); + if (genericInfoAttr != null) { code.add('<'); - if (insn.contains(AFlag.EXPLICIT_GENERICS)) { + if (genericInfoAttr.isExplicit()) { boolean first = true; - for (ArgType type : argType.getGenericTypes()) { + for (ArgType type : genericInfoAttr.getGenericTypes()) { if (!first) { code.add(','); + } else { + first = false; } mgen.getClassGen().useType(code, type); - first = false; } } code.add('>'); @@ -849,7 +847,7 @@ public class InsnGen { regs[regNums[i]] = arg; } // replace args - InsnNode inlCopy = inl.copy(); + InsnNode inlCopy = inl.copyWithoutResult(); List inlArgs = new ArrayList<>(); inlCopy.getRegisterArgs(inlArgs); for (RegisterArg r : inlArgs) { diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/AFlag.java b/jadx-core/src/main/java/jadx/core/dex/attributes/AFlag.java index 85d54f45c..8f5687f01 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/AFlag.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/AFlag.java @@ -60,7 +60,6 @@ public enum AFlag { FALL_THROUGH, - EXPLICIT_GENERICS, VARARG_CALL, /** diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/AType.java b/jadx-core/src/main/java/jadx/core/dex/attributes/AType.java index 00d018af4..6003ae9c8 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/AType.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/AType.java @@ -12,6 +12,7 @@ import jadx.core.dex.attributes.nodes.EnumClassAttr; import jadx.core.dex.attributes.nodes.EnumMapAttr; import jadx.core.dex.attributes.nodes.FieldReplaceAttr; import jadx.core.dex.attributes.nodes.ForceReturnAttr; +import jadx.core.dex.attributes.nodes.GenericInfoAttr; import jadx.core.dex.attributes.nodes.IgnoreEdgeAttr; import jadx.core.dex.attributes.nodes.JadxError; import jadx.core.dex.attributes.nodes.JumpInfo; @@ -36,6 +37,7 @@ import jadx.core.dex.trycatch.SplitterBlockAttr; * * @param attribute class implementation */ +@SuppressWarnings("InstantiationOfUtilityClass") public class AType { // class, method, field @@ -81,6 +83,7 @@ public class AType { public static final AType LOOP_LABEL = new AType<>(); public static final AType> JUMP = new AType<>(); public static final AType METHOD_DETAILS = new AType<>(); + public static final AType GENERIC_INFO = new AType<>(); // register public static final AType REG_DEBUG_INFO = new AType<>(); diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/EnumClassAttr.java b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/EnumClassAttr.java index 53f72877c..dd9f24c50 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/EnumClassAttr.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/EnumClassAttr.java @@ -1,6 +1,5 @@ package jadx.core.dex.attributes.nodes; -import java.util.ArrayList; import java.util.List; import jadx.core.dex.attributes.AType; @@ -53,8 +52,8 @@ public class EnumClassAttr implements IAttribute { private final List fields; private MethodNode staticMethod; - public EnumClassAttr(int fieldsCount) { - this.fields = new ArrayList<>(fieldsCount); + public EnumClassAttr(List fields) { + this.fields = fields; } public List getFields() { diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/GenericInfoAttr.java b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/GenericInfoAttr.java new file mode 100644 index 000000000..5466919a9 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/GenericInfoAttr.java @@ -0,0 +1,31 @@ +package jadx.core.dex.attributes.nodes; + +import jadx.core.dex.attributes.AType; +import jadx.core.dex.attributes.IAttribute; +import jadx.core.dex.instructions.args.ArgType; + +public class GenericInfoAttr implements IAttribute { + private final ArgType[] genericTypes; + private boolean explicit; + + public GenericInfoAttr(ArgType[] genericTypes) { + this.genericTypes = genericTypes; + } + + public ArgType[] getGenericTypes() { + return genericTypes; + } + + public boolean isExplicit() { + return explicit; + } + + public void setExplicit(boolean explicit) { + this.explicit = explicit; + } + + @Override + public AType getType() { + return AType.GENERIC_INFO; + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/NotificationAttrNode.java b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/NotificationAttrNode.java index c2f72087b..4f2f55261 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/NotificationAttrNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/NotificationAttrNode.java @@ -37,4 +37,9 @@ public abstract class NotificationAttrNode extends LineAttrNode implements ICode addAttr(AType.COMMENTS, commentStr); LOG.info("{} in {}", commentStr, this); } + + public void addDebugComment(String commentStr) { + addAttr(AType.COMMENTS, "JADX DEBUG: " + commentStr); + LOG.debug("{} in {}", commentStr, this); + } } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/ArithNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/ArithNode.java index a2717ff2e..6346acc99 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/ArithNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/ArithNode.java @@ -51,6 +51,10 @@ public class ArithNode extends InsnNode { addArg(b); } + public ArithNode(ArithOp op, InsnArg a, InsnArg b) { + this(op, null, a, b); + } + /** * Create one argument arithmetic instructions (a+=2). * Result is not set (null). @@ -58,7 +62,7 @@ public class ArithNode extends InsnNode { * @param res argument to change */ public static ArithNode oneArgOp(ArithOp op, InsnArg res, InsnArg a) { - ArithNode insn = new ArithNode(op, null, res, a); + ArithNode insn = new ArithNode(op, res, a); insn.add(AFlag.ARITH_ONEARG); return insn; } @@ -97,10 +101,7 @@ public class ArithNode extends InsnNode { @Override public InsnNode copy() { - ArithNode copy = new ArithNode(op, - getResult().duplicate(), - getArg(0).duplicate(), - getArg(1).duplicate()); + ArithNode copy = new ArithNode(op, getArg(0).duplicate(), getArg(1).duplicate()); return copyCommonParams(copy); } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnArg.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnArg.java index cf35cc5eb..207452a82 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnArg.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnArg.java @@ -1,5 +1,6 @@ package jadx.core.dex.instructions.args; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -96,6 +97,11 @@ public abstract class InsnArg extends Typed { @Nullable("if wrap failed") public InsnArg wrapInstruction(MethodNode mth, InsnNode insn) { + return wrapInstruction(mth, insn, true); + } + + @Nullable("if wrap failed") + public InsnArg wrapInstruction(MethodNode mth, InsnNode insn, boolean unbind) { InsnNode parent = parentInsn; if (parent == null) { return null; @@ -116,14 +122,24 @@ public abstract class InsnArg extends Typed { if (arg.isRegister()) { ((RegisterArg) arg).setNameIfUnknown(name); } else if (arg.isInsnWrap()) { - ((InsnWrapArg) arg).getWrapInsn().getResult().setNameIfUnknown(name); + InsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn(); + RegisterArg registerArg = wrapInsn.getResult(); + if (registerArg != null) { + registerArg.setNameIfUnknown(name); + } } } } InsnArg arg = wrapInsnIntoArg(insn); + InsnArg oldArg = parent.getArg(i); parent.setArg(i, arg); - InsnRemover.unbindArgUsage(mth, this); - InsnRemover.unbindResult(mth, insn); + InsnRemover.unbindArgUsage(mth, oldArg); + if (unbind) { + InsnRemover.unbindArgUsage(mth, this); + // result not needed in wrapped insn + InsnRemover.unbindResult(mth, insn); + insn.setResult(null); + } return arg; } @@ -137,29 +153,29 @@ public abstract class InsnArg extends Typed { return -1; } + @NotNull public static InsnArg wrapInsnIntoArg(InsnNode insn) { - InsnArg arg; InsnType type = insn.getType(); if (type == InsnType.CONST || type == InsnType.MOVE) { if (insn.contains(AFlag.FORCE_ASSIGN_INLINE)) { RegisterArg resArg = insn.getResult(); - arg = wrap(insn); + InsnArg arg = wrap(insn); if (resArg != null) { arg.setType(resArg.getType()); } + return arg; } else { - arg = insn.getArg(0); - insn.add(AFlag.REMOVE); + InsnArg arg = insn.getArg(0); insn.add(AFlag.DONT_GENERATE); + return arg; } - } else { - arg = wrapArg(insn); } - return arg; + return wrapArg(insn); } /** - * Prefer {@link InsnArg#wrapInsnIntoArg}. + * Prefer {@link InsnArg#wrapInsnIntoArg(InsnNode)}. + *

* This method don't support MOVE and CONST insns! */ public static InsnArg wrapArg(InsnNode insn) { diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnWrapArg.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnWrapArg.java index 70d61333a..186aa2b19 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnWrapArg.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnWrapArg.java @@ -36,7 +36,7 @@ public final class InsnWrapArg extends InsnArg { @Override public InsnArg duplicate() { - InsnWrapArg copy = new InsnWrapArg(wrappedInsn.copy()); + InsnWrapArg copy = new InsnWrapArg(wrappedInsn.copyWithoutResult()); copy.setType(type); return copyCommonParams(copy); } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java index 6b8ada9d4..ca9fe1ce9 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java @@ -130,6 +130,10 @@ public class RegisterArg extends InsnArg implements Named { return duplicate(getRegNum(), sVar); } + public RegisterArg duplicate(@Nullable SSAVar ssaVar) { + return duplicate(getRegNum(), ssaVar); + } + public RegisterArg duplicate(int regNum, @Nullable SSAVar sVar) { RegisterArg dup = new RegisterArg(regNum, getInitType()); if (sVar != null) { diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/SSAVar.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/SSAVar.java index 6d8a10d7d..c005f9e0c 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/SSAVar.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/SSAVar.java @@ -270,6 +270,7 @@ public class SSAVar { if (!types.isEmpty()) { sb.append(", types: ").append(types); } + sb.append(", assign insn: ").append(getAssign().getParentInsn()); return sb.toString(); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java index 71aff0ad4..98b3b7070 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java @@ -51,6 +51,7 @@ public class InsnNode extends LineAttrNode { } public void setResult(@Nullable RegisterArg res) { + this.result = res; if (res != null) { res.setParentInsn(this); SSAVar ssaVar = res.getSVar(); @@ -58,7 +59,6 @@ public class InsnNode extends LineAttrNode { ssaVar.setAssign(res); } } - this.result = res; } public void addArg(InsnArg arg) { @@ -326,17 +326,9 @@ public class InsnNode extends LineAttrNode { } protected final T copyCommonParams(T copy) { - if (copy.getResult() == null && result != null) { - copy.setResult(result.duplicate()); - } if (copy.getArgsCount() == 0) { for (InsnArg arg : this.getArguments()) { - if (arg.isInsnWrap()) { - InsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn(); - copy.addArg(InsnArg.wrapInsnIntoArg(wrapInsn.copy())); - } else { - copy.addArg(arg.duplicate()); - } + copy.addArg(arg.duplicate()); } } copy.copyAttributesFrom(this); @@ -347,6 +339,17 @@ public class InsnNode extends LineAttrNode { /** * Make copy of InsnNode object. + *

+ * NOTE: can't copy instruction with result argument + * (SSA variable can't be used in two different assigns). + *

+ * Prefer use next methods: + *

    + *
  • {@link #copyWithoutResult()} to explicitly state that result not needed + *
  • {@link #copy(RegisterArg)} to provide new result arg + *
  • {@link #copyWithNewSsaVar(MethodNode)} to make new SSA variable for result arg + *
+ *

*/ public InsnNode copy() { if (this.getClass() != InsnNode.class) { @@ -355,6 +358,49 @@ public class InsnNode extends LineAttrNode { return copyCommonParams(new InsnNode(insnType, getArgsCount())); } + /** + * See {@link #copy()} + */ + @SuppressWarnings("unchecked") + public T copyWithoutResult() { + return (T) copy(); + } + + public InsnNode copyWithoutSsa() { + InsnNode copy = copyWithoutResult(); + if (result != null) { + if (result.getSVar() == null) { + copy.setResult(result.duplicate()); + } else { + throw new JadxRuntimeException("Can't copy if SSA var is set"); + } + } + return copy; + } + + /** + * See {@link #copy()} + */ + public InsnNode copy(RegisterArg newReturnArg) { + InsnNode copy = copy(); + copy.setResult(newReturnArg); + return copy; + } + + /** + * See {@link #copy()} + */ + public InsnNode copyWithNewSsaVar(MethodNode mth) { + RegisterArg result = getResult(); + if (result == null) { + throw new JadxRuntimeException("Result in null"); + } + int regNum = result.getRegNum(); + RegisterArg resDupArg = result.duplicate(regNum, null); + mth.makeNewSVar(resDupArg); + return copy(resDupArg); + } + /** * Fix SSAVar info in register arguments. * Must be used after altering instructions. diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java index 7e30b07af..6aea15ab7 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java @@ -647,7 +647,8 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails, return debugInfoOffset; } - public SSAVar makeNewSVar(int regNum, @NotNull RegisterArg assignArg) { + public SSAVar makeNewSVar(@NotNull RegisterArg assignArg) { + int regNum = assignArg.getRegNum(); return makeNewSVar(regNum, getNextSVarVersion(regNum), assignArg); } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlineVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlineVisitor.java index 4dfe07854..a358b7628 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlineVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlineVisitor.java @@ -93,8 +93,7 @@ public class ConstInlineVisitor extends AbstractVisitor { String s = ((ConstStringNode) insn).getString(); FieldNode f = mth.getParentClass().getConstField(s); if (f == null) { - InsnNode copy = insn.copy(); - copy.setResult(null); + InsnNode copy = insn.copyWithoutResult(); constArg = InsnArg.wrapArg(copy); } else { InsnNode constGet = new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/EnumVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/EnumVisitor.java index edfe3fd4b..a5f8b428c 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/EnumVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/EnumVisitor.java @@ -44,6 +44,7 @@ import jadx.core.dex.visitors.shrink.CodeShrinkVisitor; import jadx.core.utils.BlockInsnPair; import jadx.core.utils.InsnRemover; import jadx.core.utils.InsnUtils; +import jadx.core.utils.Utils; import jadx.core.utils.exceptions.JadxException; import static jadx.core.utils.InsnUtils.checkInsnType; @@ -152,14 +153,13 @@ public class EnumVisitor extends AbstractVisitor { toRemove.add(valuesInitInsn); // all checks complete, perform transform - EnumClassAttr attr = new EnumClassAttr(enumFields.size()); + EnumClassAttr attr = new EnumClassAttr(enumFields); attr.setStaticMethod(classInitMth); - attr.getFields().addAll(enumFields); cls.addAttr(attr); - for (EnumField field : attr.getFields()) { - ConstructorInsn co = field.getConstrInsn(); - FieldNode fieldNode = field.getField(); + for (EnumField enumField : attr.getFields()) { + ConstructorInsn co = enumField.getConstrInsn(); + FieldNode fieldNode = enumField.getField(); // use string arg from the constructor as enum field name String name = getConstString(cls.dex(), co.getArg(0)); @@ -172,13 +172,16 @@ public class EnumVisitor extends AbstractVisitor { if (!co.getClassType().equals(cls.getClassInfo())) { // enum contains additional methods for (ClassNode innerCls : cls.getInnerClasses()) { - processEnumInnerCls(co, field, innerCls); + processEnumInnerCls(co, enumField, innerCls); } } + fieldNode.add(AFlag.DONT_GENERATE); } + List constrInsns = Utils.collectionMap(attr.getFields(), EnumField::getConstrInsn); + InsnRemover.removeAllWithoutUnbind(staticBlock, constrInsns); + valuesField.add(AFlag.DONT_GENERATE); - enumFields.forEach(f -> f.getField().add(AFlag.DONT_GENERATE)); InsnRemover.removeAllAndUnbind(classInitMth, staticBlock, toRemove); if (classInitMth.countInsns() == 0) { classInitMth.add(AFlag.DONT_GENERATE); @@ -242,7 +245,6 @@ public class EnumVisitor extends AbstractVisitor { } toRemove.add(sgetInsn); toRemove.add(sputInsn); - toRemove.add(co); return createEnumFieldByConstructor(cls, enumFieldNode, co); } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/GenericTypesVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/GenericTypesVisitor.java new file mode 100644 index 000000000..f58e0391e --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/GenericTypesVisitor.java @@ -0,0 +1,61 @@ +package jadx.core.dex.visitors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import jadx.core.dex.attributes.nodes.GenericInfoAttr; +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.mods.ConstructorInsn; +import jadx.core.dex.nodes.BlockNode; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.InsnNode; +import jadx.core.dex.nodes.MethodNode; +import jadx.core.dex.visitors.shrink.CodeShrinkVisitor; +import jadx.core.dex.visitors.typeinference.TypeInferenceVisitor; +import jadx.core.utils.exceptions.JadxException; + +@JadxVisitor( + name = "GenericTypesVisitor", + desc = "Fix and apply generic type info", + runAfter = TypeInferenceVisitor.class, + runBefore = { CodeShrinkVisitor.class, MethodInvokeVisitor.class } +) +public class GenericTypesVisitor extends AbstractVisitor { + private static final Logger LOG = LoggerFactory.getLogger(GenericTypesVisitor.class); + + @Override + public void visit(MethodNode mth) throws JadxException { + if (mth.isNoCode()) { + return; + } + for (BlockNode block : mth.getBasicBlocks()) { + for (InsnNode insn : block.getInstructions()) { + if (insn.getType() == InsnType.CONSTRUCTOR) { + attachGenericTypesInfo(mth, (ConstructorInsn) insn); + } + } + } + } + + private void attachGenericTypesInfo(MethodNode mth, ConstructorInsn insn) { + try { + RegisterArg resultArg = insn.getResult(); + if (resultArg == null) { + return; + } + ArgType argType = resultArg.getSVar().getCodeVar().getType(); + if (argType == null || argType.getGenericTypes() == null) { + return; + } + ClassNode cls = mth.dex().resolveClass(insn.getClassType()); + if (cls != null && cls.getGenericTypeParameters().isEmpty()) { + return; + } + insn.addAttr(new GenericInfoAttr(argType.getGenericTypes())); + } catch (Exception e) { + LOG.error("Failed to attach constructor generic info", e); + } + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/MethodInlineVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/MethodInlineVisitor.java index c8cdb960e..993b65fb1 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/MethodInlineVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/MethodInlineVisitor.java @@ -73,7 +73,7 @@ public class MethodInlineVisitor extends AbstractVisitor { if (Consts.DEBUG) { mth.addAttr(AType.COMMENTS, "Removed for inline"); } else { - InsnNode copy = insn.copy(); + InsnNode copy = insn.copyWithoutResult(); // unbind SSA variables from copy instruction List regArgs = new ArrayList<>(); copy.getRegisterArgs(regArgs); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java index 33e487345..09ae30f4a 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java @@ -39,6 +39,7 @@ import jadx.core.dex.visitors.shrink.CodeShrinkVisitor; import jadx.core.utils.BlockUtils; import jadx.core.utils.InsnList; import jadx.core.utils.InsnRemover; +import jadx.core.utils.Utils; import jadx.core.utils.exceptions.JadxRuntimeException; public class SimplifyVisitor extends AbstractVisitor { @@ -393,7 +394,8 @@ public class SimplifyVisitor extends AbstractVisitor { // all check passed removeStringBuilderInsns(mth, toStrInsn, chain); - InsnNode concatInsn = new InsnNode(InsnType.STR_CONCAT, args); + List dupArgs = Utils.collectionMap(args, InsnArg::duplicate); + InsnNode concatInsn = new InsnNode(InsnType.STR_CONCAT, dupArgs); concatInsn.setResult(toStrInsn.getResult()); concatInsn.add(AFlag.SYNTHETIC); concatInsn.copyAttributesFrom(toStrInsn); @@ -499,7 +501,8 @@ public class SimplifyVisitor extends AbstractVisitor { || !wrap.getArg(0).isInsnWrap()) { return null; } - InsnNode get = ((InsnWrapArg) wrap.getArg(0)).getWrapInsn(); + InsnArg getWrap = wrap.getArg(0); + InsnNode get = ((InsnWrapArg) getWrap).getWrapInsn(); InsnType getType = get.getType(); if (getType != InsnType.IGET && getType != InsnType.SGET) { return null; @@ -517,7 +520,8 @@ public class SimplifyVisitor extends AbstractVisitor { return null; } } - InsnArg fArg = InsnArg.wrapArg(get); + InsnArg fArg = getWrap.duplicate(); + InsnRemover.unbindInsn(mth, get); if (insn.getType() == InsnType.IPUT) { InsnRemover.unbindArgUsage(mth, insn.getArg(1)); } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/BlockProcessor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/BlockProcessor.java index f758a1ee1..0f9c929ed 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/BlockProcessor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/BlockProcessor.java @@ -750,7 +750,7 @@ public class BlockProcessor extends AbstractVisitor { first = false; } else { for (InsnNode oldInsn : exitBlock.getInstructions()) { - newRetBlock.getInstructions().add(oldInsn.copy()); + newRetBlock.getInstructions().add(oldInsn.copyWithoutSsa()); } } BlockSplitter.replaceConnection(pred, exitBlock, newRetBlock); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/LoopRegionVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/LoopRegionVisitor.java index 38b3c77ec..ce562c236 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/LoopRegionVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/LoopRegionVisitor.java @@ -255,27 +255,19 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor return false; } List toSkip = new LinkedList<>(); - RegisterArg iterVar = nextCall.getResult(); - if (iterVar == null) { - return false; - } - if (!usedOnlyInLoop(mth, loopRegion, iterVar)) { - return false; - } - if (!assignOnlyInLoop(mth, loopRegion, iterVar)) { - return false; - } - + RegisterArg iterVar; if (nextCall.contains(AFlag.WRAPPED)) { InsnArg wrapArg = BlockUtils.searchWrappedInsnParent(mth, nextCall); if (wrapArg != null && wrapArg.getParentInsn() != null) { InsnNode parentInsn = wrapArg.getParentInsn(); - if (parentInsn.getType() != InsnType.CHECK_CAST) { - if (!fixIterableType(mth, iterableArg, iterVar)) { - return false; - } - parentInsn.replaceArg(wrapArg, iterVar); - } else { + BlockNode block = BlockUtils.getBlockByInsn(mth, parentInsn); + if (block == null) { + return false; + } + if (!RegionUtils.isRegionContainsBlock(loopRegion, block)) { + return false; + } + if (parentInsn.getType() == InsnType.CHECK_CAST) { iterVar = parentInsn.getResult(); if (iterVar == null || !fixIterableType(mth, iterableArg, iterVar)) { return false; @@ -287,12 +279,32 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor // cast not inlined toSkip.add(parentInsn); } + } else { + iterVar = nextCall.getResult(); + if (iterVar == null) { + return false; + } + nextCall.add(AFlag.DONT_GENERATE); + if (!fixIterableType(mth, iterableArg, iterVar)) { + return false; + } + parentInsn.replaceArg(wrapArg, iterVar); } } else { LOG.warn(" checkIterableForEach: Wrapped insn not found: {}, mth: {}", nextCall, mth); return false; } } else { + iterVar = nextCall.getResult(); + if (iterVar == null) { + return false; + } + if (!usedOnlyInLoop(mth, loopRegion, iterVar)) { + return false; + } + if (!assignOnlyInLoop(mth, loopRegion, iterVar)) { + return false; + } toSkip.add(nextCall); } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/shrink/CodeShrinkVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/shrink/CodeShrinkVisitor.java index 40d731ce7..694aa7042 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/shrink/CodeShrinkVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/shrink/CodeShrinkVisitor.java @@ -25,7 +25,7 @@ import jadx.core.utils.exceptions.JadxRuntimeException; @JadxVisitor( name = "CodeShrinkVisitor", - desc = "Inline variables for make code smaller", + desc = "Inline variables to make code smaller", runAfter = { ModVisitor.class } ) public class CodeShrinkVisitor extends AbstractVisitor { @@ -125,12 +125,11 @@ public class CodeShrinkVisitor extends AbstractVisitor { if (useInsn == null || useInsn.contains(AFlag.DONT_GENERATE)) { return false; } - InsnArg replaceArg = InsnArg.wrapInsnIntoArg(assignInsn.copy()); + if (!InsnRemover.removeWithoutUnbind(mth, assignBlock, assignInsn)) { + return false; + } + InsnArg replaceArg = InsnArg.wrapInsnIntoArg(assignInsn); useInsn.replaceArg(useArg, replaceArg); - - assignInsn.add(AFlag.REMOVE); - assignInsn.add(AFlag.DONT_GENERATE); - InsnRemover.remove(mth, assignBlock, assignInsn); return true; } @@ -142,9 +141,11 @@ public class CodeShrinkVisitor extends AbstractVisitor { if (insn.contains(AFlag.FORCE_ASSIGN_INLINE)) { return assignInline(mth, arg, insn, block); } - boolean replaced = arg.wrapInstruction(mth, insn) != null; + // just move instruction into arg, don't unbind/copy/duplicate + InsnArg wrappedArg = arg.wrapInstruction(mth, insn, false); + boolean replaced = wrappedArg != null; if (replaced) { - InsnList.remove(block, insn); + InsnRemover.removeWithoutUnbind(mth, block, insn); } return replaced; } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java index 8451d6031..5dfc501e3 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java @@ -422,7 +422,6 @@ public class SSATransform extends AbstractVisitor { if (resArg.getRegNum() != arg.getRegNum() && !resArg.getSVar().isUsedInPhi()) { markThisArgs(resArg); - parentInsn.add(AFlag.REMOVE); parentInsn.add(AFlag.DONT_GENERATE); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java index a86e3f4c7..7979a3e24 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java @@ -404,7 +404,7 @@ public final class TypeInferenceVisitor extends AbstractVisitor { if (apply) { int regNum = reg.getRegNum(); RegisterArg resultArg = reg.duplicate(regNum, null); - SSAVar newSsaVar = mth.makeNewSVar(regNum, resultArg); + SSAVar newSsaVar = mth.makeNewSVar(resultArg); RegisterArg arg = reg.duplicate(regNum, var); InsnNode moveInsn = new InsnNode(InsnType.MOVE, 1); @@ -476,7 +476,7 @@ public final class TypeInferenceVisitor extends AbstractVisitor { castNode.addArg(bound.getArg()); castNode.setResult(InsnArg.reg(bound.getArg().getRegNum(), bound.getType())); - SSAVar newVar = mth.makeNewSVar(castNode.getResult().getRegNum(), castNode.getResult()); + SSAVar newVar = mth.makeNewSVar(castNode.getResult()); CodeVar codeVar = new CodeVar(); codeVar.setType(bound.getType()); newVar.setCodeVar(codeVar); diff --git a/jadx-core/src/main/java/jadx/core/utils/InsnRemover.java b/jadx-core/src/main/java/jadx/core/utils/InsnRemover.java index 54b944531..623d4a1dc 100644 --- a/jadx-core/src/main/java/jadx/core/utils/InsnRemover.java +++ b/jadx-core/src/main/java/jadx/core/utils/InsnRemover.java @@ -56,8 +56,6 @@ public class InsnRemover { public void addWithoutUnbind(InsnNode insn) { toRemove.add(insn); - insn.add(AFlag.REMOVE); - insn.add(AFlag.DONT_GENERATE); } public void perform() { @@ -69,7 +67,8 @@ public class InsnRemover { remove(mth, remInsn); } } else { - removeAll(mth, instrList, toRemove); + unbindInsns(mth, toRemove); + removeAll(instrList, toRemove); } toRemove.clear(); } @@ -77,10 +76,15 @@ public class InsnRemover { public static void unbindInsn(@Nullable MethodNode mth, InsnNode insn) { unbindAllArgs(mth, insn); unbindResult(mth, insn); - insn.add(AFlag.REMOVE); insn.add(AFlag.DONT_GENERATE); } + public static void unbindInsns(@Nullable MethodNode mth, List insns) { + for (InsnNode insn : insns) { + unbindInsn(mth, insn); + } + } + public static void unbindAllArgs(@Nullable MethodNode mth, InsnNode insn) { for (InsnArg arg : insn.getArguments()) { unbindArgUsage(mth, arg); @@ -154,7 +158,7 @@ public class InsnRemover { // Don't use 'instrList.removeAll(toRemove)' because it will remove instructions by content // and here can be several instructions with same content - private static void removeAll(MethodNode mth, List insns, List toRemove) { + private static void removeAll(List insns, List toRemove) { if (toRemove == null || toRemove.isEmpty()) { return; } @@ -164,7 +168,6 @@ public class InsnRemover { for (int i = 0; i < insnsCount; i++) { if (insns.get(i) == rem) { insns.remove(i); - unbindInsn(mth, rem); found = true; break; } @@ -179,32 +182,58 @@ public class InsnRemover { } public static void remove(MethodNode mth, InsnNode insn) { + if (insn.contains(AFlag.WRAPPED)) { + unbindInsn(mth, insn); + return; + } BlockNode block = BlockUtils.getBlockByInsn(mth, insn); if (block != null) { remove(mth, block, insn); } else { + insn.add(AFlag.DONT_GENERATE); mth.addWarnComment("Not found block with instruction: " + insn); } } public static void remove(MethodNode mth, BlockNode block, InsnNode insn) { unbindInsn(mth, insn); + removeWithoutUnbind(mth, block, insn); + } + + public static boolean removeWithoutUnbind(MethodNode mth, BlockNode block, InsnNode insn) { // remove by pointer (don't use equals) Iterator it = block.getInstructions().iterator(); while (it.hasNext()) { InsnNode ir = it.next(); if (ir == insn) { it.remove(); - return; + return true; } } + mth.addWarnComment("Failed to remove instruction: " + insn + " from block: " + block); + return false; } public static void removeAllAndUnbind(MethodNode mth, BlockNode block, List insns) { - for (InsnNode insn : insns) { - unbindInsn(mth, insn); + unbindInsns(mth, insns); + removeAll(block.getInstructions(), insns); + } + + public static void removeAllWithoutUnbind(BlockNode block, List insns) { + removeAll(block.getInstructions(), insns); + } + + public static void removeAllMarked(MethodNode mth) { + InsnRemover insnRemover = new InsnRemover(mth); + for (BlockNode blockNode : mth.getBasicBlocks()) { + for (InsnNode insn : blockNode.getInstructions()) { + if (insn.contains(AFlag.REMOVE)) { + insnRemover.addWithoutUnbind(insn); + } + } + insnRemover.setBlock(blockNode); + insnRemover.perform(); } - removeAll(mth, block.getInstructions(), insns); } public static void remove(MethodNode mth, BlockNode block, int index) { diff --git a/jadx-core/src/main/java/jadx/core/utils/InsnUtils.java b/jadx-core/src/main/java/jadx/core/utils/InsnUtils.java index 97ec70a1c..d76c86779 100644 --- a/jadx-core/src/main/java/jadx/core/utils/InsnUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/InsnUtils.java @@ -181,7 +181,7 @@ public class InsnUtils { } @Nullable - public static InsnNode checkInsnType(InsnNode insn, InsnType insnType) { + public static InsnNode checkInsnType(@Nullable InsnNode insn, InsnType insnType) { if (insn != null && insn.getType() == insnType) { return insn; } diff --git a/jadx-core/src/main/java/jadx/core/utils/exceptions/JadxRuntimeException.java b/jadx-core/src/main/java/jadx/core/utils/exceptions/JadxRuntimeException.java index ced9878ae..eb56d010c 100644 --- a/jadx-core/src/main/java/jadx/core/utils/exceptions/JadxRuntimeException.java +++ b/jadx-core/src/main/java/jadx/core/utils/exceptions/JadxRuntimeException.java @@ -1,5 +1,11 @@ package jadx.core.utils.exceptions; +import java.util.Arrays; + +import jadx.core.utils.Utils; + +import static jadx.core.codegen.CodeWriter.NL; + public class JadxRuntimeException extends RuntimeException { private static final long serialVersionUID = -7410848445429898248L; @@ -8,6 +14,10 @@ public class JadxRuntimeException extends RuntimeException { super(message); } + public JadxRuntimeException(String... lines) { + super(Utils.listToString(Arrays.asList(lines), NL + " ")); + } + public JadxRuntimeException(String message, Throwable cause) { super(message, cause); }