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