All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.aspectj.ajdt.internal.compiler.ast.ValidateAtAspectJAnnotationsVisitor Maven / Gradle / Ivy

Go to download

AspectJ tools most notably contains the AspectJ compiler (AJC). AJC applies aspects to Java classes during compilation, fully replacing Javac for plain Java classes and also compiling native AspectJ or annotation-based @AspectJ syntax. Furthermore, AJC can weave aspects into existing class files in a post-compile binary weaving step. This library is a superset of AspectJ weaver and hence also of AspectJ runtime.

There is a newer version: 1.9.22.1
Show newest version
/* *******************************************************************
 * Copyright (c) 2005 IBM Corporation Ltd
 * All rights reserved.
 * This program and the accompanying materials are made available
 * under the terms of the Eclipse Public License v 2.0
 * which accompanies this distribution and is available at
 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
 *
 * Contributors:
 *     Adrian Colyer  initial implementation
 * ******************************************************************/
package org.aspectj.ajdt.internal.compiler.ast;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import org.aspectj.ajdt.internal.compiler.lookup.EclipseFactory;
import org.aspectj.ajdt.internal.compiler.lookup.EclipseScope;
import org.aspectj.ajdt.internal.core.builder.EclipseSourceContext;
import org.aspectj.bridge.context.CompilationAndWeavingContext;
import org.aspectj.bridge.context.ContextToken;
import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
import org.aspectj.org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Argument;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.NameReference;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.NormalAnnotation;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.StringLiteral;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.aspectj.org.eclipse.jdt.internal.compiler.impl.Constant;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.aspectj.weaver.AdviceKind;
import org.aspectj.weaver.AjAttribute;
import org.aspectj.weaver.ISourceContext;
import org.aspectj.weaver.ResolvedPointcutDefinition;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.patterns.AbstractPatternNodeVisitor;
import org.aspectj.weaver.patterns.FormalBinding;
import org.aspectj.weaver.patterns.IfPointcut;
import org.aspectj.weaver.patterns.ParserException;
import org.aspectj.weaver.patterns.PatternParser;
import org.aspectj.weaver.patterns.Pointcut;

public class ValidateAtAspectJAnnotationsVisitor extends ASTVisitor {

	private static final char[] beforeAdviceSig = "Lorg/aspectj/lang/annotation/Before;".toCharArray();
	private static final char[] afterAdviceSig = "Lorg/aspectj/lang/annotation/After;".toCharArray();
	private static final char[] afterReturningAdviceSig = "Lorg/aspectj/lang/annotation/AfterReturning;".toCharArray();
	private static final char[] afterThrowingAdviceSig = "Lorg/aspectj/lang/annotation/AfterThrowing;".toCharArray();
	private static final char[] aroundAdviceSig = "Lorg/aspectj/lang/annotation/Around;".toCharArray();
	private static final char[] pointcutSig = "Lorg/aspectj/lang/annotation/Pointcut;".toCharArray();
	private static final char[] aspectSig = "Lorg/aspectj/lang/annotation/Aspect;".toCharArray();
	private static final char[] declareParentsSig = "Lorg/aspectj/lang/annotation/DeclareParents;".toCharArray();
	private static final char[] adviceNameSig = "Lorg/aspectj/lang/annotation/AdviceName;".toCharArray();
	// private static final char[] orgAspectJLangAnnotation =
	// "org/aspectj/lang/annotation/".toCharArray();
	private static final char[] voidType = "void".toCharArray();
	private static final char[] booleanType = "boolean".toCharArray();
	private static final char[] joinPoint = "Lorg/aspectj/lang/JoinPoint;".toCharArray();
	private static final char[] joinPointStaticPart = "Lorg/aspectj/lang/JoinPoint$StaticPart;".toCharArray();
	private static final char[] joinPointEnclosingStaticPart = "Lorg/aspectj/lang/JoinPoint$EnclosingStaticPart;".toCharArray();
	private static final char[] proceedingJoinPoint = "Lorg/aspectj/lang/ProceedingJoinPoint;".toCharArray();
	// private static final char[][] adviceSigs = new char[][] {
	// beforeAdviceSig, afterAdviceSig, afterReturningAdviceSig,
	// afterThrowingAdviceSig, aroundAdviceSig };

