core: add classpath for precise class types resolving
This commit is contained in:
+2
-1
@@ -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'
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
@@ -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) {
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1,34 +1,23 @@
|
||||
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)
|
||||
dexNode.loadClasses();
|
||||
|
||||
for (DexNode dexNode : dexNodes) {
|
||||
for (ClassNode cls : dexNode.getClasses())
|
||||
dexNode.loadClasses();
|
||||
}
|
||||
|
||||
List<ClassNode> classes = new ArrayList<ClassNode>();
|
||||
for (DexNode dexNode : dexNodes) {
|
||||
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);
|
||||
}
|
||||
|
||||
public void init() {
|
||||
private void initClassPath(List<ClassNode> classes) throws IOException, DecodeException {
|
||||
clsp = new ClspGraph();
|
||||
clsp.load();
|
||||
clsp.addApp(classes);
|
||||
|
||||
ArgType.setClsp(clsp);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user