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 cc7617be9..d0b391263 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 @@ -120,8 +120,12 @@ public class IfMakerHelper { List preds = block.getPredecessors(); List ifBlocks = info.getMergedBlocks(); for (BlockNode pred : preds) { + if (pred.contains(AFlag.LOOP_END)) { + // ignore loop back edge + continue; + } BlockNode top = BlockUtils.skipSyntheticPredecessor(pred); - if (!ifBlocks.contains(top) && !top.contains(AFlag.LOOP_END)) { + if (!ifBlocks.contains(top)) { return false; } } 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 589335098..2112813e2 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 @@ -182,7 +182,7 @@ public class RegionMaker { Set exitBlocksSet = loop.getExitNodes(); // set exit blocks scan order priority - // this can help if loop have several exits (after using 'break' or 'return' in loop) + // this can help if loop has several exits (after using 'break' or 'return' in loop) List exitBlocks = new ArrayList<>(exitBlocksSet.size()); BlockNode nextStart = getNextBlock(loopStart); if (nextStart != null && exitBlocksSet.remove(nextStart)) { @@ -299,10 +299,7 @@ public class RegionMaker { // skip nested loop condition continue; } - BlockNode loopEnd = loop.getEnd(); - boolean exitAtLoopEnd = block == loopEnd - || (loopEnd.getInstructions().isEmpty() && ListUtils.isSingleElement(loopEnd.getPredecessors(), block)); - + boolean exitAtLoopEnd = isExitAtLoopEnd(block, loop); LoopRegion loopRegion = new LoopRegion(curRegion, loop, block, exitAtLoopEnd); boolean found; if (block == loop.getStart() || exitAtLoopEnd @@ -345,6 +342,18 @@ public class RegionMaker { return null; } + private static boolean isExitAtLoopEnd(BlockNode exit, LoopInfo loop) { + BlockNode loopEnd = loop.getEnd(); + if (exit == loopEnd) { + return true; + } + BlockNode loopStart = loop.getStart(); + if (loopStart.getInstructions().isEmpty() && ListUtils.isSingleElement(loopStart.getSuccessors(), exit)) { + return false; + } + return loopEnd.getInstructions().isEmpty() && ListUtils.isSingleElement(loopEnd.getPredecessors(), exit); + } + private boolean checkLoopExits(LoopInfo loop, BlockNode mainExitBlock) { List exitEdges = loop.getExitEdges(); if (exitEdges.size() < 2) { diff --git a/jadx-core/src/test/java/jadx/tests/integration/loops/TestLoopRestore2.java b/jadx-core/src/test/java/jadx/tests/integration/loops/TestLoopRestore2.java new file mode 100644 index 000000000..3a47a081f --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/loops/TestLoopRestore2.java @@ -0,0 +1,18 @@ +package jadx.tests.integration.loops; + +import org.junit.jupiter.api.Test; + +import jadx.tests.api.RaungTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +public class TestLoopRestore2 extends RaungTest { + + @Test + public void test() { + disableCompilation(); // unreachable statement + assertThat(getClassNodeFromRaung()) + .code() + .containsOne("while (1 == 0) {"); + } +} diff --git a/jadx-core/src/test/raung/loops/TestLoopRestore2.raung b/jadx-core/src/test/raung/loops/TestLoopRestore2.raung new file mode 100644 index 000000000..6c2ae48d2 --- /dev/null +++ b/jadx-core/src/test/raung/loops/TestLoopRestore2.raung @@ -0,0 +1,20 @@ +.version 45.3 # Java 1.1 +.class public super loops/TestLoopRestore2 + +.method public test([I[I)V + .max stack 5 + .max locals 6 + + iconst_0 + istore 3 + sipush 0 + ifeq :L0 + goto :L1 + :L1 + sipush 1 + ifeq :L1 + goto :L0 + :L0 + iinc 3 1 + return +.end method