diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/finaly/MarkFinallyVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/finaly/MarkFinallyVisitor.java index f7f57385a..bf00c8ebe 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/finaly/MarkFinallyVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/finaly/MarkFinallyVisitor.java @@ -1,7 +1,9 @@ package jadx.core.dex.visitors.finaly; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -32,6 +34,7 @@ import jadx.core.utils.BlockUtils; import jadx.core.utils.InsnList; import jadx.core.utils.ListUtils; import jadx.core.utils.Utils; +import jadx.core.utils.blocks.BlockPair; @JadxVisitor( name = "MarkFinallyVisitor", @@ -351,9 +354,14 @@ public class MarkFinallyVisitor extends AbstractVisitor { if (dupSlice == null) { return null; } - if (!dupSlice.isComplete() - && !checkBlocksTree(dupBlock, startBlock, dupSlice, extractInfo)) { - return null; + if (!dupSlice.isComplete()) { + Map checkCache = new HashMap<>(); + if (checkBlocksTree(dupBlock, startBlock, dupSlice, extractInfo, checkCache)) { + dupSlice.setComplete(true); + extractInfo.getFinallyInsnsSlice().setComplete(true); + } else { + return null; + } } return checkTempSlice(dupSlice); } @@ -470,30 +478,41 @@ public class MarkFinallyVisitor extends AbstractVisitor { } private static boolean checkBlocksTree(BlockNode dupBlock, BlockNode finallyBlock, - InsnsSlice dupSlice, FinallyExtractInfo extractInfo) { + InsnsSlice dupSlice, FinallyExtractInfo extractInfo, + Map checksCache) { + BlockPair checkBlocks = new BlockPair(dupBlock, finallyBlock); + Boolean checked = checksCache.get(checkBlocks); + if (checked != null) { + return checked; + } + boolean same; InsnsSlice finallySlice = extractInfo.getFinallyInsnsSlice(); - List finallyCS = getSuccessorsWithoutLoop(finallyBlock); List dupCS = getSuccessorsWithoutLoop(dupBlock); if (finallyCS.size() == dupCS.size()) { + same = true; for (int i = 0; i < finallyCS.size(); i++) { BlockNode finSBlock = finallyCS.get(i); BlockNode dupSBlock = dupCS.get(i); if (extractInfo.getAllHandlerBlocks().contains(finSBlock)) { if (!compareBlocks(dupSBlock, finSBlock, dupSlice, extractInfo)) { - return false; + same = false; + break; } - if (!checkBlocksTree(dupSBlock, finSBlock, dupSlice, extractInfo)) { - return false; + if (!checkBlocksTree(dupSBlock, finSBlock, dupSlice, extractInfo, checksCache)) { + same = false; + break; } dupSlice.addBlock(dupSBlock); finallySlice.addBlock(finSBlock); } } + } else { + // stop checks at start blocks (already compared) + same = true; } - dupSlice.setComplete(true); - finallySlice.setComplete(true); - return true; + checksCache.put(checkBlocks, same); + return same; } private static List getSuccessorsWithoutLoop(BlockNode block) { diff --git a/jadx-core/src/main/java/jadx/core/utils/blocks/BlockPair.java b/jadx-core/src/main/java/jadx/core/utils/blocks/BlockPair.java new file mode 100644 index 000000000..571b1176f --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/utils/blocks/BlockPair.java @@ -0,0 +1,43 @@ +package jadx.core.utils.blocks; + +import jadx.core.dex.nodes.BlockNode; + +public class BlockPair { + private final BlockNode first; + private final BlockNode second; + + public BlockPair(BlockNode first, BlockNode second) { + this.first = first; + this.second = second; + } + + public BlockNode getFirst() { + return first; + } + + public BlockNode getSecond() { + return second; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof BlockPair)) { + return false; + } + BlockPair other = (BlockPair) o; + return first.equals(other.first) && second.equals(other.second); + } + + @Override + public int hashCode() { + return first.hashCode() + 31 * second.hashCode(); + } + + @Override + public String toString() { + return "(" + first + ", " + second + ')'; + } +} diff --git a/jadx-core/src/main/java/jadx/core/utils/blocks/BlockSet.java b/jadx-core/src/main/java/jadx/core/utils/blocks/BlockSet.java new file mode 100644 index 000000000..ecd695bd8 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/utils/blocks/BlockSet.java @@ -0,0 +1,68 @@ +package jadx.core.utils.blocks; + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; + +import jadx.core.dex.nodes.BlockNode; +import jadx.core.dex.nodes.MethodNode; +import jadx.core.utils.EmptyBitSet; + +public class BlockSet { + + private final MethodNode mth; + private final BitSet bs; + + public BlockSet(MethodNode mth) { + this.mth = mth; + this.bs = new BitSet(mth.getBasicBlocks().size()); + } + + public boolean get(BlockNode block) { + return bs.get(block.getId()); + } + + public void set(BlockNode block) { + bs.set(block.getId()); + } + + public boolean checkAndSet(BlockNode block) { + int id = block.getId(); + boolean state = bs.get(id); + bs.set(id); + return state; + } + + public void forEach(Consumer consumer) { + if (bs.isEmpty()) { + return; + } + List blocks = mth.getBasicBlocks(); + for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) { + consumer.accept(blocks.get(i)); + } + } + + public List toList() { + if (bs == null || bs == EmptyBitSet.EMPTY) { + return Collections.emptyList(); + } + int size = bs.cardinality(); + if (size == 0) { + return Collections.emptyList(); + } + List mthBlocks = mth.getBasicBlocks(); + List blocks = new ArrayList<>(size); + for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) { + blocks.add(mthBlocks.get(i)); + } + return blocks; + } + + @Override + public String toString() { + return toList().toString(); + } +}