| /* ******************************************************************* |
| * 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 Eclipse Public License v1.0 |
| * which accompanies this distribution and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * PARC initial implementation |
| * ******************************************************************/ |
| |
| |
| package org.aspectj.ajdt.internal.compiler.ast; |
| |
| import java.lang.reflect.Modifier; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.aspectj.ajdt.internal.compiler.lookup.EclipseFactory; |
| import org.aspectj.ajdt.internal.compiler.lookup.EclipseTypeMunger; |
| import org.aspectj.ajdt.internal.compiler.lookup.InterTypeScope; |
| import org.aspectj.ajdt.internal.core.builder.EclipseSourceContext; |
| import org.aspectj.bridge.context.CompilationAndWeavingContext; |
| import org.aspectj.bridge.context.ContextToken; |
| import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ClassFile; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.CompilationResult; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Annotation; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeReference; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ClassScope; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.MethodScope; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.RawTypeBinding; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeBinding; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; |
| import org.aspectj.weaver.AjAttribute; |
| import org.aspectj.weaver.ResolvedMember; |
| import org.aspectj.weaver.ResolvedTypeMunger; |
| import org.aspectj.weaver.Shadow; |
| |
| /** |
| * Base type for all inter-type declarations including methods, fields and constructors. |
| * |
| * @author Jim Hugunin |
| */ |
| public abstract class InterTypeDeclaration extends AjMethodDeclaration { |
| protected TypeReference onType; |
| protected ReferenceBinding onTypeBinding; |
| protected ResolvedTypeMunger munger; |
| public int declaredModifiers; // so others can see (these differ from the modifiers in the superclass) |
| protected char[] declaredSelector; |
| |
| /** |
| * If targetting a generic type and wanting to use its type variables, an ITD can use an alternative name for |
| * them. This is a list of strings representing the alternative names - the position in the list is used to |
| * match it to the real type variable in the target generic type. |
| */ |
| protected List<String> typeVariableAliases; |
| |
| protected InterTypeScope interTypeScope; |
| |
| /** |
| * When set to true, the scope hierarchy for the field/method declaration has been correctly modified to |
| * include an intertypescope which resolves things relative to the targetted type. |
| */ |
| private boolean scopeSetup = false; |
| |
| // XXXAJ5 - When the compiler is changed, these will exist somewhere in it... |
| private final static short ACC_ANNOTATION = 0x2000; |
| private final static short ACC_ENUM = 0x4000; |
| |
| |
| public InterTypeDeclaration(CompilationResult result, TypeReference onType) { |
| super(result); |
| setOnType(onType); |
| modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccStatic; |
| } |
| |
| public void setOnType(TypeReference onType) { |
| this.onType = onType; |
| determineTypeVariableAliases(); |
| } |
| |
| public void setDeclaredModifiers(int modifiers) { |
| this.declaredModifiers = modifiers; |
| } |
| |
| public void setSelector(char[] selector) { |
| declaredSelector = selector; |
| this.selector = CharOperation.concat(selector, Integer.toHexString(sourceStart).toCharArray()); |
| this.selector = CharOperation.concat(getPrefix(),this.selector); |
| } |
| |
| // return the selector prefix for this itd that is to be used before resolution replaces it with a "proper" name |
| protected abstract char[] getPrefix(); |
| |
| |
| public void addAtAspectJAnnotations() { |
| if (munger == null) return; |
| Annotation ann = AtAspectJAnnotationFactory.createITDAnnotation( |
| munger.getSignature().getDeclaringType().getName().toCharArray(), |
| declaredModifiers,declaredSelector,declarationSourceStart); |
| AtAspectJAnnotationFactory.addAnnotation(this,ann,this.scope); |
| } |
| |
| /** |
| * Checks that the target for the ITD is not an annotation. If it is, an error message |
| * is signaled. We return true if it is annotation so the caller knows to stop processing. |
| * kind is 'constructor', 'field', 'method' |
| */ |
| public boolean isTargetAnnotation(ClassScope classScope,String kind) { |
| if ((onTypeBinding.getAccessFlags() & ACC_ANNOTATION)!=0) { |
| classScope.problemReporter().signalError(sourceStart,sourceEnd, |
| "can't make inter-type "+kind+" declarations on annotation types."); |
| ignoreFurtherInvestigation = true; |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Checks that the target for the ITD is not an enum. If it is, an error message |
| * is signaled. We return true if it is enum so the caller knows to stop processing. |
| */ |
| public boolean isTargetEnum(ClassScope classScope,String kind) { |
| if ((onTypeBinding.getAccessFlags() & ACC_ENUM)!=0) { |
| classScope.problemReporter().signalError(sourceStart,sourceEnd, |
| "can't make inter-type "+kind+" declarations on enum types."); |
| ignoreFurtherInvestigation = true; |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public void resolve(ClassScope upperScope) { |
| if (ignoreFurtherInvestigation) return; |
| |
| if (!scopeSetup) { |
| interTypeScope = new InterTypeScope(upperScope, onTypeBinding,typeVariableAliases); |
| scope.parent = interTypeScope; |
| this.scope.isStatic = Modifier.isStatic(declaredModifiers); |
| scopeSetup = true; |
| } |
| fixSuperCallsForInterfaceContext(upperScope); |
| if (ignoreFurtherInvestigation) return; |
| |
| super.resolve((ClassScope)scope.parent);//newParent); |
| fixSuperCallsInBody(); |
| } |
| |
| private void fixSuperCallsForInterfaceContext(ClassScope scope) { |
| if (onTypeBinding.isInterface()) { |
| ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.FIXING_SUPER_CALLS, selector); |
| InterSuperFixerVisitor v = |
| new InterSuperFixerVisitor(this, |
| EclipseFactory.fromScopeLookupEnvironment(scope), scope); |
| this.traverse(v, scope); |
| CompilationAndWeavingContext.leavingPhase(tok); |
| } |
| } |
| |
| /** |
| * Called from AspectDeclarations.buildInterTypeAndPerClause |
| */ |
| public abstract EclipseTypeMunger build(ClassScope classScope); |
| |
| public void fixSuperCallsInBody() { |
| ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.FIXING_SUPER_CALLS_IN_ITDS, selector); |
| SuperFixerVisitor v = new SuperFixerVisitor(this, onTypeBinding); |
| this.traverse(v, (ClassScope)null); |
| munger.setSuperMethodsCalled(v.superMethodsCalled); |
| CompilationAndWeavingContext.leavingPhase(tok); |
| } |
| |
| protected void resolveOnType(ClassScope classScope) { |
| checkSpec(); |
| |
| if (onType==null) return; // error reported elsewhere. |
| |
| // If they did supply a parameterized single type reference, we need to do |
| // some extra checks... |
| if (onType instanceof ParameterizedSingleTypeReference || onType instanceof ParameterizedQualifiedTypeReference) { |
| resolveTypeParametersForITDOnGenericType(classScope); |
| } else { |
| onTypeBinding = (ReferenceBinding)onType.getTypeBindingPublic(classScope); |
| if (!onTypeBinding.isValidBinding()) { |
| classScope.problemReporter().invalidType(onType, onTypeBinding); |
| ignoreFurtherInvestigation = true; |
| } |
| if (onTypeBinding.isParameterizedType()) { |
| // might be OK... pr132349 |
| ParameterizedTypeBinding ptb = (ParameterizedTypeBinding)onTypeBinding; |
| if (ptb.isNestedType()) { |
| if (ptb.typeVariables()==null || ptb.typeVariables().length==0) { |
| if (ptb.enclosingType().isRawType()) onTypeBinding = ptb.type; |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Transform the parameterized type binding (e.g. SomeType<A,B,C>) to a |
| * real type (e.g. SomeType). The only kind of parameterization allowed |
| * is with type variables and those are references to type variables on |
| * the target type. Once we have worked out the base generic type intended |
| * then we do lots of checks to verify the declaration was well formed. |
| */ |
| private void resolveTypeParametersForITDOnGenericType(ClassScope classScope) { |
| |
| // Collapse the parameterized reference to its generic type |
| if (onType instanceof ParameterizedSingleTypeReference) { |
| ParameterizedSingleTypeReference pref = (ParameterizedSingleTypeReference) onType; |
| long pos = (((long)pref.sourceStart) << 32) | pref.sourceEnd; |
| onType = new SingleTypeReference(pref.token,pos); |
| } else { |
| ParameterizedQualifiedTypeReference pref = (ParameterizedQualifiedTypeReference) onType; |
| long pos = (((long)pref.sourceStart) << 32) | pref.sourceEnd; |
| onType = new QualifiedTypeReference(pref.tokens,new long[]{pos}); |
| |
| } |
| |
| onTypeBinding = (ReferenceBinding)onType.getTypeBindingPublic(classScope); |
| if (!onTypeBinding.isValidBinding()) { |
| classScope.problemReporter().invalidType(onType, onTypeBinding); |
| ignoreFurtherInvestigation = true; |
| } |
| |
| |
| if (onTypeBinding.isRawType()) { |
| onTypeBinding = ((RawTypeBinding)onTypeBinding).type; |
| } |
| |
| int aliasCount = (typeVariableAliases==null?0:typeVariableAliases.size()); |
| |
| // Cannot specify a parameterized target type for the ITD if the target |
| // type is not generic. |
| if (aliasCount!=0 && !onTypeBinding.isGenericType()) { |
| scope.problemReporter().signalError(sourceStart,sourceEnd, |
| "Type parameters can not be specified in the ITD target type - the target type "+onTypeBinding.debugName()+" is not generic."); |
| ignoreFurtherInvestigation = true; |
| return; |
| } |
| |
| // Check they have supplied the right number of type parameters on the ITD target type |
| if (aliasCount>0) { |
| if (onTypeBinding.typeVariables().length != aliasCount) { // typeParameters.length) { phantom contains the fake ones from the ontype, typeparameters will also include extra things if it is a generic method |
| scope.problemReporter().signalError(sourceStart, sourceEnd, |
| "Incorrect number of type parameters supplied. The generic type "+onTypeBinding.debugName()+" has "+ |
| onTypeBinding.typeVariables().length+" type parameters, not "+aliasCount+"."); |
| ignoreFurtherInvestigation = true; |
| return; |
| } |
| } |
| |
| // check if they used stupid names for type variables |
| if (aliasCount>0) { |
| for (int i = 0; i < aliasCount; i++) { |
| String array_element = typeVariableAliases.get(i); |
| SingleTypeReference str = new SingleTypeReference(array_element.toCharArray(),0); |
| TypeBinding tb = str.getTypeBindingPublic(classScope); |
| if (tb!=null && !(tb instanceof ProblemReferenceBinding)) {// && !(tb instanceof TypeVariableBinding)) { |
| scope.problemReporter().signalError(sourceStart,sourceEnd, |
| "Intertype declarations can only be made on the generic type, not on a parameterized type. The name '"+ |
| array_element+"' cannot be used as a type parameter, since it refers to a real type."); |
| ignoreFurtherInvestigation = true; |
| return; |
| |
| } |
| } |
| } |
| // TypeVariableBinding[] tVarsInGenericType = onTypeBinding.typeVariables(); |
| // typeVariableAliases = new ArrayList(); /* Name>GenericTypeVariablePosition */ // FIXME ASC DONT THINK WE NEED TO BUILD IT HERE AS WELL... |
| // TypeReference[] targs = pref.typeArguments; |
| // if (targs!=null) { |
| // for (int i = 0; i < targs.length; i++) { |
| // TypeReference tref = targs[i]; |
| // typeVariableAliases.add(CharOperation.toString(tref.getTypeName()));//tVarsInGenericType[i]); |
| // } |
| // } |
| } |
| |
| |
| protected void checkSpec() { |
| if (Modifier.isProtected(declaredModifiers)) { |
| scope.problemReporter().signalError(sourceStart, sourceEnd, |
| "protected inter-type declarations are not allowed"); |
| ignoreFurtherInvestigation = true; |
| } |
| } |
| |
| protected List makeEffectiveSignatureAttribute( |
| ResolvedMember sig, |
| Shadow.Kind kind, |
| boolean weaveBody) |
| { |
| List l = new ArrayList(1); |
| l.add(new EclipseAttributeAdapter( |
| new AjAttribute.EffectiveSignatureAttribute(sig, kind, weaveBody))); |
| return l; |
| } |
| |
| protected void setMunger(ResolvedTypeMunger munger) { |
| munger.getSignature().setPosition(sourceStart, sourceEnd); |
| munger.getSignature().setSourceContext(new EclipseSourceContext(compilationResult)); |
| this.munger = munger; |
| } |
| |
| @Override |
| protected int generateInfoAttributes(ClassFile classFile) { |
| List l; |
| Shadow.Kind kind = getShadowKindForBody(); |
| if (kind != null) { |
| l = makeEffectiveSignatureAttribute(munger.getSignature(), kind, true); |
| } else { |
| l = new ArrayList(0); |
| } |
| addDeclarationStartLineAttribute(l,classFile); |
| |
| return classFile.generateMethodInfoAttributes(binding, l); |
| } |
| |
| protected abstract Shadow.Kind getShadowKindForBody(); |
| |
| public ResolvedMember getSignature() { |
| if (munger==null) return null; // Can be null in an erroneous program I think |
| return munger.getSignature(); |
| } |
| |
| public char[] getDeclaredSelector() { |
| return declaredSelector; |
| } |
| |
| public TypeReference getOnType() { |
| return onType; |
| } |
| |
| |
| /** |
| * Create the list of aliases based on what was supplied as parameters for the ontype. |
| * For example, if the declaration is 'List<N> SomeType<N>.foo' then the alias list |
| * will simply contain 'N' and 'N' will mean 'the first type variable declared for |
| * type SomeType' |
| */ |
| public void determineTypeVariableAliases() { |
| if (onType!=null) { |
| // TODO loses distinction about which level the type variables are at... is that a problem? |
| if (onType instanceof ParameterizedSingleTypeReference) { |
| ParameterizedSingleTypeReference paramRef = (ParameterizedSingleTypeReference) onType; |
| TypeReference[] rb = paramRef.typeArguments; |
| typeVariableAliases = new ArrayList(); |
| for (int i = 0; i < rb.length; i++) { |
| typeVariableAliases.add(CharOperation.toString(rb[i].getTypeName())); |
| } |
| } else if (onType instanceof ParameterizedQualifiedTypeReference) { |
| ParameterizedQualifiedTypeReference paramRef = (ParameterizedQualifiedTypeReference) onType; |
| typeVariableAliases = new ArrayList(); |
| for (int j = 0; j < paramRef.typeArguments.length; j++) { |
| TypeReference[] rb = paramRef.typeArguments[j]; |
| for (int i = 0; rb!=null && i < rb.length; i++) { |
| typeVariableAliases.add(CharOperation.toString(rb[i].getTypeName())); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Called just before the compiler is going to start resolving elements of a declaration, this method |
| * adds an intertypescope between the methodscope and classscope so that elements of the type targetted |
| * by the ITD can be resolved. For example, if type variables are referred to in the ontype for the ITD, |
| * they have to be resolved against the ontype, not the aspect containing the ITD. |
| */ |
| @Override |
| public void ensureScopeSetup() { |
| if (scopeSetup) return; // don't do it again |
| MethodScope scope = this.scope; |
| |
| TypeReference ot = onType; |
| ReferenceBinding rb = null; |
| |
| if (ot instanceof ParameterizedQualifiedTypeReference) { // pr132349 |
| ParameterizedQualifiedTypeReference pref = (ParameterizedQualifiedTypeReference) ot; |
| if (pref.typeArguments!=null && pref.typeArguments.length!=0) { |
| boolean usingNonTypeVariableInITD = false; |
| // Check if any of them are not type variables |
| for (int i = 0; i < pref.typeArguments.length; i++) { |
| TypeReference[] refs = pref.typeArguments[i]; |
| for (int j = 0; refs!=null && j < refs.length; j++) { |
| TypeBinding tb = refs[j].getTypeBindingPublic(scope.parent); |
| if (!tb.isTypeVariable() && !(tb instanceof ProblemReferenceBinding)) { |
| usingNonTypeVariableInITD = true; |
| } |
| |
| } |
| } |
| if (usingNonTypeVariableInITD) { |
| scope.problemReporter().signalError(sourceStart,sourceEnd, |
| "Cannot make inter-type declarations on parameterized types"); |
| // to prevent disgusting cascading errors after this problem - lets null out what leads to them (pr105038) |
| this.arguments=null; |
| this.returnType=new SingleTypeReference(TypeReference.VOID,0L); |
| |
| this.ignoreFurtherInvestigation=true; |
| ReferenceBinding closestMatch = null; |
| rb = new ProblemReferenceBinding(ot.getParameterizedTypeName(),closestMatch,0); |
| onType=null; |
| } |
| } |
| |
| } |
| |
| // Work out the real base type |
| if (ot instanceof ParameterizedSingleTypeReference) { |
| ParameterizedSingleTypeReference pref = (ParameterizedSingleTypeReference) ot; |
| long pos = (((long)pref.sourceStart) << 32) | pref.sourceEnd; |
| ot = new SingleTypeReference(pref.token,pos); |
| } else if (ot instanceof ParameterizedQualifiedTypeReference) { |
| ParameterizedQualifiedTypeReference pref = (ParameterizedQualifiedTypeReference) ot; |
| long pos = (((long)pref.sourceStart) << 32) | pref.sourceEnd; |
| ot = new QualifiedTypeReference(pref.tokens,new long[]{pos});//SingleTypeReference(pref.Quatoken,pos); |
| } |
| |
| // resolve it |
| if (rb==null) { |
| rb = (ReferenceBinding)ot.getTypeBindingPublic(scope.parent); |
| } |
| |
| // pr203646 - if we have ended up with the raw type, get back to the underlying generic one. |
| if (rb.isRawType() && rb.isMemberType()) { |
| // if the real target type used a type variable alias then we can do this OK, but need to switch things around, we want the generic type |
| rb = ((RawTypeBinding)rb).type; |
| } |
| |
| if (rb instanceof TypeVariableBinding) { |
| scope.problemReporter().signalError(sourceStart,sourceEnd, |
| "Cannot make inter-type declarations on type variables, use an interface and declare parents"); |
| // to prevent disgusting cascading errors after this problem - lets null out what leads to them (pr105038) |
| this.arguments=null; |
| this.returnType=new SingleTypeReference(TypeReference.VOID,0L); |
| |
| this.ignoreFurtherInvestigation=true; |
| ReferenceBinding closestMatch = null; |
| if (((TypeVariableBinding)rb).firstBound!=null) { |
| closestMatch = ((TypeVariableBinding)rb).firstBound.enclosingType(); |
| } |
| rb = new ProblemReferenceBinding(rb.compoundName,closestMatch,0); |
| } |
| |
| |
| // if resolution failed, give up - someone else is going to report an error |
| if (rb instanceof ProblemReferenceBinding) return; |
| |
| interTypeScope = new InterTypeScope(scope.parent, rb, typeVariableAliases); |
| // FIXME asc verify the choice of lines here... |
| // Two versions of this next line. |
| // First one tricks the JDT variable processing code so that it won't complain if |
| // you refer to a type variable from a static ITD - it *is* a problem and it *will* be caught, but later and |
| // by the AJDT code so we can put out a much nicer message. |
| scope.isStatic = (typeVariableAliases!=null?false:Modifier.isStatic(declaredModifiers)); |
| // this is the original version in case tricking the JDT causes grief (if you reinstate this variant, you |
| // will need to change the expected messages output for some of the generic ITD tests) |
| // scope.isStatic = Modifier.isStatic(declaredModifiers); |
| scope.parent = interTypeScope; |
| scopeSetup = true; |
| } |
| } |