From 62ca30bbc61d51e062bd90cf74472a7596c4051e Mon Sep 17 00:00:00 2001 From: Skylot Date: Sun, 19 Jan 2020 11:12:23 +0000 Subject: [PATCH] fix: additional patterns to restore enum classes (#830) --- .../dex/attributes/nodes/EnumClassAttr.java | 8 +- .../jadx/core/dex/visitors/EnumVisitor.java | 307 ++++++++++++------ .../test/java/jadx/tests/api/SmaliTest.java | 5 + .../api/utils/assertj/JadxCodeAssertions.java | 9 +- .../tests/integration/enums/TestEnums5.java | 23 ++ .../integration/enums/TestEnumsInterface.java | 2 + .../enums/TestEnumsWithStaticFields.java | 22 ++ .../src/test/smali/enums/TestEnums5.smali | 129 ++++++++ .../enums/TestEnumsWithStaticFields.smali | 174 ++++++++++ 9 files changed, 576 insertions(+), 103 deletions(-) create mode 100644 jadx-core/src/test/java/jadx/tests/integration/enums/TestEnums5.java create mode 100644 jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumsWithStaticFields.java create mode 100644 jadx-core/src/test/smali/enums/TestEnums5.smali create mode 100644 jadx-core/src/test/smali/enums/TestEnumsWithStaticFields.smali diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/EnumClassAttr.java b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/EnumClassAttr.java index 3d41105b..53f72877 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/EnumClassAttr.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/EnumClassAttr.java @@ -5,26 +5,26 @@ import java.util.List; import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.IAttribute; -import jadx.core.dex.info.FieldInfo; import jadx.core.dex.instructions.mods.ConstructorInsn; import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.MethodNode; public class EnumClassAttr implements IAttribute { public static class EnumField { - private final FieldInfo field; + private final FieldNode field; private final ConstructorInsn constrInsn; private final int startArg; private ClassNode cls; - public EnumField(FieldInfo field, ConstructorInsn co, int startArg) { + public EnumField(FieldNode field, ConstructorInsn co, int startArg) { this.field = field; this.constrInsn = co; this.startArg = startArg; } - public FieldInfo getField() { + public FieldNode getField() { return field; } 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 d016a37c..60d6a9fc 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 @@ -1,7 +1,11 @@ package jadx.core.dex.visitors; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; import org.jetbrains.annotations.Nullable; @@ -22,7 +26,9 @@ import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.InsnWrapArg; +import jadx.core.dex.instructions.args.LiteralArg; import jadx.core.dex.instructions.args.RegisterArg; +import jadx.core.dex.instructions.args.SSAVar; import jadx.core.dex.instructions.mods.ConstructorInsn; import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.ClassNode; @@ -31,7 +37,7 @@ import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.visitors.shrink.CodeShrinkVisitor; -import jadx.core.utils.ErrorsCounter; +import jadx.core.utils.InsnRemover; import jadx.core.utils.InsnUtils; import jadx.core.utils.exceptions.JadxException; @@ -48,7 +54,7 @@ public class EnumVisitor extends AbstractVisitor { AccessInfo accessFlags = cls.getAccessFlags(); if (accessFlags.isEnum()) { cls.setAccessFlags(accessFlags.remove(AccessFlags.ACC_ENUM)); - cls.addAttr(AType.COMMENTS, "'enum' modifier removed"); + cls.addAttr(AType.COMMENTS, "JADX INFO: Failed to restore enum class, 'enum' modifier removed"); } } return true; @@ -58,122 +64,237 @@ public class EnumVisitor extends AbstractVisitor { if (!cls.isEnum()) { return false; } - // search class init method - MethodNode staticMethod = null; - for (MethodNode mth : cls.getMethods()) { - MethodInfo mi = mth.getMethodInfo(); - if (mi.isClassInit()) { - staticMethod = mth; - break; - } + MethodNode classInitMth = cls.getClassInitMth(); + if (classInitMth == null) { + cls.addAttr(AType.COMMENTS, "JADX INFO: Enum class init method not found"); + return false; } - if (staticMethod == null) { - ErrorsCounter.classWarn(cls, "Enum class init method not found"); + if (classInitMth.getBasicBlocks().isEmpty()) { return false; } + BlockNode staticBlock = classInitMth.getBasicBlocks().get(0); ArgType clsType = cls.getClassInfo().getType(); - String enumConstructor = "(Ljava/lang/String;I)V"; - // TODO: detect these methods by analyzing method instructions - String valuesOfMethod = "valueOf(Ljava/lang/String;)" + TypeGen.signature(clsType); - String valuesMethod = "values()" + TypeGen.signature(ArgType.array(clsType)); - // collect enum fields, remove synthetic - List enumFields = new ArrayList<>(); - for (FieldNode f : cls.getFields()) { - if (f.getAccessFlags().isEnum()) { - enumFields.add(f); - f.add(AFlag.DONT_GENERATE); - } else if (f.getAccessFlags().isSynthetic()) { - f.add(AFlag.DONT_GENERATE); + // search "$VALUES" field (holds all enum values) + List valuesCandidates = cls.getFields().stream() + .filter(f -> f.getAccessFlags().isStatic()) + .filter(f -> f.getType().isArray()) + .filter(f -> Objects.equals(f.getType().getArrayRootElement(), clsType)) + .collect(Collectors.toList()); + + if (valuesCandidates.isEmpty()) { + return false; + } + if (valuesCandidates.size() > 1) { + valuesCandidates.removeIf(f -> !f.getAccessFlags().isSynthetic()); + } + if (valuesCandidates.size() > 1) { + Optional valuesOpt = valuesCandidates.stream().filter(f -> f.getName().equals("$VALUES")).findAny(); + if (valuesOpt.isPresent()) { + valuesCandidates.clear(); + valuesCandidates.add(valuesOpt.get()); } } + if (valuesCandidates.size() != 1) { + cls.addAttr(AType.COMMENTS, "JADX INFO: found several \"values\" enum fields: " + valuesCandidates); + return false; + } + FieldNode valuesField = valuesCandidates.get(0); + List toRemove = new ArrayList<>(); - // remove synthetic methods - for (MethodNode mth : cls.getMethods()) { - MethodInfo mi = mth.getMethodInfo(); - if (mi.isClassInit()) { + // search "$VALUES" array init and collect enum fields + List enumFields = null; + for (InsnNode insn : staticBlock.getInstructions()) { + if (insn.getType() != InsnType.SPUT) { continue; } - String shortId = mi.getShortId(); - boolean isSynthetic = mth.getAccessFlags().isSynthetic(); - if (mi.isConstructor() && !isSynthetic) { - if (shortId.equals(enumConstructor)) { - mth.add(AFlag.DONT_GENERATE); + FieldInfo f = (FieldInfo) ((IndexInsnNode) insn).getIndex(); + if (f.equals(valuesField.getFieldInfo())) { + InsnArg arrArg = insn.getArg(0); + if (arrArg.isInsnWrap()) { + InsnNode arrFillInsn = ((InsnWrapArg) arrArg).getWrapInsn(); + InsnType insnType = arrFillInsn.getType(); + if (insnType == InsnType.FILLED_NEW_ARRAY) { + enumFields = extractEnumFields(cls, arrFillInsn, staticBlock, toRemove); + } else if (insnType == InsnType.NEW_ARRAY) { + // empty enum + InsnArg arg = arrFillInsn.getArg(0); + if (arg.isLiteral() && ((LiteralArg) arg).getLiteral() == 0) { + enumFields = Collections.emptyList(); + } + } } - } else if (isSynthetic - || shortId.equals(valuesMethod) - || shortId.equals(valuesOfMethod)) { - mth.add(AFlag.DONT_GENERATE); + toRemove.add(insn); + break; } } + if (enumFields == null) { + return false; + } + // all checks complete, perform transform EnumClassAttr attr = new EnumClassAttr(enumFields.size()); + attr.setStaticMethod(classInitMth); + attr.getFields().addAll(enumFields); cls.addAttr(attr); - attr.setStaticMethod(staticMethod); - ClassInfo classInfo = cls.getClassInfo(); + for (EnumField field : attr.getFields()) { + ConstructorInsn co = field.getConstrInsn(); + FieldNode fieldNode = field.getField(); - // move enum specific instruction from static method to separate list - BlockNode staticBlock = staticMethod.getBasicBlocks().get(0); - List enumPutInsns = new ArrayList<>(); - List list = staticBlock.getInstructions(); - int size = list.size(); - for (int i = 0; i < size; i++) { - InsnNode insn = list.get(i); - if (insn.getType() != InsnType.SPUT) { - continue; - } - FieldInfo f = (FieldInfo) ((IndexInsnNode) insn).getIndex(); - if (!f.getDeclClass().equals(classInfo)) { - continue; + // use string arg from the constructor as enum field name + String name = getConstString(cls.dex(), co.getArg(0)); + if (name != null + && !fieldNode.getAlias().equals(name) + && NameMapper.isValidAndPrintable(name) + && cls.root().getArgs().isRenameValid()) { + fieldNode.getFieldInfo().setAlias(name); } - FieldNode fieldNode = cls.searchField(f); - if (fieldNode != null && isEnumArrayField(classInfo, fieldNode)) { - if (i == size - 1) { - staticMethod.add(AFlag.DONT_GENERATE); - } else { - list.subList(0, i + 1).clear(); + if (!co.getClassType().equals(cls.getClassInfo())) { + // enum contains additional methods + for (ClassNode innerCls : cls.getInnerClasses()) { + processEnumInnerCls(co, field, innerCls); } - break; - } else { - enumPutInsns.add(insn); } } - for (InsnNode putInsn : enumPutInsns) { - ConstructorInsn co = getConstructorInsn(putInsn); - if (co == null || co.getArgsCount() < 2) { - continue; - } - ClassInfo clsInfo = co.getClassType(); - ClassNode constrCls = cls.dex().resolveClass(clsInfo); - if (constrCls == null) { - continue; + valuesField.add(AFlag.DONT_GENERATE); + enumFields.forEach(f -> f.getField().add(AFlag.DONT_GENERATE)); + InsnRemover.removeAllAndUnbind(classInitMth, staticBlock, toRemove); + if (classInitMth.countInsns() == 0) { + classInitMth.add(AFlag.DONT_GENERATE); + } + removeEnumMethods(cls, clsType); + return true; + } + + private List extractEnumFields(ClassNode cls, InsnNode arrFillInsn, BlockNode staticBlock, List toRemove) { + List enumFields = new ArrayList<>(); + for (InsnArg arg : arrFillInsn.getArguments()) { + EnumField field = null; + if (arg.isInsnWrap()) { + InsnNode wrappedInsn = ((InsnWrapArg) arg).getWrapInsn(); + field = processEnumFieldByField(cls, wrappedInsn, staticBlock, toRemove); + } else if (arg.isRegister()) { + field = processEnumFiledByRegister(cls, ((RegisterArg) arg), toRemove); } - if (!clsInfo.equals(classInfo) && !constrCls.getAccessFlags().isEnum()) { - continue; + if (field == null) { + return null; } - FieldInfo fieldInfo = (FieldInfo) ((IndexInsnNode) putInsn).getIndex(); - String name = getConstString(cls.dex(), co.getArg(0)); - if (name != null - && !fieldInfo.getAlias().equals(name) - && NameMapper.isValidAndPrintable(name) - && cls.root().getArgs().isRenameValid()) { - fieldInfo.setAlias(name); + enumFields.add(field); + } + return enumFields; + } + + @Nullable + private EnumField processEnumFieldByField(ClassNode cls, InsnNode sgetInsn, BlockNode staticBlock, List toRemove) { + if (sgetInsn.getType() != InsnType.SGET) { + return null; + } + FieldInfo fieldInfo = (FieldInfo) ((IndexInsnNode) sgetInsn).getIndex(); + FieldNode enumFieldNode = cls.searchField(fieldInfo); + if (enumFieldNode == null) { + return null; + } + InsnNode sputInsn = searchFieldPutInsn(cls, staticBlock, enumFieldNode); + if (sputInsn == null) { + return null; + } + + ConstructorInsn co = getConstructorInsn(sputInsn); + if (co == null) { + return null; + } + toRemove.add(sgetInsn); + toRemove.add(sputInsn); + toRemove.add(co); + return createEnumFieldByConstructor(cls, enumFieldNode, co); + } + + @Nullable + private EnumField processEnumFiledByRegister(ClassNode cls, RegisterArg arg, List toRemove) { + SSAVar ssaVar = arg.getSVar(); + if (ssaVar.getUseCount() == 1) { + return null; + } + final InsnNode sputInsn = ssaVar.getUseList().get(0).getParentInsn(); + if (sputInsn == null || sputInsn.getType() != InsnType.SPUT) { + return null; + } + FieldInfo fieldInfo = (FieldInfo) ((IndexInsnNode) sputInsn).getIndex(); + FieldNode enumFieldNode = cls.searchField(fieldInfo); + if (enumFieldNode == null) { + return null; + } + + InsnNode constrInsn = ssaVar.getAssign().getParentInsn(); + if (constrInsn == null || constrInsn.getType() != InsnType.CONSTRUCTOR) { + return null; + } + toRemove.add(sputInsn); + toRemove.add(constrInsn); + return createEnumFieldByConstructor(cls, enumFieldNode, (ConstructorInsn) constrInsn); + } + + private EnumField createEnumFieldByConstructor(ClassNode cls, FieldNode enumFieldNode, ConstructorInsn co) { + // usually constructor signature is '(Ljava/lang/String;I)V'. + // sometimes for one field enum second arg can be omitted + if (co.getArgsCount() < 1) { + return null; + } + ClassInfo clsInfo = co.getClassType(); + ClassNode constrCls = cls.dex().resolveClass(clsInfo); + if (constrCls == null) { + return null; + } + if (!clsInfo.equals(cls.getClassInfo()) && !constrCls.getAccessFlags().isEnum()) { + return null; + } + int startArg = co.getArgsCount() == 1 ? 1 : 2; + return new EnumField(enumFieldNode, co, startArg); + } + + @Nullable + private InsnNode searchFieldPutInsn(ClassNode cls, BlockNode staticBlock, FieldNode enumFieldNode) { + for (InsnNode sputInsn : staticBlock.getInstructions()) { + if (sputInsn != null && sputInsn.getType() == InsnType.SPUT) { + FieldInfo f = (FieldInfo) ((IndexInsnNode) sputInsn).getIndex(); + FieldNode fieldNode = cls.searchField(f); + if (Objects.equals(fieldNode, enumFieldNode)) { + return sputInsn; + } } + } + return null; + } - EnumField field = new EnumField(fieldInfo, co, 2); - attr.getFields().add(field); + // TODO: detect these methods by analyzing method instructions + private void removeEnumMethods(ClassNode cls, ArgType clsType) { + String enumConstructor = "(Ljava/lang/String;I)V"; + String enumConstructorAlt = "(Ljava/lang/String;)V"; + String valuesOfMethod = "valueOf(Ljava/lang/String;)" + TypeGen.signature(clsType); + String valuesMethod = "values()" + TypeGen.signature(ArgType.array(clsType)); - if (!co.getClassType().equals(classInfo)) { - // enum contains additional methods - for (ClassNode innerCls : cls.getInnerClasses()) { - processEnumInnerCls(co, field, innerCls); + // remove synthetic methods + for (MethodNode mth : cls.getMethods()) { + MethodInfo mi = mth.getMethodInfo(); + if (mi.isClassInit()) { + continue; + } + String shortId = mi.getShortId(); + boolean isSynthetic = mth.getAccessFlags().isSynthetic(); + if (mi.isConstructor() && !isSynthetic) { + if (shortId.equals(enumConstructor) + || shortId.equals(enumConstructorAlt)) { + mth.add(AFlag.DONT_GENERATE); } + } else if (isSynthetic + || shortId.equals(valuesMethod) + || shortId.equals(valuesOfMethod)) { + mth.add(AFlag.DONT_GENERATE); } } - return true; } private static void processEnumInnerCls(ConstructorInsn co, EnumField field, ClassNode innerCls) { @@ -190,19 +311,11 @@ public class EnumVisitor extends AbstractVisitor { innerCls.add(AFlag.DONT_GENERATE); } - private boolean isEnumArrayField(ClassInfo classInfo, FieldNode fieldNode) { - if (fieldNode.getAccessFlags().isSynthetic()) { - ArgType fType = fieldNode.getType(); - return fType.isArray() && fType.getArrayRootElement().equals(classInfo.getType()); - } - return false; - } - - private ConstructorInsn getConstructorInsn(InsnNode putInsn) { - if (putInsn.getArgsCount() != 1) { + private ConstructorInsn getConstructorInsn(InsnNode insn) { + if (insn.getArgsCount() != 1) { return null; } - InsnArg arg = putInsn.getArg(0); + InsnArg arg = insn.getArg(0); if (arg.isInsnWrap()) { return castConstructorInsn(((InsnWrapArg) arg).getWrapInsn()); } diff --git a/jadx-core/src/test/java/jadx/tests/api/SmaliTest.java b/jadx-core/src/test/java/jadx/tests/api/SmaliTest.java index 98e10a4c..241760dc 100644 --- a/jadx-core/src/test/java/jadx/tests/api/SmaliTest.java +++ b/jadx-core/src/test/java/jadx/tests/api/SmaliTest.java @@ -38,6 +38,11 @@ public abstract class SmaliTest extends IntegrationTest { return getClassNodeFromSmaliWithPkg(getTestPkg(), getTestName()); } + protected ClassNode getClassNodeFromSmaliWithClsName(String fullClsName) { + return getClassNodeFromSmali(getTestPkg() + File.separatorChar + getTestName(), fullClsName); + } + + @Deprecated protected ClassNode getClassNodeFromSmali(String clsName) { return getClassNodeFromSmali(clsName, clsName); } diff --git a/jadx-core/src/test/java/jadx/tests/api/utils/assertj/JadxCodeAssertions.java b/jadx-core/src/test/java/jadx/tests/api/utils/assertj/JadxCodeAssertions.java index c933a09c..1a24ac94 100644 --- a/jadx-core/src/test/java/jadx/tests/api/utils/assertj/JadxCodeAssertions.java +++ b/jadx-core/src/test/java/jadx/tests/api/utils/assertj/JadxCodeAssertions.java @@ -42,14 +42,19 @@ public class JadxCodeAssertions extends AbstractStringAssert } String indent = TestUtils.indent(commonIndent); StringBuilder sb = new StringBuilder(); + boolean first = true; for (String line : lines) { if (!line.isEmpty()) { + if (first) { + first = false; + } else { + sb.append(CodeWriter.NL); + } sb.append(indent); sb.append(line); } - sb.append(CodeWriter.NL); } - return countString(1, sb.toString()); + return containsOnlyOnce(sb.toString()); } public JadxCodeAssertions print() { diff --git a/jadx-core/src/test/java/jadx/tests/integration/enums/TestEnums5.java b/jadx-core/src/test/java/jadx/tests/integration/enums/TestEnums5.java new file mode 100644 index 00000000..5484dc6c --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/enums/TestEnums5.java @@ -0,0 +1,23 @@ +package jadx.tests.integration.enums; + +import org.junit.jupiter.api.Test; + +import jadx.tests.api.SmaliTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +public class TestEnums5 extends SmaliTest { + + @Test + public void test() { + assertThat(getClassNodeFromSmaliWithClsName("kotlin.collections.State")) + .code() + .containsLines( + "enum State {", + indent() + "Ready,", + indent() + "NotReady,", + indent() + "Done,", + indent() + "Failed", + "}"); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumsInterface.java b/jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumsInterface.java index 9ead7309..4b782a04 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumsInterface.java +++ b/jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumsInterface.java @@ -14,11 +14,13 @@ public class TestEnumsInterface extends IntegrationTest { public enum Operation implements IOperation { PLUS { + @Override public int apply(int x, int y) { return x + y; } }, MINUS { + @Override public int apply(int x, int y) { return x - y; } diff --git a/jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumsWithStaticFields.java b/jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumsWithStaticFields.java new file mode 100644 index 00000000..f4c669bb --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumsWithStaticFields.java @@ -0,0 +1,22 @@ +package jadx.tests.integration.enums; + +import org.junit.jupiter.api.Test; + +import jadx.tests.api.SmaliTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +public class TestEnumsWithStaticFields extends SmaliTest { + + @Test + public void test() { + disableCompilation(); + assertThat(getClassNodeFromSmali()) + .code() + .containsOnlyOnce("INSTANCE;") + .containsOnlyOnce("private static c sB;") + .doesNotContain(" sA") + .doesNotContain(" sC") + .doesNotContain("private TestEnumsWithStaticFields(String str) {"); + } +} diff --git a/jadx-core/src/test/smali/enums/TestEnums5.smali b/jadx-core/src/test/smali/enums/TestEnums5.smali new file mode 100644 index 00000000..41cd3791 --- /dev/null +++ b/jadx-core/src/test/smali/enums/TestEnums5.smali @@ -0,0 +1,129 @@ +.class final enum Lkotlin/collections/State; +.super Ljava/lang/Enum; + + +# annotations +.annotation system Ldalvik/annotation/Signature; + value = { + "Ljava/lang/Enum<", + "Lkotlin/collections/State;", + ">;" + } +.end annotation + +# static fields +.field private static final synthetic $VALUES:[Lkotlin/collections/State; + +.field public static final enum Done:Lkotlin/collections/State; + +.field public static final enum Failed:Lkotlin/collections/State; + +.field public static final enum NotReady:Lkotlin/collections/State; + +.field public static final enum Ready:Lkotlin/collections/State; + + +# direct methods +.method static constructor ()V + .registers 4 + + const/4 v0, 0x4 + + new-array v0, v0, [Lkotlin/collections/State; + + new-instance v1, Lkotlin/collections/State; + + const-string v2, "Ready" + + const/4 v3, 0x0 + + invoke-direct {v1, v2, v3}, Lkotlin/collections/State;->(Ljava/lang/String;I)V + + sput-object v1, Lkotlin/collections/State;->Ready:Lkotlin/collections/State; + + aput-object v1, v0, v3 + + new-instance v1, Lkotlin/collections/State; + + const-string v2, "NotReady" + + const/4 v3, 0x1 + + invoke-direct {v1, v2, v3}, Lkotlin/collections/State;->(Ljava/lang/String;I)V + + sput-object v1, Lkotlin/collections/State;->NotReady:Lkotlin/collections/State; + + aput-object v1, v0, v3 + + new-instance v1, Lkotlin/collections/State; + + const-string v2, "Done" + + const/4 v3, 0x2 + + invoke-direct {v1, v2, v3}, Lkotlin/collections/State;->(Ljava/lang/String;I)V + + sput-object v1, Lkotlin/collections/State;->Done:Lkotlin/collections/State; + + aput-object v1, v0, v3 + + new-instance v1, Lkotlin/collections/State; + + const-string v2, "Failed" + + const/4 v3, 0x3 + + invoke-direct {v1, v2, v3}, Lkotlin/collections/State;->(Ljava/lang/String;I)V + + sput-object v1, Lkotlin/collections/State;->Failed:Lkotlin/collections/State; + + aput-object v1, v0, v3 + + sput-object v0, Lkotlin/collections/State;->$VALUES:[Lkotlin/collections/State; + + return-void +.end method + +.method protected constructor (Ljava/lang/String;I)V + .registers 3 + .param p1, "$enum_name_or_ordinal$0" # Ljava/lang/String; + .param p2, "$enum_name_or_ordinal$1" # I + .annotation system Ldalvik/annotation/Signature; + value = { + "()V" + } + .end annotation + + .line 4 + invoke-direct {p0, p1, p2}, Ljava/lang/Enum;->(Ljava/lang/String;I)V + + return-void +.end method + +.method public static valueOf(Ljava/lang/String;)Lkotlin/collections/State; + .registers 2 + + const-class v0, Lkotlin/collections/State; + + invoke-static {v0, p0}, Ljava/lang/Enum;->valueOf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; + + move-result-object p0 + + check-cast p0, Lkotlin/collections/State; + + return-object p0 +.end method + +.method public static values()[Lkotlin/collections/State; + .registers 1 + + sget-object v0, Lkotlin/collections/State;->$VALUES:[Lkotlin/collections/State; + + invoke-virtual {v0}, [Lkotlin/collections/State;->clone()Ljava/lang/Object; + + move-result-object v0 + + check-cast v0, [Lkotlin/collections/State; + + return-object v0 +.end method diff --git a/jadx-core/src/test/smali/enums/TestEnumsWithStaticFields.smali b/jadx-core/src/test/smali/enums/TestEnumsWithStaticFields.smali new file mode 100644 index 00000000..845af406 --- /dev/null +++ b/jadx-core/src/test/smali/enums/TestEnumsWithStaticFields.smali @@ -0,0 +1,174 @@ +.class public final enum Lenums/TestEnumsWithStaticFields; +.super Ljava/lang/Enum; +.source "SourceFile" + +# interfaces +.implements Lx/a/c; + + +# annotations +.annotation system Ldalvik/annotation/MemberClasses; + value = { + Lx/a/d$a; + } +.end annotation + +.annotation system Ldalvik/annotation/Signature; + value = { + "Ljava/lang/Enum", + "<", + "Lenums/TestEnumsWithStaticFields;", + ">;", + "Lx/a/c;" + } +.end annotation + + +# static fields +.field public static final enum sA:Lenums/TestEnumsWithStaticFields; + +.field private static sB:Lx/a/c; + +.field private static final synthetic sC:[Lenums/TestEnumsWithStaticFields; + + +# direct methods +.method static constructor ()V + .registers 4 + + .prologue + const v3, 0x23900 + + const/4 v2, 0x0 + + invoke-static {v3}, Lx/q;->i(I)V + + .line 10 + new-instance v0, Lenums/TestEnumsWithStaticFields; + + const-string/jumbo v1, "INSTANCE" + + invoke-direct {v0, v1}, Lenums/TestEnumsWithStaticFields;->(Ljava/lang/String;)V + + sput-object v0, Lenums/TestEnumsWithStaticFields;->sA:Lenums/TestEnumsWithStaticFields; + + .line 9 + const/4 v0, 0x1 + + new-array v0, v0, [Lenums/TestEnumsWithStaticFields; + + sget-object v1, Lenums/TestEnumsWithStaticFields;->sA:Lenums/TestEnumsWithStaticFields; + + aput-object v1, v0, v2 + + sput-object v0, Lenums/TestEnumsWithStaticFields;->sC:[Lenums/TestEnumsWithStaticFields; + + .line 36 + new-instance v0, Lx/a/d$a; + + invoke-direct {v0, v2}, Lx/a/d$a;->(B)V + + sput-object v0, Lenums/TestEnumsWithStaticFields;->sB:Lx/a/c; + + invoke-static {v3}, Lx/q;->o(I)V + + return-void +.end method + +.method private constructor (Ljava/lang/String;)V + .registers 3 + .annotation system Ldalvik/annotation/Signature; + value = { + "()V" + } + .end annotation + + .prologue + .line 9 + const/4 v0, 0x0 + + invoke-direct {p0, p1, v0}, Ljava/lang/Enum;->(Ljava/lang/String;I)V + + return-void +.end method + +.method public static a(Lx/a/c;)V + .registers 1 + + .prologue + .line 79 + if-eqz p0, :cond_4 + + .line 80 + sput-object p0, Lenums/TestEnumsWithStaticFields;->sB:Lx/a/c; + + .line 82 + :cond_4 + return-void +.end method + +.method public static valueOf(Ljava/lang/String;)Lenums/TestEnumsWithStaticFields; + .registers 3 + + .prologue + const v1, 0x238f8 + + invoke-static {v1}, Lx/q;->i(I)V + + .line 9 + const-class v0, Lenums/TestEnumsWithStaticFields; + + invoke-static {v0, p0}, Ljava/lang/Enum;->valueOf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; + + move-result-object v0 + + check-cast v0, Lenums/TestEnumsWithStaticFields; + + invoke-static {v1}, Lx/q;->o(I)V + + return-object v0 +.end method + +.method public static values()[Lenums/TestEnumsWithStaticFields; + .registers 2 + + .prologue + const v1, 0x238f7 + + invoke-static {v1}, Lx/q;->i(I)V + + .line 9 + sget-object v0, Lenums/TestEnumsWithStaticFields;->sC:[Lenums/TestEnumsWithStaticFields; + + invoke-virtual {v0}, [Lenums/TestEnumsWithStaticFields;->clone()Ljava/lang/Object; + + move-result-object v0 + + check-cast v0, [Lenums/TestEnumsWithStaticFields; + + invoke-static {v1}, Lx/q;->o(I)V + + return-object v0 +.end method + + +# virtual methods +.method public final FR(I)V + .registers 4 + + .prologue + const v1, 0x238fb + + invoke-static {v1}, Lx/q;->i(I)V + + .line 96 + sget-object v0, Lenums/TestEnumsWithStaticFields;->sB:Lx/a/c; + + invoke-interface {v0, p1}, Lx/a/c;->FR(I)V + + .line 97 + invoke-static {v1}, Lx/q;->o(I)V + + return-void +.end method + -- GitLab