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 3816ceb42..eda230027 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 @@ -192,7 +192,14 @@ public class EnumVisitor extends AbstractVisitor { if (ctrMth != null) { markArgsForSkip(ctrMth); } - InsnRemover.removeWithoutUnbind(classInitMth, staticBlock, co); + if (co.getResult().getSVar().getUseList().size() <= 2) { + InsnRemover.removeWithoutUnbind(classInitMth, staticBlock, co); + } else { + // constructor result used in other places -> replace constructor with enum field get (SGET) + IndexInsnNode enumGet = new IndexInsnNode(InsnType.SGET, enumField.getField().getFieldInfo(), 0); + enumGet.setResult(co.getResult().duplicate()); + BlockUtils.replaceInsn(classInitMth, staticBlock, co, enumGet); + } } @Nullable @@ -335,7 +342,6 @@ public class EnumVisitor extends AbstractVisitor { enumFieldNode = createFakeField(cls, "EF" + arg.getRegNum()); cls.addField(enumFieldNode); } - toRemove.add(constrInsn); return createEnumFieldByConstructor(cls, enumFieldNode, (ConstructorInsn) constrInsn); } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ExtractFieldInit.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ExtractFieldInit.java index 80f6fce96..2dc7d16a8 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ExtractFieldInit.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ExtractFieldInit.java @@ -21,6 +21,7 @@ import jadx.core.dex.nodes.ClassNode; 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.BlockUtils; import jadx.core.utils.InsnRemover; import jadx.core.utils.exceptions.JadxException; @@ -80,8 +81,16 @@ public class ExtractFieldInit extends AbstractVisitor { if (classInitMth == null) { return; } + while (processFields(cls, classInitMth)) { + // sometimes instructions moved to field init prevent from vars inline -> inline and try again + CodeShrinkVisitor.shrinkMethod(classInitMth); + } + } + + private static boolean processFields(ClassNode cls, MethodNode classInitMth) { + boolean changed = false; for (FieldNode field : cls.getFields()) { - if (field.contains(AFlag.DONT_GENERATE)) { + if (field.contains(AFlag.DONT_GENERATE) || field.contains(AType.FIELD_INIT)) { continue; } if (field.getAccessFlags().isStatic()) { @@ -95,10 +104,12 @@ public class ExtractFieldInit extends AbstractVisitor { } InsnRemover.remove(classInitMth, insn); addFieldInitAttr(classInitMth, field, insn); + changed = true; } } } } + return changed; } private static class InitInfo { diff --git a/jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumWithFields.java b/jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumWithFields.java new file mode 100644 index 000000000..0daf1cb5b --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/enums/TestEnumWithFields.java @@ -0,0 +1,47 @@ +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; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TestEnumWithFields extends SmaliTest { + + public static class TestCls { + + public enum SearchTimeout { + DISABLED(0), TWO_SECONDS(2), FIVE_SECONDS(5); + + public static final SearchTimeout DEFAULT = DISABLED; + public static final SearchTimeout MAX = FIVE_SECONDS; + + public final int sec; + + SearchTimeout(int val) { + this.sec = val; + } + } + + public void check() { + assertEquals(0, SearchTimeout.DISABLED.sec); + assertEquals(0, SearchTimeout.DEFAULT.sec); + assertEquals(2, SearchTimeout.TWO_SECONDS.sec); + assertEquals(5, SearchTimeout.MAX.sec); + } + } + + @Test + public void test() { + noDebugInfo(); + assertThat(getClassNode(TestCls.class)) + .code(); + } + + @Test + public void test2() { + assertThat(getClassNodeFromSmali()) + .code(); + } +} diff --git a/jadx-core/src/test/smali/enums/TestEnumWithFields.smali b/jadx-core/src/test/smali/enums/TestEnumWithFields.smali new file mode 100644 index 000000000..b7d58a3c3 --- /dev/null +++ b/jadx-core/src/test/smali/enums/TestEnumWithFields.smali @@ -0,0 +1,149 @@ +.class public final enum Lenums/TestEnumWithFields; +.super Ljava/lang/Enum; + + +# annotations +.annotation system Ldalvik/annotation/Signature; + value = { + "Ljava/lang/Enum<", + "Lenums/TestEnumWithFields;", + ">;" + } +.end annotation + + +# static fields +.field public static final synthetic $VALUES:[Lenums/TestEnumWithFields; + +.field public static final DEFAULT:Lenums/TestEnumWithFields; + +.field public static final enum DISABLED:Lenums/TestEnumWithFields; + +.field public static final enum FIVE_SECONDS:Lenums/TestEnumWithFields; + +.field public static final MAX:Lenums/TestEnumWithFields; + +.field public static final enum TWO_AND_A_HALF_SECONDS:Lenums/TestEnumWithFields; + +.field public static final sValues:[Lenums/TestEnumWithFields; + + +# instance fields +.field public final mRawValue:I + + +# direct methods +.method public static constructor ()V + .locals 6 + + .line 1 + new-instance v0, Lenums/TestEnumWithFields; + + const/4 v1, 0x0 + + const-string v2, "DISABLED" + + invoke-direct {v0, v2, v1, v1}, Lenums/TestEnumWithFields;->(Ljava/lang/String;II)V + + sput-object v0, Lenums/TestEnumWithFields;->DISABLED:Lenums/TestEnumWithFields; + + .line 2 + new-instance v0, Lenums/TestEnumWithFields; + + const/4 v2, 0x1 + + const-string v3, "TWO_AND_A_HALF_SECONDS" + + invoke-direct {v0, v3, v2, v2}, Lenums/TestEnumWithFields;->(Ljava/lang/String;II)V + + sput-object v0, Lenums/TestEnumWithFields;->TWO_AND_A_HALF_SECONDS:Lenums/TestEnumWithFields; + + .line 3 + new-instance v0, Lenums/TestEnumWithFields; + + const/4 v3, 0x2 + + const-string v4, "FIVE_SECONDS" + + invoke-direct {v0, v4, v3, v3}, Lenums/TestEnumWithFields;->(Ljava/lang/String;II)V + + sput-object v0, Lenums/TestEnumWithFields;->FIVE_SECONDS:Lenums/TestEnumWithFields; + + const/4 v4, 0x3 + + new-array v4, v4, [Lenums/TestEnumWithFields; + + .line 4 + sget-object v5, Lenums/TestEnumWithFields;->DISABLED:Lenums/TestEnumWithFields; + + aput-object v5, v4, v1 + + sget-object v1, Lenums/TestEnumWithFields;->TWO_AND_A_HALF_SECONDS:Lenums/TestEnumWithFields; + + aput-object v1, v4, v2 + + aput-object v0, v4, v3 + + sput-object v4, Lenums/TestEnumWithFields;->$VALUES:[Lenums/TestEnumWithFields; + + .line 5 + sput-object v5, Lenums/TestEnumWithFields;->DEFAULT:Lenums/TestEnumWithFields; + + .line 6 + sput-object v0, Lenums/TestEnumWithFields;->MAX:Lenums/TestEnumWithFields; + + .line 7 + invoke-static {}, Lenums/TestEnumWithFields;->values()[Lenums/TestEnumWithFields; + + move-result-object v0 + + sput-object v0, Lenums/TestEnumWithFields;->sValues:[Lenums/TestEnumWithFields; + + return-void +.end method + +.method public constructor (Ljava/lang/String;II)V + .locals 0 + .annotation system Ldalvik/annotation/Signature; + value = { + "(I)V" + } + .end annotation + + .line 1 + invoke-direct {p0, p1, p2}, Ljava/lang/Enum;->(Ljava/lang/String;I)V + + iput p3, p0, Lenums/TestEnumWithFields;->mRawValue:I + + return-void +.end method + +.method public static valueOf(Ljava/lang/String;)Lenums/TestEnumWithFields; + .locals 1 + + .line 1 + const-class v0, Lenums/TestEnumWithFields; + + invoke-static {v0, p0}, Ljava/lang/Enum;->valueOf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; + + move-result-object p0 + + check-cast p0, Lenums/TestEnumWithFields; + + return-object p0 +.end method + +.method public static values()[Lenums/TestEnumWithFields; + .locals 1 + + .line 1 + sget-object v0, Lenums/TestEnumWithFields;->$VALUES:[Lenums/TestEnumWithFields; + + invoke-virtual {v0}, [Lenums/TestEnumWithFields;->clone()Ljava/lang/Object; + + move-result-object v0 + + check-cast v0, [Lenums/TestEnumWithFields; + + return-object v0 +.end method