|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377 |
- <appendix id="implementation" xreflabel="Implementation Notes">
-
- <title>Implementation Notes</title>
-
- <sect1>
- <title>Compiler Notes</title>
-
- <para>
- The initial implementations of AspectJ have all been
- compiler-based implementations. Certain elements of AspectJ's
- semantics are difficult to implement without making modifications
- to the virtual machine, which a compiler-based implementation
- cannot do. One way to deal with this problem would be to specify
- only the behavior that is easiest to implement. We have chosen a
- somewhat different approach, which is to specify an ideal language
- semantics, as well as a clearly defined way in which
- implementations are allowed to deviate from that semantics. This
- makes it possible to develop conforming AspectJ implementations
- today, while still making it clear what later, and presumably
- better, implementations should do tomorrow.
- </para>
-
- <para>
- According to the AspectJ language semantics, the declaration
- </para>
-
- <programlisting><![CDATA[
- before(): get(int Point.x) { System.out.println("got x"); }
- ]]></programlisting>
-
- <para>
- should advise all accesses of a field of type int and name x from
- instances of type (or subtype of) Point. It should do this
- regardless of whether all the source code performing the access
- was available at the time the aspect containing this advice was
- compiled, whether changes were made later, etc.
- </para>
-
- <para>
- But AspectJ implementations are permitted to deviate from this in
- a well-defined way -- they are permitted to advise only accesses
- in <emphasis>code the implementation controls</emphasis>. Each
- implementation is free within certain bounds to provide its own
- definition of what it means to control code.
- </para>
-
- <para>
- In the current AspectJ compiler, ajc, control of the code means
- having bytecode for any aspects and all the code they should
- affect available during the compile. This means that if some class
- Client contains code with the expression <literal>new
- Point().x</literal> (which results in a field get join point at
- runtime), the current AspectJ compiler will fail to advise that
- access unless Client.java or Client.class is compiled as well. It
- also means that join points associated with code in native methods
- (including their execution join points) cannot be advised.
- </para>
-
- <para>
- Different join points have different requirements. Method and
- constructor call join points can be advised only if ajc controls
- the bytecode for the caller. Field reference or assignment join
- points can be advised only if ajc controls the bytecode for the
- "caller", the code actually making the reference or assignment.
- Initialization join points can be advised only if ajc controls the
- bytecode of the type being initialized, and execution join points
- can be advised only if ajc controls the bytecode for the method or
- constructor body in question.
- The end of an exception handler is underdetermined in bytecode,
- so ajc will not implement after or around advice on handler join
- points.
- Similarly, ajc cannot implement around advice on initialization
- or preinitialization join points.
- In cases where ajc cannot implement advice, it will emit a
- compile-time error noting this as a compiler limitation.
- </para>
-
- <para>
- Aspects that are defined <literal>perthis</literal> or
- <literal>pertarget</literal> also have restrictions based on
- control of the code. In particular, at a join point where the
- bytecode for the currently executing object is not available, an
- aspect defined <literal>perthis</literal> of that join point will
- not be associated. So aspects defined
- <literal>perthis(Object)</literal> will not create aspect
- instances for every object unless <literal>Object</literal>is part
- of the compile. Similar restrictions apply to
- <literal>pertarget</literal> aspects.
- </para>
-
- <para>
- Inter-type declarations such as <literal>declare parents</literal>
- also have restrictions based on control of the code. If the
- bytecode for the target of an inter-type declaration is not
- available, then the inter-type declaration is not made on that
- target. So, <literal>declare parents : String implements
- MyInterface</literal> will not work for
- <literal>java.lang.String</literal> unless
- <literal>java.lang.String</literal> is part of the compile.
- </para>
- <para>
- When declaring members on interfaces, the implementation must
- control both the interface and the top-level implementors of
- that interface (the classes that implement the interface but
- do not have a superclass that implements the interface).
- You may weave these separately, but be aware that you will get
- runtime exceptions if you run the affected top-level classes
- without the interface as produced by the same ajc implementation.
- Any intertype declaration of an abstract method on an interface
- must be specified as public, you will get a compile time error
- message indicating this is a compiler limitation if you do not
- specify public. A non-abstract method declared on an interface
- can use any access modifier except protected. Note that this is
- different to normal Java rules where all members declared in
- an interface are implicitly public.
- Finally, note that one cannot define static fields or methods
- on interfaces.
- </para>
- <para>
- When declaring methods on target types, only methods declared
- public are recognizable in the bytecode, so methods must be
- declared public to be overridden in any subtype or to be called
- from code in a later compile using the target type as a library.
- </para>
-
- <para>
- Other AspectJ implementations, indeed, future versions of ajc, may
- define <emphasis>code the implementation controls</emphasis> more
- liberally or restrictively, so long as they comport with the Java
- language. For example, the <literal>call</literal> pointcut does
- not pick out reflective calls to a method implemented in
- <literal>java.lang.reflect.Method.invoke(Object, Object[])</literal>.
- Some suggest that the call "happens" and the call pointcut should
- pick it out, but the AspectJ language shouldn't anticipate what happens
- in code outside the control of the implementation, even when it
- is a a well-defined API in a Java standard library.
- </para>
-
- <para>
- The important thing to remember is that core concepts of AspectJ,
- such as the join point, are unchanged, regardless of which
- implementation is used. During your development, you will have to
- be aware of the limitations of the ajc compiler you're using, but
- these limitations should not drive the design of your aspects.
- </para>
- </sect1>
-
- <sect1>
- <title>Bytecode Notes</title>
-
- <sect2 id="the-class-expression-and-string" xreflabel="the-class-expression-and-string">
- <title>The .class expression and String +</title>
-
- <para> The java language form <literal>Foo.class</literal> is
- implemented in bytecode with a call to
- <literal>Class.forName</literal> guarded by an exception
- handler catching a <literal>ClassNotFoundException</literal>.
- </para>
-
- <para> The java language + operator, when applied to String
- arguments, is implemented in bytecode by calls to
- <literal>StringBuffer.append</literal>.
- </para>
-
- <para> In both of these cases, the current AspectJ compiler
- operates on the bytecode implementation of these language
- features; in short, it operates on what is really happening rather
- than what was written in source code. This means that there may
- be call join points to <literal>Class.forName</literal> or
- <literal>StringBuffer.append</literal> from programs that do not,
- at first glance, appear to contain such calls:
- </para>
-
- <programlisting><![CDATA[
- class Test {
- void main(String[] args) {
- System.out.println(Test.class); // calls Class.forName
- System.out.println(args[0] + args[1]); // calls StringBuffer.append
- }
- }
- ]]></programlisting>
-
- <para>In short, the join point model of the current AspectJ
- compiler considers these as valid join points.
- </para>
-
- </sect2>
-
- <sect2 id="the-handler-join-point" xreflabel="the-handler-join-point">
- <title>The Handler join point</title>
-
-
- <para>The end of exception handlers cannot reliably be found in Java
- bytecode. Instead of removing the handler join point entirely, the
- current AspectJ compiler restricts what can be done with the handler
- join point:
- </para>
-
- <itemizedlist>
- <listitem>After and around advice cannot apply to handler
- join points.</listitem>
-
- <listitem>The control flow of a handler join point cannot be
- detected. </listitem>
- </itemizedlist>
-
- <para>
- The first of these is relatively straightforward. If any piece of
- after advice (returning, throwing, or "finally") would normally
- apply to a handler join point, it will not in code output by the
- current AspectJ compiler. A compiler warning is generated whenever
- this is detected to be the case. Before advice is allowed.
- </para>
-
- <para> The second is that the control flow of a handler join point
- is not picked out. For example, the following pointcut
- </para>
-
- <programlisting><![CDATA[
- cflow(call(void foo()) || handler(java.io.IOException))
- ]]></programlisting>
-
- <para> will capture all join points in the control flow of a call to
- <literal>void foo()</literal>, but it will <emphasis>not</emphasis>
- capture those in the control flow of an
- <literal>IOException</literal> handler. It is equivalent to
- <literal>cflow(call(void foo()))</literal>. In general,
- <literal>cflow(handler(<replaceable>Type</replaceable>))</literal>
- will not pick out any join points, the one exception to this is join points
- that occur during the execution of any before advice on the handler.
- </para>
-
- <para> This does not restrict programs from placing before advice on
- handlers inside <emphasis>other</emphasis> control flows. This
- advice, for example, is perfectly fine:
- </para>
-
- <programlisting><![CDATA[
- before(): handler(java.io.IOException) && cflow(void parse()) {
- System.out.println("about to handle an exception while parsing");
- }
- ]]></programlisting>
-
- <para>
- A source-code implementation of AspectJ (such as AspectJ 1.0.6) is
- able to detect the endpoint of a handler join point, and as such
- will likely have fewer such restrictions.
- </para>
-
- </sect2>
-
- <sect2 id="initializers-and-inter-type-constructors" xreflabel="initializers-and-inter-type-constructors">
- <title>Initializers and Inter-type Constructors</title>
-
- <para>
- The code for Java initializers, such as the assignment to the
- field d in
- </para>
-
- <programlisting><![CDATA[
- class C {
- double d = Math.sqrt(2);
- }
- ]]></programlisting>
-
- <para>
- are considered part of constructors by the time AspectJ gets ahold
- of bytecode. That is, the assignment of d to the square root of
- two happens <emphasis>inside</emphasis> the default constructor of
- C.
- </para>
-
- <para>
- Thus inter-type constructors will not necessarily run a target
- type's initialization code. In particular, if the inter-type
- constructor calls a super-constructor (as opposed to a
- <literal>this</literal> constructor), the target type's
- initialization code will <emphasis>not</emphasis> be run when that
- inter-type constructor is called.
- </para>
-
- <programlisting><![CDATA[
- aspect A {
- C.new(Object o) {} // implicitly calls super()
-
- public static void main(String[] args) {
- System.out.println((new C() ).d); // prints 1.414...
- System.out.println((new C(null)).d); // prints 0.0
- }
- ]]></programlisting>
-
- <para>
- It is the job of an inter-type constructor to do all the required
- initialization, or to delegate to a <literal>this</literal>
- constructor if necessary.
- </para>
- </sect2>
- </sect1>
-
- <sect1>
- <title>Annotation-style Notes</title>
- <para>Writing aspects in annotation-style is subject to the same
- bytecode limitations since the binary aspects take the same
- form and are woven in the same way. However, the implementation
- differences (e.g., the mechanism for implementing around advice)
- may be apparent at runtime. See the documentation on annotation-style
- for more information.
- </para>
- </sect1>
- <sect1>
- <title>Summary of implementation requirements</title>
- <para>
- This summarizes the requirements of our implementation of AspectJ.
- For more details, see the relevant sections of this guide.
- </para>
- <itemizedlist spacing="compact">
- <listitem>
- <para>The invoking code must be under the control of ajc
- for the following join points:</para>
- <itemizedlist spacing="compact">
- <listitem>call join point</listitem>
- <listitem>get join point</listitem>
- <listitem>set join point</listitem>
- </itemizedlist>
- </listitem>
- <listitem>
- <para>The declaring/target code must be under the control of ajc
- for the following join points and inter-type declarations:</para>
- <itemizedlist spacing="compact">
- <listitem>execution join point</listitem>
- <listitem>adviceexecution join point</listitem>
- <listitem>handler join point</listitem>
- <listitem>initialization join point</listitem>
- <listitem>preinitialiaztion join point</listitem>
- <listitem>staticinitialization join point</listitem>
- <listitem>perthis aspect</listitem>
- <listitem>pertarget aspect</listitem>
- <listitem>declare parents</listitem>
- <listitem>declare method or field (see interface caveats below)</listitem>
- </itemizedlist>
- </listitem>
- <listitem>
- <para>Implementation Caveats</para>
- <itemizedlist spacing="compact">
- <listitem>
- <para>The initialization and preinitialization join points
- do not support around advice</para>
- </listitem>
- <listitem>
- <para>The handler join point does not support...</para>
- <itemizedlist spacing="compact">
- <listitem>after advice</listitem>
- <listitem>around advice</listitem>
- <listitem>cflow(handler(..))</listitem>
- </itemizedlist>
- </listitem>
- <listitem>
- <para>Declaring members on an interface in an aspect affects only
- the topmost implementing classes the implementation controls.</para>
- </listitem>
- <listitem>
- <para>cflow and cflowbelow pointcuts work within a single thread.</para>
- </listitem>
- <listitem>
- <para>
- Runtime <literal>ClassCastException</literal> may result
- from supplying a supertype of the actual type as an argument
- to proceed(..) in around advice.</para>
- </listitem>
- </itemizedlist>
- </listitem>
- </itemizedlist>
- </sect1>
- </appendix>
-
-
-
|