org.aspectj.weaver.bcel.AtAjAttributes Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aspectjtools Show documentation
Show all versions of aspectjtools Show documentation
Tools from the AspectJ project
/*******************************************************************************
* 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;
}
}
}