mirror of
https://github.com/revanced/smali.git
synced 2025-05-23 18:16:23 +02:00
Fully implement common superclass functionality
This commit is contained in:
parent
8f383501c1
commit
39e4d4487e
127
dexlib2/src/main/java/org/jf/dexlib2/analysis/ArrayProto.java
Normal file
127
dexlib2/src/main/java/org/jf/dexlib2/analysis/ArrayProto.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
@ -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;";
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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";
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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() {}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
51
dexlib2/src/test/java/org/jf/dexlib2/analysis/TestUtils.java
Normal file
51
dexlib2/src/test/java/org/jf/dexlib2/analysis/TestUtils.java
Normal 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);
|
||||
}
|
||||
}
|
@ -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)));
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user