1 /* *******************************************************************
2 * Copyright (c) 2005 IBM Corporation Ltd
4 * This program and the accompanying materials are made available
5 * under the terms of the Eclipse Public License v 2.0
6 * which accompanies this distribution and is available at
7 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
10 * Adrian Colyer initial implementation
11 * ******************************************************************/
12 package org.aspectj.ajdt.internal.compiler.ast;
14 import java.lang.reflect.Modifier;
15 import java.util.ArrayList;
16 import java.util.List;
17 import java.util.Stack;
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;
61 public class ValidateAtAspectJAnnotationsVisitor extends ASTVisitor {
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 };
84 private final CompilationUnitDeclaration unit;
85 private final Stack<TypeDeclaration> typeStack = new Stack<>();
86 private AspectJAnnotations ajAnnotations;
88 public ValidateAtAspectJAnnotationsVisitor(CompilationUnitDeclaration unit) {
92 public boolean visit(TypeDeclaration localTypeDeclaration, BlockScope scope) {
93 typeStack.push(localTypeDeclaration);
94 ajAnnotations = new AspectJAnnotations(localTypeDeclaration.annotations);
95 checkTypeDeclaration(localTypeDeclaration);
99 public void endVisit(TypeDeclaration localTypeDeclaration, BlockScope scope) {
103 public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
104 typeStack.push(memberTypeDeclaration);
105 ajAnnotations = new AspectJAnnotations(memberTypeDeclaration.annotations);
106 checkTypeDeclaration(memberTypeDeclaration);
110 public void endVisit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
114 public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
115 typeStack.push(typeDeclaration);
116 ajAnnotations = new AspectJAnnotations(typeDeclaration.annotations);
117 checkTypeDeclaration(typeDeclaration);
121 public void endVisit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
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);
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");
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");
156 CompilationAndWeavingContext.leavingPhase(tok);
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");
168 public boolean visit(MethodDeclaration methodDeclaration, ClassScope scope) {
169 if (methodDeclaration.hasErrors()) {
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
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");
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");
191 if (ajAnnotations.hasAspectJAnnotations()) {
192 methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart,
193 methodDeclaration.sourceEnd, "@AspectJ annotations cannot be declared on this aspect member");
196 CompilationAndWeavingContext.leavingPhase(tok);
200 if (ajAnnotations.hasAdviceAnnotation) {
201 validateAdvice(methodDeclaration);
202 } else if (ajAnnotations.hasPointcutAnnotation) {
203 convertToPointcutDeclaration(methodDeclaration, scope);
205 CompilationAndWeavingContext.leavingPhase(tok);
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);
215 private boolean insideAspect() {
216 if (typeStack.empty())
218 TypeDeclaration typeDecl = typeStack.peek();
219 return isAspect(typeDecl);
222 private boolean isAspect(TypeDeclaration typeDecl) {
223 if (typeDecl instanceof AspectDeclaration)
225 return new AspectJAnnotations(typeDecl.annotations).hasAspectAnnotation;
229 * aspect must be public nested aspect must be static cannot extend a concrete aspect pointcut in perclause must be good.
231 private void validateAspectDeclaration(TypeDeclaration typeDecl) {
232 if (typeStack.size() > 1) {
233 // it's a nested aspect
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
240 typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart, typeDecl.sourceEnd,
241 "inner aspects must be static");
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");
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");
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");
278 Annotation aspectAnnotation = ajAnnotations.aspectAnnotation;
280 int[] pcLoc = new int[2];
281 String perClause = getStringLiteralFor("value", aspectAnnotation, pcLoc);
282 // AspectDeclaration aspectDecl = new
283 // AspectDeclaration(typeDecl.compilationResult);
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;
291 pc.resolve(new EclipseScope(bindings, typeDecl.scope));
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() });
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
305 private void validateAdvice(MethodDeclaration methodDeclaration) {
307 if (!insideAspect()) {
308 methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart, methodDeclaration.sourceEnd,
309 "Advice must be declared inside an aspect type");
312 if (!Modifier.isPublic(methodDeclaration.modifiers)) {
313 methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart, methodDeclaration.sourceEnd,
314 "advice must be public");
317 if (Modifier.isStatic(methodDeclaration.modifiers)) {
318 methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart, methodDeclaration.sourceEnd,
319 "advice can not be declared static");
322 if (ajAnnotations.hasMultipleAdviceAnnotations) {
323 methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.duplicateAdviceAnnotation);
325 if (ajAnnotations.hasPointcutAnnotation) {
326 methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.pointcutAnnotation);
328 if (ajAnnotations.hasAspectAnnotation) {
329 methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.aspectAnnotation);
331 if (ajAnnotations.hasAdviceNameAnnotation) {
332 methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.adviceNameAnnotation);
335 if (ajAnnotations.adviceKind != AdviceKind.Around) {
336 ensureVoidReturnType(methodDeclaration);
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");
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");
364 resolveAndSetPointcut(methodDeclaration, ajAnnotations.adviceAnnotation);
369 * Get the argument names as a string list
372 * @return argument names (possibly empty)
374 private List<String> toArgumentNames(Argument[] arguments) {
375 List<String> names = new ArrayList<>();
376 if (arguments == null) {
379 for (Argument argument : arguments) {
380 names.add(new String(argument.name));
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);
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");
399 PatternParser pp = new PatternParser(pointcutExpression, context);
400 Pointcut pc = pp.parsePointcut();
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() });
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)) {
432 methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
433 methodDeclaration.returnType.sourceEnd, "This advice must return void");
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)
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);
458 ret[i] = new FormalBinding(type, name, i, arg.sourceStart, arg.sourceEnd);
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]);
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();
489 if (!(inAnnotation instanceof NormalAnnotation))
491 NormalAnnotation ann = (NormalAnnotation) inAnnotation;
492 MemberValuePair[] mvps = ann.memberValuePairs;
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());
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");
517 if (methodDeclaration.thrownExceptions != null && methodDeclaration.thrownExceptions.length > 0) {
518 methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart, methodDeclaration.sourceEnd,
519 "pointcuts cannot throw exceptions!");
522 PointcutDeclaration pcDecl = new PointcutDeclaration(unit.compilationResult);
523 copyAllFields(methodDeclaration, pcDecl);
525 if (ajAnnotations.hasAdviceAnnotation) {
526 methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.adviceAnnotation);
528 if (ajAnnotations.hasAspectAnnotation) {
529 methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.aspectAnnotation);
531 if (ajAnnotations.hasAdviceNameAnnotation) {
532 methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.adviceNameAnnotation);
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);
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
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;
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");
573 swap(onType, methodDeclaration, pcDecl);
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();
581 eScope.setLimitedImports(packageName);
583 HasIfPCDVisitor ifFinder = new HasIfPCDVisitor();
584 pc.traverse(ifFinder, null);
585 containsIfPcd = ifFinder.containsIfPcd;
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() });
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))
599 if (CharOperation.equals(booleanType, retType.token))
600 returnsBoolean = true;
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");
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)");
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");
619 if (pcDecl.pointcutDesignator == null) {
620 if (Modifier.isAbstract(methodDeclaration.modifiers) || noValueSupplied // this
626 // those 2 checks makes sense for aop.xml concretization but NOT for
627 // regular abstraction of pointcut
629 // && (methodDeclaration.arguments == null ||
630 // methodDeclaration.arguments.length == 0)) {
634 methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
635 methodDeclaration.returnType.sourceEnd,
636 "Method annotated with @Pointcut() for abstract pointcut must be abstract");
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");
645 private void copyAllFields(MethodDeclaration from, MethodDeclaration to) {
646 to.annotations = from.annotations;
647 to.arguments = from.arguments;
648 to.binding = from.binding;
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;
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;
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;
688 AdviceKind adviceKind = null;
689 Annotation adviceAnnotation = null;
690 Annotation pointcutAnnotation = null;
691 Annotation aspectAnnotation = null;
692 Annotation adviceNameAnnotation = null;
694 Annotation duplicateAdviceAnnotation = null;
695 Annotation duplicatePointcutAnnotation = null;
696 Annotation duplicateAspectAnnotation = null;
698 public AspectJAnnotations(Annotation[] annotations) {
699 if (annotations == null)
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;
730 hasAspectAnnotation = true;
731 aspectAnnotation = annotation;
733 } else if (CharOperation.equals(pointcutSig, sig)) {
734 if (hasPointcutAnnotation) {
735 hasMultiplePointcutAnnotations = true;
736 duplicatePointcutAnnotation = annotation;
738 hasPointcutAnnotation = true;
739 pointcutAnnotation = annotation;
746 public boolean hasAspectJAnnotations() {
747 return hasAdviceAnnotation || hasPointcutAnnotation || hasAdviceNameAnnotation || hasAspectAnnotation;
750 private void addAdviceAnnotation(Annotation annotation) {
751 if (!hasAdviceAnnotation) {
752 hasAdviceAnnotation = true;
753 adviceAnnotation = annotation;
755 hasMultipleAdviceAnnotations = true;
756 duplicateAdviceAnnotation = annotation;
761 private static class HasIfPCDVisitor extends AbstractPatternNodeVisitor {
762 public boolean containsIfPcd = false;
764 public Object visit(IfPointcut node, Object data) {
765 containsIfPcd = true;