From 64b52cf36f89ea4404cddaaea9bce8aaf7562a85 Mon Sep 17 00:00:00 2001 From: acolyer Date: Thu, 4 Aug 2005 13:21:00 +0000 Subject: [PATCH] signature matching algorithm simplified, sorted, and fully genericised. --- .../aspectj/weaver/JoinPointSignature.java | 354 +++++++++ .../aspectj/weaver/ResolvedMemberImpl.java | 674 ++++++++++++++++++ 2 files changed, 1028 insertions(+) create mode 100644 weaver/src/org/aspectj/weaver/JoinPointSignature.java create mode 100644 weaver/src/org/aspectj/weaver/ResolvedMemberImpl.java diff --git a/weaver/src/org/aspectj/weaver/JoinPointSignature.java b/weaver/src/org/aspectj/weaver/JoinPointSignature.java new file mode 100644 index 000000000..12f66fca1 --- /dev/null +++ b/weaver/src/org/aspectj/weaver/JoinPointSignature.java @@ -0,0 +1,354 @@ +/* ******************************************************************* + * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Collection; + +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.weaver.AjAttribute.EffectiveSignatureAttribute; + +/** + * @author colyer + * Instances of this class are created by ResolvedMember.getSignatures() when collating + * all of the signatures for a member. We need to create entries in the set for the "gaps" + * in the hierarchy. For example: + * + * class A { + * void foo(); + * } + * + * class B extends A {} + * + * Join Point : call(* B.foo()) + * + * has signatures: + * + * B.foo() AND A.foo() + * B.foo() will be created as a ResolvedMemberWithSubstituteDeclaringType + * + * Oh for a JDK 1.4 dynamic proxy.... we have to run on 1.3 :( + */ +public class JoinPointSignature implements ResolvedMember { + + private ResolvedMember realMember; + private ResolvedType substituteDeclaringType; + + public JoinPointSignature(ResolvedMember backing, ResolvedType aType) { + this.realMember = backing; + this.substituteDeclaringType = aType; + } + + public UnresolvedType getDeclaringType() { + return substituteDeclaringType; + } + + public int getModifiers(World world) { + return realMember.getModifiers(world); + } + + public int getModifiers() { + return realMember.getModifiers(); + } + + public UnresolvedType[] getExceptions(World world) { + return realMember.getExceptions(world); + } + + public UnresolvedType[] getExceptions() { + return realMember.getExceptions(); + } + + public ShadowMunger getAssociatedShadowMunger() { + return realMember.getAssociatedShadowMunger(); + } + + public boolean isAjSynthetic() { + return realMember.isAjSynthetic(); + } + + public boolean hasAnnotations() { + return realMember.hasAnnotations(); + } + + public boolean hasAnnotation(UnresolvedType ofType) { + return realMember.hasAnnotation(ofType); + } + + public ResolvedType[] getAnnotationTypes() { + return realMember.getAnnotationTypes(); + } + + public void setAnnotationTypes(UnresolvedType[] annotationtypes) { + realMember.setAnnotationTypes(annotationtypes); + } + + public void addAnnotation(AnnotationX annotation) { + realMember.addAnnotation(annotation); + } + + public boolean isBridgeMethod() { + return realMember.isBridgeMethod(); + } + + public boolean isVarargsMethod() { + return realMember.isVarargsMethod(); + } + + public boolean isSynthetic() { + return realMember.isSynthetic(); + } + + public void write(DataOutputStream s) throws IOException { + realMember.write(s); + } + + public ISourceContext getSourceContext(World world) { + return realMember.getSourceContext(world); + } + + public String[] getParameterNames() { + return realMember.getParameterNames(); + } + + public String[] getParameterNames(World world) { + return realMember.getParameterNames(world); + } + + public EffectiveSignatureAttribute getEffectiveSignature() { + return realMember.getEffectiveSignature(); + } + + public ISourceLocation getSourceLocation() { + return realMember.getSourceLocation(); + } + + public int getEnd() { + return realMember.getEnd(); + } + + public ISourceContext getSourceContext() { + return realMember.getSourceContext(); + } + + public int getStart() { + return realMember.getStart(); + } + + public void setPosition(int sourceStart, int sourceEnd) { + realMember.setPosition(sourceStart,sourceEnd); + } + + public void setSourceContext(ISourceContext sourceContext) { + realMember.setSourceContext(sourceContext); + } + + public boolean isAbstract() { + return realMember.isAbstract(); + } + + public boolean isPublic() { + return realMember.isPublic(); + } + + public boolean isProtected() { + return realMember.isProtected(); + } + + public boolean isNative() { + return realMember.isNative(); + } + + public boolean isDefault() { + return realMember.isDefault(); + } + + public boolean isVisible(ResolvedType fromType) { + return realMember.isVisible(fromType); + } + + public void setCheckedExceptions(UnresolvedType[] checkedExceptions) { + realMember.setCheckedExceptions(checkedExceptions); + } + + public void setAnnotatedElsewhere(boolean b) { + realMember.setAnnotatedElsewhere(b); + } + + public boolean isAnnotatedElsewhere() { + return realMember.isAnnotatedElsewhere(); + } + + public UnresolvedType getGenericReturnType() { + return realMember.getGenericReturnType(); + } + + public UnresolvedType[] getGenericParameterTypes() { + return realMember.getGenericParameterTypes(); + } + + public ResolvedMemberImpl parameterizedWith(UnresolvedType[] typeParameters, ResolvedType newDeclaringType, boolean isParameterized) { + return realMember.parameterizedWith(typeParameters, newDeclaringType, isParameterized); + } + + public void setTypeVariables(UnresolvedType[] types) { + realMember.setTypeVariables(types); + } + + public UnresolvedType[] getTypeVariables() { + return realMember.getTypeVariables(); + } + + public ResolvedMember getErasure() { + throw new UnsupportedOperationException("Adrian doesn't think you should be asking for the erasure of one of these..."); + } + + public boolean matches(ResolvedMember aCandidateMatch) { + return realMember.matches(aCandidateMatch); + } + + public ResolvedMember resolve(World world) { + return realMember.resolve(world); + } + + public int compareTo(Object other) { + return realMember.compareTo(other); + } + + public String toLongString() { + return realMember.toLongString(); + } + + public Kind getKind() { + return realMember.getKind(); + } + + public UnresolvedType getReturnType() { + return realMember.getReturnType(); + } + + public UnresolvedType getType() { + return realMember.getType(); + } + + public String getName() { + return realMember.getName(); + } + + public UnresolvedType[] getParameterTypes() { + return realMember.getParameterTypes(); + } + + public String getSignature() { + return realMember.getSignature(); + } + + public String getDeclaredSignature() { + return realMember.getDeclaredSignature(); + } + + public int getArity() { + return realMember.getArity(); + } + + public String getParameterSignature() { + return realMember.getParameterSignature(); + } + + public boolean isCompatibleWith(Member am) { + return realMember.isCompatibleWith(am); + } + + public boolean isProtected(World world) { + return realMember.isProtected(world); + } + + public boolean isStatic(World world) { + return realMember.isStatic(world); + } + + public boolean isStrict(World world) { + return realMember.isStrict(world); + } + + public boolean isStatic() { + return realMember.isStatic(); + } + + public boolean isInterface() { + return realMember.isInterface(); + } + + public boolean isPrivate() { + return realMember.isPrivate(); + } + + public boolean canBeParameterized() { + return realMember.canBeParameterized(); + } + + public int getCallsiteModifiers() { + return realMember.getCallsiteModifiers(); + } + + public String getExtractableName() { + return realMember.getExtractableName(); + } + + public AnnotationX[] getAnnotations() { + return realMember.getAnnotations(); + } + + public Collection getDeclaringTypes(World world) { + throw new UnsupportedOperationException("Adrian doesn't think you should be calling this..."); + } + + public String getSignatureMakerName() { + return realMember.getSignatureMakerName(); + } + + public String getSignatureType() { + return realMember.getSignatureType(); + } + + public String getSignatureString(World world) { + return realMember.getSignatureString(world); + } + + public JoinPointSignature[] getJoinPointSignatures(World world) { + return realMember.getJoinPointSignatures(world); + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append(getReturnType().getName()); + buf.append(' '); + buf.append(getDeclaringType().getName()); + buf.append('.'); + buf.append(getName()); + if (getKind() != FIELD) { + buf.append("("); + UnresolvedType[] parameterTypes = getParameterTypes(); + if (parameterTypes.length != 0) { + buf.append(parameterTypes[0]); + for (int i=1, len = parameterTypes.length; i < len; i++) { + buf.append(", "); + buf.append(parameterTypes[i].getName()); + } + } + buf.append(")"); + } + return buf.toString(); + } + +} diff --git a/weaver/src/org/aspectj/weaver/ResolvedMemberImpl.java b/weaver/src/org/aspectj/weaver/ResolvedMemberImpl.java new file mode 100644 index 000000000..de94d3c55 --- /dev/null +++ b/weaver/src/org/aspectj/weaver/ResolvedMemberImpl.java @@ -0,0 +1,674 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.weaver; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.aspectj.bridge.ISourceLocation; + +/** + * This is the declared member, i.e. it will always correspond to an + * actual method/... declaration + */ +public class ResolvedMemberImpl extends MemberImpl implements IHasPosition, AnnotatedElement, TypeVariableDeclaringElement, ResolvedMember { + + public String[] parameterNames = null; + 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 Set annotationTypes = 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; // this field is not serialized. + private boolean isAjSynthetic = true; + + // generic methods have type variables + private UnresolvedType[] 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( + Kind kind, + UnresolvedType declaringType, + int modifiers, + UnresolvedType returnType, + String name, + UnresolvedType[] parameterTypes) + { + super(kind, declaringType, modifiers, returnType, name, parameterTypes); + } + + + + public ResolvedMemberImpl( + Kind 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( + Kind 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( + Kind 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 new JoinPointSignature[0]; + } + // 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 new JoinPointSignature[0]; + } +// else if (shadowMember.isStatic()) { +// return new ResolvedMember[] {firstDefiningMember}; +// } + } + + List declaringTypes = new ArrayList(); + accumulateTypesInBetween(originalDeclaringType, + firstDefiningType, + declaringTypes); + List memberSignatures = new ArrayList(); + for (Iterator iter = declaringTypes.iterator(); iter.hasNext();) { + ResolvedType declaringType = (ResolvedType) iter.next(); + ResolvedMember member = firstDefiningMember.withSubstituteDeclaringType(declaringType); + memberSignatures.add(member); + } + + 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); + } + + 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 (aMember.isStatic()) 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 = (ResolvedType) 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, + List foundMembers) { + while(typesToLookIn.hasNext()) { + ResolvedType toLookIn = (ResolvedType) typesToLookIn.next(); + if (!typesAlreadyVisited.contains(toLookIn)) { + typesAlreadyVisited.add(toLookIn); + ResolvedMemberImpl foundMember = (ResolvedMemberImpl) toLookIn.lookupResolvedMember(memberToMatch); + if (foundMember != null) { + 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 (Iterator iter = declaringTypes.iterator(); iter.hasNext();) { + ResolvedType declaringType = (ResolvedType) iter.next(); + typesAlreadyVisited.add(declaringType); + ResolvedMember member = foundMember.withSubstituteDeclaringType(declaringType); + foundMembers.add(member); + } + if (toLookIn.isParameterizedType() && (foundMember.backingGenericMember != null)) { + foundMembers.add(new JoinPointSignature(foundMember.backingGenericMember,foundMember.declaringType.resolve(toLookIn.getWorld()))); + } + accumulateMembersMatching(foundMember,toLookIn.getDirectSupertypes(),typesAlreadyVisited,foundMembers); + // if this was a parameterized type, look in the generic type that backs it too + } + } + } + } + + + // ---- + + public final int getModifiers(World world) { + return modifiers; + } + public final int getModifiers() { + return modifiers; + } + + // ---- + + + 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; + } + + public boolean hasAnnotations() { + return (annotationTypes==null); + } + + 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 (annotationTypes==null) return false; + return annotationTypes.contains(ofType); + } + + 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 (annotationTypes == null) return null; + return (ResolvedType[])annotationTypes.toArray(new ResolvedType[]{}); + } + + public void setAnnotationTypes(UnresolvedType[] annotationtypes) { + if (annotationTypes == null) annotationTypes = new HashSet(); + for (int i = 0; i < annotationtypes.length; i++) { + UnresolvedType typeX = annotationtypes[i]; + annotationTypes.add(typeX); + } + } + + public void addAnnotation(AnnotationX annotation) { + // FIXME asc only allows for annotation types, not instances - should it? + if (annotationTypes == null) annotationTypes = new HashSet(); + annotationTypes.add(annotation.getSignature()); + } + + public boolean isBridgeMethod() { + return (modifiers & Constants.ACC_BRIDGE)!=0; + } + + public boolean isVarargsMethod() { + return (modifiers & Constants.ACC_VARARGS)!=0; + } + + public boolean isSynthetic() { + return false; + } + + public void write(DataOutputStream s) throws IOException { + getKind().write(s); + getDeclaringType().write(s); + s.writeInt(modifiers); + s.writeUTF(getName()); + s.writeUTF(getSignature()); + UnresolvedType.writeArray(getExceptions(), s); + + s.writeInt(getStart()); + s.writeInt(getEnd()); + + // Write out any type variables... + if (typeVariables==null) { + s.writeInt(0); + } else { + s.writeInt(typeVariables.length); + for (int i = 0; i < typeVariables.length; i++) { + typeVariables[i].write(s); + } + } + } + + public static void writeArray(ResolvedMember[] members, DataOutputStream 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 { + ResolvedMemberImpl m = new ResolvedMemberImpl(Kind.read(s), UnresolvedType.read(s), s.readInt(), s.readUTF(), s.readUTF()); + m.checkedExceptions = UnresolvedType.readArray(s); + + m.start = s.readInt(); + m.end = s.readInt(); + m.sourceContext = sourceContext; + + // Read in the type variables... + if (s.getMajorVersion()>=AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150) { + int tvcount = s.readInt(); + if (tvcount!=0) { + m.typeVariables = new UnresolvedType[tvcount]; + for (int i=0;i will turn into List (for example), + // but if !isParameterized List will turn into List. + public ResolvedMemberImpl parameterizedWith(UnresolvedType[] typeParameters,ResolvedType newDeclaringType, boolean isParameterized) { + if (!this.getDeclaringType().isGenericType()) { + throw new IllegalStateException("Can't ask to parameterize a member of a non-generic type"); + } + TypeVariable[] typeVariables = getDeclaringType().getTypeVariables(); + if (typeVariables.length != typeParameters.length) { + throw new IllegalStateException("Wrong number of type parameters supplied"); + } + Map typeMap = new HashMap(); + for (int i = 0; i < typeVariables.length; i++) { + typeMap.put(typeVariables[i].getName(), typeParameters[i]); + } + UnresolvedType parameterizedReturnType = parameterize(getGenericReturnType(),typeMap,isParameterized); + UnresolvedType[] parameterizedParameterTypes = new UnresolvedType[getGenericParameterTypes().length]; + for (int i = 0; i < parameterizedParameterTypes.length; i++) { + parameterizedParameterTypes[i] = + parameterize(getGenericParameterTypes()[i], typeMap,isParameterized); + } + return new ResolvedMemberImpl( + getKind(), + newDeclaringType, + getModifiers(), + parameterizedReturnType, + getName(), + parameterizedParameterTypes, + getExceptions(), + this + ); + } + + + public void setTypeVariables(UnresolvedType[] types) { + typeVariables = types; + } + + public UnresolvedType[] getTypeVariables() { + return typeVariables; + } + + private UnresolvedType parameterize(UnresolvedType aType, Map typeVariableMap, boolean inParameterizedType) { + if (aType instanceof TypeVariableReferenceType) { + String variableName = ((TypeVariableReferenceType)aType).getTypeVariable().getName(); + if (!typeVariableMap.containsKey(variableName)) { + return aType; // if the type variable comes from the method (and not the type) thats OK + } + return (UnresolvedType) typeVariableMap.get(variableName); + } else if (aType.isParameterizedType()) { + if (inParameterizedType) { + return aType.parameterize(typeVariableMap); + } else { + return aType.getRawType(); + } + } + return aType; + } + + + /** + * If this member is defined by a parameterized super-type, return the erasure + * of that member. + * For example: + * interface I { T foo(T aTea); } + * class C implements I { + * 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; + + + /** + * Returns a copy of this member but with the declaring type swapped. + * Copy only needs to be shallow. + * @param newDeclaringType + */ + private JoinPointSignature withSubstituteDeclaringType(ResolvedType newDeclaringType) { + JoinPointSignature ret = new JoinPointSignature(this,newDeclaringType); + return ret; + } + + /** + * 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) { + ResolvedMemberImpl candidateMatchImpl = (ResolvedMemberImpl)aCandidateMatch; + if (!getName().equals(aCandidateMatch.getName())) return false; + UnresolvedType[] myParameterTypes = getGenericParameterTypes(); + UnresolvedType[] candidateParameterTypes = aCandidateMatch.getGenericParameterTypes(); + if (myParameterTypes.length != candidateParameterTypes.length) return false; + String myParameterSignature = getParameterSigWithBoundsRemoved(); + String candidateParameterSignature = candidateMatchImpl.getParameterSigWithBoundsRemoved(); + if (myParameterSignature.equals(candidateParameterSignature)) { + return true; + } else { + // try erasure + myParameterSignature = getParameterSigErasure(); + candidateParameterSignature = candidateMatchImpl.getParameterSigErasure(); + return myParameterSignature.equals(candidateParameterSignature); + } + } + + /** 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 (int i = 0; i < myParameterTypes.length; i++) { + appendSigWithTypeVarBoundsRemoved(myParameterTypes[i], sig); + } + myParameterSignatureWithBoundsRemoved = sig.toString(); + return myParameterSignatureWithBoundsRemoved; + } + + private String getParameterSigErasure() { + if (myParameterSignatureErasure != null) return myParameterSignatureErasure; + StringBuffer sig = new StringBuffer(); + UnresolvedType[] myParameterTypes = getParameterTypes(); + for (int i = 0; i < myParameterTypes.length; i++) { + sig.append(myParameterTypes[i].getSignature()); + } + myParameterSignatureErasure = sig.toString(); + return myParameterSignatureErasure; + } + + // does NOT produce a meaningful java signature, but does give a unique string suitable for + // comparison. + private void appendSigWithTypeVarBoundsRemoved(UnresolvedType aType, StringBuffer toBuffer) { + if (aType.isTypeVariableReference()) { + 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); + } + toBuffer.append(">;"); + } else { + toBuffer.append(aType.getSignature()); + } + } +} + -- 2.39.5