From b2ce899471be1c136aa13d502e885585fa59d460 Mon Sep 17 00:00:00 2001 From: Izzat Bahadirov Date: Fri, 19 Apr 2013 00:29:30 -0400 Subject: [PATCH 01/25] Getting instance fields by offset and methods by vtable index. - Dump utilities --- .../jf/dexlib/Code/Analysis/ClassPath.java | 10 +- .../jf/dexlib/Code/Analysis/DumpFields.java | 160 ++++++++ .../jf/dexlib/Code/Analysis/DumpVtables.java | 159 ++++++++ .../org/jf/dexlib2/analysis/ArrayProto.java | 8 +- .../org/jf/dexlib2/analysis/ClassProto.java | 370 +++++++++++++++++- .../org/jf/dexlib2/analysis/DumpFields.java | 170 ++++++++ .../org/jf/dexlib2/analysis/DumpVtables.java | 172 ++++++++ .../dexlib2/dexbacked/raw/OdexHeaderItem.java | 2 +- 8 files changed, 1041 insertions(+), 10 deletions(-) create mode 100644 dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DumpFields.java create mode 100644 dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DumpVtables.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpFields.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpVtables.java diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java index 9fdefe75..a1a93d6c 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java @@ -724,6 +724,14 @@ public class ClassPath { return superclass; } + VirtualMethod[] getVtable() { + return vtable; + } + + SparseArray getInstanceFields() { + return instanceFields; + } + public int getClassDepth() { return classDepth; } @@ -1207,7 +1215,7 @@ public class ClassPath { } } - private static class VirtualMethod { + static class VirtualMethod { public String containingClass; public String method; public boolean isPackagePrivate; diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DumpFields.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DumpFields.java new file mode 100644 index 00000000..3f6ccf23 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DumpFields.java @@ -0,0 +1,160 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib.Code.Analysis; + +import com.google.common.base.Splitter; +import com.google.common.collect.Lists; +import org.apache.commons.cli.*; +import org.jf.dexlib.ClassDefItem; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.SparseArray; +import org.jf.util.ConsoleUtil; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; + +public class DumpFields { + private static final Options options; + + static { + options = new Options(); + buildOptions(); + } + + public static void main(String[] args) { + CommandLineParser parser = new PosixParser(); + CommandLine commandLine; + + try { + commandLine = parser.parse(options, args); + } catch (ParseException ex) { + usage(); + return; + } + + String[] remainingArgs = commandLine.getArgs(); + + Option[] parsedOptions = commandLine.getOptions(); + ArrayList bootClassPathDirs = Lists.newArrayList(); + String outFile = "fields.txt"; + + for (int i=0; i fields = classDef.getInstanceFields(); + String className = "Class " + classDef.getClassType() + " : " + fields.size() + " instance fields\n"; + outStream.write(className.getBytes()); + for (int i=0;i"); + } + + private static void buildOptions() { + Option classPathDirOption = OptionBuilder.withLongOpt("bootclasspath-dir") + .withDescription("the base folder to look for the bootclasspath files in. Defaults to the current " + + "directory") + .hasArg() + .withArgName("DIR") + .create("d"); + + Option outputFileOption = OptionBuilder.withLongOpt("out-file") + .withDescription("output file") + .hasArg() + .withArgName("FILE") + .create("o"); + + options.addOption(classPathDirOption); + options.addOption(outputFileOption); + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DumpVtables.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DumpVtables.java new file mode 100644 index 00000000..5aab9edc --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DumpVtables.java @@ -0,0 +1,159 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib.Code.Analysis; + +import com.google.common.base.Splitter; +import com.google.common.collect.Lists; +import org.apache.commons.cli.*; +import org.jf.dexlib.ClassDefItem; +import org.jf.dexlib.DexFile; +import org.jf.util.ConsoleUtil; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; + +public class DumpVtables { + private static final Options options; + + static { + options = new Options(); + buildOptions(); + } + + public static void main(String[] args) { + CommandLineParser parser = new PosixParser(); + CommandLine commandLine; + + try { + commandLine = parser.parse(options, args); + } catch (ParseException ex) { + usage(); + return; + } + + String[] remainingArgs = commandLine.getArgs(); + + Option[] parsedOptions = commandLine.getOptions(); + ArrayList bootClassPathDirs = Lists.newArrayList(); + String outFile = "vtables.txt"; + + for (int i=0; i" + methods[i].method + "\n"; + outStream.write(method.getBytes()); + } + outStream.write("\n".getBytes()); + } + outStream.close(); + } catch (IOException ex) { + System.out.println("IOException thrown when trying to open a dex file or write out vtables: " + ex); + } + + } + + /** + * Prints the usage message. + */ + private static void usage() { + int consoleWidth = ConsoleUtil.getConsoleWidth(); + if (consoleWidth <= 0) { + consoleWidth = 80; + } + + System.out.println("java -cp baksmali.jar org.jf.dexlib.Code.Analysis.DumpVtables -d path/to/jar/files "); + } + + private static void buildOptions() { + Option classPathDirOption = OptionBuilder.withLongOpt("bootclasspath-dir") + .withDescription("the base folder to look for the bootclasspath files in. Defaults to the current " + + "directory") + .hasArg() + .withArgName("DIR") + .create("d"); + + Option outputFileOption = OptionBuilder.withLongOpt("out-file") + .withDescription("output file") + .hasArg() + .withArgName("FILE") + .create("o"); + + options.addOption(classPathDirOption); + options.addOption(outputFileOption); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ArrayProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ArrayProto.java index 6172a5c9..8fcfc8c5 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ArrayProto.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ArrayProto.java @@ -34,6 +34,7 @@ package org.jf.dexlib2.analysis; import com.google.common.base.Strings; import org.jf.dexlib2.iface.reference.FieldReference; import org.jf.dexlib2.iface.reference.MethodReference; +import org.jf.dexlib2.immutable.reference.ImmutableFieldReference; import org.jf.dexlib2.util.TypeUtils; import org.jf.util.ExceptionWithContext; @@ -151,12 +152,15 @@ public class ArrayProto implements TypeProto { @Override @Nullable public FieldReference getFieldByOffset(int fieldOffset) { - return classPath.getClass("Ljava/lang/Array;").getFieldByOffset(fieldOffset); + if (fieldOffset==8) { + return new ImmutableFieldReference(getType(), "length", "int"); + } + return null; } @Override @Nullable public MethodReference getMethodByVtableIndex(int vtableIndex) { - return classPath.getClass("Ljava/lang/Array;").getMethodByVtableIndex(vtableIndex); + return classPath.getClass("Ljava/lang/Object;").getMethodByVtableIndex(vtableIndex); } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java index cb3dca38..1bc45166 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java @@ -33,18 +33,23 @@ package org.jf.dexlib2.analysis; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import com.google.common.collect.Sets; import org.jf.dexlib2.AccessFlags; import org.jf.dexlib2.analysis.util.TypeProtoUtils; import org.jf.dexlib2.iface.ClassDef; +import org.jf.dexlib2.iface.Field; +import org.jf.dexlib2.iface.Method; import org.jf.dexlib2.iface.reference.FieldReference; import org.jf.dexlib2.iface.reference.MethodReference; +import org.jf.dexlib2.util.FieldUtil; +import org.jf.dexlib2.util.MethodUtil; import org.jf.util.ExceptionWithContext; +import org.jf.util.SparseArray; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.List; -import java.util.Set; +import java.util.*; /** * A class "prototype". This contains things like the interfaces, the superclass, the vtable and the instance fields @@ -55,6 +60,9 @@ public class ClassProto implements TypeProto { @Nonnull protected final String type; @Nullable protected ClassDef classDef; @Nullable protected Set interfaces; + @Nullable protected LinkedHashMap interfaceTable; + @Nullable protected Method[] vtable; + @Nullable protected SparseArray instanceFields; protected boolean interfacesFullyResolved = true; public ClassProto(@Nonnull ClassPath classPath, @Nonnull String type) { @@ -77,6 +85,27 @@ public class ClassProto implements TypeProto { return classDef; } + Method[] getVtable() { + if (vtable == null) { + vtable = loadVtable(); + } + return vtable; + } + + SparseArray getInstanceFields() { + if (instanceFields == null) { + instanceFields = loadFields(); + } + return instanceFields; + } + + LinkedHashMap getInterfaceTable() { + if (interfaceTable == null) { + interfaceTable = loadInterfaceTable(); + } + return interfaceTable; + } + /** * Returns true if this class is an interface. * @@ -274,14 +303,343 @@ public class ClassProto implements TypeProto { @Override @Nullable public FieldReference getFieldByOffset(int fieldOffset) { - // TODO: implement this - return null; + if (getInstanceFields() == null) { + return null; + } + return getInstanceFields().get(fieldOffset); } @Override @Nullable public MethodReference getMethodByVtableIndex(int vtableIndex) { - // TODO: implement this - return null; + if (getVtable() == null + || vtableIndex < 0 + || vtableIndex >= getVtable().length) { + return null; + } + return getVtable()[vtableIndex]; + } + + private SparseArray loadFields() { + //This is a bit of an "involved" operation. We need to follow the same algorithm that dalvik uses to + //arrange fields, so that we end up with the same field offsets (which is needed for deodexing). + //See mydroid/dalvik/vm/oo/Class.c - computeFieldOffsets() + + final byte REFERENCE = 0; + final byte WIDE = 1; + final byte OTHER = 2; + + ArrayList loadedFields = getInstanceFields(getClassDef()); + Field[] fields = new Field[loadedFields.size()]; + //the "type" for each field in fields. 0=reference,1=wide,2=other + byte[] fieldTypes = new byte[fields.length]; + for (int i=0;i front) { + if (fieldTypes[back] == REFERENCE) { + swap(fieldTypes, fields, front, back--); + break; + } + back--; + } + } + + if (fieldTypes[front] != REFERENCE) { + break; + } + } + + int startFieldOffset = 8; + String superclassType = getSuperclass(); + ClassProto superclass = null; + if (superclassType != null) { + superclass = (ClassProto) classPath.getClass(superclassType); + if (superclass != null) { + startFieldOffset = superclass.getNextFieldOffset(); + } + } + + int fieldIndexMod; + if ((startFieldOffset % 8) == 0) { + fieldIndexMod = 0; + } else { + fieldIndexMod = 1; + } + + //next, we need to group all the wide fields after the reference fields. But the wide fields have to be + //8-byte aligned. If we're on an odd field index, we need to insert a 32-bit field. If the next field + //is already a 32-bit field, use that. Otherwise, find the first 32-bit field from the end and swap it in. + //If there are no 32-bit fields, do nothing for now. We'll add padding when calculating the field offsets + if (front < fields.length && (front % 2) != fieldIndexMod) { + if (fieldTypes[front] == WIDE) { + //we need to swap in a 32-bit field, so the wide fields will be correctly aligned + back = fields.length - 1; + while (back > front) { + if (fieldTypes[back] == OTHER) { + swap(fieldTypes, fields, front++, back); + break; + } + back--; + } + } else { + //there's already a 32-bit field here that we can use + front++; + } + } + + //do the swap thing for wide fields + back = fields.length - 1; + for (; front front) { + if (fieldTypes[back] == WIDE) { + swap(fieldTypes, fields, front, back--); + break; + } + back--; + } + } + + if (fieldTypes[front] != WIDE) { + break; + } + } + + int superFieldCount = 0; + if (superclass != null) { + superFieldCount = superclass.instanceFields.size(); + } + + //now the fields are in the correct order. Add them to the SparseArray and lookup, and calculate the offsets + int totalFieldCount = superFieldCount + fields.length; + SparseArray instanceFields = new SparseArray(totalFieldCount); + + int fieldOffset; + + if (superclass != null && superFieldCount > 0) { + for (int i=0; i getInstanceFields(ClassDef classDef) { + ArrayList instanceFields = Lists.newArrayList(); + for (Field field: classDef.getFields()) { + if (!FieldUtil.isStatic(field)) { + instanceFields.add(field); + } + } + return instanceFields; + } + + private byte getFieldType(String fieldType) { + switch (fieldType.charAt(0)) { + case '[': + case 'L': + return 0; //REFERENCE + case 'J': + case 'D': + return 1; //WIDE + default: + return 2; //OTHER + } + } + + private void swap(byte[] fieldTypes, FieldReference[] fields, int position1, int position2) { + byte tempType = fieldTypes[position1]; + fieldTypes[position1] = fieldTypes[position2]; + fieldTypes[position2] = tempType; + + FieldReference tempField = fields[position1]; + fields[position1] = fields[position2]; + fields[position2] = tempField; + } + + private int getNextFieldOffset() { + SparseArray instanceFields = getInstanceFields(); + if (instanceFields == null || instanceFields.size() == 0) { + return 8; + } + + int lastItemIndex = instanceFields.size()-1; + int fieldOffset = instanceFields.keyAt(lastItemIndex); + FieldReference lastField = instanceFields.valueAt(lastItemIndex); + + switch (lastField.getType().charAt(0)) { + case 'J': + case 'D': + return fieldOffset + 8; + default: + return fieldOffset + 4; + } + } + + //TODO: check the case when we have a package private method that overrides an interface method + private Method[] loadVtable() { + //TODO: it might be useful to keep track of which class's implementation is used for each virtual method. In other words, associate the implementing class type with each vtable entry + List virtualMethodList = Lists.newLinkedList(); + + //copy the virtual methods from the superclass + String superclassType = getSuperclass(); + if (superclassType != null) { + ClassProto superclass = (ClassProto) classPath.getClass(superclassType); + for (int i=0; i interfaceTable = loadInterfaceTable(); + if (interfaceTable != null) { + for (ClassDef interfaceDef: interfaceTable.values()) { + addToVtable(getVirtualMethods(interfaceDef), virtualMethodList); + } + } + } + + Method[] vtable = new Method[virtualMethodList.size()]; + for (int i=0; i loadInterfaceTable() { + LinkedHashMap interfaceTable = Maps.newLinkedHashMap(); + + for (String interfaceType: getClassDef().getInterfaces()) { + if (!interfaceTable.containsKey(interfaceType)) { + ClassDef interfaceDef; + try { + interfaceDef = classPath.getClassDef(interfaceType); + } catch (Exception ex) { + throw ExceptionWithContext.withContext(ex, + String.format("Could not find interface %s", interfaceType)); + } + interfaceTable.put(interfaceType, interfaceDef); + + ClassProto interfaceProto = (ClassProto) classPath.getClass(interfaceType); + for (ClassDef superInterface: interfaceProto.getInterfaceTable().values()) { + if (!interfaceTable.containsKey(superInterface.getType())) { + interfaceTable.put(superInterface.getType(), superInterface); + } + } + } + } + + return interfaceTable; + } + + private LinkedList getVirtualMethods(ClassDef classDef) { + LinkedList virtualMethods = Lists.newLinkedList(); + for (Method method: classDef.getMethods()) { + if (!MethodUtil.isDirect(method)) { + virtualMethods.add(method); + } + } + + return virtualMethods; + } + + private void addToVtable(List localMethods, List vtable) { + for (Method virtualMethod: localMethods) { + boolean found = false; + for (int i=0; i bootClassPathDirs = Lists.newArrayList(); + String outFile = "fields.txt"; + int apiLevel = 15; + + for (int i=0; i bootClassPaths = Splitter.on(":").split("core.jar:ext.jar:framework.jar:android.policy.jar:services.jar"); + ClassPath classPath = ClassPath.fromClassPath(bootClassPathDirs, bootClassPaths, dexFile, apiLevel); + FileOutputStream outStream = new FileOutputStream(outFile); + + for (ClassDef classDef: dexFile.getClasses()) { + ClassProto classProto = (ClassProto) classPath.getClass(classDef); + SparseArray fields = classProto.getInstanceFields(); + String className = "Class " + classDef.getType() + " : " + fields.size() + " instance fields\n"; + outStream.write(className.getBytes()); + for (int i=0;i"); + } + + private static void buildOptions() { + Option classPathDirOption = OptionBuilder.withLongOpt("bootclasspath-dir") + .withDescription("the base folder to look for the bootclasspath files in. Defaults to the current " + + "directory") + .hasArg() + .withArgName("DIR") + .create("d"); + + Option outputFileOption = OptionBuilder.withLongOpt("out-file") + .withDescription("output file") + .hasArg() + .withArgName("FILE") + .create("o"); + + Option apiLevelOption = OptionBuilder.withLongOpt("api-level") + .withDescription("The numeric api-level of the file being disassembled. If not " + + "specified, it defaults to 15 (ICS).") + .hasArg() + .withArgName("API_LEVEL") + .create("a"); + + options.addOption(classPathDirOption); + options.addOption(outputFileOption); + options.addOption(apiLevelOption); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpVtables.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpVtables.java new file mode 100644 index 00000000..9f507c80 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpVtables.java @@ -0,0 +1,172 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.analysis; + +import com.google.common.base.Splitter; +import com.google.common.collect.Lists; +import org.apache.commons.cli.*; +import org.jf.dexlib2.DexFileFactory; +import org.jf.dexlib2.dexbacked.DexBackedDexFile; +import org.jf.dexlib2.iface.ClassDef; +import org.jf.dexlib2.iface.Method; +import org.jf.util.ConsoleUtil; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class DumpVtables { + private static final Options options; + + static { + options = new Options(); + buildOptions(); + } + + public static void main(String[] args) { + CommandLineParser parser = new PosixParser(); + CommandLine commandLine; + + try { + commandLine = parser.parse(options, args); + } catch (ParseException ex) { + usage(); + return; + } + + String[] remainingArgs = commandLine.getArgs(); + + Option[] parsedOptions = commandLine.getOptions(); + ArrayList bootClassPathDirs = Lists.newArrayList(); + String outFile = "vtables.txt"; + int apiLevel = 15; + + for (int i=0; i bootClassPaths = Splitter.on(":").split("core.jar:ext.jar:framework.jar:android.policy.jar:services.jar"); + ClassPath classPath = ClassPath.fromClassPath(bootClassPathDirs, bootClassPaths, dexFile, apiLevel); + FileOutputStream outStream = new FileOutputStream(outFile); + + for (ClassDef classDef: dexFile.getClasses()) { + ClassProto classProto = (ClassProto) classPath.getClass(classDef); + Method[] methods = classProto.getVtable(); + String className = "Class " + classDef.getType() + " extends " + classDef.getSuperclass() + " : " + methods.length + " methods\n"; + outStream.write(className.getBytes()); + for (int i=0;i" + methods[i].getName() + "("; + for (CharSequence parameter: methods[i].getParameterTypes()) { + method += parameter; + } + method += ")" + methods[i].getReturnType() + "\n"; + outStream.write(method.getBytes()); + } + outStream.write("\n".getBytes()); + } + outStream.close(); + } catch (IOException ex) { + System.out.println("IOException thrown when trying to open a dex file or write out vtables: " + ex); + } + + } + + /** + * Prints the usage message. + */ + private static void usage() { + int consoleWidth = ConsoleUtil.getConsoleWidth(); + if (consoleWidth <= 0) { + consoleWidth = 80; + } + + System.out.println("java -cp baksmali.jar org.jf.dexlib2.analysis.DumpVtables -d path/to/jar/files "); + } + + private static void buildOptions() { + Option classPathDirOption = OptionBuilder.withLongOpt("bootclasspath-dir") + .withDescription("the base folder to look for the bootclasspath files in. Defaults to the current " + + "directory") + .hasArg() + .withArgName("DIR") + .create("d"); + + Option outputFileOption = OptionBuilder.withLongOpt("out-file") + .withDescription("output file") + .hasArg() + .withArgName("FILE") + .create("o"); + + Option apiLevelOption = OptionBuilder.withLongOpt("api-level") + .withDescription("The numeric api-level of the file being disassembled. If not " + + "specified, it defaults to 15 (ICS).") + .hasArg() + .withArgName("API_LEVEL") + .create("a"); + + options.addOption(classPathDirOption); + options.addOption(outputFileOption); + options.addOption(apiLevelOption); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/OdexHeaderItem.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/OdexHeaderItem.java index 4e91736f..c6599bc4 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/OdexHeaderItem.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/OdexHeaderItem.java @@ -51,7 +51,7 @@ public class OdexHeaderItem { public static final int AUX_LENGTH_OFFSET = 28; public static final int FLAGS_OFFSET = 32; - private static int getVersion(byte[] magic) { + public static int getVersion(byte[] magic) { if (magic.length < 8) { return 0; } From 10fdd197785daef678e4193eafbad7ac250f07a7 Mon Sep 17 00:00:00 2001 From: Izzat Bahadirov Date: Fri, 19 Apr 2013 01:00:59 -0400 Subject: [PATCH 02/25] Moving to ClassDef.getVirtualMethods() API. --- .../org/jf/dexlib2/analysis/ClassProto.java | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java index 1bc45166..4694e361 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java @@ -539,12 +539,12 @@ public class ClassProto implements TypeProto { //iterate over the virtual methods in the current class, and only add them when we don't already have the //method (i.e. if it was implemented by the superclass) if (!isInterface()) { - addToVtable(getVirtualMethods(getClassDef()), virtualMethodList); + addToVtable(getClassDef().getVirtualMethods(), virtualMethodList); LinkedHashMap interfaceTable = loadInterfaceTable(); if (interfaceTable != null) { for (ClassDef interfaceDef: interfaceTable.values()) { - addToVtable(getVirtualMethods(interfaceDef), virtualMethodList); + addToVtable(interfaceDef.getVirtualMethods(), virtualMethodList); } } } @@ -583,18 +583,7 @@ public class ClassProto implements TypeProto { return interfaceTable; } - private LinkedList getVirtualMethods(ClassDef classDef) { - LinkedList virtualMethods = Lists.newLinkedList(); - for (Method method: classDef.getMethods()) { - if (!MethodUtil.isDirect(method)) { - virtualMethods.add(method); - } - } - - return virtualMethods; - } - - private void addToVtable(List localMethods, List vtable) { + private void addToVtable(Iterable localMethods, List vtable) { for (Method virtualMethod: localMethods) { boolean found = false; for (int i=0; i Date: Fri, 19 Apr 2013 17:43:14 -0400 Subject: [PATCH 03/25] Better wording for dump usage printout. --- dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpFields.java | 2 +- dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpVtables.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpFields.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpFields.java index dfc46763..3850bc3f 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpFields.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpFields.java @@ -139,7 +139,7 @@ public class DumpFields { consoleWidth = 80; } - System.out.println("java -cp baksmali.jar org.jf.dexlib2.analysis.DumpFields -d path/to/jar/files "); + System.out.println("java -cp baksmali.jar org.jf.dexlib2.analysis.DumpFields -d path/to/framework/jar/files "); } private static void buildOptions() { diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpVtables.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpVtables.java index 9f507c80..af99852c 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpVtables.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpVtables.java @@ -141,7 +141,7 @@ public class DumpVtables { consoleWidth = 80; } - System.out.println("java -cp baksmali.jar org.jf.dexlib2.analysis.DumpVtables -d path/to/jar/files "); + System.out.println("java -cp baksmali.jar org.jf.dexlib2.analysis.DumpVtables -d path/to/framework/jar/files "); } private static void buildOptions() { From d6737943e120fe2e363e302537df4f543453901d Mon Sep 17 00:00:00 2001 From: Izzat Bahadirov Date: Fri, 19 Apr 2013 17:44:54 -0400 Subject: [PATCH 04/25] Fixed alignment. --- .../main/java/org/jf/dexlib2/analysis/DumpFields.java | 10 +++++----- .../main/java/org/jf/dexlib2/analysis/DumpVtables.java | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpFields.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpFields.java index 3850bc3f..f528f440 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpFields.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpFields.java @@ -157,11 +157,11 @@ public class DumpFields { .create("o"); Option apiLevelOption = OptionBuilder.withLongOpt("api-level") - .withDescription("The numeric api-level of the file being disassembled. If not " + - "specified, it defaults to 15 (ICS).") - .hasArg() - .withArgName("API_LEVEL") - .create("a"); + .withDescription("The numeric api-level of the file being disassembled. If not " + + "specified, it defaults to 15 (ICS).") + .hasArg() + .withArgName("API_LEVEL") + .create("a"); options.addOption(classPathDirOption); options.addOption(outputFileOption); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpVtables.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpVtables.java index af99852c..b243e422 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpVtables.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpVtables.java @@ -159,11 +159,11 @@ public class DumpVtables { .create("o"); Option apiLevelOption = OptionBuilder.withLongOpt("api-level") - .withDescription("The numeric api-level of the file being disassembled. If not " + - "specified, it defaults to 15 (ICS).") - .hasArg() - .withArgName("API_LEVEL") - .create("a"); + .withDescription("The numeric api-level of the file being disassembled. If not " + + "specified, it defaults to 15 (ICS).") + .hasArg() + .withArgName("API_LEVEL") + .create("a"); options.addOption(classPathDirOption); options.addOption(outputFileOption); From 9c72df65f76a70b7215769fe11e1fde99059519a Mon Sep 17 00:00:00 2001 From: Izzat Bahadirov Date: Mon, 22 Apr 2013 14:00:49 -0400 Subject: [PATCH 05/25] Using standard InlineMethodResolver if no custom one is provided. --- baksmali/src/main/java/org/jf/baksmali/baksmali.java | 3 +++ .../java/org/jf/dexlib2/dexbacked/DexBackedOdexFile.java | 5 +++++ .../main/java/org/jf/dexlib2/dexbacked/raw/HeaderItem.java | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/baksmali/src/main/java/org/jf/baksmali/baksmali.java b/baksmali/src/main/java/org/jf/baksmali/baksmali.java index 0be61b1e..da2f1863 100644 --- a/baksmali/src/main/java/org/jf/baksmali/baksmali.java +++ b/baksmali/src/main/java/org/jf/baksmali/baksmali.java @@ -34,6 +34,7 @@ import com.google.common.collect.Iterables; import org.jf.baksmali.Adaptors.ClassDefinition; import org.jf.dexlib2.analysis.ClassPath; import org.jf.dexlib2.analysis.CustomInlineMethodResolver; +import org.jf.dexlib2.analysis.InlineMethodResolver; import org.jf.dexlib2.dexbacked.DexBackedOdexFile; import org.jf.dexlib2.iface.ClassDef; import org.jf.dexlib2.iface.DexFile; @@ -91,6 +92,8 @@ public class baksmali { if (inlineTable != null) { options.inlineResolver = new CustomInlineMethodResolver(options.classPath, new File(inlineTable)); + } else if (dexFile instanceof DexBackedOdexFile) { + options.inlineResolver = InlineMethodResolver.createInlineMethodResolver(((DexBackedOdexFile) dexFile).getVersion()); } } catch (Exception ex) { System.err.println("\n\nError occured while loading boot class path files. Aborting."); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedOdexFile.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedOdexFile.java index 61561695..eb9eece1 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedOdexFile.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedOdexFile.java @@ -33,6 +33,7 @@ package org.jf.dexlib2.dexbacked; import com.google.common.io.ByteStreams; import org.jf.dexlib2.Opcodes; +import org.jf.dexlib2.dexbacked.raw.HeaderItem; import org.jf.dexlib2.dexbacked.raw.OdexHeaderItem; import org.jf.dexlib2.dexbacked.util.VariableSizeList; @@ -121,6 +122,10 @@ public class DexBackedOdexFile extends DexBackedDexFile { } } + public int getVersion() { + return HeaderItem.getVersion(getBuf(), 0); + } + public static class NotAnOdexFile extends RuntimeException { public NotAnOdexFile() { } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/HeaderItem.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/HeaderItem.java index ca55d67c..fbbdb797 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/HeaderItem.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/HeaderItem.java @@ -219,7 +219,7 @@ public class HeaderItem { return "Invalid"; } - private static int getVersion(byte[] buf, int offset) { + public static int getVersion(byte[] buf, int offset) { if (buf.length - offset < 8) { return 0; } From 6cf22f1eaca4b1af73fe7d0570ec5e8e7e9680f8 Mon Sep 17 00:00:00 2001 From: Izzat Bahadirov Date: Mon, 22 Apr 2013 16:12:48 -0400 Subject: [PATCH 06/25] Object register value now lives in register C, not D. --- .../src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java index 7cc47fa2..e92e9a7a 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java @@ -1545,7 +1545,7 @@ public class MethodAnalyzer { } else { Instruction35ms instruction = (Instruction35ms)analyzedInstruction.instruction; methodIndex = instruction.getVtableIndex(); - objectRegister = instruction.getRegisterD(); + objectRegister = instruction.getRegisterC(); } RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, objectRegister, From d4e3ab3fd5d23d39fc7d75635e2de7a1dbc888af Mon Sep 17 00:00:00 2001 From: Izzat Bahadirov Date: Mon, 22 Apr 2013 17:20:26 -0400 Subject: [PATCH 07/25] Fixed querying for odex version. --- .../main/java/org/jf/dexlib2/dexbacked/DexBackedOdexFile.java | 2 +- .../src/main/java/org/jf/dexlib2/dexbacked/raw/HeaderItem.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedOdexFile.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedOdexFile.java index eb9eece1..95c10167 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedOdexFile.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedOdexFile.java @@ -123,7 +123,7 @@ public class DexBackedOdexFile extends DexBackedDexFile { } public int getVersion() { - return HeaderItem.getVersion(getBuf(), 0); + return OdexHeaderItem.getVersion(odexBuf); } public static class NotAnOdexFile extends RuntimeException { diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/HeaderItem.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/HeaderItem.java index fbbdb797..ca55d67c 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/HeaderItem.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/HeaderItem.java @@ -219,7 +219,7 @@ public class HeaderItem { return "Invalid"; } - public static int getVersion(byte[] buf, int offset) { + private static int getVersion(byte[] buf, int offset) { if (buf.length - offset < 8) { return 0; } From 389c3a6afa3dae4106734db40c87ff88ba68f03f Mon Sep 17 00:00:00 2001 From: Izzat Bahadirov Date: Sat, 27 Apr 2013 23:19:17 -0400 Subject: [PATCH 08/25] Fix for invoke-virtual/range with no register range. --- .../src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java index e92e9a7a..231634a2 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java @@ -1590,8 +1590,8 @@ public class MethodAnalyzer { opcode = Opcode.INVOKE_VIRTUAL_RANGE; } - deodexedInstruction = new ImmutableInstruction3rc(opcode, instruction.getRegisterCount(), - instruction.getStartRegister(), resolvedMethod); + deodexedInstruction = new ImmutableInstruction3rc(opcode, instruction.getStartRegister(), + instruction.getRegisterCount(), resolvedMethod); } else { Instruction35ms instruction = (Instruction35ms)analyzedInstruction.instruction; Opcode opcode; From e3b5efd7d883d3cb5eb5b8ade74f3c3d8f4768f0 Mon Sep 17 00:00:00 2001 From: Izzat Bahadirov Date: Sun, 28 Apr 2013 17:30:11 -0400 Subject: [PATCH 09/25] Unified interface table creation logic. No more duplication. --- .../org/jf/dexlib2/analysis/ClassProto.java | 101 ++++-------------- 1 file changed, 22 insertions(+), 79 deletions(-) diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java index 4694e361..593392cd 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java @@ -34,7 +34,6 @@ package org.jf.dexlib2.analysis; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.google.common.collect.Sets; import org.jf.dexlib2.AccessFlags; import org.jf.dexlib2.analysis.util.TypeProtoUtils; import org.jf.dexlib2.iface.ClassDef; @@ -43,7 +42,6 @@ import org.jf.dexlib2.iface.Method; import org.jf.dexlib2.iface.reference.FieldReference; import org.jf.dexlib2.iface.reference.MethodReference; import org.jf.dexlib2.util.FieldUtil; -import org.jf.dexlib2.util.MethodUtil; import org.jf.util.ExceptionWithContext; import org.jf.util.SparseArray; @@ -59,8 +57,7 @@ public class ClassProto implements TypeProto { @Nonnull protected final ClassPath classPath; @Nonnull protected final String type; @Nullable protected ClassDef classDef; - @Nullable protected Set interfaces; - @Nullable protected LinkedHashMap interfaceTable; + @Nullable protected LinkedHashMap interfaces; @Nullable protected Method[] vtable; @Nullable protected SparseArray instanceFields; protected boolean interfacesFullyResolved = true; @@ -99,13 +96,6 @@ public class ClassProto implements TypeProto { return instanceFields; } - LinkedHashMap getInterfaceTable() { - if (interfaceTable == null) { - interfaceTable = loadInterfaceTable(); - } - return interfaceTable; - } - /** * Returns true if this class is an interface. * @@ -118,47 +108,31 @@ public class ClassProto implements TypeProto { return (classDef.getAccessFlags() & AccessFlags.INTERFACE.getValue()) != 0; } - private void addInterfacesRecursively(@Nonnull ClassDef classDef) { - assert interfaces != null; - for (String iface: classDef.getInterfaces()) { - interfaces.add(iface); - addInterfacesRecursively(iface); - } - } - - private void addInterfacesRecursively(@Nonnull String cls) { - ClassDef classDef; - try { - classDef = classPath.getClassDef(cls); - addInterfacesRecursively(classDef); - } catch (UnresolvedClassException ex) { - interfacesFullyResolved = false; - } - } - @Nonnull - protected Set getInterfaces() { + protected LinkedHashMap getInterfaces() { if (interfaces != null) { return interfaces; } - interfaces = Sets.newHashSet(); + interfaces = Maps.newLinkedHashMap(); try { - ClassDef classDef = getClassDef(); + for (String interfaceType: getClassDef().getInterfaces()) { + if (!interfaces.containsKey(interfaceType)) { + ClassDef interfaceDef; + try { + interfaceDef = classPath.getClassDef(interfaceType); + interfaces.put(interfaceType, interfaceDef); + } catch (UnresolvedClassException ex) { + interfacesFullyResolved = false; + } - if (isInterface()) { - interfaces.add(getType()); - } - - while (true) { - addInterfacesRecursively(classDef); - - String superclass = classDef.getSuperclass(); - if (superclass != null) { - classDef = classPath.getClassDef(superclass); - } else { - break; + ClassProto interfaceProto = (ClassProto) classPath.getClass(interfaceType); + for (ClassDef superInterface: interfaceProto.getInterfaces().values()) { + if (!interfaces.containsKey(superInterface.getType())) { + interfaces.put(superInterface.getType(), superInterface); + } + } } } } catch (UnresolvedClassException ex) { @@ -179,10 +153,8 @@ public class ClassProto implements TypeProto { */ @Override public boolean implementsInterface(@Nonnull String iface) { - for (String implementIface: getInterfaces()) { - if (implementIface.equals(iface)) { - return true; - } + if (getInterfaces().containsKey(iface)) { + return true; } if (!interfacesFullyResolved) { throw new UnresolvedClassException("Interfaces for class %s not fully resolved", getType()); @@ -541,11 +513,8 @@ public class ClassProto implements TypeProto { if (!isInterface()) { addToVtable(getClassDef().getVirtualMethods(), virtualMethodList); - LinkedHashMap interfaceTable = loadInterfaceTable(); - if (interfaceTable != null) { - for (ClassDef interfaceDef: interfaceTable.values()) { - addToVtable(interfaceDef.getVirtualMethods(), virtualMethodList); - } + for (ClassDef interfaceDef: getInterfaces().values()) { + addToVtable(interfaceDef.getVirtualMethods(), virtualMethodList); } } @@ -557,32 +526,6 @@ public class ClassProto implements TypeProto { return vtable; } - private LinkedHashMap loadInterfaceTable() { - LinkedHashMap interfaceTable = Maps.newLinkedHashMap(); - - for (String interfaceType: getClassDef().getInterfaces()) { - if (!interfaceTable.containsKey(interfaceType)) { - ClassDef interfaceDef; - try { - interfaceDef = classPath.getClassDef(interfaceType); - } catch (Exception ex) { - throw ExceptionWithContext.withContext(ex, - String.format("Could not find interface %s", interfaceType)); - } - interfaceTable.put(interfaceType, interfaceDef); - - ClassProto interfaceProto = (ClassProto) classPath.getClass(interfaceType); - for (ClassDef superInterface: interfaceProto.getInterfaceTable().values()) { - if (!interfaceTable.containsKey(superInterface.getType())) { - interfaceTable.put(superInterface.getType(), superInterface); - } - } - } - } - - return interfaceTable; - } - private void addToVtable(Iterable localMethods, List vtable) { for (Method virtualMethod: localMethods) { boolean found = false; From 2206c7638b318e6c9aa7aa7dc58e64ce2254a9df Mon Sep 17 00:00:00 2001 From: Izzat Bahadirov Date: Mon, 29 Apr 2013 15:58:35 -0400 Subject: [PATCH 10/25] Adding @Nonnull's and removing unnecessary null checks. --- .../java/org/jf/dexlib2/analysis/ClassProto.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java index 593392cd..c0294c50 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java @@ -82,6 +82,7 @@ public class ClassProto implements TypeProto { return classDef; } + @Nonnull Method[] getVtable() { if (vtable == null) { vtable = loadVtable(); @@ -89,6 +90,7 @@ public class ClassProto implements TypeProto { return vtable; } + @Nonnull SparseArray getInstanceFields() { if (instanceFields == null) { instanceFields = loadFields(); @@ -275,7 +277,7 @@ public class ClassProto implements TypeProto { @Override @Nullable public FieldReference getFieldByOffset(int fieldOffset) { - if (getInstanceFields() == null) { + if (getInstanceFields().size() == 0) { return null; } return getInstanceFields().get(fieldOffset); @@ -284,14 +286,13 @@ public class ClassProto implements TypeProto { @Override @Nullable public MethodReference getMethodByVtableIndex(int vtableIndex) { - if (getVtable() == null - || vtableIndex < 0 - || vtableIndex >= getVtable().length) { + if (vtableIndex < 0 || vtableIndex >= getVtable().length) { return null; } return getVtable()[vtableIndex]; } + @Nonnull private SparseArray loadFields() { //This is a bit of an "involved" operation. We need to follow the same algorithm that dalvik uses to //arrange fields, so that we end up with the same field offsets (which is needed for deodexing). @@ -477,7 +478,7 @@ public class ClassProto implements TypeProto { private int getNextFieldOffset() { SparseArray instanceFields = getInstanceFields(); - if (instanceFields == null || instanceFields.size() == 0) { + if (instanceFields.size() == 0) { return 8; } @@ -495,6 +496,7 @@ public class ClassProto implements TypeProto { } //TODO: check the case when we have a package private method that overrides an interface method + @Nonnull private Method[] loadVtable() { //TODO: it might be useful to keep track of which class's implementation is used for each virtual method. In other words, associate the implementing class type with each vtable entry List virtualMethodList = Lists.newLinkedList(); @@ -526,7 +528,7 @@ public class ClassProto implements TypeProto { return vtable; } - private void addToVtable(Iterable localMethods, List vtable) { + private void addToVtable(@Nonnull Iterable localMethods, @Nonnull List vtable) { for (Method virtualMethod: localMethods) { boolean found = false; for (int i=0; i Date: Mon, 29 Apr 2013 16:09:36 -0400 Subject: [PATCH 11/25] Added API level check to adding methods to vtable. --- .../org/jf/dexlib2/analysis/ClassPath.java | 18 ++++++++++++------ .../org/jf/dexlib2/analysis/ClassProto.java | 2 +- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java index a681f6ae..7ff939e9 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java @@ -55,14 +55,15 @@ public class ClassPath { @Nonnull private final TypeProto unknownClass; @Nonnull private DexFile[] dexFiles; @Nonnull private HashMap loadedClasses = Maps.newHashMap(); + @Nonnull private int api; /** * Creates a new ClassPath instance that can load classes from the given dex files * * @param classPath An array of DexFile objects. When loading a class, these dex files will be searched in order */ - public ClassPath(DexFile... classPath) throws IOException { - this(classPath, true); + public ClassPath(int api, DexFile... classPath) throws IOException { + this(api, classPath, true); } /** @@ -70,11 +71,11 @@ public class ClassPath { * * @param classPath An iterable of DexFile objects. When loading a class, these dex files will be searched in order */ - public ClassPath(Iterable classPath) { - this(Iterables.toArray(classPath, DexFile.class), false); + public ClassPath(int api, Iterable classPath) { + this(api, Iterables.toArray(classPath, DexFile.class), false); } - private ClassPath(@Nonnull DexFile[] classPath, boolean copyArray) { + private ClassPath(int api, @Nonnull DexFile[] classPath, boolean copyArray) { if (copyArray) { dexFiles = new DexFile[classPath.length+1]; System.arraycopy(classPath, 0, dexFiles, 0, classPath.length); @@ -86,6 +87,7 @@ public class ClassPath { unknownClass = new UnknownClassProto(this); loadedClasses.put(unknownClass.getType(), unknownClass); + this.api = api; loadPrimitiveType("Z"); loadPrimitiveType("B"); @@ -150,6 +152,10 @@ public class ClassPath { return unknownClass; } + public int getApi() { + return api; + } + @Nonnull public static ClassPath fromClassPath(Iterable classPathDirs, Iterable classPath, DexFile dexFile, int api) { @@ -159,7 +165,7 @@ public class ClassPath { dexFiles.add(loadClassPathEntry(classPathDirs, classPathEntry, api)); } dexFiles.add(dexFile); - return new ClassPath(dexFiles); + return new ClassPath(api, dexFiles); } private static final Pattern dalvikCacheOdexPattern = Pattern.compile("@([^@]+)@classes.dex$"); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java index c0294c50..bec5a143 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java @@ -534,7 +534,7 @@ public class ClassProto implements TypeProto { for (int i=0; i Date: Mon, 29 Apr 2013 16:27:45 -0400 Subject: [PATCH 12/25] Renamed getVersion() to getOdexVersion() in DexBackedOdexFile. --- baksmali/src/main/java/org/jf/baksmali/baksmali.java | 2 +- .../main/java/org/jf/dexlib2/dexbacked/DexBackedOdexFile.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/baksmali/src/main/java/org/jf/baksmali/baksmali.java b/baksmali/src/main/java/org/jf/baksmali/baksmali.java index da2f1863..bbd52055 100644 --- a/baksmali/src/main/java/org/jf/baksmali/baksmali.java +++ b/baksmali/src/main/java/org/jf/baksmali/baksmali.java @@ -93,7 +93,7 @@ public class baksmali { if (inlineTable != null) { options.inlineResolver = new CustomInlineMethodResolver(options.classPath, new File(inlineTable)); } else if (dexFile instanceof DexBackedOdexFile) { - options.inlineResolver = InlineMethodResolver.createInlineMethodResolver(((DexBackedOdexFile) dexFile).getVersion()); + options.inlineResolver = InlineMethodResolver.createInlineMethodResolver(((DexBackedOdexFile) dexFile).getOdexVersion()); } } catch (Exception ex) { System.err.println("\n\nError occured while loading boot class path files. Aborting."); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedOdexFile.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedOdexFile.java index 95c10167..1aa9c1eb 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedOdexFile.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedOdexFile.java @@ -122,7 +122,7 @@ public class DexBackedOdexFile extends DexBackedDexFile { } } - public int getVersion() { + public int getOdexVersion() { return OdexHeaderItem.getVersion(odexBuf); } From 6a2a627d3b132574c50b4882994393eecf58db5f Mon Sep 17 00:00:00 2001 From: Izzat Bahadirov Date: Mon, 29 Apr 2013 16:36:13 -0400 Subject: [PATCH 13/25] Fixed formatting. --- .../org/jf/dexlib2/analysis/DumpFields.java | 28 +++++++++---------- .../org/jf/dexlib2/analysis/DumpVtables.java | 28 +++++++++---------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpFields.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpFields.java index f528f440..d57ce961 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpFields.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpFields.java @@ -144,24 +144,24 @@ public class DumpFields { private static void buildOptions() { Option classPathDirOption = OptionBuilder.withLongOpt("bootclasspath-dir") - .withDescription("the base folder to look for the bootclasspath files in. Defaults to the current " + - "directory") - .hasArg() - .withArgName("DIR") - .create("d"); + .withDescription("the base folder to look for the bootclasspath files in. Defaults to the current " + + "directory") + .hasArg() + .withArgName("DIR") + .create("d"); Option outputFileOption = OptionBuilder.withLongOpt("out-file") - .withDescription("output file") - .hasArg() - .withArgName("FILE") - .create("o"); + .withDescription("output file") + .hasArg() + .withArgName("FILE") + .create("o"); Option apiLevelOption = OptionBuilder.withLongOpt("api-level") - .withDescription("The numeric api-level of the file being disassembled. If not " + - "specified, it defaults to 15 (ICS).") - .hasArg() - .withArgName("API_LEVEL") - .create("a"); + .withDescription("The numeric api-level of the file being disassembled. If not " + + "specified, it defaults to 15 (ICS).") + .hasArg() + .withArgName("API_LEVEL") + .create("a"); options.addOption(classPathDirOption); options.addOption(outputFileOption); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpVtables.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpVtables.java index b243e422..e4880a5a 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpVtables.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpVtables.java @@ -146,24 +146,24 @@ public class DumpVtables { private static void buildOptions() { Option classPathDirOption = OptionBuilder.withLongOpt("bootclasspath-dir") - .withDescription("the base folder to look for the bootclasspath files in. Defaults to the current " + - "directory") - .hasArg() - .withArgName("DIR") - .create("d"); + .withDescription("the base folder to look for the bootclasspath files in. Defaults to the current " + + "directory") + .hasArg() + .withArgName("DIR") + .create("d"); Option outputFileOption = OptionBuilder.withLongOpt("out-file") - .withDescription("output file") - .hasArg() - .withArgName("FILE") - .create("o"); + .withDescription("output file") + .hasArg() + .withArgName("FILE") + .create("o"); Option apiLevelOption = OptionBuilder.withLongOpt("api-level") - .withDescription("The numeric api-level of the file being disassembled. If not " + - "specified, it defaults to 15 (ICS).") - .hasArg() - .withArgName("API_LEVEL") - .create("a"); + .withDescription("The numeric api-level of the file being disassembled. If not " + + "specified, it defaults to 15 (ICS).") + .hasArg() + .withArgName("API_LEVEL") + .create("a"); options.addOption(classPathDirOption); options.addOption(outputFileOption); From 0f61ce9a6ff708bb0619ba2ad07d26b0ff579060 Mon Sep 17 00:00:00 2001 From: Izzat Bahadirov Date: Mon, 29 Apr 2013 17:02:14 -0400 Subject: [PATCH 14/25] Fix for broken tests. --- .../java/org/jf/dexlib2/analysis/ClassPath.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java index 7ff939e9..22c55fff 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java @@ -62,20 +62,21 @@ public class ClassPath { * * @param classPath An array of DexFile objects. When loading a class, these dex files will be searched in order */ - public ClassPath(int api, DexFile... classPath) throws IOException { - this(api, classPath, true); + public ClassPath(DexFile... classPath) throws IOException { + this(classPath, true, 15); } /** * Creates a new ClassPath instance that can load classes from the given dex files * * @param classPath An iterable of DexFile objects. When loading a class, these dex files will be searched in order + * @param api API level */ - public ClassPath(int api, Iterable classPath) { - this(api, Iterables.toArray(classPath, DexFile.class), false); + public ClassPath(Iterable classPath, int api) { + this(Iterables.toArray(classPath, DexFile.class), false, api); } - private ClassPath(int api, @Nonnull DexFile[] classPath, boolean copyArray) { + private ClassPath(@Nonnull DexFile[] classPath, boolean copyArray, int api) { if (copyArray) { dexFiles = new DexFile[classPath.length+1]; System.arraycopy(classPath, 0, dexFiles, 0, classPath.length); @@ -165,7 +166,7 @@ public class ClassPath { dexFiles.add(loadClassPathEntry(classPathDirs, classPathEntry, api)); } dexFiles.add(dexFile); - return new ClassPath(api, dexFiles); + return new ClassPath(dexFiles, api); } private static final Pattern dalvikCacheOdexPattern = Pattern.compile("@([^@]+)@classes.dex$"); From 5c2353878cd564b696cb65887c7f01860c5def14 Mon Sep 17 00:00:00 2001 From: Izzat Bahadirov Date: Mon, 29 Apr 2013 17:04:46 -0400 Subject: [PATCH 15/25] Sorting virtual methods when adding them. --- .../src/main/java/org/jf/dexlib2/analysis/ClassProto.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java index bec5a143..eda0bba1 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java @@ -529,7 +529,10 @@ public class ClassProto implements TypeProto { } private void addToVtable(@Nonnull Iterable localMethods, @Nonnull List vtable) { - for (Method virtualMethod: localMethods) { + List methods = Lists.newArrayList(localMethods); + Collections.sort(methods); + + for (Method virtualMethod: methods) { boolean found = false; for (int i=0; i Date: Wed, 1 May 2013 17:35:31 -0400 Subject: [PATCH 16/25] Throwing UnresolvedClassException if interfaces weren't fully resolved. --- dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java index eda0bba1..b1e7a955 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java @@ -141,6 +141,10 @@ public class ClassProto implements TypeProto { interfacesFullyResolved = false; } + if (!interfacesFullyResolved) { + throw new UnresolvedClassException("Interfaces for class %s not fully resolved", getType()); + } + return interfaces; } From d3ac72fd67e6266ee0d84743e9fcbb4af8629eca Mon Sep 17 00:00:00 2001 From: Izzat Bahadirov Date: Wed, 1 May 2013 17:58:58 -0400 Subject: [PATCH 17/25] Introduced getInterfacesFull() method to ensure that all interfaces are resolved when creating a vtable. --- .../main/java/org/jf/dexlib2/analysis/ClassProto.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java index b1e7a955..3811fc13 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java @@ -141,10 +141,16 @@ public class ClassProto implements TypeProto { interfacesFullyResolved = false; } + return interfaces; + } + + @Nonnull + protected LinkedHashMap getInterfacesFull() { + LinkedHashMap interfaces = getInterfaces(); + if (!interfacesFullyResolved) { throw new UnresolvedClassException("Interfaces for class %s not fully resolved", getType()); } - return interfaces; } @@ -519,7 +525,7 @@ public class ClassProto implements TypeProto { if (!isInterface()) { addToVtable(getClassDef().getVirtualMethods(), virtualMethodList); - for (ClassDef interfaceDef: getInterfaces().values()) { + for (ClassDef interfaceDef: getInterfacesFull().values()) { addToVtable(interfaceDef.getVirtualMethods(), virtualMethodList); } } From 063862d44c5973db723c4369692663e9843395c3 Mon Sep 17 00:00:00 2001 From: Izzat Bahadirov Date: Thu, 2 May 2013 17:15:09 -0400 Subject: [PATCH 18/25] Propagating interfaceFullyResolved flag from parent to child interfaces. --- dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java index 3811fc13..9212fa9d 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java @@ -135,6 +135,9 @@ public class ClassProto implements TypeProto { interfaces.put(superInterface.getType(), superInterface); } } + if (!interfaceProto.interfacesFullyResolved) { + interfacesFullyResolved = false; + } } } } catch (UnresolvedClassException ex) { From 9d8cf0d67c2d9cce3e8d4cf59f78e0475241ce23 Mon Sep 17 00:00:00 2001 From: Izzat Bahadirov Date: Fri, 3 May 2013 16:16:23 -0400 Subject: [PATCH 19/25] Added self (if interface) and super class interfaces to the interface table. --- .../org/jf/dexlib2/analysis/ClassProto.java | 33 ++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java index 9212fa9d..c141c7d0 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java @@ -126,13 +126,14 @@ public class ClassProto implements TypeProto { interfaceDef = classPath.getClassDef(interfaceType); interfaces.put(interfaceType, interfaceDef); } catch (UnresolvedClassException ex) { + interfaces.put(interfaceType, null); interfacesFullyResolved = false; } ClassProto interfaceProto = (ClassProto) classPath.getClass(interfaceType); - for (ClassDef superInterface: interfaceProto.getInterfaces().values()) { - if (!interfaces.containsKey(superInterface.getType())) { - interfaces.put(superInterface.getType(), superInterface); + for (String superInterface: interfaceProto.getInterfaces().keySet()) { + if (!interfaces.containsKey(superInterface)) { + interfaces.put(superInterface, interfaceProto.getInterfaces().get(superInterface)); } } if (!interfaceProto.interfacesFullyResolved) { @@ -144,6 +145,28 @@ public class ClassProto implements TypeProto { interfacesFullyResolved = false; } + // now add self and super class interfaces, required for common super class lookup + // we don't really need ClassDef's for that, so let's just use null + + if (isInterface() && !interfaces.containsKey(getType())) { + interfaces.put(getType(), null); + } + + try { + String superclass = getSuperclass(); + if (superclass != null) { + ClassProto superclassProto = (ClassProto) classPath.getClass(superclass); + for (String superclassInterface: superclassProto.getInterfaces().keySet()) { + if (!interfaces.containsKey(superclassInterface)) { + interfaces.put(superclassInterface, null); + } + } + } + } catch (UnresolvedClassException ex) { + // TODO: not sure if this is necessary + interfacesFullyResolved = false; + } + return interfaces; } @@ -529,7 +552,9 @@ public class ClassProto implements TypeProto { addToVtable(getClassDef().getVirtualMethods(), virtualMethodList); for (ClassDef interfaceDef: getInterfacesFull().values()) { - addToVtable(interfaceDef.getVirtualMethods(), virtualMethodList); + if (interfaceDef != null) { + addToVtable(interfaceDef.getVirtualMethods(), virtualMethodList); + } } } From 4147960f6e8eb128f7a18f7e39b01836b3f42204 Mon Sep 17 00:00:00 2001 From: Izzat Bahadirov Date: Mon, 6 May 2013 14:26:53 -0400 Subject: [PATCH 20/25] Propagating interfacesFullyResolved from the parent. --- dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java index c141c7d0..7478819a 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java @@ -161,9 +161,11 @@ public class ClassProto implements TypeProto { interfaces.put(superclassInterface, null); } } + if (!superclassProto.interfacesFullyResolved) { + interfacesFullyResolved = false; + } } } catch (UnresolvedClassException ex) { - // TODO: not sure if this is necessary interfacesFullyResolved = false; } From b16caa690dcc1a4772748534b1517b10adb894aa Mon Sep 17 00:00:00 2001 From: Izzat Bahadirov Date: Mon, 6 May 2013 14:46:59 -0400 Subject: [PATCH 21/25] More elegant way of getting direct interfaces, now using FluentIterable. --- .../java/org/jf/dexlib2/analysis/ClassProto.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java index 7478819a..d01887ad 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java @@ -31,6 +31,8 @@ package org.jf.dexlib2.analysis; +import com.google.common.base.Predicate; +import com.google.common.collect.FluentIterable; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -173,13 +175,12 @@ public class ClassProto implements TypeProto { } @Nonnull - protected LinkedHashMap getInterfacesFull() { - LinkedHashMap interfaces = getInterfaces(); - + protected Iterable getDirectInterfaces() { if (!interfacesFullyResolved) { throw new UnresolvedClassException("Interfaces for class %s not fully resolved", getType()); } - return interfaces; + + return FluentIterable.from(getInterfaces().values()).filter(ClassDef.class); } /** @@ -553,10 +554,8 @@ public class ClassProto implements TypeProto { if (!isInterface()) { addToVtable(getClassDef().getVirtualMethods(), virtualMethodList); - for (ClassDef interfaceDef: getInterfacesFull().values()) { - if (interfaceDef != null) { - addToVtable(interfaceDef.getVirtualMethods(), virtualMethodList); - } + for (ClassDef interfaceDef: getDirectInterfaces()) { + addToVtable(interfaceDef.getVirtualMethods(), virtualMethodList); } } From 0ac92423683c4cb68254fa54a4522ba2337dd4aa Mon Sep 17 00:00:00 2001 From: Izzat Bahadirov Date: Mon, 6 May 2013 15:21:49 -0400 Subject: [PATCH 22/25] More @Nonnull's. --- .../main/java/org/jf/dexlib2/analysis/ClassProto.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java index d01887ad..d6b04c80 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java @@ -482,7 +482,8 @@ public class ClassProto implements TypeProto { return instanceFields; } - private ArrayList getInstanceFields(ClassDef classDef) { + @Nonnull + private ArrayList getInstanceFields(@Nonnull ClassDef classDef) { ArrayList instanceFields = Lists.newArrayList(); for (Field field: classDef.getFields()) { if (!FieldUtil.isStatic(field)) { @@ -589,13 +590,13 @@ public class ClassProto implements TypeProto { } } - private boolean methodSignaturesMatch(Method a, Method b) { + private boolean methodSignaturesMatch(@Nonnull Method a, @Nonnull Method b) { return (a.getName().equals(b.getName()) && a.getReturnType().equals(b.getReturnType()) && a.getParameters().equals(b.getParameters())); } - private boolean canAccess(Method virtualMethod) { + private boolean canAccess(@Nonnull Method virtualMethod) { if (!methodIsPackagePrivate(virtualMethod.getAccessFlags())) { return true; } @@ -605,7 +606,7 @@ public class ClassProto implements TypeProto { return otherPackage.equals(ourPackage); } - private String getPackage(String classType) { + private String getPackage(@Nonnull String classType) { int lastSlash = classType.lastIndexOf('/'); if (lastSlash < 0) { return ""; From 3aa6e2d4fa142908a3b15ec392793ba188cd49aa Mon Sep 17 00:00:00 2001 From: Izzat Bahadirov Date: Mon, 6 May 2013 20:59:43 -0400 Subject: [PATCH 23/25] Using Predicates.notNull() to filter for direct interfaces. --- dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java index d6b04c80..9db3fdb5 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java @@ -32,6 +32,7 @@ package org.jf.dexlib2.analysis; import com.google.common.base.Predicate; +import com.google.common.base.Predicates; import com.google.common.collect.FluentIterable; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; @@ -180,7 +181,7 @@ public class ClassProto implements TypeProto { throw new UnresolvedClassException("Interfaces for class %s not fully resolved", getType()); } - return FluentIterable.from(getInterfaces().values()).filter(ClassDef.class); + return FluentIterable.from(getInterfaces().values()).filter(Predicates.notNull()); } /** From 490a8b2cd01ab7e441ba7017ee74f4778c97abec Mon Sep 17 00:00:00 2001 From: Izzat Bahadirov Date: Mon, 6 May 2013 21:02:31 -0400 Subject: [PATCH 24/25] Another @Nonnull. --- dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java | 1 + 1 file changed, 1 insertion(+) diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java index 9db3fdb5..ac2391cd 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java @@ -607,6 +607,7 @@ public class ClassProto implements TypeProto { return otherPackage.equals(ourPackage); } + @Nonnull private String getPackage(@Nonnull String classType) { int lastSlash = classType.lastIndexOf('/'); if (lastSlash < 0) { From 363af0a52babf1881e339606f51297a153812c4a Mon Sep 17 00:00:00 2001 From: Izzat Bahadirov Date: Mon, 6 May 2013 21:07:45 -0400 Subject: [PATCH 25/25] Using newer API getInstanceFields() instead of getFields() + filtering. --- .../src/main/java/org/jf/dexlib2/analysis/ClassProto.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java index ac2391cd..e04f0bfc 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java @@ -486,10 +486,8 @@ public class ClassProto implements TypeProto { @Nonnull private ArrayList getInstanceFields(@Nonnull ClassDef classDef) { ArrayList instanceFields = Lists.newArrayList(); - for (Field field: classDef.getFields()) { - if (!FieldUtil.isStatic(field)) { - instanceFields.add(field); - } + for (Field field: classDef.getInstanceFields()) { + instanceFields.add(field); } return instanceFields; }