diff options
author | Alexander Kriegisch <Alexander@Kriegisch.name> | 2021-06-29 13:18:25 +0700 |
---|---|---|
committer | Alexander Kriegisch <Alexander@Kriegisch.name> | 2024-01-06 10:09:11 +0100 |
commit | ebbc41255384e85db03c4eb6aae4e6464803d0a9 (patch) | |
tree | 4d90f6d206ec43bef21eb601161785c4502d358e /docs/progGuideDB/implementation.adoc | |
parent | 0ba9f25b0e5deb638f6e7472141f4edc4450c99b (diff) | |
download | aspectj-ebbc41255384e85db03c4eb6aae4e6464803d0a9.tar.gz aspectj-ebbc41255384e85db03c4eb6aae4e6464803d0a9.zip |
Add initial set of AsciiDoc files, converted from HTML/XML
Some originals have been deleted already. Others, especially the user
guides, still exist in both formats because they have not been
proof-read and probably lots of links do not function as expected. But I
want to see what the files look like directly on GitHun.
Signed-off-by: Alexander Kriegisch <Alexander@Kriegisch.name>
Diffstat (limited to 'docs/progGuideDB/implementation.adoc')
-rw-r--r-- | docs/progGuideDB/implementation.adoc | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/docs/progGuideDB/implementation.adoc b/docs/progGuideDB/implementation.adoc new file mode 100644 index 000000000..a34324d5f --- /dev/null +++ b/docs/progGuideDB/implementation.adoc @@ -0,0 +1,260 @@ +[[implementation]] +== Implementation Notes + +=== Compiler Notes + +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. + +According to the AspectJ language semantics, the declaration + +.... +before(): get(int Point.x) { System.out.println("got x"); } +.... + +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. + +But AspectJ implementations are permitted to deviate from this in a +well-defined way -- they are permitted to advise only accesses in _code +the implementation controls_. Each implementation is free within certain +bounds to provide its own definition of what it means to control code. + +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 `new + Point().x` (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. + +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. + +Aspects that are defined `perthis` or `pertarget` 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 `perthis` of that join point will not be associated. So aspects +defined `perthis(Object)` will not create aspect instances for every +object unless `Object`is part of the compile. Similar restrictions apply +to `pertarget` aspects. + +Inter-type declarations such as `declare parents` 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, `declare parents : String implements + MyInterface` will not work for `java.lang.String` unless +`java.lang.String` is part of the compile. + +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. + +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. + +Other AspectJ implementations, indeed, future versions of ajc, may +define _code the implementation controls_ more liberally or +restrictively, so long as they comport with the Java language. For +example, the `call` pointcut does not pick out reflective calls to a +method implemented in +`java.lang.reflect.Method.invoke(Object, Object[])`. 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. + +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. + +=== Bytecode Notes + +[[the-class-expression-and-string]] +==== The .class expression and String + + +The java language form `Foo.class` is implemented in bytecode with a +call to `Class.forName` guarded by an exception handler catching a +`ClassNotFoundException`. + +The java language + operator, when applied to String arguments, is +implemented in bytecode by calls to `StringBuffer.append`. + +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 +`Class.forName` or `StringBuffer.append` from programs that do not, at +first glance, appear to contain such calls: + +.... +class Test { + void main(String[] args) { + System.out.println(Test.class); // calls Class.forName + System.out.println(args[0] + args[1]); // calls StringBuffer.append + } +} +.... + +In short, the join point model of the current AspectJ compiler considers +these as valid join points. + +==== The Handler join point + +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: + +* After and around advice cannot apply to handler join points. +* The control flow of a handler join point cannot be detected. + +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. + +The second is that the control flow of a handler join point is not +picked out. For example, the following pointcut + +.... +cflow(call(void foo()) || handler(java.io.IOException)) +.... + +will capture all join points in the control flow of a call to +`void foo()`, but it will _not_ capture those in the control flow of an +`IOException` handler. It is equivalent to `cflow(call(void foo()))`. In +general, `cflow(handler(Type))` 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. + +This does not restrict programs from placing before advice on handlers +inside _other_ control flows. This advice, for example, is perfectly +fine: + +.... +before(): handler(java.io.IOException) && cflow(void parse()) { + System.out.println("about to handle an exception while parsing"); +} +.... + +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. + +==== Initializers and Inter-type Constructors + +The code for Java initializers, such as the assignment to the field d in + +.... +class C { + double d = Math.sqrt(2); +} +.... + +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 +_inside_ the default constructor of C. + +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 `this` constructor), the target +type's initialization code will _not_ be run when that inter-type +constructor is called. + +.... +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 +} +.... + +It is the job of an inter-type constructor to do all the required +initialization, or to delegate to a `this` constructor if necessary. + +=== Annotation-style Notes + +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. + +=== Summary of implementation requirements + +This summarizes the requirements of our implementation of AspectJ. For +more details, see the relevant sections of this guide. + +* The invoking code must be under the control of ajc for the following +join points: +** call join point +** get join point +** set join point +* The declaring/target code must be under the control of ajc for the +following join points and inter-type declarations: +** execution join point +** adviceexecution join point +** handler join point +** initialization join point +** preinitialiaztion join point +** staticinitialization join point +** perthis aspect +** pertarget aspect +** declare parents +** declare method or field (see interface caveats below) +* Implementation Caveats +** The initialization and preinitialization join points do not support +around advice +** The handler join point does not support... +*** after advice +*** around advice +*** cflow(handler(..)) +** Declaring members on an interface in an aspect affects only the +topmost implementing classes the implementation controls. +** cflow and cflowbelow pointcuts work within a single thread. +** Runtime `ClassCastException` may result from supplying a supertype of +the actual type as an argument to proceed(..) in around advice. |