290 likes | 445 Views
Modular Reasoning in Aspect- oriented Languages. Tim Molderez. Ansymo. Antwerp Systems and Software Modelling. Aspect-oriented programming (AOP). Typically extension of object- oriented language Aims to improve separation of concerns by modularizing crosscutting concerns
E N D
Modular Reasoning inAspect-orientedLanguages Tim Molderez Ansymo Antwerp Systems and Software Modelling
Aspect-oriented programming (AOP) • Typically extension of object-orientedlanguage • Aims toimproveseparation of concerns bymodularizingcrosscutting concerns • Crosscutting concerns: • Scattered in severalplaces • Tangledwithother concerns • Examples: logging, authentication, caching, profiling, …
A (stereo)typical example: logging After every call to Bank.do*(…) , update the log kind (before/after/around) name pointcut(specifies a set of “join points”) class Logger { … after bankLog:call(* Bank.do*(..)) { log.write(stuff); } …} body advice * Not AspectJ code; this is ContractAJ
Another example: authentication class Security{ … around auth:call(* Bank.do*(Account acc, ..)) { if(isLoggedIn(acc.getOwner())) { proceed(acc); } } …} calls Bank.do* Execute advice instead of Bank.do*
AOP and obliviousness I’m calling silver bullet! With AOP, we can implement crosscutting concerns independent of everything else! • Obliviousness: Advice is executed implicitly Chill out you guys! There’s a middle ground here. AOP considered harmful! An advice could break my existing code at any time!
Modular reasoning • The ability to reason about a module’s behaviour, considering only the module itself (and anything it explicitly refers to) • In imperative languages: no surprises… • In OOP: All classes must be behavioral subtypes (LSP) • … but what about AOP?
Modular reasoning • To benefit from obliviousness, advice may not cause any surprising behaviour.
Modular reasoning in OOP What the developer sees: Bird bird = new Duck();bird.fly(); What happens at runtime: pre’ pre Bird.fly Duck.fly time post post’
Modular reasoning in OOP • To avoid surprising behaviour, the Liskov substitution principle should be respected. • All classes should be behavioural subtypes: • Precondition may not be strengthened • Postcondition may not be weakened • Invariants must be preserved
Modular reasoning in AOP bank.doTransfer(acc1,acc2,50); What the developer sees: What happens at runtime: pre’ pre Bank.doTransfer Security.auth Proceed:Bank.doTransfer time post post’
Modular reasoning in AOP • Similar to OOP, if all advice are seen as around advice: An advice effectively substitutes for whatever it advises. • Advice substitution principle (ASP): • Advice’s precondition may not be stronger than the precondition of the advised join point • Postcondition may not be weakened • Invariant must be preserved
So.. that’s all there is to it? • Yes* • * .. well, there are some big buts (and I cannot lie) • Before and after advice • Higher-order advice • Multiple advice sharing join points • Quantification • Call and execution pointcuts • What if you can’t satisfy the advice substitution principle? • Quantification • Call and execution pointcuts • Before and after advice • …
Before and after advice • Before advice • Slightly different than around advice: postconditions refer to the moment before the implicit proceed call, instead of after. • Advice postconditions may not invalidate the preconditions of X.y • After advice • Preconditions refer to the moment afterthe implicit proceed call. • Advice preconditions can rely on the postconditions of X.y pre Before advice post Implicit proceed Implicit proceed pre After advice post
Special cases Advice 1 • Principle also applies if: • Multiple advice share join points • Advice intercept advice executions • Example • Advice 1,2 and 3 only need to comply with Method’s contracts • Meta-advice complies with Advice 3’scontracts Advice 2 Meta-advice Advice 3 Method
Quantification • ASP defined in terms of a single join point • However, pointcuts are a quantification mechanismand can match with many different join points • An advice may need to comply with many different contracts(e.g. Bank.do* can match with several methods..) • Complying with the ASP becomes more difficult as the number of contracts grows • Scaling problem can be mitigated using contract enforcement tools, as well as using definitions of observers/spectators
ASP violation: This postcondition is weaker than the postcondition of Bank.do* What if you can’t satisfy the ASP? class Security { …@preproc @post if(isLoggedIn()){proc}else{true} around auth:call(* Bank.do*(Account acc, ..)) { if(isLoggedIn(acc.getOwner())) { proceed(acc); } } …} ASP violation cannot be prevented :The advice’s very purpose is to block (the postcondition of) Bank.do* when necessary.
Restoring modular reasoning with the @advisedBy clause class Bank {@pre a1.m >= m @post old(a1.m)-m=a1.m && old(a2.m)+m=a2.m @advisedBy Security.auth, Transaction.commit void doTransfer(Account a1, Account a2, int m){ … } …} • Explicitly expecting to be advised bySecurity.auth and Transaction.commit(in that order) • @advisedBy added to all Bank.do* methods
Quantification • Doesn’t the act of adding all these @advisedBy clauses cancel out the benefits of quantification? • Yes, if you like to add the clauses manually. • In AspectJ: @advisedBy annotations can be generated fully automatically. • In ContractAJ: You can easily introduce a quantification mechanism to add clauses in the right places. • Sidenote: It goes to show that programming concerns more than just the language; the tools around it can be a crucial component too.
Overriding advice • Overriding only has purpose when you expect one thing, but another thing is executed. • Nobody expects the Spanish inq execution of advice! • .. unless an @advisedBy clause is used. • The expectation @advisedBy Authentication.auth could be filled in by e.g. RemoteAuthentication.auth • Satisfies the open-closed principle
Effective specifications • Presence of an @advisedByclause • Makes callers aware of the listed advice • Effectively changes the pre/postconditions that must be taken into account when calling the method,aka the “effective pre/postconditions” • Likewise, listed advice become aware of each other too, as theeffective pre/postcondition of proceed calls changes too • Intuition behind effective pre/postcondition: • Pre/postcondition of the first advice that will be executed,as far as you can tell statically
Find the next advice that will be executed;dynamic parts of pointcuts become part of the specification! Fill in each occurrence of the proc keyword (with the next advice) Effective specifications Before/after advice must include implicit proceed call
Effective specifications .. can be much simpler if none of the pointcuts have dynamic parts: (which is the more common case)
Soundness • Modular reasoning is guaranteed (for pre-and postconditions) if: • All bodies correctly implement their own specifications(, but they can assume modular reasoning). • All classes are behavioural subtypes. • All advice satisfy either the ASP, or are correctly mentioned in an @advisedBy clause. • ASP-compliant advice have a lower precedence than the others. • Proof by induction on reduction length • Consider all possible ways a body can be reached from a method/proceed call
Dynamic enforcement • Contract enforcement advice check all contracts at runtime and throw an exception when the approach is not satisfied. • Simple, but not exhaustive • AspectJ implementation: https://github.com/timmolderez/adbc
Static enforcement • Work in progress (see Luis’s internship & thesis) • Core problem: Determine whether one contract is weaker/stronger than another • Can be done with an SMT solver (Z3) by converting contracts to SMT-lib format • presuper && !presub should not have a solution
Frame conditions • Basic pre/postconditions and invariants don’t cut it to perform formal verification. • Frame conditions: Aside from what a body will do, it should also be specified what the body will not do. • Typically defined as @assignable clauses, which listonly those variables that might change.
Inferring frame conditions • Built on top of Ekeko and Soot • Flow-sensitive: Traverse CFG of each body, keeping track of aliases and modifications • Path-insensitive: Information is merged when if/while branches join • Incremental: Only update what’s necessary • Modular: Okay to assume modular reasoning
Frame conditions in AOP • Frame conditions can be used for multiple purposes • Detect which fields an ASP-compliant advice is allowed to modify • Detect which properties a proceed call can preserve • Detect which advice can be executed concurrent to thebody being advised
Summary • An approach to modular reasoning in AOP: • Advice subsitution principle • @advisedBy clause • The approach is sound, and can be enforced both dynamically and statically. Modular reasoning in AOP is possible; there is a useful middle ground.