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 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  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.ElementValue;
  25. import org.aspectj.apache.bcel.classfile.annotation.NameValuePair;
  26. import org.aspectj.apache.bcel.classfile.annotation.SimpleElementValue;
  27. import org.aspectj.apache.bcel.generic.FieldGen;
  28. import org.aspectj.apache.bcel.generic.InstructionConstants;
  29. import org.aspectj.apache.bcel.generic.InstructionFactory;
  30. import org.aspectj.apache.bcel.generic.InstructionHandle;
  31. import org.aspectj.apache.bcel.generic.InstructionList;
  32. import org.aspectj.apache.bcel.generic.LocalVariableTag;
  33. import org.aspectj.apache.bcel.generic.ObjectType;
  34. import org.aspectj.apache.bcel.generic.Type;
  35. import org.aspectj.bridge.IMessage;
  36. import org.aspectj.bridge.Message;
  37. import org.aspectj.weaver.AnnotationAJ;
  38. import org.aspectj.weaver.GeneratedReferenceTypeDelegate;
  39. import org.aspectj.weaver.ReferenceType;
  40. import org.aspectj.weaver.ResolvedMember;
  41. import org.aspectj.weaver.ResolvedType;
  42. import org.aspectj.weaver.UnresolvedType;
  43. import org.aspectj.weaver.World;
  44. import org.aspectj.weaver.bcel.BcelAnnotation;
  45. import org.aspectj.weaver.bcel.BcelPerClauseAspectAdder;
  46. import org.aspectj.weaver.bcel.BcelWorld;
  47. import org.aspectj.weaver.bcel.LazyClassGen;
  48. import org.aspectj.weaver.bcel.LazyMethodGen;
  49. import org.aspectj.weaver.loadtime.definition.Definition;
  50. import org.aspectj.weaver.loadtime.definition.Definition.AdviceKind;
  51. import org.aspectj.weaver.loadtime.definition.Definition.PointcutAndAdvice;
  52. import org.aspectj.weaver.patterns.PerClause;
  53. import org.aspectj.weaver.patterns.PerSingleton;
  54. /**
  55. * Generates bytecode for concrete-aspect.
  56. * <p>
  57. * The concrete aspect is @AspectJ code generated. As it is build during aop.xml definitions registration we perform the type
  58. * munging for perclause, ie. aspectOf() artifact directly, instead of waiting for it to go thru the weaver (that we are in the
  59. * middle of configuring).
  60. *
  61. * @author Alexandre Vasseur
  62. * @author Andy Clement
  63. */
  64. public class ConcreteAspectCodeGen {
  65. private final static String[] EMPTY_STRINGS = new String[0];
  66. private final static Type[] EMPTY_TYPES = new Type[0];
  67. /**
  68. * Concrete aspect definition we build for
  69. */
  70. private final Definition.ConcreteAspect concreteAspect;
  71. /**
  72. * World for which we build for
  73. */
  74. private final World world;
  75. /**
  76. * Set to true when all is checks are verified
  77. */
  78. private boolean isValid = false;
  79. /**
  80. * The parent aspect, not concretized
  81. */
  82. private ResolvedType parent;
  83. /**
  84. * Aspect perClause, used for direct munging of aspectOf artifacts
  85. */
  86. private PerClause perclause;
  87. /**
  88. * Bytecode for the generated class
  89. */
  90. private byte[] bytes;
  91. /**
  92. * Create a new compiler for a concrete aspect
  93. *
  94. * @param concreteAspect
  95. * @param world
  96. */
  97. ConcreteAspectCodeGen(Definition.ConcreteAspect concreteAspect, World world) {
  98. this.concreteAspect = concreteAspect;
  99. this.world = world;
  100. }
  101. /**
  102. * Checks that concrete aspect is valid
  103. *
  104. * @return true if ok, false otherwise
  105. */
  106. public boolean validate() {
  107. if (!(world instanceof BcelWorld)) {
  108. reportError("Internal error: world must be of type BcelWorld");
  109. return false;
  110. }
  111. // name must be undefined so far
  112. // TODO only convert the name to signature once, probably earlier than this
  113. ResolvedType current = world.lookupBySignature(UnresolvedType.forName(concreteAspect.name).getSignature());
  114. if (current != null && !current.isMissing()) {
  115. reportError("Attempt to concretize but chosen aspect name already defined: " + stringify());
  116. return false;
  117. }
  118. if (concreteAspect.pointcutsAndAdvice.size() != 0) {
  119. isValid = true;
  120. return true;
  121. }
  122. // it can happen that extends is null, for precedence only declaration
  123. if (concreteAspect.extend == null && concreteAspect.precedence != null) {
  124. if (concreteAspect.pointcuts.isEmpty()) {
  125. isValid = true;
  126. // m_perClause = new PerSingleton();
  127. parent = null;
  128. return true;// no need to checks more in that special case
  129. } else {
  130. reportError("Attempt to use nested pointcuts without extends clause: " + stringify());
  131. return false;
  132. }
  133. }
  134. String parentAspectName = concreteAspect.extend;
  135. if (parentAspectName.indexOf("<") != -1) {
  136. // yikes, generic parent
  137. parent = world.resolve(UnresolvedType.forName(parentAspectName), true);
  138. if (parent.isMissing()) {
  139. reportError("Unable to resolve type reference: " + stringify());
  140. return false;
  141. }
  142. if (parent.isParameterizedType()) {
  143. UnresolvedType[] typeParameters = parent.getTypeParameters();
  144. for (int i = 0; i < typeParameters.length; i++) {
  145. UnresolvedType typeParameter = typeParameters[i];
  146. if (typeParameter instanceof ResolvedType && ((ResolvedType) typeParameter).isMissing()) {
  147. reportError("Unablet to resolve type parameter '" + typeParameter.getName() + "' from " + stringify());
  148. return false;
  149. }
  150. }
  151. }
  152. } else {
  153. parent = world.resolve(concreteAspect.extend, true);
  154. }
  155. // handle inner classes
  156. if (parent.isMissing()) {
  157. // fallback on inner class lookup mechanism
  158. String fixedName = concreteAspect.extend;
  159. int hasDot = fixedName.lastIndexOf('.');
  160. while (hasDot > 0) {
  161. char[] fixedNameChars = fixedName.toCharArray();
  162. fixedNameChars[hasDot] = '$';
  163. fixedName = new String(fixedNameChars);
  164. hasDot = fixedName.lastIndexOf('.');
  165. parent = world.resolve(UnresolvedType.forName(fixedName), true);
  166. if (!parent.isMissing()) {
  167. break;
  168. }
  169. }
  170. }
  171. if (parent.isMissing()) {
  172. reportError("Cannot find parent aspect for: " + stringify());
  173. return false;
  174. }
  175. // extends must be abstract (allow for special case of Object where just using aspect for deows)
  176. if (!(parent.isAbstract() || parent.equals(ResolvedType.OBJECT))) {
  177. reportError("Attempt to concretize a non-abstract aspect: " + stringify());
  178. return false;
  179. }
  180. // m_parent must be aspect (allow for special case of Object where just using aspect for deows)
  181. if (!(parent.isAspect() || parent.equals(ResolvedType.OBJECT))) {
  182. reportError("Attempt to concretize a non aspect: " + stringify());
  183. return false;
  184. }
  185. // must have all abstractions defined
  186. List<String> elligibleAbstractions = new ArrayList<String>();
  187. Collection<ResolvedMember> abstractMethods = getOutstandingAbstractMethods(parent);
  188. // for (Iterator iter = abstractMethods.iterator(); iter.hasNext();) {
  189. // ResolvedMember method = (ResolvedMember) iter.next();
  190. for (ResolvedMember method : abstractMethods) {
  191. if ("()V".equals(method.getSignature())) {
  192. String n = method.getName();
  193. // Allow for the abstract pointcut being from a code style
  194. // aspect compiled with -1.5 (see test for 128744)
  195. if (n.startsWith("ajc$pointcut")) {
  196. n = n.substring(14);
  197. n = n.substring(0, n.indexOf("$"));
  198. elligibleAbstractions.add(n);
  199. } else if (hasPointcutAnnotation(method)) {
  200. elligibleAbstractions.add(method.getName());
  201. } else {
  202. // error, an outstanding abstract method that can't be
  203. // concretized in XML
  204. reportError("Abstract method '" + method.toString() + "' cannot be concretized in XML: " + stringify());
  205. return false;
  206. }
  207. } else {
  208. if (method.getName().startsWith("ajc$pointcut") || hasPointcutAnnotation(method)) {
  209. // it may be a pointcut but it doesn't meet the requirements
  210. // for XML concretization
  211. reportError("Abstract method '"
  212. + method.toString()
  213. + "' cannot be concretized as a pointcut (illegal signature, must have no arguments, must return void): "
  214. + stringify());
  215. return false;
  216. } else {
  217. // error, an outstanding abstract method that can't be
  218. // concretized in XML
  219. reportError("Abstract method '" + method.toString() + "' cannot be concretized in XML: " + stringify());
  220. return false;
  221. }
  222. }
  223. }
  224. List<String> pointcutNames = new ArrayList<String>();
  225. for (Definition.Pointcut abstractPc : concreteAspect.pointcuts) {
  226. pointcutNames.add(abstractPc.name);
  227. }
  228. for (String elligiblePc : elligibleAbstractions) {
  229. if (!pointcutNames.contains(elligiblePc)) {
  230. reportError("Abstract pointcut '" + elligiblePc + "' not configured: " + stringify());
  231. return false;
  232. }
  233. }
  234. if (concreteAspect.perclause != null) {
  235. String perclauseString = concreteAspect.perclause;
  236. if (perclauseString.startsWith("persingleton")) {
  237. } else if (perclauseString.startsWith("percflow")) {
  238. } else if (perclauseString.startsWith("pertypewithin")) {
  239. } else if (perclauseString.startsWith("perthis")) {
  240. } else if (perclauseString.startsWith("pertarget")) {
  241. } else if (perclauseString.startsWith("percflowbelow")) {
  242. } else {
  243. reportError("Unrecognized per clause specified " + stringify());
  244. return false;
  245. }
  246. }
  247. isValid = true;
  248. return isValid;
  249. }
  250. private Collection<ResolvedMember> getOutstandingAbstractMethods(ResolvedType type) {
  251. Map<String, ResolvedMember> collector = new HashMap<String, ResolvedMember>();
  252. // let's get to the top of the hierarchy and then walk down ...
  253. // recording abstract methods then removing
  254. // them if they get defined further down the hierarchy
  255. getOutstandingAbstractMethodsHelper(type, collector);
  256. return collector.values();
  257. }
  258. // We are trying to determine abstract methods left over at the bottom of a
  259. // hierarchy that have not been concretized.
  260. private void getOutstandingAbstractMethodsHelper(ResolvedType type, Map<String, ResolvedMember> collector) {
  261. if (type == null) {
  262. return;
  263. }
  264. // Get to the top
  265. if (!type.equals(ResolvedType.OBJECT)) {
  266. if (type.getSuperclass() != null) {
  267. getOutstandingAbstractMethodsHelper(type.getSuperclass(), collector);
  268. }
  269. }
  270. ResolvedMember[] rms = type.getDeclaredMethods();
  271. if (rms != null) {
  272. for (int i = 0; i < rms.length; i++) {
  273. ResolvedMember member = rms[i];
  274. String key = member.getName() + member.getSignature();
  275. if (member.isAbstract()) {
  276. collector.put(key, member);
  277. } else {
  278. collector.remove(key);
  279. }
  280. }
  281. }
  282. }
  283. /**
  284. * Rebuild the XML snip that defines this concrete aspect, for log error purpose
  285. *
  286. * @return string repr.
  287. */
  288. private String stringify() {
  289. StringBuffer sb = new StringBuffer("<concrete-aspect name='");
  290. sb.append(concreteAspect.name);
  291. sb.append("' extends='");
  292. sb.append(concreteAspect.extend);
  293. sb.append("' perclause='");
  294. sb.append(concreteAspect.perclause);
  295. sb.append("'/> in aop.xml");
  296. return sb.toString();
  297. }
  298. private boolean hasPointcutAnnotation(ResolvedMember member) {
  299. AnnotationAJ[] as = member.getAnnotations();
  300. if (as == null || as.length == 0) {
  301. return false;
  302. }
  303. for (int i = 0; i < as.length; i++) {
  304. if (as[i].getTypeSignature().equals("Lorg/aspectj/lang/annotation/Pointcut;")) {
  305. return true;
  306. }
  307. }
  308. return false;
  309. }
  310. public String getClassName() {
  311. return concreteAspect.name;
  312. }
  313. /**
  314. * Build the bytecode for the concrete aspect
  315. *
  316. * @return concrete aspect bytecode
  317. */
  318. public byte[] getBytes() {
  319. if (!isValid) {
  320. throw new RuntimeException("Must validate first");
  321. }
  322. if (bytes != null) {
  323. return bytes;
  324. }
  325. PerClause parentPerClause = (parent != null ? parent.getPerClause() : null);
  326. if (parentPerClause == null) {
  327. parentPerClause = new PerSingleton();
  328. }
  329. PerClause.Kind perclauseKind = PerClause.SINGLETON;
  330. String perclauseString = null;
  331. if (concreteAspect.perclause != null) {
  332. perclauseString = concreteAspect.perclause;
  333. if (perclauseString.startsWith("persingleton")) {
  334. perclauseKind = PerClause.SINGLETON;
  335. } else if (perclauseString.startsWith("percflow")) {
  336. perclauseKind = PerClause.PERCFLOW;
  337. } else if (perclauseString.startsWith("pertypewithin")) {
  338. perclauseKind = PerClause.PERTYPEWITHIN;
  339. } else if (perclauseString.startsWith("perthis")) {
  340. perclauseKind = PerClause.PEROBJECT;
  341. } else if (perclauseString.startsWith("pertarget")) {
  342. perclauseKind = PerClause.PEROBJECT;
  343. } else if (perclauseString.startsWith("percflowbelow")) {
  344. perclauseKind = PerClause.PERCFLOW;
  345. }
  346. }
  347. // TODO AV - abstract away from BCEL...
  348. // @Aspect //inherit clause from m_parent
  349. // @DeclarePrecedence("....") // if any
  350. // public class xxxName [extends xxxExtends] {
  351. // [@Pointcut(xxxExpression-n)
  352. // public void xxxName-n() {}]
  353. // }
  354. String parentName = "java/lang/Object";
  355. if (parent != null) {
  356. if (parent.isParameterizedType()) {
  357. parentName = parent.getGenericType().getName().replace('.', '/');
  358. } else {
  359. parentName = parent.getName().replace('.', '/');
  360. }
  361. }
  362. // @Aspect public class ...
  363. // TODO AV - we could point to the aop.xml that defines it and use
  364. // JSR-45
  365. LazyClassGen cg = new LazyClassGen(concreteAspect.name.replace('.', '/'), parentName, null, Modifier.PUBLIC
  366. + Constants.ACC_SUPER, EMPTY_STRINGS, world);
  367. if (parent != null && parent.isParameterizedType()) {
  368. cg.setSuperClass(parent);
  369. }
  370. if (perclauseString == null) {
  371. AnnotationGen ag = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/Aspect"),
  372. Collections.<NameValuePair> emptyList(), true, cg.getConstantPool());
  373. cg.addAnnotation(ag);
  374. } else {
  375. // List elems = new ArrayList();
  376. List<NameValuePair> elems = new ArrayList<NameValuePair>();
  377. elems.add(new NameValuePair("value",
  378. new SimpleElementValue(ElementValue.STRING, cg.getConstantPool(), perclauseString), cg.getConstantPool()));
  379. AnnotationGen ag = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/Aspect"), elems, true,
  380. cg.getConstantPool());
  381. cg.addAnnotation(ag);
  382. }
  383. if (concreteAspect.precedence != null) {
  384. SimpleElementValue svg = new SimpleElementValue(ElementValue.STRING, cg.getConstantPool(), concreteAspect.precedence);
  385. List<NameValuePair> elems = new ArrayList<NameValuePair>();
  386. elems.add(new NameValuePair("value", svg, cg.getConstantPool()));
  387. AnnotationGen agprec = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/DeclarePrecedence"), elems, true,
  388. cg.getConstantPool());
  389. cg.addAnnotation(agprec);
  390. }
  391. // default constructor
  392. LazyMethodGen init = new LazyMethodGen(Modifier.PUBLIC, Type.VOID, "<init>", EMPTY_TYPES, EMPTY_STRINGS, cg);
  393. InstructionList cbody = init.getBody();
  394. cbody.append(InstructionConstants.ALOAD_0);
  395. cbody.append(cg.getFactory().createInvoke(parentName, "<init>", Type.VOID, EMPTY_TYPES, Constants.INVOKESPECIAL));
  396. cbody.append(InstructionConstants.RETURN);
  397. cg.addMethodGen(init);
  398. for (Iterator<Definition.Pointcut> it = concreteAspect.pointcuts.iterator(); it.hasNext();) {
  399. Definition.Pointcut abstractPc = (Definition.Pointcut) it.next();
  400. // TODO AV - respect visibility instead of opening up as public?
  401. LazyMethodGen mg = new LazyMethodGen(Modifier.PUBLIC, Type.VOID, abstractPc.name, EMPTY_TYPES, EMPTY_STRINGS, cg);
  402. SimpleElementValue svg = new SimpleElementValue(ElementValue.STRING, cg.getConstantPool(), abstractPc.expression);
  403. List<NameValuePair> elems = new ArrayList<NameValuePair>();
  404. elems.add(new NameValuePair("value", svg, cg.getConstantPool()));
  405. AnnotationGen mag = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/Pointcut"), elems, true,
  406. cg.getConstantPool());
  407. AnnotationAJ max = new BcelAnnotation(mag, world);
  408. mg.addAnnotation(max);
  409. InstructionList body = mg.getBody();
  410. body.append(InstructionConstants.RETURN);
  411. cg.addMethodGen(mg);
  412. }
  413. if (concreteAspect.deows.size() > 0) {
  414. int counter = 1;
  415. for (Definition.DeclareErrorOrWarning deow : concreteAspect.deows) {
  416. // Building this:
  417. // @DeclareWarning("call(* javax.sql..*(..)) && !within(org.xyz.daos..*)")
  418. // static final String aMessage = "Only DAOs should be calling JDBC.";
  419. FieldGen field = new FieldGen(Modifier.FINAL, ObjectType.STRING, "rule" + (counter++), cg.getConstantPool());
  420. SimpleElementValue svg = new SimpleElementValue(ElementValue.STRING, cg.getConstantPool(), deow.pointcut);
  421. List<NameValuePair> elems = new ArrayList<NameValuePair>();
  422. elems.add(new NameValuePair("value", svg, cg.getConstantPool()));
  423. AnnotationGen mag = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/Declare"
  424. + (deow.isError ? "Error" : "Warning")), elems, true, cg.getConstantPool());
  425. field.addAnnotation(mag);
  426. field.setValue(deow.message);
  427. cg.addField(field, null);
  428. }
  429. }
  430. if (concreteAspect.pointcutsAndAdvice.size() > 0) {
  431. int adviceCounter = 1;
  432. for (PointcutAndAdvice paa : concreteAspect.pointcutsAndAdvice) {
  433. generateAdviceMethod(paa, adviceCounter, cg);
  434. adviceCounter++;
  435. }
  436. }
  437. // handle the perClause
  438. ReferenceType rt = new ReferenceType(ResolvedType.forName(concreteAspect.name).getSignature(), world);
  439. GeneratedReferenceTypeDelegate grtd = new GeneratedReferenceTypeDelegate(rt);
  440. grtd.setSuperclass(parent);
  441. rt.setDelegate(grtd);
  442. BcelPerClauseAspectAdder perClauseMunger = new BcelPerClauseAspectAdder(rt, perclauseKind);
  443. perClauseMunger.forceMunge(cg, false);
  444. // TODO AV - unsafe cast
  445. // register the fresh new class into the world repository as it does not
  446. // exist on the classpath anywhere
  447. JavaClass jc = cg.getJavaClass((BcelWorld) world);
  448. ((BcelWorld) world).addSourceObjectType(jc, true);
  449. bytes = jc.getBytes();
  450. return bytes;
  451. }
  452. /**
  453. * The PointcutAndAdvice object encapsulates an advice kind, a pointcut and names a Java method in a particular class. Generate
  454. * an annotation style advice that has that pointcut whose implementation delegates to the Java method.
  455. */
  456. private void generateAdviceMethod(PointcutAndAdvice paa, int adviceCounter, LazyClassGen cg) {
  457. // Check: Verify the class to be called does exist:
  458. ResolvedType delegateClass = world.resolve(UnresolvedType.forName(paa.adviceClass));
  459. if (delegateClass.isMissing()) {
  460. reportError("Class to invoke cannot be found: '" + paa.adviceClass + "'");
  461. return;
  462. }
  463. // Generate a name for this advice, includes advice kind plus a counter
  464. String adviceName = new StringBuilder("generated$").append(paa.adviceKind.toString().toLowerCase()).append("$advice$")
  465. .append(adviceCounter).toString();
  466. // Build the annotation that encapsulates the pointcut
  467. AnnotationAJ aaj = buildAdviceAnnotation(cg, paa);
  468. // Chop up the supplied advice method string into its pieces.
  469. // Example: foo(JoinPoint jp, java.lang.String string)
  470. // JoinPoint and friends are recognized (so dont need fq package)
  471. String method = paa.adviceMethod;
  472. int paren = method.indexOf("(");
  473. String methodName = method.substring(0, paren);
  474. String signature = method.substring(paren);
  475. // Check: signature looks ok
  476. if (signature.charAt(0) != '(' || !signature.endsWith(")")) {
  477. reportError("Badly formatted parameter signature: '" + method + "'");
  478. return;
  479. }
  480. // Extract parameter types and names
  481. List<Type> paramTypes = new ArrayList<Type>();
  482. List<String> paramNames = new ArrayList<String>();
  483. if (signature.charAt(1) != ')') {
  484. // there are parameters to convert into a signature
  485. StringBuilder convertedSignature = new StringBuilder("(");
  486. boolean paramsBroken = false;
  487. int pos = 1;
  488. while (pos < signature.length() && signature.charAt(pos) != ')' && !paramsBroken) {
  489. int nextChunkEndPos = signature.indexOf(',', pos);
  490. if (nextChunkEndPos == -1) {
  491. nextChunkEndPos = signature.indexOf(')', pos);
  492. }
  493. // chunk will be a type plus a space plus a name
  494. String nextChunk = signature.substring(pos, nextChunkEndPos).trim();
  495. int space = nextChunk.indexOf(" ");
  496. ResolvedType resolvedParamType = null;
  497. if (space == -1) {
  498. // There is no parameter name, hopefully not needed!
  499. if (nextChunk.equals("JoinPoint")) {
  500. nextChunk = "org.aspectj.lang.JoinPoint";
  501. } else if (nextChunk.equals("JoinPoint.StaticPart")) {
  502. nextChunk = "org.aspectj.lang.JoinPoint$StaticPart";
  503. } else if (nextChunk.equals("ProceedingJoinPoint")) {
  504. nextChunk = "org.aspectj.lang.ProceedingJoinPoint";
  505. }
  506. UnresolvedType unresolvedParamType = UnresolvedType.forName(nextChunk);
  507. resolvedParamType = world.resolve(unresolvedParamType);
  508. } else {
  509. String typename = nextChunk.substring(0, space);
  510. if (typename.equals("JoinPoint")) {
  511. typename = "org.aspectj.lang.JoinPoint";
  512. } else if (typename.equals("JoinPoint.StaticPart")) {
  513. typename = "org.aspectj.lang.JoinPoint$StaticPart";
  514. } else if (typename.equals("ProceedingJoinPoint")) {
  515. typename = "org.aspectj.lang.ProceedingJoinPoint";
  516. }
  517. UnresolvedType unresolvedParamType = UnresolvedType.forName(typename);
  518. resolvedParamType = world.resolve(unresolvedParamType);
  519. String paramname = nextChunk.substring(space).trim();
  520. paramNames.add(paramname);
  521. }
  522. if (resolvedParamType.isMissing()) {
  523. reportError("Cannot find type specified as parameter: '" + nextChunk + "' from signature '" + signature + "'");
  524. paramsBroken = true;
  525. }
  526. paramTypes.add(Type.getType(resolvedParamType.getSignature()));
  527. convertedSignature.append(resolvedParamType.getSignature());
  528. pos = nextChunkEndPos + 1;
  529. }
  530. convertedSignature.append(")");
  531. signature = convertedSignature.toString();
  532. if (paramsBroken) {
  533. return;
  534. }
  535. }
  536. Type returnType = Type.VOID;
  537. // If around advice we must find the actual delegate method and use its return type
  538. if (paa.adviceKind == AdviceKind.Around) {
  539. ResolvedMember[] methods = delegateClass.getDeclaredMethods();
  540. ResolvedMember found = null;
  541. for (ResolvedMember candidate : methods) {
  542. if (candidate.getName().equals(methodName)) {
  543. UnresolvedType[] cparms = candidate.getParameterTypes();
  544. if (cparms.length == paramTypes.size()) {
  545. boolean paramsMatch = true;
  546. for (int i = 0; i < cparms.length; i++) {
  547. if (!cparms[i].getSignature().equals(paramTypes.get(i).getSignature())) {
  548. paramsMatch = false;
  549. break;
  550. }
  551. }
  552. if (paramsMatch) {
  553. found = candidate;
  554. break;
  555. }
  556. }
  557. }
  558. }
  559. if (found != null) {
  560. returnType = Type.getType(found.getReturnType().getSignature());
  561. } else {
  562. reportError("Unable to find method to invoke. In class: " + delegateClass.getName() + " cant find "
  563. + paa.adviceMethod);
  564. return;
  565. }
  566. }
  567. // Time to construct the method itself:
  568. LazyMethodGen advice = new LazyMethodGen(Modifier.PUBLIC, returnType, adviceName, paramTypes.toArray(new Type[paramTypes
  569. .size()]), EMPTY_STRINGS, cg);
  570. InstructionList adviceBody = advice.getBody();
  571. // Generate code to load the parameters
  572. int pos = 1; // first slot after 'this'
  573. for (int i = 0; i < paramTypes.size(); i++) {
  574. adviceBody.append(InstructionFactory.createLoad(paramTypes.get(i), pos));
  575. pos += paramTypes.get(i).getSize();
  576. }
  577. // Generate the delegate call
  578. adviceBody.append(cg.getFactory().createInvoke(paa.adviceClass, methodName, signature + returnType.getSignature(),
  579. Constants.INVOKESTATIC));
  580. // Generate the right return
  581. if (returnType == Type.VOID) {
  582. adviceBody.append(InstructionConstants.RETURN);
  583. } else {
  584. if (returnType.getSignature().length() < 2) {
  585. String sig = returnType.getSignature();
  586. if (sig.equals("F")) {
  587. adviceBody.append(InstructionConstants.FRETURN);
  588. } else if (sig.equals("D")) {
  589. adviceBody.append(InstructionConstants.DRETURN);
  590. } else if (sig.equals("J")) {
  591. adviceBody.append(InstructionConstants.LRETURN);
  592. } else {
  593. adviceBody.append(InstructionConstants.IRETURN);
  594. }
  595. } else {
  596. adviceBody.append(InstructionConstants.ARETURN);
  597. }
  598. }
  599. // Add the annotation
  600. advice.addAnnotation(aaj);
  601. InstructionHandle start = adviceBody.getStart();
  602. // Setup the local variable targeters so that the binding will work
  603. String sig = concreteAspect.name.replace('.', '/');
  604. start.addTargeter(new LocalVariableTag("L" + sig + ";", "this", 0, start.getPosition()));
  605. if (paramNames.size() > 0) {
  606. for (int i = 0; i < paramNames.size(); i++) {
  607. start.addTargeter(new LocalVariableTag(paramTypes.get(i).getSignature(), paramNames.get(i), i + 1, start
  608. .getPosition()));
  609. }
  610. }
  611. // Record the new method in the class
  612. cg.addMethodGen(advice);
  613. }
  614. /**
  615. * For the given PointcutAndAdvice build the correct advice annotation.
  616. */
  617. private AnnotationAJ buildAdviceAnnotation(LazyClassGen cg, PointcutAndAdvice paa) {
  618. SimpleElementValue svg = new SimpleElementValue(ElementValue.STRING, cg.getConstantPool(), paa.pointcut);
  619. List<NameValuePair> elems = new ArrayList<NameValuePair>();
  620. elems.add(new NameValuePair("value", svg, cg.getConstantPool()));
  621. AnnotationGen mag = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/" + paa.adviceKind.toString()), elems,
  622. true, cg.getConstantPool());
  623. AnnotationAJ aaj = new BcelAnnotation(mag, world);
  624. return aaj;
  625. }
  626. /**
  627. * Error reporting
  628. *
  629. * @param message
  630. */
  631. private void reportError(String message) {
  632. world.getMessageHandler().handleMessage(new Message(message, IMessage.ERROR, null, null));
  633. }
  634. }