diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/BlockNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/BlockNode.java index 9ac9821d4..4945e1482 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/BlockNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/BlockNode.java @@ -5,6 +5,7 @@ import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.AttrNode; import jadx.core.dex.attributes.nodes.IgnoreEdgeAttr; import jadx.core.dex.attributes.nodes.LoopInfo; +import jadx.core.utils.BlockUtils; import jadx.core.utils.EmptyBitSet; import jadx.core.utils.InsnUtils; @@ -86,13 +87,8 @@ public class BlockNode extends AttrNode implements IBlock { } List toRemove = new LinkedList(); for (BlockNode b : sucList) { - if (b.contains(AType.EXC_HANDLER)) { + if (BlockUtils.isBlockMustBeCleared(b)) { toRemove.add(b); - } else if (b.contains(AFlag.SYNTHETIC)) { - List s = b.getSuccessors(); - if (s.size() == 1 && s.get(0).contains(AType.EXC_HANDLER)) { - toRemove.add(b); - } } } if (block.contains(AFlag.LOOP_END)) { diff --git a/jadx-core/src/main/java/jadx/core/dex/trycatch/TryCatchBlock.java b/jadx-core/src/main/java/jadx/core/dex/trycatch/TryCatchBlock.java index 8532db5a6..5fe4e5d4f 100644 --- a/jadx-core/src/main/java/jadx/core/dex/trycatch/TryCatchBlock.java +++ b/jadx-core/src/main/java/jadx/core/dex/trycatch/TryCatchBlock.java @@ -6,6 +6,7 @@ import jadx.core.dex.info.ClassInfo; import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; +import jadx.core.utils.BlockUtils; import jadx.core.utils.Utils; import java.util.ArrayList; @@ -63,6 +64,8 @@ public class TryCatchBlock { private void unbindHandler(ExceptionHandler handler) { for (BlockNode block : handler.getBlocks()) { + // skip synthetic loop exit blocks + BlockUtils.skipPredSyntheticPaths(block); block.add(AFlag.SKIP); ExcHandlerAttr excHandlerAttr = block.get(AType.EXC_HANDLER); if (excHandlerAttr != null) { 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 c31809c2e..82b66fdbe 100644 --- a/jadx-core/src/main/java/jadx/core/utils/BlockUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/BlockUtils.java @@ -72,16 +72,44 @@ public class BlockUtils { return null; } + public static boolean isBlockMustBeCleared(BlockNode b) { + if (b.contains(AType.EXC_HANDLER) || b.contains(AFlag.SKIP)) { + return true; + } + if (b.contains(AFlag.SYNTHETIC)) { + List s = b.getSuccessors(); + if (s.size() == 1 && s.get(0).contains(AType.EXC_HANDLER)) { + return true; + } + } + return false; + } + + /** + * Remove exception handlers from block nodes list + */ private static List cleanBlockList(List list) { List ret = new ArrayList(list.size()); for (BlockNode block : list) { - if (!block.contains(AType.EXC_HANDLER)) { + if (!isBlockMustBeCleared(block)) { ret.add(block); } } return ret; } + /** + * Remove exception handlers from block nodes bitset + */ + public static void cleanBitSet(MethodNode mth, BitSet bs) { + for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) { + BlockNode block = mth.getBasicBlocks().get(i); + if (isBlockMustBeCleared(block)) { + bs.clear(i); + } + } + } + /** * Return predecessors list without blocks contains 'IGNORE_EDGE' attribute. * @@ -111,18 +139,6 @@ public class BlockUtils { return from.getSuccessors().contains(to); } - /** - * Remove exception handlers from block nodes bitset - */ - public static void cleanBitSet(MethodNode mth, BitSet bs) { - for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) { - BlockNode block = mth.getBasicBlocks().get(i); - if (block.contains(AType.EXC_HANDLER)) { - bs.clear(i); - } - } - } - /** * Check if instruction contains in block (use == for comparison, not equals) */ @@ -461,6 +477,20 @@ public class BlockUtils { return list.isEmpty() ? Collections.emptyList() : list; } + /** + * Set 'SKIP' flag for all synthetic predecessors from start block. + */ + public static void skipPredSyntheticPaths(BlockNode block) { + for (BlockNode pred : block.getPredecessors()) { + if (pred.contains(AFlag.SYNTHETIC) + && !pred.contains(AType.SPLITTER_BLOCK) + && pred.getInstructions().isEmpty()) { + pred.add(AFlag.SKIP); + skipPredSyntheticPaths(pred); + } + } + } + /** * Return true if on path from start to end no instructions and no branches. */ diff --git a/jadx-core/src/test/java/jadx/tests/integration/loops/TestTryCatchInLoop2.java b/jadx-core/src/test/java/jadx/tests/integration/loops/TestTryCatchInLoop2.java new file mode 100644 index 000000000..e5f7eb7a6 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/loops/TestTryCatchInLoop2.java @@ -0,0 +1,46 @@ +package jadx.tests.integration.loops; + +import jadx.core.dex.nodes.ClassNode; +import jadx.tests.api.IntegrationTest; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; + +import static jadx.tests.api.utils.JadxMatchers.containsOne; +import static org.junit.Assert.assertThat; + +public class TestTryCatchInLoop2 extends IntegrationTest { + + public static class TestCls { + private static class MyItem { + int idx; + String name; + } + + private final Map mCache = new HashMap(); + + void test(MyItem[] items) { + synchronized (this.mCache) { + for (int i = 0; i < items.length; ++i) { + MyItem existingItem = mCache.get(items[i].idx); + if (null == existingItem) { + mCache.put(items[i].idx, items[i]); + } else { + existingItem.name = items[i].name; + } + } + } + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + + assertThat(code, containsOne("synchronized (this.mCache) {")); + assertThat(code, containsOne("for (int i = 0; i < items.length; i++) {")); + } +}