fix: restore android R class (#947) (regression)

This commit is contained in:
Skylot
2020-06-06 18:36:12 +01:00
parent 440357d2e8
commit 65553c156c
8 changed files with 85 additions and 59 deletions
@@ -49,6 +49,7 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
private final RootNode root;
private final int clsDefOffset;
@Nullable
private final Path inputPath;
private final ClassInfo clsInfo;
@@ -132,8 +133,16 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
this.accessFlags = new AccessInfo(accFlagsValue, AFType.CLASS);
}
// empty synthetic class
public ClassNode(RootNode root, String name, int accessFlags) {
public static ClassNode addSyntheticClass(RootNode root, String name, int accessFlags) {
ClassNode cls = new ClassNode(root, name, accessFlags);
cls.add(AFlag.SYNTHETIC);
cls.setState(ProcessState.PROCESS_COMPLETE);
root.addClassNode(cls);
return cls;
}
// Create empty class
private ClassNode(RootNode root, String name, int accessFlags) {
this.root = root;
this.inputPath = null;
this.clsDefOffset = 0;
@@ -106,9 +106,8 @@ public class RootNode {
if (name == null || name.isEmpty()) {
name = "CLASS_" + typeStr;
}
ClassNode clsNode = new ClassNode(this, name, classData.getAccessFlags());
ClassNode clsNode = ClassNode.addSyntheticClass(this, name, classData.getAccessFlags());
ErrorsCounter.error(clsNode, "Load error", exc);
addClassNode(clsNode);
}
public void addClassNode(ClassNode clsNode) {
@@ -6,7 +6,6 @@ import java.util.Map;
import java.util.TreeMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -40,7 +39,6 @@ public class AndroidResourcesUtils {
private AndroidResourcesUtils() {
}
@Nullable
public static ClassNode searchAppResClass(RootNode root, ResourceStorage resStorage) {
String appPackage = root.getAppPackage();
String fullName = appPackage != null ? appPackage + ".R" : "R";
@@ -59,14 +57,10 @@ public class AndroidResourcesUtils {
if (!candidates.isEmpty()) {
LOG.info("Found several 'R' class candidates: {}", candidates);
}
LOG.info("App 'R' class not found, put all resources ids to : '{}'", fullName);
resCls = makeClass(root, fullName, resStorage);
if (resCls == null) {
// We are in an APK without code therefore we don't have to update an 'R' class with the resources
return null;
}
addResourceFields(resCls, resStorage, false);
return resCls;
LOG.info("App 'R' class not found, put all resources ids into : '{}'", fullName);
ClassNode newResCls = makeClass(root, fullName, resStorage);
addResourceFields(newResCls, resStorage, false);
return newResCls;
}
public static boolean handleAppResField(CodeWriter code, ClassGen clsGen, ClassInfo declClass) {
@@ -80,9 +74,8 @@ public class AndroidResourcesUtils {
return false;
}
@Nullable
private static ClassNode makeClass(RootNode root, String clsName, ResourceStorage resStorage) {
ClassNode rCls = new ClassNode(root, clsName, AccessFlags.PUBLIC | AccessFlags.FINAL);
ClassNode rCls = ClassNode.addSyntheticClass(root, clsName, AccessFlags.PUBLIC | AccessFlags.FINAL);
rCls.addAttr(AType.COMMENTS, "This class is generated by JADX");
rCls.setState(ProcessState.PROCESS_COMPLETE);
return rCls;
@@ -131,11 +124,11 @@ public class AndroidResourcesUtils {
@NotNull
private static ClassNode addClassForResType(ClassNode resCls, boolean rClsExists, String typeName) {
ClassNode newTypeCls = new ClassNode(resCls.root(), resCls.getFullName() + '$' + typeName,
ClassNode newTypeCls = ClassNode.addSyntheticClass(resCls.root(), resCls.getFullName() + '$' + typeName,
AccessFlags.PUBLIC | AccessFlags.STATIC | AccessFlags.FINAL);
resCls.addInnerClass(newTypeCls);
if (rClsExists) {
newTypeCls.addAttr(AType.COMMENTS, "added by JADX");
newTypeCls.addAttr(AType.COMMENTS, "Added by JADX");
}
return newTypeCls;
}
@@ -1,4 +1,4 @@
package jadx.tests.integration.inner;
package jadx.tests.integration.android;
import org.junit.jupiter.api.Test;
@@ -0,0 +1,63 @@
package jadx.tests.integration.android;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Test;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.FieldInitAttr;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.JadxMatchers.containsOne;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
public class TestRFieldRestore extends IntegrationTest {
public static class TestCls {
public int test() {
return 2131230730;
}
}
@Test
public void test() {
// unknown R class
disableCompilation();
Map<Integer, String> map = new HashMap<>();
int buttonConstValue = 2131230730;
map.put(buttonConstValue, "id.Button");
setResMap(map);
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, containsOne("return R.id.Button;"));
assertThat(code, not(containsString("import R;")));
// check 'R' class
ClassNode rCls = cls.root().searchClassByFullAlias("R");
assertThat(rCls, notNullValue());
// check inner 'id' class
List<ClassNode> innerClasses = rCls.getInnerClasses();
assertThat(innerClasses, hasSize(1));
ClassNode idCls = innerClasses.get(0);
assertThat(idCls.getShortName(), is("id"));
// check 'Button' field
FieldNode buttonField = idCls.searchFieldByName("Button");
assertThat(buttonField, notNullValue());
FieldInitAttr fieldInitAttr = buttonField.get(AType.FIELD_INIT);
Integer buttonValue = (Integer) fieldInitAttr.getEncodedValue().getValue();
assertThat(buttonValue, is(buttonConstValue));
}
}
@@ -1,4 +1,4 @@
package jadx.tests.integration.inner;
package jadx.tests.integration.android;
import java.util.HashMap;
import java.util.Map;
@@ -1,4 +1,4 @@
package jadx.tests.integration.inner;
package jadx.tests.integration.android;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -1,38 +0,0 @@
package jadx.tests.integration.inner;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.Test;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.JadxMatchers.containsOne;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
public class TestRFieldRestore extends IntegrationTest {
public static class TestCls {
public int test() {
return 2131230730;
}
}
@Test
public void test() {
// unknown R class
disableCompilation();
Map<Integer, String> map = new HashMap<>();
map.put(2131230730, "id.Button");
setResMap(map);
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, containsOne("return R.id.Button;"));
assertThat(code, not(containsString("import R;")));
}
}