Commit f6f883b9 authored by Skylot's avatar Skylot

fix: change resource fields generations in R class (#308)

parent 5de4d079
...@@ -34,6 +34,7 @@ import jadx.core.dex.nodes.InsnNode; ...@@ -34,6 +34,7 @@ import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.parser.FieldInitAttr; import jadx.core.dex.nodes.parser.FieldInitAttr;
import jadx.core.dex.nodes.parser.FieldInitAttr.InitType; import jadx.core.dex.nodes.parser.FieldInitAttr.InitType;
import jadx.core.utils.CodegenUtils;
import jadx.core.utils.ErrorsCounter; import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.Utils; import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.CodegenException; import jadx.core.utils.exceptions.CodegenException;
...@@ -105,9 +106,8 @@ public class ClassGen { ...@@ -105,9 +106,8 @@ public class ClassGen {
if (cls.contains(AFlag.DONT_GENERATE)) { if (cls.contains(AFlag.DONT_GENERATE)) {
return; return;
} }
if (cls.contains(AFlag.INCONSISTENT_CODE)) { CodegenUtils.addComments(code, cls);
code.startLine("// jadx: inconsistent code"); insertDecompilationProblems(code, cls);
}
addClassDeclaration(code); addClassDeclaration(code);
addClassBody(code); addClassBody(code);
} }
...@@ -296,6 +296,7 @@ public class ClassGen { ...@@ -296,6 +296,7 @@ public class ClassGen {
} }
code.add(';'); code.add(';');
} else { } else {
CodegenUtils.addComments(code, mth);
insertDecompilationProblems(code, mth); insertDecompilationProblems(code, mth);
boolean badCode = mth.contains(AFlag.INCONSISTENT_CODE); boolean badCode = mth.contains(AFlag.INCONSISTENT_CODE);
if (badCode && showInconsistentCode) { if (badCode && showInconsistentCode) {
...@@ -325,9 +326,9 @@ public class ClassGen { ...@@ -325,9 +326,9 @@ public class ClassGen {
} }
} }
private void insertDecompilationProblems(CodeWriter code, MethodNode mth) { private void insertDecompilationProblems(CodeWriter code, AttrNode node) {
List<JadxError> errors = mth.getAll(AType.JADX_ERROR); List<JadxError> errors = node.getAll(AType.JADX_ERROR);
List<JadxWarn> warns = mth.getAll(AType.JADX_WARN); List<JadxWarn> warns = node.getAll(AType.JADX_WARN);
if (!errors.isEmpty()) { if (!errors.isEmpty()) {
errors.forEach(err -> { errors.forEach(err -> {
code.startLine("/* JADX ERROR: ").add(err.getError()); code.startLine("/* JADX ERROR: ").add(err.getError());
...@@ -351,6 +352,7 @@ public class ClassGen { ...@@ -351,6 +352,7 @@ public class ClassGen {
if (f.contains(AFlag.DONT_GENERATE)) { if (f.contains(AFlag.DONT_GENERATE)) {
continue; continue;
} }
CodegenUtils.addComments(code, f);
annotationGen.addForField(code, f); annotationGen.addForField(code, f);
if (f.getFieldInfo().isRenamed()) { if (f.getFieldInfo().isRenamed()) {
......
...@@ -36,6 +36,7 @@ public class AType<T extends IAttribute> { ...@@ -36,6 +36,7 @@ public class AType<T extends IAttribute> {
public static final AType<AttrList<JadxError>> JADX_ERROR = new AType<>(); public static final AType<AttrList<JadxError>> JADX_ERROR = new AType<>();
public static final AType<AttrList<JadxWarn>> JADX_WARN = new AType<>(); public static final AType<AttrList<JadxWarn>> JADX_WARN = new AType<>();
public static final AType<AttrList<String>> COMMENTS = new AType<>();
public static final AType<ExcHandlerAttr> EXC_HANDLER = new AType<>(); public static final AType<ExcHandlerAttr> EXC_HANDLER = new AType<>();
public static final AType<CatchAttr> CATCH_BLOCK = new AType<>(); public static final AType<CatchAttr> CATCH_BLOCK = new AType<>();
......
...@@ -15,8 +15,8 @@ import jadx.core.dex.instructions.args.PrimitiveType; ...@@ -15,8 +15,8 @@ import jadx.core.dex.instructions.args.PrimitiveType;
import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.DexNode; import jadx.core.dex.nodes.DexNode;
import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.ResRefField;
import jadx.core.dex.nodes.parser.FieldInitAttr; import jadx.core.dex.nodes.parser.FieldInitAttr;
import jadx.core.utils.ErrorsCounter;
public class ConstStorage { public class ConstStorage {
...@@ -101,16 +101,16 @@ public class ConstStorage { ...@@ -101,16 +101,16 @@ public class ConstStorage {
@Nullable @Nullable
public FieldNode getConstField(ClassNode cls, Object value, boolean searchGlobal) { public FieldNode getConstField(ClassNode cls, Object value, boolean searchGlobal) {
if (!replaceEnabled) {
return null;
}
DexNode dex = cls.dex(); DexNode dex = cls.dex();
if (value instanceof Integer) { if (value instanceof Integer) {
String str = resourcesNames.get(value); FieldNode rField = getResourceField((Integer) value, dex);
if (str != null) { if (rField != null) {
return new ResRefField(dex, str.replace('/', '.')); return rField;
} }
} }
if (!replaceEnabled) {
return null;
}
boolean foundInGlobal = globalValues.contains(value); boolean foundInGlobal = globalValues.contains(value);
if (foundInGlobal && !searchGlobal) { if (foundInGlobal && !searchGlobal) {
return null; return null;
...@@ -140,6 +140,31 @@ public class ConstStorage { ...@@ -140,6 +140,31 @@ public class ConstStorage {
} }
@Nullable @Nullable
private FieldNode getResourceField(Integer value, DexNode dex) {
String str = resourcesNames.get(value);
if (str == null) {
return null;
}
ClassNode appResClass = dex.root().getAppResClass();
if (appResClass == null) {
return null;
}
String[] parts = str.split("/", 2);
if (parts.length != 2) {
return null;
}
String typeName = parts[0];
String fieldName = parts[1];
for (ClassNode innerClass : appResClass.getInnerClasses()) {
if (innerClass.getShortName().equals(typeName)) {
return innerClass.searchFieldByName(fieldName);
}
}
ErrorsCounter.classWarn(appResClass, "Not found resource field with id: " + value + ", name: " + str.replace('/', '.'));
return null;
}
@Nullable
public FieldNode getConstFieldByLiteralArg(ClassNode cls, LiteralArg arg) { public FieldNode getConstFieldByLiteralArg(ClassNode cls, LiteralArg arg) {
PrimitiveType type = arg.getType().getPrimitiveType(); PrimitiveType type = arg.getType().getPrimitiveType();
if (type == null) { if (type == null) {
......
...@@ -13,7 +13,6 @@ import com.android.dex.ClassData.Field; ...@@ -13,7 +13,6 @@ import com.android.dex.ClassData.Field;
import com.android.dex.ClassData.Method; import com.android.dex.ClassData.Method;
import com.android.dex.ClassDef; import com.android.dex.ClassDef;
import com.android.dex.Dex; import com.android.dex.Dex;
import com.android.dx.rop.code.AccessFlags;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -51,7 +50,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode { ...@@ -51,7 +50,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
private final List<MethodNode> methods; private final List<MethodNode> methods;
private final List<FieldNode> fields; private final List<FieldNode> fields;
private List<ClassNode> innerClasses = Collections.emptyList(); private List<ClassNode> innerClasses = new ArrayList<>();
// store decompiled code // store decompiled code
private CodeWriter code; private CodeWriter code;
...@@ -132,14 +131,16 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode { ...@@ -132,14 +131,16 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
} }
// empty synthetic class // empty synthetic class
public ClassNode(DexNode dex, ClassInfo clsInfo) { public ClassNode(DexNode dex, String name, int accessFlags) {
this.dex = dex; this.dex = dex;
this.clsInfo = clsInfo; this.clsInfo = ClassInfo.fromName(dex.root(), name);
this.interfaces = Collections.emptyList(); this.interfaces = new ArrayList<>();
this.methods = Collections.emptyList(); this.methods = new ArrayList<>();
this.fields = Collections.emptyList(); this.fields = new ArrayList<>();
this.accessFlags = new AccessInfo(AccessFlags.ACC_PUBLIC | AccessFlags.ACC_SYNTHETIC, AFType.CLASS); this.accessFlags = new AccessInfo(accessFlags, AFType.CLASS);
this.parentClass = this; this.parentClass = this;
dex.addClassNode(this);
} }
private void loadAnnotations(ClassDef cls) { private void loadAnnotations(ClassDef cls) {
...@@ -368,10 +369,8 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode { ...@@ -368,10 +369,8 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
} }
public void addInnerClass(ClassNode cls) { public void addInnerClass(ClassNode cls) {
if (innerClasses.isEmpty()) {
innerClasses = new ArrayList<>(3);
}
innerClasses.add(cls); innerClasses.add(cls);
cls.parentClass = this;
} }
public boolean isEnum() { public boolean isEnum() {
......
...@@ -46,12 +46,15 @@ public class DexNode implements IDexNode { ...@@ -46,12 +46,15 @@ public class DexNode implements IDexNode {
public void loadClasses() { public void loadClasses() {
for (ClassDef cls : dexBuf.classDefs()) { for (ClassDef cls : dexBuf.classDefs()) {
ClassNode clsNode = new ClassNode(this, cls); addClassNode(new ClassNode(this, cls));
classes.add(clsNode);
clsMap.put(clsNode.getClassInfo(), clsNode);
} }
} }
public void addClassNode(ClassNode clsNode) {
classes.add(clsNode);
clsMap.put(clsNode.getClassInfo(), clsNode);
}
void initInnerClasses() { void initInnerClasses() {
// move inner classes // move inner classes
List<ClassNode> inner = new ArrayList<>(); List<ClassNode> inner = new ArrayList<>();
......
package jadx.core.dex.nodes;
import com.android.dx.rop.code.AccessFlags;
import jadx.core.dex.info.FieldInfo;
import jadx.core.dex.instructions.args.ArgType;
public class ResRefField extends FieldNode {
public ResRefField(DexNode dex, String str) {
super(dex.root().getAppResClass(),
FieldInfo.from(dex, dex.root().getAppResClass().getClassInfo(), str, ArgType.INT),
AccessFlags.ACC_PUBLIC);
}
}
...@@ -37,11 +37,12 @@ public class RootNode { ...@@ -37,11 +37,12 @@ public class RootNode {
private final ConstStorage constValues; private final ConstStorage constValues;
private final InfoStorage infoStorage = new InfoStorage(); private final InfoStorage infoStorage = new InfoStorage();
private ClspGraph clsp;
private List<DexNode> dexNodes; private List<DexNode> dexNodes;
@Nullable @Nullable
private String appPackage; private String appPackage;
@Nullable
private ClassNode appResClass; private ClassNode appResClass;
private ClspGraph clsp;
public RootNode(JadxArgs args) { public RootNode(JadxArgs args) {
this.args = args; this.args = args;
...@@ -90,8 +91,10 @@ public class RootNode { ...@@ -90,8 +91,10 @@ public class RootNode {
LOG.error("Failed to parse '.arsc' file", e); LOG.error("Failed to parse '.arsc' file", e);
return; return;
} }
processResources(parser.getResStorage());
}
ResourceStorage resStorage = parser.getResStorage(); public void processResources(ResourceStorage resStorage) {
constValues.setResourcesNames(resStorage.getResourcesNames()); constValues.setResourcesNames(resStorage.getResourcesNames());
appPackage = resStorage.getAppPackage(); appPackage = resStorage.getAppPackage();
appResClass = AndroidResourcesUtils.searchAppResClass(this, resStorage); appResClass = AndroidResourcesUtils.searchAppResClass(this, resStorage);
......
...@@ -52,7 +52,7 @@ public class EnumVisitor extends AbstractVisitor { ...@@ -52,7 +52,7 @@ public class EnumVisitor extends AbstractVisitor {
} }
} }
if (staticMethod == null) { if (staticMethod == null) {
ErrorsCounter.classError(cls, "Enum class init method not found"); ErrorsCounter.classWarn(cls, "Enum class init method not found");
return true; return true;
} }
......
package jadx.core.utils;
import jadx.core.codegen.CodeWriter;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.AttrNode;
public class CodegenUtils {
public static void addComments(CodeWriter code, AttrNode node) {
for (String comment : node.getAll(AType.COMMENTS)) {
code.startLine("/* ").add(comment).add(" */");
}
}
}
...@@ -74,8 +74,8 @@ public class ErrorsCounter { ...@@ -74,8 +74,8 @@ public class ErrorsCounter {
return cls.dex().root().getErrorsCounter().addError(cls, errorMsg, e); return cls.dex().root().getErrorsCounter().addError(cls, errorMsg, e);
} }
public static String classError(ClassNode cls, String errorMsg) { public static String classWarn(ClassNode cls, String warnMsg) {
return classError(cls, errorMsg, null); return cls.dex().root().getErrorsCounter().addWarning(cls, warnMsg);
} }
public static String methodError(MethodNode mth, String errorMsg, Throwable e) { public static String methodError(MethodNode mth, String errorMsg, Throwable e) {
......
package jadx.core.utils.android; package jadx.core.utils.android;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.TreeMap;
import com.android.dx.rop.code.AccessFlags;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import jadx.core.codegen.ClassGen; import jadx.core.codegen.ClassGen;
import jadx.core.codegen.CodeWriter; import jadx.core.codegen.CodeWriter;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.info.ClassInfo; import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.info.FieldInfo;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.DexNode; import jadx.core.dex.nodes.DexNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.ProcessState;
import jadx.core.dex.nodes.RootNode; import jadx.core.dex.nodes.RootNode;
import jadx.core.dex.nodes.parser.FieldInitAttr;
import jadx.core.xmlgen.ResourceStorage; import jadx.core.xmlgen.ResourceStorage;
import jadx.core.xmlgen.entry.ResourceEntry; import jadx.core.xmlgen.entry.ResourceEntry;
...@@ -28,22 +33,29 @@ public class AndroidResourcesUtils { ...@@ -28,22 +33,29 @@ public class AndroidResourcesUtils {
private AndroidResourcesUtils() { private AndroidResourcesUtils() {
} }
@Nullable
public static ClassNode searchAppResClass(RootNode root, ResourceStorage resStorage) { public static ClassNode searchAppResClass(RootNode root, ResourceStorage resStorage) {
String appPackage = root.getAppPackage(); String appPackage = root.getAppPackage();
String fullName = appPackage != null ? appPackage + ".R" : "R"; String fullName = appPackage != null ? appPackage + ".R" : "R";
ClassNode resCls = root.searchClassByName(fullName); ClassNode resCls = root.searchClassByName(fullName);
if (resCls != null) { if (resCls != null) {
addResourceFields(resCls, resStorage, true);
return resCls; return resCls;
} }
LOG.info("Can't find 'R' class in app package: {}", appPackage);
List<ClassNode> candidates = root.searchClassByShortName("R"); List<ClassNode> candidates = root.searchClassByShortName("R");
if (candidates.size() == 1) { if (candidates.size() == 1) {
return candidates.get(0); resCls = candidates.get(0);
addResourceFields(resCls, resStorage, true);
return resCls;
} }
if (!candidates.isEmpty()) { if (!candidates.isEmpty()) {
LOG.info("Found several 'R' class candidates: {}", candidates); LOG.info("Found several 'R' class candidates: {}", candidates);
} }
LOG.warn("Unknown 'R' class, create references to '{}'", fullName); LOG.info("App 'R' class not found, put all resources ids to : '{}'", fullName);
return makeClass(root, fullName, resStorage); resCls = makeClass(root, fullName, resStorage);
addResourceFields(resCls, resStorage, false);
return resCls;
} }
public static boolean handleAppResField(CodeWriter code, ClassGen clsGen, ClassInfo declClass) { public static boolean handleAppResField(CodeWriter code, ClassGen clsGen, ClassInfo declClass) {
...@@ -57,48 +69,45 @@ public class AndroidResourcesUtils { ...@@ -57,48 +69,45 @@ public class AndroidResourcesUtils {
return false; return false;
} }
@Nullable
private static ClassNode makeClass(RootNode root, String clsName, ResourceStorage resStorage) { private static ClassNode makeClass(RootNode root, String clsName, ResourceStorage resStorage) {
List<DexNode> dexNodes = root.getDexNodes(); List<DexNode> dexNodes = root.getDexNodes();
if (dexNodes.isEmpty()) { if (dexNodes.isEmpty()) {
return null; return null;
} }
ClassInfo r = ClassInfo.fromName(root, clsName); ClassNode rCls = new ClassNode(dexNodes.get(0), clsName, AccessFlags.ACC_PUBLIC | AccessFlags.ACC_FINAL);
ClassNode classNode = new ClassNode(dexNodes.get(0), r); rCls.addAttr(AType.COMMENTS, "This class is generated by JADX");
generateMissingRCode(classNode, resStorage); rCls.setState(ProcessState.PROCESSED);
return classNode; return rCls;
} }
private static void generateMissingRCode(ClassNode cls, ResourceStorage resStorage) { private static void addResourceFields(ClassNode cls, ResourceStorage resStorage, boolean rClsExists) {
Map<String, List<ResourceEntry>> sortedMap = new HashMap<>(); Map<String, ClassNode> innerClsMap = new TreeMap<>();
for(ResourceEntry ri : resStorage.getResources()) { if (rClsExists) {
List<ResourceEntry> entries = sortedMap.get(ri.getTypeName()); for (ClassNode innerClass : cls.getInnerClasses()) {
if(entries == null) { innerClsMap.put(innerClass.getShortName(), innerClass);
entries = new LinkedList<>();
sortedMap.put(ri.getTypeName(), entries);
} }
entries.add(ri);
} }
for (ResourceEntry resource : resStorage.getResources()) {
Set<String> addedValues = new HashSet<>(); ClassNode typeCls = innerClsMap.computeIfAbsent(resource.getTypeName(), name -> {
CodeWriter clsCode = new CodeWriter(); ClassNode newTypeCls = new ClassNode(cls.dex(), cls.getFullName() + "$" + name,
if (!"".equals(cls.getPackage())) { AccessFlags.ACC_PUBLIC | AccessFlags.ACC_STATIC | AccessFlags.ACC_FINAL);
clsCode.add("package ").add(cls.getPackage()).add(';').newLine(); cls.addInnerClass(newTypeCls);
} if (rClsExists) {
clsCode.startLine("public final class ").add(cls.getShortName()).add(" {").incIndent(); newTypeCls.addAttr(AType.COMMENTS, "added by JADX");
for(String typeName : sortedMap.keySet()) { }
clsCode.startLine("public static final class ").add(typeName).add(" {").incIndent(); return newTypeCls;
for(ResourceEntry ri : sortedMap.get(typeName)) { });
if(addedValues.add(ri.getTypeName() + "." + ri.getKeyName())) { FieldNode rField = typeCls.searchFieldByName(resource.getKeyName());
clsCode.startLine("public static final int ").add(ri.getKeyName()).add(" = ") if (rField == null) {
.add("" + ri.getId()).add(";"); FieldInfo rFieldInfo = FieldInfo.from(typeCls.dex(), typeCls.getClassInfo(), resource.getKeyName(), ArgType.INT);
rField = new FieldNode(typeCls, rFieldInfo, AccessFlags.ACC_PUBLIC | AccessFlags.ACC_STATIC | AccessFlags.ACC_FINAL);
rField.addAttr(FieldInitAttr.constValue(resource.getId()));
typeCls.getFields().add(rField);
if (rClsExists) {
rField.addAttr(AType.COMMENTS, "added by JADX");
} }
} }
clsCode.decIndent();
clsCode.add("}");
} }
clsCode.decIndent();
clsCode.add("}");
cls.setCode(clsCode);
} }
} }
...@@ -27,7 +27,8 @@ import jadx.core.dex.nodes.MethodNode; ...@@ -27,7 +27,8 @@ import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode; import jadx.core.dex.nodes.RootNode;
import jadx.core.dex.visitors.DepthTraversal; import jadx.core.dex.visitors.DepthTraversal;
import jadx.core.dex.visitors.IDexTreeVisitor; import jadx.core.dex.visitors.IDexTreeVisitor;
import jadx.core.utils.exceptions.CodegenException; import jadx.core.xmlgen.ResourceStorage;
import jadx.core.xmlgen.entry.ResourceEntry;
import jadx.tests.api.compiler.DynamicCompiler; import jadx.tests.api.compiler.DynamicCompiler;
import jadx.tests.api.compiler.StaticCompiler; import jadx.tests.api.compiler.StaticCompiler;
import jadx.tests.api.utils.TestUtils; import jadx.tests.api.utils.TestUtils;
...@@ -99,7 +100,7 @@ public abstract class IntegrationTest extends TestUtils { ...@@ -99,7 +100,7 @@ public abstract class IntegrationTest extends TestUtils {
fail(e.getMessage()); fail(e.getMessage());
} }
RootNode root = JadxInternalAccess.getRoot(d); RootNode root = JadxInternalAccess.getRoot(d);
root.getConstValues().getResourcesNames().putAll(resMap); insertResources(root);
ClassNode cls = root.searchClassByName(clsName); ClassNode cls = root.searchClassByName(clsName);
assertThat("Class not found: " + clsName, cls, notNullValue()); assertThat("Class not found: " + clsName, cls, notNullValue());
...@@ -121,6 +122,20 @@ public abstract class IntegrationTest extends TestUtils { ...@@ -121,6 +122,20 @@ public abstract class IntegrationTest extends TestUtils {
return cls; return cls;
} }
private void insertResources(RootNode root) {
if (resMap.isEmpty()) {
return;
}
ResourceStorage resStorage = new ResourceStorage();
for (Map.Entry<Integer, String> entry : resMap.entrySet()) {
Integer id = entry.getKey();
String name = entry.getValue();
String[] parts = name.split("\\.");
resStorage.add(new ResourceEntry(id, "", parts[0], parts[1]));
}
root.processResources(resStorage);
}
protected void decompile(JadxDecompiler jadx, ClassNode cls) { protected void decompile(JadxDecompiler jadx, ClassNode cls) {
List<IDexTreeVisitor> passes = Jadx.getPassesList(jadx.getArgs()); List<IDexTreeVisitor> passes = Jadx.getPassesList(jadx.getArgs());
ProcessClass.process(cls, passes, new CodeGen()); ProcessClass.process(cls, passes, new CodeGen());
......
...@@ -25,15 +25,12 @@ public class TestRFieldRestore2 extends IntegrationTest { ...@@ -25,15 +25,12 @@ public class TestRFieldRestore2 extends IntegrationTest {
@Test @Test
public void test() { public void test() {
// unknown id.Button
disableCompilation();
Map<Integer, String> map = new HashMap<>(); Map<Integer, String> map = new HashMap<>();
map.put(2131230730, "id.Button"); map.put(2131230730, "id.Button");
setResMap(map); setResMap(map);
ClassNode cls = getClassNode(TestCls.class); ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString(); String code = cls.getCode().toString();
assertThat(code, containsOne("return R.id.Button;")); assertThat(code, containsOne("R.id.Button;"));
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment