270 likes | 389 Vues
Method Shelters: Avoiding Conflicts among Class Extensions Caused by Local Rebinding. Shumpei Akai , Shigeru Chiba Tokyo Institute of Technology. Class Extensions. Destructively change method definitions in the existing classes Write new definitions in a separate file Available in
E N D
Method Shelters: Avoiding Conflicts among Class Extensions Caused by Local Rebinding Shumpei Akai , Shigeru Chiba Tokyo Institute of Technology
Class Extensions • Destructively change method definitions in the existing classes • Write new definitions in a separate file • Available in • Smalltalk, Objective-C, AspectJ … • and Ruby • (called Open Class)
Class Extensions are popular in Ruby • Ruby on Rails aggressively adds methods to built-in classes • Class extensions are used in real applications • e.g. 10.kilobytes # => NoMethodError: undefined method require “active_record” #load rails’ lib 10.kilobytes # => 10240
Conflicts among Class Extensions • Class extensions are dangerous • Cause conflicts, of course • Ruby allows class extensions • Ruby on Rails aggressively use • Serious issue in Ruby • Scope of class extensions are needed
What is the appropriate scope of class extensions? • Global? • Same as Ruby • Lexical? • Redefinitions are available in a lexical scope • Local rebinding property (of Classbox *)? • Redefine methods in another module by importing • Redefinition is limited in imported module * [‘05 Bergel et al.]
Module 1 Global Scopes Integer div(): return integer plus(): … minus(): … Module 2 No one can use original div() Integer Redefine destructively div(): return rational List avg(): average of elems using div 1.div(2) #=> (1/2) Module 3
Module 1 Lexical Scopes Integer div(): return integer plus(): … minus(): … Module 2 Integer Redefinitionin Lexical scope div(): return rational List avg(): average of elems using div [1, 2].avg() #=> (3/2) 1.div(2) #=> 0 Module 3 [1, 2].avg() #=> (3/2) 1.div(2) #=> 0 Cannot reuse redefined div()
Module 1 Local rebinding(Classbox) Integer div(): return integer plus(): … minus(): … Redefinitionin importing chain import Module 2 Integer div(): return rational import List avg(): average of elems using div Module 4 [1, 2].avg() #=> (3/2) 1.div(2) #=> (1/2) Module 3 1.div(2) #=> 0 Original div() Redefined div()
Module 1 Local rebinding(Classbox) Integer div(): return integer plus(): … minus(): … Module 2 Integer div(): return rational List avg(): average of elems using div Module 3 [1, 2].avg() #=> (3/2) 1.div(2) #=> Conflicts Module 4 1.div(2) #=> (1/2) [1, 2].avg() #=> (3/2)
Our proposal: Method Shelters • A method shelter is a module which provides a way to control scopes of class extensions • 2 mechanisms • Preserves local rebinding • Or redefine methods in limited scope • Based on Ruby
A Code with Method Shelters shelter:MathN do class Fixnum # fixed size integer in Ruby def /(x) Rational(self,x) end end end shelter :Average do class Array defavg sum = self.inject(0){|r,i|r+i} sum / self.size end end hide import :MathN end
Chambers in a method shelter • A method shelter has two parts • An exposed chamber and a hidden chamber • Each chamber contains methods and “import”s • Exposed : for public APIs (similar to public) • Hidden : for internal use (similar to protected) Exposed Import Hidden - Obj#m0 S0
Exposed Chambers - Obj#m0 S0 • for public API • Local rebinding • Methods • Visible from importer • Import • Imported methods are also visible from importer Call/redefine import S1 Call/redefine import S2
Call Hidden chamber - Obj#m0 - Obj#m1 S0 • for internally used methods • Not called/redefined from importer • Method • Visible only from the same shelter • Import • Imported methods are not visible from importer import Call S1 S2
Method Lookup Algorithm • Contexts : • (class, method name, current shelter) • Search for methods as follows: • 1. look up the current shelter’s hidden-chamber and its importing shelters • 2. look up the current shelter’s exposed-chamber and its importing shelters • 3.If not found, go to the superclass
- m0 - m1 Second, current First, look up this group *Look up from importer
Detect Ambiguity • If you use exposed chambers, it may cause conflicts • Detects and raises an error - C#m0 S3 - C#m0 S1 S2 Error! S0
Syntax • We have not modified syntax • Ruby has powerful syntax • Use ordinal methods with a block shelter :ShelterName do class Foo defhoge# <- defined in the method shelter end end end
Syntax: Import shelter :ShelterName do import :AnotherShelterName end
Syntax: hide • “hide” method switches a chamber • Methods and imports below “hide”are in the hidden chamber shelter :ShelterName do # exposed chamber hide # hidden chamber end
Example of method shelters Shelter1 Integer Sheleter 2 div(): return integer plus(): … minus(): … Integer div(): return rational Sheleter 3 List avg(): average of elems using div Shelter4 [1, 2].avg() #=> (3/2) 1.div(2) #=> 0
Example of method shelters Shelter1 Integer Sheleter 2 div(): return integer plus(): … minus(): … Integer div(): return rational Sheleter 3 List avg(): average of elems using div Shelter4 [1, 2].avg() #=> (3/2) 1.div(2) #=> (1/2)
Implementation • Based on Ruby 1.9.2 • Add one implicit argument to method: • Current method shelter • Optimize method-lookup caches • Shelter node cache • Caches method body in a shelter • Extend inline cache • Stores the found shelter • Size of an inline cache : 3 word -> 4word (per method call)
Micro benchmark : empty methods • Call an empty method in shelter 10,000,000 times • Less than 5% overhead when shelters are used
Micro benchmark : Ruby on Rails • Enabled shelters in an action method • Numeric#kilobytes method in a shelter • In the action method • 1. Call kilobytes method in shelter • 2. One access to SQLite 4% overhead
Related Work • Refinements (for Ruby) • Provide a scope of methods • Redefined methods are available in lexical scope • No local rebinding • Classboxes[‘05 Bergelet al.] • A classboxprovides the scope of class extensions • Introduce Local rebinding property
Conclusion • Method shelters provide 2 mechanisms for implementing scope of class extensions • Exposed and Hidden • You can control scopes of class extensions by combining them • Avoid conflicts • Implementation on Ruby • reasonable overhead by caches