6a-Weaving in AspectJ
Download
Report
Transcript 6a-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 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
Advice on method and constructor call
Calling
code must be available
Advice on method execution
The
code must be available
Etc.
method code must be available
Part 2: JVM Primer
Java Virtual Machine: structure
Stack (per thread)
Frame 2
locals:
this
int i
int j
Class:
Param stack:
System.out
String
…
Frame 1
Class:
locals:
this
Param stack:
Hello
Frame 2
…
…
Frame 1
Heap
SP
Frame 0
Object Hello
field: int f1
field: String f2
Constant Pool
Class Hello
method: print()
method: <init>()
Class Main
method: main()
method: <init>()
field: int fM
String “hello world”
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, putfield
getfield FID: …, objectref …, value
putfield FID: …, value, objectref …
Accessing object’s static fields: getstatic,
putstatic
getstatic FID: … …, value
putstatic FID: ..., value …
JVM primer: instructions 4
Calling methods:
invokevirtual
N:…, param1, [param2,…] result
calls Nth method of class
invokespecial
N
calls constructors etc.
invokestatic
N
Invokeinterface N
return, ireturn, …
Creating objects:
N – allocates and inits memory.
Then, constructor must be called
new
JVM Primer: example
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: exception handling
Exception handling
throw
N: throw an exception of class N
Exception table:
FROM
TO
Class
Handler
1
3
17
8
Munger
Part 3: Aspects
implementation
Shadow
Residue
AspectJ: the process
Java source
Java
Library
Aspects source
AspectJ compiler
Java bytecode
Aspects bytecode
shadows
mungers
Weaver
Woven program
Aspects
Library
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
Field-get
Target: from stack
Example: “hello world”
Args: none
public static void main(String[] s) {
System.out.println(“hello world”);
}
0: getstatic [java/lang/System.out]
Method-execution
Target: this
Args: local vars
3: ldc [String “hello world”]
5: invokevirtual [java/io/Printstream.println]
8: return
Method-call
Target: From stack
Args: From stack
!Parameters must be
stored and re-loaded
Join Point Shadows: Notes
Java bytecodes carry plentiful metainformation
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 wholeprogram analysis
The Matching process
1.
For each advice:
2.
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 value
Insert the code in the end of the shadow
Execution:
All the return points must be captured
Generate 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
Control flow entry advice
Same
After returning and After throwing
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
Logging
enabled
35
Logging
disabled
30
Logging Overhead
25
20
15
10
5
0
no logging
hand-coded
naïve AspectJ
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
1.4
1.2
1
Overhead
0.8
0.6
0.4
0.2
0
no logging hand-coded
AspectJ
loggable
AspectJ
AspectJ
if(loggable) if(enabled)
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/