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

org.aspectj.weaver.bcel.AtAjAttributes Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2005 Contributors.
 * 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:
 * initial implementation              Alexandre Vasseur
 *******************************************************************************/
package org.aspectj.weaver.bcel;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import org.aspectj.apache.bcel.Constants;
import org.aspectj.apache.bcel.classfile.Attribute;
import org.aspectj.apache.bcel.classfile.Constant;
import org.aspectj.apache.bcel.classfile.ConstantUtf8;
import org.aspectj.apache.bcel.classfile.Field;
import org.aspectj.apache.bcel.classfile.JavaClass;
import org.aspectj.apache.bcel.classfile.LocalVariable;
import org.aspectj.apache.bcel.classfile.LocalVariableTable;
import org.aspectj.apache.bcel.classfile.Method;
import org.aspectj.apache.bcel.classfile.Unknown;
import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen;
import org.aspectj.apache.bcel.classfile.annotation.ArrayElementValue;
import org.aspectj.apache.bcel.classfile.annotation.ClassElementValue;
import org.aspectj.apache.bcel.classfile.annotation.ElementValue;
import org.aspectj.apache.bcel.classfile.annotation.NameValuePair;
import org.aspectj.apache.bcel.classfile.annotation.RuntimeAnnos;
import org.aspectj.apache.bcel.classfile.annotation.RuntimeVisAnnos;
import org.aspectj.apache.bcel.generic.Type;
import org.aspectj.asm.AsmManager;
import org.aspectj.asm.IHierarchy;
import org.aspectj.asm.IProgramElement;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.ISourceLocation;
import org.aspectj.bridge.Message;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.weaver.Advice;
import org.aspectj.weaver.AdviceKind;
import org.aspectj.weaver.AjAttribute;
import org.aspectj.weaver.AjAttribute.WeaverVersionInfo;
import org.aspectj.weaver.AjcMemberMaker;
import org.aspectj.weaver.BindingScope;
import org.aspectj.weaver.ISourceContext;
import org.aspectj.weaver.MethodDelegateTypeMunger;
import org.aspectj.weaver.NameMangler;
import org.aspectj.weaver.ReferenceType;
import org.aspectj.weaver.ReferenceTypeDelegate;
import org.aspectj.weaver.ResolvedMember;
import org.aspectj.weaver.ResolvedPointcutDefinition;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.VersionedDataInputStream;
import org.aspectj.weaver.WeaverMessages;
import org.aspectj.weaver.World;
import org.aspectj.weaver.patterns.DeclareErrorOrWarning;
import org.aspectj.weaver.patterns.DeclareParents;
import org.aspectj.weaver.patterns.DeclareParentsMixin;
import org.aspectj.weaver.patterns.DeclarePrecedence;
import org.aspectj.weaver.patterns.FormalBinding;
import org.aspectj.weaver.patterns.IScope;
import org.aspectj.weaver.patterns.ParserException;
import org.aspectj.weaver.patterns.PatternParser;
import org.aspectj.weaver.patterns.PerCflow;
import org.aspectj.weaver.patterns.PerClause;
import org.aspectj.weaver.patterns.PerFromSuper;
import org.aspectj.weaver.patterns.PerObject;
import org.aspectj.weaver.patterns.PerSingleton;
import org.aspectj.weaver.patterns.PerTypeWithin;
import org.aspectj.weaver.patterns.Pointcut;
import org.aspectj.weaver.patterns.TypePattern;

/**
 * Annotation defined aspect reader. Reads the Java 5 annotations and turns them into AjAttributes
 *
 * @author Alexandre Vasseur (alex AT gnilux DOT com)
 */
public class AtAjAttributes {

	private final static List NO_ATTRIBUTES = Collections.emptyList();
	private final static String[] EMPTY_STRINGS = new String[0];
	private final static String VALUE = "value";
	private final static String ARGNAMES = "argNames";
	private final static String POINTCUT = "pointcut";
	private final static String THROWING = "throwing";
	private final static String RETURNING = "returning";
	private final static String STRING_DESC = "Ljava/lang/String;";
	private final static String ASPECTJ_ANNOTATION_PACKAGE = "org.aspectj.lang.annotation";
	private final static char PACKAGE_INITIAL_CHAR = ASPECTJ_ANNOTATION_PACKAGE.charAt(0);

	/**
	 * A struct that allows to add extra arguments without always breaking the API
	 */
	private static class AjAttributeStruct {

		/**
		 * The list of AjAttribute.XXX that we are populating from the @AJ read
		 */
		List ajAttributes = new ArrayList<>();

		/**
		 * The resolved type (class) for which we are reading @AJ for (be it class, method, field annotations)
		 */
		final ResolvedType enclosingType;

		final ISourceContext context;
		final IMessageHandler handler;

		public AjAttributeStruct(ResolvedType type, ISourceContext sourceContext, IMessageHandler messageHandler) {
			enclosingType = type;
			context = sourceContext;
			handler = messageHandler;
		}
	}

	/**
	 * A struct when we read @AJ on method
	 *
	 * @author Alexandre Vasseur
	 */
	private static class AjAttributeMethodStruct extends AjAttributeStruct {

		// argument names used for formal binding
		private String[] m_argumentNamesLazy = null;
		public String unparsedArgumentNames = null; // Set only if discovered as
		// argNames attribute of
		// annotation

		final Method method;
		final BcelMethod bMethod;

		public AjAttributeMethodStruct(Method method, BcelMethod bMethod, ResolvedType type, ISourceContext sourceContext,
				IMessageHandler messageHandler) {
			super(type, sourceContext, messageHandler);
			this.method = method;
			this.bMethod = bMethod;
		}

		public String[] getArgumentNames() {
			if (m_argumentNamesLazy == null) {
				m_argumentNamesLazy = getMethodArgumentNames(method, unparsedArgumentNames, this);
			}
			return m_argumentNamesLazy;
		}
	}

	/**
	 * A struct when we read @AJ on field
	 */
	private static class AjAttributeFieldStruct extends AjAttributeStruct {

		final Field field;

		// final BcelField bField;

		public AjAttributeFieldStruct(Field field, BcelField bField, ResolvedType type, ISourceContext sourceContext,
				IMessageHandler messageHandler) {
			super(type, sourceContext, messageHandler);
			this.field = field;
			// this.bField = bField;
		}
	}

	/**
	 * Annotations are RuntimeVisible only. This allow us to not visit RuntimeInvisible ones.
	 *
	 * @param attribute
	 * @return true if runtime visible annotation
	 */
	public static boolean acceptAttribute(Attribute attribute) {
		return (attribute instanceof RuntimeVisAnnos);
	}

