diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/PhiInsn.java b/jadx-core/src/main/java/jadx/core/dex/instructions/PhiInsn.java index caf4ede54..d3736b795 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/PhiInsn.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/PhiInsn.java @@ -10,6 +10,7 @@ import jadx.core.dex.attributes.AFlag; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.RegisterArg; +import jadx.core.dex.instructions.args.SSAVar; import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.InsnNode; import jadx.core.utils.Utils; @@ -83,6 +84,20 @@ public final class PhiInsn extends InsnNode { return reg; } + @Nullable + public RegisterArg getArgBySsaVar(SSAVar ssaVar) { + if (getArgsCount() == 0) { + return null; + } + for (InsnArg insnArg : getArguments()) { + RegisterArg reg = (RegisterArg) insnArg; + if (reg.getSVar() == ssaVar) { + return reg; + } + } + return null; + } + @Override public boolean replaceArg(InsnArg from, InsnArg to) { if (!(from instanceof RegisterArg) || !(to instanceof RegisterArg)) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java index 7979a3e24..87ec0b3ec 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java @@ -43,6 +43,7 @@ import jadx.core.dex.visitors.JadxVisitor; import jadx.core.dex.visitors.blocksmaker.BlockSplitter; import jadx.core.dex.visitors.ssa.SSATransform; import jadx.core.utils.BlockUtils; +import jadx.core.utils.InsnUtils; import jadx.core.utils.Utils; @JadxVisitor( @@ -74,29 +75,26 @@ public final class TypeInferenceVisitor extends AbstractVisitor { if (Consts.DEBUG) { LOG.info("Start type inference in method: {}", mth); } - boolean resolved = runTypePropagation(mth); - if (!resolved) { - boolean moveAdded = false; - for (SSAVar var : new ArrayList<>(mth.getSVars())) { - if (tryInsertAdditionalInsn(mth, var)) { - moveAdded = true; - } - } - if (moveAdded) { - InitCodeVariables.rerun(mth); - resolved = runTypePropagation(mth); - } - if (!resolved) { - resolved = runMultiVariableSearch(mth); - } - } - if (resolved) { + if (resolveTypes(mth)) { for (SSAVar var : new ArrayList<>(mth.getSVars())) { processIncompatiblePrimitives(mth, var); } } } + private boolean resolveTypes(MethodNode mth) { + if (runTypePropagation(mth)) { + return true; + } + if (trySplitConstInsns(mth)) { + return true; + } + if (tryInsertAdditionalMove(mth)) { + return true; + } + return runMultiVariableSearch(mth); + } + /** * Guess type from usage and try to set it to current variable * and all connected instructions with {@link TypeUpdate#apply(SSAVar, ArgType)} @@ -354,6 +352,64 @@ public final class TypeInferenceVisitor extends AbstractVisitor { return false; } + private boolean trySplitConstInsns(MethodNode mth) { + boolean constSplitted = false; + for (SSAVar var : new ArrayList<>(mth.getSVars())) { + if (checkAndSplitConstInsn(mth, var)) { + constSplitted = true; + } + } + if (!constSplitted) { + return false; + } + InitCodeVariables.rerun(mth); + return runTypePropagation(mth); + } + + private boolean checkAndSplitConstInsn(MethodNode mth, SSAVar var) { + if (var.getUsedInPhi().size() < 2) { + return false; + } + InsnNode assignInsn = var.getAssign().getAssignInsn(); + InsnNode constInsn = InsnUtils.checkInsnType(assignInsn, InsnType.CONST); + if (constInsn == null) { + return false; + } + BlockNode blockNode = BlockUtils.getBlockByInsn(mth, constInsn); + if (blockNode == null) { + return false; + } + // for every PHI make separate CONST insn + boolean first = true; + for (PhiInsn phiInsn : var.getUsedInPhi()) { + if (first) { + first = false; + continue; + } + InsnNode copyInsn = constInsn.copyWithNewSsaVar(mth); + copyInsn.add(AFlag.SYNTHETIC); + BlockUtils.insertAfterInsn(blockNode, constInsn, copyInsn); + + RegisterArg phiArg = phiInsn.getArgBySsaVar(var); + phiInsn.replaceArg(phiArg, copyInsn.getResult().duplicate()); + } + return true; + } + + private boolean tryInsertAdditionalMove(MethodNode mth) { + boolean moveAdded = false; + for (SSAVar var : new ArrayList<>(mth.getSVars())) { + if (tryInsertAdditionalInsn(mth, var)) { + moveAdded = true; + } + } + if (!moveAdded) { + return false; + } + InitCodeVariables.rerun(mth); + return runTypePropagation(mth); + } + /** * Add MOVE instruction before PHI in bound blocks to make 'soft' type link. * This allows to use different types in blocks merged by PHI. 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 d076e03a7..92f9e6aab 100644 --- a/jadx-core/src/main/java/jadx/core/utils/BlockUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/BlockUtils.java @@ -663,6 +663,19 @@ public class BlockUtils { return false; } + public static boolean insertAfterInsn(BlockNode block, InsnNode insn, InsnNode newInsn) { + List instructions = block.getInstructions(); + int size = instructions.size(); + for (int i = 0; i < size; i++) { + InsnNode instruction = instructions.get(i); + if (instruction == insn) { + instructions.add(i + 1, newInsn); + return true; + } + } + return false; + } + public static boolean replaceInsn(MethodNode mth, InsnNode oldInsn, InsnNode newInsn) { for (BlockNode block : mth.getBasicBlocks()) { if (replaceInsn(mth, block, oldInsn, newInsn)) { diff --git a/jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver14.java b/jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver14.java new file mode 100644 index 000000000..df8e69055 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/types/TestTypeResolver14.java @@ -0,0 +1,38 @@ +package jadx.tests.integration.types; + +import org.junit.jupiter.api.Test; + +import jadx.tests.api.SmaliTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +public class TestTypeResolver14 extends SmaliTest { + // @formatter:off + /* + public Date test() throws Exception { + Date date = null; + Long l = null; + Cursor query = DBUtil.query(false, (CancellationSignal) null); + try { + if (query.moveToFirst()) { + if (!query.isNull(0)) { + l = Long.valueOf(query.getLong(0)); + } + date = this.this$0.toDate(l); + } + return date; + } finally { + query.close(); + } + } + */ + // @formatter:on + + @Test + public void test() { + disableCompilation(); + assertThat(getClassNodeFromSmali()) + .code() + .doesNotContain("? r2"); + } +} diff --git a/jadx-core/src/test/smali/types/TestTypeResolver14.smali b/jadx-core/src/test/smali/types/TestTypeResolver14.smali new file mode 100644 index 000000000..b702f7a1c --- /dev/null +++ b/jadx-core/src/test/smali/types/TestTypeResolver14.smali @@ -0,0 +1,63 @@ +.class public Ltypes/TestTypeResolver14; +.super Ljava/lang/Object; +.source "SourceFile" + +.method public test()Ljava/util/Date; + .registers 5 + .annotation system Ldalvik/annotation/Throws; + value = { + Ljava/lang/Exception; + } + .end annotation + + .line 472 + const/4 v2, 0x0 + const/4 v3, 0x0 + + invoke-static {v3, v2}, Landroidx/room/util/DBUtil;->query(ZLandroid/os/CancellationSignal;)Landroid/database/Cursor; + move-result-object v0 + + .line 475 + :try_start_e + invoke-interface {v0}, Landroid/database/Cursor;->moveToFirst()Z + move-result v1 + + if-eqz v1, :cond_2d + + .line 477 + invoke-interface {v0, v3}, Landroid/database/Cursor;->isNull(I)Z + move-result v1 + + if-eqz v1, :cond_1b + + goto :goto_23 + + .line 480 + :cond_1b + invoke-interface {v0, v3}, Landroid/database/Cursor;->getLong(I)J + move-result-wide v1 + + invoke-static {v1, v2}, Ljava/lang/Long;->valueOf(J)Ljava/lang/Long; + move-result-object v2 + + .line 482 + :goto_23 + iget-object v1, p0, Ltypes/TestTypeResolver14$8;->this$0:Ltypes/TestTypeResolver14; + + invoke-virtual {v1, v2}, Ltypeconverters/DateTypeConverter;->toDate(Ljava/lang/Long;)Ljava/util/Date; + move-result-object v2 + + :try_end_2d + .catchall {:try_start_e .. :try_end_2d} :catchall_31 + + .line 488 + :cond_2d + invoke-interface {v0}, Landroid/database/Cursor;->close()V + return-object v2 + + :catchall_31 + move-exception v1 + invoke-interface {v0}, Landroid/database/Cursor;->close()V + .line 489 + throw v1 +.end method