Commit 0ee499c5 authored by Skylot's avatar Skylot

Add generic types for classes and fields

parent 3b84aec5
...@@ -12,4 +12,6 @@ public class Consts { ...@@ -12,4 +12,6 @@ public class Consts {
public static final String CLASS_CLASS = "java.lang.Class"; public static final String CLASS_CLASS = "java.lang.Class";
public static final String CLASS_THROWABLE = "java.lang.Throwable"; public static final String CLASS_THROWABLE = "java.lang.Throwable";
public static final String CLASS_ENUM = "java.lang.Enum"; public static final String CLASS_ENUM = "java.lang.Enum";
public static final String DALVIK_SIGNATURE = "dalvik.annotation.Signature";
} }
...@@ -12,7 +12,6 @@ import jadx.dex.nodes.ClassNode; ...@@ -12,7 +12,6 @@ import jadx.dex.nodes.ClassNode;
import jadx.dex.nodes.FieldNode; import jadx.dex.nodes.FieldNode;
import jadx.dex.nodes.MethodNode; import jadx.dex.nodes.MethodNode;
import jadx.utils.StringUtils; import jadx.utils.StringUtils;
import jadx.utils.Utils;
import jadx.utils.exceptions.JadxRuntimeException; import jadx.utils.exceptions.JadxRuntimeException;
import java.util.Iterator; import java.util.Iterator;
...@@ -62,13 +61,7 @@ public class AnnotationGen { ...@@ -62,13 +61,7 @@ public class AnnotationGen {
String aCls = a.getAnnotationClass(); String aCls = a.getAnnotationClass();
if (aCls.startsWith("dalvik.annotation.")) { if (aCls.startsWith("dalvik.annotation.")) {
// skip // skip
if (aCls.equals("dalvik.annotation.Signature")) { if (Consts.DEBUG) {
if (!(node instanceof MethodNode)) {
String sign = Utils.mergeSignature((List<String>) a.getValues().get("value"));
List<ArgType> types = ArgType.parseSignatureList(sign);
code.startLine("// signature: " + Utils.listToString(types));
}
} else if (Consts.DEBUG) {
code.startLine("// " + a); code.startLine("// " + a);
} }
} else { } else {
...@@ -104,13 +97,9 @@ public class AnnotationGen { ...@@ -104,13 +97,9 @@ public class AnnotationGen {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void addThrows(MethodNode mth, CodeWriter code) { public void addThrows(MethodNode mth, CodeWriter code) {
AnnotationsList anList = (AnnotationsList) mth.getAttributes().get(AttributeType.ANNOTATION_LIST); Annotation an = mth.getAttributes().getAnnotation("dalvik.annotation.Throws");
if (anList == null || anList.size() == 0)
return;
Annotation an = anList.get("dalvik.annotation.Throws");
if (an != null) { if (an != null) {
Object exs = an.getValues().get("value"); Object exs = an.getDefaultValue();
code.add(" throws "); code.add(" throws ");
for (Iterator<ArgType> it = ((List<ArgType>) exs).iterator(); it.hasNext();) { for (Iterator<ArgType> it = ((List<ArgType>) exs).iterator(); it.hasNext();) {
ArgType ex = it.next(); ArgType ex = it.next();
...@@ -122,13 +111,9 @@ public class AnnotationGen { ...@@ -122,13 +111,9 @@ public class AnnotationGen {
} }
public Object getAnnotationDefaultValue(String name) { public Object getAnnotationDefaultValue(String name) {
AnnotationsList anList = (AnnotationsList) cls.getAttributes().get(AttributeType.ANNOTATION_LIST); Annotation an = cls.getAttributes().getAnnotation("dalvik.annotation.AnnotationDefault");
if (anList == null || anList.size() == 0)
return null;
Annotation an = anList.get("dalvik.annotation.AnnotationDefault");
if (an != null) { if (an != null) {
Annotation defAnnotation = (Annotation) an.getValues().get("value"); Annotation defAnnotation = (Annotation) an.getDefaultValue();
return defAnnotation.getValues().get(name); return defAnnotation.getValues().get(name);
} }
return null; return null;
......
...@@ -22,6 +22,8 @@ import java.util.Collections; ...@@ -22,6 +22,8 @@ import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import com.android.dx.rop.code.AccessFlags; import com.android.dx.rop.code.AccessFlags;
...@@ -105,19 +107,22 @@ public class ClassGen { ...@@ -105,19 +107,22 @@ public class ClassGen {
clsCode.add("class "); clsCode.add("class ");
} }
clsCode.add(cls.getShortName()); clsCode.add(cls.getShortName());
ClassInfo sup = cls.getSuperClass();
makeGenericMap(clsCode, cls.getGenericMap());
clsCode.add(' ');
ClassInfo sup = cls.getSuperClass();
if (sup != null if (sup != null
&& !sup.getFullName().equals(Consts.CLASS_OBJECT) && !sup.getFullName().equals(Consts.CLASS_OBJECT)
&& !sup.getFullName().equals(Consts.CLASS_ENUM)) { && !sup.getFullName().equals(Consts.CLASS_ENUM)) {
clsCode.add(" extends ").add(useClass(sup)); clsCode.add("extends ").add(useClass(sup)).add(' ');
} }
if (cls.getInterfaces().size() > 0 && !af.isAnnotation()) { if (cls.getInterfaces().size() > 0 && !af.isAnnotation()) {
if (cls.getAccessFlags().isInterface()) if (cls.getAccessFlags().isInterface())
clsCode.add(" extends "); clsCode.add("extends ");
else else
clsCode.add(" implements "); clsCode.add("implements ");
for (Iterator<ClassInfo> it = cls.getInterfaces().iterator(); it.hasNext();) { for (Iterator<ClassInfo> it = cls.getInterfaces().iterator(); it.hasNext();) {
ClassInfo interf = it.next(); ClassInfo interf = it.next();
...@@ -125,11 +130,41 @@ public class ClassGen { ...@@ -125,11 +130,41 @@ public class ClassGen {
if (it.hasNext()) if (it.hasNext())
clsCode.add(", "); clsCode.add(", ");
} }
if (!cls.getInterfaces().isEmpty())
clsCode.add(' ');
}
}
public void makeGenericMap(CodeWriter code, Map<ArgType, List<ArgType>> gmap) {
if (gmap == null || gmap.isEmpty())
return;
code.add('<');
int i = 0;
for (Entry<ArgType, List<ArgType>> e : gmap.entrySet()) {
ArgType type = e.getKey();
List<ArgType> list = e.getValue();
if (i != 0) {
code.add(", ");
}
code.add(useClass(type));
if (list != null && !list.isEmpty()) {
code.add(" extends ");
for (Iterator<ArgType> it = list.iterator(); it.hasNext();) {
ArgType g = it.next();
code.add(useClass(g));
if (it.hasNext()) {
code.add(" & ");
}
}
}
i++;
} }
code.add('>');
} }
public void makeClassBody(CodeWriter clsCode) throws CodegenException { public void makeClassBody(CodeWriter clsCode) throws CodegenException {
clsCode.add(" {"); clsCode.add("{");
CodeWriter mthsCode = makeMethods(clsCode, cls.getMethods()); CodeWriter mthsCode = makeMethods(clsCode, cls.getMethods());
clsCode.add(makeFields(clsCode, cls, cls.getFields())); clsCode.add(makeFields(clsCode, cls, cls.getFields()));
...@@ -248,9 +283,15 @@ public class ClassGen { ...@@ -248,9 +283,15 @@ public class ClassGen {
} }
public String useClass(ArgType clsType) { public String useClass(ArgType clsType) {
String baseClass = useClass(ClassInfo.fromType(cls.dex(), clsType)); if (clsType.isGenericType()) {
return clsType.getObject();
}
return useClass(ClassInfo.fromType(cls.dex(), clsType));
}
ArgType[] generics = clsType.getGenericTypes(); public String useClass(ClassInfo classInfo) {
String baseClass = useClassInner(classInfo);
ArgType[] generics = classInfo.getType().getGenericTypes();
if (generics != null) { if (generics != null) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(baseClass); sb.append(baseClass);
...@@ -262,7 +303,7 @@ public class ClassGen { ...@@ -262,7 +303,7 @@ public class ClassGen {
} }
ArgType gt = generics[i]; ArgType gt = generics[i];
if (gt.isTypeKnown()) if (gt.isTypeKnown())
sb.append(useClass(gt)); sb.append(TypeGen.translate(this, gt));
else else
sb.append('?'); sb.append('?');
} }
...@@ -273,9 +314,9 @@ public class ClassGen { ...@@ -273,9 +314,9 @@ public class ClassGen {
} }
} }
public String useClass(ClassInfo classInfo) { private String useClassInner(ClassInfo classInfo) {
if (parentGen != null) if (parentGen != null)
return parentGen.useClass(classInfo); return parentGen.useClassInner(classInfo);
String clsStr = classInfo.getFullName(); String clsStr = classInfo.getFullName();
if (fallback) if (fallback)
......
...@@ -76,8 +76,11 @@ public class MethodGen { ...@@ -76,8 +76,11 @@ public class MethodGen {
if (mth.getParentClass().getAccessFlags().isInterface()) { if (mth.getParentClass().getAccessFlags().isInterface()) {
ai = ai.remove(AccessFlags.ACC_ABSTRACT); ai = ai.remove(AccessFlags.ACC_ABSTRACT);
} }
code.startLine(ai.makeString()); code.startLine(ai.makeString());
classGen.makeGenericMap(code, mth.getGenericMap());
code.add(' ');
if (mth.getAccessFlags().isConstructor()) { if (mth.getAccessFlags().isConstructor()) {
code.add(classGen.getClassNode().getShortName()); // constructor code.add(classGen.getClassNode().getShortName()); // constructor
} else { } else {
......
package jadx.dex.attributes; package jadx.dex.attributes;
import jadx.dex.attributes.annotations.Annotation;
import jadx.dex.attributes.annotations.AnnotationsList;
import jadx.utils.Utils; import jadx.utils.Utils;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -90,6 +92,14 @@ public class AttributesList { ...@@ -90,6 +92,14 @@ public class AttributesList {
} }
} }
public Annotation getAnnotation(String cls) {
AnnotationsList aList = (AnnotationsList) get(AttributeType.ANNOTATION_LIST);
if (aList == null || aList.size() == 0)
return null;
return aList.get(cls);
}
public List<IAttribute> getAll(AttributeType type) { public List<IAttribute> getAll(AttributeType type) {
assert type.notUniq(); assert type.notUniq();
......
...@@ -36,6 +36,10 @@ public class Annotation { ...@@ -36,6 +36,10 @@ public class Annotation {
return values; return values;
} }
public Object getDefaultValue() {
return values.get("value");
}
@Override @Override
public String toString() { public String toString() {
return "Annotation[" + visibility + ", " + atype + ", " + values + "]"; return "Annotation[" + visibility + ", " + atype + ", " + values + "]";
......
...@@ -49,6 +49,7 @@ public final class ClassInfo { ...@@ -49,6 +49,7 @@ public final class ClassInfo {
} }
private ClassInfo(DexNode dex, ArgType type) { private ClassInfo(DexNode dex, ArgType type) {
assert type.isObject() : "Not class type: " + type;
this.type = type; this.type = type;
String fullObjectName = type.getObject(); String fullObjectName = type.getObject();
......
package jadx.dex.info; package jadx.dex.info;
import jadx.dex.attributes.AttrNode;
import jadx.dex.instructions.args.ArgType; import jadx.dex.instructions.args.ArgType;
import jadx.dex.nodes.DexNode; import jadx.dex.nodes.DexNode;
import com.android.dx.io.FieldId; import com.android.dx.io.FieldId;
public class FieldInfo extends AttrNode { public class FieldInfo {
private final String name; private final String name;
private final ArgType type; private final ArgType type;
......
...@@ -5,9 +5,16 @@ import jadx.utils.Utils; ...@@ -5,9 +5,16 @@ import jadx.utils.Utils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class ArgType { public abstract class ArgType {
private static final Logger LOG = LoggerFactory.getLogger(ArgType.class);
public static final ArgType INT = primitive(PrimitiveType.INT); public static final ArgType INT = primitive(PrimitiveType.INT);
public static final ArgType BOOLEAN = primitive(PrimitiveType.BOOLEAN); public static final ArgType BOOLEAN = primitive(PrimitiveType.BOOLEAN);
public static final ArgType BYTE = primitive(PrimitiveType.BYTE); public static final ArgType BYTE = primitive(PrimitiveType.BYTE);
...@@ -43,6 +50,10 @@ public abstract class ArgType { ...@@ -43,6 +50,10 @@ public abstract class ArgType {
return new ObjectArg(obj); return new ObjectArg(obj);
} }
public static ArgType genericType(String type) {
return new GenericTypeArg(type);
}
public static ArgType generic(String sign) { public static ArgType generic(String sign) {
return parseSignature(sign); return parseSignature(sign);
} }
...@@ -119,6 +130,17 @@ public abstract class ArgType { ...@@ -119,6 +130,17 @@ public abstract class ArgType {
} }
} }
private static final class GenericTypeArg extends ObjectArg {
public GenericTypeArg(String obj) {
super(obj);
}
@Override
public boolean isGenericType() {
return true;
}
}
private static final class GenericObjectArg extends ObjectArg { private static final class GenericObjectArg extends ObjectArg {
private final ArgType[] generics; private final ArgType[] generics;
...@@ -249,6 +271,10 @@ public abstract class ArgType { ...@@ -249,6 +271,10 @@ public abstract class ArgType {
return false; return false;
} }
public boolean isGenericType() {
return false;
}
public ArgType[] getGenericTypes() { public ArgType[] getGenericTypes() {
return null; return null;
} }
...@@ -355,14 +381,16 @@ public abstract class ArgType { ...@@ -355,14 +381,16 @@ public abstract class ArgType {
} }
public static ArgType parse(String type) { public static ArgType parse(String type) {
assert type.length() > 0 : "Empty type";
char f = type.charAt(0); char f = type.charAt(0);
if (f == 'L') { switch (f) {
return object(type); case 'L':
} else if (f == '[') { return object(type);
return array(parse(type.substring(1))); case 'T':
} else { return genericType(type.substring(1, type.length() - 1));
return parse(f); case '[':
return array(parse(type.substring(1)));
default:
return parse(f);
} }
} }
...@@ -374,11 +402,22 @@ public abstract class ArgType { ...@@ -374,11 +402,22 @@ public abstract class ArgType {
String obj = sign.substring(0, b); String obj = sign.substring(0, b);
String genericsStr = sign.substring(b + 1, sign.length() - 2); String genericsStr = sign.substring(b + 1, sign.length() - 2);
List<ArgType> generics = parseSignatureList(genericsStr); List<ArgType> generics = parseSignatureList(genericsStr);
ArgType res = generic(obj + ";", generics.toArray(new ArgType[generics.size()])); if (generics != null)
return res; return generic(obj + ";", generics.toArray(new ArgType[generics.size()]));
else
return object(obj + ";");
} }
public static List<ArgType> parseSignatureList(String str) { public static List<ArgType> parseSignatureList(String str) {
try {
return parseSignatureListInner(str, true);
} catch (Throwable e) {
LOG.warn("Signature parse exception: {}", str, e);
return null;
}
}
private static List<ArgType> parseSignatureListInner(String str, boolean parsePrimitives) {
List<ArgType> signs = new ArrayList<ArgType>(3); List<ArgType> signs = new ArrayList<ArgType>(3);
if (str.equals("*")) { if (str.equals("*")) {
signs.add(UNKNOWN); signs.add(UNKNOWN);
...@@ -396,6 +435,7 @@ public abstract class ArgType { ...@@ -396,6 +435,7 @@ public abstract class ArgType {
char c = str.charAt(pos); char c = str.charAt(pos);
switch (c) { switch (c) {
case 'L': case 'L':
case 'T':
if (obj == 0 && gen == 0) { if (obj == 0 && gen == 0) {
obj++; obj++;
objStart = pos; objStart = pos;
...@@ -410,6 +450,15 @@ public abstract class ArgType { ...@@ -410,6 +450,15 @@ public abstract class ArgType {
} }
break; break;
case ':': // generic types map separator
if (gen == 0) {
obj = 0;
String o = str.substring(objStart, pos);
if (o.length() > 0)
type = genericType(o);
}
break;
case '<': case '<':
gen++; gen++;
break; break;
...@@ -418,11 +467,13 @@ public abstract class ArgType { ...@@ -418,11 +467,13 @@ public abstract class ArgType {
break; break;
case '[': case '[':
arr++; if (obj == 0 && gen == 0) {
arr++;
}
break; break;
default: default:
if (obj == 0 && gen == 0) { if (parsePrimitives && obj == 0 && gen == 0) {
type = parse(c); type = parse(c);
} }
break; break;
...@@ -439,12 +490,45 @@ public abstract class ArgType { ...@@ -439,12 +490,45 @@ public abstract class ArgType {
arr = 0; arr = 0;
} }
type = null; type = null;
objStart = pos + 1;
} }
pos++; pos++;
} }
return signs; return signs;
} }
public static Map<ArgType, List<ArgType>> parseGenericMap(String gen) {
try {
Map<ArgType, List<ArgType>> genericMap = null;
List<ArgType> genTypes = parseSignatureListInner(gen, false);
if (genTypes != null) {
genericMap = new LinkedHashMap<ArgType, List<ArgType>>(2);
ArgType prev = null;
List<ArgType> genList = new ArrayList<ArgType>(2);
for (ArgType arg : genTypes) {
if (arg.isGenericType()) {
if (prev != null) {
genericMap.put(prev, genList);
genList = new ArrayList<ArgType>();
}
prev = arg;
} else {
if (!arg.getObject().equals(Consts.CLASS_OBJECT))
genList.add(arg);
}
}
if (prev != null) {
genericMap.put(prev, genList);
}
// LOG.debug("sign: {} -> {}", gen, genericMap);
}
return genericMap;
} catch (Throwable e) {
LOG.warn("Generic map parse exception: {}", gen, e);
return null;
}
}
private static ArgType parse(char f) { private static ArgType parse(char f) {
switch (f) { switch (f) {
case 'Z': case 'Z':
......
package jadx.dex.nodes; package jadx.dex.nodes;
import jadx.Consts;
import jadx.dex.attributes.AttrNode; import jadx.dex.attributes.AttrNode;
import jadx.dex.attributes.AttributeType; import jadx.dex.attributes.AttributeType;
import jadx.dex.attributes.IAttribute;
import jadx.dex.attributes.annotations.Annotation; import jadx.dex.attributes.annotations.Annotation;
import jadx.dex.attributes.annotations.AnnotationsList;
import jadx.dex.info.AccessInfo; import jadx.dex.info.AccessInfo;
import jadx.dex.info.AccessInfo.AFType; import jadx.dex.info.AccessInfo.AFType;
import jadx.dex.info.ClassInfo; import jadx.dex.info.ClassInfo;
...@@ -14,6 +13,7 @@ import jadx.dex.instructions.args.ArgType; ...@@ -14,6 +13,7 @@ import jadx.dex.instructions.args.ArgType;
import jadx.dex.nodes.parser.AnnotationsParser; import jadx.dex.nodes.parser.AnnotationsParser;
import jadx.dex.nodes.parser.FieldValueAttr; import jadx.dex.nodes.parser.FieldValueAttr;
import jadx.dex.nodes.parser.StaticValuesParser; import jadx.dex.nodes.parser.StaticValuesParser;
import jadx.utils.Utils;
import jadx.utils.exceptions.DecodeException; import jadx.utils.exceptions.DecodeException;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -31,13 +31,13 @@ import com.android.dx.io.ClassData.Method; ...@@ -31,13 +31,13 @@ import com.android.dx.io.ClassData.Method;
import com.android.dx.io.ClassDef; import com.android.dx.io.ClassDef;
public class ClassNode extends AttrNode implements ILoadable { public class ClassNode extends AttrNode implements ILoadable {
private static final Logger LOG = LoggerFactory.getLogger(ClassNode.class); private static final Logger LOG = LoggerFactory.getLogger(ClassNode.class);
private final DexNode dex; private final DexNode dex;
private final ClassInfo clsInfo; private final ClassInfo clsInfo;
private final ClassInfo superClass; private ClassInfo superClass;
private final List<ClassInfo> interfaces; private List<ClassInfo> interfaces;
private Map<ArgType, List<ArgType>> genericMap;
private final List<MethodNode> methods = new ArrayList<MethodNode>(); private final List<MethodNode> methods = new ArrayList<MethodNode>();
private final List<FieldNode> fields = new ArrayList<FieldNode>(); private final List<FieldNode> fields = new ArrayList<FieldNode>();
...@@ -82,15 +82,15 @@ public class ClassNode extends AttrNode implements ILoadable { ...@@ -82,15 +82,15 @@ public class ClassNode extends AttrNode implements ILoadable {
loadAnnotations(cls); loadAnnotations(cls);
int accFlagsValue = cls.getAccessFlags(); parseClassSignature();
setFieldsTypesFromSignature();
IAttribute annotations = getAttributes().get(AttributeType.ANNOTATION_LIST); int accFlagsValue;
if (annotations != null) { Annotation a = getAttributes().getAnnotation("dalvik.annotation.InnerClass");
AnnotationsList list = (AnnotationsList) annotations; if (a != null)
Annotation iCls = list.get("dalvik.annotation.InnerClass"); accFlagsValue = (Integer) a.getValues().get("accessFlags");
if (iCls != null) else
accFlagsValue = (Integer) iCls.getValues().get("accessFlags"); accFlagsValue = cls.getAccessFlags();
}
this.accessFlags = new AccessInfo(accFlagsValue, AFType.CLASS); this.accessFlags = new AccessInfo(accFlagsValue, AFType.CLASS);
...@@ -134,6 +134,53 @@ public class ClassNode extends AttrNode implements ILoadable { ...@@ -134,6 +134,53 @@ public class ClassNode extends AttrNode implements ILoadable {
} }
} }
@SuppressWarnings("unchecked")
private void parseClassSignature() {
Annotation a = this.getAttributes().getAnnotation(Consts.DALVIK_SIGNATURE);
if (a == null)
return;
String sign = Utils.mergeSignature((List<String>) a.getDefaultValue());
// parse generic map
int end = Utils.getGenericEnd(sign);
if (end != -1) {
String gen = sign.substring(1, end);
genericMap = ArgType.parseGenericMap(gen);
sign = sign.substring(end + 1);
}
// parse super class signature and interfaces
List<ArgType> list = ArgType.parseSignatureList(sign);
if (list != null && !list.isEmpty()) {
try {
ArgType st = list.remove(0);
this.superClass = ClassInfo.fromType(dex, st);
int i = 0;
for (ArgType it : list) {
ClassInfo interf = ClassInfo.fromType(dex, it);
interfaces.set(i, interf);
i++;
}
} catch (Throwable e) {
LOG.warn("Can't set signatures for class: {}, sign: {}", this, sign, e);
}
}
}
@SuppressWarnings("unchecked")
private void setFieldsTypesFromSignature() {
for (FieldNode field : fields) {
Annotation a = field.getAttributes().getAnnotation(Consts.DALVIK_SIGNATURE);
if (a == null)
continue;
String sign = Utils.mergeSignature((List<String>) a.getDefaultValue());
ArgType gType = ArgType.parseSignature(sign);
if (gType != null)
field.setType(gType);
}
}
@Override @Override
public void load() throws DecodeException { public void load() throws DecodeException {
for (MethodNode mth : getMethods()) { for (MethodNode mth : getMethods()) {
...@@ -162,6 +209,10 @@ public class ClassNode extends AttrNode implements ILoadable { ...@@ -162,6 +209,10 @@ public class ClassNode extends AttrNode implements ILoadable {
return interfaces; return interfaces;
} }
public Map<ArgType, List<ArgType>> getGenericMap() {
return genericMap;
}
public List<MethodNode> getMethods() { public List<MethodNode> getMethods() {
return methods; return methods;
} }
......
package jadx.dex.nodes; package jadx.dex.nodes;
import jadx.dex.attributes.AttrNode;
import jadx.dex.info.AccessInfo; import jadx.dex.info.AccessInfo;
import jadx.dex.info.AccessInfo.AFType; import jadx.dex.info.AccessInfo.AFType;
import jadx.dex.info.ClassInfo;
import jadx.dex.info.FieldInfo; import jadx.dex.info.FieldInfo;
import jadx.dex.instructions.args.ArgType;
import com.android.dx.io.ClassData.Field; import com.android.dx.io.ClassData.Field;
public class FieldNode extends FieldInfo { public class FieldNode extends AttrNode {
private final AccessInfo accFlags; private final AccessInfo accFlags;
private final String name;
private final ClassInfo declClass;
private ArgType type;
public FieldNode(ClassNode cls, Field field) { public FieldNode(ClassNode cls, Field field) {
super(cls.dex(), field.getFieldIndex()); FieldInfo f = FieldInfo.fromDex(cls.dex(), field.getFieldIndex());
this.name = f.getName();
this.type = f.getType();
this.declClass = f.getDeclClass();
this.accFlags = new AccessInfo(field.getAccessFlags(), AFType.FIELD); this.accFlags = new AccessInfo(field.getAccessFlags(), AFType.FIELD);
} }
...@@ -19,4 +29,24 @@ public class FieldNode extends FieldInfo { ...@@ -19,4 +29,24 @@ public class FieldNode extends FieldInfo {
return accFlags; return accFlags;
} }
public String getName() {
return name;
}
public ArgType getType() {
return type;
}
public void setType(ArgType type) {
this.type = type;
}
public ClassInfo getDeclClass() {
return declClass;
}
@Override
public String toString() {
return declClass + "." + name + " " + type;
}
} }
package jadx.dex.nodes; package jadx.dex.nodes;
import jadx.Consts;
import jadx.dex.attributes.AttrNode; import jadx.dex.attributes.AttrNode;
import jadx.dex.attributes.AttributeFlag; import jadx.dex.attributes.AttributeFlag;
import jadx.dex.attributes.AttributeType;
import jadx.dex.attributes.JumpAttribute; import jadx.dex.attributes.JumpAttribute;
import jadx.dex.attributes.annotations.Annotation; import jadx.dex.attributes.annotations.Annotation;
import jadx.dex.attributes.annotations.AnnotationsList;
import jadx.dex.info.AccessInfo; import jadx.dex.info.AccessInfo;
import jadx.dex.info.AccessInfo.AFType; import jadx.dex.info.AccessInfo.AFType;
import jadx.dex.info.ClassInfo; import jadx.dex.info.ClassInfo;
...@@ -29,6 +28,7 @@ import java.util.ArrayList; ...@@ -29,6 +28,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import org.slf4j.Logger; import org.slf4j.Logger;
...@@ -54,6 +54,7 @@ public class MethodNode extends AttrNode implements ILoadable { ...@@ -54,6 +54,7 @@ public class MethodNode extends AttrNode implements ILoadable {
private ArgType retType; private ArgType retType;
private RegisterArg thisArg; private RegisterArg thisArg;
private List<RegisterArg> argsList; private List<RegisterArg> argsList;
private Map<ArgType, List<ArgType>> genericMap;
private List<BlockNode> blocks; private List<BlockNode> blocks;
private BlockNode enterBlock; private BlockNode enterBlock;
...@@ -131,35 +132,52 @@ public class MethodNode extends AttrNode implements ILoadable { ...@@ -131,35 +132,52 @@ public class MethodNode extends AttrNode implements ILoadable {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private boolean parseSignature() { private boolean parseSignature() {
AnnotationsList aList = (AnnotationsList) getAttributes().get(AttributeType.ANNOTATION_LIST); Annotation a = getAttributes().getAnnotation(Consts.DALVIK_SIGNATURE);
if (aList == null || aList.size() == 0)
return false;
Annotation a = aList.get("dalvik.annotation.Signature");
if (a == null) if (a == null)
return false; return false;
String sign = Utils.mergeSignature((List<String>) a.getValues().get("value")); String sign = Utils.mergeSignature((List<String>) a.getDefaultValue());
int lastBracket = sign.indexOf(')');
String argsTypesStr = sign.substring(1, lastBracket); // parse generic map
int end = Utils.getGenericEnd(sign);
if (end != -1) {
String gen = sign.substring(1, end);
genericMap = ArgType.parseGenericMap(gen);
sign = sign.substring(end + 1);
}
int firstBracket = sign.indexOf('(');
int lastBracket = sign.lastIndexOf(')');
String argsTypesStr = sign.substring(firstBracket + 1, lastBracket);
String returnType = sign.substring(lastBracket + 1); String returnType = sign.substring(lastBracket + 1);
retType = ArgType.parseSignature(returnType); retType = ArgType.parseSignature(returnType);
if (retType == null) {
LOG.warn("Signature parse error: {}", returnType);
return false;
}
if (mthInfo.getArgumentsTypes().isEmpty()) { if (mthInfo.getArgumentsTypes().isEmpty()) {
argsList = Collections.emptyList(); argsList = Collections.emptyList();
return true; return true;
} }
List<ArgType> argsTypes = ArgType.parseSignatureList(argsTypesStr); List<ArgType> argsTypes = ArgType.parseSignatureList(argsTypesStr);
if (argsTypes == null)
return false;
if (argsTypes.size() != mthInfo.getArgumentsTypes().size()) { if (argsTypes.size() != mthInfo.getArgumentsTypes().size()) {
if (!getParentClass().getAccessFlags().isEnum() && !mthInfo.isConstructor()) { if (!mthInfo.isConstructor()) {
// error parsing signature LOG.warn("Wrong signature parse result: " + sign + " -> " + argsTypes
LOG.error("Wrong parse result: " + sign + " -> " + argsTypes + ", not generic version: " + mthInfo.getArgumentsTypes());
+ " must be: " + mthInfo.getArgumentsTypes() return false;
// + " in method " + this } else if (getParentClass().getAccessFlags().isEnum()) {
); // TODO:
argsTypes.add(0, mthInfo.getArgumentsTypes().get(1));
argsTypes.add(1, mthInfo.getArgumentsTypes().get(1));
} else {
// add synthetic arg for outer class
argsTypes.add(0, mthInfo.getArgumentsTypes().get(0));
} }
return false;
} }
initArguments(argsTypes); initArguments(argsTypes);
return true; return true;
...@@ -213,7 +231,11 @@ public class MethodNode extends AttrNode implements ILoadable { ...@@ -213,7 +231,11 @@ public class MethodNode extends AttrNode implements ILoadable {
return retType; return retType;
} }
// move to external class public Map<ArgType, List<ArgType>> getGenericMap() {
return genericMap;
}
// TODO: move to external class
private void initTryCatches(Code mthCode, InsnNode[] insnByOffset) { private void initTryCatches(Code mthCode, InsnNode[] insnByOffset) {
CatchHandler[] catchBlocks = mthCode.getCatchHandlers(); CatchHandler[] catchBlocks = mthCode.getCatchHandlers();
Try[] tries = mthCode.getTries(); Try[] tries = mthCode.getTries();
......
package jadx.dex.visitors; package jadx.dex.visitors;
import jadx.dex.info.AccessInfo; import jadx.dex.info.AccessInfo;
import jadx.dex.info.MethodInfo;
import jadx.dex.nodes.BlockNode; import jadx.dex.nodes.BlockNode;
import jadx.dex.nodes.ClassNode; import jadx.dex.nodes.ClassNode;
import jadx.dex.nodes.MethodNode; import jadx.dex.nodes.MethodNode;
...@@ -23,7 +24,7 @@ public class ClassModifier extends AbstractVisitor { ...@@ -23,7 +24,7 @@ public class ClassModifier extends AbstractVisitor {
// remove bridge methods // remove bridge methods
if (af.isBridge() && af.isSynthetic()) { if (af.isBridge() && af.isSynthetic()) {
if (!isMethodIdUniq(cls, mth)) { if (!isMethodUniq(cls, mth)) {
// TODO add more checks before method deletion // TODO add more checks before method deletion
it.remove(); it.remove();
} }
...@@ -42,12 +43,17 @@ public class ClassModifier extends AbstractVisitor { ...@@ -42,12 +43,17 @@ public class ClassModifier extends AbstractVisitor {
return false; return false;
} }
private boolean isMethodIdUniq(ClassNode cls, MethodNode mth) { private boolean isMethodUniq(ClassNode cls, MethodNode mth) {
String shortId = mth.getMethodInfo().getShortId(); MethodInfo mi = mth.getMethodInfo();
for (MethodNode otherMth : cls.getMethods()) { for (MethodNode otherMth : cls.getMethods()) {
if (otherMth.getMethodInfo().getShortId().equals(shortId) MethodInfo omi = otherMth.getMethodInfo();
&& otherMth != mth) if (omi.getName().equals(mi.getName())
return false; && otherMth != mth) {
if (omi.getArgumentsTypes().size() == mi.getArgumentsTypes().size()) {
// TODO: check to args objects types
return false;
}
}
} }
return true; return true;
} }
......
...@@ -28,13 +28,35 @@ public class Utils { ...@@ -28,13 +28,35 @@ public class Utils {
} }
public static String escape(String str) { public static String escape(String str) {
return str.replace('.', '_') int len = str.length();
.replace('/', '_') StringBuilder sb = new StringBuilder(len);
.replace(';', '_') for (int i = 0; i < len; i++) {
.replace('$', '_') char c = str.charAt(i);
.replace('<', '_') switch (c) {
.replace('>', '_') case '.':
.replace("[]", "_A"); case '/':
case ';':
case '$':
case '<':
case '[':
sb.append('_');
break;
case ']':
sb.append('A');
break;
case '>':
case ',':
case ' ':
break;
default:
sb.append(c);
break;
}
}
return sb.toString();
} }
public static String listToString(Iterable<?> list) { public static String listToString(Iterable<?> list) {
...@@ -44,7 +66,7 @@ public class Utils { ...@@ -44,7 +66,7 @@ public class Utils {
StringBuilder str = new StringBuilder(); StringBuilder str = new StringBuilder();
for (Iterator<?> it = list.iterator(); it.hasNext();) { for (Iterator<?> it = list.iterator(); it.hasNext();) {
Object o = it.next(); Object o = it.next();
str.append(o.toString()); str.append(o);
if (it.hasNext()) if (it.hasNext())
str.append(", "); str.append(", ");
} }
...@@ -52,6 +74,9 @@ public class Utils { ...@@ -52,6 +74,9 @@ public class Utils {
} }
public static String arrayToString(Object[] array) { public static String arrayToString(Object[] array) {
if (array == null)
return "";
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (int i = 0; i < array.length; i++) { for (int i = 0; i < array.length; i++) {
if (i != 0) if (i != 0)
...@@ -76,6 +101,26 @@ public class Utils { ...@@ -76,6 +101,26 @@ public class Utils {
return sb.toString(); return sb.toString();
} }
public static int getGenericEnd(String sign) {
int end = -1;
if (sign.startsWith("<")) {
int pair = 1;
for (int pos = 1; pos < sign.length(); pos++) {
char c = sign.charAt(pos);
if (c == '<')
pair++;
else if (c == '>')
pair--;
if (pair == 0) {
end = pos;
break;
}
}
}
return end;
}
public static String getJadxVersion() { public static String getJadxVersion() {
try { try {
Enumeration<URL> resources = Enumeration<URL> resources =
......
...@@ -7,6 +7,134 @@ import java.util.Map; ...@@ -7,6 +7,134 @@ import java.util.Map;
public class TestGenerics extends AbstractTest { public class TestGenerics extends AbstractTest {
public List<String> strings;
public static class GenericClass implements Comparable<String> {
@Override
public int compareTo(String o) {
return 0;
}
}
public static class Box<T> {
private T t;
public void set(T t) {
this.t = t;
}
public T get() {
return t;
}
}
public static Box<Integer> integerBox = new Box<Integer>();
public interface Pair<K, LongGenericType> {
public K getKey();
public LongGenericType getValue();
}
public static class OrderedPair<K, V> implements Pair<K, V> {
private final K key;
private final V value;
public OrderedPair(K key, V value) {
this.key = key;
this.value = value;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
}
Pair<String, Integer> p1 = new OrderedPair<String, Integer>("8", 8);
OrderedPair<String, Box<Integer>> p = new OrderedPair<String, Box<Integer>>("primes", new Box<Integer>());
public static class Util {
// Generic static method
public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
return p1.getKey().equals(p2.getKey()) &&
p1.getValue().equals(p2.getValue());
}
}
public static boolean use() {
Pair<Integer, String> p1 = new OrderedPair<Integer, String>(1, "str1");
Pair<Integer, String> p2 = new OrderedPair<Integer, String>(2, "str2");
boolean same = Util.<Integer, String> compare(p1, p2);
return same;
}
public class NaturalNumber<T extends Integer> {
private final T n;
public NaturalNumber(T n) {
this.n = n;
}
public boolean isEven() {
return n.intValue() % 2 == 0;
}
}
class A {
}
interface B {
}
interface C {
}
class D<T extends A & B & C> {
}
public static <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) {
int count = 0;
for (T e : anArray)
if (e.compareTo(elem) > 0)
++count;
return count;
}
public static void process(List<? extends A> list) {
}
public static void printList(List<?> list) {
for (Object elem : list)
System.out.print(elem + " ");
System.out.println();
}
public static void addNumbers(List<? super Integer> list) {
for (int i = 1; i <= 10; i++) {
list.add(i);
}
}
public class Node<T extends Comparable<T>> {
private final T data;
private final Node<T> next;
public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
public T getData() {
return data;
}
}
private List<String> test1(Map<String, String> map) { private List<String> test1(Map<String, String> map) {
List<String> list = new ArrayList<String>(); List<String> list = new ArrayList<String>();
String str = map.get("key"); String str = map.get("key");
...@@ -26,6 +154,7 @@ public class TestGenerics extends AbstractTest { ...@@ -26,6 +154,7 @@ public class TestGenerics extends AbstractTest {
@Override @Override
public boolean testRun() throws Exception { public boolean testRun() throws Exception {
assertTrue(test1(new HashMap<String, String>()) != null); assertTrue(test1(new HashMap<String, String>()) != null);
// TODO: add other checks
return true; return true;
} }
......
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