Package org.aspectj.weaver.bcel

Source Code of org.aspectj.weaver.bcel.AtAjAttributes$ReturningFormalNotDeclaredInAdviceSignatureException

/*******************************************************************************
* Copyright (c) 2005 Contributors.
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License v1.0
* which accompanies this distribution and is available at
* http://eclipse.org/legal/epl-v10.html
*
* Contributors:
* initial implementation              Alexandre Vasseur
*******************************************************************************/
package org.aspectj.weaver.bcel;

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 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.annotation.Annotation;
import org.aspectj.apache.bcel.classfile.annotation.ClassElementValue;
import org.aspectj.apache.bcel.classfile.annotation.ElementNameValuePair;
import org.aspectj.apache.bcel.classfile.annotation.RuntimeAnnotations;
import org.aspectj.apache.bcel.classfile.annotation.RuntimeVisibleAnnotations;
import org.aspectj.apache.bcel.generic.Type;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.ISourceLocation;
import org.aspectj.bridge.Message;
import org.aspectj.weaver.Advice;
import org.aspectj.weaver.AdviceKind;
import org.aspectj.weaver.AjAttribute;
import org.aspectj.weaver.AjcMemberMaker;
import org.aspectj.weaver.IHasPosition;
import org.aspectj.weaver.ISourceContext;
import org.aspectj.weaver.MethodDelegateTypeMunger;
import org.aspectj.weaver.NameMangler;
import org.aspectj.weaver.ReferenceType;
import org.aspectj.weaver.ResolvedMember;
import org.aspectj.weaver.ResolvedPointcutDefinition;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.WeaverMessages;
import org.aspectj.weaver.patterns.AndPointcut;
import org.aspectj.weaver.patterns.DeclareErrorOrWarning;
import org.aspectj.weaver.patterns.DeclareParents;
import org.aspectj.weaver.patterns.DeclarePrecedence;
import org.aspectj.weaver.patterns.ExactTypePattern;
import org.aspectj.weaver.patterns.FormalBinding;
import org.aspectj.weaver.patterns.IScope;
import org.aspectj.weaver.patterns.IdentityPointcutVisitor;
import org.aspectj.weaver.patterns.IfPointcut;
import org.aspectj.weaver.patterns.NotPointcut;
import org.aspectj.weaver.patterns.OrPointcut;
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.SimpleScope;
import org.aspectj.weaver.patterns.TypePattern;

/**
* Annotation defined aspect reader.
* <p/>
* It reads the Java 5 annotations and turns them into AjAttributes
*
* @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
*/
public class AtAjAttributes {

    private final static List EMPTY_LIST = new ArrayList();
    private final static String[] EMPTY_STRINGS = new String[0];
    private final static String VALUE = "value";
    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;";

    /**
     * A struct that allows to add extra arguments without always breaking the API
     *
     * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
     */
    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 <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
     */
    private static class AjAttributeMethodStruct extends AjAttributeStruct {

