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 a7fd486e1..0448f2363 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 @@ -73,4 +73,6 @@ public enum AFlag { SOFT_CAST, // synthetic cast to help type inference INCONSISTENT_CODE, // warning about incorrect decompilation + + REQUEST_IF_REGION_OPTIMIZE, // run if region visitor again } 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 b3e09b379..d06e58ff2 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 @@ -26,7 +26,10 @@ public class IfRegionVisitor extends AbstractVisitor { if (mth.isNoCode()) { return; } + process(mth); + } + public static void process(MethodNode mth) { DepthRegionTraversal.traverseIterative(mth, TERNARY_VISITOR); DepthRegionTraversal.traverse(mth, PROCESS_IF_REGION_VISITOR); DepthRegionTraversal.traverseIterative(mth, REMOVE_REDUNDANT_ELSE_VISITOR); 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 5c2e4d178..c22a89e0d 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 @@ -53,35 +53,43 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor @Override public void visit(MethodNode mth) { DepthRegionTraversal.traverse(mth, this); + if (mth.contains(AFlag.REQUEST_IF_REGION_OPTIMIZE)) { + IfRegionVisitor.process(mth); + mth.remove(AFlag.REQUEST_IF_REGION_OPTIMIZE); + } } @Override public boolean enterRegion(MethodNode mth, IRegion region) { if (region instanceof LoopRegion) { - processLoopRegion(mth, (LoopRegion) region); + if (processLoopRegion(mth, (LoopRegion) region)) { + // optimize `if` block after instructions remove + mth.add(AFlag.REQUEST_IF_REGION_OPTIMIZE); + } } return true; } - private static void processLoopRegion(MethodNode mth, LoopRegion loopRegion) { + private static boolean processLoopRegion(MethodNode mth, LoopRegion loopRegion) { if (loopRegion.isConditionAtEnd()) { - return; + return false; } IfCondition condition = loopRegion.getCondition(); if (condition == null) { - return; + return false; } if (checkForIndexedLoop(mth, loopRegion, condition)) { - return; + return true; } - checkIterableForEach(mth, loopRegion, condition); + return checkIterableForEach(mth, loopRegion, condition); } /** * Check for indexed loop. */ private static boolean checkForIndexedLoop(MethodNode mth, LoopRegion loopRegion, IfCondition condition) { - InsnNode incrInsn = RegionUtils.getLastInsn(loopRegion); + BlockNode loopEndBlock = loopRegion.getInfo().getEnd(); + InsnNode incrInsn = BlockUtils.getLastInsn(BlockUtils.skipSyntheticPredecessor(loopEndBlock)); if (incrInsn == null) { return false; } diff --git a/jadx-core/src/test/java/jadx/tests/integration/loops/TestArrayForEach3.java b/jadx-core/src/test/java/jadx/tests/integration/loops/TestArrayForEach3.java new file mode 100644 index 000000000..857e1083d --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/loops/TestArrayForEach3.java @@ -0,0 +1,44 @@ +package jadx.tests.integration.loops; + +import org.junit.jupiter.api.Test; + +import jadx.tests.api.IntegrationTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; +import static org.assertj.core.api.Assertions.fail; + +/** + * Issue #977 + */ +public class TestArrayForEach3 extends IntegrationTest { + + public static class TestCls { + public void test(String[] arr) { + for (String s : arr) { + if (s.length() > 0) { + return; + } + } + throw new IllegalArgumentException("All strings are empty"); + } + + public void check() { + test(new String[] { "", "a" }); // no exception + try { + test(new String[] { "", "" }); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + // expected + } + } + } + + @Test + public void test() { + noDebugInfo(); + assertThat(getClassNode(TestCls.class)) + .code() + .doesNotContain("while") + .containsOne("for (String str : strArr) {"); + } +}