diff options
8 files changed, 253 insertions, 18 deletions
diff --git a/weaver/src/org/aspectj/weaver/ReferenceType.java b/weaver/src/org/aspectj/weaver/ReferenceType.java index c377daf50..de65d1d86 100644 --- a/weaver/src/org/aspectj/weaver/ReferenceType.java +++ b/weaver/src/org/aspectj/weaver/ReferenceType.java @@ -283,7 +283,13 @@ public class ReferenceType extends ResolvedType { } public TypeVariable[] getTypeVariables() { - return delegate.getTypeVariables(); + if (this.typeVariables == null) { + this.typeVariables = delegate.getTypeVariables(); + for (int i = 0; i < this.typeVariables.length; i++) { + this.typeVariables[i].resolve(world); + } + } + return this.typeVariables; } public PerClause getPerClause() { return delegate.getPerClause(); } diff --git a/weaver/src/org/aspectj/weaver/ResolvedType.java b/weaver/src/org/aspectj/weaver/ResolvedType.java index 20ecf94ed..f1aa7761e 100644 --- a/weaver/src/org/aspectj/weaver/ResolvedType.java +++ b/weaver/src/org/aspectj/weaver/ResolvedType.java @@ -616,6 +616,11 @@ public abstract class ResolvedType extends UnresolvedType implements AnnotatedEl public static final Missing MISSING = new Missing(); // ---- types + public static ResolvedType makeArray(ResolvedType type, int dim) { + if (dim == 0) return type; + ResolvedType array = new Array("[" + type.getSignature(),type.getWorld(),type); + return makeArray(array,dim-1); + } static class Array extends ResolvedType { ResolvedType componentType; @@ -1405,7 +1410,7 @@ public abstract class ResolvedType extends UnresolvedType implements AnnotatedEl throw new BCException("The type "+getBaseName()+" is not parameterized or raw - it has no generic type"); return null; } - + public ResolvedType parameterizedWith(UnresolvedType[] typeParameters) { if (!(isGenericType() || isParameterizedType())) return this; return TypeFactory.createParameterizedType(this.getGenericType(), typeParameters, getWorld()); @@ -1420,7 +1425,7 @@ public abstract class ResolvedType extends UnresolvedType implements AnnotatedEl if (!isParameterizedType()) throw new IllegalStateException("Can't parameterize a type that is not a parameterized type"); boolean workToDo = false; for (int i = 0; i < typeParameters.length; i++) { - if (typeParameters[i].isTypeVariable()) { + if (typeParameters[i].isTypeVariableReference()) { workToDo = true; } } @@ -1430,7 +1435,7 @@ public abstract class ResolvedType extends UnresolvedType implements AnnotatedEl UnresolvedType[] newTypeParams = new UnresolvedType[typeParameters.length]; for (int i = 0; i < newTypeParams.length; i++) { newTypeParams[i] = typeParameters[i]; - if (newTypeParams[i].isTypeVariable()) { + if (newTypeParams[i].isTypeVariableReference()) { TypeVariableReferenceType tvrt = (TypeVariableReferenceType) newTypeParams[i]; UnresolvedType binding = (UnresolvedType) typeBindings.get(tvrt.getTypeVariable().getName()); if (binding != null) newTypeParams[i] = binding; diff --git a/weaver/src/org/aspectj/weaver/TypeVariable.java b/weaver/src/org/aspectj/weaver/TypeVariable.java index 042f4425f..b4b4d0261 100644 --- a/weaver/src/org/aspectj/weaver/TypeVariable.java +++ b/weaver/src/org/aspectj/weaver/TypeVariable.java @@ -105,7 +105,11 @@ public class TypeVariable { */ public boolean canBeBoundTo(ResolvedType aCandidateType) { if (!isResolved) throw new IllegalStateException("Can't answer binding questions prior to resolving"); - // can be bound iff... + if (aCandidateType.isTypeVariableReference()) { + return matchingBounds((TypeVariableReferenceType)aCandidateType); + } + + // otherwise can be bound iff... // aCandidateType is a subtype of upperBound if (!isASubtypeOf(upperBound,aCandidateType)) { return false; @@ -123,20 +127,59 @@ public class TypeVariable { return true; } + // can match any type in the range of the type variable... + // XXX what about interfaces? + private boolean matchingBounds(TypeVariableReferenceType tvrt) { + boolean upperMatch = canBeBoundTo(tvrt.getUpperBound()); + boolean lowerMatch = true; + if (tvrt.hasLowerBound()) lowerMatch = canBeBoundTo(tvrt.getLowerBound()); + return upperMatch && lowerMatch; + } + private boolean isASubtypeOf(UnresolvedType candidateSuperType, UnresolvedType candidateSubType) { ResolvedType superType = (ResolvedType) candidateSuperType; ResolvedType subType = (ResolvedType) candidateSubType; return superType.isAssignableFrom(subType); } - // only used when resolving circular dependencies + // only used when resolving public void setUpperBound(UnresolvedType aTypeX) { this.upperBound = aTypeX; } + // only used when resolving + public void setLowerBound(UnresolvedType aTypeX) { + this.lowerBound = aTypeX; + } + + // only used when resolving + public void setAdditionalInterfaceBounds(UnresolvedType[] someTypeXs) { + this.additionalInterfaceBounds = someTypeXs; + } + + public String getDisplayName() { + StringBuffer ret = new StringBuffer(); + ret.append(name); + if (!upperBound.getName().equals("java.lang.Object")) { + ret.append(" extends "); + ret.append(upperBound.getName()); + if (additionalInterfaceBounds != null) { + for (int i = 0; i < additionalInterfaceBounds.length; i++) { + ret.append(" & "); + ret.append(additionalInterfaceBounds[i].getName()); + } + } + } + if (lowerBound != null) { + ret.append(" super "); + ret.append(lowerBound.getName()); + } + return ret.toString(); + } + // good enough approximation public String toString() { - return "T" + upperBound.getSignature(); + return "TypeVar " + getDisplayName(); } /** diff --git a/weaver/src/org/aspectj/weaver/TypeVariableReference.java b/weaver/src/org/aspectj/weaver/TypeVariableReference.java new file mode 100644 index 000000000..fc6a989c7 --- /dev/null +++ b/weaver/src/org/aspectj/weaver/TypeVariableReference.java @@ -0,0 +1,22 @@ +/* ******************************************************************* + * 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; + +/** + * Implemented by Types that represent references to type variables + * + */ +public interface TypeVariableReference { + + TypeVariable getTypeVariable(); + +} diff --git a/weaver/src/org/aspectj/weaver/TypeVariableReferenceType.java b/weaver/src/org/aspectj/weaver/TypeVariableReferenceType.java index c37bd6369..bb37f3473 100644 --- a/weaver/src/org/aspectj/weaver/TypeVariableReferenceType.java +++ b/weaver/src/org/aspectj/weaver/TypeVariableReferenceType.java @@ -14,7 +14,7 @@ package org.aspectj.weaver; /** * Represents a type variable in a type or generic method declaration */ -public class TypeVariableReferenceType extends BoundedReferenceType { +public class TypeVariableReferenceType extends BoundedReferenceType implements TypeVariableReference { private TypeVariable typeVariable; @@ -41,7 +41,7 @@ public class TypeVariableReferenceType extends BoundedReferenceType { return typeVariable; } - public boolean isTypeVariable() { + public boolean isTypeVariableReference() { return true; } diff --git a/weaver/src/org/aspectj/weaver/UnresolvedTypeVariableReferenceType.java b/weaver/src/org/aspectj/weaver/UnresolvedTypeVariableReferenceType.java index 32dd637fe..667c10c43 100644 --- a/weaver/src/org/aspectj/weaver/UnresolvedTypeVariableReferenceType.java +++ b/weaver/src/org/aspectj/weaver/UnresolvedTypeVariableReferenceType.java @@ -16,7 +16,7 @@ package org.aspectj.weaver; * Represents a type variable encountered in the Eclipse Source world, * which when resolved will turn into a TypeVariableReferenceType */ -public class UnresolvedTypeVariableReferenceType extends UnresolvedType { +public class UnresolvedTypeVariableReferenceType extends UnresolvedType implements TypeVariableReference { private TypeVariable typeVariable; @@ -42,10 +42,19 @@ public class UnresolvedTypeVariableReferenceType extends UnresolvedType { return new TypeVariableReferenceType(typeVariable,world); } - public boolean isTypeVariable() { + public boolean isTypeVariableReference() { return true; } + + public TypeVariable getTypeVariable() { + return typeVariable; + } +// public String getName() { +// if (typeVariable == null) return "<type variable not set!>"; +// return typeVariable.getDisplayName(); +// } + public String toString() { if (typeVariable == null) { return "<type variable not set!>"; diff --git a/weaver/src/org/aspectj/weaver/World.java b/weaver/src/org/aspectj/weaver/World.java index d54ebfb57..0f9ca3410 100644 --- a/weaver/src/org/aspectj/weaver/World.java +++ b/weaver/src/org/aspectj/weaver/World.java @@ -115,7 +115,7 @@ public abstract class World implements Dump.INode { // if we already have an rtx, don't re-resolve it public ResolvedType resolve(ResolvedType ty) { - if (ty.isTypeVariable()) return ty; // until type variables have proper sigs... + if (ty.isTypeVariableReference()) return ty; // until type variables have proper sigs... ResolvedType resolved = typeMap.get(ty.getSignature()); if (resolved == null) { typeMap.put(ty.getSignature(), ty); @@ -151,7 +151,10 @@ public abstract class World implements Dump.INode { // raw type from a source type, it won't if its been created just through // being referenced, e.g. java.util.List ResolvedType generic = ret.getGenericType(); - if (generic != null) { generic.world = this; return generic; } + if (generic != null) { + generic.world = this; + return generic; + } // Fault in the generic that underpins the raw type ;) ReferenceTypeDelegate thegen = resolveObjectType((ReferenceType)ret); diff --git a/weaver/src/org/aspectj/weaver/patterns/WildTypePattern.java b/weaver/src/org/aspectj/weaver/patterns/WildTypePattern.java index 0d43a4797..a4234982f 100644 --- a/weaver/src/org/aspectj/weaver/patterns/WildTypePattern.java +++ b/weaver/src/org/aspectj/weaver/patterns/WildTypePattern.java @@ -28,7 +28,10 @@ import org.aspectj.weaver.BCException; import org.aspectj.weaver.ISourceContext; import org.aspectj.weaver.ResolvedType; import org.aspectj.weaver.TypeFactory; +import org.aspectj.weaver.TypeVariable; +import org.aspectj.weaver.TypeVariableReference; import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.UnresolvedTypeVariableReferenceType; import org.aspectj.weaver.VersionedDataInputStream; import org.aspectj.weaver.WeaverMessages; @@ -47,6 +50,10 @@ public class WildTypePattern extends TypePattern { TypePattern[] additionalInterfaceBounds; // extends Foo & A,B,C TypePattern lowerBound; // super Foo + // if we have type parameters, these fields indicate whether we should be a generic type pattern or a parameterized + // type pattern. We can only tell during resolve bindings. + private boolean isGeneric = true; + WildTypePattern(NamePattern[] namePatterns, boolean includeSubtypes, int dim, boolean isVarArgs, TypePatternList typeParams) { super(includeSubtypes,isVarArgs,typeParams); this.namePatterns = namePatterns; @@ -434,6 +441,19 @@ public class WildTypePattern extends TypePattern { // resolve any type parameters if (typeParameters!=null && typeParameters.size()>0) { typeParameters.resolveBindings(scope,bindings,allowBinding,requireExactType); + // now we have to decide whether to create a "generic" type pattern or a "parameterized" type + // pattern + // start with the simple rule that if all parameters have resolved to a type variable based pattern + // then it is generic, otherwise it is parameterized + // if we have e.g. staticinitialization<T>(Foo<T,String>) then that's a parameterized type + isGeneric = true; + TypePattern[] tps = typeParameters.getTypePatterns(); + for (int i = 0; i < tps.length; i++) { + if (!tps[i].getExactType().isTypeVariableReference()) { + isGeneric = false; + break; + } + } } String fullyQualifiedName = maybeGetCleanName(); @@ -522,7 +542,11 @@ public class WildTypePattern extends TypePattern { private TypePattern resolveBindingsForExactType(IScope scope, UnresolvedType aType, String fullyQualifiedName) { TypePattern ret = null; - if (typeParameters.size()>0) { + if (aType.isTypeVariableReference()) { + // we have to set the bounds on it based on the bounds of this pattern + ret = resolveBindingsForTypeVariable(scope, (UnresolvedTypeVariableReferenceType) aType); + } else if (typeParameters.size()>0) { + if (!verifyTypeParameters(aType.resolve(scope.getWorld()),scope)) return TypePattern.NO; // messages already isued // Only if the type is exact *and* the type parameters are exact should we create an // ExactTypePattern for this WildTypePattern if (typeParameters.areAllExact()) { @@ -531,14 +555,13 @@ public class WildTypePattern extends TypePattern { for (int i = 0; i < typeParameterTypes.length; i++) { typeParameterTypes[i] = ((ExactTypePattern)typePats[i]).getExactType(); } - UnresolvedType type = TypeFactory.createParameterizedType(aType.resolve(scope.getWorld()), typeParameterTypes, scope.getWorld()); + ResolvedType type = TypeFactory.createParameterizedType(aType.resolve(scope.getWorld()), typeParameterTypes, scope.getWorld()); + if (isGeneric) type = type.getGenericType(); // UnresolvedType tx = UnresolvedType.forParameterizedTypes(aType,typeParameterTypes); // UnresolvedType type = scope.getWorld().resolve(tx,true); - if (dim != 0) type = UnresolvedType.makeArray(type, dim); + if (dim != 0) type = ResolvedType.makeArray(type, dim); ret = new ExactTypePattern(type,includeSubtypes,isVarArgs); } else { - // TODO generics not written yet - when the type parameters are not exact - //throw new RuntimeException("Type parameters are not exact"); // AMC... just leave it as a wild type pattern then? importedPrefixes = scope.getImportedPrefixes(); knownMatches = preMatch(scope.getImportedNames()); @@ -575,6 +598,130 @@ public class WildTypePattern extends TypePattern { return this; } + /** + * We resolved the type to a type variable declared in the pointcut designator. + * Now we have to create either an exact type pattern or a wild type pattern for it, + * with upper and lower bounds set accordingly. + * XXX none of this stuff gets serialized yet + * @param scope + * @param tvrType + * @return + */ + private TypePattern resolveBindingsForTypeVariable(IScope scope, UnresolvedTypeVariableReferenceType tvrType) { + Bindings emptyBindings = new Bindings(0); + if (upperBound != null) { + upperBound = upperBound.resolveBindings(scope, emptyBindings, false, false); + } + if (lowerBound != null) { + lowerBound = lowerBound.resolveBindings(scope, emptyBindings, false, false); + } + if (additionalInterfaceBounds != null) { + TypePattern[] resolvedIfBounds = new TypePattern[additionalInterfaceBounds.length]; + for (int i = 0; i < resolvedIfBounds.length; i++) { + resolvedIfBounds[i] = additionalInterfaceBounds[i].resolveBindings(scope, emptyBindings, false, false); + } + additionalInterfaceBounds = resolvedIfBounds; + } + if ( upperBound == null && lowerBound == null && additionalInterfaceBounds == null) { + // no bounds to worry about... + ResolvedType rType = tvrType.resolve(scope.getWorld()); + if (dim != 0) rType = ResolvedType.makeArray(rType, dim); + return new ExactTypePattern(rType,includeSubtypes,isVarArgs); + } else { + // we have to set bounds on the TypeVariable held by tvrType before resolving it + boolean canCreateExactTypePattern = true; + if (upperBound != null && upperBound.getExactType() == ResolvedType.MISSING) canCreateExactTypePattern = false; + if (lowerBound != null && lowerBound.getExactType() == ResolvedType.MISSING) canCreateExactTypePattern = false; + if (additionalInterfaceBounds != null) { + for (int i = 0; i < additionalInterfaceBounds.length; i++) { + if (additionalInterfaceBounds[i].getExactType() == ResolvedType.MISSING) canCreateExactTypePattern = false; + } + } + if (canCreateExactTypePattern) { + TypeVariable tv = tvrType.getTypeVariable(); + if (upperBound != null) tv.setUpperBound(upperBound.getExactType()); + if (lowerBound != null) tv.setLowerBound(lowerBound.getExactType()); + if (additionalInterfaceBounds != null) { + UnresolvedType[] ifBounds = new UnresolvedType[additionalInterfaceBounds.length]; + for (int i = 0; i < ifBounds.length; i++) { + ifBounds[i] = additionalInterfaceBounds[i].getExactType(); + } + tv.setAdditionalInterfaceBounds(ifBounds); + } + ResolvedType rType = tvrType.resolve(scope.getWorld()); + if (dim != 0) rType = ResolvedType.makeArray(rType, dim); + return new ExactTypePattern(rType,includeSubtypes,isVarArgs); + } + return this; // leave as wild type pattern then + } + } + + /** + * When this method is called, we have resolved the base type to an exact type. + * We also have a set of type patterns for the parameters. + * Time to perform some basic checks: + * - can the base type be parameterized? (is it generic) + * - can the type parameter pattern list match the number of parameters on the base type + * - do all parameter patterns meet the bounds of the respective type variables + * If any of these checks fail, a warning message is issued and we return false. + * @return + */ + private boolean verifyTypeParameters(ResolvedType baseType,IScope scope) { + ResolvedType genericType = baseType.getGenericType(); + if (genericType == null) { + // issue message "does not match because baseType.getName() is not generic" + scope.message(MessageUtil.warn( + WeaverMessages.format(WeaverMessages.NOT_A_GENERIC_TYPE,genericType.getName()), + getSourceLocation())); + return false; + } + int minRequiredTypeParameters = typeParameters.size(); + boolean foundEllipsis = false; + TypePattern[] typeParamPatterns = typeParameters.getTypePatterns(); + for (int i = 0; i < typeParamPatterns.length; i++) { + if (typeParamPatterns[i] instanceof WildTypePattern) { + WildTypePattern wtp = (WildTypePattern) typeParamPatterns[i]; + if (wtp.ellipsisCount > 0) { + foundEllipsis = true; + minRequiredTypeParameters--; + } + } + } + TypeVariable[] tvs = genericType.getTypeVariables(); + if ((tvs.length < minRequiredTypeParameters) || + (!foundEllipsis && minRequiredTypeParameters != tvs.length)) + { + // issue message "does not match because wrong no of type params" + scope.message(MessageUtil.warn( + WeaverMessages.format(WeaverMessages.INCORRECT_NUMBER_OF_TYPE_ARGUMENTS,genericType.getName(),new Integer(tvs.length)), + getSourceLocation())); + return false; + } + + // now check that each typeParameter pattern, if exact, matches the bounds + // of the type variable. + if (typeParameters.areAllExact()) { + for (int i = 0; i < tvs.length; i++) { + UnresolvedType ut = typeParamPatterns[i].getExactType(); + if (!tvs[i].canBeBoundTo(ut.resolve(scope.getWorld()))) { + // issue message that type parameter does not meet specification + String parameterName = ut.getName(); + if (ut.isTypeVariableReference()) parameterName = ((TypeVariableReference)ut).getTypeVariable().getDisplayName(); + scope.message(MessageUtil.warn( + WeaverMessages.format( + WeaverMessages.VIOLATES_TYPE_VARIABLE_BOUNDS, + parameterName, + new Integer(i+1), + tvs[i].getDisplayName(), + genericType.getName()), + getSourceLocation())); + return false; + } + } + } + return true; + } + public TypePattern resolveBindingsFromRTTI(boolean allowBinding, boolean requireExactType) { if (isStar()) { return TypePattern.ANY; //??? loses source location |