Appendix C. Implementation Limitations

Certain elements of AspectJ's semantics are difficult to implement without making modifications to the virtual machine. 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 source code 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 is compiled at the same the aspect is compiled. It also means that join points associated with code in precompiled libraries (such as java.lang), and join points associated with code in native methods (including their execution join points), can not be advised.

Different join points have different requirements. Method call join points can be advised only if ajc controls either the code for the caller or the code for the receiver, and some call pointcut designators may require caller context (what the static type of the receiver is, for example) to pick out join points. Constructor call join points can be advised only if ajc controls the code for the caller. Field reference or assignment join points can be advised only if ajc controls the code for the "caller", the code actually making the reference or assignment. Initialization join points can be advised only if ajc controls the code of the type being initialized, and execution join points can be advised only if ajc controls the code for the method or constructor body in question.

Aspects that are defined perthis or pertarget also have restrictions based on control of the code. In particular, at a join point where the source code 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, just those whose class the compiler controls. Similar restrictions apply to pertarget aspects.

Inter-type declarations such as declare parents also have restrictions based on control of the code. If the code 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.

Other AspectJ implementations, indeed, future versions of ajc, may define code the implementation controls more liberally.

Control may mean that classes need only be available in classfile or jarfile format. So, even if Client.java and Point.java were precompiled, their join points could still be advised. In such a system, though, it might still be the case that join points from code of system libraries such as java.lang could not be advised.

Or control could even include system libraries, thus allowing a call join point from java.util.Hashmap to java.lang.Object to be advised.

All AspectJ implementations are required to control the code of the files that the compiler compiles itself.

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.