org.aspectj.weaver.ResolvedMemberImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aspectjtools Show documentation
Show all versions of aspectjtools Show documentation
Tools from the AspectJ project
/* *******************************************************************
* 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 v 2.0
* which accompanies this distribution and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
* ******************************************************************/
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 declaringTypes = new ArrayList<>();
accumulateTypesInBetween(originalDeclaringType, firstDefiningType, declaringTypes);
Set memberSignatures = new LinkedHashSet<>();
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 superTypeIterator = firstDefiningType.getDirectSupertypes();
List typesAlreadyVisited = new ArrayList<>();
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 types) {
types.add(subType);
if (subType == superType) {
return;
} else {
for (Iterator 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 typesToLookIn,
List typesAlreadyVisited, Set 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 declaringTypes = new ArrayList<>();
// 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 (ResolvedType annotationType : annotationTypes) {
if (annotationType.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 (TypeVariable typeVariable : typeVariables) {
typeVariable.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 (UnresolvedType parameterType : parameterTypes) {
if (s.canCompress()) {
s.writeCompressedSignature(parameterType.getSignature());
} else {
UnresolvedType array_element = parameterType;
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() {
StringBuilder sb = new StringBuilder();
if (typeVariables != null) {
sb.append("<");
for (TypeVariable typeVariable : typeVariables) {
sb.append(typeVariable.getSignatureForAttribute()); // need
// a
// 'getSignatureForAttribute()'
}
sb.append(">");
}
sb.append("(");
for (UnresolvedType parameterType : parameterTypes) {
ResolvedType ptype = (ResolvedType) parameterType;
sb.append(ptype.getSignatureForAttribute());
}
sb.append(")");
sb.append(((ResolvedType) returnType).getSignatureForAttribute());
return sb.toString();
}
public String getGenericSignature() {
StringBuilder sb = new StringBuilder();
if (typeVariables != null) {
sb.append("<");
for (TypeVariable typeVariable : typeVariables) {
sb.append(typeVariable.getSignature());
}
sb.append(">");
}
sb.append("(");
for (UnresolvedType ptype : parameterTypes) {
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 (ResolvedMember member : members) {
member.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 r = new HashSet();
// 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 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().contains("$")) {
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 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 (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 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 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
StringBuilder newsig = new StringBuilder();
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. .... List to just Ljava/util/List; whereas the full signature would be
* Ljava/util/List;
*/
private String myParameterSignatureWithBoundsRemoved = null;
/**
* converts e.g. .... List 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 (UnresolvedType myParameterType : myParameterTypes) {
appendSigWithTypeVarBoundsRemoved(myParameterType, sig, new HashSet<>());
}
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() {
StringBuilder sb = new StringBuilder();
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 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() {
StringBuilder r = new StringBuilder();
// 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() {
StringBuilder buf = new StringBuilder();
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 (TypeVariable typeVariable : typeVariables) {
if (typeVariable.getName().equals(name)) {
return typeVariable;
}
}
}
// 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;
}
}