diff --git a/jadx-core/src/main/java/jadx/core/codegen/NameGen.java b/jadx-core/src/main/java/jadx/core/codegen/NameGen.java index fefb8d8cf..81363f4cf 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/NameGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/NameGen.java @@ -102,16 +102,11 @@ public class NameGen { if (fallback) { return getFallbackName(arg); } - String name = arg.getName(); - String varName; - if (name != null) { - if ("this".equals(name)) { - return name; - } - varName = name; - } else { - varName = guessName(arg); + if (arg.isThis()) { + return RegisterArg.THIS_ARG_NAME; } + String name = arg.getName(); + String varName = name != null ? name : guessName(arg); if (NameMapper.isReserved(varName)) { return varName + "R"; } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnArg.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnArg.java index 9e9c76a9d..f9ac7a500 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnArg.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnArg.java @@ -140,7 +140,6 @@ public abstract class InsnArg extends Typed { } public boolean isThis() { - // must be implemented in RegisterArg - return false; + return contains(AFlag.THIS); } } 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 3dcc0b2db..abe3121f2 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 @@ -4,7 +4,6 @@ import java.util.Objects; import org.jetbrains.annotations.NotNull; -import jadx.core.dex.attributes.AFlag; import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.PhiInsn; import jadx.core.dex.nodes.DexNode; @@ -13,6 +12,8 @@ import jadx.core.utils.InsnUtils; public class RegisterArg extends InsnArg implements Named { + public static final String THIS_ARG_NAME = "this"; + protected final int regNum; // not null after SSATransform pass private SSAVar sVar; @@ -44,6 +45,9 @@ public class RegisterArg extends InsnArg implements Named { } public String getName() { + if (isThis()) { + return THIS_ARG_NAME; + } if (sVar == null) { return null; } @@ -117,22 +121,6 @@ public class RegisterArg extends InsnArg implements Named { return InsnUtils.getConstValueByInsn(dex, parInsn); } - @Override - public boolean isThis() { - if (contains(AFlag.THIS)) { - return true; - } - // maybe it was moved from 'this' register - InsnNode ai = getAssignInsn(); - if (ai != null && ai.getType() == InsnType.MOVE) { - InsnArg arg = ai.getArg(0); - if (arg != this) { - return arg.isThis(); - } - } - return false; - } - public InsnNode getAssignInsn() { if (sVar == null) { return null; @@ -188,6 +176,9 @@ public class RegisterArg extends InsnArg implements Named { } sb.append(" "); sb.append(type); + if (!isAttrStorageEmpty()) { + sb.append(' ').append(getAttributesString()); + } sb.append(")"); return sb.toString(); } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/TypeImmutableArg.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/TypeImmutableArg.java index aa2ecaa50..a8f757db0 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/TypeImmutableArg.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/TypeImmutableArg.java @@ -4,8 +4,6 @@ import org.jetbrains.annotations.NotNull; public class TypeImmutableArg extends RegisterArg { - public static final String THIS_ARG_NAME = "this"; - public TypeImmutableArg(int rn, ArgType type) { super(rn, type); } @@ -20,19 +18,8 @@ public class TypeImmutableArg extends RegisterArg { // not allowed } - @Override - public String getName() { - if (isThis()) { - return THIS_ARG_NAME; - } - return super.getName(); - } - @Override void setSVar(@NotNull SSAVar sVar) { - if (isThis()) { - sVar.setName(THIS_ARG_NAME); - } sVar.setTypeImmutable(type); super.setSVar(sVar); } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/CodeShrinker.java b/jadx-core/src/main/java/jadx/core/dex/visitors/CodeShrinker.java index a63598112..72f073aeb 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/CodeShrinker.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/CodeShrinker.java @@ -89,8 +89,7 @@ public class CodeShrinker extends AbstractVisitor { } public WrapInfo checkInline(int assignPos, RegisterArg arg) { - if (!arg.isThis() - && (assignPos >= inlineBorder || !canMove(assignPos, inlineBorder))) { + if (assignPos >= inlineBorder || !canMove(assignPos, inlineBorder)) { return null; } inlineBorder = assignPos; @@ -214,9 +213,9 @@ public class CodeShrinker extends AbstractVisitor { // continue; // } SSAVar sVar = arg.getSVar(); - // allow inline only one use arg or 'this' + // allow inline only one use arg if (sVar == null - || sVar.getVariableUseCount() != 1 && !arg.isThis() + || sVar.getVariableUseCount() != 1 || sVar.contains(AFlag.DONT_INLINE)) { continue; } 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 6a2f06d47..a1de3bd21 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 @@ -53,7 +53,7 @@ public class SSATransform extends AbstractVisitor { fixLastAssignInTry(mth); removeBlockerInsns(mth); - markThisArg(mth); + markThisArgs(mth.getThisArg()); boolean repeatFix; int k = 0; @@ -407,10 +407,30 @@ public class SSATransform extends AbstractVisitor { return true; } - private static void markThisArg(MethodNode mth) { - RegisterArg thisArg = mth.getThisArg(); + private static void markThisArgs(RegisterArg thisArg) { if (thisArg != null) { - thisArg.getSVar().getUseList().forEach(arg -> arg.add(AFlag.THIS)); + markOneArgAsThis(thisArg); + thisArg.getSVar().getUseList().forEach(SSATransform::markOneArgAsThis); + } + } + + private static void markOneArgAsThis(RegisterArg arg) { + if (arg == null) { + return; + } + arg.add(AFlag.THIS); + arg.setName(RegisterArg.THIS_ARG_NAME); + // mark all moved 'this' + InsnNode parentInsn = arg.getParentInsn(); + if (parentInsn != null + && parentInsn.getType() == InsnType.MOVE + && parentInsn.getArg(0) == arg) { + RegisterArg resArg = parentInsn.getResult(); + if (resArg.getRegNum() != arg.getRegNum() + && !resArg.getSVar().isUsedInPhi()) { + markThisArgs(resArg); + parentInsn.add(AFlag.SKIP); + } } } } diff --git a/jadx-core/src/test/java/jadx/tests/integration/usethis/TestDontInlineThis.java b/jadx-core/src/test/java/jadx/tests/integration/usethis/TestDontInlineThis.java new file mode 100644 index 000000000..db8710c6f --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/usethis/TestDontInlineThis.java @@ -0,0 +1,42 @@ +package jadx.tests.integration.usethis; + +import java.util.Random; + +import org.junit.Test; + +import jadx.core.dex.nodes.ClassNode; +import jadx.tests.api.IntegrationTest; + +import static jadx.tests.api.utils.JadxMatchers.containsOne; +import static org.junit.Assert.assertThat; + +public class TestDontInlineThis extends IntegrationTest { + + public static class TestCls { + public int field = new Random().nextInt(); + + private TestCls test() { + TestCls res; + if (field == 7) { + res = this; + } else { + res = new TestCls(); + } + res.method(); + return res; + } + + private void method() { + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + + assertThat(code, containsOne("TestDontInlineThis$TestCls res")); + assertThat(code, containsOne("res = this;")); + assertThat(code, containsOne("res = new TestDontInlineThis$TestCls();")); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/integration/usethis/TestInlineThis2.java b/jadx-core/src/test/java/jadx/tests/integration/usethis/TestInlineThis2.java new file mode 100644 index 000000000..ff68382bc --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/usethis/TestInlineThis2.java @@ -0,0 +1,47 @@ +package jadx.tests.integration.usethis; + +import java.util.Objects; + +import org.junit.Test; + +import jadx.core.dex.nodes.ClassNode; +import jadx.tests.api.IntegrationTest; + +import static jadx.tests.api.utils.JadxMatchers.containsOne; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; + +public class TestInlineThis2 extends IntegrationTest { + + public static class TestCls { + public int field; + + private void test() { + TestCls thisVar = this; + if (Objects.isNull(thisVar)) { + System.out.println("null"); + } + thisVar.method(); + thisVar.field = 123; + } + + private void method() { + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + + assertThat(code, not(containsString("thisVar"))); + assertThat(code, not(containsString("thisVar.method()"))); + assertThat(code, not(containsString("thisVar.field"))); + assertThat(code, not(containsString("= this"))); + + assertThat(code, containsOne("if (Objects.isNull(this)) {")); + assertThat(code, containsOne("this.field = 123;")); + assertThat(code, containsOne("method();")); + } +}