fix: check if inner classes for missing R class already exist (#1269)

This commit is contained in:
Skylot
2021-11-15 16:17:38 +00:00
parent 985ccd6bba
commit 4ee4a34323
3 changed files with 76 additions and 37 deletions
@@ -7,6 +7,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.exceptions.JadxRuntimeException;
@@ -249,6 +250,11 @@ public final class ClassInfo implements Comparable<ClassInfo> {
splitAndApplyNames(root, type, false);
}
public void convertToInner(ClassNode parent) {
this.parentClass = parent.getClassInfo();
splitAndApplyNames(parent.root(), type, true);
}
public void updateNames(RootNode root) {
splitAndApplyNames(root, type, isInner());
}
@@ -179,7 +179,16 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
}
public static ClassNode addSyntheticClass(RootNode root, String name, int accessFlags) {
ClassNode cls = new ClassNode(root, name, accessFlags);
ClassInfo clsInfo = ClassInfo.fromName(root, name);
ClassNode existCls = root.resolveClass(clsInfo);
if (existCls != null) {
throw new JadxRuntimeException("Class already exist: " + name);
}
return addSyntheticClass(root, clsInfo, accessFlags);
}
public static ClassNode addSyntheticClass(RootNode root, ClassInfo clsInfo, int accessFlags) {
ClassNode cls = new ClassNode(root, clsInfo, accessFlags);
cls.add(AFlag.SYNTHETIC);
cls.setState(ProcessState.PROCESS_COMPLETE);
root.addClassNode(cls);
@@ -187,10 +196,10 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
}
// Create empty class
private ClassNode(RootNode root, String name, int accessFlags) {
private ClassNode(RootNode root, ClassInfo clsInfo, int accessFlags) {
this.root = root;
this.clsData = null;
this.clsInfo = ClassInfo.fromName(root, name);
this.clsInfo = clsInfo;
this.interfaces = new ArrayList<>();
this.methods = new ArrayList<>();
this.fields = new ArrayList<>();
@@ -23,7 +23,6 @@ import jadx.core.dex.info.FieldInfo;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.ProcessState;
import jadx.core.dex.nodes.RootNode;
import jadx.core.xmlgen.ResourceStorage;
import jadx.core.xmlgen.entry.ResourceEntry;
@@ -40,7 +39,8 @@ public class AndroidResourcesUtils {
public static ClassNode searchAppResClass(RootNode root, ResourceStorage resStorage) {
String appPackage = root.getAppPackage();
String fullName = appPackage != null ? appPackage + ".R" : "R";
ClassNode resCls = root.resolveClass(fullName);
ClassInfo clsInfo = ClassInfo.fromName(root, fullName);
ClassNode resCls = root.resolveClass(clsInfo);
if (resCls != null) {
addResourceFields(resCls, resStorage, true);
return resCls;
@@ -48,17 +48,18 @@ public class AndroidResourcesUtils {
LOG.info("Can't find 'R' class in app package: {}", appPackage);
List<ClassNode> candidates = root.searchClassByShortName("R");
if (candidates.size() == 1) {
resCls = candidates.get(0);
addResourceFields(resCls, resStorage, true);
return resCls;
ClassNode resClsCandidate = candidates.get(0);
addResourceFields(resClsCandidate, resStorage, true);
return resClsCandidate;
}
if (!candidates.isEmpty()) {
LOG.info("Found several 'R' class candidates: {}", candidates);
}
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;
ClassNode rCls = ClassNode.addSyntheticClass(root, clsInfo, AccessFlags.PUBLIC | AccessFlags.FINAL);
rCls.addInfoComment("This class is generated by JADX");
addResourceFields(rCls, resStorage, false);
return rCls;
}
public static boolean handleAppResField(ICodeWriter code, ClassGen clsGen, ClassInfo declClass) {
@@ -88,42 +89,52 @@ public class AndroidResourcesUtils {
return parentClass != null && parentClass.getShortName().equals("R");
}
private static ClassNode makeClass(RootNode root, String clsName, ResourceStorage resStorage) {
ClassNode rCls = ClassNode.addSyntheticClass(root, clsName, AccessFlags.PUBLIC | AccessFlags.FINAL);
rCls.addInfoComment("This class is generated by JADX");
rCls.setState(ProcessState.PROCESS_COMPLETE);
return rCls;
private static final class ResClsInfo {
private final ClassNode typeCls;
private final Map<String, FieldNode> fieldsMap = new HashMap<>();
private ResClsInfo(ClassNode typeCls) {
this.typeCls = typeCls;
}
public ClassNode getTypeCls() {
return typeCls;
}
public Map<String, FieldNode> getFieldsMap() {
return fieldsMap;
}
}
private static void addResourceFields(ClassNode resCls, ResourceStorage resStorage, boolean rClsExists) {
Map<Integer, FieldNode> resFieldsMap = fillResFieldsMap(resCls);
Map<String, ClassNode> innerClsMap = new TreeMap<>();
Map<String, ResClsInfo> innerClsMap = new TreeMap<>();
if (rClsExists) {
for (ClassNode innerClass : resCls.getInnerClasses()) {
innerClsMap.put(innerClass.getShortName(), innerClass);
ResClsInfo innerResCls = new ResClsInfo(innerClass);
innerClass.getFields().forEach(field -> innerResCls.getFieldsMap().put(field.getName(), field));
innerClsMap.put(innerClass.getShortName(), innerResCls);
}
}
for (ResourceEntry resource : resStorage.getResources()) {
final String resTypeName = resource.getTypeName();
ClassNode typeCls = innerClsMap.computeIfAbsent(
String resTypeName = resource.getTypeName();
String resName = resTypeName.equals("style") ? resource.getKeyName().replace('.', '_') : resource.getKeyName();
ResClsInfo typeClsInfo = innerClsMap.computeIfAbsent(
resTypeName,
name -> addClassForResType(resCls, rClsExists, name));
final String resName;
if ("style".equals(resTypeName)) {
resName = resource.getKeyName().replace('.', '_');
} else {
resName = resource.getKeyName();
}
FieldNode rField = typeCls.searchFieldByName(resName);
if (rField == null) {
name -> getClassForResType(resCls, rClsExists, name));
typeClsInfo.getFieldsMap().computeIfAbsent(resName, name -> {
ClassNode typeCls = typeClsInfo.getTypeCls();
FieldInfo rFieldInfo = FieldInfo.from(typeCls.root(), typeCls.getClassInfo(), resName, ArgType.INT);
rField = new FieldNode(typeCls, rFieldInfo, AccessFlags.PUBLIC | AccessFlags.STATIC | AccessFlags.FINAL);
rField.addAttr(new EncodedValue(EncodedType.ENCODED_INT, resource.getId()));
typeCls.getFields().add(rField);
FieldNode newResField = new FieldNode(typeCls, rFieldInfo,
AccessFlags.PUBLIC | AccessFlags.STATIC | AccessFlags.FINAL);
newResField.addAttr(new EncodedValue(EncodedType.ENCODED_INT, resource.getId()));
typeCls.getFields().add(newResField);
if (rClsExists) {
rField.addInfoComment("Added by JADX");
newResField.addInfoComment("Added by JADX");
}
}
return newResField;
});
FieldNode fieldNode = resFieldsMap.get(resource.getId());
if (fieldNode != null
&& !fieldNode.getName().equals(resName)
@@ -136,14 +147,27 @@ public class AndroidResourcesUtils {
}
@NotNull
private static ClassNode addClassForResType(ClassNode resCls, boolean rClsExists, String typeName) {
ClassNode newTypeCls = ClassNode.addSyntheticClass(resCls.root(), resCls.getFullName() + '$' + typeName,
private static ResClsInfo getClassForResType(ClassNode resCls, boolean rClsExists, String typeName) {
String clsFullName = resCls.getFullName() + '$' + typeName;
ClassInfo clsInfo = ClassInfo.fromName(resCls.root(), clsFullName);
ClassNode existCls = resCls.root().resolveClass(clsInfo);
if (existCls != null) {
if (!rClsExists && !existCls.isInner()) {
// convert found res cls to inner for R class
existCls.getClassInfo().convertToInner(resCls);
resCls.addInnerClass(existCls);
}
ResClsInfo resClsInfo = new ResClsInfo(existCls);
existCls.getFields().forEach(field -> resClsInfo.getFieldsMap().put(field.getName(), field));
return resClsInfo;
}
ClassNode newTypeCls = ClassNode.addSyntheticClass(resCls.root(), clsInfo,
AccessFlags.PUBLIC | AccessFlags.STATIC | AccessFlags.FINAL);
resCls.addInnerClass(newTypeCls);
if (rClsExists) {
newTypeCls.addInfoComment("Added by JADX");
}
return newTypeCls;
return new ResClsInfo(newTypeCls);
}
@NotNull