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

Java tutorial

Introduction

Here is the source code for org.aspectj.weaver.patterns.CflowPointcut.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 
 * ******************************************************************/

package org.aspectj.weaver.patterns;

import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.aspectj.bridge.IMessage;
import org.aspectj.util.FileUtil;
import org.aspectj.util.FuzzyBoolean;
import org.aspectj.weaver.Advice;
import org.aspectj.weaver.CompressingDataOutputStream;
import org.aspectj.weaver.CrosscuttingMembers;
import org.aspectj.weaver.ISourceContext;
import org.aspectj.weaver.IntMap;
import org.aspectj.weaver.Member;
import org.aspectj.weaver.NameMangler;
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.Test;
import org.aspectj.weaver.patterns.ConcreteCflowPointcut.Slot;

public class CflowPointcut extends Pointcut {
    private final Pointcut entry; // The pointcut inside the cflow() that
    // represents the 'entry' point
    boolean isBelow;// Is this cflowbelow?
    private int[] freeVars;

    /**
     * Used to indicate that we're in the context of a cflow when concretizing if's
     * 
     * Will be removed or replaced with something better when we handle this as a non-error
     */
    public static final ResolvedPointcutDefinition CFLOW_MARKER = new ResolvedPointcutDefinition(null, 0, null,
            UnresolvedType.NONE, Pointcut.makeMatchesNothing(Pointcut.RESOLVED));

    public CflowPointcut(Pointcut entry, boolean isBelow, int[] freeVars) {
        // System.err.println("Building cflow pointcut "+entry.toString());
        this.entry = entry;
        this.isBelow = isBelow;
        this.freeVars = freeVars;
        pointcutKind = CFLOW;
    }

    public boolean isCflowBelow() {
        return isBelow;
    }

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

    // enh 76055
    public Pointcut getEntry() {
        return entry;
    }

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

    protected FuzzyBoolean matchInternal(Shadow shadow) {
        // ??? this is not maximally efficient
        return FuzzyBoolean.MAYBE;
    }

    public void write(CompressingDataOutputStream s) throws IOException {
        s.writeByte(Pointcut.CFLOW);
        entry.write(s);
        s.writeBoolean(isBelow);
        FileUtil.writeIntArray(freeVars, s);
        writeLocation(s);
    }

    public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException {

        CflowPointcut ret = new CflowPointcut(Pointcut.read(s, context), s.readBoolean(), FileUtil.readIntArray(s));
        ret.readLocation(context, s);
        return ret;
    }

    public Pointcut parameterizeWith(Map<String, UnresolvedType> typeVariableMap, World w) {
        CflowPointcut ret = new CflowPointcut(entry.parameterizeWith(typeVariableMap, w), isBelow, freeVars);
        ret.copyLocationFrom(this);
        return ret;
    }

    public void resolveBindings(IScope scope, Bindings bindings) {
        if (bindings == null) {
            entry.resolveBindings(scope, null);
            entry.state = RESOLVED;
            freeVars = new int[0];
        } else {
            // ??? for if's sake we might need to be more careful here
            Bindings entryBindings = new Bindings(bindings.size());

            entry.resolveBindings(scope, entryBindings);
            entry.state = RESOLVED;

            freeVars = entryBindings.getUsedFormals();

            bindings.mergeIn(entryBindings, scope);
        }
    }

    public boolean equals(Object other) {
        if (!(other instanceof CflowPointcut)) {
            return false;
        }
        CflowPointcut o = (CflowPointcut) other;
        return o.entry.equals(entry) && o.isBelow == isBelow;
    }

    public int hashCode() {
        int result = 17;
        result = 37 * result + entry.hashCode();
        result = 37 * result + (isBelow ? 0 : 1);
        return result;
    }

    public String toString() {
        return "cflow" + (isBelow ? "below" : "") + "(" + entry + ")";
    }

