diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ConstructorVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ConstructorVisitor.java index 49720daa4..f2e622029 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ConstructorVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ConstructorVisitor.java @@ -115,8 +115,8 @@ public class ConstructorVisitor extends AbstractVisitor { PhiInsn phiInsn = SSATransform.addPhi(mth, crossBlock, useArg.getRegNum()); phiInsn.setResult(useArg.duplicate()); - phiInsn.bindArg(newResArg.duplicate(), BlockUtils.getPrevBlockOnPath(crossBlock, curBlock)); - phiInsn.bindArg(otherResArg.duplicate(), BlockUtils.getPrevBlockOnPath(crossBlock, otherBlock)); + phiInsn.bindArg(newResArg.duplicate(), BlockUtils.getPrevBlockOnPath(mth, crossBlock, curBlock)); + phiInsn.bindArg(otherResArg.duplicate(), BlockUtils.getPrevBlockOnPath(mth, crossBlock, otherBlock)); phiInsn.rebindArgs(); otherCtr.setResult(otherResArg.duplicate()); 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 ec070ff8d..00234bf10 100644 --- a/jadx-core/src/main/java/jadx/core/utils/BlockUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/BlockUtils.java @@ -37,6 +37,7 @@ import jadx.core.dex.regions.conditions.IfCondition; import jadx.core.dex.trycatch.CatchAttr; import jadx.core.dex.trycatch.ExceptionHandler; import jadx.core.utils.blocks.BlockSet; +import jadx.core.utils.blocks.DFSIteration; import jadx.core.utils.exceptions.JadxRuntimeException; public class BlockUtils { @@ -437,18 +438,21 @@ public class BlockUtils { /** * Return predecessor on path from 'pathStart' block */ - public static @Nullable BlockNode getPrevBlockOnPath(BlockNode block, BlockNode pathStart) { - List preds = block.getPredecessors(); - if (preds.contains(pathStart)) { + public static @Nullable BlockNode getPrevBlockOnPath(MethodNode mth, BlockNode block, BlockNode pathStart) { + BlockSet preds = BlockSet.from(mth, block.getPredecessors()); + if (preds.get(pathStart)) { return pathStart; } - Set path = getAllPathsBlocks(pathStart, block); - for (BlockNode p : preds) { - if (path.contains(p)) { - return p; + DFSIteration dfs = new DFSIteration(mth, pathStart, BlockNode::getCleanSuccessors); + while (true) { + BlockNode next = dfs.next(); + if (next == null) { + return null; + } + if (preds.get(next)) { + return next; } } - return null; } /** @@ -515,24 +519,13 @@ public class BlockUtils { private static void visitDFS(MethodNode mth, BlockNode startBlock, Function> nextFunc, Consumer visitor) { - BlockSet visited = new BlockSet(mth); - Deque queue = new ArrayDeque<>(); - queue.addLast(startBlock); - visited.set(startBlock); + DFSIteration dfsIteration = new DFSIteration(mth, startBlock, nextFunc); while (true) { - BlockNode current = queue.pollLast(); - if (current == null) { + BlockNode next = dfsIteration.next(); + if (next == null) { return; } - visitor.accept(current); - List nextBlocks = nextFunc.apply(current); - 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)) { - queue.addLast(next); - } - } + visitor.accept(next); } } 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 new file mode 100644 index 000000000..c8ce2b529 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/utils/blocks/DFSIteration.java @@ -0,0 +1,41 @@ +package jadx.core.utils.blocks; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.List; +import java.util.function.Function; + +import org.jetbrains.annotations.Nullable; + +import jadx.core.dex.nodes.BlockNode; +import jadx.core.dex.nodes.MethodNode; + +public class DFSIteration { + private final Function> nextFunc; + private final Deque queue; + private final BlockSet visited; + + public DFSIteration(MethodNode mth, BlockNode startBlock, Function> next) { + nextFunc = next; + queue = new ArrayDeque<>(); + visited = new BlockSet(mth); + queue.addLast(startBlock); + visited.set(startBlock); + } + + public @Nullable BlockNode next() { + BlockNode current = queue.pollLast(); + if (current == null) { + return null; + } + List nextBlocks = nextFunc.apply(current); + 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)) { + queue.addLast(next); + } + } + return current; + } +} diff --git a/jadx-core/src/test/java/jadx/tests/integration/others/TestConstructorBranched2.java b/jadx-core/src/test/java/jadx/tests/integration/others/TestConstructorBranched2.java new file mode 100644 index 000000000..462d0dcd7 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/others/TestConstructorBranched2.java @@ -0,0 +1,18 @@ +package jadx.tests.integration.others; + +import org.junit.jupiter.api.Test; + +import jadx.tests.api.SmaliTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +public class TestConstructorBranched2 extends SmaliTest { + + @Test + public void test() { + disableCompilation(); + assertThat(getClassNodeFromSmali()) + .code() + .countString(3, "new StringBuilder()"); + } +} diff --git a/jadx-core/src/test/smali/others/TestConstructorBranched2.smali b/jadx-core/src/test/smali/others/TestConstructorBranched2.smali new file mode 100644 index 000000000..2024dadf5 --- /dev/null +++ b/jadx-core/src/test/smali/others/TestConstructorBranched2.smali @@ -0,0 +1,98 @@ +.class public Lothers/TestConstructorBranched2; +.super Ljava/lang/Object; + +.method private test(Ljava/util/List;)Ljava/lang/String; + .locals 12 + iget-boolean v1, p0, Landroidx/gridlayout/widget/GridLayout$Axis;->horizontal:Z + const/4 v2, 0x1 + if-eqz v1, :cond_0 + const-string/jumbo v1, "x" + goto :goto_0 + + :cond_0 + const-string/jumbo v1, "y" + + :goto_0 + new-instance v3, Ljava/lang/StringBuilder; + invoke-direct {v3}, Ljava/lang/StringBuilder;->()V + const/4 v4, 0x1 + invoke-interface {p1}, Ljava/util/List;->iterator()Ljava/util/Iterator; + move-result-object v5 + const/16 v6, 0x98 + + :goto_1 + invoke-interface {v5}, Ljava/util/Iterator;->hasNext()Z + move-result v6 + if-eqz v6, :cond_3 + invoke-interface {v5}, Ljava/util/Iterator;->next()Ljava/lang/Object; + move-result-object v6 + check-cast v6, Landroidx/gridlayout/widget/GridLayout$Arc; + if-eqz v4, :cond_1 + const/4 v4, 0x0 + goto :goto_2 + + :cond_1 + const-string v7, ", " + invoke-virtual {v3, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + move-result-object v3 + + :goto_2 + iget-object v7, v6, Landroidx/gridlayout/widget/GridLayout$Arc;->span:Landroidx/gridlayout/widget/GridLayout$Interval; + iget v7, v7, Landroidx/gridlayout/widget/GridLayout$Interval;->min:I + iget-object v8, v6, Landroidx/gridlayout/widget/GridLayout$Arc;->span:Landroidx/gridlayout/widget/GridLayout$Interval; + iget v8, v8, Landroidx/gridlayout/widget/GridLayout$Interval;->max:I + iget-object v9, v6, Landroidx/gridlayout/widget/GridLayout$Arc;->value:Landroidx/gridlayout/widget/GridLayout$MutableInt; + iget v9, v9, Landroidx/gridlayout/widget/GridLayout$MutableInt;->value:I + const-string v10, "-" + new-instance v11, Ljava/lang/StringBuilder; + if-ge v7, v8, :cond_2 + invoke-direct {v11}, Ljava/lang/StringBuilder;->()V + invoke-virtual {v11, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + move-result-object v11 + invoke-virtual {v11, v8}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder; + move-result-object v11 + invoke-virtual {v11, v10}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + move-result-object v10 + invoke-virtual {v10, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + move-result-object v10 + invoke-virtual {v10, v7}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder; + move-result-object v10 + const-string v11, ">=" + invoke-virtual {v10, v11}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + move-result-object v10 + invoke-virtual {v10, v9}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder; + move-result-object v10 + invoke-virtual {v10}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; + move-result-object v10 + goto :goto_3 + + :cond_2 + invoke-direct {v11}, Ljava/lang/StringBuilder;->()V + invoke-virtual {v11, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + move-result-object v11 + invoke-virtual {v11, v7}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder; + move-result-object v11 + invoke-virtual {v11, v10}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + move-result-object v10 + invoke-virtual {v10, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + move-result-object v10 + invoke-virtual {v10, v8}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder; + move-result-object v10 + const-string v11, "<=" + invoke-virtual {v10, v11}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + move-result-object v10 + neg-int v11, v9 + invoke-virtual {v10, v11}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder; + move-result-object v10 + invoke-virtual {v10}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; + move-result-object v10 + + :goto_3 + invoke-virtual {v3, v10}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + goto/16 :goto_1 + + :cond_3 + invoke-virtual {v3}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; + move-result-object v5 + return-object v5 +.end method