From 2cf28eb2e7f2d4438b575d61dd36232faccb06ae Mon Sep 17 00:00:00 2001 From: Skylot Date: Tue, 25 Feb 2014 23:53:30 +0400 Subject: [PATCH] core: fix loop detection --- .../dex/visitors/regions/RegionMaker.java | 12 +++-- .../{ => loops}/TestLoopCondition.java | 2 +- .../internal/loops/TestLoopDetection.java | 37 ++++++++++++++ .../tests/internal/loops/TestNestedLoops.java | 51 +++++++++++++++++++ .../src/main/java/jadx/samples/TestCF3.java | 19 +++++++ 5 files changed, 117 insertions(+), 4 deletions(-) rename jadx-core/src/test/java/jadx/tests/internal/{ => loops}/TestLoopCondition.java (97%) create mode 100644 jadx-core/src/test/java/jadx/tests/internal/loops/TestLoopDetection.java create mode 100644 jadx-core/src/test/java/jadx/tests/internal/loops/TestNestedLoops.java 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 b59f0430..3a61f035 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 @@ -244,7 +244,6 @@ public class RegionMaker { } } - stack.push(loopRegion); curRegion.getSubBlocks().add(loopRegion); exitBlocks.remove(condBlock); @@ -290,9 +289,12 @@ public class RegionMaker { out = (bThen == loopStart ? bElse : bThen); loopStart.getAttributes().remove(AttributeType.LOOP); + + stack.push(loopRegion); stack.addExit(loop.getEnd()); loopRegion.setBody(makeRegion(loopStart, stack)); loopStart.getAttributes().add(loop); + stack.pop(); } else { Set loopBlocks = loop.getLoopBlocks(); BlockNode loopBody = null; @@ -308,14 +310,18 @@ public class RegionMaker { out = selectOther(loopBody, condBlock.getSuccessors()); AttributesList outAttrs = out.getAttributes(); if (outAttrs.contains(AttributeFlag.LOOP_START) - && outAttrs.get(AttributeType.LOOP) != loop) { + && outAttrs.get(AttributeType.LOOP) != loop + && stack.peekRegion() instanceof LoopRegion + && RegionUtils.isRegionContainsBlock(stack.peekRegion(), out)) { // exit to outer loop which already processed out = null; } + + stack.push(loopRegion); stack.addExit(out); loopRegion.setBody(makeRegion(loopBody, stack)); + stack.pop(); } - stack.pop(); return out; } diff --git a/jadx-core/src/test/java/jadx/tests/internal/TestLoopCondition.java b/jadx-core/src/test/java/jadx/tests/internal/loops/TestLoopCondition.java similarity index 97% rename from jadx-core/src/test/java/jadx/tests/internal/TestLoopCondition.java rename to jadx-core/src/test/java/jadx/tests/internal/loops/TestLoopCondition.java index bc2ec602..a0eae622 100644 --- a/jadx-core/src/test/java/jadx/tests/internal/TestLoopCondition.java +++ b/jadx-core/src/test/java/jadx/tests/internal/loops/TestLoopCondition.java @@ -1,4 +1,4 @@ -package jadx.tests.internal; +package jadx.tests.internal.loops; import jadx.api.InternalJadxTest; import jadx.core.dex.nodes.ClassNode; diff --git a/jadx-core/src/test/java/jadx/tests/internal/loops/TestLoopDetection.java b/jadx-core/src/test/java/jadx/tests/internal/loops/TestLoopDetection.java new file mode 100644 index 00000000..1a294c07 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/loops/TestLoopDetection.java @@ -0,0 +1,37 @@ +package jadx.tests.internal.loops; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; + +public class TestLoopDetection extends InternalJadxTest { + + public static class TestCls { + + private void test(int[] a, int b) { + int i = 0; + while (i < a.length && i < b) { + a[i]++; + i++; + } + while (i < a.length) { + a[i]--; + i++; + } + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + System.out.println(code); + + assertThat(code, containsString("while (i < a.length && i < b) {")); + assertThat(code, containsString("while (i < a.length) {")); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/internal/loops/TestNestedLoops.java b/jadx-core/src/test/java/jadx/tests/internal/loops/TestNestedLoops.java new file mode 100644 index 00000000..090ab370 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/loops/TestNestedLoops.java @@ -0,0 +1,51 @@ +package jadx.tests.internal.loops; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import java.util.Iterator; +import java.util.List; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; + +public class TestNestedLoops extends InternalJadxTest { + + public static class TestCls { + + private void test(List l1, List l2) { + Iterator it1 = l1.iterator(); + while (it1.hasNext()) { + String s1 = it1.next(); + Iterator it2 = l2.iterator(); + while (it2.hasNext()) { + String s2 = it2.next(); + if (s1.equals(s2)) { + if (s1.length() == 5) { + l2.add(s1); + } else { + l1.remove(s2); + } + } + } + } + if (l2.size() > 0) { + l1.clear(); + } + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + System.out.println(code); + + assertThat(code, containsString("while (it1.hasNext()) {")); + assertThat(code, containsString("while (it2.hasNext()) {")); + assertThat(code, containsString("if (s1.equals(s2)) {")); + assertThat(code, containsString("l2.add(s1);")); + } +} diff --git a/jadx-samples/src/main/java/jadx/samples/TestCF3.java b/jadx-samples/src/main/java/jadx/samples/TestCF3.java index 6b766e28..5e722ebc 100644 --- a/jadx-samples/src/main/java/jadx/samples/TestCF3.java +++ b/jadx-samples/src/main/java/jadx/samples/TestCF3.java @@ -87,6 +87,23 @@ public class TestCF3 extends AbstractTest { return j > 10; } + private int testLoops(int[] a, int b) { + int i = 0; + while (i < a.length && i < b) { + a[i]++; + i++; + } + while (i < a.length) { + a[i]--; + i++; + } + int sum = 0; + for (int e : a) { + sum += e; + } + return sum; + } + public static boolean testLabeledBreakContinue() { String searchMe = "Look for a substring in me"; String substring = "sub"; @@ -227,6 +244,8 @@ public class TestCF3 extends AbstractTest { assertEquals(testComplexIfInLoop3(6), 6); assertEquals(testComplexIfInLoop3(8), 24); + assertEquals(testLoops(new int[]{1, 2, 3, 4, 5, 6}, 2), 19); + assertTrue(testInline() > 20); assertTrue(testInline2()); return true; -- GitLab