Title: Selective Open Recursion Modular Reasoning about Components and Inheritance
1Selective Open Recursion Modular Reasoning
aboutComponents and Inheritance
- Jonathan Aldrich Carnegie Mellon
University - Kevin Donnelly Boston
University
2Outline
- The Fragile Base Class Problem
- Selective Open Recursion
- Implementation Analysis
- Discussion
3Inheritance and Information Hiding
- Parnas advice
- Modularize a system to hide information that may
change
4Inheritance 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?
5A Challenge Open Recursion
- Recursion
- Objects can make self-calls to their own methods
6A 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
7Open Recursion
Window
- class Window
- void draw()
- // draw background
- this.drawForeground()
-
- void drawForeground()
-
- class MyWindow
- void draw() super.draw()
- void drawForeground()
- super.drawForeground()
- ...
-
- ...
draw
drawFore
8Open 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
9Hook Methods
- Hook methods
- Called by this when some event occurs
- Intended as extension point for subclasses
- Open recursion is necessary
-
10Hook 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
11The 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)
- countc.size()
-
- int size() return count
- // other functions
Set
CountingSet
addAll
addAll
add
add
12The 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)
- countc.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
13The 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
14Two 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?
15Two 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?
16Two 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?
17Outline
- The Fragile Base Class Problem
- Selective Open Recursion
- Implementation Analysis
- Discussion
18Selective 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
-
19Selective 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
- 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
20Selective 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. -
21Selective 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 developers intent to
expose this method as a semantic event to
subclasses. Any changes to class Set must
preserve these semantics.
22Design Principles
- Non-open as default
- Only use open recursion when explicitly intended
23Design Principles
- Non-open as default
- Only use open recursion when explicitly intended
- Annotate the method, not the call
- Design intent is attached to operation
24Do 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
-
25Do 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
- However
- This solution relies on programmer discipline
- Language integration provides automated checking
26Outline
- The Fragile Base Class Problem
- Selective Open Recursion
- Implementation Analysis
- Future work and Conclusion
27Implementation in Java
- Extension to Barat compiler
- Bokowski Spiegel
- Rewrite calls to non-open methods on this
- Call a final method with the implementation for
the current class - Available at http//www.archjava.org/
28Open Recursion Inference
- Analysis to compute open annotations
- Method m in class C must be open if
- Method m in class C lt C calls this.m
- Class C lt C overrides m
- Class C inherits or super-calls C.m
-
29Open Recursion Inference
- Analysis to compute open annotations
- Method m in class C must be open if
- Method m in class C lt C calls this.m
- Class C lt 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
30Experiments
- Ran open recursion inference
- java. packages in JDK 1.4.2
- Except java.nio, java.sql (see paper)
31Experiments
- 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
32Frequency of Open Recursion
- 9897 methods in our portion of stdlib
- 246 (2.5) of methods were inferred open
-
33Frequency of Open Recursion
- 9897 methods in our portion of stdlib
- 246 (2.5) of methods were inferred open
- Maybe the stdlib doesnt 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
34Frequency of Open Recursion
- 9897 methods in our portion of stdlib
- 246 (2.5) of methods were inferred open
- Maybe the stdlib doesnt 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
35Optimization 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
36Outline
- The Fragile Base Class Problem
- Selective Open Recursion
- Implementation Analysis
- Future Work and Conclusion
37Future Work Application to Formal Reasoning
- Formalizing as Featherweight Java module system
-
38Future 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 -
39Future 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
40Conclusion
- 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
41(No Transcript)
42Open 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)
- countc.size()
-
- int size() return count
- // other functions
Calls to this are dispatched to subclass Thus
subclass can tell exactly when each method is
called