fix: split CONST used in PHI to help type inference (#900)
This commit is contained in:
@@ -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)) {
|
||||
|
||||
+73
-17
@@ -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.
|
||||
|
||||
@@ -663,6 +663,19 @@ public class BlockUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean insertAfterInsn(BlockNode block, InsnNode insn, InsnNode newInsn) {
|
||||
List<InsnNode> 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)) {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user