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.

implementation.xml 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. <appendix id="implementation" xreflabel="Implementation Notes">
  2. <title>Implementation Notes</title>
  3. <sect1>
  4. <title>Compiler Notes</title>
  5. <para>
  6. The initial implementations of AspectJ have all been
  7. compiler-based implementations. Certain elements of AspectJ's
  8. semantics are difficult to implement without making modifications
  9. to the virtual machine, which a compiler-based implementation
  10. cannot do. One way to deal with this problem would be to specify
  11. only the behavior that is easiest to implement. We have chosen a
  12. somewhat different approach, which is to specify an ideal language
  13. semantics, as well as a clearly defined way in which
  14. implementations are allowed to deviate from that semantics. This
  15. makes it possible to develop conforming AspectJ implementations
  16. today, while still making it clear what later, and presumably
  17. better, implementations should do tomorrow.
  18. </para>
  19. <para>
  20. According to the AspectJ language semantics, the declaration
  21. </para>
  22. <programlisting><![CDATA[
  23. before(): get(int Point.x) { System.out.println("got x"); }
  24. ]]></programlisting>
  25. <para>
  26. should advise all accesses of a field of type int and name x from
  27. instances of type (or subtype of) Point. It should do this
  28. regardless of whether all the source code performing the access
  29. was available at the time the aspect containing this advice was
  30. compiled, whether changes were made later, etc.
  31. </para>
  32. <para>
  33. But AspectJ implementations are permitted to deviate from this in
  34. a well-defined way -- they are permitted to advise only accesses
  35. in <emphasis>code the implementation controls</emphasis>. Each
  36. implementation is free within certain bounds to provide its own
  37. definition of what it means to control code.
  38. </para>
  39. <para>
  40. In the current AspectJ compiler, ajc, control of the code means
  41. having bytecode for any aspects and all the code they should
  42. affect available during the compile. This means that if some class
  43. Client contains code with the expression <literal>new
  44. Point().x</literal> (which results in a field get join point at
  45. runtime), the current AspectJ compiler will fail to advise that
  46. access unless Client.java or Client.class is compiled as well. It
  47. also means that join points associated with code in native methods
  48. (including their execution join points) cannot be advised.
  49. </para>
  50. <para>
  51. Different join points have different requirements. Method and
  52. constructor call join points can be advised only if ajc controls
  53. the bytecode for the caller. Field reference or assignment join
  54. points can be advised only if ajc controls the bytecode for the
  55. "caller", the code actually making the reference or assignment.
  56. Initialization join points can be advised only if ajc controls the
  57. bytecode of the type being initialized, and execution join points
  58. can be advised only if ajc controls the bytecode for the method or
  59. constructor body in question.
  60. The end of an exception handler is underdetermined in bytecode,
  61. so ajc will not implement after or around advice on handler join
  62. points.
  63. Similarly, ajc cannot implement around advice on initialization
  64. or preinitialization join points.
  65. In cases where ajc cannot implement advice, it will emit a
  66. compile-time error noting this as a compiler limitation.
  67. </para>
  68. <para>
  69. Aspects that are defined <literal>perthis</literal> or
  70. <literal>pertarget</literal> also have restrictions based on
  71. control of the code. In particular, at a join point where the
  72. bytecode for the currently executing object is not available, an
  73. aspect defined <literal>perthis</literal> of that join point will
  74. not be associated. So aspects defined
  75. <literal>perthis(Object)</literal> will not create aspect
  76. instances for every object unless <literal>Object</literal>is part
  77. of the compile. Similar restrictions apply to
  78. <literal>pertarget</literal> aspects.
  79. </para>
  80. <para>
  81. Inter-type declarations such as <literal>declare parents</literal>
  82. also have restrictions based on control of the code. If the
  83. bytecode for the target of an inter-type declaration is not
  84. available, then the inter-type declaration is not made on that
  85. target. So, <literal>declare parents : String implements
  86. MyInterface</literal> will not work for
  87. <literal>java.lang.String</literal> unless
  88. <literal>java.lang.String</literal> is part of the compile.
  89. </para>
  90. <para>
  91. When declaring members on interfaces, the implementation must
  92. control both the interface and the top-level implementors of
  93. that interface (the classes that implement the interface but
  94. do not have a superclass that implements the interface).
  95. You may weave these separately, but be aware that you will get
  96. runtime exceptions if you run the affected top-level classes
  97. without the interface as produced by the same ajc implementation.
  98. Any intertype declaration of an abstract method on an interface
  99. must be specified as public, you will get a compile time error
  100. message indicating this is a compiler limitation if you do not
  101. specify public. A non-abstract method declared on an interface
  102. can use any access modifier except protected. Note that this is
  103. different to normal Java rules where all members declared in
  104. an interface are implicitly public.
  105. Finally, note that one cannot define static fields or methods
  106. on interfaces.
  107. </para>
  108. <para>
  109. Other AspectJ implementations, indeed, future versions of ajc, may
  110. define <emphasis>code the implementation controls</emphasis> more
  111. liberally or restrictively.
  112. </para>
  113. <para>
  114. The important thing to remember is that core concepts of AspectJ,
  115. such as the join point, are unchanged, regardless of which
  116. implementation is used. During your development, you will have to
  117. be aware of the limitations of the ajc compiler you're using, but
  118. these limitations should not drive the design of your aspects.
  119. </para>
  120. </sect1>
  121. <sect1>
  122. <title>Bytecode Notes</title>
  123. <sect2>
  124. <title>The .class expression and String +</title>
  125. <para> The java language form <literal>Foo.class</literal> is
  126. implemented in bytecode with a call to
  127. <literal>Class.forName</literal> guarded by an exception
  128. handler catching a <literal>ClassNotFoundException</literal>.
  129. </para>
  130. <para> The java language + operator, when applied to String
  131. arguments, is implemented in bytecode by calls to
  132. <literal>StringBuffer.append</literal>.
  133. </para>
  134. <para> In both of these cases, the current AspectJ compiler
  135. operates on the bytecode implementation of these language
  136. features; in short, it operates on what is really happening rather
  137. than what was written in source code. This means that there may
  138. be call join points to <literal>Class.forName</literal> or
  139. <literal>StringBuffer.append</literal> from programs that do not,
  140. at first glance, appear to contain such calls:
  141. </para>
  142. <programlisting><![CDATA[
  143. class Test {
  144. void main(String[] args) {
  145. System.out.println(Test.class); // calls Class.forName
  146. System.out.println(args[0] + args[1]); // calls StringBuffer.append
  147. }
  148. }
  149. ]]></programlisting>
  150. <para>In short, the join point model of the current AspectJ
  151. compiler considers these as valid join points.
  152. </para>
  153. </sect2>
  154. <sect2>
  155. <title>The Handler join point</title>
  156. <para>The end of exception handlers cannot reliably be found in Java
  157. bytecode. Instead of removing the handler join point entirely, the
  158. current AspectJ compiler restricts what can be done with the handler
  159. join point:
  160. </para>
  161. <itemizedlist>
  162. <listitem>After and around advice cannot apply to handler
  163. join points.</listitem>
  164. <listitem>The control flow of a handler join point cannot be
  165. detected. </listitem>
  166. </itemizedlist>
  167. <para>
  168. The first of these is relatively straightforward. If any piece of
  169. after advice (returning, throwing, or "finally") would normally
  170. apply to a handler join point, it will not in code output by the
  171. current AspectJ compiler. A compiler warning is generated whenever
  172. this is detected to be the case. Before advice is allowed.
  173. </para>
  174. <para> The second is that the control flow of a handler join point
  175. is not picked out. For example, the following pointcut
  176. </para>
  177. <programlisting><![CDATA[
  178. cflow(call(void foo()) || handler(java.io.IOException))
  179. ]]></programlisting>
  180. <para> will capture all join points in the control flow of a call to
  181. <literal>void foo()</literal>, but it will <emphasis>not</emphasis>
  182. capture those in the control flow of an
  183. <literal>IOException</literal> handler. It is equivalent to
  184. <literal>cflow(call(void foo()))</literal>. In general,
  185. <literal>cflow(handler(<replaceable>Type</replaceable>))</literal>
  186. will not pick out any join points, the one exception to this is join points
  187. that occur during the execution of any before advice on the handler.
  188. </para>
  189. <para> This does not restrict programs from placing before advice on
  190. handlers inside <emphasis>other</emphasis> control flows. This
  191. advice, for example, is perfectly fine:
  192. </para>
  193. <programlisting><![CDATA[
  194. before(): handler(java.io.IOException) && cflow(void parse()) {
  195. System.out.println("about to handle an exception while parsing");
  196. }
  197. ]]></programlisting>
  198. <para>
  199. A source-code implementation of AspectJ (such as AspectJ 1.0.6) is
  200. able to detect the endpoint of a handler join point, and as such
  201. will likely have fewer such restrictions.
  202. </para>
  203. </sect2>
  204. <sect2>
  205. <title>Initializers and Inter-type Constructors</title>
  206. <para>
  207. The code for Java initializers, such as the assignment to the
  208. field d in
  209. </para>
  210. <programlisting><![CDATA[
  211. class C {
  212. double d = Math.sqrt(2);
  213. }
  214. ]]></programlisting>
  215. <para>
  216. are considered part of constructors by the time AspectJ gets ahold
  217. of bytecode. That is, the assignment of d to the square root of
  218. two happens <emphasis>inside</emphasis> the default constructor of
  219. C.
  220. </para>
  221. <para>
  222. Thus inter-type constructors will not necessarily run a target
  223. type's initialization code. In particular, if the inter-type
  224. constructor calls a super-constructor (as opposed to a
  225. <literal>this</literal> constructor), the target type's
  226. initialization code will <emphasis>not</emphasis> be run when that
  227. inter-type constructor is called.
  228. </para>
  229. <programlisting><![CDATA[
  230. aspect A {
  231. C.new(Object o) {} // implicitly calls super()
  232. public static void main(String[] args) {
  233. System.out.println((new C() ).d); // prints 1.414...
  234. System.out.println((new C(null)).d); // prints 0.0
  235. }
  236. ]]></programlisting>
  237. <para>
  238. It is the job of an inter-type constructor to do all the required
  239. initialization, or to delegate to a <literal>this</literal>
  240. constructor if necessary.
  241. </para>
  242. </sect2>
  243. </sect1>
  244. </appendix>