diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java b/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java index bdaa2832e..d487ad966 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java @@ -52,6 +52,13 @@ public class ConstructorInsn extends InsnNode { setSourceLine(invoke.getSourceLine()); } + public ConstructorInsn(MethodInfo callMth, CallType callType, RegisterArg instanceArg) { + super(InsnType.CONSTRUCTOR, callMth.getArgsCount()); + this.callMth = callMth; + this.callType = callType; + this.instanceArg = instanceArg; + } + public MethodInfo getCallMth() { return callMth; } @@ -64,6 +71,10 @@ public class ConstructorInsn extends InsnNode { return callMth.getDeclClass(); } + public CallType getCallType() { + return callType; + } + public boolean isNewInstance() { return callType == CallType.CONSTRUCTOR; } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java index e539490cf..b258204d5 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java @@ -1,5 +1,6 @@ package jadx.core.dex.visitors; +import jadx.core.codegen.TypeGen; import jadx.core.deobf.NameMapper; import jadx.core.dex.attributes.AType; import jadx.core.dex.info.MethodInfo; @@ -143,6 +144,10 @@ public class ModVisitor extends AbstractVisitor { if (co.isNewInstance()) { removeAssignChain(instArgAssignInsn, remover, InsnType.NEW_INSTANCE); } + ConstructorInsn replace = processConstructor(mth, co); + if (replace != null) { + replaceInsn(block, insnNumber, replace); + } } } else if (inv.getArgsCount() > 0) { for (int j = 0; j < inv.getArgsCount(); j++) { @@ -157,6 +162,42 @@ public class ModVisitor extends AbstractVisitor { } } + /** + * Replace call of synthetic constructor + */ + private static ConstructorInsn processConstructor(MethodNode mth, ConstructorInsn co) { + MethodNode callMth = mth.dex().resolveMethod(co.getCallMth()); + if (callMth != null + && callMth.getAccessFlags().isSynthetic() + && allArgsNull(co)) { + // if all arguments is null => replace with default constructor + ClassNode classNode = mth.dex().resolveClass(callMth.getParentClass().getClassInfo()); + boolean passThis = co.getArgsCount() >= 1 && co.getArg(0).isThis(); + String ctrId = "(" + (passThis ? TypeGen.signature(co.getArg(0).getType()) : "") + ")V"; + MethodNode defCtr = classNode.searchMethodByName(ctrId); + if (defCtr != null) { + ConstructorInsn newInsn = new ConstructorInsn(defCtr.getMethodInfo(), co.getCallType(), co.getInstanceArg()); + newInsn.setResult(co.getResult()); + return newInsn; + } + } + return null; + } + + private static boolean allArgsNull(InsnNode insn) { + for (InsnArg insnArg : insn.getArguments()) { + if (insnArg.isLiteral()) { + LiteralArg lit = (LiteralArg) insnArg; + if (lit.getLiteral() != 0) { + return false; + } + } else if (!insnArg.isThis()) { + return false; + } + } + return true; + } + /** * Remove instructions on 'move' chain until instruction with type 'insnType' */ diff --git a/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java b/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java index 13f7e5ecf..01160c198 100644 --- a/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java +++ b/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java @@ -121,6 +121,11 @@ public abstract class IntegrationTest extends TestUtils { public boolean isShowInconsistentCode() { return true; } + + @Override + public int getThreadsCount() { + return 1; + } }, new File(outDir)); } diff --git a/jadx-core/src/test/java/jadx/tests/integration/inner/TestInnerClass4.java b/jadx-core/src/test/java/jadx/tests/integration/inner/TestInnerClass4.java new file mode 100644 index 000000000..d10c1cd08 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/inner/TestInnerClass4.java @@ -0,0 +1,34 @@ +package jadx.tests.integration.inner; + +import jadx.core.dex.nodes.ClassNode; +import jadx.tests.api.IntegrationTest; + +import org.junit.Test; + +import static jadx.tests.api.utils.JadxMatchers.containsOne; +import static org.junit.Assert.assertThat; + +public class TestInnerClass4 extends IntegrationTest { + + public static class TestCls { + public class C { + public String c; + + private C() { + this.c = "c"; + } + } + + private String test() { + return new C().c; + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + + assertThat(code, containsOne("return new C().c;")); + } +}