https://bugs.ruby-lang.org/https://bugs.ruby-lang.org/favicon.ico?17113305112014-03-06T19:06:23ZRuby Issue Tracking SystemRuby master - Bug #9603: unusual reference class-variable with cloned class.https://bugs.ruby-lang.org/issues/9603?journal_id=456622014-03-06T19:06:23ZChiether (Norikaz Ishii)chiether+ruby@gmail.com
<ul></ul><p>maybe...<br>
<del><br>
first cloned-class write to original-class's class-variable; but new instance reference cloned-class's class-variable.<br>
second cloned-class reference original-class that class-variable updated by first cloned-class.<br>
</del><br>
.new has problem when cloned class.</p>
<a name="additional-example-code"></a>
<h2 >additional example code<a href="#additional-example-code" class="wiki-anchor">¶</a></h2>
<pre><code>class A
@@value = 1
def self.make_clone
puts self.inspect # => A
return A.clone
end
def self.check
puts self.inspect # => #<Class:0x007f7ce1160b38>
puts self.data.inspect # => 1
puts A.data.inspect # => 1
@@value = 2
puts self.data.inspect # => 2
puts A.data.inspect # => 2 ((weak)except:1, cloned-class reference A? let's say its good for example.)
instance = new
puts instance.inspect # => #<#<Class:0x007f7ce1160b38>:0x007f7ce1543d18>
puts instance.data.inspect # => 1 (expected:2)
puts instance.class.inspect # => #<Class:0x007f7ce1160b38>
puts instance.class.data.inspect # => 2
puts instance.class.new.inspect # => #<#<Class:0x007f7ce1160b38>:0x007f7ce1543b88>
puts instance.class.new.data # => 1 (expected:2)
return instance
end
def self.data
@@value
end
def data
@@value
end
end
A.make_clone.check
</code></pre> Ruby master - Bug #9603: unusual reference class-variable with cloned class.https://bugs.ruby-lang.org/issues/9603?journal_id=814982019-09-10T05:04:28Zjeremyevans0 (Jeremy Evans)merch-redmine@jeremyevans.net
<ul><li><strong>Status</strong> changed from <i>Open</i> to <i>Rejected</i></li><li><strong>Backport</strong> deleted (<del><i>1.9.3: UNKNOWN, 2.0.0: UNKNOWN, 2.1: UNKNOWN</i></del>)</li></ul><p>While not intuitive, I think this behavior is expected and not a bug. It is not relating to class cloning, it is due to class variable lookup. Normal class variable lookup (e.g. not using <code>Module#class_variable_{g,s}et</code>), uses crefs, making it more similar to constant lookup than method lookup. It skips crefs added by singleton classes as well as crefs added by *eval. See <code>vm_get_cvar_base</code> in <code>vm_inshelper.c</code> for the algorithm used to find the base class used for class variable lookup. Class variable lookup will look in the ancestors of the base class for the class variable.</p>
<p>Taking the original post as an example. The first time <code>B.make_clone.make_instance</code> is called, it runs <code>@@value = 2</code>. At the point in that call, the active cref is <code>C</code> (not the clone of <code>C</code> created by <code>B.make_clone</code>). So at that point, it sets <code>@@value = 2</code> in <code>C</code>, not in the clone of <code>C</code>. When <code>data</code> is called on that it, that is a regular method and not a singleton method, so the class variable lookup uses the cloned class variable and not the original class variable in C. The second time <code>B.make_clone.make_instance</code> is run, the value for <code>@@value</code> has already been set to <code>2</code> in <code>C</code>, so there is no behavior change.</p>
<p>Here's an example allowing you to see the difference:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">B</span>
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">make_clone</span>
<span class="no">C</span><span class="p">.</span><span class="nf">clone</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">C</span>
<span class="vc">@@value</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">make_instance</span>
<span class="vc">@@value</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="p">[</span><span class="n">data</span><span class="p">,</span> <span class="n">new</span><span class="p">.</span><span class="nf">data</span><span class="p">]</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">data</span>
<span class="vc">@@value</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">data</span>
<span class="vc">@@value</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="nb">p</span> <span class="no">B</span><span class="p">.</span><span class="nf">make_clone</span><span class="p">.</span><span class="nf">make_instance</span>
<span class="nb">p</span> <span class="no">B</span><span class="p">.</span><span class="nf">make_clone</span><span class="p">.</span><span class="nf">make_instance</span>
<span class="nb">p</span> <span class="no">B</span><span class="o">::</span><span class="no">C</span><span class="p">.</span><span class="nf">make_instance</span>
<span class="nb">p</span> <span class="no">B</span><span class="p">.</span><span class="nf">make_clone</span><span class="p">.</span><span class="nf">make_instance</span>
</code></pre>
<p>output is:</p>
<pre><code>[2, 1]
[3, 2]
[4, 4]
[5, 4]
</code></pre>
<p>To show this is not related to cloning, here's a similar example that does not use cloning:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">A</span>
<span class="vc">@@value</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">def</span> <span class="nf">foo</span>
<span class="vc">@@value</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">B</span>
<span class="vc">@@value</span> <span class="o">=</span> <span class="mi">2</span>
<span class="k">def</span> <span class="nc">A</span><span class="o">.</span><span class="nf">bar</span>
<span class="vc">@@value</span> <span class="o">=</span> <span class="mi">3</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">foo</span>
<span class="vc">@@value</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="nb">p</span> <span class="no">A</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">foo</span>
<span class="nb">p</span> <span class="no">B</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">foo</span>
<span class="no">A</span><span class="p">.</span><span class="nf">bar</span>
<span class="nb">p</span> <span class="no">A</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">foo</span>
<span class="nb">p</span> <span class="no">B</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">foo</span>
</code></pre>
<p>The output for this program is:</p>
<pre><code>1
2
1
3
</code></pre>
<p>Note how the call to <code>A.bar</code> set the <code>@@value</code> for <code>B</code>, not <code>A</code>.</p>