fix: cache finally extract checks on multiple paths (#1853)
This commit is contained in:
@@ -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<BlockPair, Boolean> 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<BlockPair, Boolean> checksCache) {
|
||||
BlockPair checkBlocks = new BlockPair(dupBlock, finallyBlock);
|
||||
Boolean checked = checksCache.get(checkBlocks);
|
||||
if (checked != null) {
|
||||
return checked;
|
||||
}
|
||||
boolean same;
|
||||
InsnsSlice finallySlice = extractInfo.getFinallyInsnsSlice();
|
||||
|
||||
List<BlockNode> finallyCS = getSuccessorsWithoutLoop(finallyBlock);
|
||||
List<BlockNode> 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<BlockNode> getSuccessorsWithoutLoop(BlockNode block) {
|
||||
|
||||
@@ -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 + ')';
|
||||
}
|
||||
}
|
||||
@@ -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<? super BlockNode> consumer) {
|
||||
if (bs.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<BlockNode> blocks = mth.getBasicBlocks();
|
||||
for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
|
||||
consumer.accept(blocks.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
public List<BlockNode> toList() {
|
||||
if (bs == null || bs == EmptyBitSet.EMPTY) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
int size = bs.cardinality();
|
||||
if (size == 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<BlockNode> mthBlocks = mth.getBasicBlocks();
|
||||
List<BlockNode> 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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user