400 likes | 536 Views
Advice Weaving in AspectJ. Alex Gontmakher. Outline. Possible implementation approaches Quick JVM primer AJC implementation Performance Evaluation. Approaches to Aspect code generation. Change the VM to recognize aspects Compile-time weaving of aspects Load-time weaving Reflection.
E N D
Advice Weaving in AspectJ Alex Gontmakher
Outline • Possible implementation approaches • Quick JVM primer • AJC implementation • Performance Evaluation
Approaches to Aspect code generation • Change the VM to recognize aspects • Compile-time weaving of aspects • Load-time weaving • Reflection
Changing the VM • Can provide full support for all Aspect features • Data for aspects separate from the code • Not portable (generated code is not Java bytecode) • Problems with the reference Java implementation • Hard to make changes to Java standard • Hard to evolve AspectJ implementation
Load-time weaving • Modify the code during loading • Probably can be done • Same benefits as with VM-supported aspects • Potentially slow • Trade-Off between load-time and run-time • Expensive to do static analysis (optimization)
Compile time code generation • Portable • Fast • Necessarily limited
Compile-time code generation:The problem before(): get(int Point.x) { System.out.println(“get x”); } • Compiler doesn't see all the code • Code that is dynamically loaded • Code that is called through reflection
Compile-time code generation:Solution • AspectJ solution:aspects apply only to code that the implementation controls • Loosely speaking: all the bytecode available at compile time • Can change between implementations and versions
Compile-time code generation:The limitations Limitations not met Compile time error! • Advice on field access • Accessor code must be available • Advice on method and constructor call • Calling code must be available • Advice on method execution • The method code must be available • Etc.
Frame 2 Frame 1 Class: Class: locals: this … locals: this int i int j Param stack: Hello … Param stack: System.out String … Java Virtual Machine: structure Constant Pool Stack (per thread) ClassHello method: print() method: <init>() ClassMain method: main() method: <init>() field: int fM SP Frame 2 String “hello world” Frame 1 Heap ObjectHello field: int f1 field: String f2 Frame 0
JVM primer: instructions 1 • Arithmetic Instructions • take inputs from param stack, write results back • Getting values to and from local vars • Getting values to and from objects • Method calls and returns • Exception handling • Etc.
JVM Primer: instructions 2 • Arithmetic instructions • iadd: …, value1, value2 …, value1+value2 • Load and store local variables • iload VAR: … …, <local variable[VAR]> • iload_<n>: … …, <local variable[N]> • istore VAR: …, value … {VAR = value} • istore_<n>: …, value … {VAR_n = value} • Stack manipulation • dup: …, value …, value, value
JVM primer: instructions 3 • Object access • Accessing object’s fields: getfield, putfieldgetfield FID: …, objectref …, valueputfield FID: …, value, objectref … • Accessing object’s static fields: getstatic, putstaticgetstatic FID: … …, valueputstatic FID: ..., value …
JVM primer: instructions 4 • Calling methods: • invokevirtual N:…, param1, [param2,…] resultcalls Nth method of class • invokespecial Ncalls constructors etc. • invokestatic N • Invokeinterface N • return, ireturn, … • Creating objects: • new N – allocates and inits memory. • Then, constructor must be called
int i; // An instance variable MyObj example() { MyObj o = new MyObj(); return silly(o); } MyObj silly(MyObj o) { if (o != null) { return o; } else { return o; } } Method MyObj example() 0 new [Class MyObj] 3 dup 4 invokespecial [MyObj.<init>()] 7 astore_1 8 aload_0 9 aload_1 10 invokevirtual [silly] 13 areturn Method MyObj silly(MyObj) 0 aload_1 1 ifnull 6 4 aload_1 5 areturn 6 aload_1 7 areturn JVM Primer: example
JVM primer: exception handling • Exception handling • throw N: throw an exception of class N • Exception table:
Munger Part 3: Aspects implementation Shadow Residue
AspectJ: the process Java source Aspects source Java Library Aspects Library AspectJ compiler Java bytecode Aspects bytecode Weaver mungers shadows Woven program
Advice Implementation:the 4 questions • WHERE - shadows • WHEN - residues • WHAT - the aspect code • HOW - weaving
Join Point Shadows: WHERE • Static code sections that potentially match a join point • Example: “hello world” public static void main(String[] s) { System.out.println(“hello world”); } 0: getstatic [java/lang/System.out] 3: ldc [String “hello world”] 5: invokevirtual [java/io/Printstream.println] 8: return Field-get Target: from stack Args: none Method-execution Target: this Args: local vars Method-call Target: From stack Args: From stack !Parameters must be stored and re-loaded
Join Point Shadows: Notes • Java bytecodes carry plentiful meta-information • Instructions’ intent easily recognizable • Shadow is completely defined by region of code • No need for source code! • It is sometimes impossible to determine statically if aspect should be executed residue
Advice compilation: WHAT • Each advice compiles to a regular Java method • Parameters statically typed used for matching • Advice always runs in the context of aspect instance • Additional information encoded in attributes • Each residue compiles to a regular Java method
Residues: WHEN • Dynamic part of the pointcut • Residue types: • if • Computes conditions on parameters • Parameters passed if necessary • instanceof • Checks type • cflow • Check the stack for cflow conditions • Store cflow status in the stack • Each relevant join point checks the status
Residues example: if residue before(): execution(void main(*)) && if(Tracing.level == 1) { System.out.println(“here”); } 0: invokestatic [A.ajc$if_0] 3: ifeq 12 6: invokestatic [A.aspectof] 9: invokevirtual [A.ajc$before$A$a6] 12: getstatic [java/lang/System.out] 15: ldc [String “hello world”] 17: invokevirtual [java/io/Printstream.println] 20: return Residue Aspect
Residues example: instanceof before(String s): execution(void go(*)) && args(s) { System.out.println(s); } • Case 1: void go(java.lang.String) { } Advice is always called Case 2: void go(java.lang.Object) { } Advice called only if the parameter is a String
Residues: instanceof contd • void go(java/lang/Object); • 0: aload_1 • 1: astore_2 • 2: aload_2 • 3: instanceof [String] • 6: ifeq 14 # skip advice • 9: invokestatic [A.aspectOf] • 10: aload_1 • 13: invokevirtual [A.ajc$before$A$a3] • 16: return Residue Aspect
Residues: cflow • On entry to the method, compute the cflow conditions • Store the result in a local variable • At the join point, check the variable • The test is completely dynamic • Static optimization would need whole-program analysis
The Matching process • For each advice: • Create a shadow munger • For each shadow munger: • For each class: • For each shadow, apply the munger • [optimization] If the munger has withincode attribute, check only in that method
Weaving: HOW • Expose the context • Copy stack parameters • Push local variables (for calls) • Create a JoinPoint object if necessary • Reflective information on the join point: getKind(), getSignature(), getLocation(), getArgs(), … • Done once per shadow • Insert the advice code • Implementation depends on advice kind • Weaving in inverse precedence order
Weaving: advice types • Before advice • Advice code just inserted in the beginning of the shadow • After returning advice • Call:Expose the return valueInsert the code in the end of the shadow • Execution:All the return points must be capturedGenerate gotos into a single return point
Weaving: more advice types • After throwing advice • Add a new exception handler in the enclosing method • After finally advice • Combine After returning and After throwing • Control flow entry advice • Same as before advice • Control flow exit advice • Same as After finally advice
Weaving: more advice types • Around advice • Replaces the original shadow code • If no call to proceed, just inline the advice code • If there is a call to proceed: class Closure$i extends AroundClosure { public void run() { // perform the advised code } } Create the Closure$i object and pass it as a parameter to advice • Declare warning and error • Just print the message, no bytecode changes
Weaving: inlining • Advice code is [almost] never inlined • Why? Because JIT does a better work. • Avoids code duplication
Aspects performance: Benchmark • Xalan – XSLT processor • 826 source files, 7700 methods, 144K lines of code • Measure the slowdown caused by aspects • Compare to hand-coded version public aspect Trace { private static Logger log = Logger.getLogger(“xalan”); pointcut traced(): execution(* *(..)); before(): traced() { Signature s = thisJoinPointStaticPart.getSignature(); log.entering( s.getDeclaringType().getName(), s.getname()); } }
Aspects Performance: first results • Loggingenabled • Loggingdisabled
Aspects performance: optimizing • Avoid class.getName() if not used before() traced() { if (!log.isLoggable(Level.FINER)) return; … } • Check log.isLoggable in the residue pointcut traced(): execution(* *(..)) && if (log.isLoggable(Level.FINER)); • Avoids method call of the aspect • Store the result of log.isLoggable() pointcut traced(): … && if (enabled) && log.isLoggable() • Faster than hand-coded version • Remove the aspect altogether
Aspects performance: results • Best implementation: 76% better than handcoded • Same could be done manually, but impractical
Conclusions • Weaving advices in Java is easy • Rich bytecode • C++ would be much harder – certainly would require source code access! • More static analysis will allow for faster code
References Advice weaving in AspectJ, Eric Hillsdale and Jim Hugunin, In Proceedings of AOSD'04 The AspectJ project homepage,http://eclipse.org/aspectj The JVM Specification book,http://java.sun.com/docs/books/vmspec/