Project

General

Profile

Bug #10943

Updated by ko1 (Koichi Sasada) over 9 years ago

# Abstract 

 Singleton class scopes should make their own namespace for constants.  
 However, Ruby versions from Ruby 1.9.0 do not respect this specification. 

 # Background and Problem 

 Singleton class is useful feature to define object specific methods, especially for Class objects. 

 ```ruby 
 obj = Object.new 

 # Define something obj's singleton class. 
 class << obj 
   def foo 
   end 
 end 

 # Idiom to make class methods 
 class C 
   class << self 
     def foo        # Define C.foo method 
     end 
   end 
 end 
 ``` 

 A `class` syntax has another job: making new namespace, especially for constants. 

 ```ruby 
 class C 
   CONST = 1 
   p CONST      #=> 1 
   def foo 
     p CONST    #=> 1 
   end 
 end 
 ``` 

 Singleton class defintion should also introduce namespace for constant. 

 ```ruby 
 obj = Object.new 
 class << obj 
   CONST = 1 
   def foo 
     CONST 
   end 
 end 

 p obj.foo #=> 1 
 ``` 

 No problem. 

 Problem is sharing a singleton class definition with multiple objects. 

 ```ruby 
 objs = [] 
 $i = 0 

 2.times{ 
   objs << obj = Object.new 
   class << obj 
     CONST = ($i += 1) 
     def foo 
       CONST 
     end 
   end 
 } 

 p objs[0].foo 
 p objs[1].foo 
 ``` 

 Please think about the answers (outputs). 
 The above code makes two singleton classes independently. 
 So that constant namespace should be made for each singleton classes. 

 In fact, before Ruby 1.9.0, this program outputs "1\n2\n". Maybe your answer is same. 

 However after Ruby 1.9.0, this program outputs "2\n2\n". This is a bug (not intentional behavior). 
 JRuby and Rubinius also output not correct answers (interestingly JRuby prints "1\n1"). 

 ``` 
 $ ruby -v t.rb 
 ruby 2.3.0dev (2015-01-27 trunk 49421) [x86_64-linux] 
 2 
 2 
 $ jruby-1.7.19/bin/jruby -v t.rb 
 jruby 1.7.19 (1.9.3p551) 2015-01-29 20786bd on OpenJDK 64-Bit Server VM 1.7.0_75-b13 +jit [linux-amd64] 
 1 
 1 
 $ jruby-9.0.0.0.pre1/bin/jruby -v t.rb 
 jruby 9.0.0.0.pre1 (2.2.0p0) 2015-01-20 d537cab OpenJDK 64-Bit Server VM 24.75-b04 on 1.7.0_75-b13 +jit [linux-amd64] 
 1 
 1 
 $ rbx-2.5.2/bin/rbx -v t.rb 
 rubinius 2.5.2 (2.1.0 7a5b05b1 2015-01-30 3.4 JI) [x86_64-linux-gnu] 
 2 
 2 
 ``` 

 I asked Matz and his answer is "This is a bug behavior". 

 Moreover, on the independent namespace can make new classes/modules. 

 ```ruby 
 obj = Object.new 
 class << obj 
   class X 
     # make <singleton class::X> 
   end 
 end 
 ``` 

 and it also has problem with multiple definitions. 

 ```ruby 
 objs = [] 
 $xs = [] 
 $i = 0 

 2.times{ 
   objs << obj = Object.new 
   class << obj 
     CONST = ($i += 1) 
     class X 
       $xs << self 
       CONST = ($i += 1) 
       def foo 
         CONST 
       end 
     end 

     def x 
       X 
     end 
   end 
 } 

 p $xs         #=> [#<Class:0x25d56cc>::X, #<Class:0x25d55f0>::X] 
 p objs[0].x #=> #<Class:0x25d55f0>::X <- should be #<Class:0x25d56cc>::X 
 p objs[1].x #=> #<Class:0x25d55f0>::X 
 p $xs[0].new.foo #=> 4 <- should be 2 
 p $xs[1].new.foo $xs[0].new.foo #=> 4 
 ``` 

 This is a bug. 

 (BTW, mruby works correctly!) 

 # Reason of this behavior 

 On MRI, the reason of this bug is wrong sharing a namespace with  
 multiple namespaces. On MRI, the term "CREF" is a name of namespace data  
 structure. 

 Simply saying, I couldn't recognize such case, sharing a namespace by  
 multiple namespaces. I was surprising that each singleton class  
 expression make their own namespace. So that I store CREF data into each  
 local ISeq (method bytecode). It means that each bytecode knows their  
 own CREF. I had believed that one bytecode only has one namespace (CREF).  
 But this assumption is not correct, as I described above. 

 # Solution 

 I decided to renew this feature. ISeq data should not have their own  
 CREF, but method only should have. Push CREF onto each method frame  
 (value stack, same location of SVAR). There is a (not small) patch and I  
 will commit soon for Ruby 2.3. 

 # Previous versions 

 This is bug fix, but it changes Ruby semantics largely. I'm not sure how  
 to treat it. 

 Matz said that "we can not expect how affect this change for existing applications, so that no  
 need for older versions". 

 Please discuss about it. 

 # Acknowledgement 

 This bug was found during investigating [Bug #10871]. 

Back