        /**
         * Argument names as they appear in the SOURCE code, ordered, and lazyly populated
         * Used to do formal binding
         */
        private String[] m_argumentNamesLazy = null;

        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 = getMethodArgumentNamesAsInSource(method);
            }
            return m_argumentNamesLazy;
        }
    }

    /**
     * A struct when we read @AJ on field
     *
     * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
     */
    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 RuntimeVisibleAnnotations);
    }

    /**
     * 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(JavaClass javaClass, ReferenceType type, ISourceContext context, IMessageHandler msgHandler, boolean isCodeStyleAspect) {
        //FIXME AV - 1.5 feature limitation, kick after implemented
        try {
            Constant[] cpool = javaClass.getConstantPool().getConstantPool();
            for (int i = 0; i < cpool.length; i++) {
                Constant constant = cpool[i];
                if (constant != null && constant.getTag() == Constants.CONSTANT_Utf8) {
                    if (!javaClass.getClassName().startsWith("org.aspectj.lang.annotation")) {
                        ConstantUtf8 constantUtf8 = (ConstantUtf8) constant;
                        if ("Lorg/aspectj/lang/annotation/DeclareAnnotation;".equals(constantUtf8.getBytes())) {
                            msgHandler.handleMessage(
                                    new Message(
                                            "Found @DeclareAnnotation while current release does not support it (see '" + type.getName() + "')",
                                            IMessage.WARNING,
                                            null,
                                            type.getSourceLocation()
                                    )
                            );
                        }
                    }
                }
            }
        } catch (Throwable t) {
            ;
        }


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

        for (int i = 0; i < attributes.length; i++) {
            Attribute attribute = attributes[i];
            if (acceptAttribute(attribute)) {
                RuntimeAnnotations rvs = (RuntimeAnnotations) attribute;
                // we don't need to look for several attribute occurence 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;
            }
        }

        // 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 EMPTY_LIST;
        }

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


        //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 = new AjAttributeMethodStruct(method, null, type, context, msgHandler);//FIXME AVASM
            Attribute[] mattributes = method.getAttributes();

            for (int j = 0; j < mattributes.length; j++) {
                Attribute mattribute = mattributes[j];
                if (acceptAttribute(mattribute)) {
                    RuntimeAnnotations mrvs = (RuntimeAnnotations) mattribute;
                    handlePointcutAnnotation(mrvs, mstruct);
                    // there can only be one RuntimeVisible bytecode attribute
                    break;
                }
            }
            // FIXME asc should check we aren't adding multiple versions... will do once I get the tests passing again...
            struct.ajAttributes.add(new AjAttribute.WeaverVersionInfo());
            struct.ajAttributes.addAll(mstruct.ajAttributes);
        }


        // code style declare error / warning / implements / parents are field attributes
        for (int i = 0; i < javaClass.getFields().length; i++) {
            Field field = javaClass.getFields()[i];
            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 (int j = 0; j < fattributes.length; j++) {
                Attribute fattribute = fattributes[j];
                if (acceptAttribute(fattribute)) {
                    RuntimeAnnotations frvs = (RuntimeAnnotations) fattribute;
                    if (handleDeclareErrorOrWarningAnnotation(frvs, fstruct)
                            || handleDeclareParentsAnnotation(frvs, fstruct)) {
                        // semantic check - must be in an @Aspect [remove if previous block bypassed in advance]
                        if (!type.isAnnotationStyleAspect()) {
                            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.EMPTY_LIST;  // 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 (int i = 0; i < attributes.length; i++) {
            Attribute attribute = attributes[i];
            try {
                if (acceptAttribute(attribute)) {
                    RuntimeAnnotations rvs = (RuntimeAnnotations) 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.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 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.EMPTY_LIST;
    }


    /**
     * Read @Aspect
     *
     * @param runtimeAnnotations
     * @param struct
     * @return true if found
     */
    private static boolean handleAspectAnnotation(RuntimeAnnotations runtimeAnnotations, AjAttributeStruct struct) {
        Annotation 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();
            }

            ElementNameValuePair 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, struct.context.getOffset(), struct.context.getOffset()+1);//FIXME AVASM
                // FIXME asc see related comment way about about the version...
                struct.ajAttributes.add(new AjAttribute.WeaverVersionInfo());
                AjAttribute.Aspect aspectAttribute = new AjAttribute.Aspect(perClause);
                struct.ajAttributes.add(aspectAttribute);
                FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0];
                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(RuntimeAnnotations runtimeAnnotations, AjAttributeStruct struct) {
        Annotation aspect = getAnnotation(runtimeAnnotations, AjcMemberMaker.DECLAREPRECEDENCE_ANNOTATION);
        if (aspect != null) {
            ElementNameValuePair 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) {
//            ElementNameValuePair 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;
//    }

    /**
     * Read @DeclareParents
     *
     * @param runtimeAnnotations
     * @param struct
     * @return true if found
     */
    private static boolean handleDeclareParentsAnnotation(RuntimeAnnotations runtimeAnnotations, AjAttributeFieldStruct struct) {//, ResolvedPointcutDefinition preResolvedPointcut) {
        Annotation decp = getAnnotation(runtimeAnnotations, AjcMemberMaker.DECLAREPARENTS_ANNOTATION);
        if (decp != null) {
            ElementNameValuePair decpPatternNVP = getAnnotationElement(decp, VALUE);
            String decpPattern = decpPatternNVP.getValue().stringifyValue();
            if (decpPattern != null) {
                TypePattern typePattern = parseTypePattern(decpPattern, struct);
                ResolvedType fieldType = UnresolvedType.forSignature(struct.field.getSignature()).resolve(struct.enclosingType.getWorld());
                if (fieldType.isInterface()) {
                    TypePattern parent = new ExactTypePattern(UnresolvedType.forSignature(struct.field.getSignature()), false, false);
                    parent.resolve(struct.enclosingType.getWorld());
                    // first add the declare implements like
                    List parents = new ArrayList(1); parents.add(parent);
                    DeclareParents dp = new DeclareParents(
                            typePattern,
                            parents,
                            false
                        );
                    //TODO kick ISourceLocation sl = struct.bField.getSourceLocation();    ??
                    dp.setLocation(struct.context,0,0); // not ideal...
                    struct.ajAttributes.add(
                            new AjAttribute.DeclareAttribute(
                                    dp
                            )
                    );


                    // do we have a defaultImpl=xxx.class (ie implementation)
                    String defaultImplClassName = null;
                    ElementNameValuePair defaultImplNVP = getAnnotationElement(decp, "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();
                            boolean hasNoCtorOrANoArgOne = true;
                            for (int i = 0; i < mm.length; i++) {
                                ResolvedMember resolvedMember = mm[i];
                                if (resolvedMember.getName().equals("<init>")) {
                                    hasNoCtorOrANoArgOne = false;
                                    if (resolvedMember.getParameterTypes().length == 0
                                        && resolvedMember.isPublic()) {
                                        hasNoCtorOrANoArgOne = true;
                                    }
                                }
                                if (hasNoCtorOrANoArgOne) {
                                    break;
                                }
                            }
                            if (!hasNoCtorOrANoArgOne) {
                                reportError("@DeclareParents: defaultImpl=\""
                                            + defaultImplClassName
                                            + "\" has no public no-arg constructor", struct);
                            }
                        }

                    }

                    // then iterate on field interface hierarchy (not object)
                    boolean hasAtLeastOneMethod = false;
                    ResolvedMember[] methods = (ResolvedMember[])fieldType.getMethodsWithoutIterator(true, false).toArray(new ResolvedMember[0]);
                    for (int i = 0; i < methods.length; i++) {
                        ResolvedMember method = (ResolvedMember)methods[i];
                        if (method.isAbstract()) {
                            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;

                            struct.ajAttributes.add(
                                    new AjAttribute.TypeMunger(
                                            new MethodDelegateTypeMunger(
                                                method,
                                                struct.enclosingType,
                                                defaultImplClassName,
                                                typePattern
                                            )
                                    )
                            );
                        }
                    }
                    // successfull so far, we thus need a bcel type munger to have
                    // a field hosting the mixin in the target type
                    if (hasAtLeastOneMethod) {
                        struct.ajAttributes.add(
                                new AjAttribute.TypeMunger(
                                        new MethodDelegateTypeMunger.FieldHostTypeMunger(
                                                AjcMemberMaker.itdAtDeclareParentsField(
                                                        null,//prototyped
                                                        fieldType,
                                                        struct.enclosingType
                                                ),
                                                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;
    }

    /**
     * Read @Before
     *
     * @param runtimeAnnotations
     * @param struct
     * @return true if found
     */
    private static boolean handleBeforeAnnotation(RuntimeAnnotations runtimeAnnotations, AjAttributeMethodStruct struct, ResolvedPointcutDefinition preResolvedPointcut) {
        Annotation before = getAnnotation(runtimeAnnotations, AjcMemberMaker.BEFORE_ANNOTATION);
        if (before != null) {
            ElementNameValuePair beforeAdvice = getAnnotationElement(before, VALUE);
            if (beforeAdvice != null) {
                // this/target/args binding
                FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0];
                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(RuntimeAnnotations runtimeAnnotations, AjAttributeMethodStruct struct, ResolvedPointcutDefinition preResolvedPointcut) {
        Annotation after = getAnnotation(runtimeAnnotations, AjcMemberMaker.AFTER_ANNOTATION);
        if (after != null) {
            ElementNameValuePair afterAdvice = getAnnotationElement(after, VALUE);
            if (afterAdvice != null) {
                // this/target/args binding
                FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0];
                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(
            RuntimeAnnotations runtimeAnnotations,
            AjAttributeMethodStruct struct,
            ResolvedPointcutDefinition preResolvedPointcut,
            BcelMethod owningMethod)
    throws ReturningFormalNotDeclaredInAdviceSignatureException
    {
        Annotation after = getAnnotation(runtimeAnnotations, AjcMemberMaker.AFTERRETURNING_ANNOTATION);
        if (after != null) {
            ElementNameValuePair annValue = getAnnotationElement(after, VALUE);
            ElementNameValuePair annPointcut = getAnnotationElement(after, POINTCUT);
            ElementNameValuePair 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);
                    }
                }
            }

            // this/target/args binding
            // exclude the return binding from the pointcut binding since it is an extraArg binding
            FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0];
            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(
            RuntimeAnnotations runtimeAnnotations,
            AjAttributeMethodStruct struct,
            ResolvedPointcutDefinition preResolvedPointcut,
            BcelMethod owningMethod)
    throws ThrownFormalNotDeclaredInAdviceSignatureException
    {
        Annotation after = getAnnotation(runtimeAnnotations, AjcMemberMaker.AFTERTHROWING_ANNOTATION);
        if (after != null) {
            ElementNameValuePair annValue = getAnnotationElement(after, VALUE);
            ElementNameValuePair annPointcut = getAnnotationElement(after, POINTCUT);
            ElementNameValuePair 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);
                    }
                }
            }

            // this/target/args binding
            // exclude the throwned binding from the pointcut binding since it is an extraArg binding
            FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0];
            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,//FIXME AVASM
                            struct.context
                    )
            );
            return true;
        }
        return false;
    }

    /**
     * Read @Around
     *
     * @param runtimeAnnotations
     * @param struct
     * @return true if found
     */
    private static boolean handleAroundAnnotation(RuntimeAnnotations runtimeAnnotations, AjAttributeMethodStruct struct, ResolvedPointcutDefinition preResolvedPointcut) {
        Annotation around = getAnnotation(runtimeAnnotations, AjcMemberMaker.AROUND_ANNOTATION);
        if (around != null) {
            ElementNameValuePair aroundAdvice = getAnnotationElement(around, VALUE);
            if (aroundAdvice != null) {
                // this/target/args binding
                FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0];
                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
     */
    private static void handlePointcutAnnotation(RuntimeAnnotations runtimeAnnotations, AjAttributeMethodStruct struct) {
        Annotation pointcut = getAnnotation(runtimeAnnotations, AjcMemberMaker.POINTCUT_ANNOTATION);
        if (pointcut != null) {
            ElementNameValuePair 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
            }

            // this/target/args binding
            final IScope binding;
            try {
                binding = new BindingScope(
                        struct.enclosingType,
                        struct.context,
                        extractBindings(struct)
                );
            } catch (UnreadableDebugInfoException e) {
                return;
            }

            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;//stop
                }
            } 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;//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;
                }
            }
            // 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
                            )
                    )
            );
        }
    }

    /**
     * Read @DeclareError, @DeclareWarning
     *
     * @param runtimeAnnotations
     * @param struct
     * @return true if found
     */
    private static boolean handleDeclareErrorOrWarningAnnotation(RuntimeAnnotations runtimeAnnotations, AjAttributeFieldStruct struct) {
        Annotation error = getAnnotation(runtimeAnnotations, AjcMemberMaker.DECLAREERROR_ANNOTATION);
        boolean hasError = false;
        if (error != null) {
            ElementNameValuePair 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;
                }
                FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0];
                IScope binding = new BindingScope(
                        struct.enclosingType,
                        struct.context,
                        bindings
                );
                Pointcut pc = parsePointcut(declareError.getValue().stringifyValue(), struct, false);
                if (pc == null) {
                    hasError = false;//cannot parse pointcut
                } else {
                    pc .resolve(binding);
                    DeclareErrorOrWarning deow = new DeclareErrorOrWarning(true, pc, struct.field.getConstantValue().toString());
                    deow.setLocation(struct.context, -1, -1);
                    struct.ajAttributes.add(new AjAttribute.DeclareAttribute(deow));
                    hasError = true;
                }
            }
        }
        Annotation warning = getAnnotation(runtimeAnnotations, AjcMemberMaker.DECLAREWARNING_ANNOTATION);
        boolean hasWarning = false;
        if (warning != null) {
            ElementNameValuePair 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;
                }
                FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0];
                IScope binding = new BindingScope(
                        struct.enclosingType,
                        struct.context,
                        bindings
                );
                Pointcut pc = parsePointcut(declareWarning.getValue().stringifyValue(), struct, false);
                if (pc == null) {
                    hasWarning = false;//cannot parse pointcut
                } else {
                    pc.resolve(binding);
                    DeclareErrorOrWarning deow = new DeclareErrorOrWarning(false, pc, struct.field.getConstantValue().toString());
                    deow.setLocation(struct.context, -1, -1);
                    struct.ajAttributes.add(new AjAttribute.DeclareAttribute(deow));
                    return hasWarning = true;
                }
            }
        }
        return hasError || hasWarning;
    }

    /**
     * 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) {
        StringBuffer sb = new StringBuffer();
        sb.append(method.getName());
        sb.append(method.getSignature());
        return sb.toString();
    }

    /**
     * Returns a readable representation of a field.
     * Field.toString() is not suitable.
     *
     * @param field
     * @return a readable representation of a field
     */
    private static String fieldToString(Field field) {
        StringBuffer sb = new StringBuffer();
        sb.append(field.getName()).append(' ');
        sb.append(field.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 '<javac debug='true'.../>' 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 (FormalBinding[]) bindings.toArray(new FormalBinding[]{});
    }

    //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 (int i = 0; i < argumentSignatures.length; i++) {
            if (AjcMemberMaker.TYPEX_JOINPOINT.getSignature().equals(argumentSignatures[i])) {
                extraArgument |= Advice.ThisJoinPoint;
            } else if (AjcMemberMaker.TYPEX_PROCEEDINGJOINPOINT.getSignature().equals(argumentSignatures[i])) {
                extraArgument |= Advice.ThisJoinPoint;
            } else if (AjcMemberMaker.TYPEX_STATICJOINPOINT.getSignature().equals(argumentSignatures[i])) {
                extraArgument |= Advice.ThisJoinPointStaticPart;
            } else if (AjcMemberMaker.TYPEX_ENCLOSINGSTATICJOINPOINT.getSignature().equals(argumentSignatures[i])) {
                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 Annotation getAnnotation(RuntimeAnnotations rvs, UnresolvedType annotationType) {
        final String annotationTypeName = annotationType.getName();
        for (Iterator iterator = rvs.getAnnotations().iterator(); iterator.hasNext();) {
            Annotation rv = (Annotation) iterator.next();
            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 ElementNameValuePair getAnnotationElement(Annotation annotation, String elementName) {
        for (Iterator iterator1 = annotation.getValues().iterator(); iterator1.hasNext();) {
            ElementNameValuePair element = (ElementNameValuePair) iterator1.next();
            if (elementName.equals(element.getNameString())) {
                return element;
            }
        }
        return null;
    }

    /**
     * Extract the method argument names as in source from debug info
     * returns an empty array upon inconsistency
     *
     * @param method
     * @return method arg names as in source
     */
    private static String[] getMethodArgumentNamesAsInSource(Method method) {
        if (method.getArgumentTypes().length == 0) {
            return EMPTY_STRINGS;
        }

        final int startAtStackIndex = method.isStatic() ? 0 : 1;
        final List arguments = new ArrayList();
        LocalVariableTable lt = (LocalVariableTable) method.getLocalVariableTable();
        if (lt != null) {
            for (int j = 0; j < lt.getLocalVariableTable().length; j++) {
                LocalVariable localVariable = lt.getLocalVariableTable()[j];
                if (localVariable.getStartPC() == 0) {
                    if (localVariable.getIndex() >= startAtStackIndex) {
                        arguments.add(new MethodArgument(localVariable.getName(), localVariable.getIndex()));
                    }
                }
            }
        }

        if (arguments.size() != method.getArgumentTypes().length) {
            return EMPTY_STRINGS;
        }

        // sort by index
        Collections.sort(
                arguments, new Comparator() {
                    public int compare(Object o, Object o1) {
                        MethodArgument mo = (MethodArgument) o;
                        MethodArgument mo1 = (MethodArgument) o1;
                        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 (Iterator iterator = arguments.iterator(); iterator.hasNext(); i++) {
            MethodArgument methodArgument = (MethodArgument) iterator.next();
            argumentNames[i] = methodArgument.name;
        }
        return argumentNames;
    }

    /**
     * A method argument, used for sorting by indexOnStack (ie order in signature)
     *
     * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
     */
    private static class MethodArgument {
        String name;
        int indexOnStack;

        public MethodArgument(String name, int indexOnStack) {
            this.name = name;
            this.indexOnStack = indexOnStack;
        }
    }

    /**
     * BindingScope that knows the enclosingType, which is needed for pointcut reference resolution
     *
     * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
     */
    public static class BindingScope extends SimpleScope {
        private ResolvedType m_enclosingType;
        private ISourceContext m_sourceContext;

        public BindingScope(ResolvedType type, ISourceContext sourceContext, FormalBinding[] bindings) {
            super(type.getWorld(), bindings);
            m_enclosingType = type;
            m_sourceContext = sourceContext;
        }

        public ResolvedType getEnclosingType() {
            return m_enclosingType;
        }

        public ISourceLocation makeSourceLocation(IHasPosition location) {
            return m_sourceContext.makeSourceLocation(location);
        }
    }

    /**
     * LazyResolvedPointcutDefinition lazyly resolve the pointcut so that we have time to register all
     * pointcut referenced before pointcut resolution happens
     *
     * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
     */
    public static class LazyResolvedPointcutDefinition extends ResolvedPointcutDefinition {
        private Pointcut m_pointcutUnresolved;
        private IScope m_binding;

        private Pointcut m_lazyPointcut = null;

        public LazyResolvedPointcutDefinition(ResolvedType declaringType, int modifiers, String name,
                                              UnresolvedType[] parameterTypes, UnresolvedType returnType,
                                              Pointcut pointcut, IScope binding) {
            super(declaringType, modifiers, name, parameterTypes, returnType, null);
            m_pointcutUnresolved = pointcut;
            m_binding = binding;
        }

        public Pointcut getPointcut() {
            if (m_lazyPointcut == 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 (int i = 0; i < bindings.length; i++) {
            FormalBinding formalBinding = bindings[i];
            if (formalBinding instanceof FormalBinding.ImplicitFormalBinding) {
                ignores.add(formalBinding.getName());
            }
        }
        pointcut.m_ignoreUnboundBindingForNames = (String[]) ignores.toArray(new String[ignores.size()]);
    }

    /**
     * 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
                    )
            );
        }
    }

    /**
     * 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 {
            Pointcut pointcut = new PatternParser(pointcutString, struct.context).parsePointcut();
            if (!allowIf && pointcutString.indexOf("if()") >= 0 && 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(), 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;
        }
    }

    /**
     * Look for an if() pointcut
     */
    private static class IfFinder extends IdentityPointcutVisitor {
        boolean hasIf = false;
        public Object visit(IfPointcut node, Object data) {
            if (node.alwaysFalse() || node.alwaysTrue()) {
                ;//IfFalse / IfTrue
            } else {
                hasIf = true;
            }
            return node;
        }

        public Object visit(AndPointcut node, Object data) {
            if (!hasIf) node.getLeft().accept(this, data);
            if (!hasIf) node.getLeft().accept(this, data);
            return node;
        }

        public Object visit(NotPointcut node, Object data) {
            if (!hasIf) node.getNegatedPointcut().accept(this, data);
            return node;
        }

        public Object visit(OrPointcut node, Object data) {
            if (!hasIf) node.getLeft().accept(this, data);
            if (!hasIf) node.getLeft().accept(this, data);
            return node;
        }
    }

    static class ThrownFormalNotDeclaredInAdviceSignatureException extends Exception {

        private String formalName;

        public ThrownFormalNotDeclaredInAdviceSignatureException(String formalName) {
            this.formalName = formalName;
        }

        public String getFormalName() { return formalName; }
    }

    static class ReturningFormalNotDeclaredInAdviceSignatureException extends Exception {

        private String formalName;

        public ReturningFormalNotDeclaredInAdviceSignatureException(String formalName) {
            this.formalName = formalName;
        }

        public String getFormalName() { return formalName; }
    }
}
TOP

Related Classes of org.aspectj.weaver.bcel.AtAjAttributes$ReturningFormalNotDeclaredInAdviceSignatureException

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.