Skip to content

Commit

Permalink
fix: deep reload for inner classes, const values and anonymous classes
Browse files Browse the repository at this point in the history
  • Loading branch information
skylot committed Jan 5, 2020
1 parent 0c4b807 commit 9dbffef
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 18 deletions.
10 changes: 7 additions & 3 deletions jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,13 @@ private static ArgType checkClassType(ArgType type) {
}

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) {
Expand Down
19 changes: 19 additions & 0 deletions jadx-core/src/main/java/jadx/core/dex/info/ConstStorage.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

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;
Expand Down Expand Up @@ -52,6 +54,18 @@ public boolean put(Object value, FieldNode fld) {
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;
Expand Down Expand Up @@ -82,6 +96,11 @@ public void processConstFields(ClassNode cls, List<FieldNode> staticFields) {
}
}

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);
Expand Down
18 changes: 13 additions & 5 deletions jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
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;
Expand Down Expand Up @@ -175,9 +176,7 @@ private void loadAnnotations(ClassDef cls) {

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);
}
}
Expand Down Expand Up @@ -281,10 +280,19 @@ public ICodeInfo getCode() {

public synchronized ICodeInfo reloadCode() {
unload();
deepUnload();
return decompile(false);
}

private void deepUnload() {
clearAttributes();
root().getConstValues().removeForClass(this);
initialLoad();
load();
return decompile(false);
ProcessAnonymous.runForClass(this);

for (ClassNode innerClass : innerClasses) {
innerClass.deepUnload();
}
}

private synchronized ICodeInfo decompile(boolean searchInCache) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,29 @@
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);
Expand All @@ -35,9 +37,7 @@ private static boolean markAnonymousClass(ClassNode cls) {
mth.add(AFlag.ANONYMOUS_CONSTRUCTOR);
}
}
return true;
}
return false;
}

private static boolean isAnonymous(ClassNode cls) {
Expand All @@ -62,5 +62,4 @@ private static int countStaticFields(ClassNode cls) {
}
return c;
}

}
Original file line number Diff line number Diff line change
@@ -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");
}
}
Original file line number Diff line number Diff line change
@@ -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;");
}
}
Original file line number Diff line number Diff line change
@@ -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 {");
}
}

0 comments on commit 9dbffef

Please sign in to comment.