Open Modules: Reconciling Aspects and Modularity

Download Report

Transcript Open Modules: Reconciling Aspects and Modularity

Selective Open Recursion
Modular Reasoning about
Components and Inheritance
Jonathan Aldrich
Carnegie Mellon University
Kevin Donnelly
Boston University
Outline




The Fragile Base Class Problem
Selective Open Recursion
Implementation & Analysis
Discussion
Inheritance and Information Hiding

Parnas’ advice:

Modularize a system to hide information that may
change
Inheritance and Information Hiding

Parnas’ advice:


Modularize a system to hide information that may
change
Research question

How to hide information in component-based
systems with inheritance?
A Challenge: Open Recursion

Recursion

Objects can make self-calls to their own methods
A Challenge: Open Recursion

Recursion


Objects can make self-calls to their own methods
Open Recursion

Self-calls are dynamically dispatched


I.e., target of call is “open” and can be overridden
Beneficial in “template method” design patterns
Open Recursion
Window
class Window {
void draw() {
// draw background
this.drawForeground();
}
void drawForeground() { … }
}
class MyWindow {
void draw() { super.draw(); }
void drawForeground() {
super.drawForeground();
...
}
...
}
draw
drawFore
Open Recursion
Window
class Window {
void draw() {
// draw background
this.drawForeground();
}
void drawForeground() { … }
}
class MyWindow {
void draw() { super.draw(); }
void drawForeground() {
super.drawForeground();
...
}
...
}
draw
drawFore
Window
MyWindow
draw
draw
drawFore
drawFore
Hook Methods

Hook methods



Called by this when some event occurs
Intended as extension point for subclasses
Open recursion is necessary
Hook Methods

Hook methods




Called by this when some event occurs
Intended as extension point for subclasses
Open recursion is necessary
Other methods



Perform some task
May be overridden, but has no “event” semantics
Open recursion unnecessary


At best, minor convenience
Has potential to cause the Fragile Base Class Problem
The Fragile Base Class Problem
class CountingSet extends Set {
private int count;
void add(Object elem) {
super.add(elem);
count++;
}
void addAll(Collection c){
super.addAll(c);
count+=c.size();
}
int size() { return count; }
// other functions
}
Set
CountingSet
addAll
addAll
add
add
The Fragile Base Class Problem
class CountingSet extends Set {
private int count;
void add(Object elem) {
super.add(elem);
count++;
}
void addAll(Collection c){
super.addAll(c);
count+=c.size();
}
int size() { return count; }
// other functions
}
Set
CountingSet
addAll
addAll
add
add
Implicit assumption:
Set.addAll does not call Set.add
If this assumption is violated
(or changed), CountingSet breaks
The Fragile Base Class Problem

Definition (for this instance of FBC)

A class may depend on the calling patterns of a
superclass, and break if these are changed
Two Solutions to the FBC

Document open recursion
[Kiczales & Lamping, Steyaert et al., Ruby & Leavens]


Exposes information, rather than hiding it
Prohibits natural changes to superclass
Use forwarding to avoid open recursion
[Bloch,Szyperski]
Gives up benefits of open recursion
Can we get the benefits of open recursion without
the reasoning costs?
Two Solutions to the FBC

Document open recursion
[Kiczales & Lamping, Steyaert et al., Ruby & Leavens]



Exposes information, rather than hiding it
Prohibits natural changes to superclass
Use forwarding to avoid open recursion
[Bloch,Szyperski]

Gives up benefits of open recursion
Can we get the benefits of open recursion without
the reasoning costs?
Two Solutions to the FBC

Document open recursion
[Kiczales & Lamping, Steyaert et al., Ruby & Leavens]



Exposes information, rather than hiding it
Prohibits natural changes to superclass
Use forwarding to avoid open recursion
[Bloch,Szyperski]


Gives up benefits of open recursion
Can we get the benefits of open recursion without
the reasoning costs?
Outline




The Fragile Base Class Problem
Selective Open Recursion
Implementation & Analysis
Discussion
Selective Open Recursion
Use open recursion only where necessary

Hook methods

Use open modifier on method


Expresses “hook method” intent
All calls to open methods dispatched dynamically

Just as in Java today
Selective Open Recursion
Use open recursion only where necessary

Hook methods

Use open modifier on method


All calls to open methods dispatched dynamically


Expresses “hook method” intent
Just as in Java today
Other methods

Calls to non-open methods on this dispatched statically



The only change vs. Java
Hides internal calling patterns from subclasses
Other calls dispatched dynamically

