Advice weaving in AspectJ

Erik Hilsdale, Jim Hugunin. Advice weaving in AspectJ. In Gail C. Murphy, Karl J. Lieberherr, editors, Proceedings of the 3rd International Conference on Aspect-Oriented Software Development, AOSD 2004, Lancaster, UK, March 22-24, 2004. pages 26-35, ACM, 2004. [doi]

Review: Review

Posted by Nathan Bruning on 12/11/2009 16:05, last updated on 12/11/2009 16:06 (Public)

The paper describes the inner workings of the weaving-process in the AspectJ compiler. Basically, jointpoints are matched to pointcuts. Java bytecode instructions are then added to execute the advice at that place. Arguments are given for not inlining the aspect code, followed by a detailed performance evaluation of both the compile-time and the run-time impact. The performance analysis is concluded with the remark that 22% of run-time performance penalty is not a problem for modern applications, which is surely debatable. Also, the paper fails to explain the separate compilation features of AspectJ which remain concealed and doubtful.

Introduction

AspectJ [3, 2] is an extension which provides aspect-oriented features for the programming language Java. The aspect-oriented features are implemented as new source-level constructs. During compilation with the AspectJ compiler, these source-level constructs are translated into normal Java bytecode. Compared to the normal Java compilation pipeline, type checking is modified to support the new cross-cutting features. Also, the back-end of the compiler has to ``weave'' the aspect code in the target modules whenever a pointcut matches. This work describes the internals of the AspectJ compilation process, in particular its task of aspect weaving.

Summary

The AspectJ compiler is based on the incremental Java compiler shipped with Eclipse [1]. To begin with, the parser in the front-end has to recognize and parse the new constructs of AspectJ. These include aspects, pointcuts, advice, inter-type declarations, parent declarations and non-singleton aspect instances. However, the syntax of these constructs is very much like existing Java constructs like classes, interfaces and methods.

Type checking an AspectJ source file is much harder than type-checking a normal Java module. Inter-type declarations extend a class with new declarations from a different module. Aspect can add an attribute to class , without having an explicit reference to . The link between the two modules is only made explicit the other way around: the advice knows for which classes is adds declarations. Parent declarations (declare parent) modify the superclass of a class, and the same one-way referencing scheme applies. The compiler requires considerable effort to port the name binding and static type checking behavior to AspectJ constructs.

Compiling advice is comparatively simple; an advice is translated to a method. Parameters to an advice body are statically checked. Furthermore, context information is made available, mainly reflective information about the particular joinpoint that resulted in advice execution. The proceed construct is able to redirect control flow, implemented by passing a closure to the advice.

The main guts of the compiler are however in the back-end, where the AST is transformed to byte-code. First, the compiler identifies all points in the bytecode that represent possible joinpoints; such points are a joinpoint’s static shadow''. Different types of static shadow are identifier; for example, a method execution is shadowed by all bytecode representing that method, a method call is shadowed by all invoke bytecode instructions to that method, and so on. Next, the compiler must match each pointcut (a collection of joinpoints on which an advice must be applied) to the static shadow. Whenever such a match is found, additional bytecode instructions must be weaved in to execute the advice body. The actual transformation is done by ashadow munger'', which is different per advice type (control flow entry, control flow exit, on error).

Besides statically checked pointcut matchers (class name, method name, return type), some conditions can only be evaluated at runtime. Examples include checks on mutable program attributes (global variables or class attributes) and context sensitive checks. For such pointcuts, the compiler surrounds the advice method call with a dynamic check, which is called the residue: the partial matching that could not be evaluated at compile-time.

The implementation of shadow mungers follows naturally from the semantics of the advice type. A before-advice is inserted before a method call, and an after-advice is placed at then end of a method. However, in the second case, care has to be taken to include all exit points; exceptions can obfuscate control flow. For advice that is executed before a method call, the context of the method call (caller, callee, parameters) has to be constructed; in practice, this means copying the values that were already pushed on the stack to the local frame.

Evaluation

The increasing interest for AspectJ can be seen by its growing community, its integration in the Eclipse project and the 2300 citations of a paper reviewing AspectJ [3]. Still, performance is an ever returning subject when evaluating aspect-oriented languages. This paper reveals the internals of the AspectJ compiler and evaluates several design decisions.

One particular design decision of the AspectJ compiler is not to inline any aspect, even though they state that aspect-instance lookup and method calls are the main causes of run-time performance overhead. There are two arguments to withdraw from inlining. First, it would require changing the visibility of aspect attributes; they must be accessible from (inlined) aspect code in any module. The second argument states that the Java JIT compiler is much better optimized and tailored for choosing which methods to inline; it would be foolish to override its default inline strategy.

A third argument, which is surprisingly not mentioned in the paper, is that of separation of concerns in the target code. When inlining is used, not only a call for aspect execution is inserted at all matching joinpoints, but the complete aspect body. The aspect’s concern is now separated in the source files, but mangled in the target code. This effectively disables separate compilation for aspects and normal modules and is problematic in the presence of unit tests [4].

The evaluation section of the paper concerns two performance measures: compile-time performance and run-time performance. For compiling source files, a comparison is made between the standard javac compiler and the AspectJ compiler, varying the number of applied aspects. Basically, the observation is that weaving takes minimal time. Nevertheless, when a pointcut does not explicitly refer to a class, each joinpoint shadow must be matched against every pointcut; the total compilation time is then about tripled. The subject of incremental compilation is not brought up. For large systems however, the difference in compilation time between incremental compilation time (in the order of seconds for smart compilers) and full compilation (measured in hours for huge systems) by far outweighs the increased per-module compilation time. The version 1.1 implemenation of AspectJ, which the paper discusses, supports some incremental compilation features but these are not discussed because it said to be beyond the scope of the article. No work has yet revealed AspectJ’s particular implementation of incremental compilation, its accompanying design decisions or a performance evaluation.

Run-time performance is tested by adding log-statements to each method of a large library. Results indicate that, using different level of optimization, overhead can be anywhere between 2500 to 22, compared to a hand-coded solution. The lower-bound of the performance penalty, 22, is states as being ``unnoticable for most applications''. While this may be true for some applications, still 22 is quite a drawback for resource-heavy applications to be developed with AspectJ. On the other hand, changing logging policies or logging implementations, or even completely (statically) disabling logging is easy with AspectJ, but requires changes to lots of code lines in the absence of aspects. There is clearly a trade-off between the developer’s flexibility and the end-users performance. Future work has to conclude whether the 22 overhead of using aspects is really an unavoidable lower bound.

References

[1] JDT Eclipse. Eclipse Java Development Tools (JDT) Subproject, 2007. [2] G. Kiczales, E. Hilsdale, J. Hugunin, M. Kersten, J. Palm, and W. Griswold. Getting started with AspectJ. 2001. [3] G. Kiczales, E. Hilsdale, J. Hugunin, M. Kersten, J. Palm, and W.G. Griswold. An overview of AspectJ. Lecture Notes in Computer Science, pages 327-353, 2001. [4] H. Rajan, R. Dyer, Y. Hanna, and H. Narayanappa. Preserving separation of concerns through compilation. In Software Engineering Properties of Languages and Aspect Technologies (SPLAT 06), A workshop affliated with AOSD, 2006.