diff --git a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java index 7e7d99737..765fa2ab2 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java @@ -652,6 +652,13 @@ public class ClassGen { code.add(clsName); } + public void addClsShortNameForced(ICodeWriter code, ClassInfo classInfo) { + code.add(classInfo.getAliasShortName()); + if (!isBothClassesInOneTopClass(cls.getClassInfo(), classInfo)) { + addImport(classInfo); + } + } + private String useClassInternal(ClassInfo useCls, ClassInfo extClsInfo) { String fullName = extClsInfo.getAliasFullName(); if (fallback || !useImports) { diff --git a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java index 1fe717189..3daa841c0 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -749,6 +749,7 @@ public class InsnGen { code.attachAnnotation(refMth); code.add("this"); } else { + boolean forceShortName = addOuterClassInstance(insn, code, callMth); code.add("new "); if (refMth == null || refMth.contains(AFlag.DONT_GENERATE)) { // use class reference if constructor method is missing (default constructor) @@ -756,7 +757,11 @@ public class InsnGen { } else { code.attachAnnotation(refMth); } - mgen.getClassGen().addClsName(code, insn.getClassType()); + if (forceShortName) { + mgen.getClassGen().addClsShortNameForced(code, insn.getClassType()); + } else { + mgen.getClassGen().addClsName(code, insn.getClassType()); + } GenericInfoAttr genericInfoAttr = insn.get(AType.GENERIC_INFO); if (genericInfoAttr != null) { code.add('<'); @@ -777,6 +782,27 @@ public class InsnGen { generateMethodArguments(code, insn, 0, callMth); } + private boolean addOuterClassInstance(ConstructorInsn insn, ICodeWriter code, MethodNode callMth) throws CodegenException { + if (callMth == null || !callMth.contains(AFlag.SKIP_FIRST_ARG)) { + return false; + } + ClassNode ctrCls = callMth.getDeclaringClass(); + if (!ctrCls.isInner() || insn.getArgsCount() == 0) { + return false; + } + InsnArg instArg = insn.getArg(0); + if (instArg.isThis()) { + return false; + } + // instance arg should be of an outer class type + if (!instArg.getType().equals(ctrCls.getDeclaringClass().getType())) { + return false; + } + addArgDot(code, instArg); + // can't use another dot, force short name of class + return true; + } + private void inlineAnonymousConstructor(ICodeWriter code, ClassNode cls, ConstructorInsn insn) throws CodegenException { cls.ensureProcessed(); if (this.mth.getParentClass() == cls) { diff --git a/jadx-core/src/test/java/jadx/tests/integration/inner/TestInnerConstructorCall.java b/jadx-core/src/test/java/jadx/tests/integration/inner/TestInnerConstructorCall.java new file mode 100644 index 000000000..ac7f3a0c4 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/inner/TestInnerConstructorCall.java @@ -0,0 +1,33 @@ +package jadx.tests.integration.inner; + +import org.junit.jupiter.api.Test; + +import jadx.tests.api.IntegrationTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +public class TestInnerConstructorCall extends IntegrationTest { + + public static class TestCls { + @SuppressWarnings("InnerClassMayBeStatic") + public class A { + public class AA { + public void test() { + } + } + } + + public void test() { + A a = new A(); + A.AA aa = a.new AA(); + aa.test(); + } + } + + @Test + public void test() { + assertThat(getClassNode(TestCls.class)) + .code() + .containsOne("A.AA aa = a.new AA();"); + } +}