open ≠ virtual
Selective Open Recursion Examples
class Set {
void add(Object o) {
// adds an element
}
void addAll(Collection c){
foreach (Object o in c)
this.add(o);
}
}
add is not open, so subclass cannot
intercept and depend on the call. Set can
change without affecting subclasses.
Selective Open Recursion Examples
class Set {
void add(Object o) {
// adds an element
}
void addAll(Collection c){
foreach (Object o in c)
this.add(o);
}
}
add is not open, so subclass cannot
intercept and depend on the call. Set can
change without affecting subclasses.
class Set {
// invoked for each add op.
open void add(Object o) {
// adds an element
}
void addAll(Collection c){
foreach (Object o in c)
this.add(o);
}
}
add is open, indicating the developer’s
intent to expose this method as a semantic
event to subclasses. Any changes to class
Set must preserve these semantics.
Design Principles

Non-open as default

Only use open recursion when explicitly intended
Design Principles

Non-open as default


Only use open recursion when explicitly intended
Annotate the method, not the call

Design intent is attached to operation
Do you have to change the language?

Coding guidelines




Never call a public method on this
Hook methods should be protected
Non-hook, protected methods should be final
Not our idea


Suggested by Ruby & Leavens
Used extensively in JDK libraries
Do you have to change the language?

Coding guidelines




Not our idea



Never call a public method on this
Hook methods should be protected
Non-hook, protected methods should be final
Suggested by Ruby & Leavens
Used extensively in JDK libraries
However—


This solution relies on programmer discipline
Language integration provides automated checking
Outline




The Fragile Base Class Problem
Selective Open Recursion
Implementation & Analysis
Future work and Conclusion
Implementation in Java

Extension to Barat compiler


Rewrite calls to non-open methods on this


Bokowski & Spiegel
Call a final method with the implementation for the
current class
Available at http://www.archjava.org/
Open Recursion Inference

Analysis to compute open annotations

Method m in class C must be open if:



Method m’ in class C’ <= C calls this.m
Class C’’ <= C’ overrides m
Class C’’ inherits or super-calls C’.m’
Open Recursion Inference

Analysis to compute open annotations

Method m in class C must be open if:




Method m’ in class C’ <= C calls this.m
Class C’’ <= C’ overrides m
Class C’’ inherits or super-calls C’.m’
Results may be imperfect

Assumes whole program is available


Misses open methods that are not overridden
May mark methods open that should be refactored to be
non-open
Experiments

Ran open recursion inference

java.* packages in JDK 1.4.2

Except java.nio, java.sql (see paper)
Experiments

Ran open recursion inference

java.* packages in JDK 1.4.2


Except java.nio, java.sql (see paper)
Hypotheses


Open recursion is rarely needed
Selective open recursion enables more
optimization
Frequency of Open Recursion


9897 methods in our portion of stdlib
246 (2.5%) of methods were inferred open
Frequency of Open Recursion

9897 methods in our portion of stdlib
246 (2.5%) of methods were inferred open

Maybe the stdlib doesn’t use inheritance



1394 of these methods are overridden
Only 18% of these are open
Open recursion is rarely needed
Thus making it selective may enable substantial information
hiding
Frequency of Open Recursion

9897 methods in our portion of stdlib
246 (2.5%) of methods were inferred open

Maybe the stdlib doesn’t use inheritance




1394 of these methods are overridden
Only 18% of these are open
Open recursion is rarely needed

Thus making it selective may enable substantial
information hiding
Optimization Potential


22339 calls in our portion of stdlib
6852 self-calls

716 to open methods


Must be dynamically dispatched
6136 to non-open methods


Can be inlined in our proposal (27% of total calls)
Inlining in Java would require private, final, or
whole-program analysis
Outline




The Fragile Base Class Problem
Selective Open Recursion
Implementation & Analysis
Future Work and Conclusion
Future Work:
Application to Formal Reasoning

Formalizing as Featherweight Java + module
system
Future Work:
Application to Formal Reasoning


Formalizing as Featherweight Java + module
system
Goal: bisimulation-based proof technique for
showing contextual equivalence of modules in the
presence of inheritance
Future Work:
Application to Formal Reasoning



Formalizing as Featherweight Java + module
system
Goal: bisimulation-based proof technique for
showing contextual equivalence of modules in the
presence of inheritance
Use selective open recursion and other
techniques to provide more information hiding

i.e. prove more programs equivalent
Conclusion

Open Recursion complicates reasoning


rarely used in practice
Selective open recursion




retains benefits of open recursion where needed
avoids fragile base class problem
can be efficiently inferred
may allow more optimization and reasoning
Open Recursion
class Set {
void add(Object elem) {
// adds an element
}
void addAll(Collection c) {
foreach (Object o in c)
this.add(o)
}
...
class CountingSet extends Set {
private int count;
void add(Object elem) {
super.add(elem);
count++;
}
void addAll(Collection c){
super.addAll(c);
count+=c.size();
}
int size() { return count; }
// other functions
Calls to this are dispatched to
subclass
Thus subclass can tell exactly
when each method is called
}