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.

ReferencePointcut.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  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 v 2.0
  6. * which accompanies this distribution and is available at
  7. * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
  8. *
  9. * Contributors:
  10. * PARC initial implementation
  11. * ******************************************************************/
  12. package org.aspectj.weaver.patterns;
  13. import java.io.IOException;
  14. import java.lang.reflect.Modifier;
  15. import java.util.HashMap;
  16. import java.util.Map;
  17. import org.aspectj.bridge.IMessage;
  18. import org.aspectj.bridge.MessageUtil;
  19. import org.aspectj.util.FuzzyBoolean;
  20. import org.aspectj.weaver.CompressingDataOutputStream;
  21. import org.aspectj.weaver.ISourceContext;
  22. import org.aspectj.weaver.IntMap;
  23. import org.aspectj.weaver.ResolvedPointcutDefinition;
  24. import org.aspectj.weaver.ResolvedType;
  25. import org.aspectj.weaver.Shadow;
  26. import org.aspectj.weaver.ShadowMunger;
  27. import org.aspectj.weaver.TypeVariable;
  28. import org.aspectj.weaver.TypeVariableReference;
  29. import org.aspectj.weaver.UnresolvedType;
  30. import org.aspectj.weaver.VersionedDataInputStream;
  31. import org.aspectj.weaver.WeaverMessages;
  32. import org.aspectj.weaver.World;
  33. import org.aspectj.weaver.ast.Test;
  34. /**
  35. */
  36. // XXX needs check that arguments contains no WildTypePatterns
  37. public class ReferencePointcut extends Pointcut {
  38. public UnresolvedType onType;
  39. public TypePattern onTypeSymbolic;
  40. public String name;
  41. public TypePatternList arguments;
  42. /**
  43. * if this is non-null then when the pointcut is concretized the result will be parameterized too.
  44. */
  45. private Map<String, UnresolvedType> typeVariableMap;
  46. // public ResolvedPointcut binding;
  47. public ReferencePointcut(TypePattern onTypeSymbolic, String name, TypePatternList arguments) {
  48. this.onTypeSymbolic = onTypeSymbolic;
  49. this.name = name;
  50. this.arguments = arguments;
  51. this.pointcutKind = REFERENCE;
  52. }
  53. public ReferencePointcut(UnresolvedType onType, String name, TypePatternList arguments) {
  54. this.onType = onType;
  55. this.name = name;
  56. this.arguments = arguments;
  57. this.pointcutKind = REFERENCE;
  58. }
  59. public int couldMatchKinds() {
  60. return Shadow.ALL_SHADOW_KINDS_BITS;
  61. }
  62. // ??? do either of these match methods make any sense???
  63. public FuzzyBoolean fastMatch(FastMatchInfo type) {
  64. return FuzzyBoolean.MAYBE;
  65. }
  66. /**
  67. * Do I really match this shadow?
  68. */
  69. protected FuzzyBoolean matchInternal(Shadow shadow) {
  70. return FuzzyBoolean.NO;
  71. }
  72. public String toString() {
  73. StringBuilder buf = new StringBuilder();
  74. if (onType != null) {
  75. buf.append(onType);
  76. buf.append(".");
  77. // for (int i=0, len=fromType.length; i < len; i++) {
  78. // buf.append(fromType[i]);
  79. // buf.append(".");
  80. // }
  81. }
  82. buf.append(name);
  83. buf.append(arguments.toString());
  84. return buf.toString();
  85. }
  86. public void write(CompressingDataOutputStream s) throws IOException {
  87. // XXX ignores onType
  88. s.writeByte(Pointcut.REFERENCE);
  89. if (onType != null) {
  90. s.writeBoolean(true);
  91. onType.write(s);
  92. } else {
  93. s.writeBoolean(false);
  94. }
  95. s.writeUTF(name);
  96. arguments.write(s);
  97. writeLocation(s);
  98. }
  99. public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException {
  100. UnresolvedType onType = null;
  101. if (s.readBoolean()) {
  102. onType = UnresolvedType.read(s);
  103. }
  104. ReferencePointcut ret = new ReferencePointcut(onType, s.readUTF(), TypePatternList.read(s, context));
  105. ret.readLocation(context, s);
  106. return ret;
  107. }
  108. public void resolveBindings(IScope scope, Bindings bindings) {
  109. if (onTypeSymbolic != null) {
  110. onType = onTypeSymbolic.resolveExactType(scope, bindings);
  111. // in this case we've already signaled an error
  112. if (ResolvedType.isMissing(onType)) {
  113. return;
  114. }
  115. }
  116. ResolvedType searchType;
  117. if (onType != null) {
  118. searchType = scope.getWorld().resolve(onType);
  119. } else {
  120. searchType = scope.getEnclosingType();
  121. }
  122. if (searchType.isTypeVariableReference()) {
  123. searchType = ((TypeVariableReference) searchType).getTypeVariable().getFirstBound().resolve(scope.getWorld());
  124. }
  125. arguments.resolveBindings(scope, bindings, true, true);
  126. // XXX ensure that arguments has no ..'s in it
  127. // check that I refer to a real pointcut declaration and that I match
  128. ResolvedPointcutDefinition pointcutDef = searchType.findPointcut(name);
  129. // if we're not a static reference, then do a lookup of outers
  130. if (pointcutDef == null && onType == null) {
  131. while (true) {
  132. UnresolvedType declaringType = searchType.getDeclaringType();
  133. if (declaringType == null) {
  134. break;
  135. }
  136. searchType = declaringType.resolve(scope.getWorld());
  137. pointcutDef = searchType.findPointcut(name);
  138. if (pointcutDef != null) {
  139. // make this a static reference
  140. onType = searchType;
  141. break;
  142. }
  143. }
  144. }
  145. if (pointcutDef == null) {
  146. scope.message(IMessage.ERROR, this, "can't find referenced pointcut " + name);
  147. return;
  148. }
  149. // check visibility
  150. if (!pointcutDef.isVisible(scope.getEnclosingType())) {
  151. scope.message(IMessage.ERROR, this, "pointcut declaration " + pointcutDef + " is not accessible");
  152. return;
  153. }
  154. if (Modifier.isAbstract(pointcutDef.getModifiers())) {
  155. if (onType != null && !onType.isTypeVariableReference()) {
  156. scope.message(IMessage.ERROR, this, "can't make static reference to abstract pointcut");
  157. return;
  158. } else if (!searchType.isAbstract()) {
  159. scope.message(IMessage.ERROR, this, "can't use abstract pointcut in concrete context");
  160. return;
  161. }
  162. }
  163. ResolvedType[] parameterTypes = scope.getWorld().resolve(pointcutDef.getParameterTypes());
  164. if (parameterTypes.length != arguments.size()) {
  165. scope.message(IMessage.ERROR, this, "incompatible number of arguments to pointcut, expected " + parameterTypes.length
  166. + " found " + arguments.size());
  167. return;
  168. }
  169. // if (onType == null) onType = pointcutDef.getDeclaringType();
  170. if (onType != null) {
  171. if (onType.isParameterizedType()) {
  172. // build a type map mapping type variable names in the generic type to
  173. // the type parameters presented
  174. typeVariableMap = new HashMap<>();
  175. ResolvedType underlyingGenericType = ((ResolvedType) onType).getGenericType();
  176. TypeVariable[] tVars = underlyingGenericType.getTypeVariables();
  177. ResolvedType[] typeParams = ((ResolvedType) onType).getResolvedTypeParameters();
  178. for (int i = 0; i < tVars.length; i++) {
  179. typeVariableMap.put(tVars[i].getName(), typeParams[i]);
  180. }
  181. } else if (onType.isGenericType()) {
  182. scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.CANT_REFERENCE_POINTCUT_IN_RAW_TYPE),
  183. getSourceLocation()));
  184. }
  185. }
  186. for (int i = 0, len = arguments.size(); i < len; i++) {
  187. TypePattern p = arguments.get(i);
  188. // we are allowed to bind to pointcuts which use subtypes as this is type safe
  189. if (typeVariableMap != null) {
  190. p = p.parameterizeWith(typeVariableMap, scope.getWorld());
  191. }
  192. if (p == TypePattern.NO) {
  193. scope.message(IMessage.ERROR, this, "bad parameter to pointcut reference");
  194. return;
  195. }
  196. boolean reportProblem = false;
  197. if (parameterTypes[i].isTypeVariableReference() && p.getExactType().isTypeVariableReference()) {
  198. UnresolvedType One = ((TypeVariableReference) parameterTypes[i]).getTypeVariable().getFirstBound();
  199. UnresolvedType Two = ((TypeVariableReference) p.getExactType()).getTypeVariable().getFirstBound();
  200. reportProblem = !One.resolve(scope.getWorld()).isAssignableFrom(Two.resolve(scope.getWorld()));
  201. } else {
  202. reportProblem = !p.matchesSubtypes(parameterTypes[i]) && !p.getExactType().equals(UnresolvedType.OBJECT);
  203. }
  204. if (reportProblem) {
  205. scope.message(IMessage.ERROR, this, "incompatible type, expected " + parameterTypes[i].getName() + " found " + p
  206. + ". Check the type specified in your pointcut");
  207. return;
  208. }
  209. }
  210. }
  211. public void postRead(ResolvedType enclosingType) {
  212. arguments.postRead(enclosingType);
  213. }
  214. protected Test findResidueInternal(Shadow shadow, ExposedState state) {
  215. throw new RuntimeException("shouldn't happen");
  216. }
  217. // ??? This is not thread safe, but this class is not designed for multi-threading
  218. private boolean concretizing = false;
  219. // declaring type is the type that declared the member referencing this pointcut.
  220. // If it declares a matching private pointcut, then that pointcut should be used
  221. // and not one in a subtype that happens to have the same name.
  222. public Pointcut concretize1(ResolvedType searchStart, ResolvedType declaringType, IntMap bindings) {
  223. if (concretizing) {
  224. // Thread.currentThread().dumpStack();
  225. searchStart
  226. .getWorld()
  227. .getMessageHandler()
  228. .handleMessage(
  229. MessageUtil.error(WeaverMessages.format(WeaverMessages.CIRCULAR_POINTCUT, this), getSourceLocation()));
  230. Pointcut p = Pointcut.makeMatchesNothing(Pointcut.CONCRETE);
  231. p.sourceContext = sourceContext;
  232. return p;
  233. }
  234. try {
  235. concretizing = true;
  236. ResolvedPointcutDefinition pointcutDec;
  237. if (onType != null) {
  238. searchStart = onType.resolve(searchStart.getWorld());
  239. if (searchStart.isMissing()) {
  240. return Pointcut.makeMatchesNothing(Pointcut.CONCRETE);
  241. }
  242. if (onType.isTypeVariableReference()) {
  243. // need to replace on type with the binding for the type variable
  244. // in the declaring type
  245. if (declaringType.isParameterizedType()) {
  246. TypeVariable[] tvs = declaringType.getGenericType().getTypeVariables();
  247. String typeVariableName = ((TypeVariableReference) onType).getTypeVariable().getName();
  248. for (int i = 0; i < tvs.length; i++) {
  249. if (tvs[i].getName().equals(typeVariableName)) {
  250. ResolvedType realOnType = declaringType.getTypeParameters()[i].resolve(declaringType.getWorld());
  251. onType = realOnType;
  252. searchStart = realOnType;
  253. break;
  254. }
  255. }
  256. }
  257. }
  258. }
  259. if (declaringType == null) {
  260. declaringType = searchStart;
  261. }
  262. pointcutDec = declaringType.findPointcut(name);
  263. boolean foundMatchingPointcut = (pointcutDec != null && Modifier.isPrivate(pointcutDec.getModifiers()));
  264. if (!foundMatchingPointcut) {
  265. pointcutDec = searchStart.findPointcut(name);
  266. if (pointcutDec == null) {
  267. searchStart
  268. .getWorld()
  269. .getMessageHandler()
  270. .handleMessage(
  271. MessageUtil.error(
  272. WeaverMessages.format(WeaverMessages.CANT_FIND_POINTCUT, name, searchStart.getName()),
  273. getSourceLocation()));
  274. return Pointcut.makeMatchesNothing(Pointcut.CONCRETE);
  275. }
  276. }
  277. if (pointcutDec.isAbstract()) {
  278. // Thread.currentThread().dumpStack();
  279. ShadowMunger enclosingAdvice = bindings.getEnclosingAdvice();
  280. searchStart.getWorld().showMessage(IMessage.ERROR,
  281. WeaverMessages.format(WeaverMessages.ABSTRACT_POINTCUT, pointcutDec), getSourceLocation(),
  282. (null == enclosingAdvice) ? null : enclosingAdvice.getSourceLocation());
  283. return Pointcut.makeMatchesNothing(Pointcut.CONCRETE);
  284. }
  285. // System.err.println("start: " + searchStart);
  286. // ResolvedType[] parameterTypes = searchStart.getWorld().resolve(pointcutDec.getParameterTypes());
  287. TypePatternList arguments = this.arguments.resolveReferences(bindings);
  288. IntMap newBindings = new IntMap();
  289. for (int i = 0, len = arguments.size(); i < len; i++) {
  290. TypePattern p = arguments.get(i);
  291. if (p == TypePattern.NO) {
  292. continue;
  293. }
  294. // we are allowed to bind to pointcuts which use subtypes as this is type safe
  295. // this will be checked in ReferencePointcut.resolveBindings(). Can't check it here
  296. // as we don't know about any new parents added via decp.
  297. if (p instanceof BindingTypePattern) {
  298. newBindings.put(i, ((BindingTypePattern) p).getFormalIndex());
  299. }
  300. }
  301. if (searchStart.isParameterizedType()) {
  302. // build a type map mapping type variable names in the generic type to
  303. // the type parameters presented
  304. typeVariableMap = new HashMap<>();
  305. ResolvedType underlyingGenericType = searchStart.getGenericType();
  306. TypeVariable[] tVars = underlyingGenericType.getTypeVariables();
  307. ResolvedType[] typeParams = searchStart.getResolvedTypeParameters();
  308. for (int i = 0; i < tVars.length; i++) {
  309. typeVariableMap.put(tVars[i].getName(), typeParams[i]);
  310. }
  311. }
  312. newBindings.copyContext(bindings);
  313. newBindings.pushEnclosingDefinition(pointcutDec);
  314. try {
  315. Pointcut ret = pointcutDec.getPointcut();
  316. if (typeVariableMap != null && !hasBeenParameterized) {
  317. ret = ret.parameterizeWith(typeVariableMap, searchStart.getWorld());
  318. ret.hasBeenParameterized = true;
  319. }
  320. return ret.concretize(searchStart, declaringType, newBindings);
  321. } finally {
  322. newBindings.popEnclosingDefinitition();
  323. }
  324. } finally {
  325. concretizing = false;
  326. }
  327. }
  328. /**
  329. * make a version of this pointcut with any refs to typeVariables replaced by their entry in the map. Tricky thing is, we can't
  330. * do this at the point in time this method will be called, so we make a version that will parameterize the pointcut it
  331. * ultimately resolves to.
  332. */
  333. public Pointcut parameterizeWith(Map<String, UnresolvedType> typeVariableMap, World w) {
  334. ReferencePointcut ret = new ReferencePointcut(onType, name, arguments);
  335. ret.onTypeSymbolic = onTypeSymbolic;
  336. ret.typeVariableMap = typeVariableMap;
  337. return ret;
  338. }
  339. // We want to keep the original source location, not the reference location
  340. protected boolean shouldCopyLocationForConcretize() {
  341. return false;
  342. }
  343. public boolean equals(Object other) {
  344. if (!(other instanceof ReferencePointcut)) {
  345. return false;
  346. }
  347. if (this == other) {
  348. return true;
  349. }
  350. ReferencePointcut o = (ReferencePointcut) other;
  351. return o.name.equals(name) && o.arguments.equals(arguments)
  352. && ((o.onType == null) ? (onType == null) : o.onType.equals(onType));
  353. }
  354. public int hashCode() {
  355. int result = 17;
  356. result = 37 * result + ((onType == null) ? 0 : onType.hashCode());
  357. result = 37 * result + arguments.hashCode();
  358. result = 37 * result + name.hashCode();
  359. return result;
  360. }
  361. public Object accept(PatternNodeVisitor visitor, Object data) {
  362. return visitor.visit(this, data);
  363. }
  364. }