Commit 47d65fcd authored by Skylot's avatar Skylot

core: improve signature parser

parent 85ab0956
......@@ -338,28 +338,35 @@ public class ClassGen {
public String useClass(ClassInfo classInfo) {
String baseClass = useClassInternal(cls.getClassInfo(), classInfo);
ArgType[] generics = classInfo.getType().getGenericTypes();
if (generics != null) {
StringBuilder sb = new StringBuilder();
sb.append(baseClass);
sb.append('<');
int len = generics.length;
for (int i = 0; i < len; i++) {
if (i != 0) {
sb.append(", ");
}
ArgType gt = generics[i];
if (gt.isTypeKnown()) {
sb.append(TypeGen.translate(this, gt));
} else {
sb.append('?');
ArgType type = classInfo.getType();
ArgType[] generics = type.getGenericTypes();
if (generics == null) {
return baseClass;
}
StringBuilder sb = new StringBuilder();
sb.append(baseClass);
sb.append('<');
int len = generics.length;
for (int i = 0; i < len; i++) {
if (i != 0) {
sb.append(", ");
}
ArgType gt = generics[i];
ArgType wt = gt.getWildcardType();
if (wt != null) {
sb.append('?');
int bounds = gt.getWildcardBounds();
if (bounds != 0) {
sb.append(bounds == -1 ? " super " : " extends ");
sb.append(TypeGen.translate(this, wt));
}
} else {
sb.append(TypeGen.translate(this, gt));
}
sb.append('>');
return sb.toString();
} else {
return baseClass;
}
sb.append('>');
return sb.toString();
}
private String useClassInternal(ClassInfo useCls, ClassInfo classInfo) {
......
......@@ -279,15 +279,20 @@ public class MethodGen {
mth.load();
DepthTraverser.visit(new FallbackModeVisitor(), mth);
} catch (DecodeException e) {
// ignore
code.startLine("Can't loadFile method instructions");
LOG.error("Error reload instructions in fallback mode:", e);
code.startLine("// Can't loadFile method instructions: " + e.getMessage());
return;
}
}
List<InsnNode> insns = mth.getInstructions();
if (insns == null) {
code.startLine("// Can't load method instructions.");
return;
}
if (mth.getThisArg() != null) {
code.startLine(getFallbackMethodGen(mth).makeArgName(mth.getThisArg())).add(" = this;");
}
addFallbackInsns(code, mth, mth.getInstructions(), true);
addFallbackInsns(code, mth, insns, true);
}
public static void addFallbackInsns(CodeWriter code, MethodNode mth, List<InsnNode> insns, boolean addLabels) {
......
......@@ -2,20 +2,14 @@ package jadx.core.dex.instructions.args;
import jadx.core.Consts;
import jadx.core.clsp.ClspGraph;
import jadx.core.dex.nodes.parser.SignatureParser;
import jadx.core.utils.Utils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
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);
......@@ -59,19 +53,31 @@ public abstract class ArgType {
}
public static ArgType object(String obj) {
return new ObjectArg(obj);
return new ObjectType(obj);
}
public static ArgType genericType(String type) {
return new GenericTypeArg(type);
return new GenericType(type);
}
public static ArgType wildcard() {
return new WildcardType(OBJECT, 0);
}
public static ArgType wildcard(ArgType obj, int bound) {
return new WildcardType(obj, bound);
}
public static ArgType generic(String sign) {
return parseSignature(sign);
return new SignatureParser(sign).consumeType();
}
public static ArgType generic(String obj, ArgType[] generics) {
return new GenericObjectArg(obj, generics);
return new GenericObject(obj, generics);
}
public static ArgType genericInner(ArgType genericType, String innerName, ArgType[] generics) {
return new GenericObject((GenericObject) genericType, innerName, generics);
}
public static ArgType array(ArgType vtype) {
......@@ -82,14 +88,14 @@ public abstract class ArgType {
return new UnknownArg(types);
}
private abstract static class KnownTypeArg extends ArgType {
private abstract static class KnownType extends ArgType {
@Override
public boolean isTypeKnown() {
return true;
}
}
private static final class PrimitiveArg extends KnownTypeArg {
private static final class PrimitiveArg extends KnownType {
private final PrimitiveType type;
public PrimitiveArg(PrimitiveType type) {
......@@ -118,10 +124,10 @@ public abstract class ArgType {
}
}
private static class ObjectArg extends KnownTypeArg {
private static class ObjectType extends KnownType {
private final String object;
public ObjectArg(String obj) {
public ObjectType(String obj) {
this.object = Utils.cleanObjectName(obj);
this.hash = object.hashCode();
}
......@@ -143,7 +149,7 @@ public abstract class ArgType {
@Override
boolean internalEquals(Object obj) {
return object.equals(((ObjectArg) obj).object);
return object.equals(((ObjectType) obj).object);
}
@Override
......@@ -152,8 +158,8 @@ public abstract class ArgType {
}
}
private static final class GenericTypeArg extends ObjectArg {
public GenericTypeArg(String obj) {
private static final class GenericType extends ObjectType {
public GenericType(String obj) {
super(obj);
}
......@@ -163,24 +169,82 @@ public abstract class ArgType {
}
}
private static final class GenericObjectArg extends ObjectArg {
private static final class WildcardType extends ObjectType {
private final ArgType type;
private final int bounds;
public WildcardType(ArgType obj, int bound) {
super(obj.getObject());
this.type = obj;
this.bounds = bound;
}
@Override
public ArgType getWildcardType() {
return type;
}
/**
* Return wildcard bounds:
* <ul>
* <li> 1 for upper bound (? extends A) </li>
* <li> 0 no bounds (?) </li>
* <li>-1 for lower bound (? super A) </li>
* </ul>
*/
@Override
public int getWildcardBounds() {
return bounds;
}
@Override
boolean internalEquals(Object obj) {
return super.internalEquals(obj)
&& bounds == ((WildcardType) obj).bounds;
}
@Override
public String toString() {
if (bounds == 0) {
return "?";
}
return "? " + (bounds == -1 ? "super" : "extends") + " " + super.toString();
}
}
private static class GenericObject extends ObjectType {
private final ArgType[] generics;
private final GenericObject outerType;
public GenericObjectArg(String obj, ArgType[] generics) {
public GenericObject(String obj, ArgType[] generics) {
super(obj);
this.outerType = null;
this.generics = generics;
this.hash = obj.hashCode() + 31 * Arrays.hashCode(generics);
}
public GenericObject(GenericObject outerType, String innerName, ArgType[] generics) {
super(outerType.getObject() + "." + innerName);
this.outerType = outerType;
this.generics = generics;
this.hash = outerType.hashCode() + 31 * innerName.hashCode()
+ 31 * 31 * Arrays.hashCode(generics);
}
@Override
public ArgType[] getGenericTypes() {
return generics;
}
@Override
public ArgType getOuterType() {
return outerType;
}
@Override
boolean internalEquals(Object obj) {
return super.internalEquals(obj)
&& Arrays.equals(generics, ((GenericObjectArg) obj).generics);
&& Arrays.equals(generics, ((GenericObject) obj).generics);
}
@Override
......@@ -189,7 +253,7 @@ public abstract class ArgType {
}
}
private static final class ArrayArg extends KnownTypeArg {
private static final class ArrayArg extends KnownType {
private final ArgType arrayElement;
public ArrayArg(ArgType arrayElement) {
......@@ -314,6 +378,21 @@ public abstract class ArgType {
return null;
}
public ArgType getWildcardType() {
return null;
}
/**
* @see jadx.core.dex.instructions.args.ArgType.WildcardType#getWildcardBounds()
*/
public int getWildcardBounds() {
return 0;
}
public ArgType getOuterType() {
return null;
}
public boolean isArray() {
return false;
}
......@@ -459,148 +538,7 @@ public abstract class ArgType {
}
}
public static ArgType parseSignature(String sign) {
int b = sign.indexOf('<');
if (b == -1) {
return parse(sign);
}
if (sign.charAt(0) == '[') {
return array(parseSignature(sign.substring(1)));
}
String obj = sign.substring(0, b) + ";";
String genericsStr = sign.substring(b + 1, sign.length() - 2);
List<ArgType> generics = parseSignatureList(genericsStr);
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) {
if (str.isEmpty()) {
return Collections.emptyList();
}
if (str.equals("*")) {
return Arrays.asList(UNKNOWN);
}
List<ArgType> signs = new ArrayList<ArgType>(3);
int obj = 0;
int objStart = 0;
int gen = 0;
int arr = 0;
int pos = 0;
ArgType type = null;
while (pos < str.length()) {
char c = str.charAt(pos);
switch (c) {
case 'L':
case 'T':
if (obj == 0 && gen == 0) {
obj++;
objStart = pos;
}
break;
case ';':
if (obj == 1 && gen == 0) {
obj--;
String o = str.substring(objStart, pos + 1);
type = parseSignature(o);
}
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;
case '>':
gen--;
break;
case '[':
if (obj == 0 && gen == 0) {
arr++;
}
break;
default:
if (parsePrimitives && obj == 0 && gen == 0) {
type = parse(c);
}
break;
}
if (type != null) {
if (arr == 0) {
signs.add(type);
} else {
for (int i = 0; i < arr; i++) {
type = array(type);
}
signs.add(type);
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);
}
}
return genericMap;
} catch (Throwable e) {
LOG.warn("Generic map parse exception: {}", gen, e);
return null;
}
}
private static ArgType parse(char f) {
public static ArgType parse(char f) {
switch (f) {
case 'Z':
return BOOLEAN;
......
......@@ -16,9 +16,10 @@ import jadx.core.dex.instructions.args.LiteralArg;
import jadx.core.dex.instructions.args.PrimitiveType;
import jadx.core.dex.nodes.parser.AnnotationsParser;
import jadx.core.dex.nodes.parser.FieldValueAttr;
import jadx.core.dex.nodes.parser.SignatureParser;
import jadx.core.dex.nodes.parser.StaticValuesParser;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.exceptions.JadxRuntimeException;
import java.util.ArrayList;
import java.util.Collections;
......@@ -159,48 +160,41 @@ public class ClassNode extends LineAttrNode implements ILoadable {
}
}
@SuppressWarnings("unchecked")
private void parseClassSignature() {
Annotation a = this.getAttributes().getAnnotation(Consts.DALVIK_SIGNATURE);
if (a == null) {
SignatureParser sp = SignatureParser.fromNode(this);
if (sp == 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(st);
int i = 0;
for (ArgType it : list) {
ClassInfo interf = ClassInfo.fromType(it);
interfaces.set(i, interf);
i++;
try {
// parse class generic map
genericMap = sp.consumeGenericMap();
// parse super class signature
superClass = ClassInfo.fromType(sp.consumeType());
// parse interfaces signatures
for (int i = 0; i < interfaces.size(); i++) {
ArgType type = sp.consumeType();
if (type != null) {
interfaces.set(i, ClassInfo.fromType(type));
} else {
break;
}
} catch (Throwable e) {
LOG.warn("Can't set signatures for class: {}, sign: {}", this, sign, e);
}
} catch (JadxRuntimeException e) {
LOG.error("Class signature parse error: " + this, e);
}
}
@SuppressWarnings("unchecked")
private void setFieldsTypesFromSignature() {
for (FieldNode field : fields) {
Annotation a = field.getAttributes().getAnnotation(Consts.DALVIK_SIGNATURE);
if (a != null) {
String sign = Utils.mergeSignature((List<String>) a.getDefaultValue());
ArgType gType = ArgType.parseSignature(sign);
if (gType != null) {
field.setType(gType);
SignatureParser sp = SignatureParser.fromNode(field);
if (sp != null) {
try {
ArgType gType = sp.consumeType();
if (gType != null) {
field.setType(gType);
}
} catch (JadxRuntimeException e) {
LOG.error("Field signature parse error: " + field, e);
}
}
}
......
......@@ -60,6 +60,6 @@ public class FieldNode extends LineAttrNode {
@Override
public String toString() {
return fieldInfo.getDeclClass() + "." + fieldInfo.getName() + " " + type;
return fieldInfo.getDeclClass() + "." + fieldInfo.getName() + " :" + type;
}
}
package jadx.core.dex.nodes;
import jadx.core.Consts;
import jadx.core.dex.attributes.AttributeFlag;
import jadx.core.dex.attributes.JumpAttribute;
import jadx.core.dex.attributes.LineAttrNode;
import jadx.core.dex.attributes.LoopAttr;
import jadx.core.dex.attributes.annotations.Annotation;
import jadx.core.dex.info.AccessInfo;
import jadx.core.dex.info.AccessInfo.AFType;
import jadx.core.dex.info.ClassInfo;
......@@ -18,12 +16,14 @@ import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.nodes.parser.DebugInfoParser;
import jadx.core.dex.nodes.parser.SignatureParser;
import jadx.core.dex.regions.Region;
import jadx.core.dex.trycatch.ExcHandlerAttr;
import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.dex.trycatch.TryCatchBlock;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.exceptions.JadxRuntimeException;
import java.util.ArrayList;
import java.util.Collections;
......@@ -140,62 +140,42 @@ public class MethodNode extends LineAttrNode implements ILoadable {
noCode = true;
}
@SuppressWarnings("unchecked")
private boolean parseSignature() {
Annotation a = getAttributes().getAnnotation(Consts.DALVIK_SIGNATURE);
if (a == null) {
return false;
}
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;
}
List<ArgType> argsTypes = ArgType.parseSignatureList(argsTypesStr);
if (argsTypes == null) {
SignatureParser sp = SignatureParser.fromNode(this);
if (sp == null) {
return false;
}
try {
genericMap = sp.consumeGenericMap();
List<ArgType> argsTypes = sp.consumeMethodArgs();
retType = sp.consumeType();
List<ArgType> mthArgs = mthInfo.getArgumentsTypes();
if (argsTypes.size() != mthArgs.size()) {
if (argsTypes.isEmpty()) {
return false;
}
if (!mthInfo.isConstructor()) {
LOG.warn("Wrong signature parse result: " + sign + " -> " + argsTypes
+ ", not generic version: " + mthArgs);
return false;
} else if (getParentClass().getAccessFlags().isEnum()) {
// TODO:
argsTypes.add(0, mthArgs.get(0));
argsTypes.add(1, mthArgs.get(1));
} else {
// add synthetic arg for outer class
argsTypes.add(0, mthArgs.get(0));
}
List<ArgType> mthArgs = mthInfo.getArgumentsTypes();
if (argsTypes.size() != mthArgs.size()) {
return false;
if (argsTypes.isEmpty()) {
return false;
}
if (!mthInfo.isConstructor()) {
LOG.warn("Wrong signature parse result: " + sp + " -> " + argsTypes
+ ", not generic version: " + mthArgs);
return false;
} else if (getParentClass().getAccessFlags().isEnum()) {
// TODO:
argsTypes.add(0, mthArgs.get(0));
argsTypes.add(1, mthArgs.get(1));
} else {
// add synthetic arg for outer class
argsTypes.add(0, mthArgs.get(0));
}
if (argsTypes.size() != mthArgs.size()) {
return false;
}
}
initArguments(argsTypes);
} catch (JadxRuntimeException e) {
LOG.error("Method signature parse error: " + this, e);
return false;
}
initArguments(argsTypes);
return true;
}
......
package jadx.core.dex.nodes.parser;
import jadx.core.Consts;
import jadx.core.dex.attributes.IAttributeNode;
import jadx.core.dex.attributes.annotations.Annotation;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.utils.exceptions.JadxRuntimeException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class SignatureParser {
private static final char STOP_CHAR = 0;
private final String sign;
private final int end;
private int pos;
private int mark;
public SignatureParser(String signature) {
sign = signature;
pos = -1;
mark = 0;
end = sign.length();
}
@SuppressWarnings("unchecked")
public static SignatureParser fromNode(IAttributeNode node) {
Annotation a = node.getAttributes().getAnnotation(Consts.DALVIK_SIGNATURE);
if (a == null) {
return null;
}
return new SignatureParser(mergeSignature((List<String>) a.getDefaultValue()));
}
private char next() {
pos++;
if (pos >= end) {
return STOP_CHAR;
}
return sign.charAt(pos);
}
private boolean lookAhead(char ch) {
int next = pos + 1;
return next < end && sign.charAt(next) == ch;
}
private void mark() {
mark = pos;
}
/**
* Exclusive slice.
*
* @return string from 'mark' to current position (not including current character)
*/
private String slice() {
if (mark >= pos) {
return "";
}
return sign.substring(mark, pos);
}
/**
* Inclusive slice (includes current character)
*/
private String inclusiveSlice() {
if (mark >= pos) {
return "";
}
return sign.substring(mark, pos + 1);
}
private boolean forwardTo(char lastChar) {
int startPos = pos;
char ch;
while ((ch = next()) != STOP_CHAR) {
if (ch == lastChar) {
return true;
}
}
pos = startPos;
return false;
}
private void consume(char exp) {
char c = next();
if (exp != c) {
throw new JadxRuntimeException("Consume wrong char: '" + c + "' != '" + exp
+ "', sign: " + debugString());
}
}
private boolean tryConsume(char exp) {
if (lookAhead(exp)) {
next();
return true;
}
return false;
}
private String consumeUntil(char lastChar) {
mark();
return forwardTo(lastChar) ? slice() : null;
}
public ArgType consumeType() {
char ch = next();
mark();
switch (ch) {
case 'L':
ArgType obj = consumeObjectType(false);
if (obj != null) {
return obj;
}
break;
case 'T':
next();
mark();
if (forwardTo(';')) {
return ArgType.genericType(slice());
}
break;
case '[':
return ArgType.array(consumeType());
case STOP_CHAR:
return null;
default:
// primitive type (one char)
ArgType type = ArgType.parse(ch);
if (type != null) {
return type;
}
break;
}
throw new JadxRuntimeException("Can't parse type: " + debugString());
}
private ArgType consumeObjectType(boolean incompleteType) {
mark();
int ch;
do {
ch = next();
if (ch == STOP_CHAR) {
return null;
}
} while (ch != '<' && ch != ';');
if (ch == ';') {
return ArgType.object(incompleteType ? slice() : inclusiveSlice());
} else {
// generic type start ('<')
String obj = slice();
if (!incompleteType) {
obj += ";";
}
ArgType[] genArr = consumeGenericArgs();
consume('>');
ArgType genericType = ArgType.generic(obj, genArr);
if (lookAhead('.')) {
consume('.');
next();
// type parsing not completed, proceed to inner class
ArgType inner = consumeObjectType(true);
return ArgType.genericInner(genericType, inner.getObject(), inner.getGenericTypes());
} else {
consume(';');
return genericType;
}
}
}
private ArgType[] consumeGenericArgs() {
List<ArgType> list = new ArrayList<ArgType>(1);
ArgType type;
do {
if (lookAhead('*')) {
next();
type = ArgType.wildcard();
} else if (lookAhead('+')) {
next();
type = ArgType.wildcard(consumeType(), 1);
} else if (lookAhead('-')) {
next();
type = ArgType.wildcard(consumeType(), -1);
} else {
type = consumeType();
}
if (type != null) {
list.add(type);
}
} while (type != null && !lookAhead('>'));
return list.toArray(new ArgType[list.size()]);
}
/**
* Map of generic types names to extends classes.
* <p/>
* Example: "<T:Ljava/lang/Exception;:Ljava/lang/Object;>"
*/
public Map<ArgType, List<ArgType>> consumeGenericMap() {
if (!lookAhead('<')) {
return null;
}
Map<ArgType, List<ArgType>> map = new LinkedHashMap<ArgType, List<ArgType>>(2);
consume('<');
while (true) {
if (lookAhead('>') || next() == STOP_CHAR) {
break;
}
String id = consumeUntil(':');
tryConsume(':');
List<ArgType> types = consumeExtendsTypesList();
map.put(ArgType.genericType(id), types);
}
consume('>');
return map;
}
/**
* List of types separated by ':' last type is 'java.lang.Object'.
* <p/>
* Example: "Ljava/lang/Exception;:Ljava/lang/Object;"
*/
private List<ArgType> consumeExtendsTypesList() {
List<ArgType> types = Collections.emptyList();
boolean next;
do {
ArgType argType = consumeType();
if (!argType.equals(ArgType.OBJECT)) {
if (types.isEmpty()) {
types = new LinkedList<ArgType>();
}
types.add(argType);
}
next = lookAhead(':');
if (next) {
consume(':');
}
} while (next);
return types;
}
public List<ArgType> consumeMethodArgs() {
consume('(');
if (lookAhead(')')) {
consume(')');
return Collections.emptyList();
}
List<ArgType> args = new LinkedList<ArgType>();
do {
args.add(consumeType());
} while (!lookAhead(')'));
consume(')');
return args;
}
private static String mergeSignature(List<String> list) {
if (list.size() == 1) {
return list.get(0);
}
StringBuilder sb = new StringBuilder();
for (String s : list) {
sb.append(s);
}
return sb.toString();
}
private String debugString() {
return sign + " at position " + pos + " ('" + sign.charAt(pos) + "')";
}
@Override
public String toString() {
if (pos == -1) {
return sign;
}
return sign.substring(0, mark) + '{' + sign.substring(mark, pos) + '}' + sign.substring(pos);
}
}
......@@ -6,13 +6,10 @@ import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Iterator;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Utils {
private static final Logger LOG = LoggerFactory.getLogger(Utils.class);
private Utils() {
}
public static String cleanObjectName(String obj) {
int last = obj.length() - 1;
......@@ -97,34 +94,6 @@ public class Utils {
return sw.getBuffer().toString();
}
public static String mergeSignature(List<String> list) {
StringBuilder sb = new StringBuilder();
for (String s : list) {
sb.append(s);
}
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 void makeDirsForFile(File file) {
File dir = file.getParentFile();
if (!dir.exists()) {
......
package jadx.core.dex.nodes.parser;
import jadx.core.dex.instructions.args.ArgType;
import java.util.List;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class TestSignatureParser {
private SignatureParser p(String str) {
return new SignatureParser(str);
}
@Test
public void testType() {
assertEquals(p("").consumeType(), null);
assertEquals(p("I").consumeType(), ArgType.INT);
assertEquals(p("[I").consumeType(), ArgType.array(ArgType.INT));
assertEquals(p("Ljava/lang/Object;").consumeType(), ArgType.OBJECT);
assertEquals(p("[Ljava/lang/Object;").consumeType(), ArgType.array(ArgType.OBJECT));
assertEquals(p("[[I").consumeType(), ArgType.array(ArgType.array(ArgType.INT)));
}
@Test
public void testType2() {
assertEquals(p("TD;").consumeType(), ArgType.genericType("D"));
}
@Test
public void testGenericType() {
assertEquals(p("La<TV;Lb;>;").consumeType(),
ArgType.generic("La;", new ArgType[]{ArgType.genericType("V"), ArgType.object("b")}));
assertEquals(p("La<Lb<Lc;>;>;").consumeType(),
ArgType.generic("La;", new ArgType[]{
ArgType.generic("Lb;", new ArgType[]{
ArgType.object("Lc;")})}));
}
@Test
public void testGenericInnerType() {
assertEquals(p("La<TD;>.c;").consumeType(),
ArgType.genericInner(ArgType.generic("La;", new ArgType[]{ArgType.genericType("D")}), "c", null));
assertEquals(p("La<Lb;>.c<TV;>;").consumeType(),
ArgType.genericInner(ArgType.generic("La;", new ArgType[]{ArgType.object("Lb;")}),
"c", new ArgType[]{ArgType.genericType("V")}));
assertEquals(p("La<TV;>.LinkedHashIterator<Lb$c<Ls;TV;>;>;").consumeType().getObject(),
"a.LinkedHashIterator");
}
@Test
public void testWildCards() {
assertEquals(p("La<*>;").consumeType(),
ArgType.generic("La;", new ArgType[]{ArgType.wildcard()}));
assertEquals(p("La<+Lb;>;").consumeType(),
ArgType.generic("La;", new ArgType[]{ArgType.wildcard(ArgType.object("b"), 1)}));
assertEquals(p("La<-Lb;>;").consumeType(),
ArgType.generic("La;", new ArgType[]{ArgType.wildcard(ArgType.object("b"), -1)}));
assertEquals(p("La<+TV;>;").consumeType(),
ArgType.generic("La;", new ArgType[]{ArgType.wildcard(ArgType.genericType("V"), 1)}));
assertEquals(p("La<-TV;>;").consumeType(),
ArgType.generic("La;", new ArgType[]{ArgType.wildcard(ArgType.genericType("V"), -1)}));
}
@Test
public void testWildCards2() {
assertEquals(p("La<*>;").consumeType(),
ArgType.generic("La;", new ArgType[]{ArgType.wildcard()}));
assertEquals(p("La<**>;").consumeType(),
ArgType.generic("La;", new ArgType[]{ArgType.wildcard(), ArgType.wildcard()}));
assertEquals(p("La<*Lb;>;").consumeType(),
ArgType.generic("La;", new ArgType[]{ArgType.wildcard(), ArgType.object("b")}));
assertEquals(p("La<*TV;>;").consumeType(),
ArgType.generic("La;", new ArgType[]{ArgType.wildcard(), ArgType.genericType("V")}));
}
@Test
public void testGenericMap() {
assertEquals(p("<T:Ljava/lang/Object;>").consumeGenericMap().toString(),
"{T=[]}");
assertEquals(p("<K:Ljava/lang/Object;LongGenericType:Ljava/lang/Object;>").consumeGenericMap().toString(),
"{K=[], LongGenericType=[]}");
assertEquals(p("<ResultT:Ljava/lang/Exception;:Ljava/lang/Object;>").consumeGenericMap().toString(),
"{ResultT=[java.lang.Exception]}");
}
@Test
public void testMethodsArgs() {
List<ArgType> argTypes = p("(Ljava/util/List<*>;)V").consumeMethodArgs();
assertEquals(argTypes.size(), 1);
assertEquals(argTypes.get(0), ArgType.generic("Ljava/util/List;", new ArgType[]{ArgType.wildcard()}));
}
}
package jadx.tests.internal.generics;
import jadx.api.InternalJadxTest;
import jadx.core.dex.nodes.ClassNode;
import java.util.List;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertThat;
public class TestGenerics extends InternalJadxTest {
public static class TestCls {
class A {
}
public static void mthWildcard(List<?> list) {
}
public static void mthExtends(List<? extends A> list) {
}
public static void mthSuper(List<? super A> list) {
}
}
@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
System.out.println(code);
assertThat(code, containsString("mthWildcard(List<?> list)"));
assertThat(code, containsString("mthExtends(List<? extends A> list)"));
assertThat(code, containsString("mthSuper(List<? super A> list)"));
}
}
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