	private final CompilationUnitDeclaration unit;
	private final Stack typeStack = new Stack<>();
	private AspectJAnnotations ajAnnotations;

	public ValidateAtAspectJAnnotationsVisitor(CompilationUnitDeclaration unit) {
		this.unit = unit;
	}

	public boolean visit(TypeDeclaration localTypeDeclaration, BlockScope scope) {
		typeStack.push(localTypeDeclaration);
		ajAnnotations = new AspectJAnnotations(localTypeDeclaration.annotations);
		checkTypeDeclaration(localTypeDeclaration);
		return true;
	}

	public void endVisit(TypeDeclaration localTypeDeclaration, BlockScope scope) {
		typeStack.pop();
	}

	public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
		typeStack.push(memberTypeDeclaration);
		ajAnnotations = new AspectJAnnotations(memberTypeDeclaration.annotations);
		checkTypeDeclaration(memberTypeDeclaration);
		return true;
	}

	public void endVisit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
		typeStack.pop();
	}

	public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
		typeStack.push(typeDeclaration);
		ajAnnotations = new AspectJAnnotations(typeDeclaration.annotations);
		checkTypeDeclaration(typeDeclaration);
		return true;
	}

	public void endVisit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
		typeStack.pop();
	}

	private void checkTypeDeclaration(TypeDeclaration typeDecl) {
		ContextToken tok = CompilationAndWeavingContext.enteringPhase(
				CompilationAndWeavingContext.VALIDATING_AT_ASPECTJ_ANNOTATIONS, typeDecl.name);
		if (!(typeDecl instanceof AspectDeclaration)) {
			if (ajAnnotations.hasAspectAnnotation) {
				validateAspectDeclaration(typeDecl);
			} else {
				// check that class doesn't extend aspect
				TypeReference parentRef = typeDecl.superclass;
				if (parentRef != null) {
					TypeBinding parentBinding = parentRef.resolvedType;
					if (parentBinding instanceof SourceTypeBinding) {
						SourceTypeBinding parentSTB = (SourceTypeBinding) parentBinding;
						if (parentSTB.scope != null) {
							TypeDeclaration parentDecl = parentSTB.scope.referenceContext;
							if (isAspect(parentDecl)) {
								typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart, typeDecl.sourceEnd,
										"a class cannot extend an aspect");
							}
						}
					}
				}
			}
		} else {
			// check that aspect doesn't have @Aspect annotation, we've already
			// added on ourselves.
			if (ajAnnotations.hasMultipleAspectAnnotations) {
				typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart, typeDecl.sourceEnd,
						"aspects cannot have @Aspect annotation");
			}
		}
		CompilationAndWeavingContext.leavingPhase(tok);
	}

	public boolean visit(FieldDeclaration fieldDeclaration, MethodScope scope) {
		ajAnnotations = new AspectJAnnotations(fieldDeclaration.annotations);
		if (ajAnnotations.hasDeclareParents && !insideAspect()) {
			scope.problemReporter().signalError(fieldDeclaration.sourceStart, fieldDeclaration.sourceEnd,
					"DeclareParents can only be used inside an aspect type");
		}
		return true;
	}

	public boolean visit(MethodDeclaration methodDeclaration, ClassScope scope) {
		if (methodDeclaration.hasErrors()) {
			return false;
		}
		ContextToken tok = CompilationAndWeavingContext.enteringPhase(
				CompilationAndWeavingContext.VALIDATING_AT_ASPECTJ_ANNOTATIONS, methodDeclaration.selector);
		ajAnnotations = new AspectJAnnotations(methodDeclaration.annotations);
		if (!methodDeclaration.getClass().equals(AjMethodDeclaration.class)) {
			// simply test for innapropriate use of annotations on code-style
			// members
			if (methodDeclaration instanceof PointcutDeclaration) {
				if (ajAnnotations.hasMultiplePointcutAnnotations || ajAnnotations.hasAdviceAnnotation
						|| ajAnnotations.hasAspectAnnotation || ajAnnotations.hasAdviceNameAnnotation) {
					methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart,
							methodDeclaration.sourceEnd, "@AspectJ annotations cannot be declared on this aspect member");
				}
			} else if (methodDeclaration instanceof AdviceDeclaration) {
				if (ajAnnotations.hasMultipleAdviceAnnotations || ajAnnotations.hasAspectAnnotation
						|| ajAnnotations.hasPointcutAnnotation) {
					methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart,
							methodDeclaration.sourceEnd, "Only @AdviceName AspectJ annotation allowed on advice");
				}
			} else {
				if (ajAnnotations.hasAspectJAnnotations()) {
					methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart,
							methodDeclaration.sourceEnd, "@AspectJ annotations cannot be declared on this aspect member");
				}
			}
			CompilationAndWeavingContext.leavingPhase(tok);
			return false;
		}

		if (ajAnnotations.hasAdviceAnnotation) {
			validateAdvice(methodDeclaration);
		} else if (ajAnnotations.hasPointcutAnnotation) {
			convertToPointcutDeclaration(methodDeclaration, scope);
		}
		CompilationAndWeavingContext.leavingPhase(tok);
		return false;
	}

	// private boolean isAspectJAnnotation(Annotation ann) {
	// if (ann.resolvedType == null) return false;
	// char[] sig = ann.resolvedType.signature();
	// return CharOperation.contains(orgAspectJLangAnnotation, sig);
	// }

	private boolean insideAspect() {
		if (typeStack.empty())
			return false;
		TypeDeclaration typeDecl = typeStack.peek();
		return isAspect(typeDecl);
	}

	private boolean isAspect(TypeDeclaration typeDecl) {
		if (typeDecl instanceof AspectDeclaration)
			return true;
		return new AspectJAnnotations(typeDecl.annotations).hasAspectAnnotation;
	}

	/**
	 * aspect must be public nested aspect must be static cannot extend a concrete aspect pointcut in perclause must be good.
	 */
	private void validateAspectDeclaration(TypeDeclaration typeDecl) {
		if (typeStack.size() > 1) {
			// it's a nested aspect
			if (
				!Modifier.isStatic(typeDecl.modifiers) &&
				// Inner classes/aspects of interfaces are implicitly static,
				// see https://github.com/eclipse-aspectj/aspectj/issues/162
				(typeDecl.enclosingType.modifiers & ClassFileConstants.AccInterface) == 0
			) {
				typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart, typeDecl.sourceEnd,
						"inner aspects must be static");
				return;
			}
		}

		SourceTypeBinding binding = typeDecl.binding;
		if (binding != null) {
			if (binding.isEnum() || binding.isInterface() || binding.isAnnotationType()) {
				typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart, typeDecl.sourceEnd,
						"only classes can have an @Aspect annotation");
			}
		}

		// FIXME AV - do we really want that
		// if (!Modifier.isPublic(typeDecl.modifiers)) {
		// typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart,
		// typeDecl.sourceEnd,"@Aspect class must be public");
		// }

		TypeReference parentRef = typeDecl.superclass;
		if (parentRef != null) {
			TypeBinding parentBinding = parentRef.resolvedType;
			if (parentBinding instanceof SourceTypeBinding) {
				SourceTypeBinding parentSTB = (SourceTypeBinding) parentBinding;
				if (parentSTB.scope != null) { // scope is null if its a
					// binarytypebinding (in AJ
					// world, thats a subclass of
					// SourceTypeBinding)
					TypeDeclaration parentDecl = parentSTB.scope.referenceContext;
					if (isAspect(parentDecl) && !Modifier.isAbstract(parentDecl.modifiers)) {
						typeDecl.scope.problemReporter().signalError(typeDecl.sourceStart, typeDecl.sourceEnd,
								"cannot extend a concrete aspect");
					}
				}
			}
		}

		Annotation aspectAnnotation = ajAnnotations.aspectAnnotation;

		int[] pcLoc = new int[2];
		String perClause = getStringLiteralFor("value", aspectAnnotation, pcLoc);
		// AspectDeclaration aspectDecl = new
		// AspectDeclaration(typeDecl.compilationResult);

		try {
			if (perClause != null && !perClause.equals("")) {
				ISourceContext context = new EclipseSourceContext(unit.compilationResult, pcLoc[0]);
				Pointcut pc = new PatternParser(perClause, context).maybeParsePerClause();
				FormalBinding[] bindings = FormalBinding.NONE;
				if (pc != null)
					pc.resolve(new EclipseScope(bindings, typeDecl.scope));
			}
		} catch (ParserException pEx) {
			typeDecl.scope.problemReporter().parseError(pcLoc[0] + pEx.getLocation().getStart(),
					pcLoc[0] + pEx.getLocation().getEnd(), -1, perClause.toCharArray(), perClause,
					new String[] { pEx.getMessage() });
		}
	}

	/**
	 * 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
	 * annotations 4) After throwing advice must declare the thrown formal 5) After returning advice must declare the returning
	 * formal 6) Advice must not be static
	 */
	private void validateAdvice(MethodDeclaration methodDeclaration) {

		if (!insideAspect()) {
			methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart, methodDeclaration.sourceEnd,
					"Advice must be declared inside an aspect type");
		}

		if (!Modifier.isPublic(methodDeclaration.modifiers)) {
			methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart, methodDeclaration.sourceEnd,
					"advice must be public");
		}

		if (Modifier.isStatic(methodDeclaration.modifiers)) {
			methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart, methodDeclaration.sourceEnd,
					"advice can not be declared static");
		}

		if (ajAnnotations.hasMultipleAdviceAnnotations) {
			methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.duplicateAdviceAnnotation);
		}
		if (ajAnnotations.hasPointcutAnnotation) {
			methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.pointcutAnnotation);
		}
		if (ajAnnotations.hasAspectAnnotation) {
			methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.aspectAnnotation);
		}
		if (ajAnnotations.hasAdviceNameAnnotation) {
			methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.adviceNameAnnotation);
		}

		if (ajAnnotations.adviceKind != AdviceKind.Around) {
			ensureVoidReturnType(methodDeclaration);
		}

		if (ajAnnotations.adviceKind == AdviceKind.AfterThrowing) {
			int[] throwingLocation = new int[2];
			String thrownFormal = getStringLiteralFor("throwing", ajAnnotations.adviceAnnotation, throwingLocation);
			if (thrownFormal != null) {
				// Argument[] arguments = methodDeclaration.arguments;
				if (!toArgumentNames(methodDeclaration.arguments).contains(thrownFormal)) {
					methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart,
							methodDeclaration.sourceEnd,
							"throwing formal '" + thrownFormal + "' must be declared as a parameter in the advice signature");
				}
			}
		}

		if (ajAnnotations.adviceKind == AdviceKind.AfterReturning) {
			int[] throwingLocation = new int[2];
			String returningFormal = getStringLiteralFor("returning", ajAnnotations.adviceAnnotation, throwingLocation);
			if (returningFormal != null) {
				if (!toArgumentNames(methodDeclaration.arguments).contains(returningFormal)) {
					methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart,
							methodDeclaration.sourceEnd,
							"returning formal '" + returningFormal + "' must be declared as a parameter in the advice signature");
				}
			}
		}

		resolveAndSetPointcut(methodDeclaration, ajAnnotations.adviceAnnotation);

	}

	/**
	 * Get the argument names as a string list
	 *
	 * @param arguments
	 * @return argument names (possibly empty)
	 */
	private List toArgumentNames(Argument[] arguments) {
		List names = new ArrayList<>();
		if (arguments == null) {
			return names;
		} else {
			for (Argument argument : arguments) {
				names.add(new String(argument.name));
			}
			return names;
		}
	}

	private void resolveAndSetPointcut(MethodDeclaration methodDeclaration, Annotation adviceAnn) {
		int[] pcLocation = new int[2];
		String pointcutExpression = getStringLiteralFor("pointcut", adviceAnn, pcLocation);
		if (pointcutExpression == null)
			pointcutExpression = getStringLiteralFor("value", adviceAnn, pcLocation);
		try {
			// +1 to give first char of pointcut string
			ISourceContext context = new EclipseSourceContext(unit.compilationResult, pcLocation[0] + 1);
			if (pointcutExpression == null) {
				methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart,
						methodDeclaration.sourceEnd, "the advice annotation must specify a pointcut value");
				return;
			}
			PatternParser pp = new PatternParser(pointcutExpression, context);
			Pointcut pc = pp.parsePointcut();
			pp.checkEof();
			FormalBinding[] bindings = buildFormalAdviceBindingsFrom(methodDeclaration);
			pc.resolve(new EclipseScope(bindings, methodDeclaration.scope));
			EclipseFactory factory = EclipseFactory.fromScopeLookupEnvironment(methodDeclaration.scope);
			// now create a ResolvedPointcutDefinition,make an attribute out of
			// it, and add it to the method
			UnresolvedType[] paramTypes = new UnresolvedType[bindings.length];
			for (int i = 0; i < paramTypes.length; i++)
				paramTypes[i] = bindings[i].getType();
			ResolvedPointcutDefinition resPcutDef = new ResolvedPointcutDefinition(factory.fromBinding(typeStack.peek().binding),
					methodDeclaration.modifiers, "anonymous", paramTypes, pc);
			AjAttribute attr = new AjAttribute.PointcutDeclarationAttribute(resPcutDef);
			((AjMethodDeclaration) methodDeclaration).addAttribute(new EclipseAttributeAdapter(attr));
		} catch (ParserException pEx) {
			methodDeclaration.scope.problemReporter().parseError(pcLocation[0] + pEx.getLocation().getStart(),
					pcLocation[0] + pEx.getLocation().getEnd(), -1, pointcutExpression.toCharArray(), pointcutExpression,
					new String[] { pEx.getMessage() });
		}
	}

	private void ensureVoidReturnType(MethodDeclaration methodDeclaration) {
		boolean returnsVoid = true;
		if ((methodDeclaration.returnType instanceof SingleTypeReference)) {
			SingleTypeReference retType = (SingleTypeReference) methodDeclaration.returnType;
			if (!CharOperation.equals(voidType, retType.token)) {
				returnsVoid = false;
			}
		} else {
			returnsVoid = false;
		}
		if (!returnsVoid) {
			methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
					methodDeclaration.returnType.sourceEnd, "This advice must return void");
		}
	}

	private FormalBinding[] buildFormalAdviceBindingsFrom(MethodDeclaration mDecl) {
		if (mDecl.arguments == null)
			return FormalBinding.NONE;
		if (mDecl.binding == null)
			return FormalBinding.NONE;
		EclipseFactory factory = EclipseFactory.fromScopeLookupEnvironment(mDecl.scope);
		String extraArgName = maybeGetExtraArgName();
		if (extraArgName == null)
			extraArgName = "";
		FormalBinding[] ret = new FormalBinding[mDecl.arguments.length];
		for (int i = 0; i < mDecl.arguments.length; i++) {
			Argument arg = mDecl.arguments[i];
			String name = new String(arg.name);
			TypeBinding argTypeBinding = mDecl.binding.parameters[i];
			UnresolvedType type = factory.fromBinding(argTypeBinding);
			if (CharOperation.equals(joinPoint, argTypeBinding.signature())
					|| CharOperation.equals(joinPointStaticPart, argTypeBinding.signature())
					|| CharOperation.equals(joinPointEnclosingStaticPart, argTypeBinding.signature())
					|| CharOperation.equals(proceedingJoinPoint, argTypeBinding.signature()) || name.equals(extraArgName)) {
				ret[i] = new FormalBinding.ImplicitFormalBinding(type, name, i);
			} else {
				ret[i] = new FormalBinding(type, name, i, arg.sourceStart, arg.sourceEnd);
			}
		}
		return ret;
	}

	private String maybeGetExtraArgName() {
		String argName = null;
		if (ajAnnotations.adviceKind == AdviceKind.AfterReturning) {
			argName = getStringLiteralFor("returning", ajAnnotations.adviceAnnotation, new int[2]);
		} else if (ajAnnotations.adviceKind == AdviceKind.AfterThrowing) {
			argName = getStringLiteralFor("throwing", ajAnnotations.adviceAnnotation, new int[2]);
		}
		return argName;
	}

	private String getStringLiteralFor(String memberName, Annotation inAnnotation, int[] location) {
		if (inAnnotation instanceof SingleMemberAnnotation && memberName.equals("value")) {
			SingleMemberAnnotation sma = (SingleMemberAnnotation) inAnnotation;
			if (sma.memberValue instanceof StringLiteral) {
				StringLiteral sv = (StringLiteral) sma.memberValue;
				location[0] = sv.sourceStart;
				location[1] = sv.sourceEnd;
				return new String(sv.source());
			} else if (sma.memberValue instanceof NameReference
					&& (((NameReference) sma.memberValue).binding instanceof FieldBinding)) {
				Binding b = ((NameReference) sma.memberValue).binding;
				Constant c = ((FieldBinding) b).constant();
				return c.stringValue();
			}
		}
		if (!(inAnnotation instanceof NormalAnnotation))
			return null;
		NormalAnnotation ann = (NormalAnnotation) inAnnotation;
		MemberValuePair[] mvps = ann.memberValuePairs;
		if (mvps == null)
			return null;
		for (MemberValuePair mvp : mvps) {
			if (CharOperation.equals(memberName.toCharArray(), mvp.name)) {
				if (mvp.value instanceof StringLiteral) {
					StringLiteral sv = (StringLiteral) mvp.value;
					location[0] = sv.sourceStart;
					location[1] = sv.sourceEnd;
					return new String(sv.source());
				}
			}
		}
		return null;
	}

	private void convertToPointcutDeclaration(MethodDeclaration methodDeclaration, ClassScope scope) {
		TypeDeclaration typeDecl = typeStack.peek();
		if (typeDecl.binding != null) {
			if (!typeDecl.binding.isClass()) {
				methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart, methodDeclaration.sourceEnd,
						"pointcuts can only be declared in a class or an aspect");
			}
		}

		if (methodDeclaration.thrownExceptions != null && methodDeclaration.thrownExceptions.length > 0) {
			methodDeclaration.scope.problemReporter().signalError(methodDeclaration.sourceStart, methodDeclaration.sourceEnd,
					"pointcuts cannot throw exceptions!");
		}

		PointcutDeclaration pcDecl = new PointcutDeclaration(unit.compilationResult);
		copyAllFields(methodDeclaration, pcDecl);

		if (ajAnnotations.hasAdviceAnnotation) {
			methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.adviceAnnotation);
		}
		if (ajAnnotations.hasAspectAnnotation) {
			methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.aspectAnnotation);
		}
		if (ajAnnotations.hasAdviceNameAnnotation) {
			methodDeclaration.scope.problemReporter().disallowedTargetForAnnotation(ajAnnotations.adviceNameAnnotation);
		}

		boolean noValueSupplied = true;
		boolean containsIfPcd = false;
		boolean isIfTrueOrFalse = false;
		int[] pcLocation = new int[2];
		String pointcutExpression = getStringLiteralFor("value", ajAnnotations.pointcutAnnotation, pcLocation);
		try {
			ISourceContext context = new EclipseSourceContext(unit.compilationResult, pcLocation[0]);
			Pointcut pc = null;// abstract
			if (pointcutExpression == null || pointcutExpression.length() == 0) {
				noValueSupplied = true; // matches nothing pointcut
			} else {
				noValueSupplied = false;
				pc = new PatternParser(pointcutExpression, context).parsePointcut();
				if (pc instanceof IfPointcut) {
					if (((IfPointcut)pc).alwaysFalse() || ((IfPointcut)pc).alwaysTrue()) {
						isIfTrueOrFalse = true;
					}
				}
			}
			pcDecl.pointcutDesignator = (pc == null) ? null : new PointcutDesignator(pc);
			pcDecl.setGenerateSyntheticPointcutMethod();
			TypeDeclaration onType = typeStack.peek();
			pcDecl.postParse(onType);
			// EclipseFactory factory =
			// EclipseFactory.fromScopeLookupEnvironment
			// (methodDeclaration.scope);
			// int argsLength = methodDeclaration.arguments == null ? 0 :
			// methodDeclaration.arguments.length;
			FormalBinding[] bindings = buildFormalAdviceBindingsFrom(methodDeclaration);
			// FormalBinding[] bindings = new FormalBinding[argsLength];
			// for (int i = 0, len = bindings.length; i < len; i++) {
			// Argument arg = methodDeclaration.arguments[i];
			// String name = new String(arg.name);
			// UnresolvedType type =
			// factory.fromBinding(methodDeclaration.binding.parameters[i]);
			// bindings[i] = new FormalBinding(type, name, i, arg.sourceStart,
			// arg.sourceEnd, "unknown");
			// }
			swap(onType, methodDeclaration, pcDecl);
			if (pc != null) {
				// has an expression
				EclipseScope eScope = new EclipseScope(bindings, methodDeclaration.scope);
				char[] packageName = null;
				if (typeDecl.binding != null && typeDecl.binding.getPackage() != null) {
					packageName = typeDecl.binding.getPackage().readableName();
				}
				eScope.setLimitedImports(packageName);
				pc.resolve(eScope);
				HasIfPCDVisitor ifFinder = new HasIfPCDVisitor();
				pc.traverse(ifFinder, null);
				containsIfPcd = ifFinder.containsIfPcd;
			}
		} catch (ParserException pEx) {
			methodDeclaration.scope.problemReporter().parseError(pcLocation[0] + pEx.getLocation().getStart(),
					pcLocation[0] + pEx.getLocation().getEnd(), -1, pointcutExpression.toCharArray(), pointcutExpression,
					new String[] { pEx.getMessage() });
		}

		boolean returnsVoid = false;
		boolean returnsBoolean = false;
		if ((methodDeclaration.returnType instanceof SingleTypeReference)) {
			SingleTypeReference retType = (SingleTypeReference) methodDeclaration.returnType;
			if (CharOperation.equals(voidType, retType.token))
				returnsVoid = true;
			if (CharOperation.equals(booleanType, retType.token))
				returnsBoolean = true;
		}
		if (!returnsVoid && !containsIfPcd) {
			methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
					methodDeclaration.returnType.sourceEnd,
					"Methods annotated with @Pointcut must return void unless the pointcut contains an if() expression");
		}
		if (!returnsBoolean && containsIfPcd && !isIfTrueOrFalse) {
			methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
					methodDeclaration.returnType.sourceEnd,
					"Methods annotated with @Pointcut must return boolean when the pointcut contains an if() expression unless it is if(false) or if(true)");
		}

		if (methodDeclaration.statements != null && methodDeclaration.statements.length > 0 && !containsIfPcd) {
			methodDeclaration.scope.problemReporter()
					.signalError(methodDeclaration.returnType.sourceStart, methodDeclaration.returnType.sourceEnd,
							"Pointcuts without an if() expression should have an empty method body");
		}

		if (pcDecl.pointcutDesignator == null) {
			if (Modifier.isAbstract(methodDeclaration.modifiers) || noValueSupplied // this
			// is
			// a
			// matches
			// nothing
			// pointcut
			// those 2 checks makes sense for aop.xml concretization but NOT for
			// regular abstraction of pointcut
			// && returnsVoid
			// && (methodDeclaration.arguments == null ||
			// methodDeclaration.arguments.length == 0)) {
			) {
				// fine
			} else {
				methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
						methodDeclaration.returnType.sourceEnd,
						"Method annotated with @Pointcut() for abstract pointcut must be abstract");
			}
		} else if (Modifier.isAbstract(methodDeclaration.modifiers)) {
			methodDeclaration.scope.problemReporter().signalError(methodDeclaration.returnType.sourceStart,
					methodDeclaration.returnType.sourceEnd,
					"Method annotated with non abstract @Pointcut(\"" + pointcutExpression + "\") is abstract");
		}
	}

	private void copyAllFields(MethodDeclaration from, MethodDeclaration to) {
		to.annotations = from.annotations;
		to.arguments = from.arguments;
		to.binding = from.binding;
		to.bits = from.bits;
		to.bodyEnd = from.bodyEnd;
		to.bodyStart = from.bodyStart;
		to.declarationSourceEnd = from.declarationSourceEnd;
		to.declarationSourceStart = from.declarationSourceStart;
		to.explicitDeclarations = from.explicitDeclarations;
		to.ignoreFurtherInvestigation = from.ignoreFurtherInvestigation;
		to.javadoc = from.javadoc;
		to.modifiers = from.modifiers;
		to.modifiersSourceStart = from.modifiersSourceStart;
		to.returnType = from.returnType;
		to.scope = from.scope;
		to.selector = from.selector;
		to.sourceEnd = from.sourceEnd;
		to.sourceStart = from.sourceStart;
		to.statements = from.statements;
		to.thrownExceptions = from.thrownExceptions;
		to.typeParameters = from.typeParameters;
	}

	private void swap(TypeDeclaration inType, MethodDeclaration thisDeclaration, MethodDeclaration forThatDeclaration) {
		for (int i = 0; i < inType.methods.length; i++) {
			if (inType.methods[i] == thisDeclaration) {
				inType.methods[i] = forThatDeclaration;
				break;
			}
		}
	}

	private static class AspectJAnnotations {
		boolean hasAdviceAnnotation = false;
		boolean hasPointcutAnnotation = false;
		boolean hasAspectAnnotation = false;
		boolean hasAdviceNameAnnotation = false;
		boolean hasDeclareParents = false;
		boolean hasMultipleAdviceAnnotations = false;
		boolean hasMultiplePointcutAnnotations = false;
		boolean hasMultipleAspectAnnotations = false;

		AdviceKind adviceKind = null;
		Annotation adviceAnnotation = null;
		Annotation pointcutAnnotation = null;
		Annotation aspectAnnotation = null;
		Annotation adviceNameAnnotation = null;

		Annotation duplicateAdviceAnnotation = null;
		Annotation duplicatePointcutAnnotation = null;
		Annotation duplicateAspectAnnotation = null;

		public AspectJAnnotations(Annotation[] annotations) {
			if (annotations == null)
				return;
			for (Annotation annotation : annotations) {
				if (annotation.resolvedType == null)
					continue; // user messed up annotation declaration
				char[] sig = annotation.resolvedType.signature();
				if (CharOperation.equals(afterAdviceSig, sig)) {
					adviceKind = AdviceKind.After;
					addAdviceAnnotation(annotation);
				} else if (CharOperation.equals(afterReturningAdviceSig, sig)) {
					adviceKind = AdviceKind.AfterReturning;
					addAdviceAnnotation(annotation);
				} else if (CharOperation.equals(afterThrowingAdviceSig, sig)) {
					adviceKind = AdviceKind.AfterThrowing;
					addAdviceAnnotation(annotation);
				} else if (CharOperation.equals(beforeAdviceSig, sig)) {
					adviceKind = AdviceKind.Before;
					addAdviceAnnotation(annotation);
				} else if (CharOperation.equals(aroundAdviceSig, sig)) {
					adviceKind = AdviceKind.Around;
					addAdviceAnnotation(annotation);
				} else if (CharOperation.equals(adviceNameSig, sig)) {
					hasAdviceNameAnnotation = true;
					adviceNameAnnotation = annotation;
				} else if (CharOperation.equals(declareParentsSig, sig)) {
					hasDeclareParents = true;
				} else if (CharOperation.equals(aspectSig, sig)) {
					if (hasAspectAnnotation) {
						hasMultipleAspectAnnotations = true;
						duplicateAspectAnnotation = annotation;
					} else {
						hasAspectAnnotation = true;
						aspectAnnotation = annotation;
					}
				} else if (CharOperation.equals(pointcutSig, sig)) {
					if (hasPointcutAnnotation) {
						hasMultiplePointcutAnnotations = true;
						duplicatePointcutAnnotation = annotation;
					} else {
						hasPointcutAnnotation = true;
						pointcutAnnotation = annotation;
					}

				}
			}
		}

		public boolean hasAspectJAnnotations() {
			return hasAdviceAnnotation || hasPointcutAnnotation || hasAdviceNameAnnotation || hasAspectAnnotation;
		}

		private void addAdviceAnnotation(Annotation annotation) {
			if (!hasAdviceAnnotation) {
				hasAdviceAnnotation = true;
				adviceAnnotation = annotation;
			} else {
				hasMultipleAdviceAnnotations = true;
				duplicateAdviceAnnotation = annotation;
			}
		}
	}

	private static class HasIfPCDVisitor extends AbstractPatternNodeVisitor {
		public boolean containsIfPcd = false;

		public Object visit(IfPointcut node, Object data) {
			containsIfPcd = true;
			return data;
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy