From 280f3870a93f46a6555ec0132267c226db7122d0 Mon Sep 17 00:00:00 2001 From: Skylot <118523+skylot@users.noreply.github.com> Date: Fri, 9 Aug 2024 22:54:13 +0100 Subject: [PATCH] fix: handle quick return on branched constructor (#2240) --- .../core/dex/visitors/ConstructorVisitor.java | 17 +++- .../main/java/jadx/core/utils/BlockUtils.java | 9 +- .../others/TestConstructorBranched3.java | 18 ++++ .../others/TestConstructorBranched3.smali | 91 +++++++++++++++++++ 4 files changed, 131 insertions(+), 4 deletions(-) create mode 100644 jadx-core/src/test/java/jadx/tests/integration/others/TestConstructorBranched3.java create mode 100644 jadx-core/src/test/smali/others/TestConstructorBranched3.smali 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 f2e622029..8e382c3c7 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 @@ -1,5 +1,7 @@ package jadx.core.dex.visitors; +import java.util.List; + import org.jetbrains.annotations.Nullable; import jadx.core.codegen.TypeGen; @@ -107,7 +109,20 @@ public class ConstructorVisitor extends AbstractVisitor { } BlockNode crossBlock = BlockUtils.getPathCross(mth, curBlock, otherBlock); if (crossBlock == null) { - throw new JadxRuntimeException("Path cross not found for blocks: " + curBlock + " and " + otherBlock); + // no path cross => PHI insn not needed + // use new SSA var on usage from current path + RegisterArg newResArg = instArg.duplicateWithNewSSAVar(mth); + List pathBlocks = BlockUtils.collectAllSuccessors(mth, curBlock, true); + for (RegisterArg useReg : instArg.getSVar().getUseList()) { + InsnNode parentInsn = useReg.getParentInsn(); + if (parentInsn != null) { + BlockNode useBlock = BlockUtils.getBlockByInsn(mth, parentInsn, pathBlocks); + if (useBlock != null) { + parentInsn.replaceArg(useReg, newResArg.duplicate()); + } + } + } + return newResArg; } RegisterArg newResArg = instArg.duplicateWithNewSSAVar(mth); RegisterArg useArg = otherCtr.getResult(); 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 00234bf10..9c6d0b761 100644 --- a/jadx-core/src/main/java/jadx/core/utils/BlockUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/BlockUtils.java @@ -249,8 +249,11 @@ public class BlockUtils { || type == InsnType.CONTINUE; } - @Nullable - public static BlockNode getBlockByInsn(MethodNode mth, @Nullable InsnNode insn) { + public static @Nullable BlockNode getBlockByInsn(MethodNode mth, @Nullable InsnNode insn) { + return getBlockByInsn(mth, insn, mth.getBasicBlocks()); + } + + public static @Nullable BlockNode getBlockByInsn(MethodNode mth, @Nullable InsnNode insn, List blocks) { if (insn == null) { return null; } @@ -260,7 +263,7 @@ public class BlockUtils { if (insn.contains(AFlag.WRAPPED)) { return getBlockByWrappedInsn(mth, insn); } - for (BlockNode bn : mth.getBasicBlocks()) { + for (BlockNode bn : blocks) { if (blockContains(bn, insn)) { return bn; } diff --git a/jadx-core/src/test/java/jadx/tests/integration/others/TestConstructorBranched3.java b/jadx-core/src/test/java/jadx/tests/integration/others/TestConstructorBranched3.java new file mode 100644 index 000000000..7ebd1731d --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/others/TestConstructorBranched3.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 TestConstructorBranched3 extends SmaliTest { + + @Test + public void test() { + disableCompilation(); + assertThat(getClassNodeFromSmali()) + .code() + .countString(4, "return new f("); + } +} diff --git a/jadx-core/src/test/smali/others/TestConstructorBranched3.smali b/jadx-core/src/test/smali/others/TestConstructorBranched3.smali new file mode 100644 index 000000000..a3cb44511 --- /dev/null +++ b/jadx-core/src/test/smali/others/TestConstructorBranched3.smali @@ -0,0 +1,91 @@ +.class public Lothers/TestConstructorBranched3; +.super Ljava/lang/Object; + +.method public static final test(Ljava/lang/Class;)Lml/f; + .registers 5 + const/4 v0, 0x0 + :goto_1 + invoke-virtual {p0}, Ljava/lang/Class;->isArray()Z + move-result v1 + if-eqz v1, :cond_13 + add-int/lit8 v0, v0, 0x1 + invoke-virtual {p0}, Ljava/lang/Class;->getComponentType()Ljava/lang/Class; + move-result-object p0 + const-string v1, "currentClass.componentType" + invoke-static {p0, v1}, Lve/e;->l(Ljava/lang/Object;Ljava/lang/String;)V + goto :goto_1 + + :cond_13 + invoke-virtual {p0}, Ljava/lang/Class;->isPrimitive()Z + move-result v1 + if-eqz v1, :cond_68 + sget-object v1, Ljava/lang/Void;->TYPE:Ljava/lang/Class; + invoke-static {p0, v1}, Lve/e;->g(Ljava/lang/Object;Ljava/lang/Object;)Z + move-result v1 + + if-eqz v1, :cond_31 + new-instance p0, Lml/f; + sget-object v1, Lgk/k$a;->e:Lhl/c; + invoke-virtual {v1}, Lhl/c;->i()Lhl/b; + move-result-object v1 + invoke-static {v1}, Lhl/a;->l(Lhl/b;)Lhl/a; + move-result-object v1 + invoke-direct {p0, v1, v0}, Lml/f;->(Lhl/a;I)V + return-object p0 + + :cond_31 + invoke-virtual {p0}, Ljava/lang/Class;->getName()Ljava/lang/String; + move-result-object p0 + invoke-static {p0}, Lpl/b;->c(Ljava/lang/String;)Lpl/b; + move-result-object p0 + invoke-virtual {p0}, Lpl/b;->r()Lgk/i; + move-result-object p0 + const-string v1, "get(currentClass.name).primitiveType" + invoke-static {p0, v1}, Lve/e;->l(Ljava/lang/Object;Ljava/lang/String;)V + new-instance v1, Lml/f; + if-lez v0, :cond_58 + .line 1 + iget-object p0, p0, Lgk/i;->d:Ljj/d; + invoke-interface {p0}, Ljj/d;->getValue()Ljava/lang/Object; + move-result-object p0 + check-cast p0, Lhl/b; + .line 2 + invoke-static {p0}, Lhl/a;->l(Lhl/b;)Lhl/a; + move-result-object p0 + add-int/lit8 v0, v0, -0x1 + invoke-direct {v1, p0, v0}, Lml/f;->(Lhl/a;I)V + return-object v1 + + .line 3 + :cond_58 + iget-object p0, p0, Lgk/i;->c:Ljj/d; + invoke-interface {p0}, Ljj/d;->getValue()Ljava/lang/Object; + move-result-object p0 + check-cast p0, Lhl/b; + .line 4 + invoke-static {p0}, Lhl/a;->l(Lhl/b;)Lhl/a; + move-result-object p0 + invoke-direct {v1, p0, v0}, Lml/f;->(Lhl/a;I)V + return-object v1 + + :cond_68 + invoke-static {p0}, Lpk/b;->b(Ljava/lang/Class;)Lhl/a; + move-result-object p0 + sget-object v1, Lik/c;->a:Lik/c; + invoke-virtual {p0}, Lhl/a;->b()Lhl/b; + move-result-object v2 + const-string v3, "javaClassId.asSingleFqName()" + invoke-static {v2, v3}, Lve/e;->l(Ljava/lang/Object;Ljava/lang/String;)V + invoke-virtual {v1, v2}, Lik/c;->f(Lhl/b;)Lhl/a; + move-result-object v1 + if-nez v1, :cond_7e + goto :goto_7f + + :cond_7e + move-object p0, v1 + + :goto_7f + new-instance v1, Lml/f; + invoke-direct {v1, p0, v0}, Lml/f;->(Lhl/a;I)V + return-object v1 +.end method