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 b7feecff5..1ec50ae15 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 @@ -25,10 +25,9 @@ public final class BlockNode extends AttrNode implements IBlock, Comparable dominatesOn = new ArrayList<>(3); - public BlockNode(int cid, int id, int offset) { + public BlockNode(int cid, int pos, int offset) { this.cid = cid; - this.id = id; + this.pos = pos; this.startOffset = offset; } @@ -81,12 +80,20 @@ public final class BlockNode extends AttrNode implements IBlock, Comparable getPredecessors() { @@ -105,6 +112,13 @@ public final class BlockNode extends AttrNode implements IBlock, Comparable blocks) { + int count = blocks.size(); + for (int i = 0; i < count; i++) { + blocks.get(i).setPos(i); + } + } + public void lock() { try { List successorsList = successors; @@ -161,7 +175,7 @@ public final class BlockNode extends AttrNode implements IBlock, Comparable blocks) { this.blocks = blocks; - updateBlockIds(blocks); + updateBlockPositions(); } - public void updateBlockIds(List blocks) { - int count = blocks.size(); - for (int i = 0; i < count; i++) { - blocks.get(i).setId(i); - } + public void updateBlockPositions() { + BlockNode.updateBlockPositions(blocks); } public int getNextBlockCId() { diff --git a/jadx-core/src/main/java/jadx/core/dex/regions/conditions/ConditionRegion.java b/jadx-core/src/main/java/jadx/core/dex/regions/conditions/ConditionRegion.java index 2756f72b2..f7f86a054 100644 --- a/jadx-core/src/main/java/jadx/core/dex/regions/conditions/ConditionRegion.java +++ b/jadx-core/src/main/java/jadx/core/dex/regions/conditions/ConditionRegion.java @@ -68,11 +68,11 @@ public abstract class ConditionRegion extends AbstractRegion implements IConditi } /** - * Prefer way for update condition info + * Preferred way to update condition info */ public void updateCondition(IfInfo info) { this.condition = info.getCondition(); - this.conditionBlocks = info.getMergedBlocks(); + this.conditionBlocks = info.getMergedBlocks().toList(); } public void updateCondition(IfCondition condition, List conditionBlocks) { diff --git a/jadx-core/src/main/java/jadx/core/dex/regions/conditions/IfInfo.java b/jadx-core/src/main/java/jadx/core/dex/regions/conditions/IfInfo.java index b7667e71f..bd85f069c 100644 --- a/jadx-core/src/main/java/jadx/core/dex/regions/conditions/IfInfo.java +++ b/jadx-core/src/main/java/jadx/core/dex/regions/conditions/IfInfo.java @@ -8,11 +8,12 @@ import java.util.Set; import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; +import jadx.core.utils.blocks.BlockSet; public final class IfInfo { private final MethodNode mth; private final IfCondition condition; - private final List mergedBlocks; + private final BlockSet mergedBlocks; private final BlockNode thenBlock; private final BlockNode elseBlock; private final Set skipBlocks; @@ -20,7 +21,7 @@ public final class IfInfo { private BlockNode outBlock; public IfInfo(MethodNode mth, IfCondition condition, BlockNode thenBlock, BlockNode elseBlock) { - this(mth, condition, thenBlock, elseBlock, new ArrayList<>(), new HashSet<>(), new ArrayList<>()); + this(mth, condition, thenBlock, elseBlock, BlockSet.empty(mth), new HashSet<>(), new ArrayList<>()); } public IfInfo(IfInfo info, BlockNode thenBlock, BlockNode elseBlock) { @@ -29,7 +30,7 @@ public final class IfInfo { } private IfInfo(MethodNode mth, IfCondition condition, BlockNode thenBlock, BlockNode elseBlock, - List mergedBlocks, Set skipBlocks, List forceInlineInsns) { + BlockSet mergedBlocks, Set skipBlocks, List forceInlineInsns) { this.mth = mth; this.condition = condition; this.thenBlock = thenBlock; @@ -56,7 +57,11 @@ public final class IfInfo { @Deprecated public BlockNode getFirstIfBlock() { - return mergedBlocks.get(0); + return mergedBlocks.getFirst(); + } + + public BlockSet getMergedBlocks() { + return mergedBlocks; } public MethodNode getMth() { @@ -67,10 +72,6 @@ public final class IfInfo { return condition; } - public List getMergedBlocks() { - return mergedBlocks; - } - public Set getSkipBlocks() { return skipBlocks; } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java index 27d28a2a6..b521d1489 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java @@ -89,7 +89,7 @@ public class DotGraphVisitor extends AbstractVisitor { public void process(MethodNode mth) { dot.startLine("digraph \"CFG for"); - dot.add(escape(mth.getParentClass() + "." + mth.getMethodInfo().getShortId())); + dot.add(escape(mth.getMethodInfo().getFullId())); dot.add("\" {"); BlockNode enterBlock = mth.getEnterBlock(); @@ -204,7 +204,7 @@ public class DotGraphVisitor extends AbstractVisitor { dot.add("color=red,"); } dot.add("label=\"{"); - dot.add(String.valueOf(block.getId())).add("\\:\\ "); + dot.add(String.valueOf(block.getCId())).add("\\:\\ "); dot.add(InsnUtils.formatOffset(block.getStartOffset())); if (!attrs.isEmpty()) { dot.add('|').add(attrs); @@ -237,10 +237,10 @@ public class DotGraphVisitor extends AbstractVisitor { if (PRINT_DOMINATORS) { for (BlockNode c : block.getDominatesOn()) { - conn.startLine(block.getId() + " -> " + c.getId() + "[color=green];"); + conn.startLine(block.getCId() + " -> " + c.getCId() + "[color=green];"); } for (BlockNode dom : BlockUtils.bitSetToBlocks(mth, block.getDomFrontier())) { - conn.startLine("f_" + block.getId() + " -> f_" + dom.getId() + "[color=blue];"); + conn.startLine("f_" + block.getCId() + " -> f_" + dom.getCId() + "[color=blue];"); } } } @@ -280,7 +280,7 @@ public class DotGraphVisitor extends AbstractVisitor { private String makeName(IContainer c) { String name; if (c instanceof BlockNode) { - name = "Node_" + ((BlockNode) c).getId(); + name = "Node_" + ((BlockNode) c).getCId(); } else if (c instanceof IBlock) { name = "Node_" + c.getClass().getSimpleName() + '_' + c.hashCode(); } else { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockExceptionHandler.java b/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockExceptionHandler.java index c8408d0f2..75cc71c71 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockExceptionHandler.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockExceptionHandler.java @@ -68,7 +68,7 @@ public class BlockExceptionHandler { BlockProcessor.removeMarkedBlocks(mth); BlockSet sorted = new BlockSet(mth); - BlockUtils.visitDFS(mth, sorted::set); + BlockUtils.visitDFS(mth, sorted::add); removeUnusedExcHandlers(mth, tryBlocks, sorted); return true; } @@ -623,7 +623,7 @@ public class BlockExceptionHandler { for (ExceptionHandler eh : mth.getExceptionHandlers()) { boolean notProcessed = true; BlockNode handlerBlock = eh.getHandlerBlock(); - if (handlerBlock == null || blocks.get(handlerBlock)) { + if (handlerBlock == null || blocks.contains(handlerBlock)) { continue; } for (TryCatchBlockAttr tcb : tryBlocks) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockProcessor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockProcessor.java index f60d4735e..3d3809d97 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockProcessor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockProcessor.java @@ -103,7 +103,7 @@ public class BlockProcessor extends AbstractVisitor { * - post dominators (only if {@link AFlag#COMPUTE_POST_DOM} added to method) * - loops and nested loop info * - * + *

* This method should be called after changing a block tree in custom passes added before * {@link BlockFinisher}. */ @@ -679,7 +679,7 @@ public class BlockProcessor extends AbstractVisitor { } public static void removeMarkedBlocks(MethodNode mth) { - mth.getBasicBlocks().removeIf(block -> { + boolean removed = mth.getBasicBlocks().removeIf(block -> { if (block.contains(AFlag.REMOVE)) { if (!block.getPredecessors().isEmpty() || !block.getSuccessors().isEmpty()) { LOG.warn("Block {} not deleted, method: {}", block, mth); @@ -693,6 +693,9 @@ public class BlockProcessor extends AbstractVisitor { } return false; }); + if (removed) { + mth.updateBlockPositions(); + } } private static void removeUnreachableBlocks(MethodNode mth) { @@ -728,6 +731,7 @@ public class BlockProcessor extends AbstractVisitor { toRemove.forEach(BlockSplitter::detachBlock); mth.getBasicBlocks().removeAll(toRemove); + mth.updateBlockPositions(); } private static void clearBlocksState(MethodNode mth) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockSplitter.java b/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockSplitter.java index c10f28c18..3464fe716 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockSplitter.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockSplitter.java @@ -79,6 +79,7 @@ public class BlockSplitter extends AbstractVisitor { addTempConnectionsForExcHandlers(mth, blocksMap); setupExitConnections(mth); + mth.updateBlockPositions(); mth.unloadInsnArr(); } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/PostDominatorTree.java b/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/PostDominatorTree.java index 59f31e056..e09decd9f 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/PostDominatorTree.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/blocks/PostDominatorTree.java @@ -20,14 +20,14 @@ public class PostDominatorTree { int mthBlocksCount = mth.getBasicBlocks().size(); List sorted = new ArrayList<>(mthBlocksCount); BlockUtils.visitReverseDFS(mth, sorted::add); - // temporary set block ids to match reverse sorted order - // save old ids for later remapping + // temporary set block positions to match reverse sorted order + // save old positions for later remapping int blocksCount = sorted.size(); - int[] ids = new int[mthBlocksCount]; + int[] posMapping = new int[mthBlocksCount]; for (int i = 0; i < blocksCount; i++) { - ids[i] = sorted.get(i).getId(); + posMapping[i] = sorted.get(i).getPos(); } - mth.updateBlockIds(sorted); + BlockNode.updateBlockPositions(sorted); BlockNode[] postDoms = DominatorTree.build(sorted, BlockNode::getSuccessors); BlockNode firstBlock = sorted.get(0); @@ -43,8 +43,8 @@ public class PostDominatorTree { for (int i = 1; i < blocksCount; i++) { BlockNode block = sorted.get(i); BitSet bs = new BitSet(blocksCount); - block.getPostDoms().stream().forEach(n -> bs.set(ids[n])); - bs.clear(ids[i]); + block.getPostDoms().stream().forEach(n -> bs.set(posMapping[n])); + bs.clear(posMapping[i]); block.setPostDoms(bs); } // check for missing blocks in 'sorted' list @@ -65,8 +65,8 @@ public class PostDominatorTree { // show error as a warning because this info not always used mth.addWarnComment("Failed to build post-dominance tree", e); } finally { - // revert block ids change - mth.updateBlockIds(mth.getBasicBlocks()); + // revert block positions change + mth.updateBlockPositions(); } } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/maker/IfRegionMaker.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/maker/IfRegionMaker.java index ce94e72ec..4497adbaa 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/maker/IfRegionMaker.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/maker/IfRegionMaker.java @@ -29,6 +29,7 @@ import jadx.core.dex.regions.conditions.IfInfo; import jadx.core.dex.regions.conditions.IfRegion; import jadx.core.dex.regions.loops.LoopRegion; import jadx.core.utils.BlockUtils; +import jadx.core.utils.blocks.BlockSet; import jadx.core.utils.exceptions.JadxRuntimeException; import static jadx.core.utils.BlockUtils.isEqualPaths; @@ -228,7 +229,7 @@ final class IfRegionMaker { private static boolean allPathsFromIf(BlockNode block, IfInfo info) { List preds = block.getPredecessors(); - List ifBlocks = info.getMergedBlocks(); + BlockSet ifBlocks = info.getMergedBlocks(); for (BlockNode pred : preds) { if (pred.contains(AFlag.LOOP_END)) { // ignore loop back edge diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/maker/LoopRegionMaker.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/maker/LoopRegionMaker.java index 5ce38ac86..d1d31061b 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/maker/LoopRegionMaker.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/maker/LoopRegionMaker.java @@ -77,7 +77,7 @@ final class LoopRegionMaker { loopRegion.updateCondition(condInfo); // prevent if's merge with loop condition condInfo.getMergedBlocks().forEach(b -> b.add(AFlag.ADDED_TO_REGION)); - exitBlocks.removeAll(condInfo.getMergedBlocks()); + exitBlocks.removeAll(condInfo.getMergedBlocks().toList()); if (!exitBlocks.isEmpty()) { BlockNode loopExit = condInfo.getElseBlock(); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/maker/RegionMaker.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/maker/RegionMaker.java index aadd88add..3fda1bae5 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/maker/RegionMaker.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/maker/RegionMaker.java @@ -1,7 +1,6 @@ package jadx.core.dex.visitors.regions.maker; import java.util.ArrayList; -import java.util.BitSet; import java.util.List; import java.util.Objects; @@ -19,6 +18,7 @@ import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.regions.Region; import jadx.core.utils.BlockUtils; +import jadx.core.utils.blocks.BlockSet; import jadx.core.utils.exceptions.JadxOverflowException; import static jadx.core.utils.BlockUtils.getNextBlock; @@ -30,7 +30,7 @@ public class RegionMaker { private final IfRegionMaker ifMaker; private final LoopRegionMaker loopMaker; - private final BitSet processedBlocks; + private final BlockSet processedBlocks; private final int regionsLimit; private int regionsCount; @@ -40,9 +40,8 @@ public class RegionMaker { this.stack = new RegionStack(mth); this.ifMaker = new IfRegionMaker(mth, this); this.loopMaker = new LoopRegionMaker(mth, this, ifMaker); - int blocksCount = mth.getBasicBlocks().size(); - this.processedBlocks = new BitSet(blocksCount); - this.regionsLimit = blocksCount * 100; + this.processedBlocks = BlockSet.empty(mth); + this.regionsLimit = mth.getBasicBlocks().size() * 100; } public Region makeMthRegion() { @@ -57,12 +56,10 @@ public class RegionMaker { return region; } - int startBlockId = startBlock.getId(); - if (processedBlocks.get(startBlockId)) { + if (processedBlocks.addChecked(startBlock)) { mth.addWarn("Removed duplicated region for block: " + startBlock + ' ' + startBlock.getAttributesString()); return region; } - processedBlocks.set(startBlockId); BlockNode next = startBlock; while (next != null) { @@ -159,10 +156,10 @@ public class RegionMaker { } boolean isProcessed(BlockNode block) { - return processedBlocks.get(block.getId()); + return processedBlocks.contains(block); } void clearBlockProcessedState(BlockNode block) { - processedBlocks.clear(block.getId()); + processedBlocks.remove(block); } } 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 0d040b8e2..ba28ca1c0 100644 --- a/jadx-core/src/main/java/jadx/core/utils/BlockUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/BlockUtils.java @@ -443,7 +443,7 @@ public class BlockUtils { */ public static @Nullable BlockNode getPrevBlockOnPath(MethodNode mth, BlockNode block, BlockNode pathStart) { BlockSet preds = BlockSet.from(mth, block.getPredecessors()); - if (preds.get(pathStart)) { + if (preds.contains(pathStart)) { return pathStart; } DFSIteration dfs = new DFSIteration(mth, pathStart, BlockNode::getCleanSuccessors); @@ -452,7 +452,7 @@ public class BlockUtils { if (next == null) { return null; } - if (preds.get(next)) { + if (preds.contains(next)) { return next; } } 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 index 1c9073645..f8a6b5ca1 100644 --- a/jadx-core/src/main/java/jadx/core/utils/blocks/BlockSet.java +++ b/jadx-core/src/main/java/jadx/core/utils/blocks/BlockSet.java @@ -4,20 +4,32 @@ import java.util.ArrayList; import java.util.BitSet; import java.util.Collection; import java.util.Collections; +import java.util.Iterator; import java.util.List; +import java.util.NoSuchElementException; +import java.util.Spliterator; +import java.util.Spliterators; import java.util.function.Consumer; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.utils.EmptyBitSet; -public class BlockSet { +/** + * BlockNode set implementation based on BitSet. + */ +public class BlockSet implements Iterable { + + public static BlockSet empty(MethodNode mth) { + return new BlockSet(mth); + } public static BlockSet from(MethodNode mth, Collection blocks) { BlockSet newBS = new BlockSet(mth); - newBS.set(blocks); + newBS.addAll(blocks); return newBS; } @@ -29,28 +41,49 @@ public class BlockSet { this.bs = new BitSet(mth.getBasicBlocks().size()); } - public boolean get(BlockNode block) { - return bs.get(block.getId()); + public boolean contains(BlockNode block) { + return bs.get(block.getPos()); } - public void set(BlockNode block) { - bs.set(block.getId()); + public void add(BlockNode block) { + bs.set(block.getPos()); } - public void set(Collection blocks) { - blocks.forEach(this::set); + public void addAll(Collection blocks) { + blocks.forEach(this::add); } - public boolean checkAndSet(BlockNode block) { - int id = block.getId(); + public void addAll(BlockSet otherBlockSet) { + bs.or(otherBlockSet.bs); + } + + public void remove(BlockNode block) { + bs.clear(block.getPos()); + } + + public void remove(Collection blocks) { + blocks.forEach(this::remove); + } + + public boolean addChecked(BlockNode block) { + int id = block.getPos(); boolean state = bs.get(id); bs.set(id); return state; } + public boolean containsAll(List blocks) { + for (BlockNode block : blocks) { + if (!contains(block)) { + return false; + } + } + return true; + } + public boolean intersects(List blocks) { for (BlockNode block : blocks) { - if (get(block)) { + if (contains(block)) { return true; } } @@ -67,13 +100,17 @@ public class BlockSet { } public boolean isEmpty() { - return bs.cardinality() == 0; + return bs.isEmpty(); } public int size() { return bs.cardinality(); } + public void remove() { + bs.clear(); + } + public @Nullable BlockNode getOne() { if (bs.cardinality() == 1) { return mth.getBasicBlocks().get(bs.nextSetBit(0)); @@ -81,6 +118,11 @@ public class BlockSet { return null; } + public BlockNode getFirst() { + return mth.getBasicBlocks().get(bs.nextSetBit(0)); + } + + @Override public void forEach(Consumer consumer) { if (bs.isEmpty()) { return; @@ -91,6 +133,18 @@ public class BlockSet { } } + @Override + public @NotNull Iterator iterator() { + return new BlockSetIterator(bs, size(), mth.getBasicBlocks()); + } + + @Override + public Spliterator spliterator() { + int size = size(); + BlockSetIterator iterator = new BlockSetIterator(bs, size, mth.getBasicBlocks()); + return Spliterators.spliterator(iterator, size, Spliterator.ORDERED | Spliterator.DISTINCT); + } + public List toList() { if (bs == null || bs == EmptyBitSet.EMPTY) { return Collections.emptyList(); @@ -111,4 +165,35 @@ public class BlockSet { public String toString() { return toList().toString(); } + + private static final class BlockSetIterator implements Iterator { + private final BitSet bs; + private final int size; + private final List blocks; + + private int cursor; + private int start; + + public BlockSetIterator(BitSet bs, int size, List blocks) { + this.bs = bs; + this.size = size; + this.blocks = blocks; + } + + @Override + public boolean hasNext() { + return cursor != size; + } + + @Override + public BlockNode next() { + int pos = bs.nextSetBit(start); + if (pos == -1) { + throw new NoSuchElementException(); + } + start = pos + 1; + cursor++; + return blocks.get(pos); + } + } } diff --git a/jadx-core/src/main/java/jadx/core/utils/blocks/DFSIteration.java b/jadx-core/src/main/java/jadx/core/utils/blocks/DFSIteration.java index c8ce2b529..1212ac64c 100644 --- a/jadx-core/src/main/java/jadx/core/utils/blocks/DFSIteration.java +++ b/jadx-core/src/main/java/jadx/core/utils/blocks/DFSIteration.java @@ -20,7 +20,7 @@ public class DFSIteration { queue = new ArrayDeque<>(); visited = new BlockSet(mth); queue.addLast(startBlock); - visited.set(startBlock); + visited.add(startBlock); } public @Nullable BlockNode next() { @@ -32,7 +32,7 @@ public class DFSIteration { int count = nextBlocks.size(); for (int i = count - 1; i >= 0; i--) { // to preserve order in queue BlockNode next = nextBlocks.get(i); - if (!visited.checkAndSet(next)) { + if (!visited.addChecked(next)) { queue.addLast(next); } }