org.aspectj.weaver.patterns.IfPointcut.java Source code

Java tutorial

Introduction

Here is the source code for org.aspectj.weaver.patterns.IfPointcut.java

Source

/* *******************************************************************
 * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
 * 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://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     PARC                 initial implementation
 *     Alexandre Vasseur    if() implementation for @AJ style
 * ******************************************************************/
package org.aspectj.weaver.patterns;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.util.FuzzyBoolean;
import org.aspectj.weaver.Advice;
import org.aspectj.weaver.AjcMemberMaker;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.CompressingDataOutputStream;
import org.aspectj.weaver.ISourceContext;
import org.aspectj.weaver.IntMap;
import org.aspectj.weaver.ResolvedMember;
import org.aspectj.weaver.ResolvedMemberImpl;
import org.aspectj.weaver.ResolvedPointcutDefinition;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.Shadow;
import org.aspectj.weaver.ShadowMunger;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.VersionedDataInputStream;
import org.aspectj.weaver.WeaverMessages;
import org.aspectj.weaver.World;
import org.aspectj.weaver.ast.Expr;
import org.aspectj.weaver.ast.Literal;
import org.aspectj.weaver.ast.Test;
import org.aspectj.weaver.ast.Var;

public class IfPointcut extends Pointcut {
    public ResolvedMember testMethod;
    public int extraParameterFlags;

    /**
     * A token source dump that looks like a pointcut (but is NOT parseable at all - just here to help debugging etc)
     */
    private final String enclosingPointcutHint;

    public Pointcut residueSource;
    int baseArgsCount;

    // XXX some way to compute args

    public IfPointcut(ResolvedMember testMethod, int extraParameterFlags) {
        this.testMethod = testMethod;
        this.extraParameterFlags = extraParameterFlags;
        this.pointcutKind = IF;
        this.enclosingPointcutHint = null;
    }

    /**
     * No-arg constructor for @AJ style, where the if() body is actually the @Pointcut annotated method
     */
    public IfPointcut(String enclosingPointcutHint) {
        this.pointcutKind = IF;
        this.enclosingPointcutHint = enclosingPointcutHint;
        this.testMethod = null;// resolved during concretize
        this.extraParameterFlags = -1;// allows to keep track of the @Aj style
    }

    @Override
    public int couldMatchKinds() {
        return Shadow.ALL_SHADOW_KINDS_BITS;
    }

    @Override
    public FuzzyBoolean fastMatch(FastMatchInfo type) {
        return FuzzyBoolean.MAYBE;
    }

    @Override
    protected FuzzyBoolean matchInternal(Shadow shadow) {
        if ((extraParameterFlags & Advice.ConstantReference) != 0) {
            if ((extraParameterFlags & Advice.ConstantValue) != 0) {
                return FuzzyBoolean.YES;
            } else {
                return FuzzyBoolean.NO;
            }
        }
        // ??? this is not maximally efficient
        return FuzzyBoolean.MAYBE;
    }

    public boolean alwaysFalse() {
        return false;
    }

    public boolean alwaysTrue() {
        return false;
    }

    // enh 76055
    public Pointcut getResidueSource() {
        return residueSource;
    }

    @Override
    public void write(CompressingDataOutputStream s) throws IOException {
        s.writeByte(Pointcut.IF);
        s.writeBoolean(testMethod != null); // do we have a test method?
        if (testMethod != null) {
            testMethod.write(s);
        }
        s.writeByte(extraParameterFlags);
        writeLocation(s);
    }

    public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException {
        boolean hasTestMethod = s.readBoolean();
        ResolvedMember resolvedTestMethod = null;
        if (hasTestMethod) { // should always have a test method unless @AJ style
            resolvedTestMethod = ResolvedMemberImpl.readResolvedMember(s, context);
        }
        IfPointcut ret = new IfPointcut(resolvedTestMethod, s.readByte());
        ret.readLocation(context, s);
        return ret;
    }

    @Override
    public void resolveBindings(IScope scope, Bindings bindings) {
        // ??? all we need is good error messages in here in cflow contexts
    }

