A Formal Model of Modularity in AspectOriented Programming

  • Slides: 43
Download presentation
A Formal Model of Modularity in Aspect-Oriented Programming Jonathan Aldrich 15 -819: Objects and

A Formal Model of Modularity in Aspect-Oriented Programming Jonathan Aldrich 15 -819: Objects and Aspects Carnegie Mellon University

Outline AOP Modularity Challenges n Open Modules n A Bit of Formality n Comparison

Outline AOP Modularity Challenges n Open Modules n A Bit of Formality n Comparison to Aspect-Aware Interfaces n Lessons Learned and Discussion n

Modularity and Encapsulation n Parnas’ advice: n n Encapsulation n Modularize a system to

Modularity and Encapsulation n Parnas’ advice: n n Encapsulation n Modularize a system to hide information that may change A mechanism for enforcing information hiding Java classes & packages, ML modules… Aspect-oriented Programming n More flexible ways of modularizing a system

Is AOP Modular? Back to Parnas: Does AOP hide information that is likely to

Is AOP Modular? Back to Parnas: Does AOP hide information that is likely to change? n Yes, within the aspect n n n Aspect code can be evolved separately No, not within the base code n Minor changes to base code break the aspect

Example: Assurance Aspect class Point extends Shape { void move. By(int dx, int dy)

Example: Assurance Aspect class Point extends Shape { void move. By(int dx, int dy) { x += dx; y += dy; . . . }

Example: Assurance Aspect class Point extends Shape { void move. By(int dx, int dy)

Example: Assurance Aspect class Point extends Shape { void move. By(int dx, int dy) { x += dx; y += dy; . . . } class Rectangle extends Shape { void move. By(int dx, int dy) { p 1 x += dx; p 1 y += dy; p 2 x += dx; p 2 y += dy; . . . }

Example: Assurance Aspect class Point extends Shape { void move. By(int dx, int dy)

Example: Assurance Aspect class Point extends Shape { void move. By(int dx, int dy) { x += dx; y += dy; . . . } class Rectangle extends Shape { void move. By(int dx, int dy) { p 1 x += dx; p 1 y += dy; p 2 x += dx; p 2 y += dy; . . . } aspect Assure. Shape. Invariants { }

Example: Assurance Aspect class Point extends Shape { void move. By(int dx, int dy)

Example: Assurance Aspect class Point extends Shape { void move. By(int dx, int dy) { x += dx; y += dy; . . . } class Rectangle extends Shape { void move. By(int dx, int dy) { p 1 x += dx; p 1 y += dy; p 2 x += dx; p 2 y += dy; . . . } aspect Assure. Shape. Invariants { pointcut moves() = call(Shape+. move. By(. . )); }

Example: Assurance Aspect class Point extends Shape { void move. By(int dx, int dy)

Example: Assurance Aspect class Point extends Shape { void move. By(int dx, int dy) { x += dx; y += dy; . . . } class Rectangle extends Shape { void move. By(int dx, int dy) { p 1 x += dx; p 1 y += dy; p 2 x += dx; p 2 y += dy; . . . } aspect Assure. Shape. Invariants { pointcut moves() = call(Shape+. move. By(. . )); after(): moves() { scene. check. Invariants(); } }

Example: Broken Assurance Aspect class Point extends Shape { void move. By(int dx, int

Example: Broken Assurance Aspect class Point extends Shape { void move. By(int dx, int dy) { x += dx; y += dy; . . . } class Rectangle extends Shape { void move. By(int dx, int dy) { p 1 x += dx; p 1 y += dy; p 2 x += dx; p 2 y += dy; . . . } Change representation to use Point aspect Assure. Shape. Invariants { pointcut moves() = call(Shape+. move. By(. . )); after(): moves() { scene. check. Invariants(); } }

Example: Broken Assurance Aspect class Point extends Shape { void move. By(int dx, int

Example: Broken Assurance Aspect class Point extends Shape { void move. By(int dx, int dy) { x += dx; y += dy; . . . } class Rectangle extends Shape { void move. By(int dx, int dy) { p 1. move. By(dx, dy); p 2. move. By(dx, dy); . . . } Change representation to use Point aspect Assure. Shape. Invariants { pointcut moves() = call(Shape+. move. By(. . )); after(): moves() { scene. check. Invariants(); } }

Example: Broken Assurance Aspect class Point extends Shape { void move. By(int dx, int

Example: Broken Assurance Aspect class Point extends Shape { void move. By(int dx, int dy) { x += dx; y += dy; . . . } class Rectangle extends Shape { void move. By(int dx, int dy) { p 1. move. By(dx, dy); p 2. move. By(dx, dy); . . . } Change representation to use Point aspect Assure. Shape. Invariants { pointcut moves() = call(Shape+. move. By(. . )); after(): moves() { scene. check. Invariants(); } } Now the scene invariants are checked in the middle of a Rectangle move—when they might be broken!

Analysis n Aspects can violate information hiding n n Similar to OO Fragile Base

Analysis n Aspects can violate information hiding n n Similar to OO Fragile Base Class Problem n n Assurance aspect depends on Shape internals Observing impl. dependant calling patterns Can fix each individual problem n Better: use modules to forestall issue

Fix #1: external advice class Point extends Shape { void move. By(int dx, int

Fix #1: external advice class Point extends Shape { void move. By(int dx, int dy) { x += dx; y += dy; . . . } class Rectangle extends Shape { void move. By(int dx, int dy) { p 1 x += dx; p 1 y += dy; p 2 x += dx; p 2 y += dy; . . . } aspect Assure. Shape. Invariants { pointcut moves(): call(Shape+. move. By(. . )) && !within(shape. *); after(): moves() { scene. check. Invariants(); } } Only specifies calls that are external to the shape package

Fix #2: semantic pointcut class Point extends Shape { void move. By(int dx, int

Fix #2: semantic pointcut class Point extends Shape { void move. By(int dx, int dy) { x += dx; y += dy; . . . } class Rectangle extends Shape { void move. By(int dx, int dy) { p 1 x += dx; p 1 y += dy; p 2 x += dx; p 2 y += dy; . . . } class Shape { public pointcut moves(): call(Shape+. move. By(. . )); } aspect Assure. Shape. Invariants { after(): Shape. moves() { scene. check. Invariants(); } } Move pointcut to the shape package Now the shape maintainer is responsible for preserving its semantics when shapes evolve

Open Modules Ordinary functional interface Open Module void move. By(int, int); void animate(Motion);

Open Modules Ordinary functional interface Open Module void move. By(int, int); void animate(Motion);

Open Modules Ordinary functional interface Open Module void move. By(int, int); void animate(Motion); Semantic

Open Modules Ordinary functional interface Open Module void move. By(int, int); void animate(Motion); Semantic Pointcut • Denotes some internal event • Promise to maintain event semantics as code evolves [Gudmunson & Kiczales] pointcut moves;

Open Modules Clients can call interface functions Open Module void move. By(int, int); void

Open Modules Clients can call interface functions Open Module void move. By(int, int); void animate(Motion); pointcut moves;

Open Modules Clients can call interface functions Clients can advise external calls to interface

Open Modules Clients can call interface functions Clients can advise external calls to interface functions Open Module void move. By(int, int); void animate(Motion); pointcut moves;

Open Modules Clients can call interface functions Clients can advise external calls to interface

Open Modules Clients can call interface functions Clients can advise external calls to interface functions Open Module void move. By(int, int); void animate(Motion); pointcut moves; Clients can advise pointcuts in interface

Open Modules Clients can call interface functions Clients can advise external calls to interface

Open Modules Clients can call interface functions Clients can advise external calls to interface functions Open Module void move. By(int, int); void animate(Motion); pointcut moves; X Clients can advise pointcuts in interface Clients cannot advise any internal calls (not even to exported functions)

Open Module Properties: Equivalence n Motivation n Rules describe when changes could affect clients

Open Module Properties: Equivalence n Motivation n Rules describe when changes could affect clients Can be used to prove correctness of refactorings Bisimulation-based equivalence n n All functions map args to same results Invoke internal pointcuts in same way n Same sequence, same context

Open Module Properties: Abstraction n Verifies correctness of equivalence rules n n Shows that

Open Module Properties: Abstraction n Verifies correctness of equivalence rules n n Shows that information hiding works Informal statement of theorem n n Consider two module implementations that are equivalent according to the bisimulation rules No client code can distinguish the behavior of these modules (even by using aspects) n Note: this would fail for Aspect. J!

Comparison to Aspect-Aware Interfaces n AAI: more obliviousness, extensibility n n OM: truly separate

Comparison to Aspect-Aware Interfaces n AAI: more obliviousness, extensibility n n OM: truly separate development n n Don’t have to anticipate semantic pointcuts AAI cannot be computed in this case OM: shows technical properties of AAI n n n AAI is an OM interface computed by tools Abstraction supports evolvability Not all information in AAI is needed n n Don’t need exact advice Don’t need pointcuts for external calls to interface functions

Tool and Language Implications n Tools: Provide editable interface pointcuts n n Tools: Support

Tool and Language Implications n Tools: Provide editable interface pointcuts n n Tools: Support dependency tracking n n n Change base code and affected pointcuts at the same time Let you know when you’re depending on impl. Warn you to re-check pointcuts when impl. Changes Language n Make it easier to write non-invasive pointcuts

Discussion Extensibility vs. Reasoning n Tool vs. Language-based reasoning n Open Modules into real

Discussion Extensibility vs. Reasoning n Tool vs. Language-based reasoning n Open Modules into real AOP systems n Analysis based on Open Modules n

End of Presentation/Extra Slides

End of Presentation/Extra Slides

Tiny. Aspect Example (* fibonacci function *) val fib = fn x: int =>

Tiny. Aspect Example (* fibonacci function *) val fib = fn x: int => 1 around call(fib) (x: int) = if (x > 2) then fib(x-1) + fib(x-2) else proceed x (* caching library *) val in. Cache = fn. . . val lookup. Cache = fn. . . val update. Cache = fn. . . (* advice to cache calls to fib *) pointcut cache. Function = call(fib) around cache. Function(x: int) = if (in. Cache x) then lookup. Cache x else let v = proceed x in update. Cache x v; v

Tiny. Aspect: Syntax

Tiny. Aspect: Syntax

Evaluation n Environment captures advice n Declarations add labels to environment n Functions are

Evaluation n Environment captures advice n Declarations add labels to environment n Functions are looked up just before application

Evaluation n Environment captures advice n Declarations add labels to environment n Advice updates

Evaluation n Environment captures advice n Declarations add labels to environment n Advice updates environment

Tiny. Aspect: Values and Contexts

Tiny. Aspect: Values and Contexts

Tiny. Aspect: Reduction Rules

Tiny. Aspect: Reduction Rules

Tiny. Aspect: Expression Typing

Tiny. Aspect: Expression Typing

Tiny. Aspect: Declaration Typing

Tiny. Aspect: Declaration Typing

Tiny. Aspect: Properties

Tiny. Aspect: Properties

Open Modules: Syntax

Open Modules: Syntax

Open Modules: Example structure Math = struct val fib = fn x: int =>

Open Modules: Example structure Math = struct val fib = fn x: int => 1 around call(fib) (x: int) = … structure cache. Fib = Cache(struct pointcut f = call(fib) end : > sig fib : int->int end structure Cache = functor(X : sig f : pc(int->int) end) => struct around X. f(x: int) =. . . (* same definition *) end

Open Modules: Semantics n Standard type system n n Signature subtyping permitted Runtime semantics

Open Modules: Semantics n Standard type system n n Signature subtyping permitted Runtime semantics mostly standard n E. g, functor application uses substitution

Open Modules: Semantics n Sealing has operational effect n Value bindings given fresh labels

Open Modules: Semantics n Sealing has operational effect n Value bindings given fresh labels n n n External advice doesn’t affect internal calls C. f. “freeze” operator in Jigsaw, other systems Pointcuts are unchanged

Reynolds’ Abstraction Property n No client can distinguish two modules that are “observationally equivalent”

Reynolds’ Abstraction Property n No client can distinguish two modules that are “observationally equivalent” (c. f. Pi-calculus) n n calling functions in interface advising external calls to interface advising pointcuts in interface Means that information hiding works n You can change the internals of a module w/o affecting clients

Observational Equivalence n Functions behave equivalently for all args n Expression evaluation must be

Observational Equivalence n Functions behave equivalently for all args n Expression evaluation must be bisimilar n w. r. t. a set of available labels Can take any step other than looking up n Can both look up the same label in n

Formal Abstraction Theorem n Proof insight n n n Client can only advise labels

Formal Abstraction Theorem n Proof insight n n n Client can only advise labels in Libraries treat these labels equivalently Key invariant n Clients are structurally identical n Except for embedded equivalent values