core: fix variable names propagation (#219)
This commit is contained in:
@@ -63,6 +63,21 @@ public class RegisterArg extends InsnArg implements Named {
|
||||
return n.equals(((Named) arg).getName());
|
||||
}
|
||||
|
||||
public void mergeName(InsnArg arg) {
|
||||
if (arg instanceof Named) {
|
||||
Named otherArg = (Named) arg;
|
||||
String otherName = otherArg.getName();
|
||||
String name = getName();
|
||||
if (!Objects.equals(name, otherName)) {
|
||||
if (name == null) {
|
||||
setName(otherName);
|
||||
} else if (otherName == null) {
|
||||
otherArg.setName(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setType(ArgType type) {
|
||||
if (sVar != null) {
|
||||
|
||||
@@ -78,6 +78,8 @@ public class DebugInfoParser {
|
||||
addrChange(-1, 1, line);
|
||||
setLine(addr, line);
|
||||
|
||||
boolean varsInfoFound = false;
|
||||
|
||||
int c = section.readByte() & 0xFF;
|
||||
while (c != DBG_END_SEQUENCE) {
|
||||
switch (c) {
|
||||
@@ -98,6 +100,7 @@ public class DebugInfoParser {
|
||||
int type = section.readUleb128() - 1;
|
||||
LocalVar var = new LocalVar(dex, regNum, nameId, type, DexNode.NO_INDEX);
|
||||
startVar(var, addr, line);
|
||||
varsInfoFound = true;
|
||||
break;
|
||||
}
|
||||
case DBG_START_LOCAL_EXTENDED: {
|
||||
@@ -107,6 +110,7 @@ public class DebugInfoParser {
|
||||
int sign = section.readUleb128() - 1;
|
||||
LocalVar var = new LocalVar(dex, regNum, nameId, type, sign);
|
||||
startVar(var, addr, line);
|
||||
varsInfoFound = true;
|
||||
break;
|
||||
}
|
||||
case DBG_RESTART_LOCAL: {
|
||||
@@ -118,6 +122,7 @@ public class DebugInfoParser {
|
||||
}
|
||||
var.start(addr, line);
|
||||
}
|
||||
varsInfoFound = true;
|
||||
break;
|
||||
}
|
||||
case DBG_END_LOCAL: {
|
||||
@@ -127,6 +132,7 @@ public class DebugInfoParser {
|
||||
var.end(addr, line);
|
||||
setVar(var);
|
||||
}
|
||||
varsInfoFound = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -160,10 +166,12 @@ public class DebugInfoParser {
|
||||
c = section.readByte() & 0xFF;
|
||||
}
|
||||
|
||||
for (LocalVar var : locals) {
|
||||
if (var != null && !var.isEnd()) {
|
||||
var.end(mth.getCodeSize() - 1, line);
|
||||
setVar(var);
|
||||
if (varsInfoFound) {
|
||||
for (LocalVar var : locals) {
|
||||
if (var != null && !var.isEnd()) {
|
||||
var.end(mth.getCodeSize() - 1, line);
|
||||
setVar(var);
|
||||
}
|
||||
}
|
||||
}
|
||||
setSourceLines(addr, insnByOffset.length, line);
|
||||
|
||||
@@ -142,6 +142,7 @@ public class PrepareForCodeGen extends AbstractVisitor {
|
||||
}
|
||||
if (replace) {
|
||||
insn.add(AFlag.ARITH_ONEARG);
|
||||
insn.getResult().mergeName(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import jadx.core.utils.exceptions.JadxException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
@@ -8,6 +8,7 @@ import jadx.core.dex.instructions.PhiInsn;
|
||||
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.instructions.args.VarName;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
@@ -141,14 +142,7 @@ public class SSATransform extends AbstractVisitor {
|
||||
PhiListAttr phiList = enterBlock.get(AType.PHI_LIST);
|
||||
if (phiList != null) {
|
||||
for (PhiInsn phiInsn : phiList.getList()) {
|
||||
int regNum = phiInsn.getResult().getRegNum();
|
||||
SSAVar var = vars[regNum];
|
||||
if (var == null) {
|
||||
continue;
|
||||
}
|
||||
RegisterArg arg = phiInsn.bindArg(enterBlock);
|
||||
var.use(arg);
|
||||
var.setUsedInPhi(phiInsn);
|
||||
bindPhiArg(vars, enterBlock, phiInsn);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -183,14 +177,7 @@ public class SSATransform extends AbstractVisitor {
|
||||
continue;
|
||||
}
|
||||
for (PhiInsn phiInsn : phiList.getList()) {
|
||||
int regNum = phiInsn.getResult().getRegNum();
|
||||
SSAVar var = vars[regNum];
|
||||
if (var == null) {
|
||||
continue;
|
||||
}
|
||||
RegisterArg arg = phiInsn.bindArg(block);
|
||||
var.use(arg);
|
||||
var.setUsedInPhi(phiInsn);
|
||||
bindPhiArg(vars, block, phiInsn);
|
||||
}
|
||||
}
|
||||
for (BlockNode domOn : block.getDominatesOn()) {
|
||||
@@ -199,6 +186,17 @@ public class SSATransform extends AbstractVisitor {
|
||||
System.arraycopy(inputVars, 0, vars, 0, vars.length);
|
||||
}
|
||||
|
||||
private static void bindPhiArg(SSAVar[] vars, BlockNode block, PhiInsn phiInsn) {
|
||||
int regNum = phiInsn.getResult().getRegNum();
|
||||
SSAVar var = vars[regNum];
|
||||
if (var == null) {
|
||||
return;
|
||||
}
|
||||
RegisterArg arg = phiInsn.bindArg(block);
|
||||
var.use(arg);
|
||||
var.setUsedInPhi(phiInsn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix last try/catch assign instruction
|
||||
*/
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
package jadx.core.dex.visitors.typeinference;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.PhiListAttr;
|
||||
import jadx.core.dex.instructions.PhiInsn;
|
||||
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.instructions.args.VarName;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.visitors.AbstractVisitor;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TypeInference extends AbstractVisitor {
|
||||
|
||||
@Override
|
||||
@@ -19,6 +23,8 @@ public class TypeInference extends AbstractVisitor {
|
||||
if (mth.isNoCode()) {
|
||||
return;
|
||||
}
|
||||
fixPhiVarNames(mth);
|
||||
|
||||
DexNode dex = mth.dex();
|
||||
for (SSAVar var : mth.getSVars()) {
|
||||
// inference variable type
|
||||
@@ -80,6 +86,32 @@ public class TypeInference extends AbstractVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
private static void fixPhiVarNames(MethodNode mth) {
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
PhiListAttr phiList = block.get(AType.PHI_LIST);
|
||||
if (phiList == null) {
|
||||
continue;
|
||||
}
|
||||
for (PhiInsn phiInsn : phiList.getList()) {
|
||||
RegisterArg resArg = phiInsn.getResult();
|
||||
int argsCount = phiInsn.getArgsCount();
|
||||
for (int i = 0; i < argsCount; i++) {
|
||||
RegisterArg arg = phiInsn.getArg(i);
|
||||
arg.mergeName(resArg);
|
||||
}
|
||||
VarName varName = resArg.getSVar().getVarName();
|
||||
if (varName == null) {
|
||||
varName = new VarName();
|
||||
resArg.getSVar().setVarName(varName);
|
||||
}
|
||||
for (int i = 0; i < argsCount; i++) {
|
||||
RegisterArg arg = phiInsn.getArg(i);
|
||||
arg.getSVar().setVarName(varName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String processVarName(SSAVar var) {
|
||||
String name = var.getAssign().getName();
|
||||
if (name != null) {
|
||||
|
||||
@@ -14,7 +14,7 @@ public class TestConditions10 extends IntegrationTest {
|
||||
|
||||
public static class TestCls {
|
||||
|
||||
public void test(boolean a, int b) throws Exception {
|
||||
public void test(boolean a, int b) {
|
||||
if (a || b > 2) {
|
||||
b++;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package jadx.tests.integration.debuginfo;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.tests.api.IntegrationTest;
|
||||
import jadx.tests.api.SmaliTest;
|
||||
|
||||
import static jadx.tests.api.utils.JadxMatchers.containsLines;
|
||||
import static jadx.tests.api.utils.JadxMatchers.containsOne;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class TestVariablesNames extends SmaliTest {
|
||||
/*
|
||||
public static class TestCls {
|
||||
|
||||
public void test(String s, int k) {
|
||||
f1(s);
|
||||
int i = k + 3;
|
||||
String s2 = "i" + i;
|
||||
f2(i, s2);
|
||||
double d = i * 5;
|
||||
String s3 = "d" + d;
|
||||
f3(d, s3);
|
||||
}
|
||||
|
||||
private void f1(String s) {
|
||||
}
|
||||
|
||||
private void f2(int i, String i2) {
|
||||
}
|
||||
|
||||
private void f3(double d, String d2) {
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parameter register reused in variables assign with different types and names
|
||||
* No variables names in debug info
|
||||
*/
|
||||
@Test
|
||||
public void test() {
|
||||
ClassNode cls = getClassNodeFromSmaliWithPath("debuginfo", "TestVariablesNames");
|
||||
String code = cls.getCode().toString();
|
||||
|
||||
// TODO: don't use current variables naming in tests
|
||||
assertThat(code, containsOne("f1(str);"));
|
||||
assertThat(code, containsOne("f2(i2, \"i\" + i2);"));
|
||||
assertThat(code, containsOne("f3(d, \"d\" + d);"));
|
||||
}
|
||||
}
|
||||
@@ -29,10 +29,11 @@ public class TestTryCatch7 extends IntegrationTest {
|
||||
ClassNode cls = getClassNode(TestCls.class);
|
||||
String code = cls.getCode().toString();
|
||||
|
||||
String excVarName = "exception";
|
||||
String excVarName = "e";
|
||||
String catchExcVarName = "e2";
|
||||
assertThat(code, containsOne("Exception " + excVarName + " = new Exception();"));
|
||||
assertThat(code, containsOne("} catch (Exception e) {"));
|
||||
assertThat(code, containsOne(excVarName + " = e;"));
|
||||
assertThat(code, containsOne("} catch (Exception " + catchExcVarName + ") {"));
|
||||
assertThat(code, containsOne(excVarName + " = " + catchExcVarName + ";"));
|
||||
assertThat(code, containsOne(excVarName + ".printStackTrace();"));
|
||||
assertThat(code, containsOne("return " + excVarName + ";"));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
.class public LTestVariablesNames;
|
||||
.super Ljava/lang/Object;
|
||||
.source "TestVariablesNames.java"
|
||||
|
||||
.method public test(Ljava/lang/String;I)V
|
||||
.registers 10
|
||||
|
||||
.prologue
|
||||
.line 17
|
||||
invoke-direct {p0, p1}, LTestVariablesNames;->f1(Ljava/lang/String;)V
|
||||
|
||||
.line 18
|
||||
add-int/lit8 p1, p2, 0x3
|
||||
|
||||
.line 19
|
||||
new-instance v5, Ljava/lang/StringBuilder;
|
||||
|
||||
invoke-direct {v5}, Ljava/lang/StringBuilder;-><init>()V
|
||||
|
||||
const-string v6, "i"
|
||||
|
||||
invoke-virtual {v5, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
move-result-object v5
|
||||
|
||||
invoke-virtual {v5, p1}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
|
||||
|
||||
move-result-object v5
|
||||
|
||||
invoke-virtual {v5}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
|
||||
|
||||
move-result-object v3
|
||||
|
||||
.line 20
|
||||
invoke-direct {p0, p1, v3}, LTestVariablesNames;->f2(ILjava/lang/String;)V
|
||||
|
||||
.line 21
|
||||
mul-int/lit8 v5, p1, 0x5
|
||||
|
||||
int-to-double p1, v5
|
||||
|
||||
.line 22
|
||||
new-instance v5, Ljava/lang/StringBuilder;
|
||||
|
||||
invoke-direct {v5}, Ljava/lang/StringBuilder;-><init>()V
|
||||
|
||||
const-string v6, "d"
|
||||
|
||||
invoke-virtual {v5, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
move-result-object v5
|
||||
|
||||
invoke-virtual {v5, p1, v1}, Ljava/lang/StringBuilder;->append(D)Ljava/lang/StringBuilder;
|
||||
|
||||
move-result-object v5
|
||||
|
||||
invoke-virtual {v5}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
|
||||
|
||||
move-result-object v4
|
||||
|
||||
.line 23
|
||||
invoke-direct {p0, p1, v1, v4}, LTestVariablesNames;->f3(DLjava/lang/String;)V
|
||||
|
||||
.line 24
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
.method public constructor <init>()V
|
||||
.registers 1
|
||||
.prologue
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private f1(Ljava/lang/String;)V
|
||||
.registers 2
|
||||
.prologue
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private f2(ILjava/lang/String;)V
|
||||
.registers 3
|
||||
.prologue
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private f3(DLjava/lang/String;)V
|
||||
.registers 4
|
||||
.prologue
|
||||
return-void
|
||||
.end method
|
||||
Reference in New Issue
Block a user