]> source.dussan.org Git - aspectj.git/blob
69088b38c1f69f8d368df8876da4cd3939b8b675
[aspectj.git] /
1 /* *******************************************************************
2  * Copyright (c) 2005 IBM Corporation Ltd
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  *     Adrian Colyer  initial implementation 
11  * ******************************************************************/
12 package org.aspectj.ajdt.internal.compiler.ast;
13
14 import java.lang.reflect.Modifier;
15 import java.util.Stack;
16 import java.util.ArrayList;
17 import java.util.List;
18
19 import org.aspectj.ajdt.internal.compiler.lookup.EclipseFactory;
20 import org.aspectj.ajdt.internal.compiler.lookup.EclipseScope;
21 import org.aspectj.ajdt.internal.core.builder.EclipseSourceContext;
22 import org.aspectj.bridge.context.CompilationAndWeavingContext;
23 import org.aspectj.bridge.context.ContextToken;
24 import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
25 import org.aspectj.org.eclipse.jdt.internal.compiler.ASTVisitor;
26 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Annotation;
27 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Argument;
28 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
29 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
30 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
31 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
32 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.NameReference;
33 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.NormalAnnotation;
34 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
35 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
36 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.StringLiteral;
37 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
38 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeReference;
39 import org.aspectj.org.eclipse.jdt.internal.compiler.impl.Constant;
40 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.Binding;
41 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.BlockScope;
42 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ClassScope;
43 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
44 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
45 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.MethodScope;
46 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
47 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
48 import org.aspectj.weaver.AdviceKind;
49 import org.aspectj.weaver.AjAttribute;
50 import org.aspectj.weaver.BCException;
51 import org.aspectj.weaver.ISourceContext;
52 import org.aspectj.weaver.ResolvedPointcutDefinition;
53 import org.aspectj.weaver.UnresolvedType;
54 import org.aspectj.weaver.patterns.AbstractPatternNodeVisitor;
55 import org.aspectj.weaver.patterns.FormalBinding;
56 import org.aspectj.weaver.patterns.IfPointcut;
57 import org.aspectj.weaver.patterns.ParserException;
58 import org.aspectj.weaver.patterns.PatternParser;
59 import org.aspectj.weaver.patterns.Pointcut;
60
61 public class ValidateAtAspectJAnnotationsVisitor extends ASTVisitor {
62
63         private static final char[] beforeAdviceSig = "Lorg/aspectj/lang/annotation/Before;".toCharArray();
64         private static final char[] afterAdviceSig = "Lorg/aspectj/lang/annotation/After;".toCharArray();
65         private static final char[] afterReturningAdviceSig = "Lorg/aspectj/lang/annotation/AfterReturning;".toCharArray();
66         private static final char[] afterThrowingAdviceSig = "Lorg/aspectj/lang/annotation/AfterThrowing;".toCharArray();
67         private static final char[] aroundAdviceSig = "Lorg/aspectj/lang/annotation/Around;".toCharArray();
68         private static final char[] pointcutSig = "Lorg/aspectj/lang/annotation/Pointcut;".toCharArray();
69         private static final char[] aspectSig = "Lorg/aspectj/lang/annotation/Aspect;".toCharArray();
70         private static final char[] declareParentsSig = "Lorg/aspectj/lang/annotation/DeclareParents;".toCharArray();
71         private static final char[] adviceNameSig = "Lorg/aspectj/lang/annotation/AdviceName;".toCharArray();
72         private static final char[] orgAspectJLangAnnotation = "org/aspectj/lang/annotation/".toCharArray();
73         private static final char[] voidType = "void".toCharArray();
74         private static final char[] booleanType = "boolean".toCharArray();
75         private static final char[] joinPoint = "Lorg/aspectj/lang/JoinPoint;".toCharArray();
76         private static final char[] joinPointStaticPart = "Lorg/aspectj/lang/JoinPoint$StaticPart;".toCharArray();
77         private static final char[] joinPointEnclosingStaticPart = "Lorg/aspectj/lang/JoinPoint$EnclosingStaticPart;".toCharArray();
78         private static final char[] proceedingJoinPoint = "Lorg/aspectj/lang/ProceedingJoinPoint;".toCharArray();
79         private static final char[][] adviceSigs = new char[][] {beforeAdviceSig,afterAdviceSig,afterReturningAdviceSig,afterThrowingAdviceSig,aroundAdviceSig};
80         
81         
82         private CompilationUnitDeclaration unit;
83         private Stack typeStack = new Stack();
84         private AspectJAnnotations ajAnnotations;
85         
86         public ValidateAtAspectJAnnotationsVisitor(CompilationUnitDeclaration unit) {
87                 this.unit = unit;
88         }
89         
90         public boolean visit(TypeDeclaration localTypeDeclaration, BlockScope scope) {
91                 typeStack.push(localTypeDeclaration);
92                 ajAnnotations = new AspectJAnnotations(localTypeDeclaration.annotations);
93                 checkTypeDeclaration(localTypeDeclaration);
94                 return true;
95         }
96         
97         public void endVisit(TypeDeclaration localTypeDeclaration,BlockScope scope) {
98                 typeStack.pop();
99         }
100         
101         public boolean visit(TypeDeclaration memberTypeDeclaration,ClassScope scope) {
102                 typeStack.push(memberTypeDeclaration);
103                 ajAnnotations = new AspectJAnnotations(memberTypeDeclaration.annotations);
104                 checkTypeDeclaration(memberTypeDeclaration);
105                 return true;
106         }
107         
108         public void endVisit(TypeDeclaration memberTypeDeclaration,ClassScope scope) {
109                 typeStack.pop();
110         }
111         
112         public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
113                 typeStack.push(typeDeclaration);
114                 ajAnnotations = new AspectJAnnotations(typeDeclaration.annotations);
115                 checkTypeDeclaration(typeDeclaration);
116                 return true;
117         }
118         
119         public void endVisit(TypeDeclaration typeDeclaration,CompilationUnitScope scope) {
120                 typeStack.pop();
121         }
122         
123         private void checkTypeDeclaration(TypeDeclaration typeDecl) {
124                 ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.VALIDATING_AT_ASPECTJ_ANNOTATIONS, typeDecl.name);
125                 if (!(typeDecl instanceof AspectDeclaration)) {
126                         if (ajAnnotations.hasAspectAnnotation) {
127                                 validateAspectDeclaration(typeDecl);
128                         } else {
129                                 // check that class doesn't extend aspect
130                                 TypeReference parentRef = typeDecl.superclass;
131                                 if (parentRef != null) {
132                                         TypeBinding parentBinding = parentRef.resolvedType;
133                                         if (parentBinding instanceof SourceTypeBinding) {
134                                                 SourceTypeBinding parentSTB = (SourceTypeBinding) parentBinding;
135                                                 if (parentSTB.scope != null) {
136                                                         TypeDeclaration parentDecl = parentSTB.scope.referenceContext;
137                                                         if (isAspect(parentDecl)) {
138                                                                 typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart,typeDecl.sourceEnd,"a class cannot extend an aspect");
139                                                         }
140                                                 }
141                                         }
142                                 }
143                         }
144                 } else {
145                         // check that aspect doesn't have @Aspect annotation, we've already added on ourselves.
146                         if (ajAnnotations.hasMultipleAspectAnnotations) {
147                                         typeDecl.scope.problemReporter().signalError(
148                                                         typeDecl.sourceStart,
149                                                         typeDecl.sourceEnd,
150                                                         "aspects cannot have @Aspect annotation"
151                                                         );
152                         }
153                 }
154                 CompilationAndWeavingContext.leavingPhase(tok);
155         }
156
157         public boolean visit(FieldDeclaration fieldDeclaration, MethodScope scope) {
158                 ajAnnotations = new AspectJAnnotations(fieldDeclaration.annotations);
159                 if (ajAnnotations.hasDeclareParents && !insideAspect()) {
160                         scope.problemReporter().signalError(fieldDeclaration.sourceStart,
161                                           fieldDeclaration.sourceEnd, 
162                                           "DeclareParents can only be used inside an aspect type");                     
163                 }
164                 return true;
165         }
166         public boolean visit(MethodDeclaration methodDeclaration, ClassScope scope) {
167                 if (methodDeclaration.hasErrors()) {
168                         return false;
169                 }
170                 ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.VALIDATING_AT_ASPECTJ_ANNOTATIONS, methodDeclaration.selector);
171                 ajAnnotations = new AspectJAnnotations(methodDeclaration.annotations);
172                 if (!methodDeclaration.getClass().equals(AjMethodDeclaration.class)) {
173                         // simply test for innapropriate use of annotations on code-style members
174                         if (methodDeclaration instanceof PointcutDeclaration) {
175                                 if (ajAnnotations.hasMultiplePointcutAnnotations ||
176                                     ajAnnotations.hasAdviceAnnotation ||
177                                     ajAnnotations.hasAspectAnnotation ||
178                                     ajAnnotations.hasAdviceNameAnnotation) {
179                                         methodDeclaration.scope.problemReporter().signalError(
180                                                         methodDeclaration.sourceStart,
181                                                         methodDeclaration.sourceEnd,
182                                                         "@AspectJ annotations cannot be declared on this aspect member");
183                                 }
184                         } else if (methodDeclaration instanceof AdviceDeclaration) {
185                                 if (ajAnnotations.hasMultipleAdviceAnnotations ||
186                                         ajAnnotations.hasAspectAnnotation ||
187                                         ajAnnotations.hasPointcutAnnotation) {
188                                         methodDeclaration.scope.problemReporter().signalError(
189                                                                                 methodDeclaration.sourceStart,
190                                                                                 methodDeclaration.sourceEnd,
191                                                                                 "Only @AdviceName AspectJ annotation allowed on advice");
192                                 }                               
193                         } else {
194                                 if (ajAnnotations.hasAspectJAnnotations()) {
195                                         methodDeclaration.scope.problemReporter().signalError(
196                                                         methodDeclaration.sourceStart,
197                                                         methodDeclaration.sourceEnd,
198                                                         "@AspectJ annotations cannot be declared on this aspect member");                                       
199                                 }
200                         }
201                         CompilationAndWeavingContext.leavingPhase(tok);
202                         return false;
203                 }
204                 
205                 if (ajAnnotations.hasAdviceAnnotation) {
206                         validateAdvice(methodDeclaration);
207                 } else if (ajAnnotations.hasPointcutAnnotation) {
208                         convertToPointcutDeclaration(methodDeclaration,scope);
209                 }
210                 CompilationAndWeavingContext.leavingPhase(tok);
211                 return false;
212         }
213         
214         private boolean isAspectJAnnotation(Annotation ann) {
215                 if (ann.resolvedType == null) return false;
216                 char[] sig = ann.resolvedType.signature();
217                 return CharOperation.contains(orgAspectJLangAnnotation, sig);
218         }
219         
220         private boolean insideAspect() {
221                 if (typeStack.empty()) return false;
222                 TypeDeclaration typeDecl = (TypeDeclaration) typeStack.peek();
223                 return isAspect(typeDecl);
224         }
225         
226         private boolean isAspect(TypeDeclaration typeDecl) {
227                 if (typeDecl instanceof AspectDeclaration) return true;
228                 return new AspectJAnnotations(typeDecl.annotations).hasAspectAnnotation;
229         }
230         
231         /**
232      * aspect must be public
233          * nested aspect must be static
234          * cannot extend a concrete aspect
235          * pointcut in perclause must be good.
236          */
237         private void validateAspectDeclaration(TypeDeclaration typeDecl) {
238                 if (typeStack.size() > 1) {
239                         // it's a nested aspect
240                         if (!Modifier.isStatic(typeDecl.modifiers)) {
241                                 typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart, typeDecl.sourceEnd, "inner aspects must be static");
242                                 return;
243                         }
244                 }
245                 
246                 SourceTypeBinding binding = typeDecl.binding;
247                 if (binding != null) {
248                         if (binding.isEnum() || binding.isInterface() || binding.isAnnotationType()) {
249                                 typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart,typeDecl.sourceEnd,"only classes can have an @Aspect annotation");
250                         }
251                 }
252
253         //FIXME AV - do we really want that
254 //        if (!Modifier.isPublic(typeDecl.modifiers)) {
255 //            typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart,typeDecl.sourceEnd,"@Aspect class must be public");
256 //        }
257
258         TypeReference parentRef = typeDecl.superclass;
259                 if (parentRef != null) {
260                         TypeBinding parentBinding = parentRef.resolvedType;
261                         if (parentBinding instanceof SourceTypeBinding) {
262                                 SourceTypeBinding parentSTB = (SourceTypeBinding) parentBinding;
263                                 if (parentSTB.scope!=null) { // scope is null if its a binarytypebinding (in AJ world, thats a subclass of SourceTypeBinding)
264                                         TypeDeclaration parentDecl = parentSTB.scope.referenceContext; 
265                                         if (isAspect(parentDecl) && !Modifier.isAbstract(parentDecl.modifiers)) {
266                                                 typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart,typeDecl.sourceEnd,"cannot extend a concrete aspect");
267                                         }                       
268                                 }
269                         }
270                 }
271
272                 Annotation aspectAnnotation = ajAnnotations.aspectAnnotation;
273
274                 int[] pcLoc = new int[2];
275                 String perClause = getStringLiteralFor("value", aspectAnnotation, pcLoc);
276                 AspectDeclaration aspectDecl = new AspectDeclaration(typeDecl.compilationResult);
277
278                 try {
279                         if (perClause != null && !perClause.equals("")) {
280                                 ISourceContext context = new EclipseSourceContext(unit.compilationResult,pcLoc[0]);
281                                 Pointcut pc = new PatternParser(perClause,context).maybeParsePerClause();
282                             FormalBinding[] bindings = new FormalBinding[0];
283                                 if (pc != null) pc.resolve(new EclipseScope(bindings,typeDecl.scope));
284                         }
285                 } catch(ParserException pEx) {
286                         typeDecl.scope.problemReporter().parseError(
287                                         pcLoc[0] + pEx.getLocation().getStart(),
288                                         pcLoc[0] + pEx.getLocation().getEnd() ,
289                                         -1, 
290                                         perClause.toCharArray(), 
291                                         perClause, 
292                                         new String[] {pEx.getMessage()});
293                 }
294         }
295         
296         /**
297          * 1) Advice must be public
298          * 2) Advice must have a void return type if not around advice
299          * 3) Advice must not have any other @AspectJ annotations
300          * 4) After throwing advice must declare the thrown formal
301          * 5) After returning advice must declare the returning formal
302          * 6) Advice must not be static
303          */
304         private void validateAdvice(MethodDeclaration methodDeclaration) {
305                 
306                 if (!insideAspect()) {
307                         methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart,
308                                           methodDeclaration.sourceEnd, 
309                                           "Advice must be declared inside an aspect type");                     
310                 }
311                 
312                 if (!Modifier.isPublic(methodDeclaration.modifiers)) {
313                         methodDeclaration.scope.problemReporter()
314                                 .signalError(methodDeclaration.sourceStart,methodDeclaration.sourceEnd,"advice must be public");
315                 }
316
317                 if (Modifier.isStatic(methodDeclaration.modifiers)) {
318                         methodDeclaration.scope.problemReporter()
319                                 .signalError(methodDeclaration.sourceStart,methodDeclaration.sourceEnd,"advice can not be declared static");
320                 }
321                                 
322                 if (ajAnnotations.hasMultipleAdviceAnnotations) {
323                         methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.duplicateAdviceAnnotation);
324                 }
325                 if (ajAnnotations.hasPointcutAnnotation) {
326                         methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.pointcutAnnotation);                      
327                 }
328                 if (ajAnnotations.hasAspectAnnotation) {
329                         methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.aspectAnnotation);                        
330                 }
331                 if (ajAnnotations.hasAdviceNameAnnotation) {
332                         methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.adviceNameAnnotation);                    
333                 }
334
335                 if (ajAnnotations.adviceKind != AdviceKind.Around) {
336                         ensureVoidReturnType(methodDeclaration);
337                 }         
338                 
339                 if (ajAnnotations.adviceKind == AdviceKind.AfterThrowing) {
340                         int[] throwingLocation = new int[2];
341                         String thrownFormal = getStringLiteralFor("throwing",ajAnnotations.adviceAnnotation,throwingLocation);
342                         if (thrownFormal != null) {
343                                 Argument[] arguments = methodDeclaration.arguments;
344                                 if (!toArgumentNames(methodDeclaration.arguments).contains(thrownFormal)) {
345                                                 methodDeclaration.scope.problemReporter()
346                                                         .signalError(methodDeclaration.sourceStart,methodDeclaration.sourceEnd,"throwing formal '" + thrownFormal + "' must be declared as a parameter in the advice signature");
347                                 }
348                         }
349                 }
350                 
351                 if (ajAnnotations.adviceKind == AdviceKind.AfterReturning) {
352                         int[] throwingLocation = new int[2];
353                         String returningFormal = getStringLiteralFor("returning",ajAnnotations.adviceAnnotation,throwingLocation);
354                         if (returningFormal != null) {
355                                 if (!toArgumentNames(methodDeclaration.arguments).contains(returningFormal)) {
356                                                 methodDeclaration.scope.problemReporter()
357                                                         .signalError(methodDeclaration.sourceStart,methodDeclaration.sourceEnd,"returning formal '" + returningFormal + "' must be declared as a parameter in the advice signature");
358                                 }
359                         }
360                 }
361
362                 resolveAndSetPointcut(methodDeclaration, ajAnnotations.adviceAnnotation);
363                 
364         }
365
366     /**
367      * Get the argument names as a string list
368      * @param arguments
369      * @return argument names (possibly empty)
370      */
371     private List toArgumentNames(Argument[] arguments) {
372         List names = new ArrayList();
373         if (arguments == null) {
374             return names;
375         } else {
376             for (int i = 0; i < arguments.length; i++) {
377                 names.add(new String(arguments[i].name));
378             }
379             return names;
380         }
381     }
382
383     private void resolveAndSetPointcut(MethodDeclaration methodDeclaration, Annotation adviceAnn) {
384                 int[] pcLocation = new int[2];
385                 String pointcutExpression = getStringLiteralFor("pointcut",adviceAnn,pcLocation);
386                 if (pointcutExpression == null) pointcutExpression = getStringLiteralFor("value",adviceAnn,pcLocation);
387                 try {
388                         ISourceContext context = new EclipseSourceContext(unit.compilationResult,pcLocation[0]);
389                         Pointcut pc = new PatternParser(pointcutExpression,context).parsePointcut();
390                         FormalBinding[] bindings = buildFormalAdviceBindingsFrom(methodDeclaration);
391                         pc.resolve(new EclipseScope(bindings,methodDeclaration.scope));
392                         EclipseFactory factory = EclipseFactory.fromScopeLookupEnvironment(methodDeclaration.scope);
393                         // now create a ResolvedPointcutDefinition,make an attribute out of it, and add it to the method
394                         UnresolvedType[] paramTypes = new UnresolvedType[bindings.length];
395                         for (int i = 0; i < paramTypes.length; i++) paramTypes[i] = bindings[i].getType();
396                         ResolvedPointcutDefinition resPcutDef = 
397                                 new ResolvedPointcutDefinition(
398                                                 factory.fromBinding(((TypeDeclaration)typeStack.peek()).binding),
399                                                 methodDeclaration.modifiers,
400                                                 "anonymous",
401                                                 paramTypes,
402                                                 pc
403                                 );
404                         AjAttribute attr = new AjAttribute.PointcutDeclarationAttribute(resPcutDef);
405                         ((AjMethodDeclaration)methodDeclaration).addAttribute(new EclipseAttributeAdapter(attr));
406                 } catch(ParserException pEx) {
407                         methodDeclaration.scope.problemReporter().parseError(
408                                         pcLocation[0] + pEx.getLocation().getStart(),
409                                         pcLocation[0] + pEx.getLocation().getEnd() ,
410                                         -1, 
411                                         pointcutExpression.toCharArray(), 
412                                         pointcutExpression, 
413                                         new String[] {pEx.getMessage()});
414                 }
415         }
416
417         private void ensureVoidReturnType(MethodDeclaration methodDeclaration) {
418                 boolean returnsVoid = true;
419                 if ((methodDeclaration.returnType instanceof SingleTypeReference)) {
420                         SingleTypeReference retType = (SingleTypeReference) methodDeclaration.returnType;
421                         if (!CharOperation.equals(voidType,retType.token)) {
422                                 returnsVoid = false;
423                         }
424                 } else {
425                         returnsVoid = false;
426                 }
427                 if (!returnsVoid) {
428                         methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
429                                                                                                                                                                           methodDeclaration.returnType.sourceEnd, 
430                                                                                                                                                                           "This advice must return void");
431                 }
432         }
433         
434         private FormalBinding[] buildFormalAdviceBindingsFrom(MethodDeclaration mDecl) {
435                 if (mDecl.arguments == null) return new FormalBinding[0];
436                 if (mDecl.binding == null) return new FormalBinding[0];
437                 EclipseFactory factory = EclipseFactory.fromScopeLookupEnvironment(mDecl.scope);
438                 String extraArgName = maybeGetExtraArgName();
439                 if (extraArgName == null) extraArgName = "";
440                 FormalBinding[] ret = new FormalBinding[mDecl.arguments.length];
441                 for (int i = 0; i < mDecl.arguments.length; i++) {
442             Argument arg = mDecl.arguments[i];
443             String name = new String(arg.name);
444             TypeBinding argTypeBinding = mDecl.binding.parameters[i];
445             UnresolvedType type = factory.fromBinding(argTypeBinding);
446                         if  (CharOperation.equals(joinPoint,argTypeBinding.signature()) ||
447                                  CharOperation.equals(joinPointStaticPart,argTypeBinding.signature()) ||
448                                  CharOperation.equals(joinPointEnclosingStaticPart,argTypeBinding.signature()) ||
449                                  CharOperation.equals(proceedingJoinPoint,argTypeBinding.signature()) ||
450                                  name.equals(extraArgName)) {
451                                 ret[i] = new FormalBinding.ImplicitFormalBinding(type,name,i);
452                         } else {
453                     ret[i] = new FormalBinding(type, name, i, arg.sourceStart, arg.sourceEnd, "unknown");                                               
454                         }
455                 }
456                 return ret;
457         }
458         
459         private String maybeGetExtraArgName() {
460                 String argName = null;
461                 if (ajAnnotations.adviceKind == AdviceKind.AfterReturning) {
462                         argName = getStringLiteralFor("returning",ajAnnotations.adviceAnnotation,new int[2]);
463                 } else if (ajAnnotations.adviceKind == AdviceKind.AfterThrowing) {
464                         argName = getStringLiteralFor("throwing",ajAnnotations.adviceAnnotation,new int[2]);
465                 }
466                 return argName;
467         }
468
469         private String getStringLiteralFor(String memberName, Annotation inAnnotation, int[] location) {
470                 if (inAnnotation instanceof SingleMemberAnnotation && memberName.equals("value")) {
471                         SingleMemberAnnotation sma = (SingleMemberAnnotation) inAnnotation;
472                         if (sma.memberValue instanceof StringLiteral) {
473                                 StringLiteral sv = (StringLiteral) sma.memberValue;
474                                 location[0] = sv.sourceStart;
475                                 location[1] = sv.sourceEnd;
476                                 return new String(sv.source());
477                         } else if (sma.memberValue instanceof NameReference && (((NameReference)sma.memberValue).binding instanceof FieldBinding)) {
478                                 Binding b = ((NameReference)sma.memberValue).binding;
479                                 Constant c = ((FieldBinding)b).constant;
480                                 return c.stringValue();
481                         }
482                 }
483                 if (! (inAnnotation instanceof NormalAnnotation)) return null;
484                 NormalAnnotation ann = (NormalAnnotation) inAnnotation;
485                 MemberValuePair[] mvps = ann.memberValuePairs;
486                 if (mvps == null) return null;
487                 for (int i = 0; i < mvps.length; i++) {
488                         if (CharOperation.equals(memberName.toCharArray(),mvps[i].name)) {
489                                 if (mvps[i].value instanceof StringLiteral) {
490                                         StringLiteral sv = (StringLiteral) mvps[i].value;
491                                         location[0] = sv.sourceStart;
492                                         location[1] = sv.sourceEnd;
493                                         return new String(sv.source());
494                                 }
495                         }
496                 }
497                 return null;
498         }
499         
500         private void convertToPointcutDeclaration(MethodDeclaration methodDeclaration, ClassScope scope) {
501                 TypeDeclaration typeDecl = (TypeDeclaration) typeStack.peek();
502                 if (typeDecl.binding != null) {
503                         if (!typeDecl.binding.isClass()) {
504                                 methodDeclaration.scope.problemReporter()
505                                         .signalError(methodDeclaration.sourceStart,methodDeclaration.sourceEnd,"pointcuts can only be declared in a class or an aspect");
506                         }
507                 }
508                 
509                 if (methodDeclaration.thrownExceptions != null && methodDeclaration.thrownExceptions.length > 0) {
510                         methodDeclaration.scope.problemReporter()
511                                 .signalError(methodDeclaration.sourceStart,methodDeclaration.sourceEnd,"pointcuts cannot throw exceptions!");
512                 }
513                 
514                 PointcutDeclaration pcDecl = new PointcutDeclaration(unit.compilationResult);
515                 copyAllFields(methodDeclaration,pcDecl);
516
517                 if (ajAnnotations.hasAdviceAnnotation) {
518                         methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.adviceAnnotation);                        
519                 }
520                 if (ajAnnotations.hasAspectAnnotation) {
521                         methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.aspectAnnotation);                        
522                 }
523                 if (ajAnnotations.hasAdviceNameAnnotation) {
524                         methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.adviceNameAnnotation);                    
525                 }
526         
527                 boolean noValueSupplied=true;
528                 boolean containsIfPcd = false;
529                 int[] pcLocation = new int[2];
530                 String pointcutExpression = getStringLiteralFor("value",ajAnnotations.pointcutAnnotation,pcLocation);
531                 try {
532                         ISourceContext context = new EclipseSourceContext(unit.compilationResult,pcLocation[0]);
533             Pointcut pc = null;//abstract
534             if (pointcutExpression == null  || pointcutExpression.length() == 0) {
535                 noValueSupplied=true; // matches nothing pointcut
536             } else {
537                 noValueSupplied=false;
538                 pc = new PatternParser(pointcutExpression,context).parsePointcut();
539             }
540             pcDecl.pointcutDesignator = (pc==null)?null:new PointcutDesignator(pc);
541                         pcDecl.setGenerateSyntheticPointcutMethod();
542                         TypeDeclaration onType = (TypeDeclaration) typeStack.peek();
543                         pcDecl.postParse(onType);
544 //                      EclipseFactory factory = EclipseFactory.fromScopeLookupEnvironment(methodDeclaration.scope);
545 //                      int argsLength = methodDeclaration.arguments == null ? 0 : methodDeclaration.arguments.length;
546                         FormalBinding[] bindings = buildFormalAdviceBindingsFrom(methodDeclaration);
547 //                  FormalBinding[] bindings = new FormalBinding[argsLength];
548 //              for (int i = 0, len = bindings.length; i < len; i++) {
549 //                  Argument arg = methodDeclaration.arguments[i];
550 //                  String name = new String(arg.name);
551 //                  UnresolvedType type = factory.fromBinding(methodDeclaration.binding.parameters[i]);
552 //                  bindings[i] = new FormalBinding(type, name, i, arg.sourceStart, arg.sourceEnd, "unknown");
553 //              }
554                         swap(onType,methodDeclaration,pcDecl);
555                         if (pc != null) {
556                 // has an expression
557                 pc.resolve(new EclipseScope(bindings,methodDeclaration.scope));
558                 HasIfPCDVisitor ifFinder = new HasIfPCDVisitor();
559                 pc.traverse(ifFinder, null);
560                 containsIfPcd = ifFinder.containsIfPcd;
561             }
562         } catch(ParserException pEx) {
563                         methodDeclaration.scope.problemReporter().parseError(
564                                         pcLocation[0] + pEx.getLocation().getStart(),
565                                         pcLocation[0] + pEx.getLocation().getEnd() ,
566                                         -1, 
567                                         pointcutExpression.toCharArray(), 
568                                         pointcutExpression, 
569                                         new String[] {pEx.getMessage()});
570                 }
571                 
572                 boolean returnsVoid = false;
573                 boolean returnsBoolean = false;
574                 if ((methodDeclaration.returnType instanceof SingleTypeReference)) {
575                         SingleTypeReference retType = (SingleTypeReference) methodDeclaration.returnType;
576                         if (CharOperation.equals(voidType,retType.token)) returnsVoid = true;
577                         if (CharOperation.equals(booleanType,retType.token)) returnsBoolean = true;
578                 } 
579                 if (!returnsVoid && !containsIfPcd) {
580                         methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
581                                                                                                                                                                           methodDeclaration.returnType.sourceEnd, 
582                                                                                                                                                                           "Methods annotated with @Pointcut must return void unless the pointcut contains an if() expression");
583                 }
584                 if (!returnsBoolean && containsIfPcd) {
585                         methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
586                                                                                                                                                                           methodDeclaration.returnType.sourceEnd, 
587                                                                                                                                                                           "Methods annotated with @Pointcut must return boolean when the pointcut contains an if() expression");
588                 }
589                 
590                 if (methodDeclaration.statements != null && methodDeclaration.statements.length > 0 && !containsIfPcd) {
591                         methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
592                                           methodDeclaration.returnType.sourceEnd, 
593                                           "Pointcuts without an if() expression should have an empty method body");                     
594                 }
595
596         if (pcDecl.pointcutDesignator == null) {
597             if (Modifier.isAbstract(methodDeclaration.modifiers) 
598                 || noValueSupplied // this is a matches nothing pointcut
599                 //those 2 checks makes sense for aop.xml concretization but NOT for regular abstraction of pointcut
600                 //&& returnsVoid
601                 //&& (methodDeclaration.arguments == null || methodDeclaration.arguments.length == 0)) {
602                 ) {
603                 ;//fine
604             } else {
605                         methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
606                                   methodDeclaration.returnType.sourceEnd,
607                                   "Method annotated with @Pointcut() for abstract pointcut must be abstract");
608             }
609         } else if (Modifier.isAbstract(methodDeclaration.modifiers)) {
610             methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
611                       methodDeclaration.returnType.sourceEnd,
612                       "Method annotated with non abstract @Pointcut(\""+pointcutExpression+"\") is abstract");
613         }
614     }
615         
616         private void copyAllFields(MethodDeclaration from, MethodDeclaration to) {
617                 to.annotations = from.annotations;
618                 to.arguments = from.arguments;
619                 to.binding = from.binding;
620                 to.bits = from.bits;
621                 to.bodyEnd = from.bodyEnd;
622                 to.bodyStart = from.bodyStart;
623                 to.declarationSourceEnd = from.declarationSourceEnd;
624                 to.declarationSourceStart = from.declarationSourceStart;
625                 to.explicitDeclarations = from.explicitDeclarations;
626                 to.ignoreFurtherInvestigation = from.ignoreFurtherInvestigation;
627                 to.javadoc = from.javadoc;
628                 to.modifiers = from.modifiers;
629                 to.modifiersSourceStart = from.modifiersSourceStart;
630                 to.returnType = from.returnType;
631                 to.scope = from.scope;
632                 to.selector = from.selector;
633                 to.sourceEnd = from.sourceEnd;
634                 to.sourceStart = from.sourceStart;
635                 to.statements = from.statements;
636                 to.thrownExceptions = from.thrownExceptions;
637                 to.typeParameters = from.typeParameters;
638         }
639         
640         private void swap(TypeDeclaration inType, MethodDeclaration thisDeclaration, MethodDeclaration forThatDeclaration) {
641                 for (int i = 0; i < inType.methods.length; i++) {
642                         if (inType.methods[i]  == thisDeclaration) {
643                                 inType.methods[i] = forThatDeclaration;
644                                 break;
645                         }
646                 }
647         }
648         
649         private static class AspectJAnnotations {
650                 boolean hasAdviceAnnotation = false;
651                 boolean hasPointcutAnnotation = false;
652                 boolean hasAspectAnnotation = false;
653                 boolean hasAdviceNameAnnotation = false;
654                 boolean hasDeclareParents = false;
655                 boolean hasMultipleAdviceAnnotations = false;
656                 boolean hasMultiplePointcutAnnotations = false;
657                 boolean hasMultipleAspectAnnotations = false;
658                 
659                 AdviceKind adviceKind = null;
660                 Annotation adviceAnnotation = null;
661                 Annotation pointcutAnnotation = null;
662                 Annotation aspectAnnotation = null;
663                 Annotation adviceNameAnnotation = null;
664
665                 Annotation duplicateAdviceAnnotation = null;
666                 Annotation duplicatePointcutAnnotation = null;
667                 Annotation duplicateAspectAnnotation = null;
668                 
669                 public AspectJAnnotations(Annotation[] annotations) {
670                         if (annotations == null) return;
671                         for (int i = 0; i < annotations.length; i++) {
672                                 if (annotations[i].resolvedType == null) continue; // user messed up annotation declaration
673                                 char[] sig = annotations[i].resolvedType.signature();
674                                 if (CharOperation.equals(afterAdviceSig,sig)) {
675                                         adviceKind = AdviceKind.After;
676                                         addAdviceAnnotation(annotations[i]);
677                                 } else if (CharOperation.equals(afterReturningAdviceSig,sig)) {
678                                         adviceKind = AdviceKind.AfterReturning;
679                                         addAdviceAnnotation(annotations[i]);
680                                 } else if (CharOperation.equals(afterThrowingAdviceSig,sig)) {
681                                         adviceKind = AdviceKind.AfterThrowing;
682                                         addAdviceAnnotation(annotations[i]);
683                                 } else if (CharOperation.equals(beforeAdviceSig,sig)) {
684                                         adviceKind = AdviceKind.Before;
685                                         addAdviceAnnotation(annotations[i]);
686                                 } else if (CharOperation.equals(aroundAdviceSig,sig)) {
687                                         adviceKind = AdviceKind.Around;
688                                         addAdviceAnnotation(annotations[i]);
689                                 } else if (CharOperation.equals(adviceNameSig,sig)) {
690                                         hasAdviceNameAnnotation = true;
691                                         adviceNameAnnotation = annotations[i];
692                                 } else if (CharOperation.equals(declareParentsSig,sig)) {
693                                         hasDeclareParents = true;
694                                 } else if (CharOperation.equals(aspectSig,sig)) {
695                                         if (hasAspectAnnotation) {
696                                                 hasMultipleAspectAnnotations = true;
697                                                 duplicateAspectAnnotation = annotations[i];
698                                         } else {
699                                                 hasAspectAnnotation = true;
700                                                 aspectAnnotation = annotations[i];
701                                         }
702                                 } else if (CharOperation.equals(pointcutSig,sig)) {
703                                         if (hasPointcutAnnotation) {
704                                                 hasMultiplePointcutAnnotations = true;
705                                                 duplicatePointcutAnnotation = annotations[i];
706                                         } else {
707                                                 hasPointcutAnnotation = true;
708                                                 pointcutAnnotation = annotations[i];
709                                         }
710                                         
711                                 }
712                         }
713                 }
714                 
715                 public boolean hasAspectJAnnotations() {
716                         return hasAdviceAnnotation || hasPointcutAnnotation || hasAdviceNameAnnotation || hasAspectAnnotation;
717                 }
718
719                 private void addAdviceAnnotation(Annotation annotation) {
720                         if (!hasAdviceAnnotation) {
721                                 hasAdviceAnnotation = true;
722                                 adviceAnnotation = annotation;
723                         } else {
724                                 hasMultipleAdviceAnnotations = true;
725                                 duplicateAdviceAnnotation = annotation;
726                         }
727                 }
728         }
729         
730         private static class HasIfPCDVisitor extends AbstractPatternNodeVisitor {
731                 public boolean containsIfPcd = false;
732                 
733                 public Object visit(IfPointcut node, Object data) {
734                         containsIfPcd = true;
735                         return data;
736                 }
737         }
738 }