Advice Weaving in AspectJ
description
Transcript of Advice Weaving in AspectJ
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 weavingReflection
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
implementationHard to make changes to Java standardHard 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-timeExpensive 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 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.
Limitations not met Compile time error!
Part 2: JVM Primer
Java Virtual Machine: structure
Frame 0
Frame 1
Frame 2
SP
Stack (per thread)Constant Pool
Class Hellomethod: print()method: <init>()
Class Mainmethod: main()method: <init>() field: int fM
String “hello world”
Frame 2
locals:thisint iint j
Param stack:System.outString…
Class:
Frame 1
locals:this
…
Param stack:Hello
…
Class:
HeapObject Hellofield: int f1field: String f2
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 accessAccessing object’s fields: getfield, putfield
getfield 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
JVM Primer: exampleint i; // An instance variableMyObj 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: exception handling
Exception handling throw N: throw an exception of class NException table:
FROM TO Class Handler
1 3 17 8
Part 3: Aspects implementation
Munger
Shadow
Residue
AspectJ: the processJava source Aspects source
Java bytecode Aspects bytecode
JavaLibrary
AspectsLibraryAspectJ compiler
Weaver
Woven program
shadows mungers
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-getTarget: from stackArgs: none
Method-callTarget: From stackArgs: From stack!Parameters must be stored and re-loaded
Method-executionTarget: thisArgs: local vars
Join Point Shadows: Notes Java bytecodes carry plentiful meta-
information Instructions’ intent easily recognizableShadow is completely defined by region of
codeNo 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
methodParameters statically typed
used for matchingAdvice always runs in the context of aspect
instanceAdditional information encoded in attributes
Each residue compiles to a regular Java method
Residues: WHENDynamic part of the pointcutResidue 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 residuebefore(): execution(void main(*)) && if(Tracing.level == 1) { System.out.println(“here”);}
0: invokestatic [A.ajc$if_0]3: ifeq 126: 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: instanceofbefore(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
conditionsStore the result in a local variableAt the join point, check the variable
The test is completely dynamicStatic optimization would need whole-program
analysis
The Matching process1. For each advice:
Create a shadow munger
2. 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 parametersPush 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 kindWeaving in inverse precedence order
Weaving: advice types Before advice
Advice code just inserted in the beginning of the shadow
After returning adviceCall:
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 adviceCombine After returning and After throwing
Control flow entry adviceSame as before advice
Control flow exit adviceSame 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
0
5
10
15
20
25
30
35
no logging hand-coded naïve AspectJ
Logg
ing
Ove
rhea
d
Aspects performance: optimizing Avoid class.getName() if not used
before() traced() { if (!log.isLoggable(Level.FINER)) return; …}
Check log.isLoggable in the residuepointcut 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
0
0.2
0.4
0.6
0.8
1
1.2
1.4
no logging hand-coded AspectJloggable
AspectJif(loggable)
AspectJif(enabled)
Ove
rhea
d
Conclusions
Weaving advices in Java is easyRich bytecodeC++ 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/