diff --git a/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java b/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java index ae95d6c6b..7e9858cb4 100644 --- a/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java +++ b/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java @@ -66,9 +66,13 @@ public final class ClassInfo implements Comparable { } public void changeShortName(String aliasName) { - ClassAliasInfo newAlias = new ClassAliasInfo(getAliasPkg(), aliasName); - fillAliasFullName(newAlias); - this.alias = newAlias; + if (!Objects.equals(name, aliasName)) { + ClassAliasInfo newAlias = new ClassAliasInfo(getAliasPkg(), aliasName); + fillAliasFullName(newAlias); + this.alias = newAlias; + } else { + this.alias = null; + } } public void changePkg(String aliasPkg) { diff --git a/jadx-core/src/main/java/jadx/core/dex/info/ConstStorage.java b/jadx-core/src/main/java/jadx/core/dex/info/ConstStorage.java index fbd706150..d599d83bc 100644 --- a/jadx-core/src/main/java/jadx/core/dex/info/ConstStorage.java +++ b/jadx-core/src/main/java/jadx/core/dex/info/ConstStorage.java @@ -2,8 +2,10 @@ package jadx.core.dex.info; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import org.jetbrains.annotations.Nullable; @@ -52,6 +54,18 @@ public class ConstStorage { public boolean contains(Object value) { return duplicates.contains(value) || values.containsKey(value); } + + void removeForCls(ClassNode cls) { + Iterator> it = values.entrySet().iterator(); + while (it.hasNext()) { + Entry entry = it.next(); + FieldNode field = entry.getValue(); + if (field.getParentClass().equals(cls)) { + it.remove(); + duplicates.remove(entry.getKey()); + } + } + } } private final boolean replaceEnabled; @@ -82,6 +96,11 @@ public class ConstStorage { } } + public void removeForClass(ClassNode cls) { + classes.remove(cls); + globalValues.removeForCls(cls); + } + private void addConstField(ClassNode cls, FieldNode fld, Object value, boolean isPublic) { if (isPublic) { globalValues.put(value, fld); diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java index 00f3b0390..62d693d29 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java @@ -35,6 +35,7 @@ import jadx.core.dex.nodes.parser.AnnotationsParser; import jadx.core.dex.nodes.parser.FieldInitAttr; import jadx.core.dex.nodes.parser.SignatureParser; import jadx.core.dex.nodes.parser.StaticValuesParser; +import jadx.core.dex.visitors.ProcessAnonymous; import jadx.core.utils.SmaliUtils; import jadx.core.utils.exceptions.DecodeException; import jadx.core.utils.exceptions.JadxRuntimeException; @@ -175,9 +176,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode { private void loadStaticValues(ClassDef cls, List staticFields) throws DecodeException { for (FieldNode f : staticFields) { - AccessInfo flags = f.getAccessFlags(); - if (flags.isStatic() && flags.isFinal()) { - LOG.debug("loadStaticValues(): Adding NULL initializer to static final field {}", f.getAlias()); + if (f.getAccessFlags().isFinal()) { f.addAttr(FieldInitAttr.NULL_VALUE); } } @@ -281,12 +280,21 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode { public synchronized ICodeInfo reloadCode() { unload(); - clearAttributes(); - initialLoad(); - load(); + deepUnload(); return decompile(false); } + private void deepUnload() { + clearAttributes(); + root().getConstValues().removeForClass(this); + initialLoad(); + ProcessAnonymous.runForClass(this); + + for (ClassNode innerClass : innerClasses) { + innerClass.deepUnload(); + } + } + private synchronized ICodeInfo decompile(boolean searchInCache) { ICodeCache codeCache = root().getCodeCache(); ClassNode topParentClass = getTopParentClass(); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ProcessAnonymous.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ProcessAnonymous.java index faa66b823..749e5eaca 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ProcessAnonymous.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ProcessAnonymous.java @@ -5,27 +5,29 @@ import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.RootNode; -import jadx.core.dex.visitors.regions.RegionMakerVisitor; @JadxVisitor( name = "ProcessAnonymous", - desc = "Mark anonymous and lambda classes (for future inline)", - runAfter = RegionMakerVisitor.class + desc = "Mark anonymous and lambda classes (for future inline)" ) public class ProcessAnonymous extends AbstractVisitor { @Override public void init(RootNode root) { - if (!root.getArgs().isInlineAnonymousClasses()) { - return; + if (root.getArgs().isInlineAnonymousClasses()) { + for (ClassNode cls : root.getClasses(true)) { + markAnonymousClass(cls); + } } + } - for (ClassNode cls : root.getClasses(true)) { + public static void runForClass(ClassNode cls) { + if (cls.root().getArgs().isInlineAnonymousClasses()) { markAnonymousClass(cls); } } - private static boolean markAnonymousClass(ClassNode cls) { + private static void markAnonymousClass(ClassNode cls) { if (isAnonymous(cls) || isLambdaCls(cls)) { cls.add(AFlag.ANONYMOUS_CLASS); cls.add(AFlag.DONT_GENERATE); @@ -35,9 +37,7 @@ public class ProcessAnonymous extends AbstractVisitor { mth.add(AFlag.ANONYMOUS_CONSTRUCTOR); } } - return true; } - return false; } private static boolean isAnonymous(ClassNode cls) { @@ -62,5 +62,4 @@ public class ProcessAnonymous extends AbstractVisitor { } return c; } - } diff --git a/jadx-core/src/test/java/jadx/tests/integration/rename/TestAnonymousInline.java b/jadx-core/src/test/java/jadx/tests/integration/rename/TestAnonymousInline.java new file mode 100644 index 000000000..712264884 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/rename/TestAnonymousInline.java @@ -0,0 +1,34 @@ +package jadx.tests.integration.rename; + +import org.junit.jupiter.api.Test; + +import jadx.core.dex.nodes.ClassNode; +import jadx.tests.api.IntegrationTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +public class TestAnonymousInline extends IntegrationTest { + + public static class TestCls { + public Runnable test() { + return new Runnable() { + @Override + public void run() { + System.out.println("run"); + } + }; + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + assertThat(cls.getCode()) + .containsOnlyOnce("return new Runnable() {"); + + assertThat(cls.reloadCode()) + .print() + .containsOnlyOnce("return new Runnable() {") + .doesNotContain("AnonymousClass1"); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/integration/rename/TestConstReplace.java b/jadx-core/src/test/java/jadx/tests/integration/rename/TestConstReplace.java new file mode 100644 index 000000000..378fc28c0 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/rename/TestConstReplace.java @@ -0,0 +1,30 @@ +package jadx.tests.integration.rename; + +import org.junit.jupiter.api.Test; + +import jadx.core.dex.nodes.ClassNode; +import jadx.tests.api.IntegrationTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +public class TestConstReplace extends IntegrationTest { + + public static class TestCls { + public static final String CONST = "SOME_CONST"; + + public String test() { + return CONST; + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + assertThat(cls.getCode()) + .containsOnlyOnce("return CONST;"); + + assertThat(cls.reloadCode()) + .print() + .containsOnlyOnce("return CONST;"); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/integration/rename/TestRenameEnum.java b/jadx-core/src/test/java/jadx/tests/integration/rename/TestRenameEnum.java new file mode 100644 index 000000000..2ae89b18a --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/rename/TestRenameEnum.java @@ -0,0 +1,44 @@ +package jadx.tests.integration.rename; + +import org.junit.jupiter.api.Test; + +import jadx.core.dex.nodes.ClassNode; +import jadx.tests.api.IntegrationTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +public class TestRenameEnum extends IntegrationTest { + + public static class TestCls { + + public enum A implements Runnable { + ONE { + @Override + public void run() { + System.out.println("ONE"); + } + }, + TWO { + @Override + public void run() { + System.out.println("TWO"); + } + }; + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + assertThat(cls.getCode()) + .containsOnlyOnce("public enum A ") + .containsOnlyOnce("ONE {"); + + cls.getInnerClasses().get(0).getClassInfo().changeShortName("ARenamed"); + + assertThat(cls.reloadCode()) + .print() + .containsOnlyOnce("public enum ARenamed ") + .containsOnlyOnce("ONE {"); + } +}