diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java index 972fcf8f6..fb66ab3c4 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java @@ -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) { diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/parser/DebugInfoParser.java b/jadx-core/src/main/java/jadx/core/dex/nodes/parser/DebugInfoParser.java index 3e9ad9982..c9fdbfea9 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/parser/DebugInfoParser.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/parser/DebugInfoParser.java @@ -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); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/PrepareForCodeGen.java b/jadx-core/src/main/java/jadx/core/dex/visitors/PrepareForCodeGen.java index 8552ab8aa..ba0421670 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/PrepareForCodeGen.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/PrepareForCodeGen.java @@ -142,6 +142,7 @@ public class PrepareForCodeGen extends AbstractVisitor { } if (replace) { insn.add(AFlag.ARITH_ONEARG); + insn.getResult().mergeName(arg); } } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessVariables.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessVariables.java index aaf814828..1c1e0f35e 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessVariables.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessVariables.java @@ -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; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java index 21b88ae6f..5e9dd61f1 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java @@ -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 */ diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInference.java b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInference.java index 91ad5efcd..78020fb31 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInference.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInference.java @@ -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) { diff --git a/jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions10.java b/jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions10.java index 339c476dc..df73596b2 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions10.java +++ b/jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions10.java @@ -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++; } diff --git a/jadx-core/src/test/java/jadx/tests/integration/debuginfo/TestVariablesNames.java b/jadx-core/src/test/java/jadx/tests/integration/debuginfo/TestVariablesNames.java new file mode 100644 index 000000000..e55066091 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/debuginfo/TestVariablesNames.java @@ -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);")); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatch7.java b/jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatch7.java index 268a68dca..b8ae43c24 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatch7.java +++ b/jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatch7.java @@ -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 + ";")); } diff --git a/jadx-core/src/test/smali/debuginfo/TestVariablesNames.smali b/jadx-core/src/test/smali/debuginfo/TestVariablesNames.smali new file mode 100644 index 000000000..1f95b42b6 --- /dev/null +++ b/jadx-core/src/test/smali/debuginfo/TestVariablesNames.smali @@ -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;->()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;->()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 ()V + .registers 1 + .prologue + invoke-direct {p0}, Ljava/lang/Object;->()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