Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

ConcreteAspectCodeGen.java 37KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994
  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.ConstantPool;
  23. import org.aspectj.apache.bcel.classfile.JavaClass;
  24. import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen;
  25. import org.aspectj.apache.bcel.classfile.annotation.ClassElementValue;
  26. import org.aspectj.apache.bcel.classfile.annotation.ElementValue;
  27. import org.aspectj.apache.bcel.classfile.annotation.NameValuePair;
  28. import org.aspectj.apache.bcel.classfile.annotation.SimpleElementValue;
  29. import org.aspectj.apache.bcel.generic.FieldGen;
  30. import org.aspectj.apache.bcel.generic.InstructionConstants;
  31. import org.aspectj.apache.bcel.generic.InstructionFactory;
  32. import org.aspectj.apache.bcel.generic.InstructionHandle;
  33. import org.aspectj.apache.bcel.generic.InstructionList;
  34. import org.aspectj.apache.bcel.generic.LocalVariableTag;
  35. import org.aspectj.apache.bcel.generic.ObjectType;
  36. import org.aspectj.apache.bcel.generic.Type;
  37. import org.aspectj.bridge.IMessage;
  38. import org.aspectj.bridge.Message;
  39. import org.aspectj.weaver.AjAttribute;
  40. import org.aspectj.weaver.AnnotationAJ;
  41. import org.aspectj.weaver.GeneratedReferenceTypeDelegate;
  42. import org.aspectj.weaver.ReferenceType;
  43. import org.aspectj.weaver.ResolvedMember;
  44. import org.aspectj.weaver.ResolvedType;
  45. import org.aspectj.weaver.UnresolvedType;
  46. import org.aspectj.weaver.World;
  47. import org.aspectj.weaver.bcel.BcelAnnotation;
  48. import org.aspectj.weaver.bcel.BcelPerClauseAspectAdder;
  49. import org.aspectj.weaver.bcel.BcelWorld;
  50. import org.aspectj.weaver.bcel.LazyClassGen;
  51. import org.aspectj.weaver.bcel.LazyMethodGen;
  52. import org.aspectj.weaver.loadtime.definition.Definition;
  53. import org.aspectj.weaver.loadtime.definition.Definition.AdviceKind;
  54. import org.aspectj.weaver.loadtime.definition.Definition.DeclareAnnotationKind;
  55. import org.aspectj.weaver.loadtime.definition.Definition.PointcutAndAdvice;
  56. import org.aspectj.weaver.patterns.BasicTokenSource;
  57. import org.aspectj.weaver.patterns.DeclareAnnotation;
  58. import org.aspectj.weaver.patterns.ISignaturePattern;
  59. import org.aspectj.weaver.patterns.ITokenSource;
  60. import org.aspectj.weaver.patterns.NamePattern;
  61. import org.aspectj.weaver.patterns.PatternParser;
  62. import org.aspectj.weaver.patterns.PerClause;
  63. import org.aspectj.weaver.patterns.PerSingleton;
  64. import org.aspectj.weaver.patterns.TypePattern;
  65. /**
  66. * Generates bytecode for concrete-aspect.
  67. * <p>
  68. * The concrete aspect is generated annotation style aspect (so traditional Java constructs annotated with our AspectJ annotations).
  69. * As it is built during aop.xml definitions registration we perform the type munging for perclause, ie. aspectOf() artifact
  70. * directly, instead of waiting for it to go thru the weaver (that we are in the middle of configuring).
  71. *
  72. * @author Alexandre Vasseur
  73. * @author Andy Clement
  74. */
  75. public class ConcreteAspectCodeGen {
  76. private final static String[] EMPTY_STRINGS = new String[0];
  77. private final static Type[] EMPTY_TYPES = new Type[0];
  78. /**
  79. * Concrete aspect definition we build for
  80. */
  81. private final Definition.ConcreteAspect concreteAspect;
  82. /**
  83. * World for which we build for
  84. */
  85. private final World world;
  86. /**
  87. * Set to true when all is checks are verified
  88. */
  89. private boolean isValid = false;
  90. /**
  91. * The parent aspect, not concretized
  92. */
  93. private ResolvedType parent;
  94. /**
  95. * Aspect perClause, used for direct munging of aspectOf artifacts
  96. */
  97. private PerClause perclause;
  98. /**
  99. * Bytecode for the generated class
  100. */
  101. private byte[] bytes;
  102. /**
  103. * Create a new generator for a concrete aspect
  104. *
  105. * @param concreteAspect the aspect definition
  106. * @param world the related world (for type resolution, etc)
  107. */
  108. ConcreteAspectCodeGen(Definition.ConcreteAspect concreteAspect, World world) {
  109. this.concreteAspect = concreteAspect;
  110. this.world = world;
  111. }
  112. /**
  113. * Checks that concrete aspect is valid.
  114. *
  115. * @return true if ok, false otherwise
  116. */
  117. public boolean validate() {
  118. if (!(world instanceof BcelWorld)) {
  119. reportError("Internal error: world must be of type BcelWorld");
  120. return false;
  121. }
  122. // name must be undefined so far
  123. // TODO only convert the name to signature once, probably earlier than this
  124. ResolvedType current = world.lookupBySignature(UnresolvedType.forName(concreteAspect.name).getSignature());
  125. if (current != null && !current.isMissing()) {
  126. reportError("Attempt to concretize but chosen aspect name already defined: " + stringify());
  127. return false;
  128. }
  129. if (concreteAspect.pointcutsAndAdvice.size() != 0) {
  130. isValid = true;
  131. return true;
  132. }
  133. if (concreteAspect.declareAnnotations.size()!=0) {
  134. isValid = true;
  135. return true;
  136. }
  137. // it can happen that extends is null, for precedence only declaration
  138. if (concreteAspect.extend == null && concreteAspect.precedence != null) {
  139. if (concreteAspect.pointcuts.isEmpty()) {
  140. isValid = true;
  141. // m_perClause = new PerSingleton();
  142. parent = null;
  143. return true;// no need to checks more in that special case
  144. } else {
  145. reportError("Attempt to use nested pointcuts without extends clause: " + stringify());
  146. return false;
  147. }
  148. }
  149. String parentAspectName = concreteAspect.extend;
  150. if (parentAspectName.indexOf("<") != -1) {
  151. // yikes, generic parent
  152. parent = world.resolve(UnresolvedType.forName(parentAspectName), true);
  153. if (parent.isMissing()) {
  154. reportError("Unable to resolve type reference: " + stringify());
  155. return false;
  156. }
  157. if (parent.isParameterizedType()) {
  158. UnresolvedType[] typeParameters = parent.getTypeParameters();
  159. for (int i = 0; i < typeParameters.length; i++) {
  160. UnresolvedType typeParameter = typeParameters[i];
  161. if (typeParameter instanceof ResolvedType && ((ResolvedType) typeParameter).isMissing()) {
  162. reportError("Unablet to resolve type parameter '" + typeParameter.getName() + "' from " + stringify());
  163. return false;
  164. }
  165. }
  166. }
  167. } else {
  168. parent = world.resolve(concreteAspect.extend, true);
  169. }
  170. // handle inner classes
  171. if (parent.isMissing()) {
  172. // fallback on inner class lookup mechanism
  173. String fixedName = concreteAspect.extend;
  174. int hasDot = fixedName.lastIndexOf('.');
  175. while (hasDot > 0) {
  176. char[] fixedNameChars = fixedName.toCharArray();
  177. fixedNameChars[hasDot] = '$';
  178. fixedName = new String(fixedNameChars);
  179. hasDot = fixedName.lastIndexOf('.');
  180. parent = world.resolve(UnresolvedType.forName(fixedName), true);
  181. if (!parent.isMissing()) {
  182. break;
  183. }
  184. }
  185. }
  186. if (parent.isMissing()) {
  187. reportError("Cannot find parent aspect for: " + stringify());
  188. return false;
  189. }
  190. // extends must be abstract (allow for special case of Object where just using aspect for deows)
  191. if (!(parent.isAbstract() || parent.equals(ResolvedType.OBJECT))) {
  192. reportError("Attempt to concretize a non-abstract aspect: " + stringify());
  193. return false;
  194. }
  195. // m_parent must be aspect (allow for special case of Object where just using aspect for deows)
  196. if (!(parent.isAspect() || parent.equals(ResolvedType.OBJECT))) {
  197. reportError("Attempt to concretize a non aspect: " + stringify());
  198. return false;
  199. }
  200. // must have all abstractions defined
  201. List<String> elligibleAbstractions = new ArrayList<String>();
  202. Collection<ResolvedMember> abstractMethods = getOutstandingAbstractMethods(parent);
  203. for (ResolvedMember method : abstractMethods) {
  204. if ("()V".equals(method.getSignature())) {
  205. String n = method.getName();
  206. // Allow for the abstract pointcut being from a code style
  207. // aspect compiled with -1.5 (see test for 128744)
  208. if (n.startsWith("ajc$pointcut")) {
  209. n = n.substring(14);
  210. n = n.substring(0, n.indexOf("$"));
  211. elligibleAbstractions.add(n);
  212. } else if (hasPointcutAnnotation(method)) {
  213. elligibleAbstractions.add(method.getName());
  214. } else {
  215. // error, an outstanding abstract method that can't be
  216. // concretized in XML
  217. reportError("Abstract method '" + method.toString() + "' cannot be concretized in XML: " + stringify());
  218. return false;
  219. }
  220. } else {
  221. if (method.getName().startsWith("ajc$pointcut") || hasPointcutAnnotation(method)) {
  222. // it may be a pointcut but it doesn't meet the requirements for XML concretization
  223. reportError("Abstract method '"
  224. + method.toString()
  225. + "' cannot be concretized as a pointcut (illegal signature, must have no arguments, must return void): "
  226. + stringify());
  227. return false;
  228. } else {
  229. // error, an outstanding abstract method that can't be
  230. // concretized in XML
  231. reportError("Abstract method '" + method.toString() + "' cannot be concretized in XML: " + stringify());
  232. return false;
  233. }
  234. }
  235. }
  236. List<String> pointcutNames = new ArrayList<String>();
  237. for (Definition.Pointcut abstractPc : concreteAspect.pointcuts) {
  238. pointcutNames.add(abstractPc.name);
  239. }
  240. for (String elligiblePc : elligibleAbstractions) {
  241. if (!pointcutNames.contains(elligiblePc)) {
  242. reportError("Abstract pointcut '" + elligiblePc + "' not configured: " + stringify());
  243. return false;
  244. }
  245. }
  246. if (concreteAspect.perclause != null) {
  247. String perclauseString = concreteAspect.perclause;
  248. if (perclauseString.startsWith("persingleton")) {
  249. } else if (perclauseString.startsWith("percflow")) {
  250. } else if (perclauseString.startsWith("pertypewithin")) {
  251. } else if (perclauseString.startsWith("perthis")) {
  252. } else if (perclauseString.startsWith("pertarget")) {
  253. } else if (perclauseString.startsWith("percflowbelow")) {
  254. } else {
  255. reportError("Unrecognized per clause specified " + stringify());
  256. return false;
  257. }
  258. }
  259. isValid = true;
  260. return isValid;
  261. }
  262. private Collection<ResolvedMember> getOutstandingAbstractMethods(ResolvedType type) {
  263. Map<String, ResolvedMember> collector = new HashMap<String, ResolvedMember>();
  264. // let's get to the top of the hierarchy and then walk down ...
  265. // recording abstract methods then removing
  266. // them if they get defined further down the hierarchy
  267. getOutstandingAbstractMethodsHelper(type, collector);
  268. return collector.values();
  269. }
  270. // We are trying to determine abstract methods left over at the bottom of a
  271. // hierarchy that have not been concretized.
  272. private void getOutstandingAbstractMethodsHelper(ResolvedType type, Map<String, ResolvedMember> collector) {
  273. if (type == null) {
  274. return;
  275. }
  276. // Get to the top
  277. if (!type.equals(ResolvedType.OBJECT)) {
  278. if (type.getSuperclass() != null) {
  279. getOutstandingAbstractMethodsHelper(type.getSuperclass(), collector);
  280. }
  281. }
  282. ResolvedMember[] rms = type.getDeclaredMethods();
  283. if (rms != null) {
  284. for (int i = 0; i < rms.length; i++) {
  285. ResolvedMember member = rms[i];
  286. String key = member.getName() + member.getSignature();
  287. if (member.isAbstract()) {
  288. collector.put(key, member);
  289. } else {
  290. collector.remove(key);
  291. }
  292. }
  293. }
  294. }
  295. /**
  296. * Rebuild the XML snip that defines this concrete aspect, for log error purpose
  297. *
  298. * @return string repr.
  299. */
  300. private String stringify() {
  301. StringBuffer sb = new StringBuffer("<concrete-aspect name='");
  302. sb.append(concreteAspect.name);
  303. sb.append("' extends='");
  304. sb.append(concreteAspect.extend);
  305. sb.append("' perclause='");
  306. sb.append(concreteAspect.perclause);
  307. sb.append("'/> in aop.xml");
  308. // TODO needs the extra state from the definition (concretized pointcuts and advice definitions)
  309. return sb.toString();
  310. }
  311. private boolean hasPointcutAnnotation(ResolvedMember member) {
  312. AnnotationAJ[] as = member.getAnnotations();
  313. if (as == null || as.length == 0) {
  314. return false;
  315. }
  316. for (int i = 0; i < as.length; i++) {
  317. if (as[i].getTypeSignature().equals("Lorg/aspectj/lang/annotation/Pointcut;")) {
  318. return true;
  319. }
  320. }
  321. return false;
  322. }
  323. public String getClassName() {
  324. return concreteAspect.name;
  325. }
  326. /**
  327. * Build the bytecode for the concrete aspect
  328. *
  329. * @return concrete aspect bytecode
  330. */
  331. public byte[] getBytes() {
  332. if (!isValid) {
  333. throw new RuntimeException("Must validate first");
  334. }
  335. if (bytes != null) {
  336. return bytes;
  337. }
  338. PerClause.Kind perclauseKind = PerClause.SINGLETON;
  339. PerClause parentPerClause = (parent != null ? parent.getPerClause() : null);
  340. if (parentPerClause != null) {
  341. perclauseKind = parentPerClause.getKind();
  342. }
  343. String perclauseString = null;
  344. if (concreteAspect.perclause != null) {
  345. perclauseString = concreteAspect.perclause;
  346. if (perclauseString.startsWith("persingleton")) {
  347. perclauseKind = PerClause.SINGLETON;
  348. } else if (perclauseString.startsWith("percflow")) {
  349. perclauseKind = PerClause.PERCFLOW;
  350. } else if (perclauseString.startsWith("pertypewithin")) {
  351. perclauseKind = PerClause.PERTYPEWITHIN;
  352. } else if (perclauseString.startsWith("perthis")) {
  353. perclauseKind = PerClause.PEROBJECT;
  354. } else if (perclauseString.startsWith("pertarget")) {
  355. perclauseKind = PerClause.PEROBJECT;
  356. } else if (perclauseString.startsWith("percflowbelow")) {
  357. perclauseKind = PerClause.PERCFLOW;
  358. }
  359. }
  360. // @Aspect //inherit clause from m_parent
  361. // @DeclarePrecedence("....") // if any
  362. // public class xxxName [extends xxxExtends] {
  363. // [@Pointcut(xxxExpression-n)
  364. // public void xxxName-n() {}]
  365. // }
  366. String parentName = "java/lang/Object";
  367. if (parent != null) {
  368. if (parent.isParameterizedType()) {
  369. parentName = parent.getGenericType().getName().replace('.', '/');
  370. } else {
  371. parentName = parent.getName().replace('.', '/');
  372. }
  373. }
  374. // @Aspect public class ...
  375. // TODO AV - we could point to the aop.xml that defines it and use JSR-45
  376. LazyClassGen cg = new LazyClassGen(concreteAspect.name.replace('.', '/'), parentName, null, Modifier.PUBLIC
  377. + Constants.ACC_SUPER, EMPTY_STRINGS, world);
  378. if (parent != null && parent.isParameterizedType()) {
  379. cg.setSuperClass(parent);
  380. }
  381. if (perclauseString == null) {
  382. AnnotationGen ag = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/Aspect"),
  383. Collections.<NameValuePair> emptyList(), true, cg.getConstantPool());
  384. cg.addAnnotation(ag);
  385. } else {
  386. // List elems = new ArrayList();
  387. List<NameValuePair> elems = new ArrayList<NameValuePair>();
  388. elems.add(new NameValuePair("value",
  389. new SimpleElementValue(ElementValue.STRING, cg.getConstantPool(), perclauseString), cg.getConstantPool()));
  390. AnnotationGen ag = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/Aspect"), elems, true,
  391. cg.getConstantPool());
  392. cg.addAnnotation(ag);
  393. }
  394. if (concreteAspect.precedence != null) {
  395. SimpleElementValue svg = new SimpleElementValue(ElementValue.STRING, cg.getConstantPool(), concreteAspect.precedence);
  396. List<NameValuePair> elems = new ArrayList<NameValuePair>();
  397. elems.add(new NameValuePair("value", svg, cg.getConstantPool()));
  398. AnnotationGen agprec = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/DeclarePrecedence"), elems, true,
  399. cg.getConstantPool());
  400. cg.addAnnotation(agprec);
  401. }
  402. // default constructor
  403. LazyMethodGen init = new LazyMethodGen(Modifier.PUBLIC, Type.VOID, "<init>", EMPTY_TYPES, EMPTY_STRINGS, cg);
  404. InstructionList cbody = init.getBody();
  405. cbody.append(InstructionConstants.ALOAD_0);
  406. cbody.append(cg.getFactory().createInvoke(parentName, "<init>", Type.VOID, EMPTY_TYPES, Constants.INVOKESPECIAL));
  407. cbody.append(InstructionConstants.RETURN);
  408. cg.addMethodGen(init);
  409. for (Iterator<Definition.Pointcut> it = concreteAspect.pointcuts.iterator(); it.hasNext();) {
  410. Definition.Pointcut abstractPc = (Definition.Pointcut) it.next();
  411. // TODO AV - respect visibility instead of opening up as public?
  412. LazyMethodGen mg = new LazyMethodGen(Modifier.PUBLIC, Type.VOID, abstractPc.name, EMPTY_TYPES, EMPTY_STRINGS, cg);
  413. SimpleElementValue svg = new SimpleElementValue(ElementValue.STRING, cg.getConstantPool(), abstractPc.expression);
  414. List<NameValuePair> elems = new ArrayList<NameValuePair>();
  415. elems.add(new NameValuePair("value", svg, cg.getConstantPool()));
  416. AnnotationGen mag = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/Pointcut"), elems, true,
  417. cg.getConstantPool());
  418. AnnotationAJ max = new BcelAnnotation(mag, world);
  419. mg.addAnnotation(max);
  420. InstructionList body = mg.getBody();
  421. body.append(InstructionConstants.RETURN);
  422. cg.addMethodGen(mg);
  423. }
  424. // Construct any defined declare error/warnings
  425. if (concreteAspect.deows.size() > 0) {
  426. int counter = 1;
  427. for (Definition.DeclareErrorOrWarning deow : concreteAspect.deows) {
  428. // Building this:
  429. // @DeclareWarning("call(* javax.sql..*(..)) && !within(org.xyz.daos..*)")
  430. // static final String aMessage = "Only DAOs should be calling JDBC.";
  431. FieldGen field = new FieldGen(Modifier.FINAL, ObjectType.STRING, "rule" + (counter++), cg.getConstantPool());
  432. SimpleElementValue svg = new SimpleElementValue(ElementValue.STRING, cg.getConstantPool(), deow.pointcut);
  433. List<NameValuePair> elems = new ArrayList<NameValuePair>();
  434. elems.add(new NameValuePair("value", svg, cg.getConstantPool()));
  435. AnnotationGen mag = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/Declare"
  436. + (deow.isError ? "Error" : "Warning")), elems, true, cg.getConstantPool());
  437. field.addAnnotation(mag);
  438. field.setValue(deow.message);
  439. cg.addField(field, null);
  440. }
  441. }
  442. if (concreteAspect.pointcutsAndAdvice.size() > 0) {
  443. int adviceCounter = 1;
  444. for (PointcutAndAdvice paa : concreteAspect.pointcutsAndAdvice) {
  445. generateAdviceMethod(paa, adviceCounter, cg);
  446. adviceCounter++;
  447. }
  448. }
  449. if (concreteAspect.declareAnnotations.size()>0) {
  450. int decCounter = 1;
  451. for (Definition.DeclareAnnotation da: concreteAspect.declareAnnotations) {
  452. generateDeclareAnnotation(da,decCounter++,cg);
  453. }
  454. }
  455. // handle the perClause
  456. ReferenceType rt = new ReferenceType(ResolvedType.forName(concreteAspect.name).getSignature(), world);
  457. GeneratedReferenceTypeDelegate grtd = new GeneratedReferenceTypeDelegate(rt);
  458. grtd.setSuperclass(parent);
  459. rt.setDelegate(grtd);
  460. BcelPerClauseAspectAdder perClauseMunger = new BcelPerClauseAspectAdder(rt, perclauseKind);
  461. perClauseMunger.forceMunge(cg, false);
  462. // TODO AV - unsafe cast
  463. // register the fresh new class into the world repository as it does not
  464. // exist on the classpath anywhere
  465. JavaClass jc = cg.getJavaClass((BcelWorld) world);
  466. ((BcelWorld) world).addSourceObjectType(jc, true);
  467. bytes = jc.getBytes();
  468. return bytes;
  469. }
  470. /**
  471. * The DeclareAnnotation object encapsulates an method/field/type descriptor and an annotation. This uses a DeclareAnnotation object
  472. * captured from the XML (something like '<declare-annotation field="* field1(..)" annotation="@Annot(a='a',fred=false,'abc')"/>')
  473. * and builds the same construct that would have existed if the code style variant was used. This involves creating a member upon
  474. * which to hang the real annotation and then creating a classfile level attribute indicating a declare annotation is present
  475. * (that includes the signature pattern and a pointer to the real member holding the annotation).
  476. *
  477. */
  478. private void generateDeclareAnnotation(Definition.DeclareAnnotation da, int decCounter, LazyClassGen cg) {
  479. // Here is an example member from a code style declare annotation:
  480. //void ajc$declare_at_method_1();
  481. // RuntimeInvisibleAnnotations: length = 0x6
  482. // 00 01 00 1B 00 00
  483. // RuntimeVisibleAnnotations: length = 0x15
  484. // 00 01 00 1D 00 03 00 1E 73 00 1F 00 20 73 00 21
  485. // 00 22 73 00 23
  486. // org.aspectj.weaver.MethodDeclarationLineNumber: length = 0x8
  487. // 00 00 00 02 00 00 00 16
  488. // org.aspectj.weaver.AjSynthetic: length = 0x
  489. //
  490. // Code:
  491. // Stack=0, Locals=1, Args_size=1
  492. // 0: return
  493. // and at the class level a Declare attribute:
  494. // org.aspectj.weaver.Declare: length = 0x51
  495. // 05 00 00 00 03 01 00 05 40 41 6E 6E 6F 01 00 17
  496. // 61 6A 63 24 64 65 63 6C 61 72 65 5F 61 74 5F 6D
  497. // 65 74 68 6F 64 5F 31 01 01 00 00 00 00 05 05 00
  498. // 08 73 61 79 48 65 6C 6C 6F 00 01 04 00 00 00 00
  499. // 07 00 00 00 27 00 00 00 34 00 00 00 16 00 00 00
  500. // 3C
  501. AnnotationAJ constructedAnnotation = buildDeclareAnnotation_actualAnnotation(cg, da);
  502. if (constructedAnnotation==null) {
  503. return; // error occurred (and was reported), do not continue
  504. }
  505. String nameComponent = da.declareAnnotationKind.name().toLowerCase();
  506. String declareName = new StringBuilder("ajc$declare_at_").append(nameComponent).append("_").append(decCounter).toString();
  507. LazyMethodGen declareMethod = new LazyMethodGen(Modifier.PUBLIC, Type.VOID, declareName, Type.NO_ARGS, EMPTY_STRINGS, cg);
  508. InstructionList declareMethodBody = declareMethod.getBody();
  509. declareMethodBody.append(InstructionFactory.RETURN);
  510. declareMethod.addAnnotation(constructedAnnotation);
  511. DeclareAnnotation deca = null;
  512. ITokenSource tokenSource = BasicTokenSource.makeTokenSource(da.pattern,null);
  513. PatternParser pp = new PatternParser(tokenSource);
  514. if (da.declareAnnotationKind==DeclareAnnotationKind.Method || da.declareAnnotationKind==DeclareAnnotationKind.Field) {
  515. ISignaturePattern isp = (da.declareAnnotationKind==DeclareAnnotationKind.Method?pp.parseCompoundMethodOrConstructorSignaturePattern(true):pp.parseCompoundFieldSignaturePattern());
  516. deca = new DeclareAnnotation(da.declareAnnotationKind==DeclareAnnotationKind.Method?DeclareAnnotation.AT_METHOD:DeclareAnnotation.AT_FIELD, isp);
  517. } else if (da.declareAnnotationKind==DeclareAnnotationKind.Type) {
  518. TypePattern tp = pp.parseTypePattern();
  519. deca = new DeclareAnnotation(DeclareAnnotation.AT_TYPE,tp);
  520. }
  521. deca.setAnnotationMethod(declareName);
  522. deca.setAnnotationString(da.annotation);
  523. AjAttribute attribute = new AjAttribute.DeclareAttribute(deca);
  524. cg.addAttribute(attribute);
  525. cg.addMethodGen(declareMethod);
  526. }
  527. /**
  528. * Construct the annotation that the declare wants to add to the target.
  529. */
  530. private AnnotationAJ buildDeclareAnnotation_actualAnnotation(LazyClassGen cg, Definition.DeclareAnnotation da) {
  531. AnnotationGen anno = buildAnnotationFromString(cg.getConstantPool(),cg.getWorld(),da.annotation);
  532. if (anno==null) {
  533. return null;
  534. } else {
  535. AnnotationAJ bcelAnnotation = new BcelAnnotation(anno, world);
  536. return bcelAnnotation;
  537. }
  538. }
  539. // TODO support array values
  540. // TODO support annotation values
  541. /**
  542. * Build an AnnotationGen object based on a string, for example "@Foo(35,message='string')". Notice single quotes are fine for
  543. * strings as they don't need special handling in the XML.
  544. */
  545. private AnnotationGen buildAnnotationFromString(ConstantPool cp, World w, String annotationString) {
  546. int paren = annotationString.indexOf('(');
  547. if (paren==-1) {
  548. // the easy case, no values
  549. AnnotationGen aaj = buildBaseAnnotationType(cp,world,annotationString);
  550. return aaj;
  551. } else {
  552. // Discover the name and name/value pairs
  553. String name = annotationString.substring(0,paren);
  554. // break the rest into pieces based on the commas
  555. List<String> values = new ArrayList<String>();
  556. int pos = paren+1;
  557. int depth = 0;
  558. int len = annotationString.length();
  559. int start = pos;
  560. while (pos<len) {
  561. char ch = annotationString.charAt(pos);
  562. if (ch==')' && depth==0) {
  563. break; // reached the end
  564. }
  565. if (ch=='(' || ch=='[') {
  566. depth++;
  567. } else if (ch==')' || ch==']') {
  568. depth--;
  569. }
  570. if (ch==',' && depth==0) {
  571. // found a comma at the right level to end a name/value pair
  572. values.add(annotationString.substring(start,pos).trim());
  573. start=pos+1;
  574. }
  575. pos++;
  576. }
  577. if (start != pos) {
  578. // there is a last bit to add
  579. values.add(annotationString.substring(start,pos).trim());
  580. }
  581. AnnotationGen aaj = buildBaseAnnotationType(cp,world,name);
  582. if (aaj==null) {
  583. return null; // a check failed
  584. }
  585. String typename = aaj.getTypeName();
  586. ResolvedType type = UnresolvedType.forName(typename).resolve(world); // shame it isn't retrievable from the anno
  587. ResolvedMember[] rms = type.getDeclaredMethods();
  588. // Add the values
  589. for (String value: values) {
  590. int equalsIndex = value.indexOf("=");
  591. String key = "value";
  592. if (value.charAt(0)!='\"' && equalsIndex!=-1) {
  593. key = value.substring(0,equalsIndex).trim();
  594. value = value.substring(equalsIndex+1).trim();
  595. }
  596. boolean keyIsOk = false;
  597. for (int m=0;m<rms.length;m++) {
  598. NameValuePair nvp = null;
  599. if (rms[m].getName().equals(key)) {
  600. // found it!
  601. keyIsOk=true;
  602. UnresolvedType rt = rms[m].getReturnType();
  603. if (rt.isPrimitiveType()) {
  604. switch (rt.getSignature().charAt(0)) {
  605. case 'J': // long
  606. try {
  607. long longValue = Long.parseLong(value);
  608. nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.PRIMITIVE_LONG,cp,longValue),cp);
  609. } catch (NumberFormatException nfe) {
  610. reportError("unable to interpret annotation value '"+value+"' as a long");
  611. return null;
  612. }
  613. break;
  614. case 'S': // short
  615. try {
  616. short shortValue = Short.parseShort(value);
  617. nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.PRIMITIVE_SHORT,cp,shortValue),cp);
  618. } catch (NumberFormatException nfe) {
  619. reportError("unable to interpret annotation value '"+value+"' as a short");
  620. return null;
  621. }
  622. break;
  623. case 'F': // float
  624. try {
  625. float floatValue = Float.parseFloat(value);
  626. nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.PRIMITIVE_FLOAT,cp,floatValue),cp);
  627. } catch (NumberFormatException nfe) {
  628. reportError("unable to interpret annotation value '"+value+"' as a float");
  629. return null;
  630. }
  631. break;
  632. case 'D': // double
  633. try {
  634. double doubleValue = Double.parseDouble(value);
  635. nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.PRIMITIVE_DOUBLE,cp,doubleValue),cp);
  636. } catch (NumberFormatException nfe) {
  637. reportError("unable to interpret annotation value '"+value+"' as a double");
  638. return null;
  639. }
  640. break;
  641. case 'I': // integer
  642. try {
  643. int intValue = Integer.parseInt(value);
  644. nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.PRIMITIVE_INT,cp,intValue),cp);
  645. } catch (NumberFormatException nfe) {
  646. reportError("unable to interpret annotation value '"+value+"' as an integer");
  647. return null;
  648. }
  649. break;
  650. case 'B': // byte
  651. try {
  652. byte byteValue = Byte.parseByte(value);
  653. nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.PRIMITIVE_BYTE,cp,byteValue),cp);
  654. } catch (NumberFormatException nfe) {
  655. reportError("unable to interpret annotation value '"+value+"' as a byte");
  656. return null;
  657. }
  658. break;
  659. case 'C': // char
  660. if (value.length()<2) {
  661. reportError("unable to interpret annotation value '"+value+"' as a char");
  662. return null;
  663. }
  664. nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.PRIMITIVE_CHAR,cp,value.charAt(1)),cp);
  665. break;
  666. case 'Z': // boolean
  667. try {
  668. boolean booleanValue = Boolean.parseBoolean(value);
  669. nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.PRIMITIVE_BOOLEAN,cp,booleanValue),cp);
  670. } catch (NumberFormatException nfe) {
  671. reportError("unable to interpret annotation value '"+value+"' as a boolean");
  672. return null;
  673. }
  674. break;
  675. default:
  676. reportError("not yet supporting XML setting of annotation values of type "+rt.getName());
  677. return null;
  678. }
  679. } else if (UnresolvedType.JL_STRING.equals(rt)) {
  680. if (value.length()<2) {
  681. reportError("Invalid string value specified in annotation string: "+annotationString);
  682. return null;
  683. }
  684. value = value.substring(1,value.length()-1); // trim the quotes off
  685. nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.STRING,cp,value),cp);
  686. } else if (UnresolvedType.JL_CLASS.equals(rt)) {
  687. // format of class string:
  688. // Foo.class
  689. // java.lang.Foo.class
  690. if (value.length()<6) {
  691. reportError("Not a well formed class value for an annotation '"+value+"'");
  692. return null;
  693. }
  694. String clazz = value.substring(0,value.length()-6);
  695. boolean qualified = clazz.indexOf(".")!=-1;
  696. if (!qualified) {
  697. // if not qualified, have to assume java.lang
  698. clazz = "java.lang."+clazz;
  699. }
  700. nvp = new NameValuePair(key,new ClassElementValue(new ObjectType(clazz),cp),cp);
  701. }
  702. }
  703. if (nvp!=null) {
  704. aaj.addElementNameValuePair(nvp);
  705. }
  706. }
  707. if (!keyIsOk) {
  708. reportError("annotation @"+typename+" does not have a value named "+key);
  709. return null;
  710. }
  711. }
  712. return aaj;
  713. }
  714. }
  715. private AnnotationGen buildBaseAnnotationType(ConstantPool cp,World world, String typename) {
  716. String annoname = typename;
  717. if (annoname.startsWith("@")) {
  718. annoname= annoname.substring(1);
  719. }
  720. ResolvedType annotationType = UnresolvedType.forName(annoname).resolve(world);
  721. if (!annotationType.isAnnotation()) {
  722. reportError("declare is not specifying an annotation type :"+typename);
  723. return null;
  724. }
  725. if (!annotationType.isAnnotationWithRuntimeRetention()) {
  726. reportError("declare is using an annotation type that does not have runtime retention: "+typename);
  727. return null;
  728. }
  729. List<NameValuePair> elems = new ArrayList<NameValuePair>();
  730. return new AnnotationGen(new ObjectType(annoname), elems, true, cp);
  731. }
  732. /**
  733. * Construct the annotation that indicates this is a declare
  734. */
  735. /**
  736. * The PointcutAndAdvice object encapsulates an advice kind, a pointcut and names a Java method in a particular class. Generate
  737. * an annotation style advice that has that pointcut whose implementation delegates to the Java method.
  738. */
  739. private void generateAdviceMethod(PointcutAndAdvice paa, int adviceCounter, LazyClassGen cg) {
  740. // Check: Verify the class to be called does exist:
  741. ResolvedType delegateClass = world.resolve(UnresolvedType.forName(paa.adviceClass));
  742. if (delegateClass.isMissing()) {
  743. reportError("Class to invoke cannot be found: '" + paa.adviceClass + "'");
  744. return;
  745. }
  746. // Generate a name for this advice, includes advice kind plus a counter
  747. String adviceName = new StringBuilder("generated$").append(paa.adviceKind.toString().toLowerCase()).append("$advice$")
  748. .append(adviceCounter).toString();
  749. // Build the annotation that encapsulates the pointcut
  750. AnnotationAJ aaj = buildAdviceAnnotation(cg, paa);
  751. // Chop up the supplied advice method string into its pieces.
  752. // Example: foo(JoinPoint jp, java.lang.String string)
  753. // JoinPoint and friends are recognized (so dont need fq package)
  754. String method = paa.adviceMethod;
  755. int paren = method.indexOf("(");
  756. String methodName = method.substring(0, paren);
  757. String signature = method.substring(paren);
  758. // Check: signature looks ok
  759. if (signature.charAt(0) != '(' || !signature.endsWith(")")) {
  760. reportError("Badly formatted parameter signature: '" + method + "'");
  761. return;
  762. }
  763. // Extract parameter types and names
  764. List<Type> paramTypes = new ArrayList<Type>();
  765. List<String> paramNames = new ArrayList<String>();
  766. if (signature.charAt(1) != ')') {
  767. // there are parameters to convert into a signature
  768. StringBuilder convertedSignature = new StringBuilder("(");
  769. boolean paramsBroken = false;
  770. int pos = 1;
  771. while (pos < signature.length() && signature.charAt(pos) != ')' && !paramsBroken) {
  772. int nextChunkEndPos = signature.indexOf(',', pos);
  773. if (nextChunkEndPos == -1) {
  774. nextChunkEndPos = signature.indexOf(')', pos);
  775. }
  776. // chunk will be a type plus a space plus a name
  777. String nextChunk = signature.substring(pos, nextChunkEndPos).trim();
  778. int space = nextChunk.indexOf(" ");
  779. ResolvedType resolvedParamType = null;
  780. if (space == -1) {
  781. // There is no parameter name, hopefully not needed!
  782. if (nextChunk.equals("JoinPoint")) {
  783. nextChunk = "org.aspectj.lang.JoinPoint";
  784. } else if (nextChunk.equals("JoinPoint.StaticPart")) {
  785. nextChunk = "org.aspectj.lang.JoinPoint$StaticPart";
  786. } else if (nextChunk.equals("ProceedingJoinPoint")) {
  787. nextChunk = "org.aspectj.lang.ProceedingJoinPoint";
  788. }
  789. UnresolvedType unresolvedParamType = UnresolvedType.forName(nextChunk);
  790. resolvedParamType = world.resolve(unresolvedParamType);
  791. } else {
  792. String typename = nextChunk.substring(0, space);
  793. if (typename.equals("JoinPoint")) {
  794. typename = "org.aspectj.lang.JoinPoint";
  795. } else if (typename.equals("JoinPoint.StaticPart")) {
  796. typename = "org.aspectj.lang.JoinPoint$StaticPart";
  797. } else if (typename.equals("ProceedingJoinPoint")) {
  798. typename = "org.aspectj.lang.ProceedingJoinPoint";
  799. }
  800. UnresolvedType unresolvedParamType = UnresolvedType.forName(typename);
  801. resolvedParamType = world.resolve(unresolvedParamType);
  802. String paramname = nextChunk.substring(space).trim();
  803. paramNames.add(paramname);
  804. }
  805. if (resolvedParamType.isMissing()) {
  806. reportError("Cannot find type specified as parameter: '" + nextChunk + "' from signature '" + signature + "'");
  807. paramsBroken = true;
  808. }
  809. paramTypes.add(Type.getType(resolvedParamType.getSignature()));
  810. convertedSignature.append(resolvedParamType.getSignature());
  811. pos = nextChunkEndPos + 1;
  812. }
  813. convertedSignature.append(")");
  814. signature = convertedSignature.toString();
  815. if (paramsBroken) {
  816. return;
  817. }
  818. }
  819. Type returnType = Type.VOID;
  820. // If around advice we must find the actual delegate method and use its return type
  821. if (paa.adviceKind == AdviceKind.Around) {
  822. ResolvedMember[] methods = delegateClass.getDeclaredMethods();
  823. ResolvedMember found = null;
  824. for (ResolvedMember candidate : methods) {
  825. if (candidate.getName().equals(methodName)) {
  826. UnresolvedType[] cparms = candidate.getParameterTypes();
  827. if (cparms.length == paramTypes.size()) {
  828. boolean paramsMatch = true;
  829. for (int i = 0; i < cparms.length; i++) {
  830. if (!cparms[i].getSignature().equals(paramTypes.get(i).getSignature())) {
  831. paramsMatch = false;
  832. break;
  833. }
  834. }
  835. if (paramsMatch) {
  836. found = candidate;
  837. break;
  838. }
  839. }
  840. }
  841. }
  842. if (found != null) {
  843. returnType = Type.getType(found.getReturnType().getSignature());
  844. } else {
  845. reportError("Unable to find method to invoke. In class: " + delegateClass.getName() + " cant find "
  846. + paa.adviceMethod);
  847. return;
  848. }
  849. }
  850. // Time to construct the method itself:
  851. LazyMethodGen advice = new LazyMethodGen(Modifier.PUBLIC, returnType, adviceName, paramTypes.toArray(new Type[paramTypes
  852. .size()]), EMPTY_STRINGS, cg);
  853. InstructionList adviceBody = advice.getBody();
  854. // Generate code to load the parameters
  855. int pos = 1; // first slot after 'this'
  856. for (int i = 0; i < paramTypes.size(); i++) {
  857. adviceBody.append(InstructionFactory.createLoad(paramTypes.get(i), pos));
  858. pos += paramTypes.get(i).getSize();
  859. }
  860. // Generate the delegate call
  861. adviceBody.append(cg.getFactory().createInvoke(paa.adviceClass, methodName, signature + returnType.getSignature(),
  862. Constants.INVOKESTATIC));
  863. // Generate the right return
  864. if (returnType == Type.VOID) {
  865. adviceBody.append(InstructionConstants.RETURN);
  866. } else {
  867. if (returnType.getSignature().length() < 2) {
  868. String sig = returnType.getSignature();
  869. if (sig.equals("F")) {
  870. adviceBody.append(InstructionConstants.FRETURN);
  871. } else if (sig.equals("D")) {
  872. adviceBody.append(InstructionConstants.DRETURN);
  873. } else if (sig.equals("J")) {
  874. adviceBody.append(InstructionConstants.LRETURN);
  875. } else {
  876. adviceBody.append(InstructionConstants.IRETURN);
  877. }
  878. } else {
  879. adviceBody.append(InstructionConstants.ARETURN);
  880. }
  881. }
  882. // Add the annotation
  883. advice.addAnnotation(aaj);
  884. InstructionHandle start = adviceBody.getStart();
  885. // Setup the local variable targeters so that the binding will work
  886. String sig = concreteAspect.name.replace('.', '/');
  887. start.addTargeter(new LocalVariableTag("L" + sig + ";", "this", 0, start.getPosition()));
  888. if (paramNames.size() > 0) {
  889. for (int i = 0; i < paramNames.size(); i++) {
  890. start.addTargeter(new LocalVariableTag(paramTypes.get(i).getSignature(), paramNames.get(i), i + 1, start
  891. .getPosition()));
  892. }
  893. }
  894. // Record the new method in the class
  895. cg.addMethodGen(advice);
  896. }
  897. /**
  898. * For the given PointcutAndAdvice build the correct advice annotation.
  899. */
  900. private AnnotationAJ buildAdviceAnnotation(LazyClassGen cg, PointcutAndAdvice paa) {
  901. SimpleElementValue svg = new SimpleElementValue(ElementValue.STRING, cg.getConstantPool(), paa.pointcut);
  902. List<NameValuePair> elems = new ArrayList<NameValuePair>();
  903. elems.add(new NameValuePair("value", svg, cg.getConstantPool()));
  904. AnnotationGen mag = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/" + paa.adviceKind.toString()), elems,
  905. true, cg.getConstantPool());
  906. AnnotationAJ aaj = new BcelAnnotation(mag, world);
  907. return aaj;
  908. }
  909. /**
  910. * Error reporting
  911. *
  912. * @param message
  913. */
  914. private void reportError(String message) {
  915. world.getMessageHandler().handleMessage(new Message(message, IMessage.ERROR, null, null));
  916. }
  917. }