	/**
	 * Extract class level annotations and turn them into AjAttributes.
	 *
	 * @param javaClass
	 * @param type
	 * @param context
	 * @param msgHandler
	 * @return list of AjAttributes
	 */
	public static List readAj5ClassAttributes(AsmManager model, JavaClass javaClass, ReferenceType type,
			ISourceContext context, IMessageHandler msgHandler, boolean isCodeStyleAspect) {
		boolean ignoreThisClass = javaClass.getClassName().charAt(0) == PACKAGE_INITIAL_CHAR
				&& javaClass.getClassName().startsWith(ASPECTJ_ANNOTATION_PACKAGE);
		if (ignoreThisClass) {
			return NO_ATTRIBUTES;
		}
		boolean containsPointcut = false;
		boolean containsAnnotationClassReference = false;
		Constant[] cpool = javaClass.getConstantPool().getConstantPool();
		for (Constant constant : cpool) {
			if (constant != null && constant.getTag() == Constants.CONSTANT_Utf8) {
				String constantValue = ((ConstantUtf8) constant).getValue();
				if (constantValue.length() > 28 && constantValue.charAt(1) == PACKAGE_INITIAL_CHAR) {
					if (constantValue.startsWith("Lorg/aspectj/lang/annotation")) {
						containsAnnotationClassReference = true;
						if ("Lorg/aspectj/lang/annotation/DeclareAnnotation;".equals(constantValue)) {
							msgHandler.handleMessage(new Message(
									"Found @DeclareAnnotation while current release does not support it (see '" + type.getName()
											+ "')", IMessage.WARNING, null, type.getSourceLocation()));
						}
						if ("Lorg/aspectj/lang/annotation/Pointcut;".equals(constantValue)) {
							containsPointcut = true;
						}
					}

				}
			}
		}
		if (!containsAnnotationClassReference) {
			return NO_ATTRIBUTES;
		}

		AjAttributeStruct struct = new AjAttributeStruct(type, context, msgHandler);
		Attribute[] attributes = javaClass.getAttributes();
		boolean hasAtAspectAnnotation = false;
		boolean hasAtPrecedenceAnnotation = false;

		WeaverVersionInfo wvinfo = null;
		for (Attribute attribute : attributes) {
			if (acceptAttribute(attribute)) {
				RuntimeAnnos rvs = (RuntimeAnnos) attribute;
				// we don't need to look for several attribute occurrences since
				// it cannot happen as per JSR175
				if (!isCodeStyleAspect && !javaClass.isInterface()) {
					hasAtAspectAnnotation = handleAspectAnnotation(rvs, struct);
					// TODO AV - if put outside the if isCodeStyleAspect then we
					// would enable mix style
					hasAtPrecedenceAnnotation = handlePrecedenceAnnotation(rvs, struct);
				}
				// there can only be one RuntimeVisible bytecode attribute
				break;
			}
		}
		for (int i = attributes.length - 1; i >= 0; i--) {
			Attribute attribute = attributes[i];
			if (attribute.getName().equals(WeaverVersionInfo.AttributeName)) {
				try {
					VersionedDataInputStream s = new VersionedDataInputStream(new ByteArrayInputStream(
							((Unknown) attribute).getBytes()), null);
					wvinfo = WeaverVersionInfo.read(s);
					struct.ajAttributes.add(0, wvinfo);
				} catch (IOException ioe) {
					ioe.printStackTrace();
				}
			}
		}
		if (wvinfo == null) {
			// If we are in here due to a resetState() call (presumably because of reweavable state processing), the
			// original type delegate will have been set with a version but that version will be missing from
			// the new set of attributes (looks like a bug where the version attribute was not included in the
			// data compressed into the attribute). So rather than 'defaulting' to current, we should use one
			// if it set on the delegate for the type.
			ReferenceTypeDelegate delegate = type.getDelegate();
			if (delegate instanceof BcelObjectType) {
				wvinfo = ((BcelObjectType) delegate).getWeaverVersionAttribute();
				if (wvinfo != null) {
					if (wvinfo.getMajorVersion() != WeaverVersionInfo.WEAVER_VERSION_MAJOR_UNKNOWN) {
						// use this one
						struct.ajAttributes.add(0, wvinfo);
					} else {
						wvinfo = null;
					}
				}
			}
			if (wvinfo == null) {
				struct.ajAttributes.add(0, wvinfo = new AjAttribute.WeaverVersionInfo());
			}
		}

		// basic semantic check
		if (hasAtPrecedenceAnnotation && !hasAtAspectAnnotation) {
			msgHandler.handleMessage(new Message("Found @DeclarePrecedence on a non @Aspect type '" + type.getName() + "'",
					IMessage.WARNING, null, type.getSourceLocation()));
			// bypass what we have read
			return NO_ATTRIBUTES;
		}

		// the following block will not detect @Pointcut in non @Aspect types
		// for optimization purpose
		if (!(hasAtAspectAnnotation || isCodeStyleAspect) && !containsPointcut) {
			return NO_ATTRIBUTES;
		}

		// FIXME AV - turn on when ajcMightHaveAspect
		// if (hasAtAspectAnnotation && type.isInterface()) {
		// msgHandler.handleMessage(
		// new Message(
		// "Found @Aspect on an interface type '" + type.getName() + "'",
		// IMessage.WARNING,
		// null,
		// type.getSourceLocation()
		// )
		// );
		// // bypass what we have read
		// return EMPTY_LIST;
		// }

		// semantic check: @Aspect must be public
		// FIXME AV - do we really want to enforce that?
		// if (hasAtAspectAnnotation && !javaClass.isPublic()) {
		// msgHandler.handleMessage(
		// new Message(
		// "Found @Aspect annotation on a non public class '" +
		// javaClass.getClassName() + "'",
		// IMessage.ERROR,
		// null,
		// type.getSourceLocation()
		// )
		// );
		// return EMPTY_LIST;
		// }

		// code style pointcuts are class attributes
		// we need to gather the @AJ pointcut right now and not at method level
		// annotation extraction time
		// in order to be able to resolve the pointcut references later on
		// we don't need to look in super class, the pointcut reference in the
		// grammar will do it

		for (int i = 0; i < javaClass.getMethods().length; i++) {
			Method method = javaClass.getMethods()[i];
			if (method.getName().startsWith(NameMangler.PREFIX)) {
				continue; // already dealt with by ajc...
			}
			// FIXME alex optimize, this method struct will gets recreated for
			// advice extraction
			AjAttributeMethodStruct mstruct = null;
			boolean processedPointcut = false;
			Attribute[] mattributes = method.getAttributes();
			for (Attribute mattribute : mattributes) {
				if (acceptAttribute(mattribute)) {
					// TODO speed all this nonsense up rather than looking
					// through all the annotations every time
					// same for fields
					mstruct = new AjAttributeMethodStruct(method, null, type, context, msgHandler);
					processedPointcut = handlePointcutAnnotation((RuntimeAnnos) mattribute, mstruct);
					if (!processedPointcut) {
						processedPointcut = handleDeclareMixinAnnotation((RuntimeAnnos) mattribute, mstruct);
					}
					// there can only be one RuntimeVisible bytecode attribute
					break;
				}
			}
			if (processedPointcut) {
				struct.ajAttributes.addAll(mstruct.ajAttributes);
			}
		}

		// code style declare error / warning / implements / parents are field
		// attributes
		Field[] fs = javaClass.getFields();
		for (Field field : fs) {
			if (field.getName().startsWith(NameMangler.PREFIX)) {
				continue; // already dealt with by ajc...
			}
			// FIXME alex optimize, this method struct will gets recreated for
			// advice extraction
			AjAttributeFieldStruct fstruct = new AjAttributeFieldStruct(field, null, type, context, msgHandler);
			Attribute[] fattributes = field.getAttributes();

			for (Attribute fattribute : fattributes) {
				if (acceptAttribute(fattribute)) {
					RuntimeAnnos frvs = (RuntimeAnnos) fattribute;
					if (handleDeclareErrorOrWarningAnnotation(model, frvs, fstruct)
							|| handleDeclareParentsAnnotation(frvs, fstruct)) {
						// semantic check - must be in an @Aspect [remove if
						// previous block bypassed in advance]
						if (!type.isAnnotationStyleAspect() && !isCodeStyleAspect) {
							msgHandler.handleMessage(new Message("Found @AspectJ annotations in a non @Aspect type '"
									+ type.getName() + "'", IMessage.WARNING, null, type.getSourceLocation()));
							// go ahead
						}
					}
					// there can only be one RuntimeVisible bytecode attribute
					break;
				}
			}
			struct.ajAttributes.addAll(fstruct.ajAttributes);
		}

		return struct.ajAttributes;
	}

	/**
	 * Extract method level annotations and turn them into AjAttributes.
	 *
	 * @param method
	 * @param type
	 * @param context
	 * @param msgHandler
	 * @return list of AjAttributes
	 */
	public static List readAj5MethodAttributes(Method method, BcelMethod bMethod, ResolvedType type,
			ResolvedPointcutDefinition preResolvedPointcut, ISourceContext context, IMessageHandler msgHandler) {
		if (method.getName().startsWith(NameMangler.PREFIX)) {
			return Collections.emptyList(); // already dealt with by ajc...
		}

		AjAttributeMethodStruct struct = new AjAttributeMethodStruct(method, bMethod, type, context, msgHandler);
		Attribute[] attributes = method.getAttributes();

		// we remember if we found one @AJ annotation for minimal semantic error
		// reporting
		// the real reporting beeing done thru AJDT and the compiler mapping @AJ
		// to AjAtttribute
		// or thru APT
		//
		// Note: we could actually skip the whole thing if type is not itself an
		// @Aspect
		// but then we would not see any warning. We do bypass for pointcut but
		// not for advice since it would
		// be too silent.
		boolean hasAtAspectJAnnotation = false;
		boolean hasAtAspectJAnnotationMustReturnVoid = false;
		for (Attribute attribute : attributes) {
			try {
				if (acceptAttribute(attribute)) {
					RuntimeAnnos rvs = (RuntimeAnnos) attribute;
					hasAtAspectJAnnotationMustReturnVoid = hasAtAspectJAnnotationMustReturnVoid
							|| handleBeforeAnnotation(rvs, struct, preResolvedPointcut);
					hasAtAspectJAnnotationMustReturnVoid = hasAtAspectJAnnotationMustReturnVoid
							|| handleAfterAnnotation(rvs, struct, preResolvedPointcut);
					hasAtAspectJAnnotationMustReturnVoid = hasAtAspectJAnnotationMustReturnVoid
							|| handleAfterReturningAnnotation(rvs, struct, preResolvedPointcut, bMethod);
					hasAtAspectJAnnotationMustReturnVoid = hasAtAspectJAnnotationMustReturnVoid
							|| handleAfterThrowingAnnotation(rvs, struct, preResolvedPointcut, bMethod);
					hasAtAspectJAnnotation = hasAtAspectJAnnotation || handleAroundAnnotation(rvs, struct, preResolvedPointcut);
					// there can only be one RuntimeVisible bytecode attribute
					break;
				}
			} catch (ReturningFormalNotDeclaredInAdviceSignatureException e) {
				msgHandler.handleMessage(new Message(WeaverMessages.format(WeaverMessages.RETURNING_FORMAL_NOT_DECLARED_IN_ADVICE,
						e.getFormalName()), IMessage.ERROR, null, bMethod.getSourceLocation()));
			} catch (ThrownFormalNotDeclaredInAdviceSignatureException e) {
				msgHandler.handleMessage(new Message(WeaverMessages.format(WeaverMessages.THROWN_FORMAL_NOT_DECLARED_IN_ADVICE,
						e.getFormalName()), IMessage.ERROR, null, bMethod.getSourceLocation()));
			}
		}
		hasAtAspectJAnnotation = hasAtAspectJAnnotation || hasAtAspectJAnnotationMustReturnVoid;

		// semantic check - must be in an @Aspect [remove if previous block
		// bypassed in advance]
		if (hasAtAspectJAnnotation && !type.isAspect()) { // isAnnotationStyleAspect())
			// {
			msgHandler.handleMessage(new Message("Found @AspectJ annotations in a non @Aspect type '" + type.getName() + "'",
					IMessage.WARNING, null, type.getSourceLocation()));
			// go ahead
		}
		// semantic check - advice must be public
		if (hasAtAspectJAnnotation && !struct.method.isPublic()) {
			msgHandler.handleMessage(new Message("Found @AspectJ annotation on a non public advice '"
					+ methodToString(struct.method) + "'", IMessage.ERROR, null, type.getSourceLocation()));
			// go ahead
		}

		// semantic check - advice must not be static
		if (hasAtAspectJAnnotation && struct.method.isStatic()) {
			msgHandler.handleMessage(MessageUtil.error("Advice cannot be declared static '" + methodToString(struct.method) + "'",
					type.getSourceLocation()));
			// new Message(
			// "Advice cannot be declared static '" +
			// methodToString(struct.method) + "'",
			// IMessage.ERROR,
			// null,
			// type.getSourceLocation()
			// )
			// );
			// go ahead
		}

		// semantic check for non around advice must return void
		if (hasAtAspectJAnnotationMustReturnVoid && !Type.VOID.equals(struct.method.getReturnType())) {
			msgHandler.handleMessage(new Message("Found @AspectJ annotation on a non around advice not returning void '"
					+ methodToString(struct.method) + "'", IMessage.ERROR, null, type.getSourceLocation()));
			// go ahead
		}

		return struct.ajAttributes;
	}