    protected Test findResidueInternal(Shadow shadow, ExposedState state) {
        throw new RuntimeException("unimplemented - did concretization fail?");
    }

    public Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) {

        // the pointcut is marked as CONCRETE after returning from this
        // call - so we can't skip concretization
        // if (this.entry.state == Pointcut.SYMBOLIC) {
        // // too early to concretize, return unchanged
        // return this;
        // }

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

        // make this remap from formal positions to arrayIndices
        IntMap entryBindings = new IntMap();
        if (freeVars != null) {
            for (int i = 0, len = freeVars.length; i < len; i++) {
                int freeVar = freeVars[i];
                // int formalIndex = bindings.get(freeVar);
                entryBindings.put(freeVar, i);
            }
        }
        entryBindings.copyContext(bindings);
        // System.out.println(this + " bindings: " + entryBindings);

        World world = inAspect.getWorld();

        Pointcut concreteEntry;

        ResolvedType concreteAspect = bindings.getConcreteAspect();

        CrosscuttingMembers xcut = concreteAspect.crosscuttingMembers;
        Collection<ShadowMunger> previousCflowEntries = xcut.getCflowEntries();

        entryBindings.pushEnclosingDefinition(CFLOW_MARKER);
        // This block concretizes the pointcut within the cflow pointcut
        try {
            concreteEntry = entry.concretize(inAspect, declaringType, entryBindings);
        } finally {
            entryBindings.popEnclosingDefinitition();
        }

        List<ShadowMunger> innerCflowEntries = new ArrayList<ShadowMunger>(xcut.getCflowEntries());
        innerCflowEntries.removeAll(previousCflowEntries);

        // Four routes of interest through this code (did I hear someone say
        // refactor??)
        // 1) no state in the cflow - we can use a counter *and* we have seen
        // this pointcut
        // before - so use the same counter as before.
        // 2) no state in the cflow - we can use a counter, but this is the
        // first time
        // we have seen this pointcut, so build the infrastructure.
        // 3) state in the cflow - we need to use a stack *and* we have seen
        // this pointcut
        // before - so share the stack.
        // 4) state in the cflow - we need to use a stack, but this is the first
        // time
        // we have seen this pointcut, so build the infrastructure.

        if (freeVars == null || freeVars.length == 0) { // No state, so don't
            // use a stack, use a
            // counter.
            ResolvedMember localCflowField = null;

            Object field = getCflowfield(xcut, concreteEntry, concreteAspect, "counter");

            // Check if we have already got a counter for this cflow pointcut
            if (field != null) {
                localCflowField = (ResolvedMember) field; // Use the one we
                // already have

            } else {

                // Create a counter field in the aspect
                localCflowField = new ResolvedMemberImpl(Member.FIELD, concreteAspect,
                        Modifier.STATIC | Modifier.PUBLIC, NameMangler.cflowCounter(xcut),
                        UnresolvedType.forName(NameMangler.CFLOW_COUNTER_TYPE).getSignature());

                // Create type munger to add field to the aspect
                concreteAspect.crosscuttingMembers
                        .addTypeMunger(world.getWeavingSupport().makeCflowCounterFieldAdder(localCflowField));

                // Create shadow munger to push stuff onto the stack
                concreteAspect.crosscuttingMembers.addConcreteShadowMunger(
                        Advice.makeCflowEntry(world, concreteEntry, isBelow, localCflowField,
                                freeVars == null ? 0 : freeVars.length, innerCflowEntries, inAspect));

                putCflowfield(xcut, concreteEntry, concreteAspect, localCflowField, "counter"); // Remember
                // it
            }

            Pointcut ret = new ConcreteCflowPointcut(concreteAspect, localCflowField, null, true);
            ret.copyLocationFrom(this);
            return ret;
        } else {

            List<Slot> slots = new ArrayList<Slot>();

            for (int i = 0, len = freeVars.length; i < len; i++) {
                int freeVar = freeVars[i];

                // we don't need to keep state that isn't actually exposed to
                // advice
                // ??? this means that we will store some state that we won't
                // actually use, optimize this later
                if (!bindings.hasKey(freeVar)) {
                    continue;
                }

                int formalIndex = bindings.get(freeVar);

                // We need to look in the right place for the type of the
                // formal. Suppose the advice looks like this:
                // before(String s): somePointcut(*,s)
                // where the first argument in somePointcut is of type Number
                // for free variable 0 we want to ask the pointcut for the type
                // of its first argument, if we only
                // ask the advice for the type of its first argument then we'll
                // get the wrong type (pr86903)

                ResolvedPointcutDefinition enclosingDef = bindings.peekEnclosingDefinition();
                ResolvedType formalType = null;

                // Is there a useful enclosing pointcut?
                if (enclosingDef != null && enclosingDef.getParameterTypes().length > 0) {
                    formalType = enclosingDef.getParameterTypes()[freeVar].resolve(world);
                } else {
                    formalType = bindings.getAdviceSignature().getParameterTypes()[formalIndex].resolve(world);
                }

                ConcreteCflowPointcut.Slot slot = new ConcreteCflowPointcut.Slot(formalIndex, formalType, i);
                slots.add(slot);
            }
            ResolvedMember localCflowField = null;
            Object field = getCflowfield(xcut, concreteEntry, concreteAspect, "stack");
            if (field != null) {
                localCflowField = (ResolvedMember) field;
            } else {

                localCflowField = new ResolvedMemberImpl(Member.FIELD, concreteAspect,
                        Modifier.STATIC | Modifier.PUBLIC, NameMangler.cflowStack(xcut),
                        UnresolvedType.forName(NameMangler.CFLOW_STACK_TYPE).getSignature());
                // System.out.println("adding field to: " + inAspect + " field "
                // + cflowField);

                // add field and initializer to inAspect
                // XXX and then that info above needs to be mapped down here to
                // help with
                // XXX getting the exposed state right
                concreteAspect.crosscuttingMembers.addConcreteShadowMunger(Advice.makeCflowEntry(world,
                        concreteEntry, isBelow, localCflowField, freeVars.length, innerCflowEntries, inAspect));

                concreteAspect.crosscuttingMembers
                        .addTypeMunger(world.getWeavingSupport().makeCflowStackFieldAdder(localCflowField));
                putCflowfield(xcut, concreteEntry, concreteAspect, localCflowField, "stack");
            }
            Pointcut ret = new ConcreteCflowPointcut(concreteAspect, localCflowField, slots, false);
            ret.copyLocationFrom(this);
            return ret;
        }

    }

    private String getKey(Pointcut p, ResolvedType a, String stackOrCounter) {
        StringBuffer sb = new StringBuffer();
        sb.append(a.getName());
        sb.append("::");
        sb.append(p.toString());
        sb.append("::");
        sb.append(stackOrCounter);
        return sb.toString();
    }

    private Object getCflowfield(CrosscuttingMembers xcut, Pointcut pcutkey, ResolvedType concreteAspect,
            String stackOrCounter) {
        String key = getKey(pcutkey, concreteAspect, stackOrCounter);
        Object o = null;
        if (isBelow) {
            o = xcut.getCflowBelowFields().get(key);
        } else {
            o = xcut.getCflowFields().get(key);
        }
        // System.err.println("Retrieving for key "+key+" returning "+o);
        return o;
    }

    private void putCflowfield(CrosscuttingMembers xcut, Pointcut pcutkey, ResolvedType concreteAspect, Object o,
            String stackOrCounter) {
        String key = getKey(pcutkey, concreteAspect, stackOrCounter);
        // System.err.println("Storing cflow field for key"+key);
        if (isBelow) {
            xcut.getCflowBelowFields().put(key, o);
        } else {
            xcut.getCflowFields().put(key, o);
        }
    }

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

}