From 0b2e2ed034eed67ac9e0c283099fdce9540f5b3c Mon Sep 17 00:00:00 2001 From: Skylot Date: Sun, 5 Jun 2022 13:10:24 +0100 Subject: [PATCH] fix: improve class search for super call (#1512) --- .../main/java/jadx/core/clsp/ClspGraph.java | 3 + .../main/java/jadx/core/codegen/InsnGen.java | 70 ++++++++++--------- .../jadx/tests/api/compiler/TestCompiler.java | 3 +- .../invoke/TestSuperInvokeUnknown.java | 42 +++++++++++ 4 files changed, 83 insertions(+), 35 deletions(-) create mode 100644 jadx-core/src/test/java/jadx/tests/integration/invoke/TestSuperInvokeUnknown.java diff --git a/jadx-core/src/main/java/jadx/core/clsp/ClspGraph.java b/jadx-core/src/main/java/jadx/core/clsp/ClspGraph.java index b36d0ffd284..4f2d995af42 100644 --- a/jadx-core/src/main/java/jadx/core/clsp/ClspGraph.java +++ b/jadx-core/src/main/java/jadx/core/clsp/ClspGraph.java @@ -203,6 +203,9 @@ private void addSuperTypes(ClspClass cls, Set result) { if (isNew) { addSuperTypes(parentCls, result); } + } else { + // parent type is unknown + result.add(parentType.getObject()); } } } 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 bd5fd704a11..dc71665ef49 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -807,14 +807,9 @@ private void makeInvoke(InvokeNode insn, ICodeWriter code) throws CodegenExcepti break; case SUPER: - ClassInfo superCallCls = getClassForSuperCall(code, callMth); - if (superCallCls != null) { - useClass(code, superCallCls); - code.add('.'); - } - // use 'super' instead 'this' in 0 arg - code.add("super").add('.'); - k++; + callSuper(code, callMth); + k++; // use 'super' instead 'this' in 0 arg + code.add('.'); break; case STATIC: @@ -965,34 +960,43 @@ private void makeInlinedLambdaMethod(ICodeWriter code, InvokeCustomNode customNo code.startLine('}'); } - @Nullable - private ClassInfo getClassForSuperCall(ICodeWriter code, MethodInfo callMth) { - ClassNode useCls = mth.getParentClass(); - ClassInfo insnCls = useCls.getClassInfo(); - ClassInfo declClass = callMth.getDeclClass(); - if (insnCls.equals(declClass)) { - return null; + private void callSuper(ICodeWriter code, MethodInfo callMth) { + ClassInfo superCallCls = getClassForSuperCall(callMth); + if (superCallCls == null) { + // unknown class, add comment to keep that info + code.add("super/*").add(callMth.getDeclClass().getFullName()).add("*/"); + return; } - ClassNode topClass = useCls.getTopParentClass(); - if (topClass.getClassInfo().equals(declClass)) { - return declClass; + ClassInfo curClass = mth.getParentClass().getClassInfo(); + if (superCallCls.equals(curClass)) { + code.add("super"); + return; } - // search call class - ClassNode nextParent = useCls; - do { - ClassInfo nextClsInfo = nextParent.getClassInfo(); - if (nextClsInfo.equals(declClass) - || ArgType.isInstanceOf(mth.root(), nextClsInfo.getType(), declClass.getType())) { - if (nextParent == useCls) { - return null; - } - return nextClsInfo; - } - nextParent = nextParent.getParentClass(); - } while (nextParent != null && nextParent != topClass); + // use custom class + useClass(code, superCallCls); + code.add(".super"); + } - // search failed, just return parent class - return useCls.getParentClass().getClassInfo(); + /** + * Search call class in super types of this + * and all parent classes (needed for inlined synthetic calls) + */ + @Nullable + private ClassInfo getClassForSuperCall(MethodInfo callMth) { + ArgType declClsType = callMth.getDeclClass().getType(); + ClassNode parentNode = mth.getParentClass(); + while (true) { + ClassInfo parentCls = parentNode.getClassInfo(); + if (ArgType.isInstanceOf(root, parentCls.getType(), declClsType)) { + return parentCls; + } + ClassNode nextParent = parentNode.getParentClass(); + if (nextParent == parentNode) { + // no parent, class not found + return null; + } + parentNode = nextParent; + } } void generateMethodArguments(ICodeWriter code, BaseInvokeNode insn, int startArgNum, diff --git a/jadx-core/src/test/java/jadx/tests/api/compiler/TestCompiler.java b/jadx-core/src/test/java/jadx/tests/api/compiler/TestCompiler.java index e480edef9a3..6c42ff9f2b3 100644 --- a/jadx-core/src/test/java/jadx/tests/api/compiler/TestCompiler.java +++ b/jadx-core/src/test/java/jadx/tests/api/compiler/TestCompiler.java @@ -11,7 +11,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Locale; import javax.tools.DiagnosticListener; import javax.tools.JavaCompiler; @@ -82,7 +81,7 @@ private void compile(List jfObjects) { arguments.addAll(options.getArguments()); DiagnosticListener diagnostic = - diagObj -> System.out.println("Compiler diagnostic: " + diagObj.getMessage(Locale.ROOT)); + diagObj -> System.out.println("Compiler diagnostic: " + diagObj); Writer out = new PrintWriter(System.out); CompilationTask compilerTask = compiler.getTask(out, fileManager, diagnostic, arguments, null, jfObjects); if (Boolean.FALSE.equals(compilerTask.call())) { diff --git a/jadx-core/src/test/java/jadx/tests/integration/invoke/TestSuperInvokeUnknown.java b/jadx-core/src/test/java/jadx/tests/integration/invoke/TestSuperInvokeUnknown.java new file mode 100644 index 00000000000..6c21732ee2e --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/invoke/TestSuperInvokeUnknown.java @@ -0,0 +1,42 @@ +package jadx.tests.integration.invoke; + +import org.junit.jupiter.api.Test; + +import jadx.tests.api.IntegrationTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +public class TestSuperInvokeUnknown extends IntegrationTest { + + public static class TestCls { + public static class BaseClass { + public int doSomething() { + return 0; + } + } + + public static class NestedClass extends BaseClass { + @Override + public int doSomething() { + return super.doSomething(); + } + } + } + + @Test + public void test() { + disableCompilation(); + noDebugInfo(); + assertThat(getClassNode(TestCls.NestedClass.class)) // BaseClass unknown + .code() + .containsOne("return super.doSomething();"); + } + + @Test + public void testTopCls() { + noDebugInfo(); + assertThat(getClassNode(TestCls.class)) + .code() + .containsOne("return super.doSomething();"); + } +}