/* ******************************************************************* * 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() { StringBuffer sb = new StringBuffer(); 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() { StringBuffer sb = new StringBuffer(); 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 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. .... 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() { 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 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 (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; } }