    @Override
    public boolean equals(Object other) {
        if (!(other instanceof IfPointcut)) {
            return false;
        }
        IfPointcut o = (IfPointcut) other;
        if (o.testMethod == null) {
            return (this.testMethod == null);
        }
        return o.testMethod.equals(this.testMethod);
    }

    @Override
    public int hashCode() {
        int result = 17;
        result = 37 * result + testMethod.hashCode();
        return result;
    }

    @Override
    public String toString() {
        if (extraParameterFlags < 0) {
            // @AJ style
            return "if()";
        } else {
            return "if(" + testMethod + ")";// FIXME AV - bad, this makes it unparsable. Perhaps we can use if() for code style
            // behind the scene!
        }
    }

    // ??? The implementation of name binding and type checking in if PCDs is very convoluted
    // There has to be a better way...
    private boolean findingResidue = false;

    // Similar to lastMatchedShadowId - but only for if PCDs.
    private int ifLastMatchedShadowId;
    private Test ifLastMatchedShadowResidue;

    /**
     * At each shadow that matched, the residue can be different.
     */
    @Override
    protected Test findResidueInternal(Shadow shadow, ExposedState state) {
        if (findingResidue) {
            return Literal.TRUE;
        }
        findingResidue = true;
        try {

            // Have we already been asked this question?
            if (shadow.shadowId == ifLastMatchedShadowId) {
                return ifLastMatchedShadowResidue;
            }

            Test ret = Literal.TRUE;
            List<Var> args = new ArrayList<Var>();

            // code style
            if (extraParameterFlags >= 0) {
                if ((extraParameterFlags & Advice.ConstantReference) != 0) {
                    // it is either always true or always false, no need for test
                    if ((extraParameterFlags & Advice.ConstantValue) != 0) {
                        ret = Literal.TRUE;
                        ifLastMatchedShadowId = shadow.shadowId;
                        ifLastMatchedShadowResidue = ret;
                        return ret;
                    } else {
                        // Dont think we should be in here as the match cannot have succeeded...
                        ret = Literal.FALSE;
                        ifLastMatchedShadowId = shadow.shadowId;
                        ifLastMatchedShadowResidue = ret;
                        return ret;
                    }
                }

                // If there are no args to sort out, don't bother with the recursive call
                if (baseArgsCount > 0) {
                    ExposedState myState = new ExposedState(baseArgsCount);
                    myState.setConcreteAspect(state.getConcreteAspect());
                    // ??? we throw out the test that comes from this walk. All we want here
                    // is bindings for the arguments
                    residueSource.findResidue(shadow, myState);

                    UnresolvedType[] pTypes = (testMethod == null ? null : testMethod.getParameterTypes());
                    if (pTypes != null && baseArgsCount > pTypes.length) { // pr155347
                        throw new BCException("Unexpected problem with testMethod " + testMethod + ": expecting "
                                + baseArgsCount + " arguments");
                    }
                    // pr118149
                    // It is possible for vars in myState (which would normally be set
                    // in the call to residueSource.findResidue) to not be set (be null)
                    // in an Or pointcut with if expressions in both branches, and where
                    // one branch is known statically to not match. In this situation we
                    // simply return Test.
                    for (int i = 0; i < baseArgsCount; i++) {
                        Var v = myState.get(i);
                        if (v == null) {
                            continue; // pr118149
                        }
                        args.add(v);
                        ret = Test.makeAnd(ret, Test.makeInstanceof(v, pTypes[i].resolve(shadow.getIWorld())));
                    }
                }

                // handle thisJoinPoint parameters
                if ((extraParameterFlags & Advice.ThisJoinPoint) != 0) {
                    args.add(shadow.getThisJoinPointVar());
                }

                if ((extraParameterFlags & Advice.ThisJoinPointStaticPart) != 0) {
                    args.add(shadow.getThisJoinPointStaticPartVar());
                }

                if ((extraParameterFlags & Advice.ThisEnclosingJoinPointStaticPart) != 0) {
                    args.add(shadow.getThisEnclosingJoinPointStaticPartVar());
                }

                if ((extraParameterFlags & Advice.ThisAspectInstance) != 0) {
                    args.add(shadow.getThisAspectInstanceVar(state.getConcreteAspect()));
                }
            } else {
                // @style is slightly different
                int currentStateIndex = 0;
                // FIXME AV - "args(jp)" test(jp, thejp) will fail here
                for (int i = 0; i < testMethod.getParameterTypes().length; i++) {
                    String argSignature = testMethod.getParameterTypes()[i].getSignature();
                    if (AjcMemberMaker.TYPEX_JOINPOINT.getSignature().equals(argSignature)) {
                        args.add(shadow.getThisJoinPointVar());
                    } else if (AjcMemberMaker.TYPEX_PROCEEDINGJOINPOINT.getSignature().equals(argSignature)) {
                        args.add(shadow.getThisJoinPointVar());
                    } else if (AjcMemberMaker.TYPEX_STATICJOINPOINT.getSignature().equals(argSignature)) {
                        args.add(shadow.getThisJoinPointStaticPartVar());
                    } else if (AjcMemberMaker.TYPEX_ENCLOSINGSTATICJOINPOINT.getSignature().equals(argSignature)) {
                        args.add(shadow.getThisEnclosingJoinPointStaticPartVar());
                    } else {
                        if (state.size() == 0 || currentStateIndex > state.size()) {
                            String[] paramNames = testMethod.getParameterNames();
                            StringBuffer errorParameter = new StringBuffer();
                            if (paramNames != null) {
                                errorParameter.append(testMethod.getParameterTypes()[i].getName()).append(" ");
                                errorParameter.append(paramNames[i]);
                                shadow.getIWorld().getMessageHandler().handleMessage(MessageUtil.error(
                                        "Missing binding for if() pointcut method.  Parameter " + (i + 1) + "("
                                                + errorParameter.toString()
                                                + ") must be bound - even in reference pointcuts  (compiler limitation)",
                                        testMethod.getSourceLocation()));
                            } else {
                                shadow.getIWorld().getMessageHandler().handleMessage(MessageUtil.error(
                                        "Missing binding for if() pointcut method.  Parameter " + (i + 1)
                                                + " must be bound - even in reference pointcuts (compiler limitation)",
                                        testMethod.getSourceLocation()));
                            }
                            return Literal.TRUE; // exit quickly
                        }
                        // we don't use i as JoinPoint.* can be anywhere in the signature in @style
                        Var v = state.get(currentStateIndex++);

                        while (v == null && currentStateIndex < state.size()) { // pr162135
                            v = state.get(currentStateIndex++);
                        }
                        args.add(v);

                        ret = Test.makeAnd(ret, Test.makeInstanceof(v,
                                testMethod.getParameterTypes()[i].resolve(shadow.getIWorld())));
                    }
                }
            }

            ret = Test.makeAnd(ret, Test.makeCall(testMethod, (Expr[]) args.toArray(new Expr[args.size()])));

            // Remember...
            ifLastMatchedShadowId = shadow.shadowId;
            ifLastMatchedShadowResidue = ret;
            return ret;
        } finally {
            findingResidue = false;
        }
    }

