From 46290437211f33456a6a25b2ac8b90cc2a79ea37 Mon Sep 17 00:00:00 2001 From: Skylot Date: Tue, 30 Jul 2019 20:49:31 +0300 Subject: [PATCH] fix: convert inner enums and fix inner classes reference (#719) --- .../main/java/jadx/core/codegen/ClassGen.java | 29 +++++++- .../jadx/core/dex/visitors/EnumVisitor.java | 16 +++-- .../integration/enums/TestInnerEnums.java | 69 +++++++++++++++++++ 3 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 jadx-core/src/test/java/jadx/tests/integration/enums/TestInnerEnums.java 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 3adcc830a..fc21edf63 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java @@ -1,10 +1,12 @@ package jadx.core.codegen; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Objects; import java.util.Set; import com.android.dx.rop.code.AccessFlags; @@ -148,7 +150,7 @@ public class ClassGen { ArgType sup = cls.getSuperClass(); if (sup != null && !sup.equals(ArgType.OBJECT) - && !sup.getObject().equals(ArgType.ENUM.getObject())) { + && !cls.isEnum()) { clsCode.add("extends "); useClass(clsCode, sup); clsCode.add(' '); @@ -513,6 +515,9 @@ public class ClassGen { if (isClassInnerFor(useCls, extClsInfo)) { return shortName; } + if (extClsInfo.isInner()) { + return expandInnerClassName(useCls, extClsInfo); + } if (isBothClassesInOneTopClass(useCls, extClsInfo)) { return shortName; } @@ -550,6 +555,26 @@ public class ClassGen { return shortName; } + private String expandInnerClassName(ClassInfo useCls, ClassInfo extClsInfo) { + List clsList = new ArrayList<>(); + clsList.add(extClsInfo); + ClassInfo parentCls = extClsInfo.getParentClass(); + boolean addImport = true; + while (parentCls != null) { + if (parentCls == useCls || isClassInnerFor(useCls, parentCls)) { + addImport = false; + break; + } + clsList.add(parentCls); + parentCls = parentCls.getParentClass(); + } + Collections.reverse(clsList); + if (addImport) { + addImport(clsList.get(0)); + } + return Utils.listToString(clsList, ".", ClassInfo::getAliasShortName); + } + private void addImport(ClassInfo classInfo) { if (parentGen != null) { parentGen.addImport(classInfo); @@ -579,7 +604,7 @@ public class ClassGen { private static boolean isClassInnerFor(ClassInfo inner, ClassInfo parent) { if (inner.isInner()) { ClassInfo p = inner.getParentClass(); - return p.equals(parent) || isClassInnerFor(p, parent); + return Objects.equals(p, parent) || isClassInnerFor(p, parent); } return false; } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/EnumVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/EnumVisitor.java index 3c51f0f29..d016a37c9 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/EnumVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/EnumVisitor.java @@ -44,13 +44,19 @@ public class EnumVisitor extends AbstractVisitor { @Override public boolean visit(ClassNode cls) throws JadxException { - if (!cls.isEnum()) { + if (!convertToEnum(cls)) { AccessInfo accessFlags = cls.getAccessFlags(); if (accessFlags.isEnum()) { cls.setAccessFlags(accessFlags.remove(AccessFlags.ACC_ENUM)); - cls.addAttr(AType.COMMENTS, "'enum' access flag removed"); + cls.addAttr(AType.COMMENTS, "'enum' modifier removed"); } - return true; + } + return true; + } + + private boolean convertToEnum(ClassNode cls) { + if (!cls.isEnum()) { + return false; } // search class init method MethodNode staticMethod = null; @@ -63,7 +69,7 @@ public class EnumVisitor extends AbstractVisitor { } if (staticMethod == null) { ErrorsCounter.classWarn(cls, "Enum class init method not found"); - return true; + return false; } ArgType clsType = cls.getClassInfo().getType(); @@ -167,7 +173,7 @@ public class EnumVisitor extends AbstractVisitor { } } } - return false; + return true; } private static void processEnumInnerCls(ConstructorInsn co, EnumField field, ClassNode innerCls) { diff --git a/jadx-core/src/test/java/jadx/tests/integration/enums/TestInnerEnums.java b/jadx-core/src/test/java/jadx/tests/integration/enums/TestInnerEnums.java new file mode 100644 index 000000000..025ff5bf1 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/enums/TestInnerEnums.java @@ -0,0 +1,69 @@ +package jadx.tests.integration.enums; + +import org.junit.jupiter.api.Test; + +import jadx.core.dex.nodes.ClassNode; +import jadx.tests.api.IntegrationTest; + +import static jadx.tests.api.utils.JadxMatchers.containsOne; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TestInnerEnums extends IntegrationTest { + + public static class TestCls { + + public enum Numbers { + ONE(1, NumString.ONE), TWO(2, NumString.TWO); + + private final int num; + private final NumString str; + + public enum NumString { + ONE("one"), TWO("two"); + + private final String name; + + NumString(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + + Numbers(int n, NumString str) { + this.num = n; + this.str = str; + } + + public int getNum() { + return num; + } + + public NumString getNumStr() { + return str; + } + + public String getName() { + return str.getName(); + } + } + + public void check() { + assertEquals(1, Numbers.ONE.getNum()); + assertEquals(Numbers.NumString.ONE, Numbers.ONE.getNumStr()); + assertEquals("one", Numbers.ONE.getName()); + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + + assertThat(code, containsOne("ONE(1, NumString.ONE)")); + assertThat(code, containsOne("ONE(\"one\")")); + } +}