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.

PointcutRewriter.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. /* *******************************************************************
  2. * Copyright (c) 2004 IBM Corporation.
  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. * ******************************************************************/
  10. package org.aspectj.weaver.patterns;
  11. import java.util.Iterator;
  12. import java.util.Set;
  13. import java.util.SortedSet;
  14. import java.util.TreeSet;
  15. import org.aspectj.weaver.Shadow;
  16. import org.aspectj.weaver.patterns.Pointcut.MatchesNothingPointcut;
  17. /**
  18. * Performs term rewriting for pointcut expressions.
  19. *
  20. * @author colyer
  21. * @author clement
  22. */
  23. public class PointcutRewriter {
  24. private static final boolean WATCH_PROGRESS = false;
  25. /**
  26. * Set forcerewrite if you want to override the checking for something already in DNF (useful for some testing) Repeated
  27. * processing of something already in DNF is expensive (it ends up being done for every pointcut on every incremental compile) -
  28. * so let's not do it if we don't have to. See pr113257
  29. */
  30. public Pointcut rewrite(Pointcut pc, boolean forceRewrite) {
  31. Pointcut result = pc;// checkPC(result);
  32. if (forceRewrite || !isDNF(pc)) {
  33. if (WATCH_PROGRESS) {
  34. System.out.println("Initial pointcut is ==> " + format(pc));
  35. }
  36. result = distributeNot(result);// checkPC(result);
  37. if (WATCH_PROGRESS) {
  38. System.out.println("Distributing NOT gives ==> " + format(result));
  39. }
  40. result = pullUpDisjunctions(result);// checkPC(result);
  41. if (WATCH_PROGRESS) {
  42. System.out.println("Pull up disjunctions gives ==> " + format(result));
  43. }
  44. } else {
  45. if (WATCH_PROGRESS) {
  46. System.out.println("Not distributing NOTs or pulling up disjunctions, already DNF ==> " + format(pc));
  47. }
  48. }
  49. result = simplifyAnds(result); // checkPC(result);
  50. if (WATCH_PROGRESS) {
  51. System.out.println("Simplifying ANDs gives ==> " + format(result));
  52. }
  53. result = removeNothings(result); // checkPC(result);
  54. if (WATCH_PROGRESS) {
  55. System.out.println("Removing nothings gives ==> " + format(result));
  56. }
  57. result = sortOrs(result); // checkPC(result);
  58. if (WATCH_PROGRESS) {
  59. System.out.println("Sorting ORs gives ==> " + format(result));
  60. }
  61. return result;
  62. }
  63. // /**
  64. // * Checks pointcuts - used for debugging.
  65. // * - this variant checks if the context has been lost, since
  66. // * that can indicate an NPE will happen later reporting a message (pr162657).
  67. // * Not finished, but helped locate the problem ;)
  68. // */
  69. // private void checkPC(Pointcut pc) {
  70. // if (isNot(pc)) {
  71. // NotPointcut npc = (NotPointcut)pc;
  72. // checkPC(npc.getNegatedPointcut());
  73. // if (npc.getSourceContext()==null) {
  74. // System.out.println("Lost context for "+npc);
  75. // throw new RuntimeException("Lost context");
  76. // }
  77. // } else if (isOr(pc)) {
  78. // OrPointcut opc = (OrPointcut)pc;
  79. // checkPC(opc.getLeft());
  80. // checkPC(opc.getRight());
  81. // if (opc.getSourceContext()==null) {
  82. // System.out.println("Lost context for "+opc);
  83. // throw new RuntimeException("Lost context");
  84. // }
  85. // } else if (isAnd(pc)) {
  86. // AndPointcut apc = (AndPointcut)pc;
  87. // checkPC(apc.getLeft());
  88. // checkPC(apc.getRight());
  89. // if (apc.getSourceContext()==null) {
  90. // System.out.println("Lost context for "+apc);
  91. // throw new RuntimeException("Lost context");
  92. // }
  93. // } else {
  94. // if (pc.getSourceContext()==null) {
  95. // System.out.println("Lost context for "+pc);
  96. // throw new RuntimeException("Lost context");
  97. // }
  98. // }
  99. // }
  100. public Pointcut rewrite(Pointcut pc) {
  101. return rewrite(pc, false);
  102. }
  103. /**
  104. * Check if a pointcut is in DNF - if it is then it should be lots of 'ORs' up the top with 'ANDs' beneath them.
  105. */
  106. private boolean isDNF(Pointcut pc) {
  107. return isDNFHelper(pc, true);
  108. }
  109. /**
  110. * Helper function for determining DNFness. Records when we have crossed the point of allowing ORs.
  111. */
  112. private boolean isDNFHelper(Pointcut pc, boolean canStillHaveOrs) {
  113. if (isAnd(pc)) {
  114. AndPointcut ap = (AndPointcut) pc;
  115. return isDNFHelper(ap.getLeft(), false) && isDNFHelper(ap.getRight(), false);
  116. } else if (isOr(pc)) {
  117. if (!canStillHaveOrs) {
  118. return false;
  119. }
  120. OrPointcut op = (OrPointcut) pc;
  121. return isDNFHelper(op.getLeft(), true) && isDNFHelper(op.getRight(), true);
  122. } else if (isNot(pc)) {
  123. return isDNFHelper(((NotPointcut) pc).getNegatedPointcut(), canStillHaveOrs);
  124. } else {
  125. return true;
  126. }
  127. }
  128. /**
  129. * Allows formatting of the output pointcut for debugging...
  130. */
  131. public static String format(Pointcut p) {
  132. String s = p.toString();
  133. // Regex param needs '(' and '*' changing to '.'
  134. // s = s.replaceAll("persingleton.pkg1.monitoring.ErrorMonitoring.","M");
  135. // s = s.replaceAll("args.BindingTypePattern.java.lang.Throwable, 0.","Z");
  136. // s = s.replaceAll("within.pkg1.monitoring.DoMonitorErrors+.","X");
  137. // s=s.replaceAll("within.pkg1.monitoring....","Y");
  138. // s=s.replaceAll("if.true.","N");
  139. return s;
  140. }
  141. // !!X => X
  142. // !(X && Y) => !X || !Y
  143. // !(X || Y) => !X && !Y
  144. private Pointcut distributeNot(Pointcut pc) {
  145. if (isNot(pc)) {
  146. NotPointcut npc = (NotPointcut) pc;
  147. Pointcut notBody = distributeNot(npc.getNegatedPointcut());
  148. if (isNot(notBody)) {
  149. // !!X => X
  150. return ((NotPointcut) notBody).getNegatedPointcut();
  151. } else if (isAnd(notBody)) {
  152. // !(X && Y) => !X || !Y
  153. AndPointcut apc = (AndPointcut) notBody;
  154. Pointcut newLeft = distributeNot(new NotPointcut(apc.getLeft(), npc.getStart()));
  155. Pointcut newRight = distributeNot(new NotPointcut(apc.getRight(), npc.getStart()));
  156. return new OrPointcut(newLeft, newRight);
  157. } else if (isOr(notBody)) {
  158. // !(X || Y) => !X && !Y
  159. OrPointcut opc = (OrPointcut) notBody;
  160. Pointcut newLeft = distributeNot(new NotPointcut(opc.getLeft(), npc.getStart()));
  161. Pointcut newRight = distributeNot(new NotPointcut(opc.getRight(), npc.getStart()));
  162. return new AndPointcut(newLeft, newRight);
  163. } else {
  164. return new NotPointcut(notBody, npc.getStart());
  165. }
  166. } else if (isAnd(pc)) {
  167. AndPointcut apc = (AndPointcut) pc;
  168. Pointcut left = distributeNot(apc.getLeft());
  169. Pointcut right = distributeNot(apc.getRight());
  170. return new AndPointcut(left, right);
  171. } else if (isOr(pc)) {
  172. OrPointcut opc = (OrPointcut) pc;
  173. Pointcut left = distributeNot(opc.getLeft());
  174. Pointcut right = distributeNot(opc.getRight());
  175. return new OrPointcut(left, right);
  176. } else {
  177. return pc;
  178. }
  179. }
  180. // A && (B || C) => (A && B) || (A && C)
  181. // (A || B) && C => (A && C) || (B && C)
  182. private Pointcut pullUpDisjunctions(Pointcut pc) {
  183. if (isNot(pc)) {
  184. NotPointcut npc = (NotPointcut) pc;
  185. return new NotPointcut(pullUpDisjunctions(npc.getNegatedPointcut()));
  186. } else if (isAnd(pc)) {
  187. AndPointcut apc = (AndPointcut) pc;
  188. // dive into left and right here...
  189. Pointcut left = pullUpDisjunctions(apc.getLeft());
  190. Pointcut right = pullUpDisjunctions(apc.getRight());
  191. if (isOr(left) && !isOr(right)) {
  192. // (A || B) && C => (A && C) || (B && C)
  193. Pointcut leftLeft = ((OrPointcut) left).getLeft();
  194. Pointcut leftRight = ((OrPointcut) left).getRight();
  195. return pullUpDisjunctions(new OrPointcut(new AndPointcut(leftLeft, right), new AndPointcut(leftRight, right)));
  196. } else if (isOr(right) && !isOr(left)) {
  197. // A && (B || C) => (A && B) || (A && C)
  198. Pointcut rightLeft = ((OrPointcut) right).getLeft();
  199. Pointcut rightRight = ((OrPointcut) right).getRight();
  200. return pullUpDisjunctions(new OrPointcut(new AndPointcut(left, rightLeft), new AndPointcut(left, rightRight)));
  201. } else if (isOr(right) && isOr(left)) {
  202. // (A || B) && (C || D) => (A && C) || (A && D) || (B && C) || (B && D)
  203. Pointcut A = pullUpDisjunctions(((OrPointcut) left).getLeft());
  204. Pointcut B = pullUpDisjunctions(((OrPointcut) left).getRight());
  205. Pointcut C = pullUpDisjunctions(((OrPointcut) right).getLeft());
  206. Pointcut D = pullUpDisjunctions(((OrPointcut) right).getRight());
  207. Pointcut newLeft = new OrPointcut(new AndPointcut(A, C), new AndPointcut(A, D));
  208. Pointcut newRight = new OrPointcut(new AndPointcut(B, C), new AndPointcut(B, D));
  209. return pullUpDisjunctions(new OrPointcut(newLeft, newRight));
  210. } else {
  211. return new AndPointcut(left, right);
  212. }
  213. } else if (isOr(pc)) {
  214. OrPointcut opc = (OrPointcut) pc;
  215. return new OrPointcut(pullUpDisjunctions(opc.getLeft()), pullUpDisjunctions(opc.getRight()));
  216. } else {
  217. return pc;
  218. }
  219. }
  220. /**
  221. * Returns a NOTted form of the pointcut p - we cope with already NOTted pointcuts.
  222. */
  223. public Pointcut not(Pointcut p) {
  224. if (isNot(p)) {
  225. return ((NotPointcut) p).getNegatedPointcut();
  226. }
  227. return new NotPointcut(p);
  228. }
  229. /**
  230. * Passed an array of pointcuts, returns an AND tree with them in.
  231. */
  232. public Pointcut createAndsFor(Pointcut[] ps) {
  233. if (ps.length == 1) {
  234. return ps[0]; // dumb case
  235. }
  236. if (ps.length == 2) { // recursion exit case
  237. return new AndPointcut(ps[0], ps[1]);
  238. }
  239. // otherwise ...
  240. Pointcut[] subset = new Pointcut[ps.length - 1];
  241. for (int i = 1; i < ps.length; i++) {
  242. subset[i - 1] = ps[i];
  243. }
  244. return new AndPointcut(ps[0], createAndsFor(subset));
  245. }
  246. // NOT: execution(* TP.*(..)) => within(TP) && execution(* *(..))
  247. // since this breaks when the pattern matches an interface
  248. // NOT: withincode(* TP.*(..)) => within(TP) && withincode(* *(..))
  249. // since this is not correct when an aspect makes an ITD
  250. // private Pointcut splitOutWithins(Pointcut pc) {
  251. // if (isExecution(pc)) {
  252. // KindedPointcut kpc = (KindedPointcut) pc;
  253. // SignaturePattern sp = kpc.signature;
  254. // TypePattern within = sp.getDeclaringType();
  255. // if (isAnyType(within)) return pc;
  256. // SignaturePattern simplified = removeDeclaringTypePattern(sp);
  257. // return new AndPointcut(new WithinPointcut(within),
  258. // new KindedPointcut(kpc.kind,simplified));
  259. // } else if (isNot(pc)) {
  260. // return new NotPointcut(splitOutWithins(((NotPointcut)pc).getNegatedPointcut()));
  261. // } else if (isAnd(pc)) {
  262. // AndPointcut apc = (AndPointcut) pc;
  263. // return new AndPointcut(splitOutWithins(apc.getLeft()),
  264. // splitOutWithins(apc.getRight()));
  265. // } else if (isOr(pc)) {
  266. // OrPointcut opc = (OrPointcut) pc;
  267. // return new OrPointcut(splitOutWithins(opc.getLeft()),
  268. // splitOutWithins(opc.getRight()));
  269. // } else {
  270. // return pc;
  271. // }
  272. // }
  273. // private SignaturePattern removeDeclaringTypePattern(SignaturePattern sp) {
  274. // return new SignaturePattern(
  275. // sp.getKind(),
  276. // sp.getModifiers(),
  277. // sp.getReturnType(),
  278. // TypePattern.ANY,
  279. // sp.getName(),
  280. // sp.getParameterTypes(),
  281. // sp.getThrowsPattern(),
  282. // sp.getAnnotationPattern()
  283. // );
  284. // }
  285. // this finds the root of each && tree and then aggregates all of the branches
  286. // into a sorted set:
  287. // - duplicates are removed
  288. // - A && !A is replaced by a matchesNothingPointcut
  289. // - the kind(s) matched by the set are evaluated
  290. // - elements are sorted by evaluation complexity
  291. // - the result is written out with the least expensive branch leftmost
  292. private Pointcut simplifyAnds(Pointcut pc) {
  293. if (isNot(pc)) {
  294. NotPointcut npc = (NotPointcut) pc;
  295. Pointcut notBody = npc.getNegatedPointcut();
  296. if (isNot(notBody)) {
  297. // !!X => X
  298. return simplifyAnds(((NotPointcut) notBody).getNegatedPointcut());
  299. } else {
  300. return new NotPointcut(simplifyAnds(npc.getNegatedPointcut()));
  301. }
  302. } else if (isOr(pc)) {
  303. OrPointcut opc = (OrPointcut) pc;
  304. return new OrPointcut(simplifyAnds(opc.getLeft()), simplifyAnds(opc.getRight()));
  305. } else if (isAnd(pc)) {
  306. return simplifyAnd((AndPointcut) pc);
  307. } else {
  308. return pc;
  309. }
  310. }
  311. private Pointcut simplifyAnd(AndPointcut apc) {
  312. SortedSet<Pointcut> nodes = new TreeSet<Pointcut>(new PointcutEvaluationExpenseComparator());
  313. collectAndNodes(apc, nodes);
  314. // look for A and !A, or IfFalse
  315. for (Iterator<Pointcut> iter = nodes.iterator(); iter.hasNext();) {
  316. Pointcut element = iter.next();
  317. if (element instanceof NotPointcut) {
  318. Pointcut body = ((NotPointcut) element).getNegatedPointcut();
  319. if (nodes.contains(body)) {
  320. return Pointcut.makeMatchesNothing(body.state);
  321. }
  322. }
  323. if (element instanceof IfPointcut) {
  324. if (((IfPointcut) element).alwaysFalse()) {
  325. return Pointcut.makeMatchesNothing(element.state);
  326. }
  327. }
  328. // If it can't match anything, the whole AND can't match anything
  329. if (element.couldMatchKinds() == Shadow.NO_SHADOW_KINDS_BITS) {
  330. return element;
  331. }
  332. }
  333. if (apc.couldMatchKinds() == Shadow.NO_SHADOW_KINDS_BITS) {
  334. return Pointcut.makeMatchesNothing(apc.state);
  335. }
  336. // write out with cheapest on left
  337. Iterator<Pointcut> iter = nodes.iterator();
  338. Pointcut result = iter.next();
  339. while (iter.hasNext()) {
  340. Pointcut right = iter.next();
  341. result = new AndPointcut(result, right);
  342. }
  343. return result;
  344. }
  345. private Pointcut sortOrs(Pointcut pc) {
  346. SortedSet<Pointcut> nodes = new TreeSet<Pointcut>(new PointcutEvaluationExpenseComparator());
  347. collectOrNodes(pc, nodes);
  348. // write out with cheapest on left
  349. Iterator<Pointcut> iter = nodes.iterator();
  350. Pointcut result = iter.next();
  351. while (iter.hasNext()) {
  352. Pointcut right = iter.next();
  353. result = new OrPointcut(result, right);
  354. }
  355. return result;
  356. }
  357. /**
  358. * Removes MATCHES_NOTHING pointcuts
  359. */
  360. private Pointcut removeNothings(Pointcut pc) {
  361. if (isAnd(pc)) {
  362. AndPointcut apc = (AndPointcut) pc;
  363. Pointcut right = removeNothings(apc.getRight());
  364. Pointcut left = removeNothings(apc.getLeft());
  365. if (left instanceof MatchesNothingPointcut || right instanceof MatchesNothingPointcut) {
  366. return new MatchesNothingPointcut();
  367. }
  368. return new AndPointcut(left, right);
  369. } else if (isOr(pc)) {
  370. OrPointcut opc = (OrPointcut) pc;
  371. Pointcut right = removeNothings(opc.getRight());
  372. Pointcut left = removeNothings(opc.getLeft());
  373. if (left instanceof MatchesNothingPointcut && !(right instanceof MatchesNothingPointcut)) {
  374. return right;
  375. } else if (right instanceof MatchesNothingPointcut && !(left instanceof MatchesNothingPointcut)) {
  376. return left;
  377. } else if (!(left instanceof MatchesNothingPointcut) && !(right instanceof MatchesNothingPointcut)) {
  378. return new OrPointcut(left, right);
  379. } else if (left instanceof MatchesNothingPointcut && right instanceof MatchesNothingPointcut) {
  380. return new MatchesNothingPointcut();
  381. }
  382. }
  383. return pc;
  384. }
  385. private void collectAndNodes(AndPointcut apc, Set<Pointcut> nodesSoFar) {
  386. Pointcut left = apc.getLeft();
  387. Pointcut right = apc.getRight();
  388. if (isAnd(left)) {
  389. collectAndNodes((AndPointcut) left, nodesSoFar);
  390. } else {
  391. nodesSoFar.add(left);
  392. }
  393. if (isAnd(right)) {
  394. collectAndNodes((AndPointcut) right, nodesSoFar);
  395. } else {
  396. nodesSoFar.add(right);
  397. }
  398. }
  399. private void collectOrNodes(Pointcut pc, Set<Pointcut> nodesSoFar) {
  400. if (isOr(pc)) {
  401. OrPointcut opc = (OrPointcut) pc;
  402. collectOrNodes(opc.getLeft(), nodesSoFar);
  403. collectOrNodes(opc.getRight(), nodesSoFar);
  404. } else {
  405. nodesSoFar.add(pc);
  406. }
  407. }
  408. private boolean isNot(Pointcut pc) {
  409. return (pc instanceof NotPointcut);
  410. }
  411. private boolean isAnd(Pointcut pc) {
  412. return (pc instanceof AndPointcut);
  413. }
  414. private boolean isOr(Pointcut pc) {
  415. return (pc instanceof OrPointcut);
  416. }
  417. }