    // amc - the only reason this override seems to be here is to stop the copy, but
    // that can be prevented by overriding shouldCopyLocationForConcretization,
    // allowing me to make the method final in Pointcut.
    // public Pointcut concretize(ResolvedType inAspect, IntMap bindings) {
    // return this.concretize1(inAspect, bindings);
    // }

    @Override
    protected boolean shouldCopyLocationForConcretize() {
        return false;
    }

    private IfPointcut partiallyConcretized = null;

    @Override
    public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) {
        // System.err.println("concretize: " + this + " already: " + partiallyConcretized);

        if (isDeclare(bindings.getEnclosingAdvice())) {
            // Enforce rule about which designators are supported in declare
            inAspect.getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.IF_IN_DECLARE),
                    bindings.getEnclosingAdvice().getSourceLocation(), null);
            return Pointcut.makeMatchesNothing(Pointcut.CONCRETE);
        }

        if (partiallyConcretized != null) {
            return partiallyConcretized;
        }

        final IfPointcut ret;
        if (extraParameterFlags < 0 && testMethod == null) {
            // @AJ style, we need to find the testMethod in the aspect defining the "if()" enclosing pointcut
            ResolvedPointcutDefinition def = bindings.peekEnclosingDefinition();
            if (def != null) {
                ResolvedType aspect = inAspect.getWorld().resolve(def.getDeclaringType());
                for (Iterator memberIter = aspect.getMethods(true, true); memberIter.hasNext();) {
                    ResolvedMember method = (ResolvedMember) memberIter.next();
                    if (def.getName().equals(method.getName())
                            && def.getParameterTypes().length == method.getParameterTypes().length) {
                        boolean sameSig = true;
                        for (int j = 0; j < method.getParameterTypes().length; j++) {
                            UnresolvedType argJ = method.getParameterTypes()[j];
                            if (!argJ.equals(def.getParameterTypes()[j])) {
                                sameSig = false;
                                break;
                            }
                        }
                        if (sameSig) {
                            testMethod = method;
                            break;
                        }
                    }
                }
                if (testMethod == null) {
                    inAspect.getWorld().showMessage(IMessage.ERROR, "Cannot find if() body from '" + def.toString()
                            + "' for '" + enclosingPointcutHint + "'", this.getSourceLocation(), null);
                    return Pointcut.makeMatchesNothing(Pointcut.CONCRETE);
                }
            } else {
                testMethod = inAspect.getWorld().resolve(bindings.getAdviceSignature());
            }
            ret = new IfPointcut(enclosingPointcutHint);
            ret.testMethod = testMethod;
        } else {
            ret = new IfPointcut(testMethod, extraParameterFlags);
        }
        ret.copyLocationFrom(this);
        partiallyConcretized = ret;

        // It is possible to directly code your pointcut expression in a per clause
        // rather than defining a pointcut declaration and referencing it in your
        // per clause. If you do this, we have problems (bug #62458). For now,
        // let's police that you are trying to code a pointcut in a per clause and
        // put out a compiler error.
        if (bindings.directlyInAdvice() && bindings.getEnclosingAdvice() == null) {
            // Assumption: if() is in a per clause if we say we are directly in advice
            // but we have no enclosing advice.
            inAspect.getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.IF_IN_PERCLAUSE),
                    this.getSourceLocation(), null);
            return Pointcut.makeMatchesNothing(Pointcut.CONCRETE);
        }

        if (bindings.directlyInAdvice()) {
            ShadowMunger advice = bindings.getEnclosingAdvice();
            if (advice instanceof Advice) {
                ret.baseArgsCount = ((Advice) advice).getBaseParameterCount();
            } else {
                ret.baseArgsCount = 0;
            }
            ret.residueSource = advice.getPointcut().concretize(inAspect, inAspect, ret.baseArgsCount, advice);
        } else {
            ResolvedPointcutDefinition def = bindings.peekEnclosingDefinition();
            if (def == CflowPointcut.CFLOW_MARKER) {
                inAspect.getWorld().showMessage(IMessage.ERROR,
                        WeaverMessages.format(WeaverMessages.IF_LEXICALLY_IN_CFLOW), getSourceLocation(), null);
                return Pointcut.makeMatchesNothing(Pointcut.CONCRETE);
            }
            ret.baseArgsCount = def.getParameterTypes().length;

            // for @style, we have implicit binding for JoinPoint.* things
            // FIXME AV - will lead to failure for "args(jp)" test(jp, thejp) / see args() implementation
            if (ret.extraParameterFlags < 0) {
                ret.baseArgsCount = 0;
                for (int i = 0; i < testMethod.getParameterTypes().length; i++) {
                    String argSignature = testMethod.getParameterTypes()[i].getSignature();
                    if (AjcMemberMaker.TYPEX_JOINPOINT.getSignature().equals(argSignature)
                            || AjcMemberMaker.TYPEX_PROCEEDINGJOINPOINT.getSignature().equals(argSignature)
                            || AjcMemberMaker.TYPEX_STATICJOINPOINT.getSignature().equals(argSignature)
                            || AjcMemberMaker.TYPEX_ENCLOSINGSTATICJOINPOINT.getSignature().equals(argSignature)) {

                    } else {
                        ret.baseArgsCount++;
                    }
                }
            }

            IntMap newBindings = IntMap.idMap(ret.baseArgsCount);
            newBindings.copyContext(bindings);
            ret.residueSource = def.getPointcut().concretize(inAspect, declaringType, newBindings);
        }

        return ret;
    }

    // we can't touch "if" methods
    @Override
    public Pointcut parameterizeWith(Map typeVariableMap, World w) {
        return this;
    }

    // public static Pointcut MatchesNothing = new MatchesNothingPointcut();
    // ??? there could possibly be some good optimizations to be done at this point
    public static IfPointcut makeIfFalsePointcut(State state) {
        IfPointcut ret = new IfFalsePointcut();
        ret.state = state;
        return ret;
    }

    @Override
    public Object accept(PatternNodeVisitor visitor, Object data) {
        return visitor.visit(this, data);
    }

    public static class IfFalsePointcut extends IfPointcut {

        public IfFalsePointcut() {
            super(null, 0);
            this.pointcutKind = Pointcut.IF_FALSE;
        }

        @Override
        public int couldMatchKinds() {
            return Shadow.NO_SHADOW_KINDS_BITS;
        }

        @Override
        public boolean alwaysFalse() {
            return true;
        }

        @Override
        protected Test findResidueInternal(Shadow shadow, ExposedState state) {
            return Literal.FALSE; // can only get here if an earlier error occurred
        }

        @Override
        public FuzzyBoolean fastMatch(FastMatchInfo type) {
            return FuzzyBoolean.NO;
        }

        @Override
        protected FuzzyBoolean matchInternal(Shadow shadow) {
            return FuzzyBoolean.NO;
        }

        @Override
        public void resolveBindings(IScope scope, Bindings bindings) {
        }

        @Override
        public void postRead(ResolvedType enclosingType) {
        }

        @Override
        public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) {
            if (isDeclare(bindings.getEnclosingAdvice())) {
                // Enforce rule about which designators are supported in declare
                inAspect.getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.IF_IN_DECLARE),
                        bindings.getEnclosingAdvice().getSourceLocation(), null);
                return Pointcut.makeMatchesNothing(Pointcut.CONCRETE);
            }
            return makeIfFalsePointcut(state);
        }

        @Override
        public void write(CompressingDataOutputStream s) throws IOException {
            s.writeByte(Pointcut.IF_FALSE);
        }

        @Override
        public int hashCode() {
            int result = 17;
            return result;
        }

        @Override
        public String toString() {
            return "if(false)";
        }
    }

    public static IfPointcut makeIfTruePointcut(State state) {
        IfPointcut ret = new IfTruePointcut();
        ret.state = state;
        return ret;
    }

    public static class IfTruePointcut extends IfPointcut {

        public IfTruePointcut() {
            super(null, 0);
            this.pointcutKind = Pointcut.IF_TRUE;
        }

        @Override
        public boolean alwaysTrue() {
            return true;
        }

        @Override
        protected Test findResidueInternal(Shadow shadow, ExposedState state) {
            return Literal.TRUE; // can only get here if an earlier error occurred
        }

        @Override
        public FuzzyBoolean fastMatch(FastMatchInfo type) {
            return FuzzyBoolean.YES;
        }

        @Override
        protected FuzzyBoolean matchInternal(Shadow shadow) {
            return FuzzyBoolean.YES;
        }

        @Override
        public void resolveBindings(IScope scope, Bindings bindings) {
        }

        @Override
        public void postRead(ResolvedType enclosingType) {
        }

        @Override
        public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) {
            if (isDeclare(bindings.getEnclosingAdvice())) {
                // Enforce rule about which designators are supported in declare
                inAspect.getWorld().showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.IF_IN_DECLARE),
                        bindings.getEnclosingAdvice().getSourceLocation(), null);
                return Pointcut.makeMatchesNothing(Pointcut.CONCRETE);
            }
            return makeIfTruePointcut(state);
        }

        @Override
        public void write(CompressingDataOutputStream s) throws IOException {
            s.writeByte(IF_TRUE);
        }

        @Override
        public int hashCode() {
            int result = 37;
            return result;
        }

        @Override
        public String toString() {
            return "if(true)";
        }
    }

    /**
     * Called when it is determined that the pointcut refers to a constant value of TRUE or FALSE - enabling exact matching and no
     * unnecessary calls to the method representing the if body.
     */
    public void setAlways(boolean matches) {
        extraParameterFlags |= Advice.ConstantReference;
        if (matches) {
            extraParameterFlags |= Advice.ConstantValue;
        }
    }

}