You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Pointcut.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. /* *******************************************************************
  2. * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
  3. * All rights reserved.
  4. * This program and the accompanying materials are made available
  5. * under the terms of the Common Public License v1.0
  6. * which accompanies this distribution and is available at
  7. * http://www.eclipse.org/legal/cpl-v10.html
  8. *
  9. * Contributors:
  10. * PARC initial implementation
  11. * ******************************************************************/
  12. package org.aspectj.weaver.patterns;
  13. import java.io.DataInputStream;
  14. import java.io.DataOutputStream;
  15. import java.io.IOException;
  16. import java.lang.reflect.Member;
  17. import java.util.Collections;
  18. import java.util.Set;
  19. import org.aspectj.lang.JoinPoint;
  20. import org.aspectj.util.FuzzyBoolean;
  21. import org.aspectj.util.TypeSafeEnum;
  22. import org.aspectj.weaver.Advice;
  23. import org.aspectj.weaver.AdviceKind;
  24. import org.aspectj.weaver.BCException;
  25. import org.aspectj.weaver.Checker;
  26. import org.aspectj.weaver.ISourceContext;
  27. import org.aspectj.weaver.IntMap;
  28. import org.aspectj.weaver.ResolvedTypeX;
  29. import org.aspectj.weaver.Shadow;
  30. import org.aspectj.weaver.ShadowMunger;
  31. import org.aspectj.weaver.ast.Literal;
  32. import org.aspectj.weaver.ast.Test;
  33. /**
  34. * The lifecycle of Pointcuts is modeled by Pointcut.State. It has three things:
  35. *
  36. * <p>Creation -- SYMBOLIC -- then resolve(IScope) -- RESOLVED -- concretize(...) -- CONCRETE
  37. *
  38. * @author Erik Hilsdale
  39. * @author Jim Hugunin
  40. *
  41. * A day in the life of a pointcut.... - AMC.
  42. * ==========================================
  43. *
  44. * Pointcuts are created by the PatternParser, which is called by ajdt to
  45. * parse a pointcut from the PseudoTokens AST node (which in turn are part
  46. * of a PointcutDesignator AST node).
  47. *
  48. * Pointcuts are resolved by ajdt when an AdviceDeclaration or a
  49. * PointcutDeclaration has its statements resolved. This happens as
  50. * part of completeTypeBindings in the AjLookupEnvironment which is
  51. * called after the diet parse phase of the compiler. Named pointcuts,
  52. * and references to named pointcuts are instances of ReferencePointcut.
  53. *
  54. * At the end of the compilation process, the pointcuts are serialized
  55. * (write method) into attributes in the class file.
  56. *
  57. * When the weaver loads the class files, it unpacks the attributes
  58. * and deserializes the pointcuts (read). All aspects are added to the
  59. * world, by calling addOrReplaceAspect on
  60. * the crosscutting members set of the world. When aspects are added or
  61. * replaced, the crosscutting members in the aspect are extracted as
  62. * ShadowMungers (each holding a pointcut). The ShadowMungers are
  63. * concretized, which concretizes the pointcuts. At this stage
  64. * ReferencePointcuts are replaced by their declared content.
  65. *
  66. * During weaving, the weaver processes type by type. It first culls
  67. * potentially matching ShadowMungers by calling the fastMatch method
  68. * on their pointcuts. Only those that might match make it through to
  69. * the next phase. At the next phase, all of the shadows within the
  70. * type are created and passed to the pointcut for matching (match).
  71. *
  72. * When the actual munging happens, matched pointcuts are asked for
  73. * their residue (findResidue) - the runtime test if any. Because of
  74. * negation, findResidue may be called on pointcuts that could never
  75. * match the shadow.
  76. *
  77. */
  78. public abstract class Pointcut extends PatternNode implements PointcutExpressionMatching {
  79. public static final class State extends TypeSafeEnum {
  80. public State(String name, int key) {
  81. super(name, key);
  82. }
  83. }
  84. public static final State SYMBOLIC = new State("symbolic", 0);
  85. public static final State RESOLVED = new State("resolved", 1);
  86. public static final State CONCRETE = new State("concrete", 2);
  87. protected byte pointcutKind;
  88. public State state;
  89. protected int lastMatchedShadowId;
  90. private FuzzyBoolean lastMatchedShadowResult;
  91. private Test lastMatchedShadowResidue;
  92. /**
  93. * Constructor for Pattern.
  94. */
  95. public Pointcut() {
  96. super();
  97. this.state = SYMBOLIC;
  98. }
  99. /**
  100. * Could I match any shadows in the code defined within this type?
  101. */
  102. public abstract FuzzyBoolean fastMatch(FastMatchInfo info);
  103. /**
  104. * The set of ShadowKinds that this Pointcut could possibly match
  105. */
  106. public abstract /*Enum*/Set/*<Shadow.Kind>*/ couldMatchKinds();
  107. /**
  108. * Do I really match this shadow?
  109. * XXX implementors need to handle state
  110. */
  111. public final FuzzyBoolean match(Shadow shadow) {
  112. if (shadow.shadowId == lastMatchedShadowId) return lastMatchedShadowResult;
  113. FuzzyBoolean ret;
  114. // this next test will prevent a lot of un-needed matching going on....
  115. if (couldMatchKinds().contains(shadow.getKind())) {
  116. ret = matchInternal(shadow);
  117. } else {
  118. ret = FuzzyBoolean.NO;
  119. }
  120. lastMatchedShadowId = shadow.shadowId;
  121. lastMatchedShadowResult = ret;
  122. return ret;
  123. }
  124. protected abstract FuzzyBoolean matchInternal(Shadow shadow);
  125. /*
  126. * for runtime / dynamic pointcuts.
  127. * Default implementation delegates to StaticPart matcher
  128. */
  129. public FuzzyBoolean match(JoinPoint jp, JoinPoint.StaticPart enclosingJoinPoint) {
  130. return match(jp.getStaticPart());
  131. }
  132. /*
  133. * for runtime / dynamic pointcuts.
  134. * Not all pointcuts can be matched at runtime, those that can should overide either
  135. * match(JoinPoint), or this method, or both.
  136. */
  137. public FuzzyBoolean match(JoinPoint.StaticPart jpsp) {
  138. throw new UnsupportedOperationException("Pointcut expression " + this.toString() + "cannot be matched at runtime");
  139. }
  140. /* (non-Javadoc)
  141. * @see org.aspectj.weaver.tools.PointcutExpression#matchesDynamically(java.lang.String, java.lang.reflect.Member, java.lang.Object, java.lang.Object, java.lang.reflect.Member)
  142. */
  143. public boolean matchesDynamically(
  144. Object thisObject, Object targetObject, Object[] args) {
  145. throw new UnsupportedOperationException("Pointcut expression " + this.toString() + "cannot be matched by this operation");
  146. }
  147. /* (non-Javadoc)
  148. * @see org.aspectj.weaver.tools.PointcutExpression#matchesStatically(java.lang.String, java.lang.reflect.Member, java.lang.Class, java.lang.Class, java.lang.reflect.Member)
  149. */
  150. public FuzzyBoolean matchesStatically(
  151. String joinpointKind, Member member, Class thisClass,
  152. Class targetClass, Member withinCode) {
  153. throw new UnsupportedOperationException("Pointcut expression " + this.toString() + "cannot be matched by this operation");
  154. }
  155. public static final byte KINDED = 1;
  156. public static final byte WITHIN = 2;
  157. public static final byte THIS_OR_TARGET = 3;
  158. public static final byte ARGS = 4;
  159. public static final byte AND = 5;
  160. public static final byte OR = 6;
  161. public static final byte NOT = 7;
  162. public static final byte REFERENCE = 8;
  163. public static final byte IF = 9;
  164. public static final byte CFLOW = 10;
  165. public static final byte WITHINCODE = 12;
  166. public static final byte HANDLER = 13;
  167. public static final byte IF_TRUE = 14;
  168. public static final byte IF_FALSE = 15;
  169. public static final byte ANNOTATION = 16;
  170. public static final byte ATWITHIN = 17;
  171. public static final byte ATWITHINCODE = 18;
  172. public static final byte ATTHIS_OR_TARGET = 19;
  173. public static final byte ATARGS = 20;
  174. public static final byte NONE = 40;
  175. public byte getPointcutKind() { return pointcutKind; }
  176. // internal, only called from resolve
  177. protected abstract void resolveBindings(IScope scope, Bindings bindings);
  178. // internal, only called from resolve
  179. protected abstract void resolveBindingsFromRTTI();
  180. /**
  181. * Returns this pointcut mutated
  182. */
  183. public final Pointcut resolve(IScope scope) {
  184. assertState(SYMBOLIC);
  185. Bindings bindingTable = new Bindings(scope.getFormalCount());
  186. this.resolveBindings(scope, bindingTable);
  187. bindingTable.checkAllBound(scope);
  188. this.state = RESOLVED;
  189. return this;
  190. }
  191. /**
  192. * Returns this pointcut with type patterns etc resolved based on available RTTI
  193. */
  194. public Pointcut resolve() {
  195. assertState(SYMBOLIC);
  196. this.resolveBindingsFromRTTI();
  197. this.state = RESOLVED;
  198. return this;
  199. }
  200. /**
  201. * Returns a new pointcut
  202. * Only used by test cases
  203. */
  204. public final Pointcut concretize(ResolvedTypeX inAspect, int arity) {
  205. return concretize(inAspect, IntMap.idMap(arity));
  206. }
  207. //XXX this is the signature we're moving to
  208. public final Pointcut concretize(ResolvedTypeX inAspect, int arity, ShadowMunger advice) {
  209. //if (state == CONCRETE) return this; //???
  210. IntMap map = IntMap.idMap(arity);
  211. map.setEnclosingAdvice(advice);
  212. map.setConcreteAspect(inAspect);
  213. return concretize(inAspect, map);
  214. }
  215. public boolean isDeclare(ShadowMunger munger) {
  216. if (munger == null) return false; // ??? Is it actually an error if we get a null munger into this method.
  217. if (munger instanceof Checker) return true;
  218. if (((Advice)munger).getKind().equals(AdviceKind.Softener)) return true;
  219. return false;
  220. }
  221. public final Pointcut concretize(ResolvedTypeX inAspect, IntMap bindings) {
  222. //!!! add this test -- assertState(RESOLVED);
  223. Pointcut ret = this.concretize1(inAspect, bindings);
  224. if (shouldCopyLocationForConcretize()) ret.copyLocationFrom(this);
  225. ret.state = CONCRETE;
  226. return ret;
  227. }
  228. protected boolean shouldCopyLocationForConcretize() {
  229. return true;
  230. }
  231. /**
  232. * Resolves and removes ReferencePointcuts, replacing with basic ones
  233. *
  234. * @param inAspect the aspect to resolve relative to
  235. * @param bindings a Map from formal index in the current lexical context
  236. * -> formal index in the concrete advice that will run
  237. *
  238. * This must always return a new Pointcut object (even if the concretized
  239. * Pointcut is identical to the resolved one). That behavior is
  240. * assumed in many places.
  241. * XXX fix implementors to handle state
  242. */
  243. protected abstract Pointcut concretize1(ResolvedTypeX inAspect, IntMap bindings);
  244. //XXX implementors need to handle state
  245. /**
  246. * This can be called from NotPointcut even for Pointcuts that
  247. * don't match the shadow
  248. */
  249. public final Test findResidue(Shadow shadow, ExposedState state) {
  250. // if (shadow.shadowId == lastMatchedShadowId) return lastMatchedShadowResidue;
  251. Test ret = findResidueInternal(shadow,state);
  252. // lastMatchedShadowResidue = ret;
  253. lastMatchedShadowId = shadow.shadowId;
  254. return ret;
  255. }
  256. protected abstract Test findResidueInternal(Shadow shadow,ExposedState state);
  257. //XXX we're not sure whether or not this is needed
  258. //XXX currently it's unused we're keeping it around as a stub
  259. public void postRead(ResolvedTypeX enclosingType) {}
  260. public static Pointcut read(DataInputStream s, ISourceContext context) throws IOException {
  261. byte kind = s.readByte();
  262. Pointcut ret;
  263. switch(kind) {
  264. case KINDED: ret = KindedPointcut.read(s, context); break;
  265. case WITHIN: ret = WithinPointcut.read(s, context); break;
  266. case THIS_OR_TARGET: ret = ThisOrTargetPointcut.read(s, context); break;
  267. case ARGS: ret = ArgsPointcut.read(s, context); break;
  268. case AND: ret = AndPointcut.read(s, context); break;
  269. case OR: ret = OrPointcut.read(s, context); break;
  270. case NOT: ret = NotPointcut.read(s, context); break;
  271. case REFERENCE: ret = ReferencePointcut.read(s, context); break;
  272. case IF: ret = IfPointcut.read(s, context); break;
  273. case CFLOW: ret = CflowPointcut.read(s, context); break;
  274. case WITHINCODE: ret = WithincodePointcut.read(s, context); break;
  275. case HANDLER: ret = HandlerPointcut.read(s, context); break;
  276. case IF_TRUE: ret = IfPointcut.makeIfTruePointcut(RESOLVED); break;
  277. case IF_FALSE: ret = IfPointcut.makeIfFalsePointcut(RESOLVED); break;
  278. case ANNOTATION: ret = AnnotationPointcut.read(s, context); break;
  279. case ATWITHIN: ret = WithinAnnotationPointcut.read(s, context); break;
  280. case ATWITHINCODE: ret = WithinCodeAnnotationPointcut.read(s, context); break;
  281. case ATTHIS_OR_TARGET: ret = ThisOrTargetAnnotationPointcut.read(s, context); break;
  282. case ATARGS: ret = ArgsAnnotationPointcut.read(s,context); break;
  283. case NONE: ret = makeMatchesNothing(RESOLVED); break;
  284. default:
  285. throw new BCException("unknown kind: " + kind);
  286. }
  287. ret.state = RESOLVED;
  288. ret.pointcutKind = kind;
  289. return ret;
  290. }
  291. //public void prepare(Shadow shadow) {}
  292. // ---- test method
  293. public static Pointcut fromString(String str) {
  294. PatternParser parser = new PatternParser(str);
  295. return parser.parsePointcut();
  296. }
  297. private static class MatchesNothingPointcut extends Pointcut {
  298. protected Test findResidueInternal(Shadow shadow, ExposedState state) {
  299. return Literal.FALSE; // can only get here if an earlier error occurred
  300. }
  301. public Set couldMatchKinds() {
  302. return Collections.EMPTY_SET;
  303. }
  304. public FuzzyBoolean fastMatch(FastMatchInfo type) {
  305. return FuzzyBoolean.NO;
  306. }
  307. protected FuzzyBoolean matchInternal(Shadow shadow) {
  308. return FuzzyBoolean.NO;
  309. }
  310. public FuzzyBoolean match(JoinPoint.StaticPart jpsp) {
  311. return FuzzyBoolean.NO;
  312. }
  313. /* (non-Javadoc)
  314. * @see org.aspectj.weaver.patterns.Pointcut#matchesDynamically(java.lang.Object, java.lang.Object, java.lang.Object[])
  315. */
  316. public boolean matchesDynamically(Object thisObject,
  317. Object targetObject, Object[] args) {
  318. return false;
  319. }
  320. /* (non-Javadoc)
  321. * @see org.aspectj.weaver.patterns.Pointcut#matchesStatically(java.lang.String, java.lang.reflect.Member, java.lang.Class, java.lang.Class, java.lang.reflect.Member)
  322. */
  323. public FuzzyBoolean matchesStatically(
  324. String joinpointKind, Member member, Class thisClass,
  325. Class targetClass, Member withinCode) {
  326. return FuzzyBoolean.NO;
  327. }
  328. public void resolveBindings(IScope scope, Bindings bindings) {
  329. }
  330. public void resolveBindingsFromRTTI() {
  331. }
  332. public void postRead(ResolvedTypeX enclosingType) {
  333. }
  334. public Pointcut concretize1(
  335. ResolvedTypeX inAspect,
  336. IntMap bindings) {
  337. return makeMatchesNothing(state);
  338. }
  339. public void write(DataOutputStream s) throws IOException {
  340. s.writeByte(NONE);
  341. }
  342. public String toString() { return ""; }
  343. }
  344. //public static Pointcut MatchesNothing = new MatchesNothingPointcut();
  345. //??? there could possibly be some good optimizations to be done at this point
  346. public static Pointcut makeMatchesNothing(State state) {
  347. Pointcut ret = new MatchesNothingPointcut();
  348. ret.state = state;
  349. return ret;
  350. }
  351. public void assertState(State state) {
  352. if (this.state != state) {
  353. throw new BCException("expected state: " + state + " got: " + this.state);
  354. }
  355. }
  356. }