diff --git a/jadx-core/src/main/java/jadx/core/Jadx.java b/jadx-core/src/main/java/jadx/core/Jadx.java index d2a17cc80..b8d1ed06c 100644 --- a/jadx-core/src/main/java/jadx/core/Jadx.java +++ b/jadx-core/src/main/java/jadx/core/Jadx.java @@ -18,6 +18,7 @@ import jadx.core.dex.visitors.ReSugarCode; import jadx.core.dex.visitors.SimplifyVisitor; import jadx.core.dex.visitors.regions.CheckRegions; import jadx.core.dex.visitors.regions.IfRegionVisitor; +import jadx.core.dex.visitors.regions.LoopRegionVisitor; import jadx.core.dex.visitors.regions.ProcessVariables; import jadx.core.dex.visitors.regions.RegionMakerVisitor; import jadx.core.dex.visitors.regions.ReturnVisitor; @@ -91,6 +92,7 @@ public class Jadx { passes.add(new MethodInlineVisitor()); passes.add(new ClassModifier()); passes.add(new PrepareForCodeGen()); + passes.add(new LoopRegionVisitor()); } passes.add(new CodeGen(args)); return passes; diff --git a/jadx-core/src/main/java/jadx/core/codegen/ConditionGen.java b/jadx-core/src/main/java/jadx/core/codegen/ConditionGen.java index 5f754efd6..650b48bbd 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/ConditionGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/ConditionGen.java @@ -8,9 +8,9 @@ import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.InsnWrapArg; import jadx.core.dex.instructions.args.LiteralArg; import jadx.core.dex.nodes.InsnNode; -import jadx.core.dex.regions.Compare; -import jadx.core.dex.regions.IfCondition; -import jadx.core.dex.regions.IfCondition.Mode; +import jadx.core.dex.regions.conditions.Compare; +import jadx.core.dex.regions.conditions.IfCondition; +import jadx.core.dex.regions.conditions.IfCondition.Mode; import jadx.core.utils.ErrorsCounter; import jadx.core.utils.exceptions.CodegenException; import jadx.core.utils.exceptions.JadxRuntimeException; 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 3cbc64d91..8e520b9b2 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -58,9 +58,10 @@ public class InsnGen { protected final RootNode root; protected final boolean fallback; - private enum Flags { + protected enum Flags { BODY_ONLY, BODY_ONLY_NOWRAP, + INLINE } public InsnGen(MethodGen mgen, boolean fallback) { @@ -186,7 +187,7 @@ public class InsnGen { return makeInsn(insn, code, null); } - private boolean makeInsn(InsnNode insn, CodeWriter code, Flags flag) throws CodegenException { + protected boolean makeInsn(InsnNode insn, CodeWriter code, Flags flag) throws CodegenException { try { if (insn.getType() == InsnType.NOP) { return false; @@ -196,13 +197,17 @@ public class InsnGen { state.add(flag); makeInsnBody(code, insn, state); } else { - code.startLineWithNum(insn.getSourceLine()); + if (flag != Flags.INLINE) { + code.startLineWithNum(insn.getSourceLine()); + } if (insn.getResult() != null && insn.getType() != InsnType.ARITH_ONEARG) { assignVar(code, insn); code.add(" = "); } makeInsnBody(code, insn, state); - code.add(';'); + if (flag != Flags.INLINE) { + code.add(';'); + } } } catch (Throwable th) { throw new CodegenException(mth, "Error generate insn: " + insn, th); diff --git a/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java b/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java index cf45911a1..a7c6f0a01 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java @@ -14,17 +14,20 @@ import jadx.core.dex.nodes.IBlock; import jadx.core.dex.nodes.IContainer; import jadx.core.dex.nodes.IRegion; import jadx.core.dex.nodes.InsnNode; -import jadx.core.dex.regions.IfCondition; -import jadx.core.dex.regions.IfRegion; -import jadx.core.dex.regions.LoopRegion; import jadx.core.dex.regions.Region; import jadx.core.dex.regions.SwitchRegion; import jadx.core.dex.regions.SynchronizedRegion; +import jadx.core.dex.regions.conditions.IfCondition; +import jadx.core.dex.regions.conditions.IfRegion; +import jadx.core.dex.regions.loops.IndexLoop; +import jadx.core.dex.regions.loops.LoopRegion; +import jadx.core.dex.regions.loops.LoopType; import jadx.core.dex.trycatch.CatchAttr; import jadx.core.dex.trycatch.ExceptionHandler; import jadx.core.dex.trycatch.TryCatchBlock; import jadx.core.utils.RegionUtils; import jadx.core.utils.exceptions.CodegenException; +import jadx.core.utils.exceptions.JadxRuntimeException; import java.util.List; @@ -92,7 +95,9 @@ public class RegionGen extends InsnGen { private void makeSimpleBlock(IBlock block, CodeWriter code) throws CodegenException { for (InsnNode insn : block.getInstructions()) { - makeInsn(insn, code); + if (!insn.contains(AFlag.SKIP)) { + makeInsn(insn, code); + } } ForceReturnAttr retAttr = block.get(AType.FORCE_RETURN); if (retAttr != null) { @@ -166,8 +171,24 @@ public class RegionGen extends InsnGen { code.startLine('}'); return code; } - ConditionGen conditionGen = new ConditionGen(this); + LoopType type = region.getType(); + if (type != null) { + if (type instanceof IndexLoop) { + IndexLoop indexLoop = (IndexLoop) type; + code.startLine("for ("); + makeInsn(indexLoop.getInitInsn(), code, Flags.INLINE); + code.add("; "); + conditionGen.add(code, condition); + code.add("; "); + makeInsn(indexLoop.getIncrInsn(), code, Flags.INLINE); + code.add(") {"); + makeRegionIndent(code, region.getBody()); + code.startLine('}'); + return code; + } + throw new JadxRuntimeException("Unknown loop type: " + type.getClass()); + } if (region.isConditionAtEnd()) { code.startLine("do {"); makeRegionIndent(code, region.getBody()); 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 07393fcc1..55b9a2760 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 @@ -23,5 +23,7 @@ public enum AFlag { ELSE_IF_CHAIN, + WRAPPED, + INCONSISTENT_CODE, // warning about incorrect decompilation } 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 a0eb2880d..989d862b7 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,8 +1,12 @@ package jadx.core.dex.instructions.args; +import jadx.core.dex.attributes.AFlag; import jadx.core.dex.nodes.InsnNode; import jadx.core.utils.InsnUtils; +import java.util.ArrayList; +import java.util.List; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -76,18 +80,36 @@ public abstract class InsnArg extends Typed { return null; } if (parent == insn) { - LOG.debug("Can't wrap instruction info itself: " + insn); + LOG.debug("Can't wrap instruction info itself: {}", insn); + Thread.dumpStack(); return null; } + int i = getArgIndex(parent, this); + if (i == -1) { + return null; + } + insn.add(AFlag.WRAPPED); + InsnArg arg = wrapArg(insn); + parent.setArg(i, arg); + return arg; + } + + public static void updateParentInsn(InsnNode fromInsn, InsnNode toInsn) { + List args = new ArrayList(); + fromInsn.getRegisterArgs(args); + for (RegisterArg reg : args) { + reg.setParentInsn(toInsn); + } + } + + private static int getArgIndex(InsnNode parent, InsnArg arg) { int count = parent.getArgsCount(); for (int i = 0; i < count; i++) { - if (parent.getArg(i) == this) { - InsnArg arg = wrapArg(insn); - parent.setArg(i, arg); - return arg; + if (parent.getArg(i) == arg) { + return i; } } - return null; + return -1; } public static InsnArg wrapArg(InsnNode insn) { 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 9e219ad42..da2022a20 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 @@ -5,7 +5,7 @@ import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.LiteralArg; import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.nodes.InsnNode; -import jadx.core.dex.regions.IfCondition; +import jadx.core.dex.regions.conditions.IfCondition; import jadx.core.utils.InsnUtils; import jadx.core.utils.Utils; diff --git a/jadx-core/src/main/java/jadx/core/dex/regions/Compare.java b/jadx-core/src/main/java/jadx/core/dex/regions/conditions/Compare.java similarity index 95% rename from jadx-core/src/main/java/jadx/core/dex/regions/Compare.java rename to jadx-core/src/main/java/jadx/core/dex/regions/conditions/Compare.java index d070f5fca..10e83e0b6 100644 --- a/jadx-core/src/main/java/jadx/core/dex/regions/Compare.java +++ b/jadx-core/src/main/java/jadx/core/dex/regions/conditions/Compare.java @@ -1,4 +1,4 @@ -package jadx.core.dex.regions; +package jadx.core.dex.regions.conditions; import jadx.core.dex.instructions.IfNode; import jadx.core.dex.instructions.IfOp; diff --git a/jadx-core/src/main/java/jadx/core/dex/regions/IfCondition.java b/jadx-core/src/main/java/jadx/core/dex/regions/conditions/IfCondition.java similarity index 99% rename from jadx-core/src/main/java/jadx/core/dex/regions/IfCondition.java rename to jadx-core/src/main/java/jadx/core/dex/regions/conditions/IfCondition.java index b4bced40d..a117df897 100644 --- a/jadx-core/src/main/java/jadx/core/dex/regions/IfCondition.java +++ b/jadx-core/src/main/java/jadx/core/dex/regions/conditions/IfCondition.java @@ -1,4 +1,4 @@ -package jadx.core.dex.regions; +package jadx.core.dex.regions.conditions; import jadx.core.dex.instructions.IfNode; import jadx.core.dex.instructions.IfOp; diff --git a/jadx-core/src/main/java/jadx/core/dex/regions/IfInfo.java b/jadx-core/src/main/java/jadx/core/dex/regions/conditions/IfInfo.java similarity index 97% rename from jadx-core/src/main/java/jadx/core/dex/regions/IfInfo.java rename to jadx-core/src/main/java/jadx/core/dex/regions/conditions/IfInfo.java index e1cce4da0..928c34679 100644 --- a/jadx-core/src/main/java/jadx/core/dex/regions/IfInfo.java +++ b/jadx-core/src/main/java/jadx/core/dex/regions/conditions/IfInfo.java @@ -1,4 +1,4 @@ -package jadx.core.dex.regions; +package jadx.core.dex.regions.conditions; import jadx.core.dex.nodes.BlockNode; diff --git a/jadx-core/src/main/java/jadx/core/dex/regions/IfRegion.java b/jadx-core/src/main/java/jadx/core/dex/regions/conditions/IfRegion.java similarity index 94% rename from jadx-core/src/main/java/jadx/core/dex/regions/IfRegion.java rename to jadx-core/src/main/java/jadx/core/dex/regions/conditions/IfRegion.java index a0eb0e11a..3dddecf3d 100644 --- a/jadx-core/src/main/java/jadx/core/dex/regions/IfRegion.java +++ b/jadx-core/src/main/java/jadx/core/dex/regions/conditions/IfRegion.java @@ -1,8 +1,9 @@ -package jadx.core.dex.regions; +package jadx.core.dex.regions.conditions; import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.IContainer; import jadx.core.dex.nodes.IRegion; +import jadx.core.dex.regions.AbstractRegion; import java.util.ArrayList; import java.util.Collections; @@ -77,7 +78,7 @@ public final class IfRegion extends AbstractRegion { @Override public List getSubBlocks() { - ArrayList all = new ArrayList(3); + List all = new ArrayList(3); all.add(header); if (thenRegion != null) { all.add(thenRegion); diff --git a/jadx-core/src/main/java/jadx/core/dex/regions/loops/IndexLoop.java b/jadx-core/src/main/java/jadx/core/dex/regions/loops/IndexLoop.java new file mode 100644 index 000000000..07f0ced3e --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/regions/loops/IndexLoop.java @@ -0,0 +1,22 @@ +package jadx.core.dex.regions.loops; + +import jadx.core.dex.nodes.InsnNode; + +public class IndexLoop extends LoopType { + + private final InsnNode initInsn; + private final InsnNode incrInsn; + + public IndexLoop(InsnNode initInsn, InsnNode incrInsn) { + this.initInsn = initInsn; + this.incrInsn = incrInsn; + } + + public InsnNode getInitInsn() { + return initInsn; + } + + public InsnNode getIncrInsn() { + return incrInsn; + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/regions/LoopRegion.java b/jadx-core/src/main/java/jadx/core/dex/regions/loops/LoopRegion.java similarity index 92% rename from jadx-core/src/main/java/jadx/core/dex/regions/LoopRegion.java rename to jadx-core/src/main/java/jadx/core/dex/regions/loops/LoopRegion.java index 278936df9..b7b6bd25b 100644 --- a/jadx-core/src/main/java/jadx/core/dex/regions/LoopRegion.java +++ b/jadx-core/src/main/java/jadx/core/dex/regions/loops/LoopRegion.java @@ -1,4 +1,4 @@ -package jadx.core.dex.regions; +package jadx.core.dex.regions.loops; import jadx.core.dex.instructions.IfNode; import jadx.core.dex.instructions.args.RegisterArg; @@ -6,6 +6,8 @@ import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.IContainer; import jadx.core.dex.nodes.IRegion; import jadx.core.dex.nodes.InsnNode; +import jadx.core.dex.regions.AbstractRegion; +import jadx.core.dex.regions.conditions.IfCondition; import java.util.ArrayList; import java.util.Collections; @@ -21,6 +23,8 @@ public final class LoopRegion extends AbstractRegion { private IContainer body; private final boolean conditionAtEnd; + private LoopType type; + public LoopRegion(IRegion parent, BlockNode header, boolean reversed) { super(parent); this.conditionBlock = header; @@ -116,6 +120,15 @@ public final class LoopRegion extends AbstractRegion { } } + + public LoopType getType() { + return type; + } + + public void setType(LoopType type) { + this.type = type; + } + @Override public List getSubBlocks() { List all = new ArrayList(3); diff --git a/jadx-core/src/main/java/jadx/core/dex/regions/loops/LoopType.java b/jadx-core/src/main/java/jadx/core/dex/regions/loops/LoopType.java new file mode 100644 index 000000000..d97eee9f4 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/regions/loops/LoopType.java @@ -0,0 +1,4 @@ +package jadx.core.dex.regions.loops; + +public abstract class LoopType { +} diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/CodeShrinker.java b/jadx-core/src/main/java/jadx/core/dex/visitors/CodeShrinker.java index c1f8fc0f5..84d6ff98e 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/CodeShrinker.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/CodeShrinker.java @@ -214,6 +214,7 @@ public class CodeShrinker extends AbstractVisitor { // another block BlockNode assignBlock = BlockUtils.getBlockByInsn(mth, assignInsn); if (assignBlock != null + && assignInsn != arg.getParentInsn() && canMoveBetweenBlocks(assignInsn, assignBlock, block, argsInfo.getInsn())) { arg.wrapInstruction(assignInsn); InsnList.remove(assignBlock, assignInsn); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/PrepareForCodeGen.java b/jadx-core/src/main/java/jadx/core/dex/visitors/PrepareForCodeGen.java index 973116471..e76214744 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/PrepareForCodeGen.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/PrepareForCodeGen.java @@ -141,6 +141,7 @@ public class PrepareForCodeGen extends AbstractVisitor { } if (replace) { ArithNode newArith = new ArithNode(arith.getOp(), res, arith.getArg(1)); + InsnArg.updateParentInsn(arith, newArith); list.set(i, newArith); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java index 1fd54ee98..54eb00eec 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 @@ -19,7 +19,7 @@ import jadx.core.dex.instructions.mods.TernaryInsn; import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; -import jadx.core.dex.regions.IfCondition; +import jadx.core.dex.regions.conditions.IfCondition; import java.util.ArrayList; import java.util.Collections; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/CheckRegions.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/CheckRegions.java index 6009bf921..5a41b5746 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/CheckRegions.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/CheckRegions.java @@ -6,7 +6,7 @@ import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.IBlock; import jadx.core.dex.nodes.IRegion; import jadx.core.dex.nodes.MethodNode; -import jadx.core.dex.regions.LoopRegion; +import jadx.core.dex.regions.loops.LoopRegion; import jadx.core.dex.visitors.AbstractVisitor; import jadx.core.utils.ErrorsCounter; import jadx.core.utils.exceptions.JadxException; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/IfMakerHelper.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/IfMakerHelper.java index 9f67ad245..0f00d3fc5 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/IfMakerHelper.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/IfMakerHelper.java @@ -9,9 +9,9 @@ import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; -import jadx.core.dex.regions.IfCondition; -import jadx.core.dex.regions.IfCondition.Mode; -import jadx.core.dex.regions.IfInfo; +import jadx.core.dex.regions.conditions.IfCondition; +import jadx.core.dex.regions.conditions.IfCondition.Mode; +import jadx.core.dex.regions.conditions.IfInfo; import jadx.core.utils.BlockUtils; import java.util.Collection; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/IfRegionVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/IfRegionVisitor.java index 6d0a97a81..a6a50da70 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/IfRegionVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/IfRegionVisitor.java @@ -6,9 +6,9 @@ import jadx.core.dex.nodes.IBlock; import jadx.core.dex.nodes.IContainer; import jadx.core.dex.nodes.IRegion; import jadx.core.dex.nodes.MethodNode; -import jadx.core.dex.regions.IfCondition; -import jadx.core.dex.regions.IfCondition.Mode; -import jadx.core.dex.regions.IfRegion; +import jadx.core.dex.regions.conditions.IfCondition; +import jadx.core.dex.regions.conditions.IfCondition.Mode; +import jadx.core.dex.regions.conditions.IfRegion; import jadx.core.dex.regions.Region; import jadx.core.dex.visitors.AbstractVisitor; import jadx.core.utils.RegionUtils; 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 new file mode 100644 index 000000000..502b24bdc --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/LoopRegionVisitor.java @@ -0,0 +1,120 @@ +package jadx.core.dex.visitors.regions; + +import jadx.core.dex.attributes.AFlag; +import jadx.core.dex.instructions.PhiInsn; +import jadx.core.dex.instructions.args.RegisterArg; +import jadx.core.dex.nodes.BlockNode; +import jadx.core.dex.nodes.IBlock; +import jadx.core.dex.nodes.IRegion; +import jadx.core.dex.nodes.InsnNode; +import jadx.core.dex.nodes.MethodNode; +import jadx.core.dex.regions.conditions.IfCondition; +import jadx.core.dex.regions.loops.IndexLoop; +import jadx.core.dex.regions.loops.LoopRegion; +import jadx.core.dex.visitors.AbstractVisitor; +import jadx.core.utils.BlockUtils; +import jadx.core.utils.RegionUtils; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor { + private static final Logger LOG = LoggerFactory.getLogger(LoopRegionVisitor.class); + + @Override + public void visit(MethodNode mth) { + DepthRegionTraversal.traverseAll(mth, this); + } + + @Override + public void enterRegion(MethodNode mth, IRegion region) { + if (region instanceof LoopRegion) { + processLoopRegion(mth, (LoopRegion) region); + } + } + + private static void processLoopRegion(MethodNode mth, LoopRegion loopRegion) { + if (loopRegion.isConditionAtEnd()) { + return; + } + IfCondition condition = loopRegion.getCondition(); + if (condition == null) { + return; + } + List args = condition.getRegisterArgs(); + if (checkForIndexedLoop(mth, loopRegion, args)) { + return; + } + } + + /** + * Check for indexed loop. + */ + private static boolean checkForIndexedLoop(MethodNode mth, LoopRegion loopRegion, List condArgs) { + InsnNode incrInsn = RegionUtils.getLastInsn(loopRegion); + if (incrInsn == null) { + return false; + } + RegisterArg incrArg = incrInsn.getResult(); + if (incrArg == null + || incrArg.getSVar() == null + || !incrArg.getSVar().isUsedInPhi()) { + return false; + } + PhiInsn phiInsn = incrArg.getSVar().getUsedInPhi(); + if (phiInsn.getArgsCount() != 2 + || !phiInsn.getArg(1).equals(incrArg) + || incrArg.getSVar().getUseCount() != 1) { + return false; + } + RegisterArg arg = phiInsn.getResult(); + if (!condArgs.contains(arg) || arg.getSVar().isUsedInPhi()) { + return false; + } + RegisterArg initArg = phiInsn.getArg(0); + InsnNode initInsn = initArg.getAssignInsn(); + if (initInsn == null || initArg.getSVar().getUseCount() != 1) { + return false; + } + if (!usedOnlyInLoop(mth, loopRegion, arg)) { + return false; + } + initInsn.add(AFlag.SKIP); + incrInsn.add(AFlag.SKIP); + loopRegion.setType(new IndexLoop(initInsn, incrInsn)); + return true; + } + + private static boolean usedOnlyInLoop(MethodNode mth, LoopRegion loopRegion, RegisterArg arg) { + List useList = arg.getSVar().getUseList(); + for (RegisterArg useArg : useList) { + if (!argInLoop(mth, loopRegion, useArg)) { + return false; + } + } + return true; + } + + private static boolean argInLoop(MethodNode mth, LoopRegion loopRegion, RegisterArg arg) { + InsnNode parentInsn = arg.getParentInsn(); + if (parentInsn == null) { + return false; + } + BlockNode block = BlockUtils.getBlockByInsn(mth, parentInsn); + if (block == null) { + LOG.debug("Instruction not found: {}, mth: {}", parentInsn, mth); + return false; + } + return RegionUtils.isRegionContainsBlock(loopRegion, block); + } + + @Override + public void leaveRegion(MethodNode mth, IRegion region) { + } + + @Override + public void processBlock(MethodNode mth, IBlock container) { + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessVariables.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessVariables.java index e6e030ea6..4c6db199a 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessVariables.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessVariables.java @@ -12,7 +12,7 @@ import jadx.core.dex.nodes.IContainer; import jadx.core.dex.nodes.IRegion; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; -import jadx.core.dex.regions.IfRegion; +import jadx.core.dex.regions.conditions.IfRegion; import jadx.core.dex.regions.SwitchRegion; import jadx.core.dex.visitors.AbstractVisitor; import jadx.core.utils.RegionUtils; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java index 8179570fc..cf392ff43 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java @@ -13,9 +13,9 @@ import jadx.core.dex.nodes.Edge; import jadx.core.dex.nodes.IRegion; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; -import jadx.core.dex.regions.IfInfo; -import jadx.core.dex.regions.IfRegion; -import jadx.core.dex.regions.LoopRegion; +import jadx.core.dex.regions.conditions.IfInfo; +import jadx.core.dex.regions.conditions.IfRegion; +import jadx.core.dex.regions.loops.LoopRegion; import jadx.core.dex.regions.Region; import jadx.core.dex.regions.SwitchRegion; import jadx.core.dex.regions.SynchronizedRegion; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMakerVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMakerVisitor.java index 689efc198..322dc194e 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMakerVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMakerVisitor.java @@ -4,7 +4,7 @@ import jadx.core.dex.nodes.IContainer; import jadx.core.dex.nodes.IRegion; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; -import jadx.core.dex.regions.LoopRegion; +import jadx.core.dex.regions.loops.LoopRegion; import jadx.core.dex.regions.Region; import jadx.core.dex.regions.SynchronizedRegion; import jadx.core.dex.visitors.AbstractVisitor; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ReturnVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ReturnVisitor.java index c7944098b..6664e9c95 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ReturnVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ReturnVisitor.java @@ -8,8 +8,8 @@ import jadx.core.dex.nodes.IContainer; import jadx.core.dex.nodes.IRegion; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; -import jadx.core.dex.regions.IfRegion; -import jadx.core.dex.regions.LoopRegion; +import jadx.core.dex.regions.conditions.IfRegion; +import jadx.core.dex.regions.loops.LoopRegion; import jadx.core.dex.regions.SwitchRegion; import jadx.core.dex.visitors.AbstractVisitor; import jadx.core.utils.exceptions.JadxException; 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 659e1ac8c..6f0142c77 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 @@ -12,7 +12,7 @@ import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.IContainer; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; -import jadx.core.dex.regions.IfRegion; +import jadx.core.dex.regions.conditions.IfRegion; import jadx.core.dex.regions.Region; import jadx.core.dex.visitors.CodeShrinker; import jadx.core.utils.InsnList; 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 2b37f1130..7510855b5 100644 --- a/jadx-core/src/main/java/jadx/core/utils/BlockUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/BlockUtils.java @@ -1,7 +1,10 @@ package jadx.core.utils; +import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AType; import jadx.core.dex.instructions.InsnType; +import jadx.core.dex.instructions.args.InsnArg; +import jadx.core.dex.instructions.args.InsnWrapArg; import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; @@ -113,7 +116,9 @@ public class BlockUtils { } public static BlockNode getBlockByInsn(MethodNode mth, InsnNode insn) { - assert insn != null; + if (insn.contains(AFlag.WRAPPED)) { + return getBlockByWrappedInsn(mth, insn); + } for (BlockNode bn : mth.getBasicBlocks()) { if (blockContains(bn, insn)) { return bn; @@ -122,6 +127,29 @@ public class BlockUtils { return null; } + private static BlockNode getBlockByWrappedInsn(MethodNode mth, InsnNode insn) { + for (BlockNode bn : mth.getBasicBlocks()) { + for (InsnNode bi : bn.getInstructions()) { + if (bi == insn || foundWrappedInsn(bi, insn)) { + return bn; + } + } + } + return null; + } + + private static boolean foundWrappedInsn(InsnNode container, InsnNode insn) { + for (InsnArg arg : container.getArguments()) { + if (arg.isInsnWrap()) { + InsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn(); + if (wrapInsn == insn || foundWrappedInsn(wrapInsn, insn)) { + return true; + } + } + } + return false; + } + public static BitSet blocksToBitSet(MethodNode mth, List blocks) { BitSet bs = new BitSet(mth.getBasicBlocks().size()); for (BlockNode block : blocks) { diff --git a/jadx-core/src/main/java/jadx/core/utils/RegionUtils.java b/jadx-core/src/main/java/jadx/core/utils/RegionUtils.java index ff5d0320e..26264de16 100644 --- a/jadx-core/src/main/java/jadx/core/utils/RegionUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/RegionUtils.java @@ -6,6 +6,9 @@ import jadx.core.dex.instructions.InsnType; import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.IContainer; import jadx.core.dex.nodes.IRegion; +import jadx.core.dex.nodes.InsnNode; +import jadx.core.dex.regions.SwitchRegion; +import jadx.core.dex.regions.conditions.IfRegion; import jadx.core.dex.trycatch.CatchAttr; import jadx.core.dex.trycatch.ExceptionHandler; import jadx.core.dex.trycatch.TryCatchBlock; @@ -35,6 +38,29 @@ public class RegionUtils { } } + public static InsnNode getLastInsn(IContainer container) { + if (container instanceof BlockNode) { + BlockNode block = (BlockNode) container; + List insnList = block.getInstructions(); + if (insnList.isEmpty()) { + return null; + } + return insnList.get(insnList.size() - 1); + } else if (container instanceof IfRegion + || container instanceof SwitchRegion) { + return null; + } else if (container instanceof IRegion) { + IRegion region = (IRegion) container; + List blocks = region.getSubBlocks(); + if (blocks.isEmpty()) { + return null; + } + return getLastInsn(blocks.get(blocks.size() - 1)); + } else { + throw new JadxRuntimeException("Unknown container type: " + container.getClass()); + } + } + /** * Return true if last block in region has no successors */ diff --git a/jadx-core/src/test/java/jadx/tests/functional/TestIfCondition.java b/jadx-core/src/test/java/jadx/tests/functional/TestIfCondition.java index 4a6922f73..e17029c22 100644 --- a/jadx-core/src/test/java/jadx/tests/functional/TestIfCondition.java +++ b/jadx-core/src/test/java/jadx/tests/functional/TestIfCondition.java @@ -5,15 +5,15 @@ import jadx.core.dex.instructions.IfOp; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.LiteralArg; -import jadx.core.dex.regions.Compare; -import jadx.core.dex.regions.IfCondition; +import jadx.core.dex.regions.conditions.Compare; +import jadx.core.dex.regions.conditions.IfCondition; import org.junit.Test; -import static jadx.core.dex.regions.IfCondition.Mode; -import static jadx.core.dex.regions.IfCondition.merge; -import static jadx.core.dex.regions.IfCondition.not; -import static jadx.core.dex.regions.IfCondition.simplify; +import static jadx.core.dex.regions.conditions.IfCondition.Mode; +import static jadx.core.dex.regions.conditions.IfCondition.merge; +import static jadx.core.dex.regions.conditions.IfCondition.not; +import static jadx.core.dex.regions.conditions.IfCondition.simplify; import static org.junit.Assert.assertEquals; public class TestIfCondition { diff --git a/jadx-core/src/test/java/jadx/tests/internal/inline/TestInline2.java b/jadx-core/src/test/java/jadx/tests/internal/inline/TestInline2.java index 91bce49bb..c6f15ea16 100644 --- a/jadx-core/src/test/java/jadx/tests/internal/inline/TestInline2.java +++ b/jadx-core/src/test/java/jadx/tests/internal/inline/TestInline2.java @@ -5,7 +5,7 @@ import jadx.core.dex.nodes.ClassNode; import org.junit.Test; -import static org.hamcrest.CoreMatchers.containsString; +import static jadx.tests.utils.JadxMatchers.containsOne; import static org.junit.Assert.assertThat; public class TestInline2 extends InternalJadxTest { @@ -30,9 +30,7 @@ public class TestInline2 extends InternalJadxTest { String code = cls.getCode().toString(); System.out.println(code); - assertThat(code, containsString("i < a.length")); - assertThat(code, containsString("long i2 =")); - assertThat(code, containsString("+ i2")); - assertThat(code, containsString("i2--;")); + assertThat(code, containsOne("for (int i = 0; i < a.length; i++) {")); + assertThat(code, containsOne("for (long i2 = (long) b; i2 > 0; i2--) {")); } } diff --git a/jadx-core/src/test/java/jadx/tests/internal/loops/TestBreakInLoop.java b/jadx-core/src/test/java/jadx/tests/internal/loops/TestBreakInLoop.java index 77f9cd22b..4a86dbb40 100644 --- a/jadx-core/src/test/java/jadx/tests/internal/loops/TestBreakInLoop.java +++ b/jadx-core/src/test/java/jadx/tests/internal/loops/TestBreakInLoop.java @@ -6,6 +6,7 @@ import jadx.core.dex.nodes.ClassNode; import org.junit.Test; import static jadx.tests.utils.JadxMatchers.containsOne; +import static jadx.tests.utils.JadxMatchers.countString; import static org.junit.Assert.assertThat; public class TestBreakInLoop extends InternalJadxTest { @@ -14,13 +15,11 @@ public class TestBreakInLoop extends InternalJadxTest { private int f; private void test(int[] a, int b) { - int i = 0; - while (i < a.length) { + for (int i = 0; i < a.length; i++) { a[i]++; if (i < b) { break; } - i++; } this.f++; } @@ -32,12 +31,12 @@ public class TestBreakInLoop extends InternalJadxTest { String code = cls.getCode().toString(); System.out.println(code); - assertThat(code, containsOne("this.f++;")); + assertThat(code, containsOne("for (int i = 0; i < a.length; i++) {")); // assertThat(code, containsOne("a[i]++;")); assertThat(code, containsOne("if (i < b) {")); assertThat(code, containsOne("break;")); - assertThat(code, containsOne("i++;")); + assertThat(code, containsOne("this.f++;")); -// assertThat(code, countString(0, "else")); + assertThat(code, countString(0, "else")); } } diff --git a/jadx-core/src/test/java/jadx/tests/internal/loops/TestIndexForLoop.java b/jadx-core/src/test/java/jadx/tests/internal/loops/TestIndexForLoop.java new file mode 100644 index 000000000..2fb6fb72a --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/loops/TestIndexForLoop.java @@ -0,0 +1,38 @@ +package jadx.tests.internal.loops; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import org.junit.Test; + +import static jadx.tests.utils.JadxMatchers.containsLines; +import static org.junit.Assert.assertThat; + +public class TestIndexForLoop extends InternalJadxTest { + + public static class TestCls { + + private int test(int[] a, int b) { + int sum = 0; + for (int i = 0; i < b; i++) { + sum += a[i]; + } + return sum; + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + System.out.println(code); + + assertThat(code, containsLines(2, + "int sum = 0;", + "for (int i = 0; i < b; i++) {", + indent(1) + "sum += a[i];", + "}", + "return sum;" + )); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/internal/loops/TestLoopDetection2.java b/jadx-core/src/test/java/jadx/tests/internal/loops/TestLoopDetection2.java index 87ec41a59..6572408ff 100644 --- a/jadx-core/src/test/java/jadx/tests/internal/loops/TestLoopDetection2.java +++ b/jadx-core/src/test/java/jadx/tests/internal/loops/TestLoopDetection2.java @@ -5,6 +5,7 @@ import jadx.core.dex.nodes.ClassNode; import org.junit.Test; +import static jadx.tests.utils.JadxMatchers.containsOne; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.not; import static org.junit.Assert.assertThat; @@ -33,9 +34,8 @@ public class TestLoopDetection2 extends InternalJadxTest { String code = cls.getCode().toString(); System.out.println(code); - assertThat(code, containsString("while (i < b) {")); - assertThat(code, containsString("int c = a + b;")); + assertThat(code, containsOne("int c = a + b;")); + assertThat(code, containsOne("for (int i = a; i < b; i++) {")); assertThat(code, not(containsString("c_2"))); - assertThat(code, containsString("i++")); } } diff --git a/jadx-core/src/test/java/jadx/tests/internal/loops/TestNestedLoops2.java b/jadx-core/src/test/java/jadx/tests/internal/loops/TestNestedLoops2.java new file mode 100644 index 000000000..2de1e9a1b --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/loops/TestNestedLoops2.java @@ -0,0 +1,38 @@ +package jadx.tests.internal.loops; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import java.util.List; + +import org.junit.Test; + +import static jadx.tests.utils.JadxMatchers.containsOne; +import static org.junit.Assert.assertThat; + +public class TestNestedLoops2 extends InternalJadxTest { + + public static class TestCls { + + private boolean test(List list) { + int j = 0; + for (int i = 0; i < list.size(); i++) { + String s = list.get(i); + while (j < s.length()) { + j++; + } + } + return j > 10; + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + System.out.println(code); + + assertThat(code, containsOne("for (int i = 0; i < list.size(); i++) {")); + assertThat(code, containsOne("while (j < ((String) list.get(i)).length()) {")); + } +}