Commit 6ddb71e2 authored by Skylot's avatar Skylot

core: add classpath for precise class types resolving

parent d0f120c3
......@@ -17,8 +17,9 @@ idea/
.gradle/
gradle.properties
*-tmp/
*.dex
*.jar
*.class
*.dump
*.log
......
......@@ -20,7 +20,6 @@ subprojects {
dependencies {
compile 'org.slf4j:slf4j-api:1.7.5'
compile 'ch.qos.logback:logback-classic:1.0.13'
testCompile 'junit:junit:4.11'
}
......
ext.jadxClasspath = 'clsp-data/android-4.3.jar'
dependencies {
compile 'com.google.android.tools:dx:1.7'
compile 'ch.qos.logback:logback-classic:1.0.13'
runtime files(jadxClasspath)
}
......@@ -64,8 +64,9 @@ public final class Decompiler {
}
public List<JavaClass> getClasses() {
List<JavaClass> classes = new ArrayList<JavaClass>(root.getClasses().size());
for (ClassNode classNode : root.getClasses()) {
List<ClassNode> classNodeList = root.getClasses(false);
List<JavaClass> classes = new ArrayList<JavaClass>(classNodeList.size());
for (ClassNode classNode : classNodeList) {
classes.add(new JavaClass(this, classNode));
}
return Collections.unmodifiableList(classes);
......@@ -101,7 +102,7 @@ public final class Decompiler {
LOG.info("processing ...");
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threadsCount);
for (ClassNode cls : root.getClasses()) {
for (ClassNode cls : root.getClasses(false)) {
if (cls.getCode() == null) {
ProcessClass job = new ProcessClass(cls, passList);
executor.execute(job);
......@@ -133,10 +134,9 @@ public final class Decompiler {
ClassInfo.clearCache();
ErrorsCounter.reset();
root = new RootNode(args, inputFiles);
root = new RootNode();
LOG.info("loading ...");
root.load();
root.init();
root.load(inputFiles);
}
void processClass(ClassNode cls) {
......
package jadx.core.clsp;
import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.DecodeException;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Classes list for import into classpath graph
*/
public class ClsSet {
private static final Logger LOG = LoggerFactory.getLogger(ClsSet.class);
private static final String CLST_EXTENSION = ".jcst";
private static final String CLST_FILENAME = "core" + CLST_EXTENSION;
private static final String CLST_PKG_PATH = ClsSet.class.getPackage().getName().replace('.', '/');
private static final String JADX_CLS_SET_HEADER = "jadx-cst";
private static final int VERSION = 1;
private NClass[] classes;
public void load(RootNode root) {
List<ClassNode> list = root.getClasses(true);
Map<String, NClass> names = new HashMap<String, NClass>(list.size());
int k = 0;
for (ClassNode cls : list) {
String clsRawName = cls.getRawName();
if (cls.getAccessFlags().isPublic()) {
NClass nClass = new NClass(clsRawName, k);
if (names.put(clsRawName, nClass) != null) {
throw new RuntimeException("Duplicate class: " + clsRawName);
}
k++;
} else {
names.put(clsRawName, null);
}
}
classes = new NClass[k];
k = 0;
for (ClassNode cls : list) {
if (cls.getAccessFlags().isPublic()) {
NClass nClass = getCls(cls.getRawName(), names);
nClass.setParents(makeParentsArray(cls, names));
classes[k] = nClass;
k++;
}
}
}
public static NClass[] makeParentsArray(ClassNode cls, Map<String, NClass> names) {
List<NClass> parents = new ArrayList<NClass>(1 + cls.getInterfaces().size());
ClassInfo superClass = cls.getSuperClass();
if (superClass != null) {
NClass c = getCls(superClass.getRawName(), names);
if (c != null) {
parents.add(c);
}
}
for (ClassInfo iface : cls.getInterfaces()) {
NClass c = getCls(iface.getRawName(), names);
if (c != null) {
parents.add(c);
}
}
return parents.toArray(new NClass[parents.size()]);
}
private static NClass getCls(String fullName, Map<String, NClass> names) {
NClass id = names.get(fullName);
if (id == null && !names.containsKey(fullName)) {
LOG.warn("Class not found: " + fullName);
}
return id;
}
void save(File output) throws IOException {
Utils.makeDirsForFile(output);
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(output));
String outputName = output.getName();
if (outputName.endsWith(CLST_EXTENSION)) {
save(outputStream);
} else if (outputName.endsWith(".jar")) {
ZipOutputStream out = new ZipOutputStream(outputStream);
try {
out.putNextEntry(new ZipEntry(CLST_PKG_PATH + "/" + CLST_FILENAME));
save(out);
out.closeEntry();
} finally {
out.close();
outputStream.close();
}
} else {
throw new RuntimeException("Unknown file format: " + outputName);
}
}
public void save(OutputStream output) throws IOException {
DataOutputStream out = new DataOutputStream(output);
out.writeBytes(JADX_CLS_SET_HEADER);
out.writeByte(VERSION);
LOG.info("Classes count: " + classes.length);
out.writeInt(classes.length);
for (NClass cls : classes) {
writeString(out, cls.getName());
}
for (NClass cls : classes) {
NClass[] parents = cls.getParents();
out.writeByte(parents.length);
for (NClass parent : parents) {
out.writeInt(parent.getId());
}
}
}
public void load() throws IOException, DecodeException {
InputStream input = getClass().getResourceAsStream(CLST_FILENAME);
if (input == null) {
throw new RuntimeException("Can't load classpath file: " + CLST_FILENAME);
}
load(input);
}
public void load(File input) throws IOException, DecodeException {
String name = input.getName();
InputStream inputStream = new FileInputStream(input);
if (name.endsWith(CLST_EXTENSION)) {
load(inputStream);
} else if (name.endsWith(".jar")) {
ZipInputStream in = new ZipInputStream(inputStream);
try {
ZipEntry entry = in.getNextEntry();
while (entry != null) {
if (entry.getName().endsWith(CLST_EXTENSION)) {
load(in);
}
entry = in.getNextEntry();
}
} finally {
in.close();
}
} else {
throw new RuntimeException("Unknown file format: " + name);
}
}
public void load(InputStream input) throws IOException, DecodeException {
DataInputStream in = new DataInputStream(input);
byte[] header = new byte[JADX_CLS_SET_HEADER.length()];
in.read(header);
int version = in.readByte();
if (!JADX_CLS_SET_HEADER.equals(new String(header)) || version != VERSION) {
throw new DecodeException("Wrong jadx class set header");
}
int count = in.readInt();
classes = new NClass[count];
for (int i = 0; i < count; i++) {
String name = readString(in);
classes[i] = new NClass(name, i);
}
for (int i = 0; i < count; i++) {
int pCount = in.readByte();
NClass[] parents = new NClass[pCount];
for (int j = 0; j < pCount; j++) {
parents[j] = classes[in.readInt()];
}
classes[i].setParents(parents);
}
}
private void writeString(DataOutputStream out, String name) throws IOException {
byte[] bytes = name.getBytes();
out.writeByte(bytes.length);
out.write(bytes);
}
private static String readString(DataInputStream in) throws IOException {
int len = in.readByte();
byte[] bytes = new byte[len];
int count = in.read(bytes);
while (count != len) {
int res = in.read(bytes, count, len - count);
if (res == -1) {
throw new IOException("String read error");
} else {
count += res;
}
}
return new String(bytes);
}
public NClass[] getClasses() {
return classes;
}
}
package jadx.core.clsp;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.utils.exceptions.DecodeException;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Classes hierarchy graph
*/
public class ClspGraph {
private static final Logger LOG = LoggerFactory.getLogger(ClspGraph.class);
private Map<String, NClass> nameMap;
private Map<String, Set<String>> ancestorCache = new WeakHashMap<String, Set<String>>();
public void load() throws IOException, DecodeException {
ClsSet set = new ClsSet();
set.load();
addClasspath(set);
}
public void addClasspath(ClsSet set) {
NClass[] arr = set.getClasses();
if (nameMap == null) {
nameMap = new HashMap<String, NClass>(arr.length);
for (NClass cls : arr) {
nameMap.put(cls.getName(), cls);
}
} else {
throw new RuntimeException("Classpath already loaded");
}
}
public void addApp(List<ClassNode> classes) {
if (nameMap == null) {
throw new RuntimeException("Classpath must be loaded first");
}
int size = classes.size();
NClass[] nClasses = new NClass[size];
for (int i = 0; i < size; i++) {
ClassNode cls = classes.get(i);
NClass nClass = new NClass(cls.getRawName(), -1);
nClasses[i] = nClass;
nameMap.put(cls.getRawName(), nClass);
}
for (int i = 0; i < size; i++) {
nClasses[i].setParents(ClsSet.makeParentsArray(classes.get(i), nameMap));
}
}
public boolean isImplements(String clsName, String implClsName) {
Set<String> anc = getAncestors(clsName);
return anc.contains(implClsName);
}
public String getCommonAncestor(String clsName, String implClsName) {
if (isImplements(clsName, implClsName)) {
return implClsName;
}
Set<String> anc = getAncestors(clsName);
NClass cls = nameMap.get(implClsName);
if(cls != null) {
return searchCommonParent(anc, cls);
} else {
LOG.debug("Missing class: {}", implClsName);
return null;
}
}
private String searchCommonParent(Set<String> anc, NClass cls) {
for (NClass p : cls.getParents()) {
String name = p.getName();
if (anc.contains(name)) {
return name;
} else {
String r = searchCommonParent(anc, p);
if (r != null)
return r;
}
}
return null;
}
private Set<String> getAncestors(String clsName) {
Set<String> result = ancestorCache.get(clsName);
if (result == null) {
result = new HashSet<String>();
ancestorCache.put(clsName, result);
NClass cls = nameMap.get(clsName);
if(cls != null) {
addAncestorsNames(cls, result);
} else {
LOG.debug("Missing class: {}", clsName);
}
}
return result;
}
private void addAncestorsNames(NClass cls, Set<String> result) {
result.add(cls.getName());
for (NClass p : cls.getParents()) {
addAncestorsNames(p, result);
}
}
}
package jadx.core.clsp;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.files.InputFile;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Utility class for convert dex or jar to jadx classes set (.jcst)
*/
public class ConvertToClsSet {
private static final Logger LOG = LoggerFactory.getLogger(ConvertToClsSet.class);
public static void usage() {
LOG.info("<output .jcst or .jar file> <several input dex or jar files> ");
}
public static void main(String[] args) throws IOException, DecodeException {
if (args.length < 2) {
usage();
System.exit(1);
}
File output = new File(args[0]);
List<InputFile> inputFiles = new ArrayList<InputFile>(args.length - 1);
for (int i = 1; i < args.length; i++) {
File f = new File(args[i]);
if (f.isDirectory()) {
addFilesFromDirectory(f, inputFiles);
} else {
inputFiles.add(new InputFile(f));
}
}
for (InputFile inputFile : inputFiles) {
LOG.info("Loaded: " + inputFile.getFile());
}
RootNode root = new RootNode();
root.load(inputFiles);
ClsSet set = new ClsSet();
set.load(root);
set.save(output);
LOG.info("Output: " + output);
LOG.info("done");
}
private static void addFilesFromDirectory(File dir, List<InputFile> inputFiles) throws IOException, DecodeException {
File[] files = dir.listFiles();
for (File file : files) {
if (file.isDirectory()) {
addFilesFromDirectory(file, inputFiles);
}
if (file.getName().endsWith(".dex")) {
inputFiles.add(new InputFile(file));
}
}
}
}
package jadx.core.clsp;
/**
* Class node in classpath graph
*/
public class NClass {
private final String name;
private NClass[] parents;
private int id;
public NClass(String name, int id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public NClass[] getParents() {
return parents;
}
public void setParents(NClass[] parents) {
this.parents = parents;
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NClass nClass = (NClass) o;
if (!name.equals(nClass.name)) return false;
return true;
}
@Override
public String toString() {
return name;
}
}
package jadx.core.codegen;
import jadx.core.dex.attributes.LineAttrNode;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.Utils;
import java.io.File;
import java.io.PrintWriter;
......@@ -214,7 +214,7 @@ public class CodeWriter {
PrintWriter out = null;
try {
makeDirsForFile(file);
Utils.makeDirsForFile(file);
out = new PrintWriter(file, "UTF-8");
String code = buf.toString();
code = removeFirstEmptyLine(code);
......@@ -227,13 +227,4 @@ public class CodeWriter {
}
}
private void makeDirsForFile(File file) {
File dir = file.getParentFile();
if (!dir.exists()) {
// if directory already created in other thread mkdirs will return false,
// so check dir existence again
if (!dir.mkdirs() && !dir.exists())
throw new JadxRuntimeException("Can't create directory " + dir);
}
}
}
......@@ -172,7 +172,11 @@ public class MethodGen {
else
return name;
} else {
return base + "_" + Utils.escape(TypeGen.translate(classGen, arg.getType()));
ArgType type = arg.getType();
if (type.isPrimitive())
return base + type.getPrimitiveType().getShortName().toLowerCase();
else
return base + "_" + Utils.escape(TypeGen.translate(classGen, arg.getType()));
}
}
}
......
......@@ -105,6 +105,10 @@ public final class ClassInfo {
return name;
}
public String getRawName() {
return type.getObject();
}
public String getPackage() {
return pkg;
}
......
package jadx.core.dex.instructions.args;
import jadx.core.Consts;
import jadx.core.clsp.ClspGraph;
import jadx.core.utils.Utils;
import java.util.ArrayList;
......@@ -43,6 +44,16 @@ public abstract class ArgType {
protected int hash;
private static ClspGraph clsp;
public static ClspGraph getClsp() {
return clsp;
}
public static void setClsp(ClspGraph clsp) {
ArgType.clsp = clsp;
}
private static ArgType primitive(PrimitiveType stype) {
return new PrimitiveArg(stype);
}
......@@ -344,35 +355,30 @@ public abstract class ArgType {
}
}
} else {
if(a.isGenericType())
return a;
if(b.isGenericType())
return b;
if (a.isGenericType()) return a;
if (b.isGenericType()) return b;
if (a.isObject() && b.isObject()) {
if (a.getObject().equals(b.getObject())) {
if (a.getGenericTypes() != null)
return a;
else
return b;
} else if (a.getObject().equals(OBJECT.getObject()))
String aObj = a.getObject();
String bObj = b.getObject();
if (aObj.equals(bObj)) {
return (a.getGenericTypes() != null ? a : b);
} else if (aObj.equals(Consts.CLASS_OBJECT)) {
return b;
else if (b.getObject().equals(OBJECT.getObject()))
} else if (bObj.equals(Consts.CLASS_OBJECT)) {
return a;
else
} else {
// different objects
return null;
String obj = clsp.getCommonAncestor(aObj, bObj);
return (obj == null ? null : object(obj));
}
}
if (a.isArray() && b.isArray()) {
ArgType res = merge(a.getArrayElement(), b.getArrayElement());
return (res == null ? null : ArgType.array(res));
}
if (a.isPrimitive() && b.isPrimitive()) {
if (a.getRegCount() == b.getRegCount())
// return primitive(PrimitiveType.getWidest(a.getPrimitiveType(), b.getPrimitiveType()));
return primitive(PrimitiveType.getSmaller(a.getPrimitiveType(), b.getPrimitiveType()));
if (a.isPrimitive() && b.isPrimitive() && a.getRegCount() == b.getRegCount()) {
return primitive(PrimitiveType.getSmaller(a.getPrimitiveType(), b.getPrimitiveType()));
}
}
return null;
......
......@@ -240,7 +240,7 @@ public class ClassNode extends LineAttrNode implements ILoadable {
public FieldNode getConstField(Object o) {
FieldNode field = constFields.get(o);
if(field == null)
if (field == null)
field = dex.getConstFields().get(o);
return field;
}
......@@ -329,6 +329,10 @@ public class ClassNode extends LineAttrNode implements ILoadable {
return clsInfo.getPackage();
}
public String getRawName() {
return clsInfo.getRawName();
}
public void setCode(CodeWriter code) {
this.code = code;
}
......
package jadx.core.dex.nodes;
import jadx.api.IJadxArgs;
import jadx.core.clsp.ClspGraph;
import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.files.InputFile;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RootNode {
private static final Logger LOG = LoggerFactory.getLogger(RootNode.class);
private final IJadxArgs args;
private final List<InputFile> dexFiles;
private List<DexNode> dexNodes;
private final List<ClassNode> classes = new ArrayList<ClassNode>();
private final Map<String, ClassNode> names = new HashMap<String, ClassNode>();
private List<DexNode> dexNodes;
private ClspGraph clsp;
public RootNode(IJadxArgs args, List<InputFile> dexFiles) {
this.args = args;
this.dexFiles =dexFiles;
}
public void load() throws DecodeException {
public void load(List<InputFile> dexFiles) throws DecodeException {
dexNodes = new ArrayList<DexNode>(dexFiles.size());
for (InputFile dex : dexFiles) {
DexNode dexNode;
......@@ -39,37 +28,63 @@ public class RootNode {
}
dexNodes.add(dexNode);
}
for (DexNode dexNode : dexNodes)
for (DexNode dexNode : dexNodes) {
dexNode.loadClasses();
}
List<ClassNode> classes = new ArrayList<ClassNode>();
for (DexNode dexNode : dexNodes) {
for (ClassNode cls : dexNode.getClasses())
for (ClassNode cls : dexNode.getClasses()) {
names.put(cls.getFullName(), cls);
}
classes.addAll(dexNode.getClasses());
}
try {
initClassPath(classes);
} catch (IOException e) {
throw new DecodeException("Error loading classpath", e);
}
initInnerClasses(classes);
}
private void initClassPath(List<ClassNode> classes) throws IOException, DecodeException {
clsp = new ClspGraph();
clsp.load();
clsp.addApp(classes);
ArgType.setClsp(clsp);
}
public void init() {
private void initInnerClasses(List<ClassNode> classes) {
// move inner classes
List<ClassNode> inner = new ArrayList<ClassNode>();
for (ClassNode cls : getClasses()) {
for (ClassNode cls : classes) {
if (cls.getClassInfo().isInner())
inner.add(cls);
}
for (ClassNode cls : inner) {
ClassNode parent = resolveClass(cls.getClassInfo().getParentClass());
if (parent == null) {
cls.getClassInfo().notInner();
} else {
parent.addInnerClass(cls);
getClasses().remove(cls);
}
}
}
public List<ClassNode> getClasses() {
public List<ClassNode> getClasses(boolean includeInner) {
List<ClassNode> classes = new ArrayList<ClassNode>();
for (DexNode dexNode : dexNodes) {
for (ClassNode cls : dexNode.getClasses()) {
if (includeInner) {
classes.add(cls);
} else {
if (!cls.getClassInfo().isInner())
classes.add(cls);
}
}
}
return classes;
}
......@@ -82,12 +97,4 @@ public class RootNode {
ClassNode rCls = searchClassByName(fullName);
return rCls;
}
public List<DexNode> getDexNodes() {
return dexNodes;
}
public IJadxArgs getJadxArgs() {
return args;
}
}
package jadx.core.utils;
import jadx.core.utils.exceptions.JadxRuntimeException;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
......@@ -138,4 +141,14 @@ public class Utils {
}
return "dev";
}
public static void makeDirsForFile(File file) {
File dir = file.getParentFile();
if (!dir.exists()) {
// if directory already created in other thread mkdirs will return false,
// so check dir existence again
if (!dir.mkdirs() && !dir.exists())
throw new JadxRuntimeException("Can't create directory " + dir);
}
}
}
package jadx.tests;
import jadx.core.clsp.ClspGraph;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.PrimitiveType;
import jadx.core.utils.exceptions.DecodeException;
import junit.framework.TestCase;
import java.io.IOException;
import static jadx.core.dex.instructions.args.ArgType.BOOLEAN;
import static jadx.core.dex.instructions.args.ArgType.CHAR;
import static jadx.core.dex.instructions.args.ArgType.INT;
......@@ -12,12 +16,21 @@ import static jadx.core.dex.instructions.args.ArgType.NARROW;
import static jadx.core.dex.instructions.args.ArgType.OBJECT;
import static jadx.core.dex.instructions.args.ArgType.UNKNOWN;
import static jadx.core.dex.instructions.args.ArgType.UNKNOWN_OBJECT;
import static jadx.core.dex.instructions.args.ArgType.genericType;
import static jadx.core.dex.instructions.args.ArgType.object;
import static jadx.core.dex.instructions.args.ArgType.unknown;
public class TypeMergeTest extends TestCase {
public void testMerge() {
private void initClsp() throws IOException, DecodeException {
ClspGraph clsp = new ClspGraph();
clsp.load();
ArgType.setClsp(clsp);
}
public void testMerge() throws IOException, DecodeException {
initClsp();
first(INT, INT);
first(BOOLEAN, INT);
reject(INT, LONG);
......@@ -27,21 +40,33 @@ public class TypeMergeTest extends TestCase {
first(INT, NARROW);
first(CHAR, INT);
merge(unknown(PrimitiveType.INT, PrimitiveType.BOOLEAN, PrimitiveType.FLOAT),
check(unknown(PrimitiveType.INT, PrimitiveType.BOOLEAN, PrimitiveType.FLOAT),
unknown(PrimitiveType.INT, PrimitiveType.BOOLEAN),
unknown(PrimitiveType.INT, PrimitiveType.BOOLEAN));
merge(unknown(PrimitiveType.INT, PrimitiveType.FLOAT),
check(unknown(PrimitiveType.INT, PrimitiveType.FLOAT),
unknown(PrimitiveType.INT, PrimitiveType.BOOLEAN),
INT);
merge(unknown(PrimitiveType.INT, PrimitiveType.OBJECT),
check(unknown(PrimitiveType.INT, PrimitiveType.OBJECT),
unknown(PrimitiveType.OBJECT, PrimitiveType.ARRAY),
unknown(PrimitiveType.OBJECT));
first(object("Lsomeobj;"), object("Lsomeobj;"));
merge(object("Lsomeobj;"), object("Lotherobj;"), null);
first(object("Lsomeobj;"), OBJECT);
ArgType objExc = object("java.lang.Exception");
ArgType objThr = object("java.lang.Throwable");
ArgType objIO = object("java.io.IOException");
ArgType objArr = object("java.lang.ArrayIndexOutOfBoundsException");
ArgType objList = object("java.util.List");
first(objExc, objExc);
check(objExc, objList, OBJECT);
first(objExc, OBJECT);
check(objExc, objThr, objThr);
check(objIO, objArr, objExc);
ArgType generic = genericType("T");
first(generic, objExc);
}
private void first(ArgType t1, ArgType t2) {
......
......@@ -6,6 +6,7 @@ dependencies {
compile(project(":jadx-core"))
compile(project(":jadx-cli"))
compile 'com.fifesoft:rsyntaxtextarea:2.0.7'
compile 'ch.qos.logback:logback-classic:1.0.13'
}
startScripts {
......
......@@ -37,6 +37,9 @@ public class Utils {
}
public static String typeStr(ArgType type) {
if (type == null) {
return "null";
}
if (type.isObject()) {
String cls = type.getObject();
int dot = cls.lastIndexOf('.');
......
......@@ -168,6 +168,20 @@ public class TestTryCatch extends AbstractTest {
return b;
}
public int catchInLoop(int i, int j) {
while (true) {
try {
while (i < j)
i = j++ / i;
} catch (RuntimeException e) {
i = 10;
continue;
}
break;
}
return j;
}
@Override
public boolean testRun() throws Exception {
Object obj = new Object();
......@@ -180,8 +194,8 @@ public class TestTryCatch extends AbstractTest {
assertTrue(test6(obj));
assertTrue(test7());
assertTrue(testSynchronize(obj) == true);
assertTrue(testSynchronize("str") == false);
assertTrue(testSynchronize(obj));
assertFalse(testSynchronize("str"));
assertTrue(testSynchronize2("str"));
assertTrue(testSynchronize3());
......@@ -191,6 +205,10 @@ public class TestTryCatch extends AbstractTest {
assertTrue(test8a("a"));
assertTrue(test8a(null));
assertEquals(catchInLoop(1, 0), 0);
assertEquals(catchInLoop(0, 1), 2);
assertEquals(catchInLoop(788, 100), 100);
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