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.

PointcutDeclaration.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  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.ajdt.internal.compiler.ast;
  13. import java.lang.reflect.Modifier;
  14. import java.util.Iterator;
  15. import org.aspectj.ajdt.internal.compiler.lookup.EclipseFactory;
  16. import org.aspectj.ajdt.internal.core.builder.EclipseSourceContext;
  17. import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
  18. import org.aspectj.org.eclipse.jdt.internal.compiler.ClassFile;
  19. import org.aspectj.org.eclipse.jdt.internal.compiler.CompilationResult;
  20. import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Annotation;
  21. import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Argument;
  22. import org.aspectj.org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
  23. import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
  24. import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeReference;
  25. import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
  26. import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
  27. import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ClassScope;
  28. import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
  29. import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
  30. import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
  31. import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TagBits;
  32. import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
  33. import org.aspectj.org.eclipse.jdt.internal.compiler.parser.Parser;
  34. import org.aspectj.weaver.AjAttribute;
  35. import org.aspectj.weaver.ResolvedPointcutDefinition;
  36. import org.aspectj.weaver.UnresolvedType;
  37. import org.aspectj.weaver.patterns.Pointcut;
  38. /**
  39. * pointcut [declaredModifiers] [declaredName]([arguments]): [pointcutDesignator];
  40. *
  41. * <p>
  42. * No method will actually be generated for this node but an attribute will be added to the enclosing class.
  43. * </p>
  44. *
  45. * @author Jim Hugunin
  46. */
  47. public class PointcutDeclaration extends AjMethodDeclaration {
  48. public static final char[] mangledPrefix = "ajc$pointcut$".toCharArray();
  49. public PointcutDesignator pointcutDesignator;
  50. private int declaredModifiers;
  51. private String declaredName;
  52. private boolean generateSyntheticPointcutMethod = false;
  53. private EclipseFactory world = null;
  54. // private boolean mangleSelector = true;
  55. private ResolvedPointcutDefinition resolvedPointcutDeclaration = null;
  56. public PointcutDeclaration(CompilationResult compilationResult) {
  57. super(compilationResult);
  58. this.returnType = TypeReference.baseTypeReference(T_void, 0, null);
  59. }
  60. private Pointcut getPointcut() {
  61. if (pointcutDesignator == null) {
  62. return Pointcut.makeMatchesNothing(Pointcut.RESOLVED);
  63. } else {
  64. return pointcutDesignator.getPointcut();
  65. }
  66. }
  67. public void parseStatements(Parser parser, CompilationUnitDeclaration unit) {
  68. // do nothing
  69. }
  70. public void postParse(TypeDeclaration typeDec) {
  71. if (arguments == null)
  72. arguments = new Argument[0];
  73. this.declaredModifiers = modifiers;
  74. this.declaredName = new String(selector);
  75. // amc - if we set mangle selector to false, then the generated bytecode has the
  76. // pointcut method name that the user of an @Pointcut would expect.
  77. // But then we will unpack it again in the weaver which may cause redundant
  78. // error messages to be issued. This seems the better trade-off...
  79. // if (mangleSelector) {
  80. selector = CharOperation.concat(mangledPrefix, '$', selector, '$', Integer.toHexString(sourceStart).toCharArray());
  81. // }
  82. if (Modifier.isAbstract(this.declaredModifiers)) {
  83. if ((typeDec instanceof AspectDeclaration) && !Modifier.isAbstract(typeDec.modifiers)) {
  84. typeDec.scope.problemReporter().signalError(sourceStart, sourceEnd,
  85. "The abstract pointcut " + new String(declaredName) + " can only be defined in an abstract aspect");
  86. ignoreFurtherInvestigation = true;
  87. return;
  88. }
  89. }
  90. if (pointcutDesignator != null) {
  91. pointcutDesignator.postParse(typeDec, this);
  92. }
  93. }
  94. private boolean isAtAspectJ(TypeDeclaration typeDec) {
  95. if (typeDec.annotations == null)
  96. return false;
  97. for (int i = 0; i < typeDec.annotations.length; i++) {
  98. Annotation annotation = typeDec.annotations[i];
  99. if (CharOperation.equals(annotation.resolvedType.signature(),ASPECT_CHARS)) {
  100. return true;
  101. }
  102. }
  103. return false;
  104. }
  105. /**
  106. * Called from the AtAspectJVisitor to create the @Pointcut annotation (and corresponding method) for this pointcut
  107. *
  108. */
  109. public void addAtAspectJAnnotations() {
  110. String argNames = buildArgNameRepresentation();
  111. Annotation pcutAnnotation = AtAspectJAnnotationFactory.createPointcutAnnotation(getPointcutText(), argNames,
  112. declarationSourceStart);
  113. if (annotations == null) {
  114. annotations = new Annotation[] { pcutAnnotation };
  115. } else {
  116. Annotation[] old = annotations;
  117. annotations = new Annotation[old.length + 1];
  118. System.arraycopy(old, 0, annotations, 0, old.length);
  119. annotations[old.length] = pcutAnnotation;
  120. }
  121. generateSyntheticPointcutMethod = true;
  122. }
  123. public String getPointcutText() {
  124. String text = getPointcut().toString();
  125. if (!text.contains("BindingTypePattern"))
  126. return text;
  127. // has been wrecked by resolution, try to reconstruct from tokens
  128. if (pointcutDesignator != null) {
  129. text = pointcutDesignator.getPointcutDeclarationText();
  130. }
  131. return text;
  132. }
  133. private String buildArgNameRepresentation() {
  134. StringBuffer args = new StringBuffer();
  135. if (this.arguments != null) {
  136. for (int i = 0; i < this.arguments.length; i++) {
  137. if (i != 0)
  138. args.append(",");
  139. args.append(new String(this.arguments[i].name));
  140. }
  141. }
  142. return args.toString();
  143. }
  144. // coming from an @Pointcut declaration
  145. public void setGenerateSyntheticPointcutMethod() {
  146. generateSyntheticPointcutMethod = true;
  147. // mangleSelector = false;
  148. }
  149. private static char[] ASPECT_CHARS = "Lorg/aspectj/lang/annotation/Aspect;".toCharArray();
  150. public void resolve(ClassScope upperScope) {
  151. // we attempted to resolve annotations below, but that was too early, so we do it again
  152. // now at the 'right' time.
  153. if (binding != null) {
  154. binding.tagBits -= TagBits.AnnotationResolved;
  155. resolveAnnotations(scope, this.annotations, this.binding);
  156. TypeDeclaration typeDec = upperScope.referenceContext;
  157. if (Modifier.isAbstract(this.declaredModifiers)) {
  158. if (!(typeDec instanceof AspectDeclaration)) {
  159. if (!isAtAspectJ(typeDec)) {
  160. typeDec.scope.problemReporter().signalError(sourceStart, sourceEnd,
  161. "The abstract pointcut " + new String(declaredName) + " can only be defined in an aspect");
  162. ignoreFurtherInvestigation = true;
  163. return;
  164. }
  165. }
  166. }
  167. }
  168. // for the rest of the resolution process, this method should do nothing, use the entry point below...
  169. }
  170. public void resolvePointcut(ClassScope upperScope) {
  171. this.world = EclipseFactory.fromScopeLookupEnvironment(upperScope);
  172. super.resolve(upperScope);
  173. }
  174. public void resolveStatements() {
  175. if (isAbstract()) {
  176. this.modifiers |= ExtraCompilerModifiers.AccSemicolonBody;
  177. }
  178. if (binding == null || ignoreFurtherInvestigation)
  179. return;
  180. if (Modifier.isAbstract(this.declaredModifiers) && (pointcutDesignator != null)) {
  181. scope.problemReporter().signalError(sourceStart, sourceEnd, "abstract pointcut can't have body");
  182. ignoreFurtherInvestigation = true;
  183. return;
  184. }
  185. if (pointcutDesignator != null) {
  186. pointcutDesignator.finishResolveTypes(this, this.binding, arguments.length, scope.enclosingSourceType());
  187. }
  188. // System.out.println("resolved: " + getPointcut() + ", " + getPointcut().state);
  189. makeResolvedPointcutDefinition(world);
  190. resolvedPointcutDeclaration.setPointcut(getPointcut());
  191. super.resolveStatements();
  192. }
  193. public ResolvedPointcutDefinition makeResolvedPointcutDefinition(EclipseFactory inWorld) {
  194. if (resolvedPointcutDeclaration != null)
  195. return resolvedPointcutDeclaration;
  196. if (binding == null) {
  197. // other errors exist that will be reported separately
  198. return null;
  199. }
  200. // System.out.println("pc: " + getPointcut() + ", " + getPointcut().state);
  201. ReferenceBinding declaringClass = binding.declaringClass;
  202. TypeBinding[] parameters = binding.parameters;
  203. UnresolvedType utDeclaringClass = inWorld.fromBinding(declaringClass);
  204. UnresolvedType[] utParameters = inWorld.fromBindings(parameters);
  205. resolvedPointcutDeclaration = new ResolvedPointcutDefinition(utDeclaringClass, declaredModifiers, declaredName,
  206. utParameters, getPointcut()); // ??? might want to
  207. // use null
  208. resolvedPointcutDeclaration.setPosition(sourceStart, sourceEnd);
  209. resolvedPointcutDeclaration.setSourceContext(new EclipseSourceContext(compilationResult));
  210. return resolvedPointcutDeclaration;
  211. }
  212. public AjAttribute makeAttribute() {
  213. return new AjAttribute.PointcutDeclarationAttribute(makeResolvedPointcutDefinition(world));
  214. }
  215. /**
  216. * A pointcut declaration exists in a classfile only as an attibute on the class. Unlike advice and inter-type declarations, it
  217. * has no corresponding method.
  218. */
  219. public void generateCode(ClassScope classScope, ClassFile classFile) {
  220. this.world = EclipseFactory.fromScopeLookupEnvironment(classScope);
  221. if (ignoreFurtherInvestigation)
  222. return;
  223. classFile.extraAttributes.add(new EclipseAttributeAdapter(makeAttribute()));
  224. addVersionAttributeIfNecessary(classFile);
  225. if (generateSyntheticPointcutMethod) {
  226. this.binding.modifiers |= ClassFileConstants.AccSynthetic;
  227. super.generateCode(classScope, classFile);
  228. }
  229. return;
  230. }
  231. /**
  232. * Normally, pointcuts occur in aspects - aspects are always tagged with a weaver version attribute, see AspectDeclaration.
  233. * However, pointcuts can also occur in regular classes and in this case there is no AspectDeclaration to ensure the attribute
  234. * is added. So, this method adds the attribute if someone else hasn't already.
  235. */
  236. private void addVersionAttributeIfNecessary(ClassFile classFile) {
  237. for (Object o : classFile.extraAttributes) {
  238. EclipseAttributeAdapter element = (EclipseAttributeAdapter) o;
  239. if (CharOperation.equals(element.getNameChars(), weaverVersionChars))
  240. return;
  241. }
  242. classFile.extraAttributes.add(new EclipseAttributeAdapter(new AjAttribute.WeaverVersionInfo()));
  243. }
  244. private static char[] weaverVersionChars = "org.aspectj.weaver.WeaverVersion".toCharArray();
  245. protected int generateInfoAttributes(ClassFile classFile) {
  246. return super.generateInfoAttributes(classFile, true);
  247. }
  248. public StringBuffer printReturnType(int indent, StringBuffer output) {
  249. return output.append("pointcut");
  250. }
  251. /*
  252. * (non-Javadoc)
  253. *
  254. * @see org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration#printBody(int, java.lang.StringBuffer)
  255. */
  256. public StringBuffer printBody(int indent, StringBuffer output) {
  257. output.append(": ");
  258. output.append(getPointcut());
  259. output.append(";");
  260. return output;
  261. }
  262. }