fix: improve condition branch checks in loops (#2274)

This commit is contained in:
Skylot
2024-09-16 20:36:57 +01:00
parent 5c83c22501
commit 699ceb197e
4 changed files with 57 additions and 6 deletions
@@ -120,8 +120,12 @@ public class IfMakerHelper {
List<BlockNode> preds = block.getPredecessors();
List<BlockNode> 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;
}
}
@@ -182,7 +182,7 @@ public class RegionMaker {
Set<BlockNode> 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<BlockNode> 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<Edge> exitEdges = loop.getExitEdges();
if (exitEdges.size() < 2) {
@@ -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) {");
}
}
@@ -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