Skip to content

Commit

Permalink
feat: support inner class contruction with outer instance (#2253)
Browse files Browse the repository at this point in the history
  • Loading branch information
skylot committed Sep 21, 2024
1 parent 1d34328 commit 109dea0
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 1 deletion.
7 changes: 7 additions & 0 deletions jadx-core/src/main/java/jadx/core/codegen/ClassGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,13 @@ public void addClsName(ICodeWriter code, ClassInfo classInfo) {
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) {
Expand Down
28 changes: 27 additions & 1 deletion jadx-core/src/main/java/jadx/core/codegen/InsnGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -749,14 +749,19 @@ private void makeConstructor(ConstructorInsn insn, ICodeWriter code) throws Code
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)
code.attachAnnotation(mth.root().resolveClass(insn.getCallMth().getDeclClass()));
} 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('<');
Expand All @@ -777,6 +782,27 @@ private void makeConstructor(ConstructorInsn insn, ICodeWriter code) throws Code
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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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();");
}
}

0 comments on commit 109dea0

Please # to comment.