/******************************************************************************* * 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); 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. * * For example, the there can be two TypeReferences <Application,java.lang.Object> and <Primordial,java.lang.Object>. * These two TypeReference are <bf>NOT</bf> equal(), but they both represent the IClass which is named * <Primordial,java.lang.Object> */ @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; } }