	/**
	 * Extract field level annotations and turn them into AjAttributes.
	 *
	 * @param field
	 * @param type
	 * @param context
	 * @param msgHandler
	 * @return list of AjAttributes, always empty for now
	 */
	public static List readAj5FieldAttributes(Field field, BcelField bField, ResolvedType type,
			ISourceContext context, IMessageHandler msgHandler) {
		// Note: field annotation are for ITD and DEOW - processed at class
		// level directly
		return Collections.emptyList();
	}

	/**
	 * Read @Aspect
	 *
	 * @param runtimeAnnotations
	 * @param struct
	 * @return true if found
	 */
	private static boolean handleAspectAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeStruct struct) {
		AnnotationGen aspect = getAnnotation(runtimeAnnotations, AjcMemberMaker.ASPECT_ANNOTATION);
		if (aspect != null) {
			// semantic check for inheritance (only one level up)
			boolean extendsAspect = false;
			if (!"java.lang.Object".equals(struct.enclosingType.getSuperclass().getName())) {
				if (!struct.enclosingType.getSuperclass().isAbstract() && struct.enclosingType.getSuperclass().isAspect()) {
					reportError("cannot extend a concrete aspect", struct);
					return false;
				}
				extendsAspect = struct.enclosingType.getSuperclass().isAspect();
			}

			NameValuePair aspectPerClause = getAnnotationElement(aspect, VALUE);
			final PerClause perClause;
			if (aspectPerClause == null) {
				// empty value means singleton unless inherited
				if (!extendsAspect) {
					perClause = new PerSingleton();
				} else {
					perClause = new PerFromSuper(struct.enclosingType.getSuperclass().getPerClause().getKind());
				}
			} else {
				String perX = aspectPerClause.getValue().stringifyValue();
				if (perX == null || perX.length() <= 0) {
					perClause = new PerSingleton();
				} else {
					perClause = parsePerClausePointcut(perX, struct);
				}
			}
			if (perClause == null) {
				// could not parse it, ignore the aspect
				return false;
			} else {
				perClause.setLocation(struct.context, -1, -1);// struct.context.getOffset(),
				// struct.context.getOffset()+1);//FIXME
				// AVASM
				// Not setting version here
				// struct.ajAttributes.add(new AjAttribute.WeaverVersionInfo());
				AjAttribute.Aspect aspectAttribute = new AjAttribute.Aspect(perClause);
				struct.ajAttributes.add(aspectAttribute);
				FormalBinding[] bindings = FormalBinding.NONE;
				final IScope binding;
				binding = new BindingScope(struct.enclosingType, struct.context, bindings);

				// // we can't resolve here since the perclause typically refers
				// to pointcuts
				// // defined in the aspect that we haven't told the
				// BcelObjectType about yet.
				//
				// perClause.resolve(binding);

				// so we prepare to do it later...
				aspectAttribute.setResolutionScope(binding);
				return true;
			}
		}
		return false;
	}

	/**
	 * Read a perClause, returns null on failure and issue messages
	 *
	 * @param perClauseString like "pertarget(.....)"
	 * @param struct for which we are parsing the per clause
	 * @return a PerClause instance
	 */
	private static PerClause parsePerClausePointcut(String perClauseString, AjAttributeStruct struct) {
		final String pointcutString;
		Pointcut pointcut = null;
		TypePattern typePattern = null;
		final PerClause perClause;
		if (perClauseString.startsWith(PerClause.KindAnnotationPrefix.PERCFLOW.getName())) {
			pointcutString = PerClause.KindAnnotationPrefix.PERCFLOW.extractPointcut(perClauseString);
			pointcut = parsePointcut(pointcutString, struct, false);
			perClause = new PerCflow(pointcut, false);
		} else if (perClauseString.startsWith(PerClause.KindAnnotationPrefix.PERCFLOWBELOW.getName())) {
			pointcutString = PerClause.KindAnnotationPrefix.PERCFLOWBELOW.extractPointcut(perClauseString);
			pointcut = parsePointcut(pointcutString, struct, false);
			perClause = new PerCflow(pointcut, true);
		} else if (perClauseString.startsWith(PerClause.KindAnnotationPrefix.PERTARGET.getName())) {
			pointcutString = PerClause.KindAnnotationPrefix.PERTARGET.extractPointcut(perClauseString);
			pointcut = parsePointcut(pointcutString, struct, false);
			perClause = new PerObject(pointcut, false);
		} else if (perClauseString.startsWith(PerClause.KindAnnotationPrefix.PERTHIS.getName())) {
			pointcutString = PerClause.KindAnnotationPrefix.PERTHIS.extractPointcut(perClauseString);
			pointcut = parsePointcut(pointcutString, struct, false);
			perClause = new PerObject(pointcut, true);
		} else if (perClauseString.startsWith(PerClause.KindAnnotationPrefix.PERTYPEWITHIN.getName())) {
			pointcutString = PerClause.KindAnnotationPrefix.PERTYPEWITHIN.extractPointcut(perClauseString);
			typePattern = parseTypePattern(pointcutString, struct);
			perClause = new PerTypeWithin(typePattern);
		} else if (perClauseString.equalsIgnoreCase(PerClause.SINGLETON.getName() + "()")) {
			perClause = new PerSingleton();
		} else {
			// could not parse the @AJ perclause - fallback to singleton and
			// issue an error
			reportError("@Aspect per clause cannot be read '" + perClauseString + "'", struct);
			return null;
		}

		if (!PerClause.SINGLETON.equals(perClause.getKind()) && !PerClause.PERTYPEWITHIN.equals(perClause.getKind())
				&& pointcut == null) {
			// we could not parse the pointcut
			return null;
		}
		if (PerClause.PERTYPEWITHIN.equals(perClause.getKind()) && typePattern == null) {
			// we could not parse the type pattern
			return null;
		}
		return perClause;
	}

	/**
	 * Read @DeclarePrecedence
	 *
	 * @param runtimeAnnotations
	 * @param struct
	 * @return true if found
	 */
	private static boolean handlePrecedenceAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeStruct struct) {
		AnnotationGen aspect = getAnnotation(runtimeAnnotations, AjcMemberMaker.DECLAREPRECEDENCE_ANNOTATION);
		if (aspect != null) {
			NameValuePair precedence = getAnnotationElement(aspect, VALUE);
			if (precedence != null) {
				String precedencePattern = precedence.getValue().stringifyValue();
				PatternParser parser = new PatternParser(precedencePattern);
				DeclarePrecedence ajPrecedence = parser.parseDominates();
				struct.ajAttributes.add(new AjAttribute.DeclareAttribute(ajPrecedence));
				return true;
			}
		}
		return false;
	}

	// /**
	// * Read @DeclareImplements
	// *
	// * @param runtimeAnnotations
	// * @param struct
	// * @return true if found
	// */
	// private static boolean
	// handleDeclareImplementsAnnotation(RuntimeAnnotations runtimeAnnotations,
	// AjAttributeFieldStruct
	// struct) {//, ResolvedPointcutDefinition preResolvedPointcut) {
	// Annotation deci = getAnnotation(runtimeAnnotations,
	// AjcMemberMaker.DECLAREIMPLEMENTS_ANNOTATION);
	// if (deci != null) {
	// ElementNameValuePairGen deciPatternNVP = getAnnotationElement(deci,
	// VALUE);
	// String deciPattern = deciPatternNVP.getValue().stringifyValue();
	// if (deciPattern != null) {
	// TypePattern typePattern = parseTypePattern(deciPattern, struct);
	// ResolvedType fieldType =
	// UnresolvedType.forSignature(struct.field.getSignature()).resolve(struct.enclosingType.getWorld());
	// if (fieldType.isPrimitiveType()) {
	// return false;
	// } else if (fieldType.isInterface()) {
	// TypePattern parent = new
	// ExactTypePattern(UnresolvedType.forSignature(struct.field.getSignature()),
	// false, false);
	// parent.resolve(struct.enclosingType.getWorld());
	// List parents = new ArrayList(1);
	// parents.add(parent);
	// //TODO kick ISourceLocation sl = struct.bField.getSourceLocation(); ??
	// struct.ajAttributes.add(
	// new AjAttribute.DeclareAttribute(
	// new DeclareParents(
	// typePattern,
	// parents,
	// false
	// )
	// )
	// );
	// return true;
	// } else {
	// reportError("@DeclareImplements: can only be used on field whose type is an interface",
	// struct);
	// return false;
	// }
	// }
	// }
	// return false;
	// }

	private static boolean handleDeclareParentsAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeFieldStruct struct) {
		AnnotationGen decpAnno = getAnnotation(runtimeAnnotations, AjcMemberMaker.DECLAREPARENTS_ANNOTATION);
		if (decpAnno != null) {
			NameValuePair decpPatternNameValuePair = getAnnotationElement(decpAnno, VALUE);
			String decpPattern = decpPatternNameValuePair.getValue().stringifyValue();
			if (decpPattern != null) {
				TypePattern typePattern = parseTypePattern(decpPattern, struct);
				ResolvedType fieldType = UnresolvedType.forSignature(struct.field.getSignature()).resolve(
						struct.enclosingType.getWorld());
				if (fieldType.isParameterizedOrRawType()) {
					fieldType = fieldType.getGenericType();
				}
				if (fieldType.isInterface()) {
					TypePattern parent = parseTypePattern(fieldType.getName(), struct);
					FormalBinding[] bindings = FormalBinding.NONE;
					IScope binding = new BindingScope(struct.enclosingType, struct.context, bindings);
					// first add the declare implements like
					List parents = new ArrayList<>(1);
					parents.add(parent);
					DeclareParents dp = new DeclareParents(typePattern, parents, false);
					dp.resolve(binding); // resolves the parent and child parts of the decp

					// resolve this so that we can use it for the
					// MethodDelegateMungers below.
					// eg. '@Coloured *' will change from a WildTypePattern to
					// an 'AnyWithAnnotationTypePattern' after this resolution
					typePattern = dp.getChild(); // this retrieves the resolved version
					// TODO kick ISourceLocation sl =
					// struct.bField.getSourceLocation(); ??
					// dp.setLocation(dp.getDeclaringType().getSourceContext(),
					// dp.getDeclaringType().getSourceLocation().getOffset(),
					// dp.getDeclaringType().getSourceLocation().getOffset());
					dp.setLocation(struct.context, -1, -1); // not ideal...
					struct.ajAttributes.add(new AjAttribute.DeclareAttribute(dp));

					// do we have a defaultImpl=xxx.class (ie implementation)
					String defaultImplClassName = null;
					NameValuePair defaultImplNVP = getAnnotationElement(decpAnno, "defaultImpl");
					if (defaultImplNVP != null) {
						ClassElementValue defaultImpl = (ClassElementValue) defaultImplNVP.getValue();
						defaultImplClassName = UnresolvedType.forSignature(defaultImpl.getClassString()).getName();
						if (defaultImplClassName.equals("org.aspectj.lang.annotation.DeclareParents")) {
							defaultImplClassName = null;
						} else {
							// check public no arg ctor
							ResolvedType impl = struct.enclosingType.getWorld().resolve(defaultImplClassName, false);
							ResolvedMember[] mm = impl.getDeclaredMethods();
							int implModifiers = impl.getModifiers();
							boolean defaultVisibilityImpl = !(Modifier.isPrivate(implModifiers)
									|| Modifier.isProtected(implModifiers) || Modifier.isPublic(implModifiers));
							boolean hasNoCtorOrANoArgOne = true;
							ResolvedMember foundOneOfIncorrectVisibility = null;
							for (ResolvedMember resolvedMember : mm) {
								if (resolvedMember.getName().equals("")) {
									hasNoCtorOrANoArgOne = false;

									if (resolvedMember.getParameterTypes().length == 0) {
										if (defaultVisibilityImpl) { // default visibility implementation
											if (resolvedMember.isPublic() || resolvedMember.isDefault()) {
												hasNoCtorOrANoArgOne = true;
											} else {
												foundOneOfIncorrectVisibility = resolvedMember;
											}
										} else if (Modifier.isPublic(implModifiers)) { // public
											// implementation
											if (resolvedMember.isPublic()) {
												hasNoCtorOrANoArgOne = true;
											} else {
												foundOneOfIncorrectVisibility = resolvedMember;
											}
										}
									}
								}
								if (hasNoCtorOrANoArgOne) {
									break;
								}
							}
							if (!hasNoCtorOrANoArgOne) {
								if (foundOneOfIncorrectVisibility != null) {
									reportError(
											"@DeclareParents: defaultImpl=\""
													+ defaultImplClassName
													+ "\" has a no argument constructor, but it is of incorrect visibility.  It must be at least as visible as the type.",
											struct);
								} else {
									reportError("@DeclareParents: defaultImpl=\"" + defaultImplClassName
											+ "\" has no public no-arg constructor", struct);
								}
							}
							if (!fieldType.isAssignableFrom(impl)) {
								reportError("@DeclareParents: defaultImpl=\"" + defaultImplClassName
										+ "\" does not implement the interface '" + fieldType.toString() + "'", struct);
							}
						}

					}
					boolean hasAtLeastOneMethod = false;
					// then iterate on field interface hierarchy (not object)
					Iterator methodIterator = fieldType.getMethodsIncludingIntertypeDeclarations(false, true);
					while (methodIterator.hasNext()) {
						ResolvedMember method = methodIterator.next();
						if (method.isAbstract()) {
							// moved to be detected at weave time if the target
							// doesnt implement the methods
							// if (defaultImplClassName == null) {
							// // non marker interface with no default impl
							// provided
							// reportError("@DeclareParents: used with a non marker interface and no defaultImpl=\"...\" provided",
							// struct);
							// return false;
							// }
							hasAtLeastOneMethod = true;
							// What we are saying here:
							// We have this method 'method' and we want to put a
							// forwarding method into a type that matches
							// typePattern that should delegate to the version
							// of the method in 'defaultImplClassName'

							// Now the method may be from a supertype but the
							// declaring type of the method we pass into the
							// type
							// munger is what is used to determine the type of
							// the field that hosts the delegate instance.
							// So here we create a modified method with an
							// alternative declaring type so that we lookup
							// the right field. See pr164016.
							MethodDelegateTypeMunger mdtm = new MethodDelegateTypeMunger(method, struct.enclosingType, defaultImplClassName, typePattern);
							mdtm.setFieldType(fieldType);
							mdtm.setSourceLocation(struct.enclosingType.getSourceLocation());
							struct.ajAttributes.add(new AjAttribute.TypeMunger(mdtm));
						}
					}
					// successful so far, we thus need a bcel type munger to have
					// a field hosting the mixin in the target type
					if (hasAtLeastOneMethod && defaultImplClassName != null) {
						ResolvedMember fieldHost = AjcMemberMaker.itdAtDeclareParentsField(null, fieldType, struct.enclosingType);
						struct.ajAttributes.add(new AjAttribute.TypeMunger(new MethodDelegateTypeMunger.FieldHostTypeMunger(
								fieldHost, struct.enclosingType, typePattern)));
					}
					return true;
				} else {
					reportError("@DeclareParents: can only be used on a field whose type is an interface", struct);
					return false;
				}
			}
		}
		return false;
	}

	/**
	 * @return a nicely formatted method string, for example: int X.foo(java.lang.String)
	 */
	public static String getMethodForMessage(AjAttributeMethodStruct methodstructure) {
		StringBuilder sb = new StringBuilder();
		sb.append("Method '");
		sb.append(methodstructure.method.getReturnType().toString());
		sb.append(" ").append(methodstructure.enclosingType).append(".").append(methodstructure.method.getName());
		sb.append("(");
		Type[] args = methodstructure.method.getArgumentTypes();
		if (args != null) {
			for (int t = 0; t < args.length; t++) {
				if (t > 0) {
					sb.append(",");
				}
				sb.append(args[t].toString());
			}
		}
		sb.append(")'");
		return sb.toString();
	}

	/**
	 * Process any @DeclareMixin annotation.
	 *
	 * Example Declaration 
* * @DeclareMixin("Foo+") public I createImpl(Object o) { return new Impl(o); } * *
* @param runtimeAnnotations * @param struct * @return true if found */ private static boolean handleDeclareMixinAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeMethodStruct struct) { AnnotationGen declareMixinAnnotation = getAnnotation(runtimeAnnotations, AjcMemberMaker.DECLAREMIXIN_ANNOTATION); if (declareMixinAnnotation == null) { // No annotation found return false; } Method annotatedMethod = struct.method; World world = struct.enclosingType.getWorld(); NameValuePair declareMixinPatternNameValuePair = getAnnotationElement(declareMixinAnnotation, VALUE); // declareMixinPattern could be of the form "Bar*" or "A || B" or "Foo+" String declareMixinPattern = declareMixinPatternNameValuePair.getValue().stringifyValue(); TypePattern targetTypePattern = parseTypePattern(declareMixinPattern, struct); // Return value of the annotated method is the interface or class that the mixin delegate should have ResolvedType methodReturnType = UnresolvedType.forSignature(annotatedMethod.getReturnType().getSignature()).resolve(world); if (methodReturnType.isParameterizedOrRawType()) { methodReturnType = methodReturnType.getGenericType(); } if (methodReturnType.isPrimitiveType()) { reportError(getMethodForMessage(struct) + ": factory methods for a mixin cannot return void or a primitive type", struct); return false; } if (annotatedMethod.getArgumentTypes().length > 1) { reportError(getMethodForMessage(struct) + ": factory methods for a mixin can take a maximum of one parameter", struct); return false; } // The set of interfaces to be mixed in is either: // supplied as a list in the 'Class[] interfaces' value in the annotation value // supplied as just the interface return value of the annotated method // supplied as just the class return value of the annotated method NameValuePair interfaceListSpecified = getAnnotationElement(declareMixinAnnotation, "interfaces"); List newParents = new ArrayList<>(1); List newInterfaceTypes = new ArrayList<>(1); if (interfaceListSpecified != null) { ArrayElementValue arrayOfInterfaceTypes = (ArrayElementValue) interfaceListSpecified.getValue(); int numberOfTypes = arrayOfInterfaceTypes.getElementValuesArraySize(); ElementValue[] theTypes = arrayOfInterfaceTypes.getElementValuesArray(); for (int i = 0; i < numberOfTypes; i++) { ClassElementValue interfaceType = (ClassElementValue) theTypes[i]; // Check: needs to be resolvable // TODO crappy replace required ResolvedType ajInterfaceType = UnresolvedType.forSignature(interfaceType.getClassString().replace("/", ".")) .resolve(world); if (ajInterfaceType.isMissing() || !ajInterfaceType.isInterface()) { reportError( "Types listed in the 'interfaces' DeclareMixin annotation value must be valid interfaces. This is invalid: " + ajInterfaceType.getName(), struct); // TODO better error location, use the method position return false; } if (!ajInterfaceType.isAssignableFrom(methodReturnType)) { reportError(getMethodForMessage(struct) + ": factory method does not return something that implements '" + ajInterfaceType.getName() + "'", struct); return false; } newInterfaceTypes.add(ajInterfaceType); // Checking that it is a superinterface of the methods return value is done at weave time TypePattern newParent = parseTypePattern(ajInterfaceType.getName(), struct); newParents.add(newParent); } } else { if (methodReturnType.isClass()) { reportError( getMethodForMessage(struct) + ": factory methods for a mixin must either return an interface type or specify interfaces in the annotation and return a class", struct); return false; } // Use the method return type: this might be a class or an interface TypePattern newParent = parseTypePattern(methodReturnType.getName(), struct); newInterfaceTypes.add(methodReturnType); newParents.add(newParent); } if (newParents.size() == 0) { // Warning: did they foolishly put @DeclareMixin(value="Bar+",interfaces={}) // TODO output warning return false; } // Create the declare parents that will add the interfaces to matching targets FormalBinding[] bindings = FormalBinding.NONE; IScope binding = new BindingScope(struct.enclosingType, struct.context, bindings); // how do we mark this as a decp due to decmixin? DeclareParents dp = new DeclareParentsMixin(targetTypePattern, newParents); dp.resolve(binding); targetTypePattern = dp.getChild(); dp.setLocation(struct.context, -1, -1); // not ideal... struct.ajAttributes.add(new AjAttribute.DeclareAttribute(dp)); // The factory method for building the implementation is the // one attached to the annotation: // Method implementationFactory = struct.method; boolean hasAtLeastOneMethod = false; for (ResolvedType typeForDelegation : newInterfaceTypes) { // TODO check for overlapping interfaces. Eg. A implements I, I extends J - if they specify interfaces={I,J} we dont // want to do any methods twice ResolvedMember[] methods = typeForDelegation.getMethodsWithoutIterator(true, false, false).toArray( ResolvedMember.NONE); for (ResolvedMember resolvedMember : methods) { ResolvedMember method = resolvedMember; if (method.isAbstract()) { hasAtLeastOneMethod = true; if (method.hasBackingGenericMember()) { method = method.getBackingGenericMember(); } MethodDelegateTypeMunger mdtm = new MethodDelegateTypeMunger(method, struct.enclosingType, "", targetTypePattern, struct.method.getName(), struct.method.getSignature()); mdtm.setFieldType(methodReturnType); mdtm.setSourceLocation(struct.enclosingType.getSourceLocation()); struct.ajAttributes.add(new AjAttribute.TypeMunger(mdtm)); } } } // if any method delegate was created then a field to hold the delegate instance must also be added if (hasAtLeastOneMethod) { ResolvedMember fieldHost = AjcMemberMaker.itdAtDeclareParentsField(null, methodReturnType, struct.enclosingType); struct.ajAttributes.add(new AjAttribute.TypeMunger(new MethodDelegateTypeMunger.FieldHostTypeMunger(fieldHost, struct.enclosingType, targetTypePattern))); } return true; } /** * Read @Before * * @param runtimeAnnotations * @param struct * @return true if found */ private static boolean handleBeforeAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeMethodStruct struct, ResolvedPointcutDefinition preResolvedPointcut) { AnnotationGen before = getAnnotation(runtimeAnnotations, AjcMemberMaker.BEFORE_ANNOTATION); if (before != null) { NameValuePair beforeAdvice = getAnnotationElement(before, VALUE); if (beforeAdvice != null) { // this/target/args binding String argumentNames = getArgNamesValue(before); if (argumentNames != null) { struct.unparsedArgumentNames = argumentNames; } FormalBinding[] bindings = FormalBinding.NONE; try { bindings = extractBindings(struct); } catch (UnreadableDebugInfoException unreadableDebugInfoException) { return false; } IScope binding = new BindingScope(struct.enclosingType, struct.context, bindings); // joinpoint, staticJoinpoint binding int extraArgument = extractExtraArgument(struct.method); Pointcut pc = null; if (preResolvedPointcut != null) { pc = preResolvedPointcut.getPointcut(); // pc.resolve(binding); } else { pc = parsePointcut(beforeAdvice.getValue().stringifyValue(), struct, false); if (pc == null) { return false;// parse error } pc = pc.resolve(binding); } setIgnoreUnboundBindingNames(pc, bindings); ISourceLocation sl = struct.context.makeSourceLocation(struct.bMethod.getDeclarationLineNumber(), struct.bMethod.getDeclarationOffset()); struct.ajAttributes.add(new AjAttribute.AdviceAttribute(AdviceKind.Before, pc, extraArgument, sl.getOffset(), sl .getOffset() + 1,// FIXME AVASM struct.context)); return true; } } return false; } /** * Read @After * * @param runtimeAnnotations * @param struct * @return true if found */ private static boolean handleAfterAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeMethodStruct struct, ResolvedPointcutDefinition preResolvedPointcut) { AnnotationGen after = getAnnotation(runtimeAnnotations, AjcMemberMaker.AFTER_ANNOTATION); if (after != null) { NameValuePair afterAdvice = getAnnotationElement(after, VALUE); if (afterAdvice != null) { // this/target/args binding FormalBinding[] bindings = FormalBinding.NONE; String argumentNames = getArgNamesValue(after); if (argumentNames != null) { struct.unparsedArgumentNames = argumentNames; } try { bindings = extractBindings(struct); } catch (UnreadableDebugInfoException unreadableDebugInfoException) { return false; } IScope binding = new BindingScope(struct.enclosingType, struct.context, bindings); // joinpoint, staticJoinpoint binding int extraArgument = extractExtraArgument(struct.method); Pointcut pc = null; if (preResolvedPointcut != null) { pc = preResolvedPointcut.getPointcut(); } else { pc = parsePointcut(afterAdvice.getValue().stringifyValue(), struct, false); if (pc == null) { return false;// parse error } pc.resolve(binding); } setIgnoreUnboundBindingNames(pc, bindings); ISourceLocation sl = struct.context.makeSourceLocation(struct.bMethod.getDeclarationLineNumber(), struct.bMethod.getDeclarationOffset()); struct.ajAttributes.add(new AjAttribute.AdviceAttribute(AdviceKind.After, pc, extraArgument, sl.getOffset(), sl .getOffset() + 1,// FIXME AVASM struct.context)); return true; } } return false; } /** * Read @AfterReturning * * @param runtimeAnnotations * @param struct * @return true if found */ private static boolean handleAfterReturningAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeMethodStruct struct, ResolvedPointcutDefinition preResolvedPointcut, BcelMethod owningMethod) throws ReturningFormalNotDeclaredInAdviceSignatureException { AnnotationGen after = getAnnotation(runtimeAnnotations, AjcMemberMaker.AFTERRETURNING_ANNOTATION); if (after != null) { NameValuePair annValue = getAnnotationElement(after, VALUE); NameValuePair annPointcut = getAnnotationElement(after, POINTCUT); NameValuePair annReturned = getAnnotationElement(after, RETURNING); // extract the pointcut and returned type/binding - do some checks String pointcut = null; String returned = null; if ((annValue != null && annPointcut != null) || (annValue == null && annPointcut == null)) { reportError("@AfterReturning: either 'value' or 'poincut' must be provided, not both", struct); return false; } if (annValue != null) { pointcut = annValue.getValue().stringifyValue(); } else { pointcut = annPointcut.getValue().stringifyValue(); } if (isNullOrEmpty(pointcut)) { reportError("@AfterReturning: either 'value' or 'poincut' must be provided, not both", struct); return false; } if (annReturned != null) { returned = annReturned.getValue().stringifyValue(); if (isNullOrEmpty(returned)) { returned = null; } else { // check that thrownFormal exists as the last parameter in // the advice String[] pNames = owningMethod.getParameterNames(); if (pNames == null || pNames.length == 0 || !Arrays.asList(pNames).contains(returned)) { throw new ReturningFormalNotDeclaredInAdviceSignatureException(returned); } } } String argumentNames = getArgNamesValue(after); if (argumentNames != null) { struct.unparsedArgumentNames = argumentNames; } // this/target/args binding // exclude the return binding from the pointcut binding since it is // an extraArg binding FormalBinding[] bindings = FormalBinding.NONE; try { bindings = (returned == null ? extractBindings(struct) : extractBindings(struct, returned)); } catch (UnreadableDebugInfoException unreadableDebugInfoException) { return false; } IScope binding = new BindingScope(struct.enclosingType, struct.context, bindings); // joinpoint, staticJoinpoint binding int extraArgument = extractExtraArgument(struct.method); // return binding if (returned != null) { extraArgument |= Advice.ExtraArgument; } Pointcut pc = null; if (preResolvedPointcut != null) { pc = preResolvedPointcut.getPointcut(); } else { pc = parsePointcut(pointcut, struct, false); if (pc == null) { return false;// parse error } pc.resolve(binding); } setIgnoreUnboundBindingNames(pc, bindings); ISourceLocation sl = struct.context.makeSourceLocation(struct.bMethod.getDeclarationLineNumber(), struct.bMethod.getDeclarationOffset()); struct.ajAttributes.add(new AjAttribute.AdviceAttribute(AdviceKind.AfterReturning, pc, extraArgument, sl.getOffset(), sl.getOffset() + 1,// FIXME AVASM struct.context)); return true; } return false; } /** * Read @AfterThrowing * * @param runtimeAnnotations * @param struct * @return true if found */ private static boolean handleAfterThrowingAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeMethodStruct struct, ResolvedPointcutDefinition preResolvedPointcut, BcelMethod owningMethod) throws ThrownFormalNotDeclaredInAdviceSignatureException { AnnotationGen after = getAnnotation(runtimeAnnotations, AjcMemberMaker.AFTERTHROWING_ANNOTATION); if (after != null) { NameValuePair annValue = getAnnotationElement(after, VALUE); NameValuePair annPointcut = getAnnotationElement(after, POINTCUT); NameValuePair annThrown = getAnnotationElement(after, THROWING); // extract the pointcut and throwned type/binding - do some checks String pointcut = null; String thrownFormal = null; if ((annValue != null && annPointcut != null) || (annValue == null && annPointcut == null)) { reportError("@AfterThrowing: either 'value' or 'poincut' must be provided, not both", struct); return false; } if (annValue != null) { pointcut = annValue.getValue().stringifyValue(); } else { pointcut = annPointcut.getValue().stringifyValue(); } if (isNullOrEmpty(pointcut)) { reportError("@AfterThrowing: either 'value' or 'poincut' must be provided, not both", struct); return false; } if (annThrown != null) { thrownFormal = annThrown.getValue().stringifyValue(); if (isNullOrEmpty(thrownFormal)) { thrownFormal = null; } else { // check that thrownFormal exists as the last parameter in // the advice String[] pNames = owningMethod.getParameterNames(); if (pNames == null || pNames.length == 0 || !Arrays.asList(pNames).contains(thrownFormal)) { throw new ThrownFormalNotDeclaredInAdviceSignatureException(thrownFormal); } } } String argumentNames = getArgNamesValue(after); if (argumentNames != null) { struct.unparsedArgumentNames = argumentNames; } // this/target/args binding // exclude the throwned binding from the pointcut binding since it // is an extraArg binding FormalBinding[] bindings = FormalBinding.NONE; try { bindings = (thrownFormal == null ? extractBindings(struct) : extractBindings(struct, thrownFormal)); } catch (UnreadableDebugInfoException unreadableDebugInfoException) { return false; } IScope binding = new BindingScope(struct.enclosingType, struct.context, bindings); // joinpoint, staticJoinpoint binding int extraArgument = extractExtraArgument(struct.method); // return binding if (thrownFormal != null) { extraArgument |= Advice.ExtraArgument; } Pointcut pc = null; if (preResolvedPointcut != null) { pc = preResolvedPointcut.getPointcut(); } else { pc = parsePointcut(pointcut, struct, false); if (pc == null) { return false;// parse error } pc.resolve(binding); } setIgnoreUnboundBindingNames(pc, bindings); ISourceLocation sl = struct.context.makeSourceLocation(struct.bMethod.getDeclarationLineNumber(), struct.bMethod.getDeclarationOffset()); struct.ajAttributes.add(new AjAttribute.AdviceAttribute(AdviceKind.AfterThrowing, pc, extraArgument, sl.getOffset(), sl .getOffset() + 1, struct.context)); return true; } return false; } /** * Read @Around * * @param runtimeAnnotations * @param struct * @return true if found */ private static boolean handleAroundAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeMethodStruct struct, ResolvedPointcutDefinition preResolvedPointcut) { AnnotationGen around = getAnnotation(runtimeAnnotations, AjcMemberMaker.AROUND_ANNOTATION); if (around != null) { NameValuePair aroundAdvice = getAnnotationElement(around, VALUE); if (aroundAdvice != null) { // this/target/args binding String argumentNames = getArgNamesValue(around); if (argumentNames != null) { struct.unparsedArgumentNames = argumentNames; } FormalBinding[] bindings = FormalBinding.NONE; try { bindings = extractBindings(struct); } catch (UnreadableDebugInfoException unreadableDebugInfoException) { return false; } IScope binding = new BindingScope(struct.enclosingType, struct.context, bindings); // joinpoint, staticJoinpoint binding int extraArgument = extractExtraArgument(struct.method); Pointcut pc = null; if (preResolvedPointcut != null) { pc = preResolvedPointcut.getPointcut(); } else { pc = parsePointcut(aroundAdvice.getValue().stringifyValue(), struct, false); if (pc == null) { return false;// parse error } pc.resolve(binding); } setIgnoreUnboundBindingNames(pc, bindings); ISourceLocation sl = struct.context.makeSourceLocation(struct.bMethod.getDeclarationLineNumber(), struct.bMethod.getDeclarationOffset()); struct.ajAttributes.add(new AjAttribute.AdviceAttribute(AdviceKind.Around, pc, extraArgument, sl.getOffset(), sl .getOffset() + 1,// FIXME AVASM struct.context)); return true; } } return false; } /** * Read @Pointcut and handle the resolving in a lazy way to deal with pointcut references * * @param runtimeAnnotations * @param struct * @return true if a pointcut was handled */ private static boolean handlePointcutAnnotation(RuntimeAnnos runtimeAnnotations, AjAttributeMethodStruct struct) { AnnotationGen pointcut = getAnnotation(runtimeAnnotations, AjcMemberMaker.POINTCUT_ANNOTATION); if (pointcut == null) { return false; } NameValuePair pointcutExpr = getAnnotationElement(pointcut, VALUE); // semantic check: the method must return void, or be // "public static boolean" for if() support if (!(Type.VOID.equals(struct.method.getReturnType()) || (Type.BOOLEAN.equals(struct.method.getReturnType()) && struct.method.isStatic() && struct.method.isPublic()))) { reportWarning("Found @Pointcut on a method not returning 'void' or not 'public static boolean'", struct); // no need to stop } // semantic check: the method must not throw anything if (struct.method.getExceptionTable() != null) { reportWarning("Found @Pointcut on a method throwing exception", struct); // no need to stop } String argumentNames = getArgNamesValue(pointcut); if (argumentNames != null) { struct.unparsedArgumentNames = argumentNames; } // this/target/args binding final IScope binding; try { if (struct.method.isAbstract()) { binding = null; } else { binding = new BindingScope(struct.enclosingType, struct.context, extractBindings(struct)); } } catch (UnreadableDebugInfoException e) { return false; } UnresolvedType[] argumentTypes = new UnresolvedType[struct.method.getArgumentTypes().length]; for (int i = 0; i < argumentTypes.length; i++) { argumentTypes[i] = UnresolvedType.forSignature(struct.method.getArgumentTypes()[i].getSignature()); } Pointcut pc = null; if (struct.method.isAbstract()) { if ((pointcutExpr != null && isNullOrEmpty(pointcutExpr.getValue().stringifyValue())) || pointcutExpr == null) { // abstract pointcut // leave pc = null } else { reportError("Found defined @Pointcut on an abstract method", struct); return false;// stop } } else { if (pointcutExpr == null || isNullOrEmpty(pointcutExpr.getValue().stringifyValue())) { // the matches nothing pointcut (125475/125480) - perhaps not as // cleanly supported as it could be. } else { // if (pointcutExpr != null) { // use a LazyResolvedPointcutDefinition so that the pointcut is // resolved lazily // since for it to be resolved, we will need other pointcuts to // be registered as well pc = parsePointcut(pointcutExpr.getValue().stringifyValue(), struct, true); if (pc == null) { return false;// parse error } pc.setLocation(struct.context, -1, -1);// FIXME AVASM !! bMethod // is null here.. // } else { // reportError("Found undefined @Pointcut on a non-abstract method", // struct); // return false; // } } } // do not resolve binding now but lazily struct.ajAttributes.add(new AjAttribute.PointcutDeclarationAttribute(new LazyResolvedPointcutDefinition( struct.enclosingType, struct.method.getModifiers(), struct.method.getName(), argumentTypes, UnresolvedType .forSignature(struct.method.getReturnType().getSignature()), pc,// can // be // null // for // abstract // pointcut binding // can be null for abstract pointcut ))); return true; } /** * Read @DeclareError, @DeclareWarning * * @param runtimeAnnotations * @param struct * @return true if found */ private static boolean handleDeclareErrorOrWarningAnnotation(AsmManager model, RuntimeAnnos runtimeAnnotations, AjAttributeFieldStruct struct) { AnnotationGen error = getAnnotation(runtimeAnnotations, AjcMemberMaker.DECLAREERROR_ANNOTATION); boolean hasError = false; if (error != null) { NameValuePair declareError = getAnnotationElement(error, VALUE); if (declareError != null) { if (!STRING_DESC.equals(struct.field.getSignature()) || struct.field.getConstantValue() == null) { reportError("@DeclareError used on a non String constant field", struct); return false; } Pointcut pc = parsePointcut(declareError.getValue().stringifyValue(), struct, false); if (pc == null) { hasError = false;// cannot parse pointcut } else { DeclareErrorOrWarning deow = new DeclareErrorOrWarning(true, pc, struct.field.getConstantValue().toString()); setDeclareErrorOrWarningLocation(model, deow, struct); struct.ajAttributes.add(new AjAttribute.DeclareAttribute(deow)); hasError = true; } } } AnnotationGen warning = getAnnotation(runtimeAnnotations, AjcMemberMaker.DECLAREWARNING_ANNOTATION); boolean hasWarning = false; if (warning != null) { NameValuePair declareWarning = getAnnotationElement(warning, VALUE); if (declareWarning != null) { if (!STRING_DESC.equals(struct.field.getSignature()) || struct.field.getConstantValue() == null) { reportError("@DeclareWarning used on a non String constant field", struct); return false; } Pointcut pc = parsePointcut(declareWarning.getValue().stringifyValue(), struct, false); if (pc == null) { hasWarning = false;// cannot parse pointcut } else { DeclareErrorOrWarning deow = new DeclareErrorOrWarning(false, pc, struct.field.getConstantValue().toString()); setDeclareErrorOrWarningLocation(model, deow, struct); struct.ajAttributes.add(new AjAttribute.DeclareAttribute(deow)); return hasWarning = true; } } } return hasError || hasWarning; } /** * Sets the location for the declare error / warning using the corresponding IProgramElement in the structure model. This will * only fix bug 120356 if compiled with -emacssym, however, it does mean that the cross references view in AJDT will show the * correct information. * * Other possibilities for fix: 1. using the information in ajcDeclareSoft (if this is set correctly) which will fix the problem * if compiled with ajc but not if compiled with javac. 2. creating an AjAttribute called FieldDeclarationLineNumberAttribute * (much like MethodDeclarationLineNumberAttribute) which we can ask for the offset. This will again only fix bug 120356 when * compiled with ajc. * * @param deow * @param struct */ private static void setDeclareErrorOrWarningLocation(AsmManager model, DeclareErrorOrWarning deow, AjAttributeFieldStruct struct) { IHierarchy top = (model == null ? null : model.getHierarchy()); if (top != null && top.getRoot() != null) { IProgramElement ipe = top.findElementForLabel(top.getRoot(), IProgramElement.Kind.FIELD, struct.field.getName()); if (ipe != null && ipe.getSourceLocation() != null) { ISourceLocation sourceLocation = ipe.getSourceLocation(); int start = sourceLocation.getOffset(); int end = start + struct.field.getName().length(); deow.setLocation(struct.context, start, end); return; } } deow.setLocation(struct.context, -1, -1); } /** * Returns a readable representation of a method. Method.toString() is not suitable. * * @param method * @return a readable representation of a method */ private static String methodToString(Method method) { StringBuilder sb = new StringBuilder(); sb.append(method.getName()); sb.append(method.getSignature()); return sb.toString(); } /** * Build the bindings for a given method (pointcut / advice) * * @param struct * @return null if no debug info is available */ private static FormalBinding[] extractBindings(AjAttributeMethodStruct struct) throws UnreadableDebugInfoException { Method method = struct.method; String[] argumentNames = struct.getArgumentNames(); // assert debug info was here if (argumentNames.length != method.getArgumentTypes().length) { reportError( "Cannot read debug info for @Aspect to handle formal binding in pointcuts (please compile with 'javac -g' or '' in Ant)", struct); throw new UnreadableDebugInfoException(); } List bindings = new ArrayList<>(); for (int i = 0; i < argumentNames.length; i++) { String argumentName = argumentNames[i]; UnresolvedType argumentType = UnresolvedType.forSignature(method.getArgumentTypes()[i].getSignature()); // do not bind JoinPoint / StaticJoinPoint / // EnclosingStaticJoinPoint // TODO solve me : this means that the JP/SJP/ESJP cannot appear as // binding // f.e. when applying advice on advice etc if ((AjcMemberMaker.TYPEX_JOINPOINT.equals(argumentType) || AjcMemberMaker.TYPEX_PROCEEDINGJOINPOINT.equals(argumentType) || AjcMemberMaker.TYPEX_STATICJOINPOINT.equals(argumentType) || AjcMemberMaker.TYPEX_ENCLOSINGSTATICJOINPOINT.equals(argumentType) || AjcMemberMaker.AROUND_CLOSURE_TYPE .equals(argumentType))) { // continue;// skip bindings.add(new FormalBinding.ImplicitFormalBinding(argumentType, argumentName, i)); } else { bindings.add(new FormalBinding(argumentType, argumentName, i)); } } return bindings.toArray(FormalBinding.NONE); } // FIXME alex deal with exclude index private static FormalBinding[] extractBindings(AjAttributeMethodStruct struct, String excludeFormal) throws UnreadableDebugInfoException { FormalBinding[] bindings = extractBindings(struct); // int excludeIndex = -1; for (int i = 0; i < bindings.length; i++) { FormalBinding binding = bindings[i]; if (binding.getName().equals(excludeFormal)) { // excludeIndex = i; bindings[i] = new FormalBinding.ImplicitFormalBinding(binding.getType(), binding.getName(), binding.getIndex()); break; } } return bindings; // // if (excludeIndex >= 0) { // FormalBinding[] bindingsFiltered = new // FormalBinding[bindings.length-1]; // int k = 0; // for (int i = 0; i < bindings.length; i++) { // if (i == excludeIndex) { // ; // } else { // bindingsFiltered[k] = new FormalBinding(bindings[i].getType(), // bindings[i].getName(), k); // k++; // } // } // return bindingsFiltered; // } else { // return bindings; // } } /** * Compute the flag for the xxxJoinPoint extra argument * * @param method * @return extra arg flag */ private static int extractExtraArgument(Method method) { Type[] methodArgs = method.getArgumentTypes(); String[] sigs = new String[methodArgs.length]; for (int i = 0; i < methodArgs.length; i++) { sigs[i] = methodArgs[i].getSignature(); } return extractExtraArgument(sigs); } /** * Compute the flag for the xxxJoinPoint extra argument * * @param argumentSignatures * @return extra arg flag */ public static int extractExtraArgument(String[] argumentSignatures) { int extraArgument = 0; for (String argumentSignature : argumentSignatures) { if (AjcMemberMaker.TYPEX_JOINPOINT.getSignature().equals(argumentSignature)) { extraArgument |= Advice.ThisJoinPoint; } else if (AjcMemberMaker.TYPEX_PROCEEDINGJOINPOINT.getSignature().equals(argumentSignature)) { extraArgument |= Advice.ThisJoinPoint; } else if (AjcMemberMaker.TYPEX_STATICJOINPOINT.getSignature().equals(argumentSignature)) { extraArgument |= Advice.ThisJoinPointStaticPart; } else if (AjcMemberMaker.TYPEX_ENCLOSINGSTATICJOINPOINT.getSignature().equals(argumentSignature)) { extraArgument |= Advice.ThisEnclosingJoinPointStaticPart; } } return extraArgument; } /** * Returns the runtime (RV/RIV) annotation of type annotationType or null if no such annotation * * @param rvs * @param annotationType * @return annotation */ private static AnnotationGen getAnnotation(RuntimeAnnos rvs, UnresolvedType annotationType) { final String annotationTypeName = annotationType.getName(); for (AnnotationGen rv : rvs.getAnnotations()) { if (annotationTypeName.equals(rv.getTypeName())) { return rv; } } return null; } /** * Returns the value of a given element of an annotation or null if not found Caution: Does not handles default value. * * @param annotation * @param elementName * @return annotation NVP */ private static NameValuePair getAnnotationElement(AnnotationGen annotation, String elementName) { for (NameValuePair element : annotation.getValues()) { if (elementName.equals(element.getNameString())) { return element; } } return null; } /** * Return the argNames set for an annotation or null if it is not specified. */ private static String getArgNamesValue(AnnotationGen anno) { List elements = anno.getValues(); for (NameValuePair element : elements) { if (ARGNAMES.equals(element.getNameString())) { return element.getValue().stringifyValue(); } } return null; } private static String lastbit(String fqname) { int i = fqname.lastIndexOf("."); if (i == -1) { return fqname; } else { return fqname.substring(i + 1); } } /** * Extract the method argument names. First we try the debug info attached to the method (the LocalVariableTable) - if we cannot * find that we look to use the argNames value that may have been supplied on the associated annotation. If that fails we just * don't know and return an empty string. * * @param method * @param argNamesFromAnnotation * @param methodStruct * @return method argument names */ private static String[] getMethodArgumentNames(Method method, String argNamesFromAnnotation, AjAttributeMethodStruct methodStruct) { if (method.getArgumentTypes().length == 0) { return EMPTY_STRINGS; } final int startAtStackIndex = method.isStatic() ? 0 : 1; final List arguments = new ArrayList<>(); LocalVariableTable lt = method.getLocalVariableTable(); if (lt != null) { LocalVariable[] lvt = lt.getLocalVariableTable(); for (LocalVariable localVariable : lvt) { if (localVariable != null) { // pr348488 if (localVariable.getStartPC() == 0) { if (localVariable.getIndex() >= startAtStackIndex) { arguments.add(new MethodArgument(localVariable.getName(), localVariable.getIndex())); } } } else { String typename = (methodStruct.enclosingType != null ? methodStruct.enclosingType.getName() : ""); System.err.println("AspectJ: 348488 debug: unusual local variable table for method " + typename + "." + method.getName()); } } if (arguments.size() == 0) { // The local variable table is causing us trouble, try the annotation value // See 539121 for a jacoco variant of the cobertura issue below if (argNamesFromAnnotation != null) { String[] argNames = extractArgNamesFromAnnotationValue(method, argNamesFromAnnotation, methodStruct); if (argNames.length != 0) { return argNames; } } // could be cobertura code where some extra bytecode has been stuffed in at the start of the method // but the local variable table hasn't been repaired - for example: // LocalVariable(start_pc = 6, length = 40, index = 0:com.example.ExampleAspect this) // LocalVariable(start_pc = 6, length = 40, index = 1:org.aspectj.lang.ProceedingJoinPoint pjp) // LocalVariable(start_pc = 6, length = 40, index = 2:int __cobertura__line__number__) // LocalVariable(start_pc = 6, length = 40, index = 3:int __cobertura__branch__number__) LocalVariable localVariable = lvt[0]; if (localVariable != null) { // pr348488 if (localVariable.getStartPC() != 0) { // looks suspicious so let's use this information for (int j = 0; j < lvt.length && arguments.size() < method.getArgumentTypes().length; j++) { localVariable = lvt[j]; if (localVariable.getIndex() >= startAtStackIndex) { arguments.add(new MethodArgument(localVariable.getName(), localVariable.getIndex())); } } } } } } else { if (argNamesFromAnnotation != null) { String[] argNames = extractArgNamesFromAnnotationValue(method, argNamesFromAnnotation, methodStruct); if (argNames != null) { return argNames; } } } if (arguments.size() != method.getArgumentTypes().length) { return EMPTY_STRINGS; } // sort by index arguments.sort(new Comparator() { public int compare(MethodArgument mo, MethodArgument mo1) { if (mo.indexOnStack == mo1.indexOnStack) { return 0; } else if (mo.indexOnStack > mo1.indexOnStack) { return 1; } else { return -1; } } }); String[] argumentNames = new String[arguments.size()]; int i = 0; for (MethodArgument methodArgument : arguments) { argumentNames[i++] = methodArgument.name; } return argumentNames; } private static String[] extractArgNamesFromAnnotationValue(Method method, String argNamesFromAnnotation, AjAttributeMethodStruct methodStruct) { StringTokenizer st = new StringTokenizer(argNamesFromAnnotation, " ,"); List args = new ArrayList<>(); while (st.hasMoreTokens()) { args.add(st.nextToken()); } if (args.size() != method.getArgumentTypes().length) { StringBuilder shortString = new StringBuilder().append(lastbit(method.getReturnType().toString())).append(" ") .append(method.getName()); if (method.getArgumentTypes().length > 0) { shortString.append("("); for (int i = 0; i < method.getArgumentTypes().length; i++) { shortString.append(lastbit(method.getArgumentTypes()[i].toString())); if ((i + 1) < method.getArgumentTypes().length) { shortString.append(","); } } shortString.append(")"); } reportError("argNames annotation value does not specify the right number of argument names for the method '" + shortString.toString() + "'", methodStruct); return EMPTY_STRINGS; } return args.toArray(new String[] {}); } /** * A method argument, used for sorting by indexOnStack (ie order in signature) * * @author Alexandre Vasseur */ private static class MethodArgument { String name; int indexOnStack; public MethodArgument(String name, int indexOnStack) { this.name = name; this.indexOnStack = indexOnStack; } } /** * LazyResolvedPointcutDefinition lazyly resolve the pointcut so that we have time to register all pointcut referenced before * pointcut resolution happens * * @author Alexandre Vasseur (alex AT gnilux DOT com) */ public static class LazyResolvedPointcutDefinition extends ResolvedPointcutDefinition { private final Pointcut m_pointcutUnresolved; // null for abstract // pointcut private final IScope m_binding; private Pointcut m_lazyPointcut = null; public LazyResolvedPointcutDefinition(UnresolvedType declaringType, int modifiers, String name, UnresolvedType[] parameterTypes, UnresolvedType returnType, Pointcut pointcut, IScope binding) { super(declaringType, modifiers, name, parameterTypes, returnType, Pointcut.makeMatchesNothing(Pointcut.RESOLVED)); m_pointcutUnresolved = pointcut; m_binding = binding; } @Override public Pointcut getPointcut() { if (m_lazyPointcut == null && m_pointcutUnresolved == null) { m_lazyPointcut = Pointcut.makeMatchesNothing(Pointcut.CONCRETE); } if (m_lazyPointcut == null && m_pointcutUnresolved != null) { m_lazyPointcut = m_pointcutUnresolved.resolve(m_binding); m_lazyPointcut.copyLocationFrom(m_pointcutUnresolved); } return m_lazyPointcut; } } /** * Helper to test empty strings * * @param s * @return true if empty or null */ private static boolean isNullOrEmpty(String s) { return (s == null || s.length() <= 0); } /** * Set the pointcut bindings for which to ignore unbound issues, so that we can implicitly bind xxxJoinPoint for @AJ advices * * @param pointcut * @param bindings */ private static void setIgnoreUnboundBindingNames(Pointcut pointcut, FormalBinding[] bindings) { // register ImplicitBindings as to be ignored since unbound // TODO is it likely to fail in a bad way if f.e. this(jp) etc ? List ignores = new ArrayList<>(); for (FormalBinding formalBinding : bindings) { if (formalBinding instanceof FormalBinding.ImplicitFormalBinding) { ignores.add(formalBinding.getName()); } } pointcut.m_ignoreUnboundBindingForNames = ignores.toArray(new String[0]); } /** * A check exception when we cannot read debug info (needed for formal binding) */ private static class UnreadableDebugInfoException extends Exception { } /** * Report an error * * @param message * @param location */ private static void reportError(String message, AjAttributeStruct location) { if (!location.handler.isIgnoring(IMessage.ERROR)) { location.handler.handleMessage(new Message(message, location.enclosingType.getSourceLocation(), true)); } } // private static void reportError(String message, IMessageHandler handler, ISourceLocation sourceLocation) { // if (!handler.isIgnoring(IMessage.ERROR)) { // handler.handleMessage(new Message(message, sourceLocation, true)); // } // } /** * Report a warning * * @param message * @param location */ private static void reportWarning(String message, AjAttributeStruct location) { if (!location.handler.isIgnoring(IMessage.WARNING)) { location.handler.handleMessage(new Message(message, location.enclosingType.getSourceLocation(), false)); } } /** * Parse the given pointcut, return null on failure and issue an error * * @param pointcutString * @param struct * @param allowIf * @return pointcut, unresolved */ private static Pointcut parsePointcut(String pointcutString, AjAttributeStruct struct, boolean allowIf) { try { PatternParser parser = new PatternParser(pointcutString, struct.context); Pointcut pointcut = parser.parsePointcut(); parser.checkEof(); pointcut.check(null, struct.enclosingType.getWorld()); if (!allowIf && pointcutString.contains("if()") && hasIf(pointcut)) { reportError("if() pointcut is not allowed at this pointcut location '" + pointcutString + "'", struct); return null; } pointcut.setLocation(struct.context, -1, -1);// FIXME -1,-1 is not // good enough return pointcut; } catch (ParserException e) { reportError("Invalid pointcut '" + pointcutString + "': " + e.toString() + (e.getLocation() == null ? "" : " at position " + e.getLocation().getStart()), struct); return null; } } private static boolean hasIf(Pointcut pointcut) { IfFinder visitor = new IfFinder(); pointcut.accept(visitor, null); return visitor.hasIf; } /** * Parse the given type pattern, return null on failure and issue an error * * @param patternString * @param location * @return type pattern */ private static TypePattern parseTypePattern(String patternString, AjAttributeStruct location) { try { TypePattern typePattern = new PatternParser(patternString).parseTypePattern(); typePattern.setLocation(location.context, -1, -1);// FIXME -1,-1 is // not good // enough return typePattern; } catch (ParserException e) { reportError("Invalid type pattern'" + patternString + "' : " + e.getLocation(), location); return null; } } static class ThrownFormalNotDeclaredInAdviceSignatureException extends Exception { private final String formalName; public ThrownFormalNotDeclaredInAdviceSignatureException(String formalName) { this.formalName = formalName; } public String getFormalName() { return formalName; } } static class ReturningFormalNotDeclaredInAdviceSignatureException extends Exception { private final String formalName; public ReturningFormalNotDeclaredInAdviceSignatureException(String formalName) { this.formalName = formalName; } public String getFormalName() { return formalName; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy