123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440 |
- /* *******************************************************************
- * 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.util.Map;
-
- import org.aspectj.util.FuzzyBoolean;
- import org.aspectj.util.TypeSafeEnum;
- import org.aspectj.weaver.Advice;
- import org.aspectj.weaver.AdviceKind;
- import org.aspectj.weaver.BCException;
- import org.aspectj.weaver.Checker;
- import org.aspectj.weaver.CompressingDataOutputStream;
- import org.aspectj.weaver.ISourceContext;
- import org.aspectj.weaver.IntMap;
- import org.aspectj.weaver.PoliceExtensionUse;
- 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.World;
- import org.aspectj.weaver.ast.Literal;
- import org.aspectj.weaver.ast.Test;
-
- /**
- * The lifecycle of Pointcuts is modeled by Pointcut.State. It has three things:
- *
- * <p>
- * Creation -- SYMBOLIC -- then resolve(IScope) -- RESOLVED -- concretize(...) -- CONCRETE
- *
- * @author Erik Hilsdale
- * @author Jim Hugunin
- *
- * A day in the life of a pointcut.... - AMC. ==========================================
- *
- * Pointcuts are created by the PatternParser, which is called by ajdt to parse a pointcut from the PseudoTokens AST node
- * (which in turn are part of a PointcutDesignator AST node).
- *
- * Pointcuts are resolved by ajdt when an AdviceDeclaration or a PointcutDeclaration has its statements resolved. This
- * happens as part of completeTypeBindings in the AjLookupEnvironment which is called after the diet parse phase of the
- * compiler. Named pointcuts, and references to named pointcuts are instances of ReferencePointcut.
- *
- * At the end of the compilation process, the pointcuts are serialized (write method) into attributes in the class file.
- *
- * When the weaver loads the class files, it unpacks the attributes and deserializes the pointcuts (read). All aspects are
- * added to the world, by calling addOrReplaceAspect on the crosscutting members set of the world. When aspects are added or
- * replaced, the crosscutting members in the aspect are extracted as ShadowMungers (each holding a pointcut). The
- * ShadowMungers are concretized, which concretizes the pointcuts. At this stage ReferencePointcuts are replaced by their
- * declared content.
- *
- * During weaving, the weaver processes type by type. It first culls potentially matching ShadowMungers by calling the
- * fastMatch method on their pointcuts. Only those that might match make it through to the next phase. At the next phase,
- * all of the shadows within the type are created and passed to the pointcut for matching (match).
- *
- * When the actual munging happens, matched pointcuts are asked for their residue (findResidue) - the runtime test if any.
- * Because of negation, findResidue may be called on pointcuts that could never match the shadow.
- *
- */
- public abstract class Pointcut extends PatternNode {
- public static final class State extends TypeSafeEnum {
- public State(String name, int key) {
- super(name, key);
- }
- }
-
- /**
- * ATAJ the name of the formal for which we don't want any warning when unbound since we consider them as implicitly bound. f.e.
- * JoinPoint for @AJ advices
- */
- public String[] m_ignoreUnboundBindingForNames = EMPTY_STRING_ARRAY;
-
- public static final String[] EMPTY_STRING_ARRAY = new String[0];
-
- public static final State SYMBOLIC = new State("symbolic", 0);
- public static final State RESOLVED = new State("resolved", 1);
- public static final State CONCRETE = new State("concrete", 2);
-
- protected byte pointcutKind;
-
- public State state;
-
- protected int lastMatchedShadowId;
- private FuzzyBoolean lastMatchedShadowResult;
- private String[] typeVariablesInScope = EMPTY_STRING_ARRAY;
-
- protected boolean hasBeenParameterized = false;
-
- /**
- * Constructor for Pattern.
- */
- public Pointcut() {
- super();
- this.state = SYMBOLIC;
- }
-
- /**
- * Could I match any shadows in the code defined within this type?
- */
- public abstract FuzzyBoolean fastMatch(FastMatchInfo info);
-
- /**
- * The set of ShadowKinds that this Pointcut could possibly match - an int whose bits are set according to the Kinds specified
- * in Shadow.java
- */
- public abstract int couldMatchKinds();
-
- public String[] getTypeVariablesInScope() {
- return typeVariablesInScope;
- }
-
- public void setTypeVariablesInScope(String[] typeVars) {
- this.typeVariablesInScope = typeVars;
- }
-
- /**
- * Do I really match this shadow? XXX implementors need to handle state
- */
- public final FuzzyBoolean match(Shadow shadow) {
- if (shadow.shadowId == lastMatchedShadowId) {
- return lastMatchedShadowResult;
- }
- FuzzyBoolean ret;
- // this next test will prevent a lot of un-needed matching going on....
- if (shadow.getKind().isSet(couldMatchKinds())) {
- ret = matchInternal(shadow);
- } else {
- ret = FuzzyBoolean.NO;
- }
- lastMatchedShadowId = shadow.shadowId;
- lastMatchedShadowResult = ret;
- return ret;
- }
-
- protected abstract FuzzyBoolean matchInternal(Shadow shadow);
-
- public static final byte KINDED = 1;
- public static final byte WITHIN = 2;
- public static final byte THIS_OR_TARGET = 3;
- public static final byte ARGS = 4;
- public static final byte AND = 5;
- public static final byte OR = 6;
- public static final byte NOT = 7;
- public static final byte REFERENCE = 8;
- public static final byte IF = 9;
- public static final byte CFLOW = 10;
- public static final byte WITHINCODE = 12;
- public static final byte HANDLER = 13;
- public static final byte IF_TRUE = 14;
- public static final byte IF_FALSE = 15;
- public static final byte ANNOTATION = 16;
- public static final byte ATWITHIN = 17;
- public static final byte ATWITHINCODE = 18;
- public static final byte ATTHIS_OR_TARGET = 19;
-
- public static final byte NONE = 20; // DO NOT CHANGE OR REORDER THIS SEQUENCE, THIS VALUE CAN BE PUT OUT BY ASPECTJ1.2.1
-
- public static final byte ATARGS = 21;
- public static final byte USER_EXTENSION = 22;
- public static final byte THIS_OR_TARGET_WITH_OPTIONAL = 23;
-
- public byte getPointcutKind() {
- return pointcutKind;
- }
-
- // internal, only called from resolve
- protected abstract void resolveBindings(IScope scope, Bindings bindings);
-
- /**
- * Returns this pointcut mutated
- */
- public final Pointcut resolve(IScope scope) {
- assertState(SYMBOLIC);
- Bindings bindingTable = new Bindings(scope.getFormalCount());
- IScope bindingResolutionScope = scope;
- if (typeVariablesInScope.length > 0) {
- bindingResolutionScope = new ScopeWithTypeVariables(typeVariablesInScope, scope);
- }
- this.resolveBindings(bindingResolutionScope, bindingTable);
- bindingTable.checkAllBound(bindingResolutionScope);
- this.state = RESOLVED;
- return this;
- }
-
- /**
- * Returns a new pointcut Only used by test cases
- */
- public final Pointcut concretize(ResolvedType inAspect, ResolvedType declaringType, int arity) {
- Pointcut ret = concretize(inAspect, declaringType, IntMap.idMap(arity));
- // copy the unbound ignore list
- ret.m_ignoreUnboundBindingForNames = m_ignoreUnboundBindingForNames;
- return ret;
- }
-
- // XXX this is the signature we're moving to
- public final Pointcut concretize(ResolvedType inAspect, ResolvedType declaringType, int arity, ShadowMunger advice) {
- // if (state == CONCRETE) return this; //???
- IntMap map = IntMap.idMap(arity);
- map.setEnclosingAdvice(advice);
- map.setConcreteAspect(inAspect);
- return concretize(inAspect, declaringType, map);
- }
-
- public boolean isDeclare(ShadowMunger munger) {
- if (munger == null) {
- return false; // ??? Is it actually an error if we get a null munger into this method.
- }
- if (munger instanceof Checker) {
- return true;
- }
- if (((Advice) munger).getKind().equals(AdviceKind.Softener)) {
- return true;
- }
- return false;
- }
-
- public final Pointcut concretize(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) {
- // !!! add this test -- assertState(RESOLVED);
- Pointcut ret = this.concretize1(inAspect, declaringType, bindings);
- if (shouldCopyLocationForConcretize()) {
- ret.copyLocationFrom(this);
- }
- ret.state = CONCRETE;
- // copy the unbound ignore list
- ret.m_ignoreUnboundBindingForNames = m_ignoreUnboundBindingForNames;
- return ret;
- }
-
- protected boolean shouldCopyLocationForConcretize() {
- return true;
- }
-
- /**
- * Resolves and removes ReferencePointcuts, replacing with basic ones
- *
- * @param inAspect the aspect to resolve relative to
- * @param bindings a Map from formal index in the current lexical context -> formal index in the concrete advice that will run
- *
- * This must always return a new Pointcut object (even if the concretized Pointcut is identical to the resolved one).
- * That behavior is assumed in many places. XXX fix implementors to handle state
- */
- protected abstract Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings);
-
- // XXX implementors need to handle state
- /**
- * This can be called from NotPointcut even for Pointcuts that don't match the shadow
- */
- public final Test findResidue(Shadow shadow, ExposedState state) {
- // if (shadow.shadowId == lastMatchedShadowId) return lastMatchedShadowResidue;
- Test ret = findResidueInternal(shadow, state);
- // lastMatchedShadowResidue = ret;
- lastMatchedShadowId = shadow.shadowId;
- return ret;
- }
-
- protected abstract Test findResidueInternal(Shadow shadow, ExposedState state);
-
- // XXX we're not sure whether or not this is needed
- // XXX currently it's unused we're keeping it around as a stub
- public void postRead(ResolvedType enclosingType) {
- }
-
- public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException {
- byte kind = s.readByte();
- Pointcut ret;
-
- switch (kind) {
- case KINDED:
- ret = KindedPointcut.read(s, context);
- break;
- case WITHIN:
- ret = WithinPointcut.read(s, context);
- break;
- case THIS_OR_TARGET:
- ret = ThisOrTargetPointcut.read(s, context, false);
- break;
- case ARGS:
- ret = ArgsPointcut.read(s, context);
- break;
- case AND:
- ret = AndPointcut.read(s, context);
- break;
- case OR:
- ret = OrPointcut.read(s, context);
- break;
- case NOT:
- ret = NotPointcut.read(s, context);
- break;
- case REFERENCE:
- ret = ReferencePointcut.read(s, context);
- break;
- case IF:
- ret = IfPointcut.read(s, context);
- break;
- case CFLOW:
- ret = CflowPointcut.read(s, context);
- break;
- case WITHINCODE:
- ret = WithincodePointcut.read(s, context);
- break;
- case HANDLER:
- ret = HandlerPointcut.read(s, context);
- break;
- case IF_TRUE:
- ret = IfPointcut.makeIfTruePointcut(RESOLVED);
- break;
- case IF_FALSE:
- ret = IfPointcut.makeIfFalsePointcut(RESOLVED);
- break;
- case ANNOTATION:
- ret = AnnotationPointcut.read(s, context);
- break;
- case ATWITHIN:
- ret = WithinAnnotationPointcut.read(s, context);
- break;
- case ATWITHINCODE:
- ret = WithinCodeAnnotationPointcut.read(s, context);
- break;
- case ATTHIS_OR_TARGET:
- ret = ThisOrTargetAnnotationPointcut.read(s, context);
- break;
- case ATARGS:
- ret = ArgsAnnotationPointcut.read(s, context);
- break;
- case NONE:
- ret = makeMatchesNothing(RESOLVED);
- break;
- case THIS_OR_TARGET_WITH_OPTIONAL:
- ret = ThisOrTargetPointcut.read(s,context,true);
- break;
- default:
- throw new BCException("unknown kind: " + kind);
- }
- ret.state = RESOLVED;
- ret.pointcutKind = kind;
- return ret;
-
- }
-
- public void check(ISourceContext ctx, World world) {
- // this is a quick visitor...
- PoliceExtensionUse pointcutPolice = new PoliceExtensionUse(world, this);
- this.accept(pointcutPolice, null);
- if (pointcutPolice.synchronizationDesignatorEncountered()) {
- world.setSynchronizationPointcutsInUse();
- }
- }
-
- // public void prepare(Shadow shadow) {}
-
- // ---- test method
-
- public static Pointcut fromString(String str) {
- PatternParser parser = new PatternParser(str);
- return parser.parsePointcut();
- }
-
- static class MatchesNothingPointcut extends Pointcut {
- @Override
- protected Test findResidueInternal(Shadow shadow, ExposedState state) {
- return Literal.FALSE; // can only get here if an earlier error occurred
- }
-
- @Override
- public int couldMatchKinds() {
- return Shadow.NO_SHADOW_KINDS_BITS;
- }
-
- @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) {
- return makeMatchesNothing(state);
- }
-
- @Override
- public void write(CompressingDataOutputStream s) throws IOException {
- s.writeByte(NONE);
- }
-
- @Override
- public String toString() {
- return "";
- }
-
- @Override
- public Object accept(PatternNodeVisitor visitor, Object data) {
- return visitor.visit(this, data);
- }
-
- @Override
- public Pointcut parameterizeWith(Map<String, UnresolvedType> 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 Pointcut makeMatchesNothing(State state) {
- Pointcut ret = new MatchesNothingPointcut();
- ret.state = state;
- return ret;
- }
-
- public void assertState(State state) {
- if (this.state != state) {
- throw new BCException("expected state: " + state + " got: " + this.state);
- }
- }
-
- public abstract Pointcut parameterizeWith(Map<String, UnresolvedType> typeVariableMap, World w);
-
- }
|