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