Commit 3b84aec5 authored by Skylot's avatar Skylot

Add generic types to methods declarations

parent cc318b13
...@@ -62,11 +62,15 @@ public class AnnotationGen { ...@@ -62,11 +62,15 @@ 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 (aCls.equals("dalvik.annotation.Signature")) {
code.startLine("// signature: " if (!(node instanceof MethodNode)) {
+ Utils.mergeSignature((List<String>) a.getValues().get("value"))); String sign = Utils.mergeSignature((List<String>) a.getValues().get("value"));
else if (Consts.DEBUG) List<ArgType> types = ArgType.parseSignatureList(sign);
code.startLine("// signature: " + Utils.listToString(types));
}
} else if (Consts.DEBUG) {
code.startLine("// " + a); code.startLine("// " + a);
}
} else { } else {
code.startLine(); code.startLine();
code.add(formatAnnotation(a)); code.add(formatAnnotation(a));
......
...@@ -248,7 +248,29 @@ public class ClassGen { ...@@ -248,7 +248,29 @@ public class ClassGen {
} }
public String useClass(ArgType clsType) { public String useClass(ArgType clsType) {
return useClass(ClassInfo.fromType(cls.dex(), clsType)); String baseClass = useClass(ClassInfo.fromType(cls.dex(), clsType));
ArgType[] generics = clsType.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(useClass(gt));
else
sb.append('?');
}
sb.append(">");
return sb.toString();
} else {
return baseClass;
}
} }
public String useClass(ClassInfo classInfo) { public String useClass(ClassInfo classInfo) {
......
...@@ -94,19 +94,7 @@ public class InsnGen { ...@@ -94,19 +94,7 @@ public class InsnGen {
} }
public String declareVar(RegisterArg arg) throws CodegenException { public String declareVar(RegisterArg arg) throws CodegenException {
String type = useType(arg.getType()); return useType(arg.getType()) + " " + arg(arg);
ArgType[] generics = arg.getType().getGenericTypes();
if (generics != null) {
StringBuilder sb = new StringBuilder();
sb.append(type);
sb.append("/*<");
for (ArgType gt : generics) {
sb.append(useType(gt));
}
sb.append(">*/");
type = sb.toString();
}
return type + " " + arg(arg);
} }
private String lit(LiteralArg arg) { private String lit(LiteralArg arg) {
...@@ -144,13 +132,6 @@ public class InsnGen { ...@@ -144,13 +132,6 @@ public class InsnGen {
} }
private String useType(ArgType type) { private String useType(ArgType type) {
if (type.isObject())
return mgen.getClassGen().useClass(type);
else
return translate(type);
}
private String translate(ArgType type) {
return TypeGen.translate(mgen.getClassGen(), type); return TypeGen.translate(mgen.getClassGen(), type);
} }
...@@ -160,7 +141,6 @@ public class InsnGen { ...@@ -160,7 +141,6 @@ public class InsnGen {
private void makeInsn(InsnNode insn, CodeWriter code, boolean bodyOnly) throws CodegenException { private void makeInsn(InsnNode insn, CodeWriter code, boolean bodyOnly) throws CodegenException {
try { try {
// code.startLine("/* " + insn + "*/");
EnumSet<InsnGenState> state = EnumSet.noneOf(InsnGenState.class); EnumSet<InsnGenState> state = EnumSet.noneOf(InsnGenState.class);
if (bodyOnly) { if (bodyOnly) {
state.add(InsnGenState.BODY_ONLY); state.add(InsnGenState.BODY_ONLY);
...@@ -213,7 +193,7 @@ public class InsnGen { ...@@ -213,7 +193,7 @@ public class InsnGen {
case CHECK_CAST: case CHECK_CAST:
case CAST: case CAST:
code.add("(("); code.add("((");
code.add(translate(((ArgType) ((IndexInsnNode) insn).getIndex()))); code.add(useType(((ArgType) ((IndexInsnNode) insn).getIndex())));
code.add(") ("); code.add(") (");
code.add(arg(insn.getArg(0))); code.add(arg(insn.getArg(0)));
code.add("))"); code.add("))");
...@@ -445,7 +425,7 @@ public class InsnGen { ...@@ -445,7 +425,7 @@ public class InsnGen {
} }
int len = str.length(); int len = str.length();
str.delete(len - 2, len); str.delete(len - 2, len);
code.add("new ").add(translate(elType)).add("[] { ").add(str.toString()).add(" }"); code.add("new ").add(useType(elType)).add("[] { ").add(str.toString()).add(" }");
} }
private void makeConstructor(ConstructorInsn insn, CodeWriter code, EnumSet<InsnGenState> state) private void makeConstructor(ConstructorInsn insn, CodeWriter code, EnumSet<InsnGenState> state)
......
...@@ -81,13 +81,12 @@ public class MethodGen { ...@@ -81,13 +81,12 @@ public class MethodGen {
if (mth.getAccessFlags().isConstructor()) { if (mth.getAccessFlags().isConstructor()) {
code.add(classGen.getClassNode().getShortName()); // constructor code.add(classGen.getClassNode().getShortName()); // constructor
} else { } else {
code.add(TypeGen.translate(classGen, mth.getMethodInfo().getReturnType())); code.add(TypeGen.translate(classGen, mth.getReturnType()));
code.add(" "); code.add(' ');
code.add(mth.getName()); code.add(mth.getName());
} }
code.add("("); code.add('(');
mth.resetArgsTypes();
List<RegisterArg> args = mth.getArguments(false); List<RegisterArg> args = mth.getArguments(false);
if (mth.getMethodInfo().isConstructor() if (mth.getMethodInfo().isConstructor()
&& mth.getParentClass().getAttributes().contains(AttributeType.ENUM_CLASS)) { && mth.getParentClass().getAttributes().contains(AttributeType.ENUM_CLASS)) {
......
...@@ -28,7 +28,7 @@ public class LocalVarInfo extends RegisterArg { ...@@ -28,7 +28,7 @@ public class LocalVarInfo extends RegisterArg {
private void init(String name, ArgType type, String sign) { private void init(String name, ArgType type, String sign) {
if (sign != null) { if (sign != null) {
type = ArgType.generic(type.getObject(), sign); type = ArgType.generic(sign);
} }
TypedVar tv = new TypedVar(type); TypedVar tv = new TypedVar(type);
tv.setName(name); tv.setName(name);
......
...@@ -401,7 +401,7 @@ public class InsnDecoder { ...@@ -401,7 +401,7 @@ public class InsnDecoder {
case Opcodes.RETURN_OBJECT: case Opcodes.RETURN_OBJECT:
return insn(InsnType.RETURN, return insn(InsnType.RETURN,
null, null,
InsnArg.reg(insn, 0, method.getMethodInfo().getReturnType())); InsnArg.reg(insn, 0, method.getReturnType()));
case Opcodes.INSTANCE_OF: { case Opcodes.INSTANCE_OF: {
InsnNode node = new IndexInsnNode(method, InsnType.INSTANCE_OF, dex.getType(insn.getIndex()), 1); InsnNode node = new IndexInsnNode(method, InsnType.INSTANCE_OF, dex.getType(insn.getIndex()), 1);
......
...@@ -24,6 +24,7 @@ public abstract class ArgType { ...@@ -24,6 +24,7 @@ public abstract class ArgType {
public static final ArgType THROWABLE = object(Consts.CLASS_THROWABLE); public static final ArgType THROWABLE = object(Consts.CLASS_THROWABLE);
public static final ArgType UNKNOWN = unknown(PrimitiveType.values()); public static final ArgType UNKNOWN = unknown(PrimitiveType.values());
public static final ArgType UNKNOWN_OBJECT = unknown(PrimitiveType.OBJECT, PrimitiveType.ARRAY);
public static final ArgType NARROW = unknown( public static final ArgType NARROW = unknown(
PrimitiveType.INT, PrimitiveType.FLOAT, PrimitiveType.INT, PrimitiveType.FLOAT,
...@@ -32,8 +33,6 @@ public abstract class ArgType { ...@@ -32,8 +33,6 @@ public abstract class ArgType {
public static final ArgType WIDE = unknown(PrimitiveType.LONG, PrimitiveType.DOUBLE); public static final ArgType WIDE = unknown(PrimitiveType.LONG, PrimitiveType.DOUBLE);
public static final ArgType UNKNOWN_OBJECT = unknown(PrimitiveType.OBJECT, PrimitiveType.ARRAY);
protected int hash; protected int hash;
private static ArgType primitive(PrimitiveType stype) { private static ArgType primitive(PrimitiveType stype) {
...@@ -41,11 +40,15 @@ public abstract class ArgType { ...@@ -41,11 +40,15 @@ public abstract class ArgType {
} }
public static ArgType object(String obj) { public static ArgType object(String obj) {
return new ObjectArg(Utils.cleanObjectName(obj)); return new ObjectArg(obj);
} }
public static ArgType generic(String obj, String signature) { public static ArgType generic(String sign) {
return new GenericObjectArg(obj, signature); return parseSignature(sign);
}
public static ArgType generic(String obj, ArgType[] generics) {
return new GenericObjectArg(obj, generics);
} }
public static ArgType array(ArgType vtype) { public static ArgType array(ArgType vtype) {
...@@ -91,7 +94,7 @@ public abstract class ArgType { ...@@ -91,7 +94,7 @@ public abstract class ArgType {
private final String object; private final String object;
public ObjectArg(String obj) { public ObjectArg(String obj) {
this.object = obj; this.object = Utils.cleanObjectName(obj);
this.hash = obj.hashCode(); this.hash = obj.hashCode();
} }
...@@ -119,9 +122,9 @@ public abstract class ArgType { ...@@ -119,9 +122,9 @@ public abstract class ArgType {
private static final class GenericObjectArg extends ObjectArg { private static final class GenericObjectArg extends ObjectArg {
private final ArgType[] generics; private final ArgType[] generics;
public GenericObjectArg(String obj, String signature) { public GenericObjectArg(String obj, ArgType[] generics) {
super(obj); super(obj);
this.generics = parseSignature(signature); this.generics = generics;
this.hash = obj.hashCode() + 31 * Arrays.hashCode(generics); this.hash = obj.hashCode() + 31 * Arrays.hashCode(generics);
} }
...@@ -132,7 +135,7 @@ public abstract class ArgType { ...@@ -132,7 +135,7 @@ public abstract class ArgType {
@Override @Override
public String toString() { public String toString() {
return super.toString() + "<" + Arrays.toString(generics) + ">"; return super.toString() + "<" + Utils.arrayToString(generics) + ">";
} }
} }
...@@ -177,7 +180,7 @@ public abstract class ArgType { ...@@ -177,7 +180,7 @@ public abstract class ArgType {
@Override @Override
public String toString() { public String toString() {
return arrayElement.toString(); return arrayElement.toString() + "[]";
} }
} }
...@@ -220,7 +223,7 @@ public abstract class ArgType { ...@@ -220,7 +223,7 @@ public abstract class ArgType {
@Override @Override
public String toString() { public String toString() {
if (possibleTypes.length == PrimitiveType.values().length) if (possibleTypes.length == PrimitiveType.values().length)
return "*"; return "?";
else else
return "?" + Arrays.toString(possibleTypes); return "?" + Arrays.toString(possibleTypes);
} }
...@@ -363,30 +366,83 @@ public abstract class ArgType { ...@@ -363,30 +366,83 @@ public abstract class ArgType {
} }
} }
public static ArgType[] parseSignature(String signature) { public static ArgType parseSignature(String sign) {
int b = signature.indexOf('<') + 1; int b = sign.indexOf('<');
int e = signature.lastIndexOf('>'); if (b == -1)
String gens = signature.substring(b, e); return parse(sign);
String[] split = gens.split(";");
ArgType[] result = new ArgType[split.length]; String obj = sign.substring(0, b);
for (int i = 0; i < split.length; i++) { String genericsStr = sign.substring(b + 1, sign.length() - 2);
String g = split[i]; List<ArgType> generics = parseSignatureList(genericsStr);
switch (g.charAt(0)) { ArgType res = generic(obj + ";", generics.toArray(new ArgType[generics.size()]));
return res;
}
public static List<ArgType> parseSignatureList(String str) {
List<ArgType> signs = new ArrayList<ArgType>(3);
if (str.equals("*")) {
signs.add(UNKNOWN);
return signs;
}
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 'L':
result[i] = object(g + ";"); if (obj == 0 && gen == 0) {
obj++;
objStart = pos;
}
break; break;
case '*': case ';':
case '?': if (obj == 1 && gen == 0) {
result[i] = UNKNOWN; obj--;
String o = str.substring(objStart, pos + 1);
type = parseSignature(o);
}
break;
case '<':
gen++;
break;
case '>':
gen--;
break;
case '[':
arr++;
break; break;
default: default:
result[i] = UNKNOWN_OBJECT; if (obj == 0 && gen == 0) {
type = parse(c);
}
break; 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;
} }
return result; type = null;
}
pos++;
}
return signs;
} }
private static ArgType parse(char f) { private static ArgType parse(char f) {
...@@ -410,7 +466,7 @@ public abstract class ArgType { ...@@ -410,7 +466,7 @@ public abstract class ArgType {
case 'V': case 'V':
return VOID; return VOID;
} }
throw new RuntimeException("Unknown type: " + f); return null;
} }
public int getRegCount() { public int getRegCount() {
......
...@@ -2,7 +2,10 @@ package jadx.dex.nodes; ...@@ -2,7 +2,10 @@ package jadx.dex.nodes;
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.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;
...@@ -28,12 +31,16 @@ import java.util.HashSet; ...@@ -28,12 +31,16 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.android.dx.io.ClassData.Method; import com.android.dx.io.ClassData.Method;
import com.android.dx.io.Code; import com.android.dx.io.Code;
import com.android.dx.io.Code.CatchHandler; import com.android.dx.io.Code.CatchHandler;
import com.android.dx.io.Code.Try; import com.android.dx.io.Code.Try;
public class MethodNode extends AttrNode implements ILoadable { public class MethodNode extends AttrNode implements ILoadable {
private static final Logger LOG = LoggerFactory.getLogger(MethodNode.class);
private final MethodInfo mthInfo; private final MethodInfo mthInfo;
private final ClassNode parentClass; private final ClassNode parentClass;
...@@ -44,6 +51,7 @@ public class MethodNode extends AttrNode implements ILoadable { ...@@ -44,6 +51,7 @@ public class MethodNode extends AttrNode implements ILoadable {
private List<InsnNode> instructions; private List<InsnNode> instructions;
private boolean noCode; private boolean noCode;
private ArgType retType;
private RegisterArg thisArg; private RegisterArg thisArg;
private List<RegisterArg> argsList; private List<RegisterArg> argsList;
...@@ -65,7 +73,8 @@ public class MethodNode extends AttrNode implements ILoadable { ...@@ -65,7 +73,8 @@ public class MethodNode extends AttrNode implements ILoadable {
if (methodData.getCodeOffset() == 0) { if (methodData.getCodeOffset() == 0) {
noCode = true; noCode = true;
regsCount = 0; regsCount = 0;
initArguments(); retType = mthInfo.getReturnType();
initArguments(mthInfo.getArgumentsTypes());
} else { } else {
noCode = false; noCode = false;
} }
...@@ -81,6 +90,11 @@ public class MethodNode extends AttrNode implements ILoadable { ...@@ -81,6 +90,11 @@ public class MethodNode extends AttrNode implements ILoadable {
Code mthCode = dex.readCode(methodData); Code mthCode = dex.readCode(methodData);
regsCount = mthCode.getRegistersSize(); regsCount = mthCode.getRegistersSize();
if (!parseSignature()) {
retType = mthInfo.getReturnType();
initArguments(mthInfo.getArgumentsTypes());
}
InsnDecoder decoder = new InsnDecoder(this, mthCode); InsnDecoder decoder = new InsnDecoder(this, mthCode);
InsnNode[] insnByOffset = decoder.run(); InsnNode[] insnByOffset = decoder.run();
instructions = new ArrayList<InsnNode>(); instructions = new ArrayList<InsnNode>();
...@@ -90,7 +104,6 @@ public class MethodNode extends AttrNode implements ILoadable { ...@@ -90,7 +104,6 @@ public class MethodNode extends AttrNode implements ILoadable {
} }
((ArrayList<InsnNode>) instructions).trimToSize(); ((ArrayList<InsnNode>) instructions).trimToSize();
initArguments();
initTryCatches(mthCode, insnByOffset); initTryCatches(mthCode, insnByOffset);
initJumps(insnByOffset); initJumps(insnByOffset);
...@@ -116,8 +129,43 @@ public class MethodNode extends AttrNode implements ILoadable { ...@@ -116,8 +129,43 @@ public class MethodNode extends AttrNode implements ILoadable {
noCode = true; noCode = true;
} }
private void initArguments() { @SuppressWarnings("unchecked")
List<ArgType> args = mthInfo.getArgumentsTypes(); 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");
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 returnType = sign.substring(lastBracket + 1);
retType = ArgType.parseSignature(returnType);
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
);
}
return false;
}
initArguments(argsTypes);
return true;
}
private void initArguments(List<ArgType> args) {
int pos; int pos;
if (!noCode) { if (!noCode) {
pos = regsCount; pos = regsCount;
...@@ -134,6 +182,11 @@ public class MethodNode extends AttrNode implements ILoadable { ...@@ -134,6 +182,11 @@ public class MethodNode extends AttrNode implements ILoadable {
thisArg.getTypedVar().setName("this"); thisArg.getTypedVar().setName("this");
} }
if (args.isEmpty()) {
argsList = Collections.emptyList();
return;
}
argsList = new ArrayList<RegisterArg>(args.size()); argsList = new ArrayList<RegisterArg>(args.size());
for (ArgType arg : args) { for (ArgType arg : args) {
argsList.add(InsnArg.reg(pos, arg)); argsList.add(InsnArg.reg(pos, arg));
...@@ -156,15 +209,8 @@ public class MethodNode extends AttrNode implements ILoadable { ...@@ -156,15 +209,8 @@ public class MethodNode extends AttrNode implements ILoadable {
return thisArg; return thisArg;
} }
// TODO: args types can change during type resolving => reset and copy back names public ArgType getReturnType() {
@Deprecated return retType;
public void resetArgsTypes() {
List<InsnArg> modArgs = new ArrayList<InsnArg>(argsList);
initArguments();
for (int i = 0; i < argsList.size(); i++) {
argsList.get(i).getTypedVar().setName(modArgs.get(i).getTypedVar().getName());
}
} }
// move to external class // move to external class
...@@ -394,7 +440,7 @@ public class MethodNode extends AttrNode implements ILoadable { ...@@ -394,7 +440,7 @@ public class MethodNode extends AttrNode implements ILoadable {
@Override @Override
public String toString() { public String toString() {
return mthInfo.getReturnType() return retType
+ " " + parentClass.getFullName() + "." + mthInfo.getName() + " " + parentClass.getFullName() + "." + mthInfo.getName()
+ "(" + Utils.listToString(mthInfo.getArgumentsTypes()) + ")"; + "(" + Utils.listToString(mthInfo.getArgumentsTypes()) + ")";
} }
......
...@@ -137,7 +137,7 @@ public class ConstInlinerVisitor extends AbstractVisitor { ...@@ -137,7 +137,7 @@ public class ConstInlinerVisitor extends AbstractVisitor {
case RETURN: case RETURN:
if (insn.getArgsCount() != 0) { if (insn.getArgsCount() != 0) {
insn.getArg(0).merge(mth.getMethodInfo().getReturnType()); insn.getArg(0).merge(mth.getReturnType());
} }
break; break;
......
...@@ -64,7 +64,7 @@ public class DotGraphVisitor extends AbstractVisitor { ...@@ -64,7 +64,7 @@ public class DotGraphVisitor extends AbstractVisitor {
dot.startLine("MethodNode[shape=record,label=\"{" dot.startLine("MethodNode[shape=record,label=\"{"
+ escape(mth.getAccessFlags().makeString()) + escape(mth.getAccessFlags().makeString())
+ escape(mth.getMethodInfo().getReturnType() + " " + escape(mth.getReturnType() + " "
+ mth.getParentClass().getFullName() + "." + mth.getName() + mth.getParentClass().getFullName() + "." + mth.getName()
+ "(" + Utils.listToString(mth.getArguments(true)) + ") ") + "(" + Utils.listToString(mth.getArguments(true)) + ") ")
+ (attrs.length() == 0 ? "" : " | " + attrs) + (attrs.length() == 0 ? "" : " | " + attrs)
......
...@@ -29,7 +29,7 @@ public class PostRegionVisitor extends AbstractVisitor { ...@@ -29,7 +29,7 @@ public class PostRegionVisitor extends AbstractVisitor {
* Remove useless return at end * Remove useless return at end
*/ */
private void removeReturn(MethodNode mth) { private void removeReturn(MethodNode mth) {
if (!mth.getMethodInfo().getReturnType().equals(ArgType.VOID)) if (!mth.getReturnType().equals(ArgType.VOID))
return; return;
if (!(mth.getRegion() instanceof Region)) if (!(mth.getRegion() instanceof Region))
......
...@@ -28,8 +28,13 @@ public class Utils { ...@@ -28,8 +28,13 @@ public class Utils {
} }
public static String escape(String str) { public static String escape(String str) {
return str.replace('.', '_').replace('/', '_').replace(';', '_') return str.replace('.', '_')
.replace('$', '_').replace("[]", "_A"); .replace('/', '_')
.replace(';', '_')
.replace('$', '_')
.replace('<', '_')
.replace('>', '_')
.replace("[]", "_A");
} }
public static String listToString(Iterable<?> list) { public static String listToString(Iterable<?> list) {
...@@ -46,6 +51,16 @@ public class Utils { ...@@ -46,6 +51,16 @@ public class Utils {
return str.toString(); return str.toString();
} }
public static String arrayToString(Object[] array) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < array.length; i++) {
if (i != 0)
sb.append(", ");
sb.append(array[i]);
}
return sb.toString();
}
public static String getStackTrace(Throwable throwable) { public static String getStackTrace(Throwable throwable) {
StringWriter sw = new StringWriter(); StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw, true); PrintWriter pw = new PrintWriter(sw, true);
......
...@@ -68,6 +68,19 @@ public class TestCF3 extends AbstractTest { ...@@ -68,6 +68,19 @@ public class TestCF3 extends AbstractTest {
return l1.size() == 0; return l1.size() == 0;
} }
public boolean testNestedLoops2(List<String> list) {
int i = 0;
int j = 0;
while (i < list.size()) {
String s = list.get(i);
while (j < s.length()) {
j++;
}
i++;
}
return j > 10;
}
public static boolean testLabeledBreakContinue() { public static boolean testLabeledBreakContinue() {
String searchMe = "Look for a substring in me"; String searchMe = "Look for a substring in me";
String substring = "sub"; String substring = "sub";
......
package jadx.samples;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TestGenerics extends AbstractTest {
private List<String> test1(Map<String, String> map) {
List<String> list = new ArrayList<String>();
String str = map.get("key");
list.add(str);
return list;
}
public void test2(Map<String, String> map, List<Object> list) {
String str = map.get("key");
list.add(str);
}
public void test3(List<Object> list, int a, float[] b, String[] c, String[][][] d) {
}
@Override
public boolean testRun() throws Exception {
assertTrue(test1(new HashMap<String, String>()) != null);
return true;
}
public static void main(String[] args) throws Exception {
new TestGenerics().testRun();
}
}
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