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

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