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.

ConcreteAspectCodeGen.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. /*******************************************************************************
  2. * Copyright (c) 2005 Contributors.
  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://eclipse.org/legal/epl-v10.html
  8. *
  9. * Contributors:
  10. * Alexandre Vasseur initial implementation
  11. *******************************************************************************/
  12. package org.aspectj.weaver.loadtime;
  13. import java.lang.reflect.Modifier;
  14. import java.util.ArrayList;
  15. import java.util.Collection;
  16. import java.util.Collections;
  17. import java.util.HashMap;
  18. import java.util.Iterator;
  19. import java.util.List;
  20. import java.util.Map;
  21. import org.aspectj.apache.bcel.Constants;
  22. import org.aspectj.apache.bcel.classfile.JavaClass;
  23. import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen;
  24. import org.aspectj.apache.bcel.classfile.annotation.ElementNameValuePairGen;
  25. import org.aspectj.apache.bcel.classfile.annotation.ElementValueGen;
  26. import org.aspectj.apache.bcel.classfile.annotation.SimpleElementValueGen;
  27. import org.aspectj.apache.bcel.generic.InstructionConstants;
  28. import org.aspectj.apache.bcel.generic.InstructionList;
  29. import org.aspectj.apache.bcel.generic.ObjectType;
  30. import org.aspectj.apache.bcel.generic.Type;
  31. import org.aspectj.bridge.IMessage;
  32. import org.aspectj.bridge.Message;
  33. import org.aspectj.weaver.AnnotationX;
  34. import org.aspectj.weaver.ReferenceType;
  35. import org.aspectj.weaver.ResolvedMember;
  36. import org.aspectj.weaver.ResolvedType;
  37. import org.aspectj.weaver.UnresolvedType;
  38. import org.aspectj.weaver.World;
  39. import org.aspectj.weaver.bcel.BcelPerClauseAspectAdder;
  40. import org.aspectj.weaver.bcel.BcelWorld;
  41. import org.aspectj.weaver.bcel.LazyClassGen;
  42. import org.aspectj.weaver.bcel.LazyMethodGen;
  43. import org.aspectj.weaver.loadtime.definition.Definition;
  44. import org.aspectj.weaver.patterns.PerClause;
  45. import org.aspectj.weaver.patterns.PerSingleton;
  46. /**
  47. * Generates bytecode for concrete-aspect <p/> The concrete aspect is @AspectJ code generated. As it is build during aop.xml
  48. * definitions registration we perform the type munging for perclause ie aspectOf artifact directly, instead of waiting for it to go
  49. * thru the weaver (that we are in the middle of configuring).
  50. *
  51. * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
  52. */
  53. public class ConcreteAspectCodeGen {
  54. private final static String[] EMPTY_STRINGS = new String[0];
  55. private final static Type[] EMPTY_TYPES = new Type[0];
  56. /**
  57. * Concrete aspect definition we build for
  58. */
  59. private final Definition.ConcreteAspect m_concreteAspect;
  60. /**
  61. * World for which we build for
  62. */
  63. private final World m_world;
  64. /**
  65. * Set to true when all is checks are verified
  66. */
  67. private boolean m_isValid = false;
  68. /**
  69. * The parent aspect, not concretized
  70. */
  71. private ResolvedType m_parent;
  72. /**
  73. * Aspect perClause, used for direct munging of aspectOf artifacts
  74. */
  75. private PerClause m_perClause;
  76. /**
  77. * Create a new compiler for a concrete aspect
  78. *
  79. * @param concreteAspect
  80. * @param world
  81. */
  82. ConcreteAspectCodeGen(Definition.ConcreteAspect concreteAspect, World world) {
  83. m_concreteAspect = concreteAspect;
  84. m_world = world;
  85. }
  86. /**
  87. * Checks that concrete aspect is valid
  88. *
  89. * @return true if ok, false otherwise
  90. */
  91. public boolean validate() {
  92. if (!(m_world instanceof BcelWorld)) {
  93. reportError("Internal error: world must be of type BcelWorld");
  94. return false;
  95. }
  96. // name must be undefined so far
  97. // TODO only convert the name to signature once, probably earlier than this
  98. ResolvedType current = m_world.lookupBySignature(UnresolvedType.forName(m_concreteAspect.name).getSignature());
  99. if (current != null && !current.isMissing()) {
  100. reportError("Attempt to concretize but chosen aspect name already defined: " + stringify());
  101. return false;
  102. }
  103. // it can happen that extends is null, for precedence only declaration
  104. if (m_concreteAspect.extend == null && m_concreteAspect.precedence != null) {
  105. if (m_concreteAspect.pointcuts.isEmpty()) {
  106. m_isValid = true;
  107. m_perClause = new PerSingleton();
  108. m_parent = null;
  109. return true;// no need to checks more in that special case
  110. } else {
  111. reportError("Attempt to use nested pointcuts without extends clause: " + stringify());
  112. return false;
  113. }
  114. }
  115. m_parent = m_world.resolve(m_concreteAspect.extend, true);
  116. // handle inner classes
  117. if (m_parent.isMissing()) {
  118. // fallback on inner class lookup mechanism
  119. String fixedName = m_concreteAspect.extend;
  120. int hasDot = fixedName.lastIndexOf('.');
  121. while (hasDot > 0) {
  122. char[] fixedNameChars = fixedName.toCharArray();
  123. fixedNameChars[hasDot] = '$';
  124. fixedName = new String(fixedNameChars);
  125. hasDot = fixedName.lastIndexOf('.');
  126. m_parent = m_world.resolve(UnresolvedType.forName(fixedName), true);
  127. if (!m_parent.isMissing()) {
  128. break;
  129. }
  130. }
  131. }
  132. if (m_parent.isMissing()) {
  133. reportError("Cannot find m_parent aspect for: " + stringify());
  134. return false;
  135. }
  136. // extends must be abstract
  137. if (!m_parent.isAbstract()) {
  138. reportError("Attempt to concretize a non-abstract aspect: " + stringify());
  139. return false;
  140. }
  141. // m_parent must be aspect
  142. if (!m_parent.isAspect()) {
  143. reportError("Attempt to concretize a non aspect: " + stringify());
  144. return false;
  145. }
  146. // must have all abstractions defined
  147. List elligibleAbstractions = new ArrayList();
  148. Collection abstractMethods = getOutstandingAbstractMethods(m_parent);
  149. for (Iterator iter = abstractMethods.iterator(); iter.hasNext();) {
  150. ResolvedMember method = (ResolvedMember) iter.next();
  151. if ("()V".equals(method.getSignature())) {
  152. String n = method.getName();
  153. if (n.startsWith("ajc$pointcut")) { // Allow for the abstract pointcut being from a code style aspect compiled with
  154. // -1.5 (see test for 128744)
  155. n = n.substring(14);
  156. n = n.substring(0, n.indexOf("$"));
  157. elligibleAbstractions.add(n);
  158. } else if (hasPointcutAnnotation(method)) {
  159. elligibleAbstractions.add(method.getName());
  160. } else {
  161. // error, an outstanding abstract method that can't be concretized in XML
  162. reportError("Abstract method '" + method.toString() + "' cannot be concretized in XML: " + stringify());
  163. return false;
  164. }
  165. } else {
  166. if (method.getName().startsWith("ajc$pointcut") || hasPointcutAnnotation(method)) {
  167. // it may be a pointcut but it doesn't meet the requirements for XML concretization
  168. reportError("Abstract method '"
  169. + method.toString()
  170. + "' cannot be concretized as a pointcut (illegal signature, must have no arguments, must return void): "
  171. + stringify());
  172. return false;
  173. } else {
  174. // error, an outstanding abstract method that can't be concretized in XML
  175. reportError("Abstract method '" + method.toString() + "' cannot be concretized in XML: " + stringify());
  176. return false;
  177. }
  178. }
  179. }
  180. List pointcutNames = new ArrayList();
  181. for (Iterator it = m_concreteAspect.pointcuts.iterator(); it.hasNext();) {
  182. Definition.Pointcut abstractPc = (Definition.Pointcut) it.next();
  183. pointcutNames.add(abstractPc.name);
  184. }
  185. for (Iterator it = elligibleAbstractions.iterator(); it.hasNext();) {
  186. String elligiblePc = (String) it.next();
  187. if (!pointcutNames.contains(elligiblePc)) {
  188. reportError("Abstract pointcut '" + elligiblePc + "' not configured: " + stringify());
  189. return false;
  190. }
  191. }
  192. m_perClause = m_parent.getPerClause();
  193. m_isValid = true;
  194. return m_isValid;
  195. }
  196. private Collection getOutstandingAbstractMethods(ResolvedType type) {
  197. Map collector = new HashMap();
  198. // let's get to the top of the hierarchy and then walk down ... recording abstract methods then removing
  199. // them if they get defined further down the hierarchy
  200. getOutstandingAbstractMethodsHelper(type, collector);
  201. return collector.values();
  202. }
  203. // We are trying to determine abstract methods left over at the bottom of a hierarchy that have not been
  204. // concretized.
  205. private void getOutstandingAbstractMethodsHelper(ResolvedType type, Map collector) {
  206. if (type == null)
  207. return;
  208. // Get to the top
  209. if (!type.equals(ResolvedType.OBJECT)) {
  210. if (type.getSuperclass() != null)
  211. getOutstandingAbstractMethodsHelper(type.getSuperclass(), collector);
  212. }
  213. ResolvedMember[] rms = type.getDeclaredMethods();
  214. if (rms != null) {
  215. for (int i = 0; i < rms.length; i++) {
  216. ResolvedMember member = rms[i];
  217. String key = member.getName() + member.getSignature();
  218. if (member.isAbstract()) {
  219. collector.put(key, member);
  220. } else {
  221. collector.remove(key);
  222. }
  223. }
  224. }
  225. }
  226. /**
  227. * Rebuild the XML snip that defines this concrete aspect, for log error purpose
  228. *
  229. * @return string repr.
  230. */
  231. private String stringify() {
  232. StringBuffer sb = new StringBuffer("<concrete-aspect name='");
  233. sb.append(m_concreteAspect.name);
  234. sb.append("' extends='");
  235. sb.append(m_concreteAspect.extend);
  236. sb.append("'/> in aop.xml");
  237. return sb.toString();
  238. }
  239. private boolean hasPointcutAnnotation(ResolvedMember member) {
  240. AnnotationX[] as = member.getAnnotations();
  241. if (as == null || as.length == 0)
  242. return false;
  243. for (int i = 0; i < as.length; i++) {
  244. if (as[i].getTypeSignature().equals("Lorg/aspectj/lang/annotation/Pointcut;")) {
  245. return true;
  246. }
  247. }
  248. return false;
  249. }
  250. public String getClassName() {
  251. return m_concreteAspect.name;
  252. }
  253. /**
  254. * Build the bytecode for the concrete aspect
  255. *
  256. * @return concrete aspect bytecode
  257. */
  258. public byte[] getBytes() {
  259. if (!m_isValid) {
  260. throw new RuntimeException("Must validate first");
  261. }
  262. // TODO AV - abstract away from BCEL...
  263. // @Aspect //inherit clause from m_parent
  264. // @DeclarePrecedence("....") // if any
  265. // public class xxxName [extends xxxExtends] {
  266. // [@Pointcut(xxxExpression-n)
  267. // public void xxxName-n() {}]
  268. // }
  269. // @Aspect public class ...
  270. LazyClassGen cg = new LazyClassGen(m_concreteAspect.name.replace('.', '/'), (m_parent == null) ? "java/lang/Object"
  271. : m_parent.getName().replace('.', '/'), null,// TODO AV - we could point to the aop.xml that defines it and use
  272. // JSR-45
  273. Modifier.PUBLIC + Constants.ACC_SUPER, EMPTY_STRINGS, m_world);
  274. AnnotationGen ag = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/Aspect"), Collections.EMPTY_LIST, true, cg
  275. .getConstantPool());
  276. cg.addAnnotation(ag);
  277. if (m_concreteAspect.precedence != null) {
  278. SimpleElementValueGen svg = new SimpleElementValueGen(ElementValueGen.STRING, cg.getConstantPool(),
  279. m_concreteAspect.precedence);
  280. List elems = new ArrayList();
  281. elems.add(new ElementNameValuePairGen("value", svg, cg.getConstantPool()));
  282. AnnotationGen agprec = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/DeclarePrecedence"), elems, true,
  283. cg.getConstantPool());
  284. cg.addAnnotation(agprec);
  285. }
  286. // default constructor
  287. LazyMethodGen init = new LazyMethodGen(Modifier.PUBLIC, Type.VOID, "<init>", EMPTY_TYPES, EMPTY_STRINGS, cg);
  288. InstructionList cbody = init.getBody();
  289. cbody.append(InstructionConstants.ALOAD_0);
  290. cbody.append(cg.getFactory().createInvoke((m_parent == null) ? "java/lang/Object" : m_parent.getName().replace('.', '/'),
  291. "<init>", Type.VOID, EMPTY_TYPES, Constants.INVOKESPECIAL));
  292. cbody.append(InstructionConstants.RETURN);
  293. cg.addMethodGen(init);
  294. for (Iterator it = m_concreteAspect.pointcuts.iterator(); it.hasNext();) {
  295. Definition.Pointcut abstractPc = (Definition.Pointcut) it.next();
  296. LazyMethodGen mg = new LazyMethodGen(Modifier.PUBLIC,// TODO AV - respect visibility instead of opening up?
  297. Type.VOID, abstractPc.name, EMPTY_TYPES, EMPTY_STRINGS, cg);
  298. SimpleElementValueGen svg = new SimpleElementValueGen(ElementValueGen.STRING, cg.getConstantPool(),
  299. abstractPc.expression);
  300. List elems = new ArrayList();
  301. elems.add(new ElementNameValuePairGen("value", svg, cg.getConstantPool()));
  302. AnnotationGen mag = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/Pointcut"), elems, true, cg
  303. .getConstantPool());
  304. AnnotationX max = new AnnotationX(mag, m_world);
  305. mg.addAnnotation(max);
  306. InstructionList body = mg.getBody();
  307. body.append(InstructionConstants.RETURN);
  308. cg.addMethodGen(mg);
  309. }
  310. // handle the perClause
  311. ReferenceType rt = new ReferenceType(ResolvedType.forName(m_concreteAspect.name).getSignature(), m_world);
  312. BcelPerClauseAspectAdder perClauseMunger = new BcelPerClauseAspectAdder(rt, m_perClause.getKind());
  313. perClauseMunger.forceMunge(cg, false);
  314. // TODO AV - unsafe cast
  315. // register the fresh new class into the world repository as it does not exist on the classpath anywhere
  316. JavaClass jc = cg.getJavaClass((BcelWorld) m_world);
  317. ((BcelWorld) m_world).addSourceObjectType(jc);
  318. return jc.getBytes();
  319. }
  320. /**
  321. * Error reporting
  322. *
  323. * @param message
  324. */
  325. private void reportError(String message) {
  326. m_world.getMessageHandler().handleMessage(new Message(message, IMessage.ERROR, null, null));
  327. }
  328. }