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

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