Fully implement common superclass functionality

This commit is contained in:
Ben Gruver 2013-03-17 21:37:14 -07:00
parent 8f383501c1
commit 39e4d4487e
17 changed files with 1601 additions and 184 deletions

View File

@ -0,0 +1,127 @@
/*
* 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.Strings;
import org.jf.dexlib2.util.TypeUtils;
import org.jf.util.ExceptionWithContext;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class ArrayProto implements TypeProto {
protected final ClassPath classPath;
protected final int dimensions;
protected final String elementType;
public ArrayProto(@Nonnull ClassPath classPath, @Nonnull String type) {
this.classPath = classPath;
int i=0;
while (type.charAt(i) == '[') {
i++;
if (i == type.length()) {
throw new ExceptionWithContext("Invalid array type: %s", type);
}
}
dimensions = i;
elementType = type.substring(i);
}
@Override public String toString() { return getType(); }
@Nonnull @Override public ClassPath getClassPath() { return classPath; }
@Nonnull @Override public String getType() { return makeArrayType(elementType, dimensions); }
@Nonnull public String getElementType() { return elementType; }
@Override public boolean isInterface() { return false; }
@Override public boolean implementsInterface(@Nonnull String iface) {
return iface.equals("Ljava/lang/Cloneable;") || iface.equals("Ljava/io/Serializable;");
}
@Nullable @Override
public String getSuperclass() {
return "Ljava/lang/Object;";
}
@Nonnull @Override
public TypeProto getCommonSuperclass(@Nonnull TypeProto other) {
if (other instanceof ArrayProto) {
if (TypeUtils.isPrimitiveType(getElementType()) ||
TypeUtils.isPrimitiveType(((ArrayProto)other).getElementType())) {
if (dimensions == ((ArrayProto)other).dimensions &&
getElementType().equals(((ArrayProto)other).getElementType())) {
return this;
}
return classPath.getClass("Ljava/lang/Object;");
}
if (dimensions == ((ArrayProto)other).dimensions) {
TypeProto thisClass = classPath.getClass(elementType);
TypeProto otherClass = classPath.getClass(((ArrayProto)other).elementType);
TypeProto mergedClass = thisClass.getCommonSuperclass(otherClass);
if (thisClass == mergedClass) {
return this;
}
if (otherClass == mergedClass) {
return other;
}
return classPath.getClass(makeArrayType(mergedClass.getType(), dimensions));
}
int dimensions = Math.min(this.dimensions, ((ArrayProto)other).dimensions);
return classPath.getClass(makeArrayType("Ljava/lang/Object;", dimensions));
}
if (other instanceof ClassProto) {
try {
if (other.isInterface()) {
if (implementsInterface(other.getType())) {
return other;
}
}
} catch (UnresolvedClassException ex) {
// ignore
}
return classPath.getClass("Ljava/lang/Object;");
}
// otherwise, defer to the other class' getCommonSuperclass
return other.getCommonSuperclass(this);
}
private static final String BRACKETS = Strings.repeat("[", 256);
@Nonnull
private static String makeArrayType(@Nonnull String elementType, int dimensions) {
return BRACKETS.substring(0, dimensions) + elementType;
}
}

View File

@ -31,18 +31,22 @@
package org.jf.dexlib2.analysis;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
import org.jf.dexlib2.analysis.reflection.ReflectionClassDef;
import org.jf.dexlib2.iface.ClassDef;
import org.jf.dexlib2.iface.DexFile;
import org.jf.dexlib2.immutable.ImmutableDexFile;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
public class ClassPath {
private DexBackedDexFile[] dexFiles;
private HashMap<String, ClassProto> loadedClasses = Maps.newHashMap();
@Nonnull private final TypeProto unknownClass;
@Nonnull private DexFile[] dexFiles;
@Nonnull private HashMap<String, TypeProto> loadedClasses = Maps.newHashMap();
/**
* Creates a new ClassPath instance that can load classes from the given dex files
@ -50,25 +54,44 @@ public class ClassPath {
* @param classPath An array of DexBackedDexFile objects. When loading a class, these dex files will be searched
* in order
*/
public ClassPath(DexBackedDexFile[] classPath) throws IOException {
dexFiles = new DexBackedDexFile[classPath.length];
public ClassPath(DexFile... classPath) throws IOException {
dexFiles = new DexFile[classPath.length+1];
System.arraycopy(classPath, 0, dexFiles, 0, classPath.length);
// add fallbacks for certain special classes that must be present
dexFiles[dexFiles.length - 1] = getBasicClasses();
unknownClass = new UnknownClassProto(this);
loadedClasses.put(unknownClass.getType(), unknownClass);
}
private static DexFile getBasicClasses() {
return new ImmutableDexFile(ImmutableSet.of(
new ReflectionClassDef(Object.class),
new ReflectionClassDef(Cloneable.class),
new ReflectionClassDef(Serializable.class)));
}
@Nonnull
public ClassProto getClass(String type) {
ClassProto loadedClass = loadedClasses.get(type);
if (loadedClass != null) {
return loadedClass;
public TypeProto getClass(String type) {
TypeProto typeProto = loadedClasses.get(type);
if (typeProto != null) {
return typeProto;
}
ClassProto classProto = new ClassProto(this, type);
loadedClasses.put(type, classProto);
return classProto;
if (type.charAt(0) == '[') {
typeProto = new ArrayProto(this, type);
} else {
typeProto = new ClassProto(this, type);
}
loadedClasses.put(type, typeProto);
return typeProto;
}
@Nonnull
public ClassDef getClassDef(String type) {
for (DexBackedDexFile dexFile: dexFiles) {
// TODO: need a <= O(log) way to look up classes
for (DexFile dexFile: dexFiles) {
for (ClassDef classDef: dexFile.getClasses()) {
if (classDef.getType().equals(type)) {
return classDef;
@ -77,4 +100,37 @@ public class ClassPath {
}
throw new UnresolvedClassException("Could not resolve class %s", type);
}
@Nonnull
public RegisterType getRegisterTypeForType(@Nonnull String type) {
switch (type.charAt(0)) {
case 'Z':
return RegisterType.getRegisterType(RegisterType.BOOLEAN, null);
case 'B':
return RegisterType.getRegisterType(RegisterType.BYTE, null);
case 'S':
return RegisterType.getRegisterType(RegisterType.SHORT, null);
case 'C':
return RegisterType.getRegisterType(RegisterType.CHAR, null);
case 'I':
return RegisterType.getRegisterType(RegisterType.INTEGER, null);
case 'F':
return RegisterType.getRegisterType(RegisterType.FLOAT, null);
case 'J':
return RegisterType.getRegisterType(RegisterType.LONG_LO, null);
case 'D':
return RegisterType.getRegisterType(RegisterType.DOUBLE_LO, null);
case 'L':
case 'U':
case '[':
return RegisterType.getRegisterType(RegisterType.REFERENCE, getClass(type));
default:
throw new RuntimeException("Invalid type: " + type);
}
}
@Nonnull
public TypeProto getUnknownClass() {
return unknownClass;
}
}

View File

@ -31,177 +31,237 @@
package org.jf.dexlib2.analysis;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
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.util.ExceptionWithContext;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
/**
* A class "prototype". This contains things like the interfaces, the superclass, the vtable and the instance fields
* and their offsets.
*/
public class ClassProto {
@Nonnull public final ClassPath classPath;
@Nonnull public final String type;
@Nullable private ClassDef classDef;
public class ClassProto implements TypeProto {
@Nonnull protected final ClassPath classPath;
@Nonnull protected final String type;
@Nullable protected ClassDef classDef;
@Nullable protected Set<String> interfaces;
protected boolean interfacesFullyResolved = true;
public ClassProto(@Nonnull ClassPath classPath, @Nonnull String type) {
this.classPath = classPath;
this.type = type;
}
@Override public String toString() { return type; }
@Nonnull @Override public ClassPath getClassPath() { return classPath; }
@Nonnull @Override public String getType() { return type; }
@Nonnull
public ClassDef getClassDef() {
protected ClassDef getClassDef() {
if (classDef == null) {
classDef = classPath.getClassDef(type);
}
return classDef;
}
@Nonnull
public String getType() {
return type;
}
/**
* Returns true if this class is an interface.
*
* If this class is not defined, then this will throw an UnresolvedClassException
*
* @return True if this class is an interface
*/
public boolean isInterface() {
// TODO: implement
return false;
ClassDef classDef = getClassDef();
return (classDef.getAccessFlags() & AccessFlags.INTERFACE.getValue()) != 0;
}
public boolean implementsInterface(String iface) {
// TODO: implement
return false;
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<String> getInterfaces() {
if (interfaces != null) {
return interfaces;
}
interfaces = Sets.newHashSet();
try {
ClassDef classDef = getClassDef();
if (isInterface()) {
interfaces.add(getType());
}
while (true) {
addInterfacesRecursively(classDef);
String superclass = classDef.getSuperclass();
if (superclass != null) {
classDef = classPath.getClassDef(superclass);
} else {
break;
}
}
} catch (UnresolvedClassException ex) {
interfacesFullyResolved = false;
}
return interfaces;
}
/**
* Get the chain of superclasses of this class. The first element will be the immediate superclass followed by
* it's superclass, etc. up to java.lang.Object.
* Checks if this class implements the given interface.
*
* Returns an empty iterable if called on java.lang.Object.
* If the interfaces of this class cannot be fully resolved then this
* method will either return true or throw an UnresolvedClassException
*
* @return An iterable containing the superclasses of this class.
* @throws UnresolvedClassException if any class in the chain can't be resolved
* @param iface The interface to check for
* @return true if this class implements the given interface, otherwise false
*/
@Nonnull
public Iterable<String> getSuperclassChain() {
final ClassDef topClassDef = this.getClassDef();
return new Iterable<String>() {
private ClassDef classDef = topClassDef;
@Override public Iterator<String> iterator() {
return new Iterator<String>() {
@Override public boolean hasNext() {
return classDef == null || classDef.getSuperclass() == null;
}
@Override public String next() {
if (classDef == null) {
throw new NoSuchElementException();
}
String next = classDef.getSuperclass();
if (next == null) {
throw new NoSuchElementException();
}
classDef = classPath.getClassDef(next);
return next;
}
@Override public void remove() {
throw new UnsupportedOperationException();
}
};
@Override
public boolean implementsInterface(@Nonnull String iface) {
for (String implementIface: getInterfaces()) {
if (implementIface.equals(iface)) {
return true;
}
};
}
if (!interfacesFullyResolved) {
throw new UnresolvedClassException("Interfaces for class %s not fully resolved", getType());
}
return false;
}
@Nonnull public ClassProto getCommonSuperclass(@Nonnull ClassProto other) {
@Nullable @Override
public String getSuperclass() {
return getClassDef().getSuperclass();
}
/**
* This is a helper method for getCommonSuperclass
*
* It checks if this class is an interface, and if so, if other implements it.
*
* If this class is undefined, we go ahead and check if it is listed in other's interfaces. If not, we throw an
* UndefinedClassException
*
* If the interfaces of other cannot be fully resolved, we check the interfaces that can be resolved. If not found,
* we throw an UndefinedClassException
*
* @param other The class to check the interfaces of
* @return true if this class is an interface (or is undefined) other implements this class
*
*/
private boolean checkInterface(@Nonnull ClassProto other) {
boolean isResolved = true;
boolean isInterface = true;
try {
isInterface = isInterface();
} catch (UnresolvedClassException ex) {
isResolved = false;
// if we don't know if this class is an interface or not,
// we can still try to call other.implementsInterface(this)
}
if (isInterface) {
try {
if (other.implementsInterface(getType())) {
return true;
}
} catch (UnresolvedClassException ex) {
// There are 2 possibilities here, depending on whether we were able to resolve this class.
// 1. If this class is resolved, then we know it is an interface class. The other class either
// isn't defined, or its interfaces couldn't be fully resolved.
// In this case, we throw an UnresolvedClassException
// 2. If this class is not resolved, we had tried to call implementsInterface anyway. We don't
// know for sure if this class is an interface or not. We return false, and let processing
// continue in getCommonSuperclass
if (isResolved) {
throw ex;
}
}
}
return false;
}
@Override @Nonnull
public TypeProto getCommonSuperclass(@Nonnull TypeProto other) {
// use the other type's more specific implementation
if (!(other instanceof ClassProto)) {
return other.getCommonSuperclass(this);
}
if (this == other || getType().equals(other.getType())) {
return this;
}
if (isInterface()) {
if (other.implementsInterface(getType())) {
if (this.getType().equals("Ljava/lang/Object;")) {
return this;
}
if (other.getType().equals("Ljava/lang/Object;")) {
return other;
}
boolean gotException = false;
try {
if (checkInterface((ClassProto)other)) {
return this;
}
return classPath.getClass("Ljava/lang/Object;");
} catch (UnresolvedClassException ex) {
gotException = true;
}
if (other.isInterface()) {
if (implementsInterface(other.getType())) {
try {
if (((ClassProto)other).checkInterface(this)) {
return other;
}
return classPath.getClass("Ljava/lang/Object;");
}
boolean thisResolved = true;
boolean otherResolved = true;
List<String> thisChain = Lists.newArrayList(getType());
List<String> otherChain = Lists.newArrayList(other.getType());
// grab as much of the superclass chain as we can for both types,
// and keep track of whether we were able to get all of it
try {
for (String type: getSuperclassChain()) {
thisChain.add(type);
}
} catch (UnresolvedClassException ex) {
thisResolved = false;
gotException = true;
}
if (gotException) {
return classPath.getUnknownClass();
}
try {
for (String type: other.getSuperclassChain()) {
otherChain.add(type);
}
} catch (UnresolvedClassException ex) {
otherResolved = false;
}
List<TypeProto> thisChain = Lists.<TypeProto>newArrayList(this);
Iterables.addAll(thisChain, TypeProtoUtils.getSuperclassChain(this));
// if both were resolved, then we start looking backwards from the end of the shorter chain, until
// we find a pair of entries in the chains that match
if (thisResolved && otherResolved) {
for (int i=Math.min(thisChain.size(), otherChain.size()); i>=0; i--) {
String type = thisChain.get(i);
if (type.equals(otherChain.get(i))) {
return classPath.getClass(type);
}
}
// "This should never happen"
throw new ExceptionWithContext("Wasn't able to find a common superclass for %s and %s", this.getType(),
other.getType());
}
List<TypeProto> otherChain = Lists.newArrayList(other);
Iterables.addAll(otherChain, TypeProtoUtils.getSuperclassChain(other));
// we weren't able to fully resolve both classes. Let's see if we can find a common superclass in what we
// were able to resolve
for (String thisType: thisChain) {
for (String otherType: otherChain) {
if (thisType.equals(otherType)) {
return classPath.getClass(thisType);
}
// reverse them, so that the first entry is either Ljava/lang/Object; or Ujava/lang/Object;
thisChain = Lists.reverse(thisChain);
otherChain = Lists.reverse(otherChain);
for (int i=Math.min(thisChain.size(), otherChain.size())-1; i>=0; i--) {
TypeProto typeProto = thisChain.get(i);
if (typeProto.getType().equals(otherChain.get(i).getType())) {
return typeProto;
}
}
// Nope. We'll throw an UnresolvedClassException. The caller can catch the exception and use java.lang.Object
// as the superclass, if it is appropriate to do so
if (!thisResolved) {
if (!otherResolved) {
throw new UnresolvedClassException(
"Could not fully resolve %s or %s while getting their common superclass",
getType(), other.getType());
} else {
throw new UnresolvedClassException(
"Could not fully resolve %s while getting common superclass with %s",
getType(), other.getType());
}
}
throw new UnresolvedClassException(
"Could not fully resolve %s while getting common superclass with %s", other.getType(), getType());
return classPath.getUnknownClass();
}
}

View File

@ -0,0 +1,57 @@
/*
* 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 org.jf.util.ExceptionWithContext;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PrimitiveProto implements TypeProto {
protected final ClassPath classPath;
protected final String type;
public PrimitiveProto(@Nonnull ClassPath classPath, @Nonnull String type) {
this.classPath = classPath;
this.type = type;
}
@Override public String toString() { return type; }
@Nonnull @Override public ClassPath getClassPath() { return classPath; }
@Nonnull @Override public String getType() { return type; }
@Override public boolean isInterface() { return false; }
@Override public boolean implementsInterface(@Nonnull String iface) { return false; }
@Nullable @Override public String getSuperclass() { return null; }
@Nonnull @Override public TypeProto getCommonSuperclass(@Nonnull TypeProto other) {
throw new ExceptionWithContext("Cannot call getCommonSuperclass on PrimitiveProto");
}
}

View File

@ -41,9 +41,9 @@ import java.io.Writer;
public class RegisterType {
public final byte category;
@Nullable public final String type;
@Nullable public final TypeProto type;
private RegisterType(byte category, @Nullable String type) {
private RegisterType(byte category, @Nullable TypeProto type) {
assert ((category == REFERENCE || category == UNINIT_REF || category == UNINIT_THIS) && type != null) ||
((category != REFERENCE && category != UNINIT_REF && category != UNINIT_THIS) && type == null);
@ -61,7 +61,7 @@ public class RegisterType {
writer.write(CATEGORY_NAMES[category]);
if (type != null) {
writer.write(',');
writer.write(type);
writer.write(type.getType());
}
writer.write(')');
}
@ -184,34 +184,6 @@ public class RegisterType {
public static final RegisterType DOUBLE_HI_TYPE = new RegisterType(DOUBLE_HI, null);
public static final RegisterType CONFLICTED_TYPE = new RegisterType(CONFLICTED, null);
@Nonnull
public static RegisterType getRegisterTypeForType(@Nonnull String type) {
switch (type.charAt(0)) {
case 'Z':
return getRegisterType(BOOLEAN, null);
case 'B':
return getRegisterType(BYTE, null);
case 'S':
return getRegisterType(SHORT, null);
case 'C':
return getRegisterType(CHAR, null);
case 'I':
return getRegisterType(INTEGER, null);
case 'F':
return getRegisterType(FLOAT, null);
case 'J':
return getRegisterType(LONG_LO, null);
case 'D':
return getRegisterType(DOUBLE_LO, null);
case 'L':
case 'U':
case '[':
return getRegisterType(REFERENCE, type);
default:
throw new RuntimeException("Invalid type: " + type);
}
}
@Nonnull
public static RegisterType getWideRegisterTypeForType(@Nonnull String type, boolean firstRegister) {
switch (type.charAt(0)) {
@ -260,18 +232,18 @@ public class RegisterType {
return getRegisterType(INTEGER, null);
}
public RegisterType merge(RegisterType other) {
if (other == null || other == this) {
public RegisterType merge(@Nonnull RegisterType other) {
if (other == this) {
return this;
}
byte mergedCategory = mergeTable[this.category][other.category];
String mergedType = null;
TypeProto mergedType = null;
if (mergedCategory == REFERENCE) {
// TODO: uncomment
// TODO: make sure to handle unresolved types in getCommonSuperclass (using U prefix?)
//mergedType = ClassPath.getCommonSuperclass(this.type, other.type);
assert type != null;
assert other.type != null;
mergedType = this.type.getCommonSuperclass(other.type);
} else if (mergedCategory == UNINIT_REF || mergedCategory == UNINIT_THIS) {
if (this.category == UNKNOWN) {
return other;
@ -291,7 +263,7 @@ public class RegisterType {
return RegisterType.getRegisterType(mergedCategory, mergedType);
}
public static RegisterType getRegisterType(byte category, String classType) {
public static RegisterType getRegisterType(byte category, @Nullable TypeProto typeProto) {
switch (category) {
case UNKNOWN:
return UNKNOWN_TYPE;
@ -310,7 +282,7 @@ public class RegisterType {
case SHORT:
return SHORT_TYPE;
case POS_SHORT:
return POS_BYTE_TYPE;
return POS_SHORT_TYPE;
case CHAR:
return CHAR_TYPE;
case INTEGER:
@ -329,6 +301,6 @@ public class RegisterType {
return CONFLICTED_TYPE;
}
return new RegisterType(category, classType);
return new RegisterType(category, typeProto);
}
}

View File

@ -32,17 +32,13 @@
package org.jf.dexlib2.analysis;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class ArrayClassProto extends ClassProto {
public ArrayClassProto(@Nonnull ClassPath classPath, @Nonnull String type) {
super(classPath, type);
}
@Nonnull @Override
public ClassProto getCommonSuperclass(@Nonnull ClassProto other) {
if (other instanceof ArrayClassProto) {
// TODO: implement this
}
return super.getCommonSuperclass(other);
}
public interface TypeProto {
@Nonnull ClassPath getClassPath();
@Nonnull String getType();
boolean isInterface();
boolean implementsInterface(@Nonnull String iface);
@Nullable String getSuperclass();
@Nonnull TypeProto getCommonSuperclass(@Nonnull TypeProto other);
}

View File

@ -0,0 +1,66 @@
/*
* 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 javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class UnknownClassProto implements TypeProto {
@Nonnull protected final ClassPath classPath;
public UnknownClassProto(@Nonnull ClassPath classPath) {
this.classPath = classPath;
}
@Override public String toString() { return "Ujava/lang/Object;"; }
@Nonnull @Override public ClassPath getClassPath() { return classPath; }
@Nullable @Override public String getSuperclass() { return null; }
@Override public boolean isInterface() { return false; }
@Override public boolean implementsInterface(@Nonnull String iface) { return false; }
@Nonnull @Override public TypeProto getCommonSuperclass(@Nonnull TypeProto other) {
if (other.getType().equals("Ljava/lang/Object;")) {
return other;
}
if (other instanceof ArrayProto) {
// if it's an array class, it's safe to assume this unknown class isn't related, and so
// java.lang.Object is the only possible superclass
return classPath.getClass("Ljava/lang/Object;");
}
return this;
}
@Nonnull @Override public String getType() {
// use the otherwise used U prefix for an unknown/unresolvable class
return "Ujava/lang/Object;";
}
}

View File

@ -0,0 +1,154 @@
/*
* 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.reflection;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterators;
import org.jf.dexlib2.analysis.reflection.util.ReflectionUtils;
import org.jf.dexlib2.base.reference.BaseTypeReference;
import org.jf.dexlib2.iface.Annotation;
import org.jf.dexlib2.iface.ClassDef;
import org.jf.dexlib2.iface.Field;
import org.jf.dexlib2.iface.Method;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.Set;
/**
* Wraps a ClassDef around a class loaded in the current VM
*
* Only supports the basic information exposed by ClassProto
*/
public class ReflectionClassDef extends BaseTypeReference implements ClassDef {
private final Class cls;
public ReflectionClassDef(Class cls) {
this.cls = cls;
}
@Override public int getAccessFlags() {
// the java modifiers appear to be the same as the dex access flags
return cls.getModifiers();
}
@Nullable @Override public String getSuperclass() {
if (Modifier.isInterface(cls.getModifiers())) {
return "Ljava/lang/Object;";
}
Class superClass = cls.getSuperclass();
if (superClass == null) {
return null;
}
return ReflectionUtils.javaToDexName(superClass.getName());
}
@Nonnull @Override public Set<String> getInterfaces() {
return new AbstractSet<String>() {
@Nonnull @Override public Iterator<String> iterator() {
return Iterators.transform(Iterators.forArray(cls.getInterfaces()), new Function<Class, String>() {
@Nullable @Override public String apply(@Nullable Class input) {
if (input == null) {
return null;
}
return ReflectionUtils.javaToDexName(input.getName());
}
});
}
@Override public int size() {
return cls.getInterfaces().length;
}
};
}
@Nullable @Override public String getSourceFile() {
return null;
}
@Nonnull @Override public Set<? extends Annotation> getAnnotations() {
return ImmutableSet.of();
}
@Nonnull @Override public Set<? extends Field> getFields() {
return new AbstractSet<Field>() {
@Nonnull @Override public Iterator<Field> iterator() {
return Iterators.transform(Iterators.forArray(cls.getDeclaredFields()),
new Function<java.lang.reflect.Field, Field>() {
@Nullable @Override public Field apply(@Nullable java.lang.reflect.Field input) {
return new ReflectionField(input);
}
});
}
@Override public int size() {
return cls.getDeclaredFields().length;
}
};
}
@Nonnull @Override public Set<? extends Method> getMethods() {
return new AbstractSet<Method>() {
@Nonnull @Override public Iterator<Method> iterator() {
Iterator<Method> constructorIterator =
Iterators.transform(Iterators.forArray(cls.getDeclaredConstructors()),
new Function<Constructor, Method>() {
@Nullable @Override public Method apply(@Nullable Constructor input) {
return new ReflectionConstructor(input);
}
});
Iterator<Method> methodIterator =
Iterators.transform(Iterators.forArray(cls.getDeclaredMethods()),
new Function<java.lang.reflect.Method, Method>() {
@Nullable @Override public Method apply(@Nullable java.lang.reflect.Method input) {
return new ReflectionMethod(input);
}
});
return Iterators.concat(constructorIterator, methodIterator);
}
@Override public int size() {
return cls.getDeclaredMethods().length + cls.getDeclaredConstructors().length;
}
};
}
@Nonnull @Override public String getType() {
return ReflectionUtils.javaToDexName(cls.getName());
}
}

View File

@ -0,0 +1,121 @@
/*
* 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.reflection;
import com.google.common.collect.ImmutableSet;
import org.jf.dexlib2.analysis.reflection.util.ReflectionUtils;
import org.jf.dexlib2.base.BaseMethodParameter;
import org.jf.dexlib2.base.reference.BaseMethodReference;
import org.jf.dexlib2.iface.Annotation;
import org.jf.dexlib2.iface.Method;
import org.jf.dexlib2.iface.MethodImplementation;
import org.jf.dexlib2.iface.MethodParameter;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.reflect.Constructor;
import java.util.AbstractList;
import java.util.List;
import java.util.Set;
public class ReflectionConstructor extends BaseMethodReference implements Method {
private final Constructor constructor;
public ReflectionConstructor(Constructor constructor) {
this.constructor = constructor;
}
@Nonnull @Override public List<? extends MethodParameter> getParameters() {
final Constructor method = this.constructor;
return new AbstractList<MethodParameter>() {
private final Class[] parameters = method.getParameterTypes();
@Override public MethodParameter get(final int index) {
return new BaseMethodParameter() {
@Nonnull @Override public Set<? extends Annotation> getAnnotations() {
return ImmutableSet.of();
}
@Nullable @Override public String getName() {
return null;
}
@Nonnull @Override public String getType() {
return ReflectionUtils.javaToDexName(parameters[index].getName());
}
};
}
@Override public int size() {
return parameters.length;
}
};
}
@Override public int getAccessFlags() {
return constructor.getModifiers();
}
@Nonnull @Override public Set<? extends Annotation> getAnnotations() {
return ImmutableSet.of();
}
@Nullable @Override public MethodImplementation getImplementation() {
return null;
}
@Nonnull @Override public String getDefiningClass() {
return ReflectionUtils.javaToDexName(constructor.getDeclaringClass().getName());
}
@Nonnull @Override public String getName() {
return constructor.getName();
}
@Nonnull @Override public List<String> getParameterTypes() {
return new AbstractList<String>() {
private final List<? extends MethodParameter> parameters = getParameters();
@Override public String get(int index) {
return parameters.get(index).getType();
}
@Override public int size() {
return parameters.size();
}
};
}
@Nonnull @Override public String getReturnType() {
return "V";
}
}

View File

@ -0,0 +1,75 @@
/*
* 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.reflection;
import com.google.common.collect.ImmutableSet;
import org.jf.dexlib2.analysis.reflection.util.ReflectionUtils;
import org.jf.dexlib2.base.reference.BaseFieldReference;
import org.jf.dexlib2.iface.Annotation;
import org.jf.dexlib2.iface.Field;
import org.jf.dexlib2.iface.value.EncodedValue;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Set;
public class ReflectionField extends BaseFieldReference implements Field {
private final java.lang.reflect.Field field;
public ReflectionField(java.lang.reflect.Field field) {
this.field = field;
}
@Override public int getAccessFlags() {
return field.getModifiers();
}
@Nullable @Override public EncodedValue getInitialValue() {
return null;
}
@Nonnull @Override public Set<? extends Annotation> getAnnotations() {
return ImmutableSet.of();
}
@Nonnull @Override public String getDefiningClass() {
return ReflectionUtils.javaToDexName(field.getDeclaringClass().getName());
}
@Nonnull @Override public String getName() {
return field.getName();
}
@Nonnull @Override public String getType() {
return ReflectionUtils.javaToDexName(field.getType().getName());
}
}

View File

@ -0,0 +1,120 @@
/*
* 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.reflection;
import com.google.common.collect.ImmutableSet;
import org.jf.dexlib2.analysis.reflection.util.ReflectionUtils;
import org.jf.dexlib2.base.BaseMethodParameter;
import org.jf.dexlib2.base.reference.BaseMethodReference;
import org.jf.dexlib2.iface.Annotation;
import org.jf.dexlib2.iface.Method;
import org.jf.dexlib2.iface.MethodImplementation;
import org.jf.dexlib2.iface.MethodParameter;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.AbstractList;
import java.util.List;
import java.util.Set;
public class ReflectionMethod extends BaseMethodReference implements Method {
private final java.lang.reflect.Method method;
public ReflectionMethod(java.lang.reflect.Method method) {
this.method = method;
}
@Nonnull @Override public List<? extends MethodParameter> getParameters() {
final java.lang.reflect.Method method = this.method;
return new AbstractList<MethodParameter>() {
private final Class[] parameters = method.getParameterTypes();
@Override public MethodParameter get(final int index) {
return new BaseMethodParameter() {
@Nonnull @Override public Set<? extends Annotation> getAnnotations() {
return ImmutableSet.of();
}
@Nullable @Override public String getName() {
return null;
}
@Nonnull @Override public String getType() {
return ReflectionUtils.javaToDexName(parameters[index].getName());
}
};
}
@Override public int size() {
return parameters.length;
}
};
}
@Override public int getAccessFlags() {
return method.getModifiers();
}
@Nonnull @Override public Set<? extends Annotation> getAnnotations() {
return ImmutableSet.of();
}
@Nullable @Override public MethodImplementation getImplementation() {
return null;
}
@Nonnull @Override public String getDefiningClass() {
return ReflectionUtils.javaToDexName(method.getDeclaringClass().getName());
}
@Nonnull @Override public String getName() {
return method.getName();
}
@Nonnull @Override public List<String> getParameterTypes() {
return new AbstractList<String>() {
private final List<? extends MethodParameter> parameters = getParameters();
@Override public String get(int index) {
return parameters.get(index).getType();
}
@Override public int size() {
return parameters.size();
}
};
}
@Nonnull @Override public String getReturnType() {
return ReflectionUtils.javaToDexName(method.getReturnType().getName());
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.reflection.util;
public class ReflectionUtils {
public static String javaToDexName(String javaName) {
javaName = javaName.replace('.', '/');
if (javaName.length() > 1 && javaName.charAt(javaName.length()-1) != ';') {
javaName = 'L' + javaName + ';';
}
return javaName;
}
}

View File

@ -0,0 +1,97 @@
/*
* 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.util;
import org.jf.dexlib2.analysis.TypeProto;
import org.jf.dexlib2.analysis.UnresolvedClassException;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class TypeProtoUtils {
/**
* Get the chain of superclasses of the given class. The first element will be the immediate superclass followed by
* it's superclass, etc. up to java.lang.Object.
*
* Returns an empty iterable if called on java.lang.Object or a primitive.
*
* If any class in the superclass chain can't be resolved, the iterable will return Ujava/lang/Object; to represent
* the unknown class.
*
* @return An iterable containing the superclasses of this class.
*/
@Nonnull
public static Iterable<TypeProto> getSuperclassChain(@Nonnull final TypeProto typeProto) {
return new Iterable<TypeProto>() {
@Override public Iterator<TypeProto> iterator() {
return new Iterator<TypeProto>() {
@Nullable private TypeProto type = getSuperclassAsTypeProto(typeProto);
@Override public boolean hasNext() {
return type != null;
}
@Override public TypeProto next() {
TypeProto type = this.type;
if (type == null) {
throw new NoSuchElementException();
}
this.type = getSuperclassAsTypeProto(type);
return type;
}
@Override public void remove() {
throw new UnsupportedOperationException();
}
};
}
};
}
@Nullable
public static TypeProto getSuperclassAsTypeProto(@Nonnull TypeProto type) {
try {
String next = type.getSuperclass();
if (next != null) {
return type.getClassPath().getClass(next);
} else {
return null;
}
} catch (UnresolvedClassException ex) {
return type.getClassPath().getUnknownClass();
}
}
}

View File

@ -37,5 +37,9 @@ public final class TypeUtils {
return c == 'J' || c == 'D';
}
public static boolean isPrimitiveType(String type) {
return type.length() == 1;
}
private TypeUtils() {}
}

View File

@ -0,0 +1,314 @@
/*
* 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.collect.ImmutableSet;
import junit.framework.Assert;
import org.jf.dexlib2.immutable.ImmutableDexFile;
import org.junit.Test;
import java.io.IOException;
public class CommonSuperclassTest {
// object tree:
// object
// one
// onetwo
// onetwothree
// onethree
// five (undefined class)
// fivetwo
// fivetwothree
// fivethree
private final ClassPath classPath;
public CommonSuperclassTest() throws IOException {
classPath = new ClassPath(new ImmutableDexFile(ImmutableSet.of(
TestUtils.makeClassDef("Ljava/lang/Object;", null),
TestUtils.makeClassDef("Ltest/one;", "Ljava/lang/Object;"),
TestUtils.makeClassDef("Ltest/two;", "Ljava/lang/Object;"),
TestUtils.makeClassDef("Ltest/onetwo;", "Ltest/one;"),
TestUtils.makeClassDef("Ltest/onetwothree;", "Ltest/onetwo;"),
TestUtils.makeClassDef("Ltest/onethree;", "Ltest/one;"),
TestUtils.makeClassDef("Ltest/fivetwo;", "Ltest/five;"),
TestUtils.makeClassDef("Ltest/fivetwothree;", "Ltest/fivetwo;"),
TestUtils.makeClassDef("Ltest/fivethree;", "Ltest/five;"),
TestUtils.makeInterfaceDef("Ljava/lang/Cloneable;"),
TestUtils.makeInterfaceDef("Ljava/io/Serializable;"),
// basic class and interface
TestUtils.makeClassDef("Liface/classiface1;", "Ljava/lang/Object;", "Liface/iface1;"),
TestUtils.makeInterfaceDef("Liface/iface1;"),
// a more complex interface tree
TestUtils.makeInterfaceDef("Liface/base1;"),
// implements undefined interface
TestUtils.makeInterfaceDef("Liface/sub1;", "Liface/base1;", "Liface/base2;"),
// this implements sub1, so that its interfaces can't be fully resolved either
TestUtils.makeInterfaceDef("Liface/sub2;", "Liface/base1;", "Liface/sub1;"),
TestUtils.makeInterfaceDef("Liface/sub3;", "Liface/base1;"),
TestUtils.makeInterfaceDef("Liface/sub4;", "Liface/base1;", "Liface/sub3;"),
TestUtils.makeClassDef("Liface/classsub1;", "Ljava/lang/Object;", "Liface/sub1;"),
TestUtils.makeClassDef("Liface/classsub2;", "Ljava/lang/Object;", "Liface/sub2;"),
TestUtils.makeClassDef("Liface/classsub3;", "Ljava/lang/Object;", "Liface/sub3;", "Liface/base;"),
TestUtils.makeClassDef("Liface/classsub4;", "Ljava/lang/Object;", "Liface/sub3;", "Liface/sub4;"),
TestUtils.makeClassDef("Liface/classsubsub4;", "Liface/classsub4;"),
TestUtils.makeClassDef("Liface/classsub1234;", "Ljava/lang/Object;", "Liface/sub1;", "Liface/sub2;",
"Liface/sub3;", "Liface/sub4;")
)));
}
public void superclassTest(String commonSuperclass,
String type1, String type2) {
TypeProto commonSuperclassProto = classPath.getClass(commonSuperclass);
TypeProto type1Proto = classPath.getClass(type1);
TypeProto type2Proto = classPath.getClass(type2);
Assert.assertSame(commonSuperclassProto, type1Proto.getCommonSuperclass(type2Proto));
Assert.assertSame(commonSuperclassProto, type2Proto.getCommonSuperclass(type1Proto));
}
@Test
public void testGetCommonSuperclass() throws IOException {
String object = "Ljava/lang/Object;";
String unknown = "Ujava/lang/Object;";
String one = "Ltest/one;";
String two = "Ltest/two;";
String onetwo = "Ltest/onetwo;";
String onetwothree = "Ltest/onetwothree;";
String onethree = "Ltest/onethree;";
String five = "Ltest/five;";
String fivetwo = "Ltest/fivetwo;";
String fivetwothree = "Ltest/fivetwothree;";
String fivethree = "Ltest/fivethree;";
// same object
superclassTest(object, object, object);
superclassTest(unknown, unknown, unknown);
superclassTest(one, one, one);
superclassTest(onetwo, onetwo, onetwo);
superclassTest(onetwothree, onetwothree, onetwothree);
superclassTest(onethree, onethree, onethree);
superclassTest(five, five, five);
superclassTest(fivetwo, fivetwo, fivetwo);
superclassTest(fivetwothree, fivetwothree, fivetwothree);
superclassTest(fivethree, fivethree, fivethree);
// same value, but different object
Assert.assertEquals(
onetwo,
classPath.getClass(onetwo).getCommonSuperclass(new ClassProto(classPath, onetwo)).getType());
// other object is superclass
superclassTest(object, object, one);
// other object is superclass two levels up
superclassTest(object, object, onetwo);
// unknown and non-object class
superclassTest(unknown, one, unknown);
// unknown and object class
superclassTest(object, object, unknown);
// siblings
superclassTest(one, onetwo, onethree);
// nephew
superclassTest(one, onethree, onetwothree);
// unrelated
superclassTest(object, one, two);
// undefined superclass and object
superclassTest(object, fivetwo, object);
// undefined class and unrelated type
superclassTest(unknown, one, five);
// undefined superclass and unrelated type
superclassTest(unknown, one, fivetwo);
// undefined ancestor and unrelated type
superclassTest(unknown, one, fivetwothree);
// undefined class and direct subclass
superclassTest(five, five, fivetwo);
// undefined class and descendent
superclassTest(five, five, fivetwothree);
// undefined superclass and direct subclass
superclassTest(fivetwo, fivetwo, fivetwothree);
// siblings with undefined superclass
superclassTest(five, fivetwo, fivethree);
// undefined superclass and nephew
superclassTest(five, fivethree, fivetwothree);
}
@Test
public void testGetCommonSuperclass_interfaces() {
String classiface1 = "Liface/classiface1;";
String iface1 = "Liface/iface1;";
String base1 = "Liface/base1;";
String base2 = "Liface/base2;";
String sub1 = "Liface/sub1;";
String sub2 = "Liface/sub2;";
String sub3 = "Liface/sub3;";
String sub4 = "Liface/sub4;";
String classsub1 = "Liface/classsub1;";
String classsub2 = "Liface/classsub2;";
String classsub3 = "Liface/classsub3;";
String classsub4 = "Liface/classsub4;";
String classsubsub4 = "Liface/classsubsub4;";
String classsub1234 = "Liface/classsub1234;";
String object = "Ljava/lang/Object;";
String unknown = "Ujava/lang/Object;";
superclassTest(iface1, classiface1, iface1);
superclassTest(base1, base1, base1);
superclassTest(base1, base1, sub1);
superclassTest(base1, base1, classsub1);
superclassTest(base1, base1, sub2);
superclassTest(base1, base1, classsub2);
superclassTest(base1, base1, sub3);
superclassTest(base1, base1, classsub3);
superclassTest(base1, base1, sub4);
superclassTest(base1, base1, classsub4);
superclassTest(base1, base1, classsubsub4);
superclassTest(base1, base1, classsub1234);
superclassTest(object, sub3, iface1);
superclassTest(unknown, sub2, iface1);
superclassTest(unknown, sub1, iface1);
superclassTest(base2, base2, sub1);
superclassTest(base2, base2, classsub1);
superclassTest(base2, base2, sub2);
superclassTest(base2, base2, classsub2);
superclassTest(base2, base2, classsub1234);
superclassTest(unknown, iface1, classsub1234);
superclassTest(sub1, sub1, classsub1);
superclassTest(sub2, sub2, classsub2);
superclassTest(sub1, sub1, classsub2);
superclassTest(sub3, sub3, classsub3);
superclassTest(sub4, sub4, classsub4);
superclassTest(sub3, sub3, classsub4);
superclassTest(object, sub2, classsub4);
superclassTest(object, sub1, classsub4);
superclassTest(sub1, sub2, sub1);
superclassTest(sub1, sub1, classsub1234);
superclassTest(sub2, sub2, classsub1234);
superclassTest(sub3, sub3, classsub1234);
superclassTest(sub4, sub4, classsub1234);
superclassTest(unknown, sub3, classsub1);
superclassTest(unknown, sub4, classsub1);
superclassTest(unknown, sub3, classsub2);
superclassTest(unknown, sub4, classsub2);
superclassTest(unknown, sub4, base2);
superclassTest(unknown, classsub4, base2);
}
@Test
public void testGetCommonSuperclass_arrays() throws IOException {
String object = "Ljava/lang/Object;";
String one = "Ltest/one;";
String unknown = "Ujava/lang/Object;";
String cloneable = "Ljava/lang/Cloneable;";
String serializable = "Ljava/io/Serializable;";
String object1 = "[Ljava/lang/Object;";
String one1 = "[Ltest/one;";
String one2 = "[[Ltest/one;";
String two1 = "[Ltest/two;";
String onetwo1 = "[Ltest/onetwo;";
String onetwo2 = "[[Ltest/onetwo;";
String onethree1 = "[Ltest/onethree;";
String onethree2 = "[[Ltest/onethree;";
String five = "Ltest/five;";
String five1 = "[Ltest/five;";
String unknown1 = "[Ujava/lang/Object;";
String int1 = "[I";
String int2 = "[[I";
String float1 = "[F";
superclassTest(one1, one1, one1);
superclassTest(object1, object1, one1);
superclassTest(one1, onetwo1, onethree1);
superclassTest(one1, one1, onethree1);
superclassTest(object1, one1, two1);
superclassTest(one2, one2, one2);
superclassTest(one2, one2, onetwo2);
superclassTest(one2, onetwo2, onethree2);
superclassTest(object1, one1, one2);
superclassTest(object1, two1, one2);
superclassTest(unknown1, five1, one1);
superclassTest(object1, five1, one2);
superclassTest(unknown1, one1, unknown1);
superclassTest(object, one1, one);
superclassTest(object, object1, one);
superclassTest(object, onetwo1, one);
superclassTest(object, five1, one);
superclassTest(object, one2, one);
superclassTest(object, one1, unknown);
superclassTest(object, unknown1, unknown);
superclassTest(cloneable, one1, cloneable);
superclassTest(serializable, one1, serializable);
superclassTest(object, one1, five);
superclassTest(int1, int1, int1);
superclassTest(object, int1, float1);
superclassTest(object, int1, int2);
}
}

View File

@ -0,0 +1,51 @@
/*
* 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.collect.ImmutableSet;
import org.jf.dexlib2.AccessFlags;
import org.jf.dexlib2.iface.ClassDef;
import org.jf.dexlib2.immutable.ImmutableClassDef;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class TestUtils {
public static ClassDef makeClassDef(@Nonnull String classType, @Nullable String superType, String... interfaces) {
return new ImmutableClassDef(classType, 0, superType, ImmutableSet.copyOf(interfaces), null, null, null, null);
}
public static ClassDef makeInterfaceDef(@Nonnull String classType, String... interfaces) {
return new ImmutableClassDef(classType, AccessFlags.INTERFACE.getValue(), "Ljava/lang/Object;",
ImmutableSet.copyOf(interfaces), null, null, null, null);
}
}

View File

@ -0,0 +1,105 @@
/*
* 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.util;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import junit.framework.Assert;
import org.jf.dexlib2.analysis.ClassPath;
import org.jf.dexlib2.analysis.TestUtils;
import org.jf.dexlib2.analysis.TypeProto;
import org.jf.dexlib2.iface.ClassDef;
import org.jf.dexlib2.immutable.ImmutableDexFile;
import org.junit.Test;
import java.io.IOException;
public class SuperclassChainTest {
@Test
public void testGetSuperclassChain() throws IOException {
ClassDef objectClassDef = TestUtils.makeClassDef("Ljava/lang/Object;", null);
ClassDef oneClassDef = TestUtils.makeClassDef("Ltest/one;", "Ljava/lang/Object;");
ClassDef twoClassDef = TestUtils.makeClassDef("Ltest/two;", "Ltest/one;");
ClassDef threeClassDef = TestUtils.makeClassDef("Ltest/three;", "Ltest/two;");
ImmutableSet<ClassDef> classes = ImmutableSet.<ClassDef>of(
objectClassDef, oneClassDef, twoClassDef, threeClassDef);
ClassPath classPath = new ClassPath(new ImmutableDexFile(classes));
TypeProto objectClassProto = classPath.getClass("Ljava/lang/Object;");
TypeProto oneClassProto = classPath.getClass("Ltest/one;");
TypeProto twoClassProto = classPath.getClass("Ltest/two;");
TypeProto threeClassProto = classPath.getClass("Ltest/three;");
Assert.assertEquals(
ImmutableList.<TypeProto>of(),
ImmutableList.copyOf(TypeProtoUtils.getSuperclassChain(objectClassProto)));
Assert.assertEquals(
ImmutableList.<TypeProto>of(objectClassProto),
ImmutableList.copyOf(TypeProtoUtils.getSuperclassChain(oneClassProto)));
Assert.assertEquals(
ImmutableList.<TypeProto>of(oneClassProto, objectClassProto),
ImmutableList.copyOf(TypeProtoUtils.getSuperclassChain(twoClassProto)));
Assert.assertEquals(
ImmutableList.<TypeProto>of(twoClassProto, oneClassProto, objectClassProto),
ImmutableList.copyOf(TypeProtoUtils.getSuperclassChain(threeClassProto)));
}
@Test
public void testGetSuperclassChain_Unresolved() throws IOException {
// Ltest/one; isn't defined
ClassDef twoClassDef = TestUtils.makeClassDef("Ltest/two;", "Ltest/one;");
ClassDef threeClassDef = TestUtils.makeClassDef("Ltest/three;", "Ltest/two;");
ImmutableSet<ClassDef> classes = ImmutableSet.<ClassDef>of(twoClassDef, threeClassDef);
ClassPath classPath = new ClassPath(new ImmutableDexFile(classes));
TypeProto unknownClassProto = classPath.getUnknownClass();
TypeProto oneClassProto = classPath.getClass("Ltest/one;");
TypeProto twoClassProto = classPath.getClass("Ltest/two;");
TypeProto threeClassProto = classPath.getClass("Ltest/three;");
Assert.assertEquals(
ImmutableList.<TypeProto>of(oneClassProto, unknownClassProto),
ImmutableList.copyOf(TypeProtoUtils.getSuperclassChain(twoClassProto)));
Assert.assertEquals(
ImmutableList.<TypeProto>of(twoClassProto, oneClassProto, unknownClassProto),
ImmutableList.copyOf(TypeProtoUtils.getSuperclassChain(threeClassProto)));
}
}