Added caching for the rest of the deodexerant commands

git-svn-id: https://smali.googlecode.com/svn/trunk@443 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
JesusFreke@JesusFreke.com 2009-09-08 23:04:44 +00:00
parent f0e3677538
commit 6fa95185b7

View File

@ -46,6 +46,9 @@ public class Deodexerant {
private final int port; private final int port;
private final HashMap<TypeIdItem, ClassData> vtableMap = new HashMap<TypeIdItem, ClassData>(); private final HashMap<TypeIdItem, ClassData> vtableMap = new HashMap<TypeIdItem, ClassData>();
private final HashMap<CommonSuperclassLookup, String> cachedCommonSuperclassLookup =
new HashMap<CommonSuperclassLookup, String>();
private InlineMethod[] inlineMethods;
public final DexFile dexFile; public final DexFile dexFile;
@ -59,30 +62,34 @@ public class Deodexerant {
this.port = port; this.port = port;
} }
public InlineMethod lookupInlineMethod(int inlineMethodIndex) { private void loadInlineMethods() {
connectIfNeeded(); List<String> responseLines = sendMultilineCommand("I");
String response = sendCommand("I " + inlineMethodIndex); inlineMethods = new InlineMethod[responseLines.size()];
for (int i=0; i<inlineMethods.length; i++) {
String response = responseLines.get(i);
if (!response.startsWith("inline: ")) {
throw new RuntimeException("Invalid response from deodexerant");
}
String[] parts = response.substring(8).split(" ");
if (parts.length != 2) {
throw new RuntimeException("Invalid response from deodexerant");
}
String methodType = parts[0];
InlineMethodType type; InlineMethodType type;
if (methodType.equals("virtual")) {
if (response.startsWith("virtual")) {
type = InlineMethodType.Virtual; type = InlineMethodType.Virtual;
} else if (response.startsWith("direct")) { } else if (methodType.equals("direct")) {
type = InlineMethodType.Direct; type = InlineMethodType.Direct;
} else if (response.startsWith("static")) { } else if (methodType.equals("static")) {
type = InlineMethodType.Static; type = InlineMethodType.Static;
} else { } else {
throw new RuntimeException("Invalid response from deodexerant"); throw new RuntimeException("Invalid response from deodexerant");
} }
String methodDescriptor = parts[1];
int colon = response.indexOf(':');
if (colon == -1) {
throw new RuntimeException("Invalid response from deodexerant");
}
String methodDescriptor = response.substring(colon+2);
Matcher m = fullMethodPattern.matcher(methodDescriptor); Matcher m = fullMethodPattern.matcher(methodDescriptor);
if (!m.matches()) { if (!m.matches()) {
@ -96,30 +103,36 @@ public class Deodexerant {
TypeIdItem classTypeItem = TypeIdItem.getInternedTypeIdItem(dexFile, classType); TypeIdItem classTypeItem = TypeIdItem.getInternedTypeIdItem(dexFile, classType);
if (classTypeItem == null) { if (classTypeItem == null) {
throw new RuntimeException("Could not find type " + classType + " in the dex file"); inlineMethods[i] = null;
continue;
} }
MethodIdItem method = parseAndResolveMethod(classTypeItem, methodName, methodParams, methodRet); MethodIdItem method = parseAndResolveMethod(classTypeItem, methodName, methodParams, methodRet);
if (method == null) { if (method == null) {
throw new RuntimeException("Could not find method " + methodDescriptor + " in the dex file"); inlineMethods[i] = null;
continue;
} }
return new InlineMethod(method, type); inlineMethods[i] = new InlineMethod(method, type);
}
}
public InlineMethod lookupInlineMethod(int inlineMethodIndex) {
if (inlineMethods == null) {
loadInlineMethods();
}
if (inlineMethodIndex >= inlineMethods.length) {
throw new RuntimeException("Invalid inline method index " + inlineMethodIndex + ". Too big.");
}
return inlineMethods[inlineMethodIndex];
} }
public FieldIdItem lookupField(TypeIdItem type, int fieldOffset) { public FieldIdItem lookupField(TypeIdItem type, int fieldOffset) {
connectIfNeeded(); ClassData classData = getClassData(type);
String response = sendCommand("F " + type.getTypeDescriptor() + " " + fieldOffset); return classData.lookupField(fieldOffset);
int colon = response.indexOf(':');
if (colon == -1) {
throw new RuntimeException("Invalid response from deodexerant");
}
String fieldDescriptor = response.substring(colon+2);
return parseAndLookupField(fieldDescriptor);
} }
private ClassData getClassData(TypeIdItem type) { private ClassData getClassData(TypeIdItem type) {
@ -168,15 +181,20 @@ public class Deodexerant {
} }
public String lookupCommonSuperclass(String typeDescriptor1, String typeDescriptor2) { public String lookupCommonSuperclass(String typeDescriptor1, String typeDescriptor2) {
connectIfNeeded(); CommonSuperclassLookup lookup = new CommonSuperclassLookup(typeDescriptor1, typeDescriptor2);
String result = cachedCommonSuperclassLookup.get(lookup);
if (result == null) {
String response = sendCommand("C " + typeDescriptor1 + " " + typeDescriptor2); String response = sendCommand("C " + typeDescriptor1 + " " + typeDescriptor2);
int colon = response.indexOf(':'); int colon = response.indexOf(':');
if (colon == -1) { if (colon == -1) {
return null; return null;
} }
return response.substring(colon+2); result = response.substring(colon+2);
cachedCommonSuperclassLookup.put(lookup, result);
}
return result;
} }
private String sendCommand(String cmd) { private String sendCommand(String cmd) {
@ -199,7 +217,7 @@ public class Deodexerant {
//The command is still just a single line, but we're expecting a multi-line //The command is still just a single line, but we're expecting a multi-line
//response. The repsonse is considered finished when a line starting with "err" //response. The repsonse is considered finished when a line starting with "err"
//or with "done" is encountered //or with "done" is encountered
private List<String> sendMultlineCommand(String cmd) { private List<String> sendMultilineCommand(String cmd) {
try { try {
connectIfNeeded(); connectIfNeeded();
@ -208,6 +226,9 @@ public class Deodexerant {
ArrayList<String> responseLines = new ArrayList<String>(); ArrayList<String> responseLines = new ArrayList<String>();
String response = in.readLine(); String response = in.readLine();
if (response == null) {
throw new RuntimeException("Error talking to deodexerant");
}
while (!response.startsWith("done")) while (!response.startsWith("done"))
{ {
if (response.startsWith("err")) { if (response.startsWith("err")) {
@ -344,23 +365,28 @@ public class Deodexerant {
private static final Pattern fullMethodPattern = Pattern.compile("(\\[*(?:L[^;]+;|[ZBSCIJFD]))->([^(]+)\\(([^)]*)\\)(.+)"); private static final Pattern fullMethodPattern = Pattern.compile("(\\[*(?:L[^;]+;|[ZBSCIJFD]))->([^(]+)\\(([^)]*)\\)(.+)");
private static final Pattern shortMethodPattern = Pattern.compile("([^(]+)\\(([^)]*)\\)(.+)"); private static final Pattern shortMethodPattern = Pattern.compile("([^(]+)\\(([^)]*)\\)(.+)");
//private static final Pattern fieldPattern = Pattern.compile("(\\[*L[^;]+;)->([^:]+):(.+)");
private static final Pattern fieldPattern = Pattern.compile("(\\[*L[^;]+;)->([^:]+):(.+)");
private FieldIdItem parseAndLookupField(String field) {
//expecting a string like Lsome/class;->someField:Lfield/type;
Matcher m = fieldPattern.matcher(field); private FieldIdItem parseAndResolveField(TypeIdItem classType, String field) {
if (!m.matches()) { //expecting a string like someField:Lfield/type;
throw new RuntimeException("invalid field string: " + field); String[] parts = field.split(":");
if (parts.length != 2) {
throw new RuntimeException("Invalid field descriptor " + field);
} }
String clazz = m.group(1); String fieldName = parts[0];
String fieldName = m.group(2); String fieldType = parts[1];
String fieldType = m.group(3);
TypeIdItem classType = TypeIdItem.getInternedTypeIdItem(dexFile, clazz);
StringIdItem fieldNameItem = StringIdItem.getInternedStringIdItem(dexFile, fieldName); StringIdItem fieldNameItem = StringIdItem.getInternedStringIdItem(dexFile, fieldName);
if (fieldNameItem == null) {
return null;
}
TypeIdItem fieldTypeItem = TypeIdItem.getInternedTypeIdItem(dexFile, fieldType); TypeIdItem fieldTypeItem = TypeIdItem.getInternedTypeIdItem(dexFile, fieldType);
if (fieldTypeItem == null) {
return null;
}
FieldIdItem fieldIdItem; FieldIdItem fieldIdItem;
@ -408,13 +434,17 @@ public class Deodexerant {
private class ClassData { private class ClassData {
public final TypeIdItem ClassType; private final TypeIdItem ClassType;
public boolean vtableLoaded; private boolean vtableLoaded = false;
public String[] MethodNames; private String[] methodNames;
public String[] MethodParams; private String[] methodParams;
public String[] MethodRets; private String[] methodRets;
public MethodIdItem[] resolvedMethods; private MethodIdItem[] resolvedMethods;
private boolean fieldsLoaded = false;
private SparseArray<String> instanceFields;
private SparseArray<FieldIdItem> resolvedFields;
public ClassData(TypeIdItem classType) { public ClassData(TypeIdItem classType) {
@ -430,18 +460,38 @@ public class Deodexerant {
throw new RuntimeException("Invalid vtable index " + index + ". Too large."); throw new RuntimeException("Invalid vtable index " + index + ". Too large.");
} }
if (resolvedMethods[index] == null) { if (resolvedMethods[index] == null) {
resolvedMethods[index] = parseAndResolveMethod(ClassType, MethodNames[index], MethodParams[index], resolvedMethods[index] = parseAndResolveMethod(ClassType, methodNames[index], methodParams[index],
MethodRets[index]); methodRets[index]);
} }
return resolvedMethods[index]; return resolvedMethods[index];
} }
private void loadvtable() { public FieldIdItem lookupField(int fieldOffset) {
List<String> responseLines = sendMultlineCommand("V " + ClassType.getTypeDescriptor()); if (!fieldsLoaded) {
loadFields();
}
MethodNames = new String[responseLines.size()]; FieldIdItem fieldIdItem = resolvedFields.get(fieldOffset);
MethodParams = new String[responseLines.size()]; if (fieldIdItem == null) {
MethodRets = new String[responseLines.size()]; String field = instanceFields.get(fieldOffset);
if (field == null) {
throw new RuntimeException("Invalid field offset " + fieldOffset);
}
fieldIdItem = parseAndResolveField(ClassType, field);
if (fieldIdItem != null) {
resolvedFields.put(fieldOffset, fieldIdItem);
}
}
return fieldIdItem;
}
private void loadvtable() {
List<String> responseLines = sendMultilineCommand("V " + ClassType.getTypeDescriptor());
methodNames = new String[responseLines.size()];
methodParams = new String[responseLines.size()];
methodRets = new String[responseLines.size()];
resolvedMethods = new MethodIdItem[responseLines.size()]; resolvedMethods = new MethodIdItem[responseLines.size()];
int index = 0; int index = 0;
@ -456,13 +506,62 @@ public class Deodexerant {
throw new RuntimeException("invalid method string: " + method); throw new RuntimeException("invalid method string: " + method);
} }
MethodNames[index] = m.group(1); methodNames[index] = m.group(1);
MethodParams[index] = m.group(2); methodParams[index] = m.group(2);
MethodRets[index] = m.group(3); methodRets[index] = m.group(3);
index++; index++;
} }
vtableLoaded = true; vtableLoaded = true;
} }
private void loadFields() {
List<String> responseLines = sendMultilineCommand("F " + ClassType.getTypeDescriptor());
instanceFields = new SparseArray<String>(responseLines.size());
resolvedFields = new SparseArray<FieldIdItem>(responseLines.size());
for (String fieldLine: responseLines) {
if (!fieldLine.startsWith("field: ")) {
throw new RuntimeException("Invalid response from deodexerant");
}
String field = fieldLine.substring(7);
String[] parts = field.split(" ");
if (parts.length != 2) {
throw new RuntimeException("Invalid response from deodexerant");
}
int fieldOffset = Integer.parseInt(parts[0]);
instanceFields.put(fieldOffset, parts[1]);
}
fieldsLoaded = true;
}
}
private static class CommonSuperclassLookup {
public final String Type1;
public final String Type2;
public CommonSuperclassLookup(String type1, String type2) {
this.Type1 = type1;
this.Type2 = type2;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CommonSuperclassLookup that = (CommonSuperclassLookup) o;
return Type1.equals(that.Type1) && Type2.equals(that.Type2);
}
@Override
public int hashCode() {
return Type1.hashCode() + 31 * Type2.hashCode();
}
} }
} }