270 likes | 282 Views
Learn how to use method shelters to prevent conflicts caused by class extensions. Discover the appropriate scope for class extensions and explore the mechanisms of method shelters. Examples and syntax provided.
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