diff --git a/jadx-core/src/main/java/jadx/core/Consts.java b/jadx-core/src/main/java/jadx/core/Consts.java index e9e4bfdc1..4e46f0cc5 100644 --- a/jadx-core/src/main/java/jadx/core/Consts.java +++ b/jadx-core/src/main/java/jadx/core/Consts.java @@ -8,6 +8,7 @@ public class Consts { public static final boolean DEBUG_OVERLOADED_CASTS = false; public static final boolean DEBUG_EXC_HANDLERS = false; public static final boolean DEBUG_FINALLY = false; + public static final boolean DEBUG_ATTRIBUTES = false; public static final String CLASS_OBJECT = "java.lang.Object"; public static final String CLASS_STRING = "java.lang.String"; diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/AttrNode.java b/jadx-core/src/main/java/jadx/core/dex/attributes/AttrNode.java index d1f411d54..5666c8b8c 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/AttrNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/AttrNode.java @@ -2,9 +2,13 @@ package jadx.core.dex.attributes; import java.util.List; +import jadx.api.CommentsLevel; import jadx.api.plugins.input.data.annotations.IAnnotation; import jadx.api.plugins.input.data.attributes.IJadxAttrType; import jadx.api.plugins.input.data.attributes.IJadxAttribute; +import jadx.core.Consts; +import jadx.core.dex.attributes.nodes.JadxCommentsAttr; +import jadx.core.utils.Utils; public abstract class AttrNode implements IAttributeNode { @@ -15,11 +19,18 @@ public abstract class AttrNode implements IAttributeNode { @Override public void add(AFlag flag) { initStorage().add(flag); + if (Consts.DEBUG_ATTRIBUTES) { + addDebugComment("Add flag " + flag + " at " + Utils.currentStackTrace(2)); + } } @Override public void addAttr(IJadxAttribute attr) { initStorage().add(attr); + if (Consts.DEBUG_ATTRIBUTES) { + addDebugComment("Add attribute " + attr.getClass().getSimpleName() + + ": " + attr + " at " + Utils.currentStackTrace(2)); + } } @Override @@ -30,6 +41,9 @@ public abstract class AttrNode implements IAttributeNode { @Override public void addAttr(IJadxAttrType> type, T obj) { initStorage().add(type, obj); + if (Consts.DEBUG_ATTRIBUTES) { + addDebugComment("Add attribute " + obj + " at " + Utils.currentStackTrace(2)); + } } public void addAttr(IJadxAttrType> type, List list) { @@ -151,4 +165,13 @@ public abstract class AttrNode implements IAttributeNode { public boolean isAttrStorageEmpty() { return storage.isEmpty(); } + + private void addDebugComment(String msg) { + JadxCommentsAttr commentsAttr = get(AType.JADX_COMMENTS); + if (commentsAttr == null) { + commentsAttr = new JadxCommentsAttr(); + initStorage().add(commentsAttr); + } + commentsAttr.add(CommentsLevel.DEBUG, msg); + } } diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/JadxCommentsAttr.java b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/JadxCommentsAttr.java index a2eb1f22a..613bc7d4a 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/JadxCommentsAttr.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/JadxCommentsAttr.java @@ -11,10 +11,25 @@ import jadx.api.CommentsLevel; import jadx.api.plugins.input.data.attributes.IJadxAttrType; import jadx.api.plugins.input.data.attributes.IJadxAttribute; import jadx.core.dex.attributes.AType; +import jadx.core.dex.attributes.IAttributeNode; import jadx.core.utils.Utils; public class JadxCommentsAttr implements IJadxAttribute { + public static void add(IAttributeNode node, CommentsLevel level, String comment) { + initFor(node).add(level, comment); + } + + private static JadxCommentsAttr initFor(IAttributeNode node) { + JadxCommentsAttr currentAttr = node.get(AType.JADX_COMMENTS); + if (currentAttr != null) { + return currentAttr; + } + JadxCommentsAttr newAttr = new JadxCommentsAttr(); + node.addAttr(newAttr); + return newAttr; + } + private final Map> comments = new EnumMap<>(CommentsLevel.class); public void add(CommentsLevel level, String comment) { 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 1a7a81b08..b54f73ffb 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 @@ -20,7 +20,7 @@ public abstract class NotificationAttrNode extends LineAttrNode implements ICode public void addWarn(String warn) { ErrorsCounter.warning(this, warn); - initCommentsAttr().add(CommentsLevel.WARN, warn); + JadxCommentsAttr.add(this, CommentsLevel.WARN, warn); this.add(AFlag.INCONSISTENT_CODE); } @@ -29,32 +29,23 @@ public abstract class NotificationAttrNode extends LineAttrNode implements ICode } public void addWarnComment(String warn) { - initCommentsAttr().add(CommentsLevel.WARN, warn); + JadxCommentsAttr.add(this, CommentsLevel.WARN, warn); } public void addWarnComment(String warn, Throwable exc) { String commentStr = warn + ICodeWriter.NL + Utils.getStackTrace(exc); - initCommentsAttr().add(CommentsLevel.WARN, commentStr); + JadxCommentsAttr.add(this, CommentsLevel.WARN, commentStr); } public void addInfoComment(String commentStr) { - initCommentsAttr().add(CommentsLevel.INFO, commentStr); + JadxCommentsAttr.add(this, CommentsLevel.INFO, commentStr); } public void addDebugComment(String commentStr) { - initCommentsAttr().add(CommentsLevel.DEBUG, commentStr); + JadxCommentsAttr.add(this, CommentsLevel.DEBUG, commentStr); } public CommentsLevel getCommentsLevel() { return this.root().getArgs().getCommentsLevel(); } - - private JadxCommentsAttr initCommentsAttr() { - JadxCommentsAttr commentsAttr = this.get(AType.JADX_COMMENTS); - if (commentsAttr == null) { - commentsAttr = new JadxCommentsAttr(); - this.addAttr(commentsAttr); - } - return commentsAttr; - } } diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/PhiListAttr.java b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/PhiListAttr.java index e7e478c40..d495d30a7 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/PhiListAttr.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/PhiListAttr.java @@ -7,6 +7,7 @@ import jadx.api.ICodeWriter; import jadx.api.plugins.input.data.attributes.IJadxAttribute; import jadx.core.dex.attributes.AType; import jadx.core.dex.instructions.PhiInsn; +import jadx.core.dex.instructions.args.RegisterArg; public class PhiListAttr implements IJadxAttribute { @@ -24,12 +25,15 @@ public class PhiListAttr implements IJadxAttribute { @Override public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("PHI: "); + sb.append("PHI:"); for (PhiInsn phiInsn : list) { - sb.append('r').append(phiInsn.getResult().getRegNum()).append(' '); + RegisterArg resArg = phiInsn.getResult(); + if (resArg != null) { + sb.append(" r").append(resArg.getRegNum()); + } } for (PhiInsn phiInsn : list) { - sb.append(ICodeWriter.NL).append(" ").append(phiInsn).append(' ').append(phiInsn.getAttributesString()); + sb.append(ICodeWriter.NL).append(" ").append(phiInsn); } return sb.toString(); } 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 23ee39515..32ca36d2d 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 @@ -117,11 +117,20 @@ public class ArithNode extends InsnNode { @Override public String toString() { - return InsnUtils.formatOffset(offset) + ": " - + InsnUtils.insnTypeToString(insnType) - + getResult() + " = " - + getArg(0) + ' ' - + op.getSymbol() + ' ' - + getArg(1); + StringBuilder sb = new StringBuilder(); + sb.append(InsnUtils.formatOffset(offset)); + sb.append(": ARITH "); + if (contains(AFlag.ARITH_ONEARG)) { + sb.append(getArg(0)).append(' ').append(op.getSymbol()).append("= ").append(getArg(1)); + } else { + RegisterArg result = getResult(); + if (result != null) { + sb.append(result).append(" = "); + } + sb.append(getArg(0)).append(' ').append(op.getSymbol()).append(' ').append(getArg(1)); + } + + appendAttributes(sb); + return sb.toString(); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/IfNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/IfNode.java index 5154c9ecb..d09da7bba 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/IfNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/IfNode.java @@ -149,6 +149,7 @@ public class IfNode extends GotoNode { return InsnUtils.formatOffset(offset) + ": " + InsnUtils.insnTypeToString(insnType) + getArg(0) + ' ' + op.getSymbol() + ' ' + getArg(1) - + " -> " + (thenBlock != null ? thenBlock : InsnUtils.formatOffset(target)); + + " -> " + (thenBlock != null ? thenBlock : InsnUtils.formatOffset(target)) + + attributesString(); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomNode.java index c943941f1..71de61937 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomNode.java @@ -123,6 +123,7 @@ public class InvokeCustomNode extends InvokeNode { sb.append(getResult()).append(" = "); } appendArgs(sb); + appendAttributes(sb); sb.append("\n handle type: ").append(handleType); sb.append("\n lambda: ").append(implMthInfo); sb.append("\n call insn: ").append(callInsn); diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomRawNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomRawNode.java index a8c847cdf..79de99fa7 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomRawNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeCustomRawNode.java @@ -92,6 +92,7 @@ public class InvokeCustomRawNode extends InvokeNode { if (!appendArgs(sb)) { sb.append('\n'); } + appendAttributes(sb); sb.append(" call-site: \n ").append(Utils.listToString(callSiteValues, "\n ")).append('\n'); return sb.toString(); } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeNode.java index 32be6a080..bc32e332b 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeNode.java @@ -103,6 +103,6 @@ public class InvokeNode extends BaseInvokeNode { @Override public String toString() { - return super.toString() + " type: " + type + " call: " + mth; + return baseString() + " type: " + type + " call: " + mth + attributesString(); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/InvokePolymorphicNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/InvokePolymorphicNode.java index 08dd90d9e..115eb1a48 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/InvokePolymorphicNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/InvokePolymorphicNode.java @@ -59,6 +59,7 @@ public class InvokePolymorphicNode extends InvokeNode { if (!appendArgs(sb)) { sb.append('\n'); } + appendAttributes(sb); sb.append(" base: ").append(baseCallRef).append('\n'); sb.append(" proto: ").append(proto).append('\n'); return sb.toString(); diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/PhiInsn.java b/jadx-core/src/main/java/jadx/core/dex/instructions/PhiInsn.java index 153f657db..db5f89f7f 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/PhiInsn.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/PhiInsn.java @@ -14,7 +14,6 @@ import jadx.core.dex.instructions.args.SSAVar; import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.InsnNode; import jadx.core.utils.InsnRemover; -import jadx.core.utils.Utils; import jadx.core.utils.exceptions.JadxRuntimeException; public final class PhiInsn extends InsnNode { @@ -134,7 +133,6 @@ public final class PhiInsn extends InsnNode { @Override public String toString() { - return "PHI: " + getResult() + " = " + Utils.listToString(getArguments()) - + " binds: " + blockBinds; + return baseString() + " binds: " + blockBinds + attributesString(); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/SwitchData.java b/jadx-core/src/main/java/jadx/core/dex/instructions/SwitchData.java index b96c76af8..911686215 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/SwitchData.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/SwitchData.java @@ -44,6 +44,7 @@ public class SwitchData extends InsnNode { sb.append(keys[i]).append("->").append(InsnUtils.formatOffset(targets[i])).append(", "); } sb.append('}'); + appendAttributes(sb); return sb.toString(); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/SwitchInsn.java b/jadx-core/src/main/java/jadx/core/dex/instructions/SwitchInsn.java index 423ff4ad9..4d773d6f3 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/SwitchInsn.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/SwitchInsn.java @@ -103,7 +103,7 @@ public class SwitchInsn extends TargetInsnNode { @Override public String toString() { StringBuilder sb = new StringBuilder(); - sb.append(super.toString()); + sb.append(baseString()); if (switchData == null) { sb.append("no payload"); } else { @@ -129,6 +129,7 @@ public class SwitchInsn extends TargetInsnNode { } } } + appendAttributes(sb); return sb.toString(); } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/SSAVar.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/SSAVar.java index f0e3c250f..afb4f2c94 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 @@ -65,7 +65,13 @@ public class SSAVar { } public void setAssign(@NotNull RegisterArg assign) { - this.assign = assign; + RegisterArg oldAssign = this.assign; + if (oldAssign == null) { + this.assign = assign; + } else if (oldAssign != assign) { + oldAssign.resetSSAVar(); + this.assign = assign; + } } public List getUseList() { diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/mods/TernaryInsn.java b/jadx-core/src/main/java/jadx/core/dex/instructions/mods/TernaryInsn.java index 36e0c703c..717a039ae 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/mods/TernaryInsn.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/mods/TernaryInsn.java @@ -96,7 +96,7 @@ public final class TernaryInsn extends InsnNode { @Override public String toString() { - return InsnUtils.formatOffset(offset) + ": TERNARY" + return InsnUtils.formatOffset(offset) + ": TERNARY " + getResult() + " = (" + condition + ") ? " + getArg(0) + " : " + getArg(1); } } 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 a3c46e076..3d1ba4925 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 @@ -472,7 +472,11 @@ public class InsnNode extends LineAttrNode { public void rebindArgs() { RegisterArg resArg = getResult(); if (resArg != null) { - resArg.getSVar().setAssign(resArg); + SSAVar ssaVar = resArg.getSVar(); + if (ssaVar == null) { + throw new JadxRuntimeException("No SSA var for result arg: " + resArg + " from " + resArg.getParentInsn()); + } + ssaVar.setAssign(resArg); } for (InsnArg arg : getArguments()) { if (arg instanceof RegisterArg) { @@ -560,16 +564,36 @@ public class InsnNode extends LineAttrNode { return true; } - @Override - public String toString() { + protected String attributesString() { StringBuilder sb = new StringBuilder(); - sb.append(InsnUtils.formatOffset(offset)); - sb.append(": "); - sb.append(InsnUtils.insnTypeToString(insnType)); + appendAttributes(sb); + return sb.toString(); + } + + protected void appendAttributes(StringBuilder sb) { + if (!isAttrStorageEmpty()) { + sb.append(' ').append(getAttributesString()); + } + if (getSourceLine() != 0) { + sb.append(" (LINE:").append(getSourceLine()).append(')'); + } + } + + protected String baseString() { + StringBuilder sb = new StringBuilder(); + if (offset != -1) { + sb.append(InsnUtils.formatOffset(offset)).append(": "); + } + sb.append(insnType).append(' '); if (result != null) { sb.append(result).append(" = "); } appendArgs(sb); return sb.toString(); } + + @Override + public String toString() { + return baseString() + attributesString(); + } } 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 011e61d7f..bad9e5ce8 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 @@ -37,6 +37,7 @@ import jadx.core.dex.instructions.args.SSAVar; import jadx.core.dex.nodes.utils.TypeUtils; import jadx.core.dex.regions.Region; import jadx.core.dex.trycatch.ExceptionHandler; +import jadx.core.dex.visitors.InitCodeVariables; import jadx.core.utils.Utils; import jadx.core.utils.exceptions.DecodeException; import jadx.core.utils.exceptions.JadxRuntimeException; @@ -519,6 +520,24 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails, return argsStartReg; } + /** + * Create new fake register arg. + */ + public RegisterArg makeSyntheticRegArg(ArgType type) { + RegisterArg arg = InsnArg.reg(0, type); + arg.add(AFlag.SYNTHETIC); + SSAVar ssaVar = makeNewSVar(arg); + InitCodeVariables.initCodeVar(ssaVar); + ssaVar.setType(type); + return arg; + } + + public RegisterArg makeSyntheticRegArg(ArgType type, String name) { + RegisterArg arg = makeSyntheticRegArg(type); + arg.setName(name); + return arg; + } + 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/nodes/utils/MethodUtils.java b/jadx-core/src/main/java/jadx/core/dex/nodes/utils/MethodUtils.java index 4b2438955..1603ce9e7 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/utils/MethodUtils.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/utils/MethodUtils.java @@ -10,10 +10,12 @@ import jadx.core.clsp.ClspMethod; import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.nodes.MethodBridgeAttr; import jadx.core.dex.attributes.nodes.MethodOverrideAttr; +import jadx.core.dex.attributes.nodes.SkipMethodArgsAttr; import jadx.core.dex.info.ClassInfo; import jadx.core.dex.info.MethodInfo; import jadx.core.dex.instructions.BaseInvokeNode; import jadx.core.dex.instructions.args.ArgType; +import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.IMethodDetails; import jadx.core.dex.nodes.MethodNode; @@ -54,6 +56,19 @@ public class MethodUtils { return null; } + public boolean isSkipArg(BaseInvokeNode invokeNode, InsnArg arg) { + MethodNode mth = resolveMethod(invokeNode); + if (mth == null) { + return false; + } + SkipMethodArgsAttr skipArgsAttr = mth.get(AType.SKIP_MTH_ARGS); + if (skipArgsAttr == null) { + return false; + } + int argIndex = invokeNode.getArgIndex(arg); + return skipArgsAttr.isSkip(argIndex); + } + /** * Search methods with same name and args count in class hierarchy starting from {@code startCls} * Beware {@code startCls} can be different from {@code mthInfo.getDeclClass()} diff --git a/jadx-core/src/main/java/jadx/core/dex/regions/loops/ForEachLoop.java b/jadx-core/src/main/java/jadx/core/dex/regions/loops/ForEachLoop.java index afae84c82..d20bd6dd0 100644 --- a/jadx-core/src/main/java/jadx/core/dex/regions/loops/ForEachLoop.java +++ b/jadx-core/src/main/java/jadx/core/dex/regions/loops/ForEachLoop.java @@ -13,7 +13,7 @@ public final class ForEachLoop extends LoopType { public ForEachLoop(RegisterArg varArg, InsnArg iterableArg) { // store for-each args in fake instructions to // save code semantics and allow args manipulations like args inlining - varArgInsn = new InsnNode(InsnType.REGION_ARG, 1); + varArgInsn = new InsnNode(InsnType.REGION_ARG, 0); varArgInsn.add(AFlag.DONT_INLINE); varArgInsn.setResult(varArg.duplicate()); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java index 88bc06da2..563917177 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java @@ -79,8 +79,9 @@ public class ClassModifier extends AbstractVisitor { boolean inline = cls.isAnonymous(); if (inline || cls.getClassInfo().isInner()) { for (FieldNode field : cls.getFields()) { - if (field.getAccessFlags().isSynthetic() && field.getType().isObject()) { - ClassInfo clsInfo = ClassInfo.fromType(cls.root(), field.getType()); + ArgType fldType = field.getType(); + if (field.getAccessFlags().isSynthetic() && fldType.isObject() && !fldType.isGenericType()) { + ClassInfo clsInfo = ClassInfo.fromType(cls.root(), fldType); ClassNode fieldsCls = cls.root().resolveClass(clsInfo); ClassInfo parentClass = cls.getClassInfo().getParentClass(); if (fieldsCls != null diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ConstructorVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ConstructorVisitor.java index 7af5d59cb..73615a03a 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ConstructorVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ConstructorVisitor.java @@ -1,7 +1,5 @@ package jadx.core.dex.visitors; -import java.util.ArrayList; - import org.jetbrains.annotations.Nullable; import jadx.core.codegen.TypeGen; @@ -71,7 +69,7 @@ public class ConstructorVisitor extends AbstractVisitor { co.inheritMetadata(inv); RegisterArg instanceArg = ((RegisterArg) inv.getArg(0)); - InsnNode newInstInsn = null; + instanceArg.getSVar().removeUse(instanceArg); if (co.isNewInstance()) { InsnNode assignInsn = instanceArg.getAssignInsn(); if (assignInsn != null) { @@ -79,8 +77,9 @@ public class ConstructorVisitor extends AbstractVisitor { // arg already used in another constructor instruction mth.add(AFlag.RERUN_SSA_TRANSFORM); } else { - newInstInsn = removeAssignChain(mth, assignInsn, remover, InsnType.NEW_INSTANCE); + InsnNode newInstInsn = removeAssignChain(mth, assignInsn, remover, InsnType.NEW_INSTANCE); if (newInstInsn != null) { + co.inheritMetadata(newInstInsn); newInstInsn.add(AFlag.REMOVE); remover.addWithoutUnbind(newInstInsn); } @@ -89,23 +88,7 @@ public class ConstructorVisitor extends AbstractVisitor { // convert instance arg from 'use' to 'assign' co.setResult(instanceArg.duplicate()); } - instanceArg.getSVar().removeUse(instanceArg); - co.rebindArgs(); - if (co.isNewInstance() && newInstInsn != null) { - RegisterArg instArg = newInstInsn.getResult(); - RegisterArg resultArg = co.getResult(); - if (!resultArg.equals(instArg)) { - // replace all usages of 'instArg' with result of this constructor instruction - for (RegisterArg useArg : new ArrayList<>(instArg.getSVar().getUseList())) { - InsnNode parentInsn = useArg.getParentInsn(); - if (parentInsn != null) { - parentInsn.replaceArg(useArg, resultArg.duplicate()); - } - } - } - co.inheritMetadata(newInstInsn); - } ConstructorInsn replace = processConstructor(mth, co); if (replace != null) { remover.addAndUnbind(co); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java index 8840addea..8b333aab9 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java @@ -284,15 +284,11 @@ public class DotGraphVisitor extends AbstractVisitor { private String insertInsns(MethodNode mth, IBlock block) { if (rawInsn) { - StringBuilder str = new StringBuilder(); + StringBuilder sb = new StringBuilder(); for (InsnNode insn : block.getInstructions()) { - str.append(escape(insn + " " + insn.getAttributesString())); - if (insn.getSourceLine() != 0) { - str.append(" (LINE:").append(insn.getSourceLine()).append(')'); - } - str.append(NL); + sb.append(escape(insn)).append(NL); } - return str.toString(); + return sb.toString(); } else { ICodeWriter code = new SimpleCodeWriter(); List instructions = block.getInstructions(); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/InlineMethods.java b/jadx-core/src/main/java/jadx/core/dex/visitors/InlineMethods.java index e43878ca6..f87d8684e 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/InlineMethods.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/InlineMethods.java @@ -15,10 +15,8 @@ import jadx.core.dex.instructions.BaseInvokeNode; import jadx.core.dex.instructions.IndexInsnNode; import jadx.core.dex.instructions.InsnType; 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.RegisterArg; -import jadx.core.dex.instructions.args.SSAVar; import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.FieldNode; @@ -84,7 +82,7 @@ public class InlineMethods extends AbstractVisitor { inlCopy.setResult(resultArg.duplicate()); } else if (isAssignNeeded(mia.getInsn(), insn, callMth)) { // add fake result to make correct java expression (see test TestGetterInlineNegative) - inlCopy.setResult(makeFakeArg(mth, callMth.getReturnType(), "unused")); + inlCopy.setResult(mth.makeSyntheticRegArg(callMth.getReturnType(), "unused")); } if (!callMth.getMethodInfo().getArgumentsTypes().isEmpty()) { // remap args @@ -135,15 +133,6 @@ public class InlineMethods extends AbstractVisitor { return !callMthNode.isVoidReturn(); } - private RegisterArg makeFakeArg(MethodNode mth, ArgType varType, String name) { - RegisterArg fakeArg = RegisterArg.reg(0, varType); - SSAVar ssaVar = mth.makeNewSVar(fakeArg); - InitCodeVariables.initCodeVar(ssaVar); - fakeArg.setName(name); - ssaVar.setType(varType); - return fakeArg; - } - private void updateUsageInfo(MethodNode mth, MethodNode inlinedMth, InsnNode insn) { inlinedMth.getUseIn().remove(mth); insn.visitInsns(innerInsn -> { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java index b604a0c0a..d04c5c2aa 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java @@ -531,7 +531,7 @@ public class ModVisitor extends AbstractVisitor { List list = insn.getLiteralArgs(elType); InsnNode filledArr = new FilledNewArrayNode(elType, list.size()); - filledArr.setResult(newArrayNode.getResult()); + filledArr.setResult(newArrayNode.getResult().duplicate()); for (LiteralArg arg : list) { FieldNode f = mth.getParentClass().getConstFieldByLiteralArg(arg); if (f != null) { @@ -539,7 +539,7 @@ public class ModVisitor extends AbstractVisitor { filledArr.addArg(InsnArg.wrapArg(fGet)); f.addUseIn(mth); } else { - filledArr.addArg(arg); + filledArr.addArg(arg.duplicate()); } } return filledArr; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ReSugarCode.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ReSugarCode.java index c88be6c9f..9d581557a 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ReSugarCode.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ReSugarCode.java @@ -31,6 +31,7 @@ import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.RootNode; 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.InsnUtils; @@ -165,6 +166,9 @@ public class ReSugarCode extends AbstractVisitor { // checks complete, apply InsnNode filledArr = new FilledNewArrayNode(elemType, len); filledArr.setResult(arrArg.duplicate()); + filledArr.copyAttributesFrom(newArrayInsn); + filledArr.inheritMetadata(newArrayInsn); + filledArr.setOffset(newArrayInsn.getOffset()); long prevIndex = -1; for (Map.Entry entry : arrPuts.entrySet()) { @@ -185,6 +189,7 @@ public class ReSugarCode extends AbstractVisitor { InsnNode lastPut = arrPuts.get(arrPuts.lastKey()); int replaceIndex = InsnList.getIndex(instructions, lastPut); instructions.set(replaceIndex, filledArr); + BlockUtils.replaceInsn(mth, lastPut, filledArr); return true; } 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 4bee88c1a..4ff834664 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 @@ -113,7 +113,7 @@ public class SimplifyVisitor extends AbstractVisitor { InsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn(); InsnNode replaceInsn = simplifyInsn(mth, wrapInsn, insn); if (replaceInsn != null) { - arg.wrapInstruction(mth, replaceInsn); + arg.wrapInstruction(mth, replaceInsn, false); InsnRemover.unbindInsn(mth, wrapInsn); changed = true; } @@ -406,17 +406,18 @@ public class SimplifyVisitor extends AbstractVisitor { } // all check passed - removeStringBuilderInsns(mth, toStrInsn, chain); - List dupArgs = Utils.collectionMap(args, InsnArg::duplicate); List simplifiedArgs = concatConstArgs(dupArgs); InsnNode concatInsn = new InsnNode(InsnType.STR_CONCAT, simplifiedArgs); - concatInsn.setResult(toStrInsn.getResult()); concatInsn.add(AFlag.SYNTHETIC); + if (toStrInsn.getResult() == null && !toStrInsn.contains(AFlag.WRAPPED)) { + // string concat without assign to variable will cause compilation error + concatInsn.setResult(mth.makeSyntheticRegArg(ArgType.STRING)); + } else { + concatInsn.setResult(toStrInsn.getResult()); + } concatInsn.copyAttributesFrom(toStrInsn); - concatInsn.remove(AFlag.DONT_GENERATE); - concatInsn.remove(AFlag.REMOVE); - checkResult(mth, concatInsn); + removeStringBuilderInsns(mth, toStrInsn, chain); return concatInsn; } catch (Exception e) { mth.addWarnComment("String concatenation convert failed", e); @@ -485,17 +486,6 @@ public class SimplifyVisitor extends AbstractVisitor { return null; } - /* String concat without assign to variable will cause compilation error */ - private static void checkResult(MethodNode mth, InsnNode concatInsn) { - if (concatInsn.getResult() == null) { - RegisterArg resArg = InsnArg.reg(0, ArgType.STRING); - SSAVar ssaVar = mth.makeNewSVar(resArg); - InitCodeVariables.initCodeVar(ssaVar); - ssaVar.setType(ArgType.STRING); - concatInsn.setResult(resArg); - } - } - /** * Remove and unbind all instructions with StringBuilder */ 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 9c433bca6..bcd03bae1 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 @@ -210,11 +210,24 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor return null; } RegisterArg iterVar = arrGetInsn.getResult(); - if (iterVar == null) { - return null; - } - if (!usedOnlyInLoop(mth, loopRegion, iterVar)) { - return null; + if (iterVar != null) { + if (!usedOnlyInLoop(mth, loopRegion, iterVar)) { + return null; + } + } else { + if (!arrGetInsn.contains(AFlag.WRAPPED)) { + return null; + } + // create new variable and replace wrapped insn + InsnArg wrapArg = BlockUtils.searchWrappedInsnParent(mth, arrGetInsn); + if (wrapArg == null || wrapArg.getParentInsn() == null) { + mth.addWarnComment("checkArrayForEach: Wrapped insn not found: " + arrGetInsn); + return null; + } + iterVar = mth.makeSyntheticRegArg(wrapArg.getType()); + InsnNode parentInsn = wrapArg.getParentInsn(); + parentInsn.replaceArg(wrapArg, iterVar.duplicate()); + parentInsn.rebindArgs(); } // array for each loop confirmed @@ -224,16 +237,6 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor arrGetInsn.add(AFlag.DONT_GENERATE); compare.getInsn().add(AFlag.DONT_GENERATE); - if (arrGetInsn.contains(AFlag.WRAPPED)) { - InsnArg wrapArg = BlockUtils.searchWrappedInsnParent(mth, arrGetInsn); - if (wrapArg != null && wrapArg.getParentInsn() != null) { - InsnNode parentInsn = wrapArg.getParentInsn(); - parentInsn.replaceArg(wrapArg, iterVar.duplicate()); - parentInsn.rebindArgs(); - } else { - LOG.debug(" checkArrayForEach: Wrapped insn not found: {}, mth: {}", arrGetInsn, mth); - } - } ForEachLoop forEachLoop = new ForEachLoop(iterVar, len.getArg(0)); forEachLoop.injectFakeInsns(loopRegion); if (InsnUtils.dontGenerateIfNotUsed(len)) { @@ -327,6 +330,7 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor assignInsn.getResult().add(AFlag.DONT_GENERATE); for (InsnNode insnNode : toSkip) { + insnNode.setResult(null); insnNode.add(AFlag.DONT_GENERATE); } for (RegisterArg itArg : itUseList) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/TernaryMod.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/TernaryMod.java index 38b771468..8bcbee0df 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/TernaryMod.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/TernaryMod.java @@ -287,7 +287,7 @@ public class TernaryMod extends AbstractRegionVisitor implements IRegionIterativ } RegisterArg otherArg = null; for (InsnArg arg : phiInsn.getArguments()) { - if (arg != resArg && arg instanceof RegisterArg) { + if (!resArg.sameRegAndSVar(arg)) { otherArg = (RegisterArg) arg; break; } @@ -326,15 +326,16 @@ public class TernaryMod extends AbstractRegionVisitor implements IRegionIterativ } elseArg = InsnArg.wrapInsnIntoArg(elseAssign); } else { - elseArg = otherArg; + elseArg = otherArg.duplicate(); } - TernaryInsn ternInsn = new TernaryInsn(ifRegion.getCondition(), - phiInsn.getResult(), InsnArg.wrapInsnIntoArg(insn), elseArg); + InsnArg thenArg = InsnArg.wrapInsnIntoArg(insn); + RegisterArg resultArg = phiInsn.getResult().duplicate(); + TernaryInsn ternInsn = new TernaryInsn(ifRegion.getCondition(), resultArg, thenArg, elseArg); ternInsn.simplifyCondition(); + InsnRemover.unbindAllArgs(mth, phiInsn); InsnRemover.unbindResult(mth, insn); InsnList.remove(block, insn); - InsnRemover.unbindAllArgs(mth, phiInsn); header.getInstructions().clear(); ternInsn.rebindArgs(); header.getInstructions().add(ternInsn); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/variables/ProcessVariables.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/variables/ProcessVariables.java index d54efc374..b4fdd868a 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/variables/ProcessVariables.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/variables/ProcessVariables.java @@ -8,12 +8,14 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.nodes.DeclareVariablesAttr; +import jadx.core.dex.instructions.BaseInvokeNode; import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.CodeVar; @@ -30,6 +32,7 @@ import jadx.core.dex.visitors.regions.AbstractRegionVisitor; import jadx.core.dex.visitors.regions.DepthRegionTraversal; import jadx.core.dex.visitors.typeinference.TypeCompare; import jadx.core.dex.visitors.typeinference.TypeCompareEnum; +import jadx.core.utils.ListUtils; import jadx.core.utils.RegionUtils; import jadx.core.utils.Utils; import jadx.core.utils.exceptions.JadxException; @@ -72,15 +75,59 @@ public class ProcessVariables extends AbstractVisitor { public void processBlock(MethodNode mth, IBlock container) { for (InsnNode insn : container.getInstructions()) { RegisterArg resultArg = insn.getResult(); - if (resultArg != null) { - SSAVar ssaVar = resultArg.getSVar(); - if (ssaVar.getUseList().isEmpty() && insn.canRemoveResult()) { + if (resultArg == null) { + continue; + } + SSAVar ssaVar = resultArg.getSVar(); + if (isVarUnused(mth, ssaVar)) { + boolean remove = false; + if (insn.canRemoveResult()) { + // remove unused result + remove = true; + } else if (insn.isConstInsn()) { + // remove whole insn + insn.add(AFlag.REMOVE); + insn.add(AFlag.DONT_GENERATE); + remove = true; + } + if (remove) { insn.setResult(null); mth.removeSVar(ssaVar); + for (RegisterArg arg : ssaVar.getUseList()) { + arg.resetSSAVar(); + } } } } } + + private boolean isVarUnused(MethodNode mth, @Nullable SSAVar ssaVar) { + if (ssaVar == null) { + return true; + } + List useList = ssaVar.getUseList(); + if (useList.isEmpty()) { + return true; + } + if (ssaVar.isUsedInPhi()) { + return false; + } + return ListUtils.allMatch(useList, arg -> isArgUnused(mth, arg)); + } + + private boolean isArgUnused(MethodNode mth, RegisterArg arg) { + if (arg.contains(AFlag.REMOVE) || arg.contains(AFlag.SKIP_ARG)) { + return true; + } + InsnNode parentInsn = arg.getParentInsn(); + if (parentInsn instanceof BaseInvokeNode + && mth.root().getMethodUtils().isSkipArg(((BaseInvokeNode) parentInsn), arg)) { + arg.add(AFlag.DONT_GENERATE); + arg.add(AFlag.REMOVE); + return true; + } + return false; + } }); } 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 9a21aafda..75ba37b83 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 @@ -18,9 +18,12 @@ 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.trycatch.CatchAttr; +import jadx.core.dex.trycatch.ExcHandlerAttr; import jadx.core.dex.visitors.AbstractVisitor; import jadx.core.dex.visitors.JadxVisitor; import jadx.core.dex.visitors.blocks.BlockProcessor; +import jadx.core.utils.BlockUtils; import jadx.core.utils.InsnList; import jadx.core.utils.InsnRemover; import jadx.core.utils.exceptions.JadxException; @@ -58,20 +61,10 @@ public class SSATransform extends AbstractVisitor { placePhi(mth, i, la); } renameVariables(mth); - fixLastAssignInTry(mth); removeBlockerInsns(mth); markThisArgs(mth.getThisArg()); - - boolean repeatFix; - int k = 0; - do { - repeatFix = fixUselessPhi(mth); - if (k++ > 50) { - throw new JadxRuntimeException("Phi nodes fix limit reached!"); - } - } while (repeatFix); - + tryToFixUselessPhi(mth); hidePhiInsns(mth); removeUnusedInvokeResults(mth); } @@ -208,29 +201,42 @@ public class SSATransform extends AbstractVisitor { private static void fixLastAssignInTry(MethodNode mth) { for (BlockNode block : mth.getBasicBlocks()) { PhiListAttr phiList = block.get(AType.PHI_LIST); - if (phiList != null && block.contains(AType.EXC_HANDLER)) { - for (PhiInsn phi : phiList.getList()) { - fixPhiInTryCatch(phi); + if (phiList != null) { + ExcHandlerAttr handlerAttr = block.get(AType.EXC_HANDLER); + if (handlerAttr != null) { + for (PhiInsn phi : phiList.getList()) { + fixPhiInTryCatch(mth, phi, handlerAttr); + } } } } } - private static void fixPhiInTryCatch(PhiInsn phi) { + private static void fixPhiInTryCatch(MethodNode mth, PhiInsn phi, ExcHandlerAttr handlerAttr) { int argsCount = phi.getArgsCount(); int k = 0; while (k < argsCount) { RegisterArg arg = phi.getArg(k); - InsnNode parentInsn = arg.getAssignInsn(); - if (parentInsn != null - && parentInsn.getResult() != null - && parentInsn.contains(AFlag.TRY_LEAVE) - && phi.removeArg(arg) /* TODO: fix registers removing */) { + if (shouldSkipInsnResult(mth, arg.getAssignInsn(), handlerAttr)) { + phi.removeArg(arg); argsCount--; - continue; + } else { + k++; } - k++; } + if (phi.getArgsCount() == 0) { + throw new JadxRuntimeException("PHI empty after try-catch fix!"); + } + } + + private static boolean shouldSkipInsnResult(MethodNode mth, InsnNode insn, ExcHandlerAttr handlerAttr) { + if (insn != null + && insn.getResult() != null + && insn.contains(AFlag.TRY_LEAVE)) { + CatchAttr catchAttr = BlockUtils.getCatchAttrForInsn(mth, insn); + return catchAttr != null && catchAttr.getHandlers().contains(handlerAttr.getHandler()); + } + return false; } private static boolean removeBlockerInsns(MethodNode mth) { @@ -256,6 +262,16 @@ public class SSATransform extends AbstractVisitor { return removed; } + private static void tryToFixUselessPhi(MethodNode mth) { + int k = 0; + int maxTries = mth.getSVars().size() * 2; + while (fixUselessPhi(mth)) { + if (k++ > maxTries) { + throw new JadxRuntimeException("Phi nodes fix limit reached!"); + } + } + } + private static boolean fixUselessPhi(MethodNode mth) { boolean changed = false; List insnToRemove = new ArrayList<>(); @@ -299,10 +315,10 @@ public class SSATransform extends AbstractVisitor { return true; } boolean allSame = phi.getArgsCount() == 1 || isSameArgs(phi); - if (!allSame) { - return false; + if (allSame) { + return replacePhiWithMove(mth, block, phi, phi.getArg(0)); } - return replacePhiWithMove(mth, block, phi, phi.getArg(0)); + return false; } private static boolean isSameArgs(PhiInsn phi) { 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 afbe2bb53..970bc5ae7 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 @@ -638,9 +638,8 @@ public final class TypeInferenceVisitor extends AbstractVisitor { if (assignBlock == null) { return false; } - RegisterArg newAssignArg = assignArg.duplicateWithNewSSAVar(mth); - assignInsn.setResult(newAssignArg); - IndexInsnNode castInsn = makeSoftCastInsn(assignArg, newAssignArg, castType); + assignInsn.setResult(assignArg.duplicateWithNewSSAVar(mth)); + IndexInsnNode castInsn = makeSoftCastInsn(assignArg.duplicate(), assignInsn.getResult().duplicate(), castType); return BlockUtils.insertAfterInsn(assignBlock, assignInsn, castInsn); } @@ -657,18 +656,19 @@ public final class TypeInferenceVisitor extends AbstractVisitor { if (useBlock == null) { return false; } - RegisterArg newUseArg = useArg.duplicateWithNewSSAVar(mth); - useInsn.replaceArg(useArg, newUseArg); - - IndexInsnNode castInsn = makeSoftCastInsn(newUseArg, useArg, useArg.getInitType()); + IndexInsnNode castInsn = makeSoftCastInsn( + useArg.duplicateWithNewSSAVar(mth), + useArg.duplicate(), + useArg.getInitType()); + useInsn.replaceArg(useArg, castInsn.getResult().duplicate()); return BlockUtils.insertBeforeInsn(useBlock, useInsn, castInsn); } @NotNull private IndexInsnNode makeSoftCastInsn(RegisterArg result, RegisterArg arg, ArgType castType) { IndexInsnNode castInsn = new IndexInsnNode(InsnType.CHECK_CAST, castType, 1); - castInsn.setResult(result.duplicate()); - castInsn.addArg(arg.duplicate()); + castInsn.setResult(result); + castInsn.addArg(arg); castInsn.add(AFlag.SOFT_CAST); castInsn.add(AFlag.SYNTHETIC); return castInsn; diff --git a/jadx-core/src/main/java/jadx/core/utils/BlockUtils.java b/jadx-core/src/main/java/jadx/core/utils/BlockUtils.java index 7123b24da..2d2a61535 100644 --- a/jadx-core/src/main/java/jadx/core/utils/BlockUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/BlockUtils.java @@ -35,6 +35,7 @@ import jadx.core.dex.nodes.IBlock; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.regions.conditions.IfCondition; +import jadx.core.dex.trycatch.CatchAttr; import jadx.core.dex.trycatch.ExceptionHandler; import jadx.core.utils.exceptions.JadxRuntimeException; @@ -410,6 +411,12 @@ public class BlockUtils { return s.isEmpty() ? null : s.get(0); } + @Nullable + public static BlockNode getPrevBlock(BlockNode block) { + List preds = block.getPredecessors(); + return preds.size() == 1 ? preds.get(0) : null; + } + /** * Return successor on path to 'pathEnd' block */ @@ -1245,4 +1252,16 @@ public class BlockUtils { } return null; } + + public static @Nullable CatchAttr getCatchAttrForInsn(MethodNode mth, InsnNode insn) { + CatchAttr catchAttr = insn.get(AType.EXC_CATCH); + if (catchAttr != null) { + return catchAttr; + } + BlockNode block = getBlockByInsn(mth, insn); + if (block == null) { + return null; + } + return block.get(AType.EXC_CATCH); + } } diff --git a/jadx-core/src/main/java/jadx/core/utils/DebugChecks.java b/jadx-core/src/main/java/jadx/core/utils/DebugChecks.java index b42b8758d..7614723bf 100644 --- a/jadx-core/src/main/java/jadx/core/utils/DebugChecks.java +++ b/jadx-core/src/main/java/jadx/core/utils/DebugChecks.java @@ -80,16 +80,30 @@ public class DebugChecks { SSAVar sVar = reg.getSVar(); if (sVar == null) { + if (reg.contains(AFlag.DONT_GENERATE) || insn.contains(AFlag.DONT_GENERATE)) { + return; + } if (Utils.notEmpty(mth.getSVars())) { - throw new JadxRuntimeException("Null SSA var in " + insn + ", mth: " + mth); + throw new JadxRuntimeException("Null SSA var in " + reg + " at " + insn); } return; } + if (Utils.indexInListByRef(mth.getSVars(), sVar) == -1) { + throw new JadxRuntimeException("SSA var not present in method vars list, var: " + sVar + " from insn: " + insn); + } + RegisterArg resArg = insn.getResult(); List useList = sVar.getUseList(); - boolean assignReg = insn.getResult() == reg; - if (!assignReg && !Utils.containsInListByRef(useList, reg)) { - throw new JadxRuntimeException("Incorrect use list in ssa var: " + sVar + ", register not listed." - + ICodeWriter.NL + " insn: " + insn); + if (resArg == reg) { + if (sVar.getAssignInsn() != insn) { + throw new JadxRuntimeException("Incorrect assign in ssa var: " + sVar + + ICodeWriter.NL + " expected: " + sVar.getAssignInsn() + + ICodeWriter.NL + " got: " + insn); + } + } else { + if (!Utils.containsInListByRef(useList, reg)) { + throw new JadxRuntimeException("Incorrect use list in ssa var: " + sVar + ", register not listed." + + ICodeWriter.NL + " insn: " + insn); + } } for (RegisterArg useArg : useList) { checkRegisterArg(mth, useArg); 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 607efb63b..4e1627b9e 100644 --- a/jadx-core/src/main/java/jadx/core/utils/InsnRemover.java +++ b/jadx-core/src/main/java/jadx/core/utils/InsnRemover.java @@ -22,6 +22,9 @@ import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.utils.exceptions.JadxRuntimeException; +import static jadx.core.utils.InsnUtils.isInsnType; +import static jadx.core.utils.ListUtils.allMatch; + /** * Helper class for correct instructions removing, * can be used while iterating over instructions list @@ -108,14 +111,13 @@ public class InsnRemover { if (r == null) { return; } - r.add(AFlag.REMOVE); // don't unset result arg, can be used to restore variable - if (mth == null) { - return; - } - SSAVar ssaVar = r.getSVar(); - if (ssaVar != null && ssaVar.getAssign() == insn.getResult()) { - removeSsaVar(mth, ssaVar); + if (mth != null) { + SSAVar ssaVar = r.getSVar(); + if (ssaVar != null && ssaVar.getAssignInsn() == insn /* can be already reassigned */) { + removeSsaVar(mth, ssaVar); + } } + insn.setResult(null); } private static void removeSsaVar(MethodNode mth, SSAVar ssaVar) { @@ -125,15 +127,7 @@ public class InsnRemover { return; } // check if all usage only in PHI insns - boolean allPhis = true; - for (RegisterArg arg : ssaVar.getUseList()) { - InsnNode parentInsn = arg.getParentInsn(); - if (parentInsn == null || parentInsn.getType() != InsnType.PHI) { - allPhis = false; - break; - } - } - if (allPhis) { + if (allMatch(ssaVar.getUseList(), arg -> isInsnType(arg.getParentInsn(), InsnType.PHI))) { for (RegisterArg arg : new ArrayList<>(ssaVar.getUseList())) { InsnNode parentInsn = arg.getParentInsn(); if (parentInsn != null) { @@ -143,12 +137,19 @@ public class InsnRemover { mth.removeSVar(ssaVar); return; } - if (Consts.DEBUG_WITH_ERRORS) { - throw new JadxRuntimeException("Can't remove SSA var, still in use, count: " + useCount + ", list:" - + ICodeWriter.NL + " " + ssaVar.getUseList().stream() - .map(arg -> arg + " from " + arg.getParentInsn()) - .collect(Collectors.joining(ICodeWriter.NL + " "))); + // check if all usage only in not generated instructions + if (allMatch(ssaVar.getUseList(), + arg -> arg.contains(AFlag.DONT_GENERATE) || (InsnUtils.contains(arg.getParentInsn(), AFlag.DONT_GENERATE)))) { + for (RegisterArg arg : ssaVar.getUseList()) { + arg.resetSSAVar(); + } + mth.removeSVar(ssaVar); + return; } + throw new JadxRuntimeException("Can't remove SSA var: " + ssaVar + ", still in use, count: " + useCount + + ", list:" + ICodeWriter.NL + " " + ssaVar.getUseList().stream() + .map(arg -> arg + " from " + arg.getParentInsn()) + .collect(Collectors.joining(ICodeWriter.NL + " "))); } public static void unbindArgUsage(@Nullable MethodNode mth, InsnArg arg) { 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 d935a743e..64da254c1 100644 --- a/jadx-core/src/main/java/jadx/core/utils/InsnUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/InsnUtils.java @@ -215,6 +215,10 @@ public class InsnUtils { return null; } + public static boolean isInsnType(@Nullable InsnNode insn, InsnType insnType) { + return insn != null && insn.getType() == insnType; + } + @Nullable public static InsnNode getWrappedInsn(InsnArg arg) { if (arg != null && arg.isInsnWrap()) { @@ -250,4 +254,8 @@ public class InsnUtils { } return false; } + + public static boolean contains(InsnNode insn, AFlag flag) { + return insn != null && insn.contains(flag); + } } diff --git a/jadx-core/src/main/java/jadx/core/utils/Utils.java b/jadx-core/src/main/java/jadx/core/utils/Utils.java index 53786eafd..7f3eb40b3 100644 --- a/jadx-core/src/main/java/jadx/core/utils/Utils.java +++ b/jadx-core/src/main/java/jadx/core/utils/Utils.java @@ -119,6 +119,20 @@ public class Utils { return sb.toString(); } + public static String currentStackTrace() { + return getStackTrace(new Exception()); + } + + public static String currentStackTrace(int skipFrames) { + Exception e = new Exception(); + StackTraceElement[] stackTrace = e.getStackTrace(); + int len = stackTrace.length; + if (skipFrames < len) { + e.setStackTrace(Arrays.copyOfRange(stackTrace, skipFrames, len)); + } + return getStackTrace(e); + } + public static String getFullStackTrace(Throwable throwable) { return getStackTrace(throwable, false); }