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/