Transcript ABC: an implementation of AspectJ
ABC: an implementation of AspectJ
Oege de Moor Programming Tools Group University of Oxford
joint work with Ganesh Sittampalam, Sascha Kuzins, Chris Allan, Pavel Avgustinov, Julian Tibble, Damien Sereni (Oxford) Laurie Hendren, Jennifer Lhot ák, Ondřej Lhoták, Bruno Dufour, Christopher Goard, Clark Verbrugge (McGill) Aske Simon Christensen ( Århus)
What is AspectJ?
disciplined metaprogramming
The bluffer's guide to aspect-lingo
Static:
Intertype declarations:
inject new members into existing classes at compile-time Dynamic : aspect observes base program when certain patterns of events happen, run some extra code “join point” = event = node in (dynamic) call graph “pointcut” = pattern of events = set of nodes in call graph “shadow” = program point that corresponds to join point “advice” = extra code
EJB policy enforcement
}
public aspect
DetectEJBViolations {
pointcut
uiCalls() :
call
(* java.awt.*+.*(..));
before
() : uiCalls() &&
cflow
(
call
(* EnterpriseBean+.*(..))) { System.err.println("UI call from EJB"); }
declare error
: uiCalls() &&
within
(EnterpriseBean+) : "UI call from EJB";
pointcut
staticMemberAccess() :
set
(
static
* EnterpriseBean+.*);
declare error
: staticMemberAccess() : "EJBs are not allowed to have non-final static vars";
Counting live objects per class
public aspect
AllocFree {
public interface
Finalize {}
declare parents
: mypackage..*
implements
Finalize;
public void
Finalize.finalize()
throws
Throwable { LiveData.decr(
this
.getClass()); } } }
before
(Object tgt):
initialization
(mypackage..*.new(..)) &&
this
(tgt) { LiveData.incr(tgt.getClass());
Authorisation
public abstract aspect
AbstractAuthAspect {
private
Subject _authenticatedSubject;
public abstract pointcut
authOperations(); ...
Object
around
() : authOperations() && !
cflowbelow
(authOperations()) {
try
{
return
Subject.doAsPrivileged(_authenticatedSubject,
new
PrivilegedExceptionAction() {
public
Object run()
throws
Exception {
return proceed
(); }}, null); } } }
catch
(PrivilegedActionException ex) {
throw new
AuthorizationException(ex.getException()); }
New compiler pass
public aspect
AspectTransformPass { } AspectTransformer Assign.aspectTransformEnter(AspectTransformer at) { ....
} } Node Assign.aspectTransformLeave(AspectTransformer at) { ...
AST node Assign has subclasses LocalAssign, FieldAssign, ...
ajc: “standard” AspectJ compiler
aspects java source jars “weaving” ajc class files ● builds on Eclipse compiler ● weaving with BCEL ● incremental ● about 45KLOC, excluding IDE support ● started out as source-to-source ● java parts may be aspect-aware Daniel Sabbah (VP of development@ IBM): “critical to our survival”
Problems with jars
vs
source
public class
MethodMatch {
public static
void main(String[] args) { foo((Object)"Object"); foo("String"); }
public aspect
NewFoo {
public static
void MethodMatch.foo(String s) { System.out.println("A string: " + s); } } }
public static
void foo(Object o) { System.out.println("An object: " + o); }
compile together from source:
An object: Object A string: String
weave aspect into MethodMatch.class:
An object: Object An object: String
What do you pay at runtime?
From the FAQ on aspectj.org:
We aim for the performance of our implementation of AspectJ to be on par with the same functionality hand-coded in Java. Anything significantly less should be considered a bug.
...we believe that code generated by AspectJ has negligible performance overhead.
Measuring the cost with *J
modified ajc frontend tagging bytecode weaver standard JVM Dufour, Goard
et al
, OOPSLA 2004 JVMPI interface *J dynamic metric tool JVMPI agent metric analyser with tag propagator standard metrics
AspectJ-specific metrics
ajc performance
100 90 80 70 60 50 40 30 20 10 0 DCM ProdLin Bean NllChk Figure LoD Overheads(%) Slowdown(* )
The need for a second compiler
● language definition other than test suite ● explore AOP language design space ● experiment with better code generation ● experiment with static analyses for safety checks and optimisations
The AspectBench Compiler: ABC
AspectJ source, and jars Polyglot Java compiler AspectJ extension Java extracts of source aspectInfo intertype adjuster advice weaver class files Jimple Soot analysis and transformation framework
Polyglot: scope for intertype decls
} }
public class
A { int x;
class
B { int x; host class
when does field or call refer to host class A.B?
● no explicit receiver? if it was introduced into environment via the ITD, give it “
this
” from host.
● explicit “
this
” or “
super
”? if there is no qualifier and we're not inside a local class, it refers to the host. If there is a qualifier Q, it refers to the host if the host has an enclosing instance of type Q.
} }
aspect
Aspect {
static
int x;
static
int y; int A.B.foo() {
class
C { int x = 3; int bar() {
return
x + A.this.x;} }
return
(
new
C()).bar() + x + y; implementation: ● ● extend environment type new AST nodes “
hostSpecial
” (this/super) ● ITDs add accessible members from host ● rewrite rules to disambiguate this/super to “
Special
” or “
hostSpecial
”
if
int foo(int x, int y) { (x return (y-x); else return } (x-y); int foo(int, int) { Example this; int x, y, $i0, $i1; normal compilation this := @this:Example; x := @parameter0: int; y := @parameter1: int; if x >= y goto label0; } want to weave: aspect Aspect { after () returning : execution (* foo(..)) { System.out.println("woven"); } after () returning (int x) : execution (int foo(..)) { System.out.println("result="+x); } $i0 = y - x; return $i0; } label0: $i1 = x - y; return $i1; this := @this:Example; x := @parameter0: int; y := @parameter1: int; nop; if x >= y goto label0; $i0 = y - x; $i1 = $i0; goto label1; label0: $i1 = x - y; } label1: nop; return $i1; nop; theAspect = staticinvoke ajc/abc 3 2 1 0 8 7 6 5 4 14 13 12 11 10 9 dcm pr dlin bean nllchk f igur e LoD JIT inter pr eter public class ShadowClass implements AroundClosure$1 { } public [ret-type] proceed(int shadowID, [arg-type] arg1, ...) { switch (shadowID) { case 0: ... do what first shadow did ... case 1: ... do what second shadow did ... } } public void shadowMethod() { ... Aspect.aspectOf().adviceMethod$1(this,0,arg1,...); ... } } calls thisparam.proceed(0,...) public void anotherShadowMethod() { ... Aspect.aspectOf().adviceMethod$1(this,1,arg1,...); ... calls thisparam.proceed(1,...) cflow ( call (* foo(..))) && call (* bar(..)) matches call stack: bar ● ajc: simulates call stack by pushing and popping around foo calls ● one stack per thread ... foo ... ● abc: use counter in lieu of stack when possible ● CSE on cflow expressions ● look up thread-local stack/counter only once per body ● static estimate of possible call stacks can eliminate runtime cost completely allow cflow in declare warning/error Sereni et al , AOSD 2003. size of abc: 40KLOC polyglot: 60KLOC Soot: 180KLOC ● ● ● three extensions: local variables in pointcuts cast pointcut global pointcuts in the works: pointcuts that query program trace “pure” modifier on aspects ● ● ● ● ● ● ● 3 new ast classes 3 new weaver classes override 1 ast class 1 new node factory 1 new visitor pass total 946 lines enable with compiler flags ● Implements the same language as ajc ● Whole-program, aimed at – extensibility – static analysis ● – performance of compiled code Suite of associated tools: decompiler, performance measurement, visualisation in Eclipse ● Current status: – pass majority of ajc test suite – likely release mid-October: complete development in 9 monthsSoot: Jimple
Weaving at shadows
ABC performance
Win (1) : no closures for around
Win (2) : no stacks for cflow
Extensibility of abc
ABC Summary