1 /* *******************************************************************
2 * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
4 * This program and the accompanying materials are made available
5 * under the terms of the Eclipse Public License v 2.0
6 * which accompanies this distribution and is available at
7 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
10 * PARC initial implementation
11 * ******************************************************************/
14 package org.aspectj.ajdt.internal.compiler.ast;
16 import java.lang.reflect.Modifier;
17 import java.util.ArrayList;
18 import java.util.List;
20 import org.aspectj.ajdt.internal.compiler.lookup.EclipseFactory;
21 import org.aspectj.ajdt.internal.compiler.lookup.EclipseTypeMunger;
22 import org.aspectj.ajdt.internal.compiler.lookup.InterTypeScope;
23 import org.aspectj.ajdt.internal.core.builder.EclipseSourceContext;
24 import org.aspectj.bridge.context.CompilationAndWeavingContext;
25 import org.aspectj.bridge.context.ContextToken;
26 import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
27 import org.aspectj.org.eclipse.jdt.internal.compiler.ClassFile;
28 import org.aspectj.org.eclipse.jdt.internal.compiler.CompilationResult;
29 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Annotation;
30 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference;
31 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
32 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
33 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
34 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeReference;
35 import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
36 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ClassScope;
37 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.MethodScope;
38 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
39 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding;
40 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.RawTypeBinding;
41 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
42 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
43 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
44 import org.aspectj.weaver.AjAttribute;
45 import org.aspectj.weaver.ResolvedMember;
46 import org.aspectj.weaver.ResolvedTypeMunger;
47 import org.aspectj.weaver.Shadow;
50 * Base type for all inter-type declarations including methods, fields and constructors.
54 public abstract class InterTypeDeclaration extends AjMethodDeclaration {
55 protected TypeReference onType;
56 protected ReferenceBinding onTypeBinding;
57 protected ResolvedTypeMunger munger;
58 public int declaredModifiers; // so others can see (these differ from the modifiers in the superclass)
59 protected char[] declaredSelector;
62 * If targetting a generic type and wanting to use its type variables, an ITD can use an alternative name for
63 * them. This is a list of strings representing the alternative names - the position in the list is used to
64 * match it to the real type variable in the target generic type.
66 protected List<String> typeVariableAliases;
68 protected InterTypeScope interTypeScope;
71 * When set to true, the scope hierarchy for the field/method declaration has been correctly modified to
72 * include an intertypescope which resolves things relative to the targetted type.
74 private boolean scopeSetup = false;
76 // XXXAJ5 - When the compiler is changed, these will exist somewhere in it...
77 private final static short ACC_ANNOTATION = 0x2000;
78 private final static short ACC_ENUM = 0x4000;
81 public InterTypeDeclaration(CompilationResult result, TypeReference onType) {
84 modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccStatic;
87 public void setOnType(TypeReference onType) {
89 determineTypeVariableAliases();
92 public void setDeclaredModifiers(int modifiers) {
93 this.declaredModifiers = modifiers;
96 public void setSelector(char[] selector) {
97 declaredSelector = selector;
98 this.selector = CharOperation.concat(selector, Integer.toHexString(sourceStart).toCharArray());
99 this.selector = CharOperation.concat(getPrefix(),this.selector);
102 // return the selector prefix for this itd that is to be used before resolution replaces it with a "proper" name
103 protected abstract char[] getPrefix();
106 public void addAtAspectJAnnotations() {
107 if (munger == null) return;
108 Annotation ann = AtAspectJAnnotationFactory.createITDAnnotation(
109 munger.getSignature().getDeclaringType().getName().toCharArray(),
110 declaredModifiers,declaredSelector,declarationSourceStart);
111 AtAspectJAnnotationFactory.addAnnotation(this,ann,this.scope);
115 * Checks that the target for the ITD is not an annotation. If it is, an error message
116 * is signaled. We return true if it is annotation so the caller knows to stop processing.
117 * kind is 'constructor', 'field', 'method'
119 public boolean isTargetAnnotation(ClassScope classScope,String kind) {
120 if ((onTypeBinding.getAccessFlags() & ACC_ANNOTATION)!=0) {
121 classScope.problemReporter().signalError(sourceStart,sourceEnd,
122 "can't make inter-type "+kind+" declarations on annotation types.");
123 ignoreFurtherInvestigation = true;
130 * Checks that the target for the ITD is not an enum. If it is, an error message
131 * is signaled. We return true if it is enum so the caller knows to stop processing.
133 public boolean isTargetEnum(ClassScope classScope,String kind) {
134 if ((onTypeBinding.getAccessFlags() & ACC_ENUM)!=0) {
135 classScope.problemReporter().signalError(sourceStart,sourceEnd,
136 "can't make inter-type "+kind+" declarations on enum types.");
137 ignoreFurtherInvestigation = true;
144 public void resolve(ClassScope upperScope) {
145 if (ignoreFurtherInvestigation) return;
148 interTypeScope = new InterTypeScope(upperScope, onTypeBinding,typeVariableAliases);
149 // Use setter in order to also update member 'compilationUnitScope'
150 scope.setParent(interTypeScope);
151 this.scope.isStatic = Modifier.isStatic(declaredModifiers);
154 fixSuperCallsForInterfaceContext(upperScope);
155 if (ignoreFurtherInvestigation) return;
157 super.resolve((ClassScope)scope.parent);//newParent);
158 fixSuperCallsInBody();
161 private void fixSuperCallsForInterfaceContext(ClassScope scope) {
162 if (onTypeBinding.isInterface()) {
163 ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.FIXING_SUPER_CALLS, selector);
164 InterSuperFixerVisitor v =
165 new InterSuperFixerVisitor(this,
166 EclipseFactory.fromScopeLookupEnvironment(scope), scope);
167 this.traverse(v, scope);
168 CompilationAndWeavingContext.leavingPhase(tok);
173 * Called from AspectDeclarations.buildInterTypeAndPerClause
175 public abstract EclipseTypeMunger build(ClassScope classScope);
177 public void fixSuperCallsInBody() {
178 ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.FIXING_SUPER_CALLS_IN_ITDS, selector);
179 SuperFixerVisitor v = new SuperFixerVisitor(this, onTypeBinding);
180 this.traverse(v, (ClassScope)null);
181 munger.setSuperMethodsCalled(v.superMethodsCalled);
182 CompilationAndWeavingContext.leavingPhase(tok);
185 protected void resolveOnType(ClassScope classScope) {
188 if (onType==null) return; // error reported elsewhere.
190 // If they did supply a parameterized single type reference, we need to do
191 // some extra checks...
192 if (onType instanceof ParameterizedSingleTypeReference || onType instanceof ParameterizedQualifiedTypeReference) {
193 resolveTypeParametersForITDOnGenericType(classScope);
195 onTypeBinding = (ReferenceBinding)onType.getTypeBindingPublic(classScope);
196 if (!onTypeBinding.isValidBinding()) {
197 classScope.problemReporter().invalidType(onType, onTypeBinding);
198 ignoreFurtherInvestigation = true;
200 if (onTypeBinding.isParameterizedType()) {
201 // might be OK... pr132349
202 ParameterizedTypeBinding ptb = (ParameterizedTypeBinding)onTypeBinding;
203 if (ptb.isNestedType()) {
204 if (ptb.typeVariables()==null || ptb.typeVariables().length==0) {
205 if (ptb.enclosingType().isRawType()) onTypeBinding = ptb.type;
213 * Transform the parameterized type binding (e.g. SomeType<A,B,C>) to a
214 * real type (e.g. SomeType). The only kind of parameterization allowed
215 * is with type variables and those are references to type variables on
216 * the target type. Once we have worked out the base generic type intended
217 * then we do lots of checks to verify the declaration was well formed.
219 private void resolveTypeParametersForITDOnGenericType(ClassScope classScope) {
221 // Collapse the parameterized reference to its generic type
222 if (onType instanceof ParameterizedSingleTypeReference) {
223 ParameterizedSingleTypeReference pref = (ParameterizedSingleTypeReference) onType;
224 long pos = (((long)pref.sourceStart) << 32) | pref.sourceEnd;
225 onType = new SingleTypeReference(pref.token,pos);
227 ParameterizedQualifiedTypeReference pref = (ParameterizedQualifiedTypeReference) onType;
228 long pos = (((long)pref.sourceStart) << 32) | pref.sourceEnd;
229 onType = new QualifiedTypeReference(pref.tokens,new long[]{pos});
233 onTypeBinding = (ReferenceBinding)onType.getTypeBindingPublic(classScope);
234 if (!onTypeBinding.isValidBinding()) {
235 classScope.problemReporter().invalidType(onType, onTypeBinding);
236 ignoreFurtherInvestigation = true;
240 if (onTypeBinding.isRawType()) {
241 onTypeBinding = ((RawTypeBinding)onTypeBinding).type;
244 int aliasCount = (typeVariableAliases==null?0:typeVariableAliases.size());
246 // Cannot specify a parameterized target type for the ITD if the target
247 // type is not generic.
248 if (aliasCount!=0 && !onTypeBinding.isGenericType()) {
249 scope.problemReporter().signalError(sourceStart,sourceEnd,
250 "Type parameters can not be specified in the ITD target type - the target type "+onTypeBinding.debugName()+" is not generic.");
251 ignoreFurtherInvestigation = true;
255 // Check they have supplied the right number of type parameters on the ITD target type
257 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
258 scope.problemReporter().signalError(sourceStart, sourceEnd,
259 "Incorrect number of type parameters supplied. The generic type "+onTypeBinding.debugName()+" has "+
260 onTypeBinding.typeVariables().length+" type parameters, not "+aliasCount+".");
261 ignoreFurtherInvestigation = true;
266 // check if they used stupid names for type variables
268 for (int i = 0; i < aliasCount; i++) {
269 String array_element = typeVariableAliases.get(i);
270 SingleTypeReference str = new SingleTypeReference(array_element.toCharArray(),0);
271 TypeBinding tb = str.getTypeBindingPublic(classScope);
272 if (tb!=null && !(tb instanceof ProblemReferenceBinding)) {// && !(tb instanceof TypeVariableBinding)) {
273 scope.problemReporter().signalError(sourceStart,sourceEnd,
274 "Intertype declarations can only be made on the generic type, not on a parameterized type. The name '"+
275 array_element+"' cannot be used as a type parameter, since it refers to a real type.");
276 ignoreFurtherInvestigation = true;
282 // TypeVariableBinding[] tVarsInGenericType = onTypeBinding.typeVariables();
283 // typeVariableAliases = new ArrayList(); /* Name>GenericTypeVariablePosition */ // FIXME ASC DONT THINK WE NEED TO BUILD IT HERE AS WELL...
284 // TypeReference[] targs = pref.typeArguments;
285 // if (targs!=null) {
286 // for (int i = 0; i < targs.length; i++) {
287 // TypeReference tref = targs[i];
288 // typeVariableAliases.add(CharOperation.toString(tref.getTypeName()));//tVarsInGenericType[i]);
294 protected void checkSpec() {
295 if (Modifier.isProtected(declaredModifiers)) {
296 scope.problemReporter().signalError(sourceStart, sourceEnd,
297 "protected inter-type declarations are not allowed");
298 ignoreFurtherInvestigation = true;
302 protected List makeEffectiveSignatureAttribute(
307 List l = new ArrayList(1);
308 l.add(new EclipseAttributeAdapter(
309 new AjAttribute.EffectiveSignatureAttribute(sig, kind, weaveBody)));
313 protected void setMunger(ResolvedTypeMunger munger) {
314 munger.getSignature().setPosition(sourceStart, sourceEnd);
315 munger.getSignature().setSourceContext(new EclipseSourceContext(compilationResult));
316 this.munger = munger;
320 protected int generateInfoAttributes(ClassFile classFile) {
322 Shadow.Kind kind = getShadowKindForBody();
324 l = makeEffectiveSignatureAttribute(munger.getSignature(), kind, true);
326 l = new ArrayList(0);
328 addDeclarationStartLineAttribute(l,classFile);
330 return classFile.generateMethodInfoAttributes(binding, l);
333 protected abstract Shadow.Kind getShadowKindForBody();
335 public ResolvedMember getSignature() {
336 if (munger==null) return null; // Can be null in an erroneous program I think
337 return munger.getSignature();
340 public char[] getDeclaredSelector() {
341 return declaredSelector;
344 public TypeReference getOnType() {
350 * Create the list of aliases based on what was supplied as parameters for the ontype.
351 * For example, if the declaration is 'List<N> SomeType<N>.foo' then the alias list
352 * will simply contain 'N' and 'N' will mean 'the first type variable declared for
355 public void determineTypeVariableAliases() {
357 // TODO loses distinction about which level the type variables are at... is that a problem?
358 if (onType instanceof ParameterizedSingleTypeReference) {
359 ParameterizedSingleTypeReference paramRef = (ParameterizedSingleTypeReference) onType;
360 TypeReference[] rb = paramRef.typeArguments;
361 typeVariableAliases = new ArrayList();
362 for (TypeReference typeReference : rb) {
363 typeVariableAliases.add(CharOperation.toString(typeReference.getTypeName()));
365 } else if (onType instanceof ParameterizedQualifiedTypeReference) {
366 ParameterizedQualifiedTypeReference paramRef = (ParameterizedQualifiedTypeReference) onType;
367 typeVariableAliases = new ArrayList();
368 for (int j = 0; j < paramRef.typeArguments.length; j++) {
369 TypeReference[] rb = paramRef.typeArguments[j];
370 for (int i = 0; rb!=null && i < rb.length; i++) {
371 typeVariableAliases.add(CharOperation.toString(rb[i].getTypeName()));
379 * Called just before the compiler is going to start resolving elements of a declaration, this method
380 * adds an intertypescope between the methodscope and classscope so that elements of the type targetted
381 * by the ITD can be resolved. For example, if type variables are referred to in the ontype for the ITD,
382 * they have to be resolved against the ontype, not the aspect containing the ITD.
385 public void ensureScopeSetup() {
386 if (scopeSetup) return; // don't do it again
387 MethodScope scope = this.scope;
389 TypeReference ot = onType;
390 ReferenceBinding rb = null;
392 if (ot instanceof ParameterizedQualifiedTypeReference) { // pr132349
393 ParameterizedQualifiedTypeReference pref = (ParameterizedQualifiedTypeReference) ot;
394 if (pref.typeArguments!=null && pref.typeArguments.length!=0) {
395 boolean usingNonTypeVariableInITD = false;
396 // Check if any of them are not type variables
397 for (int i = 0; i < pref.typeArguments.length; i++) {
398 TypeReference[] refs = pref.typeArguments[i];
399 for (int j = 0; refs!=null && j < refs.length; j++) {
400 TypeBinding tb = refs[j].getTypeBindingPublic(scope.parent);
401 if (!tb.isTypeVariable() && !(tb instanceof ProblemReferenceBinding)) {
402 usingNonTypeVariableInITD = true;
407 if (usingNonTypeVariableInITD) {
408 scope.problemReporter().signalError(sourceStart,sourceEnd,
409 "Cannot make inter-type declarations on parameterized types");
410 // to prevent disgusting cascading errors after this problem - lets null out what leads to them (pr105038)
412 this.returnType=new SingleTypeReference(TypeReference.VOID,0L);
414 this.ignoreFurtherInvestigation=true;
415 ReferenceBinding closestMatch = null;
416 rb = new ProblemReferenceBinding(ot.getParameterizedTypeName(),closestMatch,0);
423 // Work out the real base type
424 if (ot instanceof ParameterizedSingleTypeReference) {
425 ParameterizedSingleTypeReference pref = (ParameterizedSingleTypeReference) ot;
426 long pos = (((long)pref.sourceStart) << 32) | pref.sourceEnd;
427 ot = new SingleTypeReference(pref.token,pos);
428 } else if (ot instanceof ParameterizedQualifiedTypeReference) {
429 ParameterizedQualifiedTypeReference pref = (ParameterizedQualifiedTypeReference) ot;
430 long pos = (((long)pref.sourceStart) << 32) | pref.sourceEnd;
431 ot = new QualifiedTypeReference(pref.tokens,new long[]{pos});//SingleTypeReference(pref.Quatoken,pos);
436 rb = (ReferenceBinding)ot.getTypeBindingPublic(scope.parent);
439 // pr203646 - if we have ended up with the raw type, get back to the underlying generic one.
440 if (rb.isRawType() && rb.isMemberType()) {
441 // 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
442 rb = ((RawTypeBinding)rb).type;
445 if (rb instanceof TypeVariableBinding) {
446 scope.problemReporter().signalError(sourceStart,sourceEnd,
447 "Cannot make inter-type declarations on type variables, use an interface and declare parents");
448 // to prevent disgusting cascading errors after this problem - lets null out what leads to them (pr105038)
450 this.returnType=new SingleTypeReference(TypeReference.VOID,0L);
452 this.ignoreFurtherInvestigation=true;
453 ReferenceBinding closestMatch = null;
454 if (((TypeVariableBinding)rb).firstBound!=null) {
455 closestMatch = ((TypeVariableBinding)rb).firstBound.enclosingType();
457 rb = new ProblemReferenceBinding(rb.compoundName,closestMatch,0);
461 // if resolution failed, give up - someone else is going to report an error
462 if (rb instanceof ProblemReferenceBinding) return;
464 interTypeScope = new InterTypeScope(scope.parent, rb, typeVariableAliases);
465 // FIXME asc verify the choice of lines here...
466 // Two versions of this next line.
467 // First one tricks the JDT variable processing code so that it won't complain if
468 // you refer to a type variable from a static ITD - it *is* a problem and it *will* be caught, but later and
469 // by the AJDT code so we can put out a much nicer message.
470 scope.isStatic = (typeVariableAliases!=null?false:Modifier.isStatic(declaredModifiers));
471 // this is the original version in case tricking the JDT causes grief (if you reinstate this variant, you
472 // will need to change the expected messages output for some of the generic ITD tests)
473 // scope.isStatic = Modifier.isStatic(declaredModifiers);
474 scope.parent = interTypeScope;