Add generic types for classes and fields
This commit is contained in:
@@ -12,4 +12,6 @@ public class Consts {
|
||||
public static final String CLASS_CLASS = "java.lang.Class";
|
||||
public static final String CLASS_THROWABLE = "java.lang.Throwable";
|
||||
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;
|
||||
import jadx.dex.nodes.FieldNode;
|
||||
import jadx.dex.nodes.MethodNode;
|
||||
import jadx.utils.StringUtils;
|
||||
import jadx.utils.Utils;
|
||||
import jadx.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.util.Iterator;
|
||||
@@ -62,13 +61,7 @@ public class AnnotationGen {
|
||||
String aCls = a.getAnnotationClass();
|
||||
if (aCls.startsWith("dalvik.annotation.")) {
|
||||
// skip
|
||||
if (aCls.equals("dalvik.annotation.Signature")) {
|
||||
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) {
|
||||
if (Consts.DEBUG) {
|
||||
code.startLine("// " + a);
|
||||
}
|
||||
} else {
|
||||
@@ -104,13 +97,9 @@ public class AnnotationGen {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void addThrows(MethodNode mth, CodeWriter code) {
|
||||
AnnotationsList anList = (AnnotationsList) mth.getAttributes().get(AttributeType.ANNOTATION_LIST);
|
||||
if (anList == null || anList.size() == 0)
|
||||
return;
|
||||
|
||||
Annotation an = anList.get("dalvik.annotation.Throws");
|
||||
Annotation an = mth.getAttributes().getAnnotation("dalvik.annotation.Throws");
|
||||
if (an != null) {
|
||||
Object exs = an.getValues().get("value");
|
||||
Object exs = an.getDefaultValue();
|
||||
code.add(" throws ");
|
||||
for (Iterator<ArgType> it = ((List<ArgType>) exs).iterator(); it.hasNext();) {
|
||||
ArgType ex = it.next();
|
||||
@@ -122,13 +111,9 @@ public class AnnotationGen {
|
||||
}
|
||||
|
||||
public Object getAnnotationDefaultValue(String name) {
|
||||
AnnotationsList anList = (AnnotationsList) cls.getAttributes().get(AttributeType.ANNOTATION_LIST);
|
||||
if (anList == null || anList.size() == 0)
|
||||
return null;
|
||||
|
||||
Annotation an = anList.get("dalvik.annotation.AnnotationDefault");
|
||||
Annotation an = cls.getAttributes().getAnnotation("dalvik.annotation.AnnotationDefault");
|
||||
if (an != null) {
|
||||
Annotation defAnnotation = (Annotation) an.getValues().get("value");
|
||||
Annotation defAnnotation = (Annotation) an.getDefaultValue();
|
||||
return defAnnotation.getValues().get(name);
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -22,6 +22,8 @@ import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import com.android.dx.rop.code.AccessFlags;
|
||||
@@ -105,19 +107,22 @@ public class ClassGen {
|
||||
clsCode.add("class ");
|
||||
}
|
||||
clsCode.add(cls.getShortName());
|
||||
ClassInfo sup = cls.getSuperClass();
|
||||
|
||||
makeGenericMap(clsCode, cls.getGenericMap());
|
||||
clsCode.add(' ');
|
||||
|
||||
ClassInfo sup = cls.getSuperClass();
|
||||
if (sup != null
|
||||
&& !sup.getFullName().equals(Consts.CLASS_OBJECT)
|
||||
&& !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.getAccessFlags().isInterface())
|
||||
clsCode.add(" extends ");
|
||||
clsCode.add("extends ");
|
||||
else
|
||||
clsCode.add(" implements ");
|
||||
clsCode.add("implements ");
|
||||
|
||||
for (Iterator<ClassInfo> it = cls.getInterfaces().iterator(); it.hasNext();) {
|
||||
ClassInfo interf = it.next();
|
||||
@@ -125,11 +130,41 @@ public class ClassGen {
|
||||
if (it.hasNext())
|
||||
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 {
|
||||
clsCode.add(" {");
|
||||
clsCode.add("{");
|
||||
CodeWriter mthsCode = makeMethods(clsCode, cls.getMethods());
|
||||
clsCode.add(makeFields(clsCode, cls, cls.getFields()));
|
||||
|
||||
@@ -248,9 +283,15 @@ public class ClassGen {
|
||||
}
|
||||
|
||||
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) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(baseClass);
|
||||
@@ -262,7 +303,7 @@ public class ClassGen {
|
||||
}
|
||||
ArgType gt = generics[i];
|
||||
if (gt.isTypeKnown())
|
||||
sb.append(useClass(gt));
|
||||
sb.append(TypeGen.translate(this, gt));
|
||||
else
|
||||
sb.append('?');
|
||||
}
|
||||
@@ -273,9 +314,9 @@ public class ClassGen {
|
||||
}
|
||||
}
|
||||
|
||||
public String useClass(ClassInfo classInfo) {
|
||||
private String useClassInner(ClassInfo classInfo) {
|
||||
if (parentGen != null)
|
||||
return parentGen.useClass(classInfo);
|
||||
return parentGen.useClassInner(classInfo);
|
||||
|
||||
String clsStr = classInfo.getFullName();
|
||||
if (fallback)
|
||||
|
||||
@@ -76,8 +76,11 @@ public class MethodGen {
|
||||
if (mth.getParentClass().getAccessFlags().isInterface()) {
|
||||
ai = ai.remove(AccessFlags.ACC_ABSTRACT);
|
||||
}
|
||||
|
||||
code.startLine(ai.makeString());
|
||||
|
||||
classGen.makeGenericMap(code, mth.getGenericMap());
|
||||
code.add(' ');
|
||||
|
||||
if (mth.getAccessFlags().isConstructor()) {
|
||||
code.add(classGen.getClassNode().getShortName()); // constructor
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package jadx.dex.attributes;
|
||||
|
||||
import jadx.dex.attributes.annotations.Annotation;
|
||||
import jadx.dex.attributes.annotations.AnnotationsList;
|
||||
import jadx.utils.Utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -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) {
|
||||
assert type.notUniq();
|
||||
|
||||
|
||||
@@ -36,6 +36,10 @@ public class Annotation {
|
||||
return values;
|
||||
}
|
||||
|
||||
public Object getDefaultValue() {
|
||||
return values.get("value");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Annotation[" + visibility + ", " + atype + ", " + values + "]";
|
||||
|
||||
@@ -49,6 +49,7 @@ public final class ClassInfo {
|
||||
}
|
||||
|
||||
private ClassInfo(DexNode dex, ArgType type) {
|
||||
assert type.isObject() : "Not class type: " + type;
|
||||
this.type = type;
|
||||
|
||||
String fullObjectName = type.getObject();
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package jadx.dex.info;
|
||||
|
||||
import jadx.dex.attributes.AttrNode;
|
||||
import jadx.dex.instructions.args.ArgType;
|
||||
import jadx.dex.nodes.DexNode;
|
||||
|
||||
import com.android.dx.io.FieldId;
|
||||
|
||||
public class FieldInfo extends AttrNode {
|
||||
public class FieldInfo {
|
||||
|
||||
private final String name;
|
||||
private final ArgType type;
|
||||
|
||||
@@ -5,9 +5,16 @@ import jadx.utils.Utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
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 BOOLEAN = primitive(PrimitiveType.BOOLEAN);
|
||||
public static final ArgType BYTE = primitive(PrimitiveType.BYTE);
|
||||
@@ -43,6 +50,10 @@ public abstract class ArgType {
|
||||
return new ObjectArg(obj);
|
||||
}
|
||||
|
||||
public static ArgType genericType(String type) {
|
||||
return new GenericTypeArg(type);
|
||||
}
|
||||
|
||||
public static ArgType generic(String sign) {
|
||||
return parseSignature(sign);
|
||||
}
|
||||
@@ -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 final ArgType[] generics;
|
||||
|
||||
@@ -249,6 +271,10 @@ public abstract class ArgType {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isGenericType() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public ArgType[] getGenericTypes() {
|
||||
return null;
|
||||
}
|
||||
@@ -355,14 +381,16 @@ public abstract class ArgType {
|
||||
}
|
||||
|
||||
public static ArgType parse(String type) {
|
||||
assert type.length() > 0 : "Empty type";
|
||||
char f = type.charAt(0);
|
||||
if (f == 'L') {
|
||||
return object(type);
|
||||
} else if (f == '[') {
|
||||
return array(parse(type.substring(1)));
|
||||
} else {
|
||||
return parse(f);
|
||||
switch (f) {
|
||||
case 'L':
|
||||
return object(type);
|
||||
case 'T':
|
||||
return genericType(type.substring(1, type.length() - 1));
|
||||
case '[':
|
||||
return array(parse(type.substring(1)));
|
||||
default:
|
||||
return parse(f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,11 +402,22 @@ public abstract class ArgType {
|
||||
String obj = sign.substring(0, b);
|
||||
String genericsStr = sign.substring(b + 1, sign.length() - 2);
|
||||
List<ArgType> generics = parseSignatureList(genericsStr);
|
||||
ArgType res = generic(obj + ";", generics.toArray(new ArgType[generics.size()]));
|
||||
return res;
|
||||
if (generics != null)
|
||||
return generic(obj + ";", generics.toArray(new ArgType[generics.size()]));
|
||||
else
|
||||
return object(obj + ";");
|
||||
}
|
||||
|
||||
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);
|
||||
if (str.equals("*")) {
|
||||
signs.add(UNKNOWN);
|
||||
@@ -396,6 +435,7 @@ public abstract class ArgType {
|
||||
char c = str.charAt(pos);
|
||||
switch (c) {
|
||||
case 'L':
|
||||
case 'T':
|
||||
if (obj == 0 && gen == 0) {
|
||||
obj++;
|
||||
objStart = pos;
|
||||
@@ -410,6 +450,15 @@ public abstract class ArgType {
|
||||
}
|
||||
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 '<':
|
||||
gen++;
|
||||
break;
|
||||
@@ -418,11 +467,13 @@ public abstract class ArgType {
|
||||
break;
|
||||
|
||||
case '[':
|
||||
arr++;
|
||||
if (obj == 0 && gen == 0) {
|
||||
arr++;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (obj == 0 && gen == 0) {
|
||||
if (parsePrimitives && obj == 0 && gen == 0) {
|
||||
type = parse(c);
|
||||
}
|
||||
break;
|
||||
@@ -439,12 +490,45 @@ public abstract class ArgType {
|
||||
arr = 0;
|
||||
}
|
||||
type = null;
|
||||
objStart = pos + 1;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
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) {
|
||||
switch (f) {
|
||||
case 'Z':
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package jadx.dex.nodes;
|
||||
|
||||
import jadx.Consts;
|
||||
import jadx.dex.attributes.AttrNode;
|
||||
import jadx.dex.attributes.AttributeType;
|
||||
import jadx.dex.attributes.IAttribute;
|
||||
import jadx.dex.attributes.annotations.Annotation;
|
||||
import jadx.dex.attributes.annotations.AnnotationsList;
|
||||
import jadx.dex.info.AccessInfo;
|
||||
import jadx.dex.info.AccessInfo.AFType;
|
||||
import jadx.dex.info.ClassInfo;
|
||||
@@ -14,6 +13,7 @@ import jadx.dex.instructions.args.ArgType;
|
||||
import jadx.dex.nodes.parser.AnnotationsParser;
|
||||
import jadx.dex.nodes.parser.FieldValueAttr;
|
||||
import jadx.dex.nodes.parser.StaticValuesParser;
|
||||
import jadx.utils.Utils;
|
||||
import jadx.utils.exceptions.DecodeException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -31,13 +31,13 @@ import com.android.dx.io.ClassData.Method;
|
||||
import com.android.dx.io.ClassDef;
|
||||
|
||||
public class ClassNode extends AttrNode implements ILoadable {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ClassNode.class);
|
||||
|
||||
private final DexNode dex;
|
||||
private final ClassInfo clsInfo;
|
||||
private final ClassInfo superClass;
|
||||
private final List<ClassInfo> interfaces;
|
||||
private ClassInfo superClass;
|
||||
private List<ClassInfo> interfaces;
|
||||
private Map<ArgType, List<ArgType>> genericMap;
|
||||
|
||||
private final List<MethodNode> methods = new ArrayList<MethodNode>();
|
||||
private final List<FieldNode> fields = new ArrayList<FieldNode>();
|
||||
@@ -82,15 +82,15 @@ public class ClassNode extends AttrNode implements ILoadable {
|
||||
|
||||
loadAnnotations(cls);
|
||||
|
||||
int accFlagsValue = cls.getAccessFlags();
|
||||
parseClassSignature();
|
||||
setFieldsTypesFromSignature();
|
||||
|
||||
IAttribute annotations = getAttributes().get(AttributeType.ANNOTATION_LIST);
|
||||
if (annotations != null) {
|
||||
AnnotationsList list = (AnnotationsList) annotations;
|
||||
Annotation iCls = list.get("dalvik.annotation.InnerClass");
|
||||
if (iCls != null)
|
||||
accFlagsValue = (Integer) iCls.getValues().get("accessFlags");
|
||||
}
|
||||
int accFlagsValue;
|
||||
Annotation a = getAttributes().getAnnotation("dalvik.annotation.InnerClass");
|
||||
if (a != null)
|
||||
accFlagsValue = (Integer) a.getValues().get("accessFlags");
|
||||
else
|
||||
accFlagsValue = cls.getAccessFlags();
|
||||
|
||||
this.accessFlags = new AccessInfo(accFlagsValue, AFType.CLASS);
|
||||
|
||||
@@ -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
|
||||
public void load() throws DecodeException {
|
||||
for (MethodNode mth : getMethods()) {
|
||||
@@ -162,6 +209,10 @@ public class ClassNode extends AttrNode implements ILoadable {
|
||||
return interfaces;
|
||||
}
|
||||
|
||||
public Map<ArgType, List<ArgType>> getGenericMap() {
|
||||
return genericMap;
|
||||
}
|
||||
|
||||
public List<MethodNode> getMethods() {
|
||||
return methods;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
package jadx.dex.nodes;
|
||||
|
||||
import jadx.dex.attributes.AttrNode;
|
||||
import jadx.dex.info.AccessInfo;
|
||||
import jadx.dex.info.AccessInfo.AFType;
|
||||
import jadx.dex.info.ClassInfo;
|
||||
import jadx.dex.info.FieldInfo;
|
||||
import jadx.dex.instructions.args.ArgType;
|
||||
|
||||
import com.android.dx.io.ClassData.Field;
|
||||
|
||||
public class FieldNode extends FieldInfo {
|
||||
public class FieldNode extends AttrNode {
|
||||
|
||||
private final AccessInfo accFlags;
|
||||
private final String name;
|
||||
private final ClassInfo declClass;
|
||||
|
||||
private ArgType type;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -19,4 +29,24 @@ public class FieldNode extends FieldInfo {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package jadx.dex.nodes;
|
||||
|
||||
import jadx.Consts;
|
||||
import jadx.dex.attributes.AttrNode;
|
||||
import jadx.dex.attributes.AttributeFlag;
|
||||
import jadx.dex.attributes.AttributeType;
|
||||
import jadx.dex.attributes.JumpAttribute;
|
||||
import jadx.dex.attributes.annotations.Annotation;
|
||||
import jadx.dex.attributes.annotations.AnnotationsList;
|
||||
import jadx.dex.info.AccessInfo;
|
||||
import jadx.dex.info.AccessInfo.AFType;
|
||||
import jadx.dex.info.ClassInfo;
|
||||
@@ -29,6 +28,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@@ -54,6 +54,7 @@ public class MethodNode extends AttrNode implements ILoadable {
|
||||
private ArgType retType;
|
||||
private RegisterArg thisArg;
|
||||
private List<RegisterArg> argsList;
|
||||
private Map<ArgType, List<ArgType>> genericMap;
|
||||
|
||||
private List<BlockNode> blocks;
|
||||
private BlockNode enterBlock;
|
||||
@@ -131,35 +132,52 @@ public class MethodNode extends AttrNode implements ILoadable {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private boolean parseSignature() {
|
||||
AnnotationsList aList = (AnnotationsList) getAttributes().get(AttributeType.ANNOTATION_LIST);
|
||||
if (aList == null || aList.size() == 0)
|
||||
return false;
|
||||
|
||||
Annotation a = aList.get("dalvik.annotation.Signature");
|
||||
Annotation a = getAttributes().getAnnotation(Consts.DALVIK_SIGNATURE);
|
||||
if (a == null)
|
||||
return false;
|
||||
|
||||
String sign = Utils.mergeSignature((List<String>) a.getValues().get("value"));
|
||||
int lastBracket = sign.indexOf(')');
|
||||
String argsTypesStr = sign.substring(1, lastBracket);
|
||||
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);
|
||||
}
|
||||
|
||||
int firstBracket = sign.indexOf('(');
|
||||
int lastBracket = sign.lastIndexOf(')');
|
||||
String argsTypesStr = sign.substring(firstBracket + 1, lastBracket);
|
||||
String returnType = sign.substring(lastBracket + 1);
|
||||
|
||||
retType = ArgType.parseSignature(returnType);
|
||||
if (retType == null) {
|
||||
LOG.warn("Signature parse error: {}", returnType);
|
||||
return false;
|
||||
}
|
||||
if (mthInfo.getArgumentsTypes().isEmpty()) {
|
||||
argsList = Collections.emptyList();
|
||||
return true;
|
||||
}
|
||||
|
||||
List<ArgType> argsTypes = ArgType.parseSignatureList(argsTypesStr);
|
||||
if (argsTypes.size() != mthInfo.getArgumentsTypes().size()) {
|
||||
if (!getParentClass().getAccessFlags().isEnum() && !mthInfo.isConstructor()) {
|
||||
// error parsing signature
|
||||
LOG.error("Wrong parse result: " + sign + " -> " + argsTypes
|
||||
+ " must be: " + mthInfo.getArgumentsTypes()
|
||||
// + " in method " + this
|
||||
);
|
||||
}
|
||||
if (argsTypes == null)
|
||||
return false;
|
||||
|
||||
if (argsTypes.size() != mthInfo.getArgumentsTypes().size()) {
|
||||
if (!mthInfo.isConstructor()) {
|
||||
LOG.warn("Wrong signature parse result: " + sign + " -> " + argsTypes
|
||||
+ ", not generic version: " + mthInfo.getArgumentsTypes());
|
||||
return false;
|
||||
} 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));
|
||||
}
|
||||
}
|
||||
initArguments(argsTypes);
|
||||
return true;
|
||||
@@ -213,7 +231,11 @@ public class MethodNode extends AttrNode implements ILoadable {
|
||||
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) {
|
||||
CatchHandler[] catchBlocks = mthCode.getCatchHandlers();
|
||||
Try[] tries = mthCode.getTries();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package jadx.dex.visitors;
|
||||
|
||||
import jadx.dex.info.AccessInfo;
|
||||
import jadx.dex.info.MethodInfo;
|
||||
import jadx.dex.nodes.BlockNode;
|
||||
import jadx.dex.nodes.ClassNode;
|
||||
import jadx.dex.nodes.MethodNode;
|
||||
@@ -23,7 +24,7 @@ public class ClassModifier extends AbstractVisitor {
|
||||
|
||||
// remove bridge methods
|
||||
if (af.isBridge() && af.isSynthetic()) {
|
||||
if (!isMethodIdUniq(cls, mth)) {
|
||||
if (!isMethodUniq(cls, mth)) {
|
||||
// TODO add more checks before method deletion
|
||||
it.remove();
|
||||
}
|
||||
@@ -42,12 +43,17 @@ public class ClassModifier extends AbstractVisitor {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isMethodIdUniq(ClassNode cls, MethodNode mth) {
|
||||
String shortId = mth.getMethodInfo().getShortId();
|
||||
private boolean isMethodUniq(ClassNode cls, MethodNode mth) {
|
||||
MethodInfo mi = mth.getMethodInfo();
|
||||
for (MethodNode otherMth : cls.getMethods()) {
|
||||
if (otherMth.getMethodInfo().getShortId().equals(shortId)
|
||||
&& otherMth != mth)
|
||||
return false;
|
||||
MethodInfo omi = otherMth.getMethodInfo();
|
||||
if (omi.getName().equals(mi.getName())
|
||||
&& otherMth != mth) {
|
||||
if (omi.getArgumentsTypes().size() == mi.getArgumentsTypes().size()) {
|
||||
// TODO: check to args objects types
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -28,13 +28,35 @@ public class Utils {
|
||||
}
|
||||
|
||||
public static String escape(String str) {
|
||||
return str.replace('.', '_')
|
||||
.replace('/', '_')
|
||||
.replace(';', '_')
|
||||
.replace('$', '_')
|
||||
.replace('<', '_')
|
||||
.replace('>', '_')
|
||||
.replace("[]", "_A");
|
||||
int len = str.length();
|
||||
StringBuilder sb = new StringBuilder(len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
char c = str.charAt(i);
|
||||
switch (c) {
|
||||
case '.':
|
||||
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) {
|
||||
@@ -44,7 +66,7 @@ public class Utils {
|
||||
StringBuilder str = new StringBuilder();
|
||||
for (Iterator<?> it = list.iterator(); it.hasNext();) {
|
||||
Object o = it.next();
|
||||
str.append(o.toString());
|
||||
str.append(o);
|
||||
if (it.hasNext())
|
||||
str.append(", ");
|
||||
}
|
||||
@@ -52,6 +74,9 @@ public class Utils {
|
||||
}
|
||||
|
||||
public static String arrayToString(Object[] array) {
|
||||
if (array == null)
|
||||
return "";
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
if (i != 0)
|
||||
@@ -76,6 +101,26 @@ public class Utils {
|
||||
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() {
|
||||
try {
|
||||
Enumeration<URL> resources =
|
||||
|
||||
@@ -7,6 +7,134 @@ import java.util.Map;
|
||||
|
||||
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) {
|
||||
List<String> list = new ArrayList<String>();
|
||||
String str = map.get("key");
|
||||
@@ -26,6 +154,7 @@ public class TestGenerics extends AbstractTest {
|
||||
@Override
|
||||
public boolean testRun() throws Exception {
|
||||
assertTrue(test1(new HashMap<String, String>()) != null);
|
||||
// TODO: add other checks
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user