fix: deep reload for inner classes, const values and anonymous classes
This commit is contained in:
@@ -66,9 +66,13 @@ public final class ClassInfo implements Comparable<ClassInfo> {
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -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<Entry<Object, FieldNode>> it = values.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
Entry<Object, FieldNode> 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);
|
||||
|
||||
@@ -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<FieldNode> 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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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;");
|
||||
}
|
||||
}
|
||||
@@ -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 {");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user