blob: 84e3824223b3a5d8c80a49c927ff8013084a84bd [file] [log] [blame]
/* *******************************************************************
* Copyright (c) 2002-2010 Contributors
* 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
* ******************************************************************/
package org.aspectj.weaver;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.aspectj.bridge.ISourceLocation;
/**
* Represent a resolved member. Components of it are expected to exist. This member will correspond to a real member *unless* it is
* being used to represent the effect of an ITD.
*
* @author PARC
* @author Andy Clement
*/
public class ResolvedMemberImpl extends MemberImpl implements IHasPosition, ResolvedMember {
private String[] parameterNames = null;
private boolean isResolved = false;
protected UnresolvedType[] checkedExceptions = UnresolvedType.NONE;
/**
* if this member is a parameterized version of a member in a generic type, then this field holds a reference to the member we
* parameterize.
*/
protected ResolvedMember backingGenericMember = null;
protected AnnotationAJ[] annotations = null;
protected ResolvedType[] annotationTypes = null;
protected AnnotationAJ[][] parameterAnnotations = null;
protected ResolvedType[][] parameterAnnotationTypes = null;
// Some members are 'created' to represent other things (for example ITDs).
// These members have their annotations stored elsewhere, and this flag indicates
// that is the case. It is up to the caller to work out where that is!
// Once determined the caller may choose to stash the annotations in this
// member...
private boolean isAnnotatedElsewhere = false;
private boolean isAjSynthetic = false;
// generic methods have type variables
protected TypeVariable[] typeVariables;
// these three fields hold the source location of this member
protected int start, end;
protected ISourceContext sourceContext = null;
// XXX deprecate this in favor of the constructor below
public ResolvedMemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers, UnresolvedType returnType, String name,
UnresolvedType[] parameterTypes) {
super(kind, declaringType, modifiers, returnType, name, parameterTypes);
}
public ResolvedMemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers, UnresolvedType returnType, String name,
UnresolvedType[] parameterTypes, UnresolvedType[] checkedExceptions) {
super(kind, declaringType, modifiers, returnType, name, parameterTypes);
this.checkedExceptions = checkedExceptions;
}
public ResolvedMemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers, UnresolvedType returnType, String name,
UnresolvedType[] parameterTypes, UnresolvedType[] checkedExceptions, ResolvedMember backingGenericMember) {
this(kind, declaringType, modifiers, returnType, name, parameterTypes, checkedExceptions);
this.backingGenericMember = backingGenericMember;
this.isAjSynthetic = backingGenericMember.isAjSynthetic();
}
public ResolvedMemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers, String name, String signature) {
super(kind, declaringType, modifiers, name, signature);
}
/**
* Compute the full set of signatures for a member. This walks up the hierarchy giving the ResolvedMember in each defining type
* in the hierarchy. A shadowMember can be created with a target type (declaring type) that does not actually define the member.
* This is ok as long as the member is inherited in the declaring type. Each declaring type in the line to the actual declaring
* type is added as an additional signature. For example:
*
* class A { void foo(); } class B extends A {}
*
* shadowMember : void B.foo()
*
* gives { void B.foo(), void A.foo() }
*
* @param joinPointSignature
* @param inAWorld
*/
public static JoinPointSignature[] getJoinPointSignatures(Member joinPointSignature, World inAWorld) {
// Walk up hierarchy creating one member for each type up to and
// including the
// first defining type
ResolvedType originalDeclaringType = joinPointSignature.getDeclaringType().resolve(inAWorld);
ResolvedMemberImpl firstDefiningMember = (ResolvedMemberImpl) joinPointSignature.resolve(inAWorld);
if (firstDefiningMember == null) {
return JoinPointSignature.EMPTY_ARRAY;
}
// declaringType can be unresolved if we matched a synthetic member
// generated by Aj...
// should be fixed elsewhere but add this resolve call on the end for
// now so that we can
// focus on one problem at a time...
ResolvedType firstDefiningType = firstDefiningMember.getDeclaringType().resolve(inAWorld);
if (firstDefiningType != originalDeclaringType) {
if (joinPointSignature.getKind() == Member.CONSTRUCTOR) {
return JoinPointSignature.EMPTY_ARRAY;
}
// else if (shadowMember.isStatic()) {
// return new ResolvedMember[] {firstDefiningMember};
// }
}
List<ResolvedType> declaringTypes = new ArrayList<ResolvedType>();
accumulateTypesInBetween(originalDeclaringType, firstDefiningType, declaringTypes);
Set<ResolvedMember> memberSignatures = new LinkedHashSet<ResolvedMember>();
for (ResolvedType declaringType : declaringTypes) {
memberSignatures.add(new JoinPointSignature(firstDefiningMember, declaringType));
}
if (shouldWalkUpHierarchyFor(firstDefiningMember)) {
// now walk up the hierarchy from the firstDefiningMember and
// include the signature for
// every type between the firstDefiningMember and the root defining
// member.
Iterator<ResolvedType> superTypeIterator = firstDefiningType.getDirectSupertypes();
List<ResolvedType> typesAlreadyVisited = new ArrayList<ResolvedType>();
accumulateMembersMatching(firstDefiningMember, superTypeIterator, typesAlreadyVisited, memberSignatures, false);
}
JoinPointSignature[] ret = new JoinPointSignature[memberSignatures.size()];
memberSignatures.toArray(ret);
return ret;
}
private static boolean shouldWalkUpHierarchyFor(Member aMember) {
if (aMember.getKind() == Member.CONSTRUCTOR) {
return false;
}
if (aMember.getKind() == Member.FIELD) {
return false;
}
if (Modifier.isStatic(aMember.getModifiers())) {
return false;
}
return true;
}
/**
* Build a list containing every type between subtype and supertype, inclusively.
*/
private static void accumulateTypesInBetween(ResolvedType subType, ResolvedType superType, List<ResolvedType> types) {
types.add(subType);
if (subType == superType) {
return;
} else {
for (Iterator<ResolvedType> iter = subType.getDirectSupertypes(); iter.hasNext();) {
ResolvedType parent = iter.next();
if (superType.isAssignableFrom(parent)) {
accumulateTypesInBetween(parent, superType, types);
}
}
}
}
/**
* We have a resolved member, possibly with type parameter references as parameters or return type. We need to find all its
* ancestor members. When doing this, a type parameter matches regardless of bounds (bounds can be narrowed down the hierarchy).
*/
private static void accumulateMembersMatching(ResolvedMemberImpl memberToMatch, Iterator<ResolvedType> typesToLookIn,
List<ResolvedType> typesAlreadyVisited, Set<ResolvedMember> foundMembers, boolean ignoreGenerics) {
while (typesToLookIn.hasNext()) {
ResolvedType toLookIn = typesToLookIn.next();
if (!typesAlreadyVisited.contains(toLookIn)) {
typesAlreadyVisited.add(toLookIn);
ResolvedMemberImpl foundMember = (ResolvedMemberImpl) toLookIn.lookupResolvedMember(memberToMatch, true,
ignoreGenerics);
if (foundMember != null && isVisibleTo(memberToMatch, foundMember)) {
List<ResolvedType> declaringTypes = new ArrayList<ResolvedType>();
// declaring type can be unresolved if the member can from
// an ITD...
ResolvedType resolvedDeclaringType = foundMember.getDeclaringType().resolve(toLookIn.getWorld());
accumulateTypesInBetween(toLookIn, resolvedDeclaringType, declaringTypes);
for (ResolvedType declaringType : declaringTypes) {
// typesAlreadyVisited.add(declaringType);
foundMembers.add(new JoinPointSignature(foundMember, declaringType));
}
if (!ignoreGenerics && toLookIn.isParameterizedType() && (foundMember.backingGenericMember != null)) {
foundMembers.add(new JoinPointSignature(foundMember.backingGenericMember, foundMember.declaringType
.resolve(toLookIn.getWorld())));
}
accumulateMembersMatching(foundMember, toLookIn.getDirectSupertypes(), typesAlreadyVisited, foundMembers,
ignoreGenerics);
// if this was a parameterized type, look in the generic
// type that backs it too
}
}
}
}
/**
* Returns true if the parent member is visible to the child member In the same declaring type this is always true, otherwise if
* parent is private it is false.
*
* @param childMember
* @param parentMember
* @return
*/
private static boolean isVisibleTo(ResolvedMember childMember, ResolvedMember parentMember) {
if (childMember.getDeclaringType().equals(parentMember.getDeclaringType())) {
return true;
}
if (Modifier.isPrivate(parentMember.getModifiers())) {
return false;
} else {
return true;
}
}
// ----
@Override
public final int getModifiers(World world) {
return modifiers;
}
@Override
public final int getModifiers() {
return modifiers;
}
// ----
@Override
public final UnresolvedType[] getExceptions(World world) {
return getExceptions();
}
public UnresolvedType[] getExceptions() {
return checkedExceptions;
}
public ShadowMunger getAssociatedShadowMunger() {
return null;
}
// ??? true or false?
public boolean isAjSynthetic() {
return isAjSynthetic;
}
protected void setAjSynthetic(boolean b) {
isAjSynthetic = b;
}
public boolean hasAnnotations() {
return (annotationTypes != null);
}
/**
* Check if this member has an annotation of the specified type. If the member has a backing generic member then this member
* represents a parameterization of a member in a generic type and the annotations available on the backing generic member
* should be used.
*
* @param ofType the type of the annotation being searched for
* @return true if the annotation is found on this member or its backing generic member
*/
public boolean hasAnnotation(UnresolvedType ofType) {
// The ctors don't allow annotations to be specified ... yet - but
// that doesn't mean it is an error to call this method.
// Normally the weaver will be working with subtypes of
// this type - BcelField/BcelMethod
if (backingGenericMember != null) {
if (annotationTypes != null) {
throw new BCException("Unexpectedly found a backing generic member and a local set of annotations");
}
return backingGenericMember.hasAnnotation(ofType);
}
if (annotationTypes != null) {
for (int i = 0, max = annotationTypes.length; i < max; i++) {
if (annotationTypes[i].equals(ofType)) {
return true;
}
}
}
return false;
}
public ResolvedType[] getAnnotationTypes() {
// The ctors don't allow annotations to be specified ... yet - but
// that doesn't mean it is an error to call this method.
// Normally the weaver will be working with subtypes of
// this type - BcelField/BcelMethod
if (backingGenericMember != null) {
if (annotationTypes != null) {
throw new BCException("Unexpectedly found a backing generic member and a local set of annotations");
}
return backingGenericMember.getAnnotationTypes();
}
return annotationTypes;
}
public String getAnnotationDefaultValue() {
throw new UnsupportedOperationException(
"You should resolve this member and call getAnnotationDefaultValue() on the result...");
}
@Override
public AnnotationAJ[] getAnnotations() {
if (backingGenericMember != null) {
return backingGenericMember.getAnnotations();
}
if (annotations!=null) {
return annotations;
}
return super.getAnnotations();
}
public AnnotationAJ getAnnotationOfType(UnresolvedType ofType) {
if (annotations!=null) {
// this means they have been set (we are likely a placeholder for an ITD, so a fake member)
for (AnnotationAJ annotation: annotations) {
if (annotation.getType().equals(ofType)) {
return annotation;
}
}
return null;
}
throw new UnsupportedOperationException("You should resolve this member and call getAnnotationOfType() on the result...");
}
public void setAnnotations(AnnotationAJ[] annotations) {
this.annotations = annotations;
}
public void setAnnotationTypes(ResolvedType[] annotationTypes) {
this.annotationTypes = annotationTypes;
}
public ResolvedType[][] getParameterAnnotationTypes() {
return parameterAnnotationTypes;
}
public AnnotationAJ[][] getParameterAnnotations() {
if (backingGenericMember != null) {
return backingGenericMember.getParameterAnnotations();
}
throw new BCException("Cannot return parameter annotations for a " + this.getClass().getName() + " member");
// return super.getParameterAnnotations();
}
public void addAnnotation(AnnotationAJ annotation) {
if (annotationTypes == null) {
annotationTypes = new ResolvedType[1];
annotationTypes[0] = annotation.getType();
annotations = new AnnotationAJ[1];
annotations[0] = annotation;
} else {
int len = annotations.length;
AnnotationAJ[] ret = new AnnotationAJ[len + 1];
System.arraycopy(annotations, 0, ret, 0, len);
ret[len] = annotation;
annotations = ret;
ResolvedType[] newAnnotationTypes = new ResolvedType[len + 1];
System.arraycopy(annotationTypes, 0, newAnnotationTypes, 0, len);
newAnnotationTypes[len] = annotation.getType();
annotationTypes = newAnnotationTypes;
}
}
public boolean isBridgeMethod() {
return (modifiers & Constants.ACC_BRIDGE) != 0 && getKind().equals(METHOD);
}
public boolean isVarargsMethod() {
return (modifiers & Constants.ACC_VARARGS) != 0;
}
public void setVarargsMethod() {
modifiers = modifiers | Constants.ACC_VARARGS;
}
public boolean isSynthetic() {
// See Bcelmethod.isSynthetic() which takes account of preJava5
// Synthetic modifier
return (modifiers & 4096) != 0; // do we know better?
}
public void write(CompressingDataOutputStream s) throws IOException {
getKind().write(s);
s.writeBoolean(s.canCompress()); // boolean indicates if parts of this are compressed references
// write out the signature of the declaring type of this member
if (s.canCompress()) {
s.writeCompressedSignature(getDeclaringType().getSignature());
} else {
getDeclaringType().write(s);
}
// write out the modifiers
s.writeInt(modifiers);
// write out the name and the signature of this member
if (s.canCompress()) {
s.writeCompressedName(getName());
s.writeCompressedSignature(getSignature());
} else {
s.writeUTF(getName());
s.writeUTF(getSignature());
}
// write out the array clauses
UnresolvedType.writeArray(getExceptions(), s);
s.writeInt(getStart());
s.writeInt(getEnd());
s.writeBoolean(isVarargsMethod());
// Write out any type variables...
if (typeVariables == null) {
s.writeByte(0);
} else {
s.writeByte(typeVariables.length);
for (int i = 0; i < typeVariables.length; i++) {
typeVariables[i].write(s);
}
}
String gsig = getGenericSignature();
// change this to a byte: 255=false 0>254 means true and encodes the number of parameters
if (getSignature().equals(gsig)) {
s.writeByte(0xff);
} else {
s.writeByte(parameterTypes.length);
for (int i = 0; i < parameterTypes.length; i++) {
if (s.canCompress()) {
s.writeCompressedSignature(parameterTypes[i].getSignature());
} else {
UnresolvedType array_element = parameterTypes[i];
array_element.write(s);
}
}
if (s.canCompress()) {
s.writeCompressedSignature(returnType.getSignature());
} else {
returnType.write(s);
}
}
}
/**
* Return the member generic signature that would be suitable for inclusion in a class file Signature attribute. For: <T>
* List<String> getThem(T t) {} we would create: <T:Ljava/lang/Object;>(TT;)Ljava/util/List<Ljava/lang/String;>;;
*
* @return the generic signature for the member that could be inserted into a class file
*/
public String getSignatureForAttribute() {
StringBuffer sb = new StringBuffer();
if (typeVariables != null) {
sb.append("<");
for (int i = 0; i < typeVariables.length; i++) {
sb.append(typeVariables[i].getSignatureForAttribute()); // need
// a
// 'getSignatureForAttribute()'
}
sb.append(">");
}
sb.append("(");
for (int i = 0; i < parameterTypes.length; i++) {
ResolvedType ptype = (ResolvedType) parameterTypes[i];
sb.append(ptype.getSignatureForAttribute());
}
sb.append(")");
sb.append(((ResolvedType) returnType).getSignatureForAttribute());
return sb.toString();
}
public String getGenericSignature() {
StringBuffer sb = new StringBuffer();
if (typeVariables != null) {
sb.append("<");
for (int i = 0; i < typeVariables.length; i++) {
sb.append(typeVariables[i].getSignature());
}
sb.append(">");
}
sb.append("(");
for (int i = 0; i < parameterTypes.length; i++) {
UnresolvedType ptype = parameterTypes[i];
sb.append(ptype.getSignature());
}
sb.append(")");
sb.append(returnType.getSignature());
return sb.toString();
}
public static void writeArray(ResolvedMember[] members, CompressingDataOutputStream s) throws IOException {
s.writeInt(members.length);
for (int i = 0, len = members.length; i < len; i++) {
members[i].write(s);
}
}
public static ResolvedMemberImpl readResolvedMember(VersionedDataInputStream s, ISourceContext sourceContext)
throws IOException {
MemberKind mk = MemberKind.read(s);
boolean compressed = (s.isAtLeast169() ? s.readBoolean() : false);
UnresolvedType declaringType = compressed ? UnresolvedType.forSignature(s.readUtf8(s.readShort())) : UnresolvedType.read(s);
int modifiers = s.readInt();
String name = compressed ? s.readUtf8(s.readShort()) : s.readUTF();
String signature = compressed ? s.readUtf8(s.readShort()) : s.readUTF();
ResolvedMemberImpl m = new ResolvedMemberImpl(mk, declaringType, modifiers, name, signature);
m.checkedExceptions = UnresolvedType.readArray(s);
m.start = s.readInt();
m.end = s.readInt();
m.sourceContext = sourceContext;
if (s.getMajorVersion() >= AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150) {
if (s.getMajorVersion() >= AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150M4) {
boolean isvarargs = s.readBoolean();
if (isvarargs) {
m.setVarargsMethod();
}
}
int tvcount = s.isAtLeast169() ? s.readByte() : s.readInt();
if (tvcount != 0) {
m.typeVariables = new TypeVariable[tvcount];
for (int i = 0; i < tvcount; i++) {
m.typeVariables[i] = TypeVariable.read(s);
m.typeVariables[i].setDeclaringElement(m);
m.typeVariables[i].setRank(i);
}
}
if (s.getMajorVersion() >= AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150M4) {
int pcount = -1;
boolean hasAGenericSignature = false;
if (s.isAtLeast169()) {
pcount = s.readByte();
hasAGenericSignature = (pcount >= 0 && pcount < 255);
} else {
hasAGenericSignature = s.readBoolean();
}
if (hasAGenericSignature) {
int ps = (s.isAtLeast169() ? pcount : s.readInt());
UnresolvedType[] params = new UnresolvedType[ps];
for (int i = 0; i < params.length; i++) {
if (compressed) {
params[i] = TypeFactory.createTypeFromSignature(s.readSignature());
} else {
params[i] = TypeFactory.createTypeFromSignature(s.readUTF());
}
}
UnresolvedType rt = compressed ? TypeFactory.createTypeFromSignature(s.readSignature()) : TypeFactory
.createTypeFromSignature(s.readUTF());
m.parameterTypes = params;
m.returnType = rt;
}
}
}
return m;
}
public static ResolvedMember[] readResolvedMemberArray(VersionedDataInputStream s, ISourceContext context) throws IOException {
int len = s.readInt();
ResolvedMember[] members = new ResolvedMember[len];
for (int i = 0; i < len; i++) {
members[i] = ResolvedMemberImpl.readResolvedMember(s, context);
}
return members;
}
// OPTIMIZE dont like how resolve(world) on ResolvedMemberImpl does
// something different to world.resolve(member)
@Override
public ResolvedMember resolve(World world) {
if (isResolved) {
return this;
}
// make sure all the pieces of a resolvedmember really are resolved
try {
if (typeVariables != null && typeVariables.length > 0) {
for (int i = 0; i < typeVariables.length; i++) {
typeVariables[i] = typeVariables[i].resolve(world);
}
}
world.setTypeVariableLookupScope(this);
// if (annotationTypes != null) {
// Set<ResolvedType> r = new HashSet<ResolvedType>();
// for (UnresolvedType element : annotationTypes) {
// // for (Iterator iter = annotationTypes.iterator(); iter.hasNext();) {
// // UnresolvedType element = (UnresolvedType) iter.next();
// r.add(world.resolve(element));
// }
// annotationTypes = r;
// }
declaringType = declaringType.resolve(world);
if (declaringType.isRawType()) {
declaringType = ((ReferenceType) declaringType).getGenericType();
}
if (parameterTypes != null && parameterTypes.length > 0) {
for (int i = 0; i < parameterTypes.length; i++) {
parameterTypes[i] = parameterTypes[i].resolve(world);
}
}
returnType = returnType.resolve(world);
} finally {
world.setTypeVariableLookupScope(null);
}
isResolved = true;
return this;
}
public ISourceContext getSourceContext(World world) {
return getDeclaringType().resolve(world).getSourceContext();
}
public String[] getParameterNames() {
return parameterNames;
}
public final void setParameterNames(String[] pnames) {
parameterNames = pnames;
}
@Override
public final String[] getParameterNames(World world) {
return getParameterNames();
}
public AjAttribute.EffectiveSignatureAttribute getEffectiveSignature() {
return null;
}
public ISourceLocation getSourceLocation() {
// System.out.println("get context: " + this + " is " + sourceContext);
if (getSourceContext() == null) {
// System.err.println("no context: " + this);
return null;
}
return getSourceContext().makeSourceLocation(this);
}
public int getEnd() {
return end;
}
public ISourceContext getSourceContext() {
return sourceContext;
}
public int getStart() {
return start;
}
public void setPosition(int sourceStart, int sourceEnd) {
this.start = sourceStart;
this.end = sourceEnd;
}
public void setDeclaringType(ReferenceType rt) {
declaringType = rt;
}
public void setSourceContext(ISourceContext sourceContext) {
this.sourceContext = sourceContext;
}
public boolean isAbstract() {
return Modifier.isAbstract(modifiers);
}
public boolean isPublic() {
return Modifier.isPublic(modifiers);
}
public boolean isDefault() {
int mods = getModifiers();
return !(Modifier.isPublic(mods) || Modifier.isProtected(mods) || Modifier.isPrivate(mods));
}
public boolean isVisible(ResolvedType fromType) {
UnresolvedType declaringType = getDeclaringType();
ResolvedType type = null;
if (fromType.equals(declaringType)) {
type = fromType;
} else {
World world = fromType.getWorld();
type = declaringType.resolve(world);
}
return ResolvedType.isVisible(getModifiers(), type, fromType);
}
public void setCheckedExceptions(UnresolvedType[] checkedExceptions) {
this.checkedExceptions = checkedExceptions;
}
public void setAnnotatedElsewhere(boolean b) {
isAnnotatedElsewhere = b;
}
public boolean isAnnotatedElsewhere() {
return isAnnotatedElsewhere;
}
/**
* Get the UnresolvedType for the return type, taking generic signature into account
*/
@Override
public UnresolvedType getGenericReturnType() {
return getReturnType();
}
/**
* Get the TypeXs of the parameter types, taking generic signature into account
*/
@Override
public UnresolvedType[] getGenericParameterTypes() {
return getParameterTypes();
}
public ResolvedMemberImpl parameterizedWith(UnresolvedType[] typeParameters, ResolvedType newDeclaringType,
boolean isParameterized) {
return parameterizedWith(typeParameters, newDeclaringType, isParameterized, null);
}
/**
* Return a resolvedmember in which all the type variables in the signature have been replaced with the given bindings. The
* 'isParameterized' flag tells us whether we are creating a raw type version or not. if (isParameterized) then List<T> will
* turn into List<String> (for example) - if (!isParameterized) then List<T> will turn into List.
*/
public ResolvedMemberImpl parameterizedWith(UnresolvedType[] typeParameters, ResolvedType newDeclaringType,
boolean isParameterized, List<String> aliases) {
// PR308773
// this check had problems for the inner type of a generic type because the inner type can be represented
// by a 'simple type' if it is only sharing type variables with the outer and has none of its own. To avoid the
// check going bang in this case we check for $ (crap...) - we can't check the outer because the declaring type
// is considered unresolved...
if (// isParameterized && <-- might need this bit...
!getDeclaringType().isGenericType() && getDeclaringType().getName().indexOf("$") == -1) {
throw new IllegalStateException("Can't ask to parameterize a member of non-generic type: " + getDeclaringType()
+ " kind(" + getDeclaringType().typeKind + ")");
}
TypeVariable[] typeVariables = getDeclaringType().getTypeVariables();
if (isParameterized && (typeVariables.length != typeParameters.length)) {
throw new IllegalStateException("Wrong number of type parameters supplied");
}
Map<String, UnresolvedType> typeMap = new HashMap<String, UnresolvedType>();
boolean typeParametersSupplied = typeParameters != null && typeParameters.length > 0;
if (typeVariables != null) {
// If no 'replacements' were supplied in the typeParameters array
// then collapse
// type variables to their first bound.
for (int i = 0; i < typeVariables.length; i++) {
UnresolvedType ut = (!typeParametersSupplied ? typeVariables[i].getFirstBound() : typeParameters[i]);
typeMap.put(typeVariables[i].getName(), ut);
}
}
// For ITDs on generic types that use type variables from the target type, the aliases
// record the alternative names used throughout the ITD expression that must map to
// the same value as the type variables real name.
if (aliases != null) {
int posn = 0;
for (String typeVariableAlias : aliases) {
typeMap.put(typeVariableAlias, (!typeParametersSupplied ? typeVariables[posn].getFirstBound()
: typeParameters[posn]));
posn++;
}
}
UnresolvedType parameterizedReturnType = parameterize(getGenericReturnType(), typeMap, isParameterized,
newDeclaringType.getWorld());
UnresolvedType[] parameterizedParameterTypes = new UnresolvedType[getGenericParameterTypes().length];
UnresolvedType[] genericParameterTypes = getGenericParameterTypes();
for (int i = 0; i < parameterizedParameterTypes.length; i++) {
parameterizedParameterTypes[i] = parameterize(genericParameterTypes[i], typeMap, isParameterized,
newDeclaringType.getWorld());
}
ResolvedMemberImpl ret = new ResolvedMemberImpl(getKind(), newDeclaringType, getModifiers(), parameterizedReturnType,
getName(), parameterizedParameterTypes, getExceptions(), this);
ret.setTypeVariables(getTypeVariables());
ret.setSourceContext(getSourceContext());
ret.setPosition(getStart(), getEnd());
ret.setParameterNames(getParameterNames());
return ret;
}
/**
* Replace occurrences of type variables in the signature with values contained in the map. The map is of the form
* A=String,B=Integer and so a signature List<A> Foo.m(B i) {} would become List<String> Foo.m(Integer i) {}
*/
public ResolvedMember parameterizedWith(Map<String, UnresolvedType> m, World w) {
// if (//isParameterized && <-- might need this bit...
// !getDeclaringType().isGenericType()) {
// throw new IllegalStateException(
// "Can't ask to parameterize a member of non-generic type: "
// +getDeclaringType()+" kind("+
// getDeclaringType().typeKind+")");
// }
declaringType = declaringType.resolve(w);
if (declaringType.isRawType()) {
declaringType = ((ResolvedType) declaringType).getGenericType();
// TypeVariable[] typeVariables = getDeclaringType().getTypeVariables();
// if (isParameterized && (typeVariables.length !=
// typeParameters.length)) {
// throw new
// IllegalStateException("Wrong number of type parameters supplied");
// }
// Map typeMap = new HashMap();
// boolean typeParametersSupplied = typeParameters!=null &&
// typeParameters.length>0;
// if (typeVariables!=null) {
// // If no 'replacements' were supplied in the typeParameters array
// then collapse
// // type variables to their first bound.
// for (int i = 0; i < typeVariables.length; i++) {
// UnresolvedType ut =
// (!typeParametersSupplied?typeVariables[i].getFirstBound
// ():typeParameters[i]);
// typeMap.put(typeVariables[i].getName(),ut);
// }
// }
// // For ITDs on generic types that use type variables from the target
// type, the aliases
// // record the alternative names used throughout the ITD expression
// that must map to
// // the same value as the type variables real name.
// if (aliases!=null) {
// int posn = 0;
// for (Iterator iter = aliases.iterator(); iter.hasNext();) {
// String typeVariableAlias = (String) iter.next();
// typeMap.put(typeVariableAlias,(!typeParametersSupplied?typeVariables[
// posn].getFirstBound():typeParameters[posn]));
// posn++;
// }
// }
}
UnresolvedType parameterizedReturnType = parameterize(getGenericReturnType(), m, true, w);
UnresolvedType[] parameterizedParameterTypes = new UnresolvedType[getGenericParameterTypes().length];
UnresolvedType[] genericParameterTypes = getGenericParameterTypes();
for (int i = 0; i < parameterizedParameterTypes.length; i++) {
parameterizedParameterTypes[i] = parameterize(genericParameterTypes[i], m, true, w);
}
ResolvedMemberImpl ret = new ResolvedMemberImpl(getKind(), declaringType, getModifiers(), parameterizedReturnType,
getName(), parameterizedParameterTypes, getExceptions(), this);
ret.setTypeVariables(getTypeVariables());
ret.setSourceContext(getSourceContext());
ret.setPosition(getStart(), getEnd());
ret.setParameterNames(getParameterNames());
return ret;
}
public void setTypeVariables(TypeVariable[] tvars) {
typeVariables = tvars;
}
public TypeVariable[] getTypeVariables() {
return typeVariables;
}
protected UnresolvedType parameterize(UnresolvedType aType, Map<String, UnresolvedType> typeVariableMap,
boolean inParameterizedType, World w) {
if (aType instanceof TypeVariableReference) {
String variableName = ((TypeVariableReference) aType).getTypeVariable().getName();
if (!typeVariableMap.containsKey(variableName)) {
return aType; // if the type variable comes from the method (and
// not the type) thats OK
}
return typeVariableMap.get(variableName);
} else if (aType.isParameterizedType()) {
if (inParameterizedType) {
if (w != null) {
aType = aType.resolve(w);
} else {
UnresolvedType dType = getDeclaringType();
aType = aType.resolve(((ResolvedType) dType).getWorld());
}
return aType.parameterize(typeVariableMap);
} else {
return aType.getRawType();
}
} else if (aType.isArray()) {
// The component type might be a type variable (pr150095)
int dims = 1;
String sig = aType.getSignature();
// while (sig.charAt(dims) == '[')
// dims++;
UnresolvedType arrayType = null;
UnresolvedType componentSig = UnresolvedType.forSignature(sig.substring(dims));
UnresolvedType parameterizedComponentSig = parameterize(componentSig, typeVariableMap, inParameterizedType, w);
if (parameterizedComponentSig.isTypeVariableReference()
&& parameterizedComponentSig instanceof UnresolvedTypeVariableReferenceType
&& typeVariableMap.containsKey(((UnresolvedTypeVariableReferenceType) parameterizedComponentSig)
.getTypeVariable().getName())) { // pr250632
// TODO ASC bah, this code is rubbish - i should fix it properly
StringBuffer newsig = new StringBuffer();
newsig.append("[T");
newsig.append(((UnresolvedTypeVariableReferenceType) parameterizedComponentSig).getTypeVariable().getName());
newsig.append(";");
arrayType = UnresolvedType.forSignature(newsig.toString());
} else {
arrayType = ResolvedType.makeArray(parameterizedComponentSig, dims);
}
return arrayType;
}
return aType;
}
/**
* If this member is defined by a parameterized super-type, return the erasure of that member. For example: interface I<T> { T
* foo(T aTea); } class C implements I<String> { String foo(String aString) { return "something"; } } The resolved member for
* C.foo has signature String foo(String). The erasure of that member is Object foo(Object) -- use upper bound of type variable.
* A type is a supertype of itself.
*/
// public ResolvedMember getErasure() {
// if (calculatedMyErasure) return myErasure;
// calculatedMyErasure = true;
// ResolvedType resolvedDeclaringType = (ResolvedType) getDeclaringType();
// // this next test is fast, and the result is cached.
// if (!resolvedDeclaringType.hasParameterizedSuperType()) {
// return null;
// } else {
// // we have one or more parameterized super types.
// // this member may be defined by one of them... we need to find out.
// Collection declaringTypes =
// this.getDeclaringTypes(resolvedDeclaringType.getWorld());
// for (Iterator iter = declaringTypes.iterator(); iter.hasNext();) {
// ResolvedType aDeclaringType = (ResolvedType) iter.next();
// if (aDeclaringType.isParameterizedType()) {
// // we've found the (a?) parameterized type that defines this member.
// // now get the erasure of it
// ResolvedMemberImpl matchingMember = (ResolvedMemberImpl)
// aDeclaringType.lookupMemberNoSupers(this);
// if (matchingMember != null && matchingMember.backingGenericMember !=
// null) {
// myErasure = matchingMember.backingGenericMember;
// return myErasure;
// }
// }
// }
// }
// return null;
// }
//
// private ResolvedMember myErasure = null;
// private boolean calculatedMyErasure = false;
public boolean hasBackingGenericMember() {
return backingGenericMember != null;
}
public ResolvedMember getBackingGenericMember() {
return backingGenericMember;
}
/**
* For ITDs, we use the default factory methods to build a resolved member, then alter a couple of characteristics using this
* method - this is safe.
*/
public void resetName(String newName) {
this.name = newName;
}
public void resetKind(MemberKind newKind) {
this.kind = newKind;
}
public void resetModifiers(int newModifiers) {
this.modifiers = newModifiers;
}
public void resetReturnTypeToObjectArray() {
returnType = UnresolvedType.OBJECTARRAY;
}
/**
* Returns true if this member matches the other. The matching takes into account name and parameter types only. When comparing
* parameter types, we allow any type variable to match any other type variable regardless of bounds.
*/
public boolean matches(ResolvedMember aCandidateMatch, boolean ignoreGenerics) {
ResolvedMemberImpl candidateMatchImpl = (ResolvedMemberImpl) aCandidateMatch;
if (!getName().equals(aCandidateMatch.getName())) {
return false;
}
UnresolvedType[] parameterTypes = getGenericParameterTypes();
UnresolvedType[] candidateParameterTypes = aCandidateMatch.getGenericParameterTypes();
if (parameterTypes.length != candidateParameterTypes.length) {
return false;
}
boolean b = false;
/*
* if (ignoreGenerics) { String myParameterSignature = getParameterSigWithBoundsRemoved(); String
* candidateParameterSignature = candidateMatchImpl.getParameterSigWithBoundsRemoved(); if
* (myParameterSignature.equals(candidateParameterSignature)) { b = true; } else { myParameterSignature =
* (hasBackingGenericMember() ? backingGenericMember.getParameterSignatureErased() : getParameterSignatureErased());
* candidateParameterSignature = (candidateMatchImpl.hasBackingGenericMember() ? candidateMatchImpl.backingGenericMember
* .getParameterSignatureErased() : candidateMatchImpl.getParameterSignatureErased()); // System.out.println("my psig = " +
* myParameterSignature); // System.out.println("can psig = " + candidateParameterSignature); b =
* myParameterSignature.equals(candidateParameterSignature); } } else {
*/
String myParameterSignature = getParameterSigWithBoundsRemoved();
String candidateParameterSignature = candidateMatchImpl.getParameterSigWithBoundsRemoved();
if (myParameterSignature.equals(candidateParameterSignature)) {
b = true;
} else {
// try erasure
myParameterSignature = getParameterSignatureErased();
candidateParameterSignature = candidateMatchImpl.getParameterSignatureErased();
// myParameterSignature = (hasBackingGenericMember() ? backingGenericMember.getParameterSignatureErased()
// : getParameterSignatureErased());
// candidateParameterSignature = (candidateMatchImpl.hasBackingGenericMember() ?
// candidateMatchImpl.backingGenericMember
// .getParameterSignatureErased() : candidateMatchImpl.getParameterSignatureErased());
// System.out.println("my psig = " + myParameterSignature);
// System.out.println("can psig = " + candidateParameterSignature);
b = myParameterSignature.equals(candidateParameterSignature);
// }
}
// System.out.println("Checking param signatures: " + b);
return b;
}
/**
* converts e.g. <T extends Number>.... List<T> to just Ljava/util/List<T;>; whereas the full signature would be
* Ljava/util/List<T:Ljava/lang/Number;>;
*/
private String myParameterSignatureWithBoundsRemoved = null;
/**
* converts e.g. <T extends Number>.... List<T> to just Ljava/util/List;
*/
private String myParameterSignatureErasure = null;
// does NOT produce a meaningful java signature, but does give a unique
// string suitable for
// comparison.
private String getParameterSigWithBoundsRemoved() {
if (myParameterSignatureWithBoundsRemoved != null) {
return myParameterSignatureWithBoundsRemoved;
}
StringBuffer sig = new StringBuffer();
UnresolvedType[] myParameterTypes = getGenericParameterTypes();
for (int i = 0; i < myParameterTypes.length; i++) {
appendSigWithTypeVarBoundsRemoved(myParameterTypes[i], sig, new HashSet<UnresolvedType>());
}
myParameterSignatureWithBoundsRemoved = sig.toString();
return myParameterSignatureWithBoundsRemoved;
}
/**
* Return the erased form of the signature with bounds collapsed for type variables, etc. Does not include the return type, @see
* getParam
*/
public String getParameterSignatureErased() {
if (myParameterSignatureErasure == null) {
StringBuilder sig = new StringBuilder();
for (UnresolvedType parameter : getParameterTypes()) {
sig.append(parameter.getErasureSignature());
}
myParameterSignatureErasure = sig.toString();
}
return myParameterSignatureErasure;
}
public String getSignatureErased() {
StringBuffer sb = new StringBuffer();
sb.append("(");
sb.append(getParameterSignatureErased());
sb.append(")");
sb.append(getReturnType().getErasureSignature());
return sb.toString();
}
// does NOT produce a meaningful java signature, but does give a unique
// string suitable for
// comparison.
public static void appendSigWithTypeVarBoundsRemoved(UnresolvedType aType, StringBuffer toBuffer,
Set<UnresolvedType> alreadyUsedTypeVars) {
if (aType.isTypeVariableReference()) {
TypeVariableReferenceType typeVariableRT = (TypeVariableReferenceType) aType;
// pr204505
if (alreadyUsedTypeVars.contains(aType)) {
toBuffer.append("...");
} else {
alreadyUsedTypeVars.add(aType);
appendSigWithTypeVarBoundsRemoved(typeVariableRT.getTypeVariable().getFirstBound(), toBuffer, alreadyUsedTypeVars);
}
// toBuffer.append("T;");
} else if (aType.isParameterizedType()) {
toBuffer.append(aType.getRawType().getSignature());
toBuffer.append("<");
for (int i = 0; i < aType.getTypeParameters().length; i++) {
appendSigWithTypeVarBoundsRemoved(aType.getTypeParameters()[i], toBuffer, alreadyUsedTypeVars);
}
toBuffer.append(">;");
} else {
toBuffer.append(aType.getSignature());
}
}
/**
* Useful for writing tests, returns *everything* we know about this member.
*/
public String toDebugString() {
StringBuffer r = new StringBuffer();
// modifiers
int mods = modifiers;
if ((mods & 4096) > 0) {
mods = mods - 4096; // remove synthetic (added in the ASM case but
}
// not in the BCEL case...)
if ((mods & 512) > 0) {
mods = mods - 512; // remove interface (added in the BCEL case but
}
// not in the ASM case...)
if ((mods & 131072) > 0) {
mods = mods - 131072; // remove deprecated (added in the ASM case
}
// but not in the BCEL case...)
String modsStr = Modifier.toString(mods);
if (modsStr.length() != 0) {
r.append(modsStr).append("(" + mods + ")").append(" ");
}
// type variables
if (typeVariables != null && typeVariables.length > 0) {
r.append("<");
for (int i = 0; i < typeVariables.length; i++) {
if (i > 0) {
r.append(",");
}
TypeVariable t = typeVariables[i];
r.append(t.toDebugString());
}
r.append("> ");
}
// 'declaring' type
r.append(getGenericReturnType().toDebugString());
r.append(' ');
// name
r.append(declaringType.getName());
r.append('.');
r.append(name);
// parameter signature if a method
if (kind != FIELD) {
r.append("(");
UnresolvedType[] params = getGenericParameterTypes();
boolean parameterNamesExist = showParameterNames && parameterNames != null && parameterNames.length == params.length;
if (params.length != 0) {
for (int i = 0, len = params.length; i < len; i++) {
if (i > 0) {
r.append(", ");
}
r.append(params[i].toDebugString());
if (parameterNamesExist) {
r.append(" ").append(parameterNames[i]);
}
}
}
r.append(")");
}
return r.toString();
}
// SECRETAPI - controlling whether parameter names come out in the debug
// string (for testing purposes)
public static boolean showParameterNames = true;
public String toGenericString() {
StringBuffer buf = new StringBuffer();
buf.append(getGenericReturnType().getSimpleName());
buf.append(' ');
buf.append(declaringType.getName());
buf.append('.');
buf.append(name);
if (kind != FIELD) {
buf.append("(");
UnresolvedType[] params = getGenericParameterTypes();
if (params.length != 0) {
buf.append(params[0].getSimpleName());
for (int i = 1, len = params.length; i < len; i++) {
buf.append(", ");
buf.append(params[i].getSimpleName());
}
}
buf.append(")");
}
return buf.toString();
}
public boolean isCompatibleWith(Member am) {
if (kind != METHOD || am.getKind() != METHOD) {
return true;
}
if (!name.equals(am.getName())) {
return true;
}
if (!equalTypes(getParameterTypes(), am.getParameterTypes())) {
return true;
}
return getReturnType().equals(am.getReturnType());
}
private static boolean equalTypes(UnresolvedType[] a, UnresolvedType[] b) {
int len = a.length;
if (len != b.length) {
return false;
}
for (int i = 0; i < len; i++) {
if (!a[i].equals(b[i])) {
return false;
}
}
return true;
}
public TypeVariable getTypeVariableNamed(String name) {
// Check locally...
if (typeVariables != null) {
for (int i = 0; i < typeVariables.length; i++) {
if (typeVariables[i].getName().equals(name)) {
return typeVariables[i];
}
}
}
// check the declaring type!
return declaringType.getTypeVariableNamed(name);
// Do generic aspects with ITDs that share type variables with the
// aspect and the target type and have their own tvars cause
// this to be messier?
}
public void evictWeavingState() {
}
public boolean isEquivalentTo(Object other) {
return this.equals(other);
}
public boolean isDefaultConstructor() {
return false;
}
}