220 likes | 311 Views
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)
E N D
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) ABC: an implementation of AspectJ
disciplined metaprogramming What is AspectJ?
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 publicaspect DetectEJBViolations { pointcut uiCalls() : call(* java.awt.*+.*(..)); before() : uiCalls() && cflow(call(* EnterpriseBean+.*(..))) { System.err.println("UI call from EJB"); } declareerror : uiCalls() && within(EnterpriseBean+) : "UI call from EJB"; pointcut staticMemberAccess() : set(static * EnterpriseBean+.*); declareerror : staticMemberAccess() : "EJBs are not allowed to have non-final static vars"; }
Counting live objects per class publicaspect AllocFree { publicinterface Finalize {} declareparents: mypackage..* implements Finalize; publicvoid Finalize.finalize() throws Throwable { LiveData.decr(this.getClass()); } before(Object tgt): initialization(mypackage..*.new(..)) && this(tgt) { LiveData.incr(tgt.getClass()); } }
Authorisation publicabstractaspect AbstractAuthAspect { private Subject _authenticatedSubject; publicabstractpointcut authOperations(); ... Object around() : authOperations() && !cflowbelow(authOperations()) { try { return Subject.doAsPrivileged(_authenticatedSubject, new PrivilegedExceptionAction() { public Object run() throws Exception { returnproceed(); }}, null); } catch (PrivilegedActionException ex) { thrownew AuthorizationException(ex.getException()); } } }
New compiler pass publicaspect AspectTransformPass { AspectTransformer Assign.aspectTransformEnter(AspectTransformer at) { .... } Node Assign.aspectTransformLeave(AspectTransformer at) { ... } } AST node Assign has subclasses LocalAssign, FieldAssign, ...
ajc: “standard” AspectJ compiler aspects “weaving” ajc javasource class files jars • 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 publicclass MethodMatch { publicstatic void main(String[] args) { foo((Object)"Object"); foo("String"); } publicstatic void foo(Object o) { System.out.println("An object: " + o); } } publicaspect NewFoo { publicstatic void MethodMatch.foo(String s) { System.out.println("A string: " + s); } } 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 bytecodeweaver standard JVM Dufour, Goard et al, OOPSLA 2004 JVMPI interface *J dynamic metric tool JVMPI agent standard metrics metric analyser with tag propagator AspectJ-specific metrics
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
Polyglot Java compiler AspectJ source, and jars Java extracts of source AspectJ extension aspectInfo Jimple Soot analysis and transformation framework intertype adjuster advice weaver class files The AspectBench Compiler: ABC
public class A { int x; class B { int x; } } 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; } } 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. 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” Polyglot: scope for intertype decls host class
Soot: Jimple int foo(int, int) { Example this; int x, y, $i0, $i1; this := @this:Example; x := @parameter0: int; y := @parameter1: int; if x >= y goto label0; $i0 = y - x; return $i0; label0: $i1 = x - y; return $i1; } int foo(int x, int y) { if (x<y) return (y-x); else return (x-y); } normal compilation 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); } }
Weaving at shadows 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 <Aspect: Aspect aspectOf()>(); virtualinvoke theAspect.<Aspect: void afterReturning$0()>(); nop; theAspect1 = staticinvoke <Aspect: Aspect aspectOf()>(); adviceformal = $i1; virtualinvoke theAspect1.<Aspect: void afterReturning$1(int)> (adviceformal); nop;
Win (1) : no closures for around publicclass 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,...); ... } public void anotherShadowMethod() { ... Aspect.aspectOf().adviceMethod$1(this,1,arg1,...); ... } } calls thisparam.proceed(0,...) calls thisparam.proceed(1,...)
Win (2) : no stacks for cflow cflow(call(* foo(..))) && call (* bar(..)) ajc: • simulates call stack by pushing and popping around foo calls • one stack per thread matches call stack: bar ... 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 foo ... Sereni et al, AOSD 2003.
Extensibility of abc size of abc: 40KLOC polyglot: 60KLOC Soot: 180KLOC • 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 three extensions: • local variables in pointcuts • cast pointcut • global pointcuts in the works: pointcuts that query program trace “pure” modifier on aspects
ABC Summary • 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 months