fix: restore android R class (#947) (regression)
This commit is contained in:
@@ -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
-1
@@ -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
-1
@@ -1,4 +1,4 @@
|
||||
package jadx.tests.integration.inner;
|
||||
package jadx.tests.integration.android;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
+1
-1
@@ -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;")));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user