WALA/com.ibm.wala.core/src/com/ibm/wala/types/TypeReference.java

647 lines
24 KiB
Java

/*******************************************************************************
* Copyright (c) 2002 - 2006 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package com.ibm.wala.types;
import static com.ibm.wala.types.TypeName.ArrayMask;
import static com.ibm.wala.types.TypeName.ElementBits;
import static com.ibm.wala.types.TypeName.PrimitiveMask;
import java.io.Serializable;
import java.util.Map;
import com.ibm.wala.util.collections.HashMapFactory;
/**
* A class to represent the reference in a class file to some type (class, primitive or array). A type reference is
* uniquely defined by
* <ul>
* <li> an initiating class loader
* <li> a type name
* </ul>
* Resolving a TypeReference to a Type can be an expensive operation. Therefore we canonicalize TypeReference instances
* and cache the result of resolution.
*/
public final class TypeReference implements Serializable {
/* Serial version */
private static final long serialVersionUID = -3256390509887654327L;
/**
* NOTE: initialisation order is important!
*
* TypeReferences are canonical.
*/
/**
* Used for fast access to primitives. Primitives appear in the main dictionary also.
*/
private final static Map<TypeName, TypeReference> primitiveMap = HashMapFactory.make();
/**
* Used to canonicalize TypeReferences.
*/
private final static Map<Key, TypeReference> dictionary = HashMapFactory.make();
/*********************************************************************************************************************
* Primitive Dispatch *
********************************************************************************************************************/
public final static TypeName BooleanName = TypeName.string2TypeName("Z");
public final static byte BooleanTypeCode = 'Z';
public final static TypeReference Boolean = makePrimitive(BooleanName);
public final static TypeName ByteName = TypeName.string2TypeName("B");
public final static byte ByteTypeCode = 'B';
public final static TypeReference Byte = makePrimitive(ByteName);
public final static TypeName CharName = TypeName.string2TypeName("C");
public final static byte CharTypeCode = 'C';
public final static TypeReference Char = makePrimitive(CharName);
public final static TypeName DoubleName = TypeName.string2TypeName("D");
public final static byte DoubleTypeCode = 'D';
public final static TypeReference Double = makePrimitive(DoubleName);
public final static TypeName FloatName = TypeName.string2TypeName("F");
public final static byte FloatTypeCode = 'F';
public final static TypeReference Float = makePrimitive(FloatName);
public final static TypeName IntName = TypeName.string2TypeName("I");
public final static byte IntTypeCode = 'I';
public final static TypeReference Int = makePrimitive(IntName);
public final static TypeName LongName = TypeName.string2TypeName("J");
public final static byte LongTypeCode = 'J';
public final static TypeReference Long = makePrimitive(LongName);
public final static TypeName ShortName = TypeName.string2TypeName("S");
public final static byte ShortTypeCode = 'S';
public final static TypeReference Short = makePrimitive(ShortName);
public final static TypeName VoidName = TypeName.string2TypeName("V");
public final static byte VoidTypeCode = 'V';
public final static TypeReference Void = makePrimitive(VoidName);
public final static byte OtherPrimitiveTypeCode = 'P';
/*********************************************************************************************************************
* Primitive Array Dispatch *
********************************************************************************************************************/
public final static TypeReference BooleanArray = findOrCreateArrayOf(Boolean);
public final static TypeReference ByteArray = findOrCreateArrayOf(Byte);
public final static TypeReference CharArray = findOrCreateArrayOf(Char);
public final static TypeReference DoubleArray = findOrCreateArrayOf(Double);
public final static TypeReference FloatArray = findOrCreateArrayOf(Float);
public final static TypeReference IntArray = findOrCreateArrayOf(Int);
public final static TypeReference LongArray = findOrCreateArrayOf(Long);
public final static TypeReference ShortArray = findOrCreateArrayOf(Short);
/*********************************************************************************************************************
* Special object types *
********************************************************************************************************************/
private final static TypeName JavaLangArithmeticExceptionName = TypeName.string2TypeName("Ljava/lang/ArithmeticException");
public final static TypeReference JavaLangArithmeticException = findOrCreate(ClassLoaderReference.Primordial,
JavaLangArithmeticExceptionName);
private final static TypeName JavaLangArrayStoreExceptionName = TypeName.string2TypeName("Ljava/lang/ArrayStoreException");
public final static TypeReference JavaLangArrayStoreException = findOrCreate(ClassLoaderReference.Primordial,
JavaLangArrayStoreExceptionName);
private final static TypeName JavaLangArrayIndexOutOfBoundsExceptionName = TypeName
.string2TypeName("Ljava/lang/ArrayIndexOutOfBoundsException");
public final static TypeReference JavaLangArrayIndexOutOfBoundsException = findOrCreate(ClassLoaderReference.Primordial,
JavaLangArrayIndexOutOfBoundsExceptionName);
private final static TypeName JavaLangClassName = TypeName.string2TypeName("Ljava/lang/Class");
public final static TypeReference JavaLangClass = findOrCreate(ClassLoaderReference.Primordial, JavaLangClassName);
private final static TypeName JavaLangInvokeMethodHandleName = TypeName.string2TypeName("Ljava/lang/invoke/MethodHandle");
public final static TypeReference JavaLangInvokeMethodHandle = findOrCreate(ClassLoaderReference.Primordial, JavaLangInvokeMethodHandleName);
private final static TypeName JavaLangInvokeMethodTypeName = TypeName.string2TypeName("Ljava/lang/invoke/MethodType");
public final static TypeReference JavaLangInvokeMethodType = findOrCreate(ClassLoaderReference.Primordial, JavaLangInvokeMethodTypeName);
private final static TypeName JavaLangClassCastExceptionName = TypeName.string2TypeName("Ljava/lang/ClassCastException");
public final static TypeReference JavaLangClassCastException = findOrCreate(ClassLoaderReference.Primordial,
JavaLangClassCastExceptionName);
private final static TypeName JavaLangComparableName = TypeName.string2TypeName("Ljava/lang/Comparable");
public final static TypeReference JavaLangComparable = findOrCreate(ClassLoaderReference.Primordial, JavaLangComparableName);
private final static TypeName JavaLangReflectConstructorName = TypeName.string2TypeName("Ljava/lang/reflect/Constructor");
public final static TypeReference JavaLangReflectConstructor = findOrCreate(ClassLoaderReference.Primordial,
JavaLangReflectConstructorName);
private final static TypeName JavaLangReflectMethodName = TypeName.string2TypeName("Ljava/lang/reflect/Method");
public final static TypeReference JavaLangReflectMethod = findOrCreate(ClassLoaderReference.Primordial,
JavaLangReflectMethodName);
private final static TypeName JavaLangEnumName = TypeName.string2TypeName("Ljava/lang/Enum");
public final static TypeReference JavaLangEnum = findOrCreate(ClassLoaderReference.Primordial, JavaLangEnumName);
private final static TypeName JavaLangErrorName = TypeName.string2TypeName("Ljava/lang/Error");
public final static TypeReference JavaLangError = findOrCreate(ClassLoaderReference.Primordial, JavaLangErrorName);
private final static TypeName JavaLangExceptionName = TypeName.string2TypeName("Ljava/lang/Exception");
public final static TypeReference JavaLangException = findOrCreate(ClassLoaderReference.Primordial, JavaLangExceptionName);
private final static TypeName JavaLangNegativeArraySizeExceptionName = TypeName
.string2TypeName("Ljava/lang/NegativeArraySizeException");
public final static TypeReference JavaLangNegativeArraySizeException = findOrCreate(ClassLoaderReference.Primordial,
JavaLangNegativeArraySizeExceptionName);
private final static TypeName JavaLangNullPointerExceptionName = TypeName.string2TypeName("Ljava/lang/NullPointerException");
public final static TypeReference JavaLangNullPointerException = findOrCreate(ClassLoaderReference.Primordial,
JavaLangNullPointerExceptionName);
private final static TypeName JavaLangRuntimeExceptionName = TypeName.string2TypeName("Ljava/lang/RuntimeException");
public final static TypeReference JavaLangRuntimeException = findOrCreate(ClassLoaderReference.Primordial,
JavaLangRuntimeExceptionName);
private final static TypeName JavaLangClassNotFoundExceptionName = TypeName.string2TypeName("Ljava/lang/ClassNotFoundException");
public final static TypeReference JavaLangClassNotFoundException = findOrCreate(ClassLoaderReference.Primordial,
JavaLangClassNotFoundExceptionName);
private final static TypeName JavaLangOutOfMemoryErrorName = TypeName.string2TypeName("Ljava/lang/OutOfMemoryError");
public final static TypeReference JavaLangOutOfMemoryError = findOrCreate(ClassLoaderReference.Primordial,
JavaLangOutOfMemoryErrorName);
private final static TypeName JavaLangExceptionInInitializerErrorName = TypeName
.string2TypeName("Ljava/lang/ExceptionInInitializerError");
public final static TypeReference JavaLangExceptionInInitializerError = findOrCreate(ClassLoaderReference.Primordial,
JavaLangExceptionInInitializerErrorName);
private final static TypeName JavaLangObjectName = TypeName.string2TypeName("Ljava/lang/Object");
public final static TypeReference JavaLangObject = findOrCreate(ClassLoaderReference.Primordial, JavaLangObjectName);
private final static TypeName JavaLangStackTraceElementName = TypeName.string2TypeName("Ljava/lang/StackTraceElement");
public final static TypeReference JavaLangStackTraceElement = findOrCreate(ClassLoaderReference.Primordial,
JavaLangStackTraceElementName);
private final static TypeName JavaLangStringName = TypeName.string2TypeName("Ljava/lang/String");
public final static TypeReference JavaLangString = findOrCreate(ClassLoaderReference.Primordial, JavaLangStringName);
private final static TypeName JavaLangStringBufferName = TypeName.string2TypeName("Ljava/lang/StringBuffer");
public final static TypeReference JavaLangStringBuffer = findOrCreate(ClassLoaderReference.Primordial, JavaLangStringBufferName);
private final static TypeName JavaLangStringBuilderName = TypeName.string2TypeName("Ljava/lang/StringBuilder");
public final static TypeReference JavaLangStringBuilder = findOrCreate(ClassLoaderReference.Primordial, JavaLangStringBuilderName);
private final static TypeName JavaLangThreadName = TypeName.string2TypeName("Ljava/lang/Thread");
public final static TypeReference JavaLangThread = findOrCreate(ClassLoaderReference.Primordial, JavaLangThreadName);
private final static TypeName JavaLangThrowableName = TypeName.string2TypeName("Ljava/lang/Throwable");
public final static TypeReference JavaLangThrowable = findOrCreate(ClassLoaderReference.Primordial, JavaLangThrowableName);
public final static TypeName JavaLangCloneableName = TypeName.string2TypeName("Ljava/lang/Cloneable");
public final static TypeReference JavaLangCloneable = findOrCreate(ClassLoaderReference.Primordial, JavaLangCloneableName);
private final static TypeName JavaLangSystemName = TypeName.string2TypeName("Ljava/lang/System");
public final static TypeReference JavaLangSystem = findOrCreate(ClassLoaderReference.Primordial, JavaLangSystemName);
private final static TypeName JavaLangIntegerName = TypeName.string2TypeName("Ljava/lang/Integer");
public final static TypeReference JavaLangInteger = findOrCreate(ClassLoaderReference.Primordial, JavaLangIntegerName);
private final static TypeName JavaLangBooleanName = TypeName.string2TypeName("Ljava/lang/Boolean");
public final static TypeReference JavaLangBoolean = findOrCreate(ClassLoaderReference.Primordial, JavaLangBooleanName);
private final static TypeName JavaLangDoubleName = TypeName.string2TypeName("Ljava/lang/Double");
public final static TypeReference JavaLangDouble = findOrCreate(ClassLoaderReference.Primordial, JavaLangDoubleName);
private final static TypeName JavaLangFloatName = TypeName.string2TypeName("Ljava/lang/Float");
public final static TypeReference JavaLangFloat = findOrCreate(ClassLoaderReference.Primordial, JavaLangFloatName);
private final static TypeName JavaLangShortName = TypeName.string2TypeName("Ljava/lang/Short");
public final static TypeReference JavaLangShort = findOrCreate(ClassLoaderReference.Primordial, JavaLangShortName);
private final static TypeName JavaLangLongName = TypeName.string2TypeName("Ljava/lang/Long");
public final static TypeReference JavaLangLong = findOrCreate(ClassLoaderReference.Primordial, JavaLangLongName);
private final static TypeName JavaLangByteName = TypeName.string2TypeName("Ljava/lang/Byte");
public final static TypeReference JavaLangByte = findOrCreate(ClassLoaderReference.Primordial, JavaLangByteName);
private final static TypeName JavaLangCharacterName = TypeName.string2TypeName("Ljava/lang/Character");
public final static TypeReference JavaLangCharacter = findOrCreate(ClassLoaderReference.Primordial, JavaLangCharacterName);
public final static TypeName JavaIoSerializableName = TypeName.string2TypeName("Ljava/io/Serializable");
public final static TypeReference JavaIoSerializable = findOrCreate(ClassLoaderReference.Primordial, JavaIoSerializableName);
private final static TypeName JavaUtilCollectionName = TypeName.string2TypeName("Ljava/util/Collection");
public final static TypeReference JavaUtilCollection = findOrCreate(ClassLoaderReference.Primordial, JavaUtilCollectionName);
private final static TypeName JavaUtilMapName = TypeName.string2TypeName("Ljava/util/Map");
public final static TypeReference JavaUtilMap = findOrCreate(ClassLoaderReference.Primordial, JavaUtilMapName);
private final static TypeName JavaUtilHashSetName = TypeName.string2TypeName("Ljava/util/HashSet");
public final static TypeReference JavaUtilHashSet = findOrCreate(ClassLoaderReference.Primordial, JavaUtilHashSetName);
private final static TypeName JavaUtilSetName = TypeName.string2TypeName("Ljava/util/Set");
public final static TypeReference JavaUtilSet = findOrCreate(ClassLoaderReference.Primordial, JavaUtilSetName);
private final static TypeName JavaUtilEnumName = TypeName.string2TypeName("Ljava/util/Enumeration");
public final static TypeReference JavaUtilEnum = findOrCreate(ClassLoaderReference.Primordial, JavaUtilEnumName);
private final static TypeName JavaUtilIteratorName = TypeName.string2TypeName("Ljava/util/Iterator");
public final static TypeReference JavaUtilIterator = findOrCreate(ClassLoaderReference.Primordial, JavaUtilIteratorName);
private final static TypeName JavaUtilVectorName = TypeName.string2TypeName("Ljava/util/Vector");
public final static TypeReference JavaUtilVector = findOrCreate(ClassLoaderReference.Primordial, JavaUtilVectorName);
public final static byte ClassTypeCode = 'L';
public final static byte ArrayTypeCode = '[';
public final static byte PointerTypeCode = '*';
public final static byte ReferenceTypeCode = '&';
// TODO! the following two are unsound hacks; kill them.
final static TypeName NullName = TypeName.string2TypeName("null");
public final static TypeReference Null = findOrCreate(ClassLoaderReference.Primordial, NullName);
// TODO: is the following necessary. Used only by ShrikeBT.
final static TypeName UnknownName = TypeName.string2TypeName("?unknown?");
public final static TypeReference Unknown = findOrCreate(ClassLoaderReference.Primordial, UnknownName);
public final static TypeReference LambdaMetaFactory = findOrCreate(ClassLoaderReference.Primordial, "Ljava/lang/invoke/LambdaMetafactory");
private static TypeReference makePrimitive(TypeName n) {
return makePrimitive(ClassLoaderReference.Primordial, n);
}
public static TypeReference makePrimitive(ClassLoaderReference cl, TypeName n) {
TypeReference t = new TypeReference(cl, n);
primitiveMap.put(t.name, t);
return t;
}
/**
* Could name a represent a primitive type?
*/
public static boolean isPrimitiveType(TypeName name) {
return name.isPrimitiveType();
}
/**
* The initiating class loader
*/
private final ClassLoaderReference classloader;
/**
* The type name
*/
private final TypeName name;
/**
* Find or create the canonical TypeReference instance for the given pair.
*
* @param cl the classloader (defining/initiating depending on usage)
*/
public static synchronized TypeReference findOrCreate(ClassLoaderReference cl, TypeName typeName) {
if (cl == null) {
throw new IllegalArgumentException("null cl");
}
TypeReference p = primitiveMap.get(typeName);
if (p != null) {
return p;
}
// Next actually findOrCreate the type reference using the proper
// classloader.
// [This is the only allocation site for TypeReference]
if (typeName.isArrayType()) {
TypeName e = typeName.getInnermostElementType();
if (e.isPrimitiveType()) {
cl = ClassLoaderReference.Primordial;
}
}
Key key = new Key(cl, typeName);
TypeReference val = dictionary.get(key);
if (val != null) {
return val;
} else {
val = new TypeReference(cl, typeName);
dictionary.put(key, val);
return val;
}
}
/**
* Find or create the canonical {@link TypeReference} instance for the given pair.
*
* @param cl the classloader (defining/initiating depending on usage)
* @param typeName something like "Ljava/util/Arrays"
*/
public static synchronized TypeReference findOrCreate(ClassLoaderReference cl, String typeName) {
return findOrCreate(cl, TypeName.string2TypeName(typeName));
}
public static synchronized TypeReference find(ClassLoaderReference cl, String typeName) {
return find(cl, TypeName.string2TypeName(typeName));
}
/**
* Find the canonical TypeReference instance for the given pair. May return null.
*
* @param cl the classloader (defining/initiating depending on usage)
*/
public static synchronized TypeReference find(ClassLoaderReference cl, TypeName typeName) {
if (cl == null) {
throw new IllegalArgumentException("null cl");
}
TypeReference p = primitiveMap.get(typeName);
if (p != null) {
return p;
}
// Next actually findOrCreate the type reference using the proper
// classloader.
// [This is the only allocation site for TypeReference]
if (typeName.isArrayType()) {
TypeName e = typeName.getInnermostElementType();
if (e.isPrimitiveType()) {
cl = ClassLoaderReference.Primordial;
}
}
Key key = new Key(cl, typeName);
TypeReference val = dictionary.get(key);
return val;
}
public static TypeReference findOrCreateArrayOf(TypeReference t) {
if (t == null) {
throw new IllegalArgumentException("t is null");
}
TypeName name = t.getName();
if (t.isPrimitiveType()) {
return findOrCreate(ClassLoaderReference.Primordial, name.getArrayTypeForElementType());
} else {
return findOrCreate(t.getClassLoader(), name.getArrayTypeForElementType());
}
}
public static TypeReference findOrCreateReferenceTo(TypeReference t) {
if (t == null) {
throw new IllegalArgumentException("t is null");
}
TypeName name = t.getName();
if (t.isPrimitiveType()) {
return findOrCreate(ClassLoaderReference.Primordial, name.getReferenceTypeForElementType());
} else {
return findOrCreate(t.getClassLoader(), name.getReferenceTypeForElementType());
}
}
public static TypeReference findOrCreatePointerTo(TypeReference t) {
if (t == null) {
throw new IllegalArgumentException("t is null");
}
TypeName name = t.getName();
if (t.isPrimitiveType()) {
return findOrCreate(ClassLoaderReference.Primordial, name.getPointerTypeForElementType());
} else {
return findOrCreate(t.getClassLoader(), name.getPointerTypeForElementType());
}
}
/**
* NB: All type names should use '/' and not '.' as a separator. eg. Ljava/lang/Class
*
* @param cl the classloader
* @param tn the type name
*/
protected TypeReference(ClassLoaderReference cl, TypeName tn) {
classloader = cl;
name = tn;
}
/**
* @return the classloader component of this type reference
*/
public final ClassLoaderReference getClassLoader() {
return classloader;
}
/**
* @return the type name component of this type reference
*/
public final TypeName getName() {
return name;
}
/**
* TODO: specialized form of TypeReference for arrays, please. Get the element type of for this array type.
*/
public final TypeReference getArrayElementType() {
TypeName element = name.parseForArrayElementName();
return findOrCreate(classloader, element);
}
/**
* Get array type corresponding to "this" array element type.
*/
public final TypeReference getArrayTypeForElementType() {
return findOrCreate(classloader, name.getArrayTypeForElementType());
}
/**
* Return the dimensionality of the type. By convention, class types have dimensionality 0, primitives -1, and arrays
* the number of [ in their descriptor.
*/
public final int getDerivedMask() {
return name.getDerivedMask();
}
/**
* Return the innermost element type reference for an array
*/
public final TypeReference getInnermostElementType() {
return findOrCreate(classloader, name.getInnermostElementType());
}
/**
* Does 'this' refer to a class?
*/
public final boolean isClassType() {
return !isArrayType() && !isPrimitiveType();
}
/**
* Does 'this' refer to an array?
*/
public final boolean isArrayType() {
return name.isArrayType();
}
/**
* Does 'this' refer to a primitive type
*/
public final boolean isPrimitiveType() {
return isPrimitiveType(name);
}
/**
* Does 'this' refer to a reference type
*/
public final boolean isReferenceType() {
return !isPrimitiveType();
}
@Override
public final int hashCode() {
return name.hashCode();
}
/**
* TypeReferences are canonical. However, note that two TypeReferences can be non-equal, yet still represent the same
* IClass.
*
* These two TypeReference are <bf>NOT</bf> equal(), but they both represent the IClass which is named
* For example, the there can be two TypeReferences &lt;Application,java.lang.Object&gt; and &lt;Primordial,java.lang.Object&gt;.
* &lt;Primordial,java.lang.Object&gt;
*/
@Override
public final boolean equals(Object other) {
return (this == other);
}
@Override
public final String toString() {
return "<" + classloader.getName() + "," + name + ">";
}
public static TypeReference findOrCreateClass(ClassLoaderReference loader, String packageName, String className) {
TypeName tn = TypeName.findOrCreateClassName(packageName, className);
return findOrCreate(loader, tn);
}
private static class Key {
/**
* The initiating class loader
*/
private final ClassLoaderReference classloader;
/**
* The type name
*/
private final TypeName name;
Key(ClassLoaderReference classloader, TypeName name) {
this.classloader = classloader;
this.name = name;
}
@Override
public final int hashCode() {
return name.hashCode();
}
@Override
public final boolean equals(Object other) {
assert other != null && other instanceof Key;
Key that = (Key) other;
return (name.equals(that.name) && classloader.equals(that.classloader));
}
}
public int getDimensionality() {
assert isArrayType();
int mask = getDerivedMask();
if ((mask&PrimitiveMask) == PrimitiveMask) {
mask >>= ElementBits;
}
int dims = 0;
while ((mask&ArrayMask) == ArrayMask) {
mask >>= ElementBits;
dims++;
}
assert dims>0;
return dims;
}
}