/* ******************************************************************* * Copyright (c) 2002,2005 Contributors * All rights reserved. * This program and the accompanying materials are made available * under the terms of the Eclipse Public License v 2.0 * which accompanies this distribution and is available at * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt * * Contributors: * PARC initial implementation * Andy Clement start of generics upgrade... * Adrian Colyer - overhaul * ******************************************************************/ package org.aspectj.weaver; import java.io.DataInputStream; import java.io.IOException; import java.util.Map; import org.aspectj.util.GenericSignature; import org.aspectj.util.GenericSignature.ClassSignature; import org.aspectj.util.GenericSignatureParser; import org.aspectj.weaver.tools.Traceable; /** * A UnresolvedType represents a type to the weaver. UnresolvedTypes are resolved in some World (a type repository). When a * UnresolvedType is resolved it turns into a ResolvedType which may be a primitive type, or a ReferenceType. ReferenceTypes may * refer to simple, generic, parameterized or type-variable based reference types. A ReferenceType is backed by a delegate that * provides information about the type based on some repository (for example an Eclipse based delegate, a bytecode based delegate or * a reflection based delegate). *
* Every UnresolvedType has a signature, the unique key for the type in the world. */ public class UnresolvedType implements Traceable, TypeVariableDeclaringElement { // common type structures public static final UnresolvedType[] NONE = new UnresolvedType[0]; public static final UnresolvedType OBJECT = forSignature("Ljava/lang/Object;"); public static final UnresolvedType OBJECTARRAY = forSignature("[Ljava/lang/Object;"); public static final UnresolvedType CLONEABLE = forSignature("Ljava/lang/Cloneable;"); public static final UnresolvedType SERIALIZABLE = forSignature("Ljava/io/Serializable;"); public static final UnresolvedType THROWABLE = forSignature("Ljava/lang/Throwable;"); public static final UnresolvedType RUNTIME_EXCEPTION = forSignature("Ljava/lang/RuntimeException;"); public static final UnresolvedType ERROR = forSignature("Ljava/lang/Error;"); public static final UnresolvedType AT_INHERITED = forSignature("Ljava/lang/annotation/Inherited;"); public static final UnresolvedType AT_RETENTION = forSignature("Ljava/lang/annotation/Retention;"); public static final UnresolvedType ENUM = forSignature("Ljava/lang/Enum;"); public static final UnresolvedType ANNOTATION = forSignature("Ljava/lang/annotation/Annotation;"); public static final UnresolvedType JL_CLASS = forSignature("Ljava/lang/Class;"); public static final UnresolvedType JAVA_LANG_CLASS_ARRAY = forSignature("[Ljava/lang/Class;"); public static final UnresolvedType JL_STRING = forSignature("Ljava/lang/String;"); public static final UnresolvedType JL_EXCEPTION = forSignature("Ljava/lang/Exception;"); public static final UnresolvedType JAVA_LANG_REFLECT_METHOD = forSignature("Ljava/lang/reflect/Method;"); public static final UnresolvedType JAVA_LANG_REFLECT_FIELD = forSignature("Ljava/lang/reflect/Field;"); public static final UnresolvedType JAVA_LANG_REFLECT_CONSTRUCTOR = forSignature("Ljava/lang/reflect/Constructor;"); public static final UnresolvedType JAVA_LANG_ANNOTATION = forSignature("Ljava/lang/annotation/Annotation;"); public static final UnresolvedType SUPPRESS_AJ_WARNINGS = forSignature("Lorg/aspectj/lang/annotation/SuppressAjWarnings;"); public static final UnresolvedType AT_TARGET = forSignature("Ljava/lang/annotation/Target;"); public static final UnresolvedType SOMETHING = new UnresolvedType("?"); public static final UnresolvedType[] ARRAY_WITH_JUST_OBJECT = new UnresolvedType[] { OBJECT }; public static final UnresolvedType JOINPOINT_STATICPART = forSignature("Lorg/aspectj/lang/JoinPoint$StaticPart;"); public static final UnresolvedType JOINPOINT_ENCLOSINGSTATICPART = forSignature("Lorg/aspectj/lang/JoinPoint$EnclosingStaticPart;"); public static final UnresolvedType AJC_PRIVILEGED = forSignature("Lorg/aspectj/internal/lang/annotation/ajcPrivileged;"); public static final UnresolvedType PROCEEDING_JOINPOINT = forSignature("Lorg/aspectj/lang/ProceedingJoinPoint;"); public static final UnresolvedType BOOLEAN = forPrimitiveType("Z"); public static final UnresolvedType BYTE = forPrimitiveType("B"); public static final UnresolvedType CHAR = forPrimitiveType("C"); public static final UnresolvedType DOUBLE = forPrimitiveType("D"); public static final UnresolvedType FLOAT = forPrimitiveType("F"); public static final UnresolvedType INT = forPrimitiveType("I"); public static final UnresolvedType LONG = forPrimitiveType("J"); public static final UnresolvedType SHORT = forPrimitiveType("S"); public static final UnresolvedType VOID = forPrimitiveType("V"); // A type is considered missing if we have a signature for it but cannot find the delegate public static final String MISSING_NAME = "@missing@"; // OPTIMIZE I dont think you can ask something unresolved what kind of type it is, how can it always know? Push down into // resolvedtype that will force references to resolvedtypes to be correct rather than relying on unresolvedtypes to answer // questions protected TypeKind typeKind = TypeKind.SIMPLE; // what kind of type am I? protected String signature; /** * The erasure of the signature. Contains only the Java signature of the type with all supertype, superinterface, type variable, * and parameter information removed. */ protected String signatureErasure; /** * Calculated on first request - the package name (java.lang for type java.lang.String) */ private String packageName; /** * Calculated on first request - the class name (String for type java.lang.String) */ private String className; /** * Iff isParameterized(), then these are the type parameters */ protected UnresolvedType[] typeParameters; /** * Iff isGeneric(), then these are the type variables declared on the type Iff isParameterized(), then these are the type * variables bound as parameters in the type */ // OPTIMIZE should be no state in here that will damage whether equals() is correct... protected TypeVariable[] typeVariables; public boolean isPrimitiveType() { return typeKind == TypeKind.PRIMITIVE; } public boolean isVoid() { // OPTIMIZE promote to bitflag? return signature.equals("V"); } public boolean isSimpleType() { return typeKind == TypeKind.SIMPLE; } public boolean isRawType() { return typeKind == TypeKind.RAW; } public boolean isGenericType() { return typeKind == TypeKind.GENERIC; } public boolean isParameterizedType() { return typeKind == TypeKind.PARAMETERIZED; } public boolean isParameterizedOrGenericType() { return typeKind == TypeKind.GENERIC || typeKind == TypeKind.PARAMETERIZED; } public boolean isParameterizedOrRawType() { return typeKind == TypeKind.PARAMETERIZED || typeKind == TypeKind.RAW; } public boolean isTypeVariableReference() { return typeKind == TypeKind.TYPE_VARIABLE; } public boolean isGenericWildcard() { return typeKind == TypeKind.WILDCARD; } public TypeKind getTypekind() { return typeKind; } // for any reference type, we can get some extra information... public final boolean isArray() { return signature.length() > 0 && signature.charAt(0) == '['; } public final int getDimensions() { return signature.replaceAll("^(\\[*).*", "$1").length(); } /** * Equality is checked based on the underlying signature. */ @Override public boolean equals(Object other) { if (!(other instanceof UnresolvedType)) { return false; } final UnresolvedType unresolvedOther = (UnresolvedType) other; return signature.equals(unresolvedOther.signature) && getDimensions() == unresolvedOther.getDimensions(); } /** * Equality is checked based on the underlying signature, so the hash code of a particular type is the hash code of its * signature string. */ @Override public int hashCode() { int result = 17; result = 37 * result + signature.hashCode(); result = 37 * result + getDimensions(); return result; } protected UnresolvedType(String signature) { this.signature = signature; this.signatureErasure = signature; } protected UnresolvedType(String signature, String signatureErasure) { this.signature = signature; this.signatureErasure = signatureErasure; } // called from TypeFactory public UnresolvedType(String signature, String signatureErasure, UnresolvedType[] typeParams) { this.signature = signature; this.signatureErasure = signatureErasure; this.typeParameters = typeParams; if (typeParams != null) { this.typeKind = TypeKind.PARAMETERIZED; } } // The operations supported by an UnresolvedType are those that do not require a world /** * This is the size of this type as used in JVM. */ public int getSize() { return size; } private int size = 1; /** * NOTE: Use forSignature() if you can, it'll be cheaper ! Constructs a UnresolvedType for a java language type name. For * example: * *
* ** * Types may equivalently be produced by this or by {@link #forSignature(String)}. * ** UnresolvedType.forName("java.lang.Thread[]") * UnresolvedType.forName("int") ** *
* ** * @param name the java language type name in question. * @return a type object representing that java language type. */ // OPTIMIZE change users of this to use forSignature, especially for simple cases public static UnresolvedType forName(String name) { return forSignature(nameToSignature(name)); } /** * Constructs a UnresolvedType for each java language type name in an incoming array. * * @param names an array of java language type names. * @return an array of UnresolvedType objects. * @see #forName(String) */ public static UnresolvedType[] forNames(String[] names) { UnresolvedType[] ret = new UnresolvedType[names.length]; for (int i = 0, len = names.length; i < len; i++) { ret[i] = UnresolvedType.forName(names[i]); } return ret; } public static UnresolvedType forGenericType(String name, TypeVariable[] tvbs, String genericSig) { String sig = nameToSignature(name); UnresolvedType ret = UnresolvedType.forSignature(sig); ret.typeKind = TypeKind.GENERIC; ret.typeVariables = tvbs; ret.signatureErasure = sig; return ret; } public static UnresolvedType forGenericTypeSignature(String sig, String declaredGenericSig) { UnresolvedType ret = UnresolvedType.forSignature(sig); ret.typeKind = TypeKind.GENERIC; ClassSignature csig = new GenericSignatureParser().parseAsClassSignature(declaredGenericSig); GenericSignature.FormalTypeParameter[] ftps = csig.formalTypeParameters; ret.typeVariables = new TypeVariable[ftps.length]; for (int i = 0; i < ftps.length; i++) { GenericSignature.FormalTypeParameter parameter = ftps[i]; if (parameter.classBound instanceof GenericSignature.ClassTypeSignature) { GenericSignature.ClassTypeSignature cts = (GenericSignature.ClassTypeSignature) parameter.classBound; ret.typeVariables[i] = new TypeVariable(ftps[i].identifier, UnresolvedType.forSignature(cts.outerType.identifier + ";")); } else if (parameter.classBound instanceof GenericSignature.TypeVariableSignature) { GenericSignature.TypeVariableSignature tvs = (GenericSignature.TypeVariableSignature) parameter.classBound; UnresolvedTypeVariableReferenceType utvrt = new UnresolvedTypeVariableReferenceType(new TypeVariable( tvs.typeVariableName)); ret.typeVariables[i] = new TypeVariable(ftps[i].identifier, utvrt); } else { throw new BCException( "UnresolvedType.forGenericTypeSignature(): Do not know how to process type variable bound of type '" + parameter.classBound.getClass() + "'. Full signature is '" + sig + "'"); } } ret.signatureErasure = sig; ret.signature = ret.signatureErasure; return ret; } public static UnresolvedType forGenericTypeVariables(String sig, TypeVariable[] tVars) { UnresolvedType ret = UnresolvedType.forSignature(sig); ret.typeKind = TypeKind.GENERIC; ret.typeVariables = tVars; ret.signatureErasure = sig; ret.signature = ret.signatureErasure; return ret; } public static UnresolvedType forRawTypeName(String name) { UnresolvedType ret = UnresolvedType.forName(name); ret.typeKind = TypeKind.RAW; return ret; } public static UnresolvedType forPrimitiveType(String signature) { UnresolvedType ret = new UnresolvedType(signature); ret.typeKind = TypeKind.PRIMITIVE; if (signature.equals("J") || signature.equals("D")) { ret.size = 2; } else if (signature.equals("V")) { ret.size = 0; } return ret; } /** * Creates a new type array with a fresh type appended to the end. * * @param types the left hand side of the new array * @param end the right hand side of the new array */ public static UnresolvedType[] add(UnresolvedType[] types, UnresolvedType end) { int len = types.length; UnresolvedType[] ret = new UnresolvedType[len + 1]; System.arraycopy(types, 0, ret, 0, len); ret[len] = end; return ret; } /** * Creates a new type array with a fresh type inserted at the beginning. * * * @param start the left hand side of the new array * @param types the right hand side of the new array */ public static UnresolvedType[] insert(UnresolvedType start, UnresolvedType[] types) { int len = types.length; UnresolvedType[] ret = new UnresolvedType[len + 1]; ret[0] = start; System.arraycopy(types, 0, ret, 1, len); return ret; } /** * Constructs a Type for a JVM bytecode signature string. For example: * ** UnresolvedType.forName("java.lang.Thread[]").equals(Type.forSignature("[Ljava/lang/Thread;") * UnresolvedType.forName("int").equals(Type.forSignature("I")) ** *
* ** * Types may equivalently be produced by this or by {@link #forName(String)}. This method should not be passed P signatures. * ** UnresolvedType.forSignature("[Ljava/lang/Thread;") * UnresolvedType.forSignature("I"); ** *
* ** * @param signature the JVM bytecode signature string for the desired type. * @return a type object represnting that JVM bytecode signature. */ public static UnresolvedType forSignature(String signature) { assert !(signature.startsWith("L") && signature.contains("<")); switch (signature.charAt(0)) { case 'B': return UnresolvedType.BYTE; case 'C': return UnresolvedType.CHAR; case 'D': return UnresolvedType.DOUBLE; case 'F': return UnresolvedType.FLOAT; case 'I': return UnresolvedType.INT; case 'J': return UnresolvedType.LONG; case 'L': return TypeFactory.createTypeFromSignature(signature); case 'P': return TypeFactory.createTypeFromSignature(signature); case 'S': return UnresolvedType.SHORT; case 'V': return UnresolvedType.VOID; case 'Z': return UnresolvedType.BOOLEAN; case '[': return TypeFactory.createTypeFromSignature(signature); case '+': return TypeFactory.createTypeFromSignature(signature); case '-': return TypeFactory.createTypeFromSignature(signature); case '*': return TypeFactory.createTypeFromSignature(signature); case 'T': return TypeFactory.createTypeFromSignature(signature); default: throw new BCException("Bad type signature " + signature); } } /** * Constructs a UnresolvedType for each JVM bytecode type signature in an incoming array. * * @param sigs an array of JVM bytecode type signatures * @return an array of UnresolvedType objects. * @see #forSignature(String) */ public static UnresolvedType[] forSignatures(String[] sigs) { UnresolvedType[] ret = new UnresolvedType[sigs.length]; for (int i = 0, len = sigs.length; i < len; i++) { ret[i] = UnresolvedType.forSignature(sigs[i]); } return ret; } /** * Returns the name of this type in java language form (e.g. java.lang.Thread or boolean[]). This produces a more aesthetically * pleasing string than {@link java.lang.Class#getName()}. * * @return the java language name of this type. */ public String getName() { return signatureToName(signature); } public String getSimpleName() { String name = getRawName(); int lastDot = name.lastIndexOf('.'); if (lastDot != -1) { name = name.substring(lastDot + 1); } if (isParameterizedType()) { StringBuilder sb = new StringBuilder(name); sb.append("<"); for (int i = 0; i < (typeParameters.length - 1); i++) { sb.append(typeParameters[i].getSimpleName()); sb.append(","); } sb.append(typeParameters[typeParameters.length - 1].getSimpleName()); sb.append(">"); name = sb.toString(); } return name; } public String getRawName() { return signatureToName((signatureErasure == null ? signature : signatureErasure)); } public String getBaseName() { String name = getName(); if (isParameterizedType() || isGenericType()) { if (typeParameters == null) { return name; } else { return name.substring(0, name.indexOf("<")); } } else { return name; } } public String getSimpleBaseName() { String name = getBaseName(); int lastDot = name.lastIndexOf('.'); if (lastDot != -1) { name = name.substring(lastDot + 1); } return name; } /** * Returns an array of strings representing the java langauge names of an array of types. * * @param types an array of UnresolvedType objects * @return an array of Strings fo the java language names of types. * @see #getName() */ public static String[] getNames(UnresolvedType[] types) { String[] ret = new String[types.length]; for (int i = 0, len = types.length; i < len; i++) { ret[i] = types[i].getName(); } return ret; } /** * Returns the name of this type in JVM signature form. For all UnresolvedType t: * ** UnresolvedType.forName("java.lang.Thread[]").equals(Type.forSignature("[Ljava/lang/Thread;") * UnresolvedType.forName("int").equals(Type.forSignature("I")) ** *
* ** * and for all String s where s is a lexically valid JVM type signature string: * ** UnresolvedType.forSignature(t.getSignature()).equals(t) ** *
* ** * @return the java JVM signature string for this type. */ public String getSignature() { return signature; } /** * For parameterized types, return the signature for the raw type */ public String getErasureSignature() { if (signatureErasure == null) { return signature; } return signatureErasure; } private boolean needsModifiableDelegate = false; public boolean needsModifiableDelegate() { return needsModifiableDelegate; } public void setNeedsModifiableDelegate(boolean b) { this.needsModifiableDelegate = b; } public UnresolvedType getRawType() { return UnresolvedType.forSignature(getErasureSignature()); } /** * Returns a UnresolvedType object representing the effective outermost enclosing type for a name type. For all other types, * this will return the type itself. * * The only guarantee is given in JLS 13.1 where code generated according to those rules will have type names that can be split * apart in this way. * * @return the outermost enclosing UnresolvedType object or this. */ public UnresolvedType getOutermostType() { if (isArray() || isPrimitiveType()) { return this; } String sig = getErasureSignature(); int dollar = sig.indexOf('$'); if (dollar != -1) { return UnresolvedType.forSignature(sig.substring(0, dollar) + ';'); } else { return this; } } /** * Returns a UnresolvedType object representing the component type of this array, or null if this type does not represent an * array type. * * @return the component UnresolvedType object, or null. */ public UnresolvedType getComponentType() { if (isArray()) { return forSignature(signature.substring(1)); } else { return null; } } /** * Returns a java language string representation of this type. */ @Override public String toString() { return getName(); // + " - " + getKind(); } public String toDebugString() { return getName(); } // ---- requires worlds /** * Returns a resolved version of this type according to a particular world. * * @param world the {@link World} within which to resolve. * @return a resolved type representing this type in the appropriate world. */ public ResolvedType resolve(World world) { return world.resolve(this); } // ---- helpers private static String signatureToName(String signature) { switch (signature.charAt(0)) { case 'B': return "byte"; case 'C': return "char"; case 'D': return "double"; case 'F': return "float"; case 'I': return "int"; case 'J': return "long"; case 'L': String name = signature.substring(1, signature.length() - 1).replace('/', '.'); return name; case 'T': StringBuilder nameBuff2 = new StringBuilder(); int colon = signature.indexOf(";"); String tvarName = signature.substring(1, colon); nameBuff2.append(tvarName); return nameBuff2.toString(); case 'P': // it's one of our parameterized type sigs StringBuilder nameBuff = new StringBuilder(); // signature for parameterized types is e.g. // List* UnresolvedType.forSignature(s).getSignature().equals(s) ** *