]> source.dussan.org Git - aspectj.git/blob
89d2482c7fef9b444270eddb11dad84d081691aa
[aspectj.git] /
1 /* *******************************************************************
2  * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
3  * All rights reserved. 
4  * This program and the accompanying materials are made available 
5  * under the terms of the Eclipse Public License v1.0 
6  * which accompanies this distribution and is available at 
7  * http://www.eclipse.org/legal/epl-v10.html 
8  *  
9  * Contributors: 
10  *     PARC     initial implementation 
11  * ******************************************************************/
12
13
14 package org.aspectj.ajdt.internal.compiler.ast;
15
16 import java.lang.reflect.Modifier;
17 import java.util.ArrayList;
18 import java.util.List;
19
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;
48
49 /**
50  * Base type for all inter-type declarations including methods, fields and constructors.
51  *
52  * @author Jim Hugunin
53  */
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;
60         
61         /** 
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.
65          */
66         protected List<String> typeVariableAliases; 
67         
68         protected InterTypeScope interTypeScope;
69         
70         /**
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.
73          */
74     private boolean scopeSetup = false;
75         
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;
79
80
81         public InterTypeDeclaration(CompilationResult result, TypeReference onType) {
82                 super(result);
83                 setOnType(onType);
84                 modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccStatic;
85         }
86
87         public void setOnType(TypeReference onType) {
88                 this.onType = onType;
89                 determineTypeVariableAliases();
90         }
91         
92         public void setDeclaredModifiers(int modifiers) {
93                 this.declaredModifiers = modifiers;
94         }
95         
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);
100         }
101         
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();
104         
105         
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);
112         }
113
114         /**
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'
118          */
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;
124                         return true;
125                 }
126                 return false;
127         }
128         
129         /**
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.
132          */
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;
138                         return true;
139                 }
140                 return false;
141         }
142         
143         @Override
144         public void resolve(ClassScope upperScope) {
145                 if (ignoreFurtherInvestigation) return;
146                 
147                 if (!scopeSetup) {
148                   interTypeScope = new InterTypeScope(upperScope, onTypeBinding,typeVariableAliases);
149                   scope.parent = interTypeScope;
150                   this.scope.isStatic = Modifier.isStatic(declaredModifiers);
151                   scopeSetup = true;
152                 }
153                 fixSuperCallsForInterfaceContext(upperScope);
154                 if (ignoreFurtherInvestigation) return;
155                 
156                 super.resolve((ClassScope)scope.parent);//newParent);
157                 fixSuperCallsInBody();
158         }
159
160         private void fixSuperCallsForInterfaceContext(ClassScope scope) {
161                 if (onTypeBinding.isInterface()) {
162                         ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.FIXING_SUPER_CALLS, selector);
163                         InterSuperFixerVisitor v =
164                                 new InterSuperFixerVisitor(this, 
165                                                 EclipseFactory.fromScopeLookupEnvironment(scope), scope);
166                         this.traverse(v, scope);
167                         CompilationAndWeavingContext.leavingPhase(tok);
168                 }
169         }
170
171         /**
172          * Called from AspectDeclarations.buildInterTypeAndPerClause
173          */
174         public abstract EclipseTypeMunger build(ClassScope classScope);
175
176         public void fixSuperCallsInBody() {
177                 ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.FIXING_SUPER_CALLS_IN_ITDS, selector);
178                 SuperFixerVisitor v = new SuperFixerVisitor(this, onTypeBinding);
179                 this.traverse(v, (ClassScope)null);
180                 munger.setSuperMethodsCalled(v.superMethodsCalled);
181                 CompilationAndWeavingContext.leavingPhase(tok);
182         }
183
184         protected void resolveOnType(ClassScope classScope) {
185                 checkSpec();
186                 
187                 if (onType==null) return; // error reported elsewhere.
188                 
189                 // If they did supply a parameterized single type reference, we need to do
190                 // some extra checks...
191                 if (onType instanceof ParameterizedSingleTypeReference  || onType instanceof ParameterizedQualifiedTypeReference) {
192                         resolveTypeParametersForITDOnGenericType(classScope);
193                 } else {
194                         onTypeBinding = (ReferenceBinding)onType.getTypeBindingPublic(classScope);
195                         if (!onTypeBinding.isValidBinding()) {
196                                 classScope.problemReporter().invalidType(onType, onTypeBinding);
197                                 ignoreFurtherInvestigation = true;
198                         }
199                         if (onTypeBinding.isParameterizedType()) {
200                                 // might be OK... pr132349
201                                 ParameterizedTypeBinding ptb = (ParameterizedTypeBinding)onTypeBinding;
202                                 if (ptb.isNestedType()) {
203                                         if (ptb.typeVariables()==null || ptb.typeVariables().length==0) {
204                                                 if (ptb.enclosingType().isRawType()) onTypeBinding = ptb.type;
205                                         }
206                                 }
207                         }
208                 }
209         }
210
211     /**
212      * Transform the parameterized type binding (e.g. SomeType<A,B,C>) to a 
213      * real type (e.g. SomeType).  The only kind of parameterization allowed
214      * is with type variables and those are references to type variables on
215      * the target type.  Once we have worked out the base generic type intended
216      * then we do lots of checks to verify the declaration was well formed.
217      */
218         private void resolveTypeParametersForITDOnGenericType(ClassScope classScope) {
219
220                 // Collapse the parameterized reference to its generic type
221                 if (onType instanceof ParameterizedSingleTypeReference) {
222                         ParameterizedSingleTypeReference pref = (ParameterizedSingleTypeReference) onType;
223                         long pos = (((long)pref.sourceStart) << 32) | pref.sourceEnd;
224                         onType = new SingleTypeReference(pref.token,pos);
225                 } else {
226                         ParameterizedQualifiedTypeReference pref = (ParameterizedQualifiedTypeReference) onType;
227                         long pos = (((long)pref.sourceStart) << 32) | pref.sourceEnd;
228                         onType = new QualifiedTypeReference(pref.tokens,new long[]{pos});
229                         
230                 }
231                 
232                 onTypeBinding = (ReferenceBinding)onType.getTypeBindingPublic(classScope);              
233                 if (!onTypeBinding.isValidBinding()) {
234                         classScope.problemReporter().invalidType(onType, onTypeBinding);
235                         ignoreFurtherInvestigation = true;
236                 }
237
238                 
239                 if (onTypeBinding.isRawType()) {
240                         onTypeBinding = ((RawTypeBinding)onTypeBinding).type;
241                 }
242                 
243                 int aliasCount = (typeVariableAliases==null?0:typeVariableAliases.size());
244                 
245                 // Cannot specify a parameterized target type for the ITD if the target
246                 // type is not generic.
247                 if (aliasCount!=0 && !onTypeBinding.isGenericType()) {
248                         scope.problemReporter().signalError(sourceStart,sourceEnd,
249                                         "Type parameters can not be specified in the ITD target type - the target type "+onTypeBinding.debugName()+" is not generic.");
250                         ignoreFurtherInvestigation = true;
251                         return;
252                 }
253                 
254                 // Check they have supplied the right number of type parameters on the ITD target type
255                 if (aliasCount>0) {
256                         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
257                                 scope.problemReporter().signalError(sourceStart, sourceEnd,
258                                         "Incorrect number of type parameters supplied.  The generic type "+onTypeBinding.debugName()+" has "+
259                                         onTypeBinding.typeVariables().length+" type parameters, not "+aliasCount+".");
260                                 ignoreFurtherInvestigation = true;
261                                 return;
262                         }
263                 }
264                 
265                 // check if they used stupid names for type variables
266                 if (aliasCount>0) {
267                         for (int i = 0; i < aliasCount; i++) {
268                                 String array_element = typeVariableAliases.get(i);
269                                 SingleTypeReference str = new SingleTypeReference(array_element.toCharArray(),0);
270                                 TypeBinding tb = str.getTypeBindingPublic(classScope);
271                                 if (tb!=null && !(tb instanceof ProblemReferenceBinding)) {// && !(tb instanceof TypeVariableBinding)) {
272                                         scope.problemReporter().signalError(sourceStart,sourceEnd,
273                                                         "Intertype declarations can only be made on the generic type, not on a parameterized type. The name '"+
274                                                         array_element+"' cannot be used as a type parameter, since it refers to a real type.");
275                                         ignoreFurtherInvestigation = true;
276                                         return;
277                                         
278                                 }
279                         }
280                 }
281 //              TypeVariableBinding[] tVarsInGenericType = onTypeBinding.typeVariables();
282 //              typeVariableAliases = new ArrayList(); /* Name>GenericTypeVariablePosition */ // FIXME ASC DONT THINK WE NEED TO BUILD IT HERE AS WELL...
283 //              TypeReference[] targs = pref.typeArguments;
284 //      if (targs!=null) {
285 //              for (int i = 0; i < targs.length; i++) {
286 //                      TypeReference tref = targs[i];
287 //                      typeVariableAliases.add(CharOperation.toString(tref.getTypeName()));//tVarsInGenericType[i]); 
288 //              }
289 //              }
290         }
291         
292         
293         protected void checkSpec() {
294                 if (Modifier.isProtected(declaredModifiers)) {
295                         scope.problemReporter().signalError(sourceStart, sourceEnd,
296                                 "protected inter-type declarations are not allowed");
297                         ignoreFurtherInvestigation = true;
298                 }
299         }
300         
301         protected List makeEffectiveSignatureAttribute(
302                 ResolvedMember sig,
303                 Shadow.Kind kind,
304                 boolean weaveBody)
305         {
306                 List l = new ArrayList(1);
307                 l.add(new EclipseAttributeAdapter(
308                                 new AjAttribute.EffectiveSignatureAttribute(sig, kind, weaveBody)));
309                 return l;
310         }
311         
312         protected void setMunger(ResolvedTypeMunger munger) {
313                 munger.getSignature().setPosition(sourceStart, sourceEnd);
314                 munger.getSignature().setSourceContext(new EclipseSourceContext(compilationResult));
315                 this.munger = munger;
316         }
317         
318         @Override
319         protected int generateInfoAttributes(ClassFile classFile) {
320                 List l;
321                 Shadow.Kind kind = getShadowKindForBody();
322                 if (kind != null) {
323                         l = makeEffectiveSignatureAttribute(munger.getSignature(), kind, true);
324                 } else {
325                         l = new ArrayList(0);
326                 }
327                 addDeclarationStartLineAttribute(l,classFile);
328
329                 return classFile.generateMethodInfoAttributes(binding, l);
330         }
331
332         protected abstract Shadow.Kind getShadowKindForBody();
333         
334         public ResolvedMember getSignature() { 
335                 if (munger==null) return null; // Can be null in an erroneous program I think
336                 return munger.getSignature(); 
337         }
338
339         public char[] getDeclaredSelector() {
340                 return declaredSelector;
341         }
342         
343         public TypeReference getOnType() {
344                 return onType;
345         }
346         
347
348         /** 
349          * Create the list of aliases based on what was supplied as parameters for the ontype.
350          * For example, if the declaration is 'List<N>  SomeType<N>.foo' then the alias list
351          * will simply contain 'N' and 'N' will mean 'the first type variable declared for
352          * type SomeType'
353          */
354         public void determineTypeVariableAliases() {
355                 if (onType!=null) {
356                     // TODO loses distinction about which level the type variables are at... is that a problem?
357                         if (onType instanceof ParameterizedSingleTypeReference) {
358                                 ParameterizedSingleTypeReference paramRef = (ParameterizedSingleTypeReference) onType;
359                                 TypeReference[] rb = paramRef.typeArguments;
360                                 typeVariableAliases = new ArrayList();
361                                 for (int i = 0; i < rb.length; i++) {
362                                         typeVariableAliases.add(CharOperation.toString(rb[i].getTypeName()));
363                                 }
364                         } else if (onType instanceof ParameterizedQualifiedTypeReference) {
365                                 ParameterizedQualifiedTypeReference paramRef = (ParameterizedQualifiedTypeReference) onType;
366                                 typeVariableAliases = new ArrayList();
367                                 for (int j = 0; j < paramRef.typeArguments.length; j++) {
368                                         TypeReference[] rb = paramRef.typeArguments[j];
369                                         for (int i = 0; rb!=null && i < rb.length; i++) {
370                                                 typeVariableAliases.add(CharOperation.toString(rb[i].getTypeName()));
371                                         }
372                                 }
373                         }
374                 }
375         }  
376
377         /**
378          * Called just before the compiler is going to start resolving elements of a declaration, this method
379          * adds an intertypescope between the methodscope and classscope so that elements of the type targetted
380          * by the ITD can be resolved.  For example, if type variables are referred to in the ontype for the ITD,
381          * they have to be resolved against the ontype, not the aspect containing the ITD.
382          */
383         @Override
384         public void ensureScopeSetup() {
385                 if (scopeSetup) return; // don't do it again
386                 MethodScope scope = this.scope;
387                 
388                 TypeReference ot = onType;
389                 ReferenceBinding rb = null;
390                 
391                 if (ot instanceof ParameterizedQualifiedTypeReference) { // pr132349
392                         ParameterizedQualifiedTypeReference pref = (ParameterizedQualifiedTypeReference) ot;
393                         if (pref.typeArguments!=null && pref.typeArguments.length!=0) {
394                                 boolean usingNonTypeVariableInITD = false;
395                                 // Check if any of them are not type variables
396                                 for (int i = 0; i < pref.typeArguments.length; i++) {
397                                         TypeReference[] refs = pref.typeArguments[i];
398                                         for (int j = 0; refs!=null && j < refs.length; j++) {
399                                                 TypeBinding tb = refs[j].getTypeBindingPublic(scope.parent);
400                                                 if (!tb.isTypeVariable() && !(tb instanceof ProblemReferenceBinding)) {
401                                                         usingNonTypeVariableInITD = true;
402                                                 }
403                                                 
404                                         }
405                                 }
406                                 if (usingNonTypeVariableInITD) {
407                                         scope.problemReporter().signalError(sourceStart,sourceEnd,
408                                           "Cannot make inter-type declarations on parameterized types");
409                                         // to prevent disgusting cascading errors after this problem - lets null out what leads to them (pr105038)
410                                         this.arguments=null;
411                                         this.returnType=new SingleTypeReference(TypeReference.VOID,0L);
412                                         
413                                         this.ignoreFurtherInvestigation=true;
414                                         ReferenceBinding closestMatch = null;
415                                         rb = new ProblemReferenceBinding(ot.getParameterizedTypeName(),closestMatch,0);         
416                                         onType=null;
417                                 }
418                         }
419                 
420                 }
421
422                 // Work out the real base type
423                 if (ot instanceof ParameterizedSingleTypeReference) {
424                         ParameterizedSingleTypeReference pref = (ParameterizedSingleTypeReference) ot;
425                         long pos = (((long)pref.sourceStart) << 32) | pref.sourceEnd;
426                         ot = new SingleTypeReference(pref.token,pos);
427                 } else if (ot instanceof ParameterizedQualifiedTypeReference) {
428                         ParameterizedQualifiedTypeReference pref = (ParameterizedQualifiedTypeReference) ot;
429                         long pos = (((long)pref.sourceStart) << 32) | pref.sourceEnd;
430                         ot = new QualifiedTypeReference(pref.tokens,new long[]{pos});//SingleTypeReference(pref.Quatoken,pos);
431                 }
432                 
433                 // resolve it
434                 if (rb==null) {
435                   rb = (ReferenceBinding)ot.getTypeBindingPublic(scope.parent);
436                 }
437                 
438                 // pr203646 - if we have ended up with the raw type, get back to the underlying generic one.
439                 if (rb.isRawType() && rb.isMemberType()) {
440                         // 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
441                         rb = ((RawTypeBinding)rb).type;
442                 }
443                 
444                 if (rb instanceof TypeVariableBinding) {
445                         scope.problemReporter().signalError(sourceStart,sourceEnd,
446                                           "Cannot make inter-type declarations on type variables, use an interface and declare parents");
447                         // to prevent disgusting cascading errors after this problem - lets null out what leads to them (pr105038)
448                         this.arguments=null;
449                         this.returnType=new SingleTypeReference(TypeReference.VOID,0L);
450                         
451                         this.ignoreFurtherInvestigation=true;
452                         ReferenceBinding closestMatch = null;
453                         if (((TypeVariableBinding)rb).firstBound!=null) {
454                                 closestMatch = ((TypeVariableBinding)rb).firstBound.enclosingType();
455                         }
456                         rb = new ProblemReferenceBinding(rb.compoundName,closestMatch,0);
457                 }
458
459                 
460                 // if resolution failed, give up - someone else is going to report an error
461                 if (rb instanceof ProblemReferenceBinding) return;
462                 
463                 interTypeScope = new InterTypeScope(scope.parent, rb, typeVariableAliases);
464                 // FIXME asc verify the choice of lines here...
465                 // Two versions of this next line.  
466                 // First one tricks the JDT variable processing code so that it won't complain if
467                 // you refer to a type variable from a static ITD - it *is* a problem and it *will* be caught, but later and 
468                 // by the AJDT code so we can put out a much nicer message.
469                 scope.isStatic = (typeVariableAliases!=null?false:Modifier.isStatic(declaredModifiers));
470                 // this is the original version in case tricking the JDT causes grief (if you reinstate this variant, you
471                 // will need to change the expected messages output for some of the generic ITD tests)
472                 // scope.isStatic = Modifier.isStatic(declaredModifiers);
473                 scope.parent = interTypeScope;
474             scopeSetup = true;
475         }
476 }