From 09a5e0893bdf9cb7b5d8e1ac62a972c15d8fd98c Mon Sep 17 00:00:00 2001 From: Skylot Date: Mon, 24 Jul 2023 21:59:27 +0100 Subject: [PATCH] fix: handle classes 'super' loop (#1942) --- .../main/java/jadx/api/JadxDecompiler.java | 1 + .../dex/visitors/OverrideMethodVisitor.java | 18 +++++---- .../core/dex/visitors/ShadowFieldVisitor.java | 8 ++++ .../integration/others/TestSuperLoop.java | 40 +++++++++++++++++++ .../test/smali/others/TestSuperLoop/A.smali | 4 ++ .../test/smali/others/TestSuperLoop/B.smali | 4 ++ 6 files changed, 68 insertions(+), 7 deletions(-) create mode 100644 jadx-core/src/test/java/jadx/tests/integration/others/TestSuperLoop.java create mode 100644 jadx-core/src/test/smali/others/TestSuperLoop/A.smali create mode 100644 jadx-core/src/test/smali/others/TestSuperLoop/B.smali diff --git a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java index bb3a49e25..3c57cb92b 100644 --- a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java +++ b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java @@ -199,6 +199,7 @@ public final class JadxDecompiler implements Closeable { } private void loadFinished() { + LOG.debug("Load finished"); List list = customPasses.get(JadxAfterLoadPass.TYPE); if (list != null) { for (JadxPass pass : list) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/OverrideMethodVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/OverrideMethodVisitor.java index 701b02ab3..a54380bf8 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/OverrideMethodVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/OverrideMethodVisitor.java @@ -2,6 +2,7 @@ package jadx.core.dex.visitors; import java.util.ArrayList; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -281,7 +282,7 @@ public class OverrideMethodVisitor extends AbstractVisitor { @Nullable private SuperTypesData collectSuperTypes(ClassNode cls) { - List superTypes = new ArrayList<>(); + Set superTypes = new LinkedHashSet<>(); Set endTypes = new HashSet<>(); collectSuperTypes(cls, superTypes, endTypes); if (superTypes.isEmpty()) { @@ -290,10 +291,10 @@ public class OverrideMethodVisitor extends AbstractVisitor { if (endTypes.isEmpty()) { throw new JadxRuntimeException("No end types in class hierarchy: " + cls); } - return new SuperTypesData(superTypes, endTypes); + return new SuperTypesData(new ArrayList<>(superTypes), endTypes); } - private void collectSuperTypes(ClassNode cls, List superTypes, Set endTypes) { + private void collectSuperTypes(ClassNode cls, Set superTypes, Set endTypes) { RootNode root = cls.root(); int k = 0; ArgType superClass = cls.getSuperClass(); @@ -308,21 +309,24 @@ public class OverrideMethodVisitor extends AbstractVisitor { } } - private int addSuperType(RootNode root, List superTypesMap, Set endTypes, ArgType superType) { + private int addSuperType(RootNode root, Set superTypes, Set endTypes, ArgType superType) { if (Objects.equals(superType, ArgType.OBJECT)) { return 0; } - superTypesMap.add(superType); + if (!superTypes.add(superType)) { + // found 'super' loop, stop processing + return 0; + } ClassNode classNode = root.resolveClass(superType); if (classNode != null) { - collectSuperTypes(classNode, superTypesMap, endTypes); + collectSuperTypes(classNode, superTypes, endTypes); return 1; } ClspClass clsDetails = root.getClsp().getClsDetails(superType); if (clsDetails != null) { int k = 0; for (ArgType parentType : clsDetails.getParents()) { - k += addSuperType(root, superTypesMap, endTypes, parentType); + k += addSuperType(root, superTypes, endTypes, parentType); } if (k == 0) { endTypes.add(superType.getObject()); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ShadowFieldVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ShadowFieldVisitor.java index d4d4e0e67..0745263cc 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ShadowFieldVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ShadowFieldVisitor.java @@ -3,8 +3,10 @@ package jadx.core.dex.visitors; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.jetbrains.annotations.Nullable; @@ -107,8 +109,14 @@ public class ShadowFieldVisitor extends AbstractVisitor { private static List collectAllInstanceFields(ClassNode cls) { List fieldsList = new ArrayList<>(); + Set visited = new HashSet<>(); ClassNode currentClass = cls; while (currentClass != null) { + if (!visited.add(currentClass)) { + String msg = "Found 'super' loop in classes: " + visited; + visited.forEach(c -> c.addWarnComment(msg)); + return fieldsList; + } for (FieldNode field : currentClass.getFields()) { if (!field.getAccessFlags().isStatic()) { fieldsList.add(field); diff --git a/jadx-core/src/test/java/jadx/tests/integration/others/TestSuperLoop.java b/jadx-core/src/test/java/jadx/tests/integration/others/TestSuperLoop.java new file mode 100644 index 000000000..fee2396d6 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/others/TestSuperLoop.java @@ -0,0 +1,40 @@ +package jadx.tests.integration.others; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +import jadx.core.dex.nodes.ClassNode; +import jadx.tests.api.SmaliTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +@SuppressWarnings("CommentedOutCode") +public class TestSuperLoop extends SmaliTest { + // @formatter:off + /* + public class A extends B { + public int a; + } + + public class B extends A { + public int b; + } + */ + // @formatter:on + + @Test + public void test() { + allowWarnInCode(); + disableCompilation(); + + List clsList = loadFromSmaliFiles(); + assertThat(searchCls(clsList, "A")) + .code() + .containsOne("public class A extends B {"); + + assertThat(searchCls(clsList, "B")) + .code() + .containsOne("public class B extends A {"); + } +} diff --git a/jadx-core/src/test/smali/others/TestSuperLoop/A.smali b/jadx-core/src/test/smali/others/TestSuperLoop/A.smali new file mode 100644 index 000000000..368f8c30a --- /dev/null +++ b/jadx-core/src/test/smali/others/TestSuperLoop/A.smali @@ -0,0 +1,4 @@ +.class public LA; +.super LB; + +.field public a:I diff --git a/jadx-core/src/test/smali/others/TestSuperLoop/B.smali b/jadx-core/src/test/smali/others/TestSuperLoop/B.smali new file mode 100644 index 000000000..9167d7e7d --- /dev/null +++ b/jadx-core/src/test/smali/others/TestSuperLoop/B.smali @@ -0,0 +1,4 @@ +.class public LB; +.super LA; + +.field public b:I