From 7470f965650bf17875632f0c5f9e5a4d9de9fc3f Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Fri, 9 Aug 2019 16:44:43 -0700 Subject: [PATCH] Fix Module#class_variables for singleton classes of classes/modules Module#class_variables should reflect class variable lookup. For singleton classes of classes/modules, this means the lookup should be: * Singleton Class * Class * All Ancestors of Class Note that this doesn't include modules included in the singleton class, because class variable lookup doesn't include those. Singleton classes of other objects do not have this behavior and always just search all ancestors of the singleton class, so do not change the behavior for them. Fixes [Bug #8297] --- test/ruby/test_variable.rb | 28 ++++++++++++++++++++++++++++ variable.c | 6 ++++++ 2 files changed, 34 insertions(+) diff --git a/test/ruby/test_variable.rb b/test/ruby/test_variable.rb index aa301f2bc7..f14b4019df 100644 --- a/test/ruby/test_variable.rb +++ b/test/ruby/test_variable.rb @@ -35,6 +35,34 @@ class TestVariable < Test::Unit::TestCase end end + def test_singleton_class_included_class_variable + c = Class.new + c.extend(Olympians) + assert_empty(c.singleton_class.class_variables) + assert_raise(NameError){ c.singleton_class.class_variable_get(:@@rule) } + c.class_variable_set(:@@foo, 1) + assert_equal([:@@foo], c.singleton_class.class_variables) + assert_equal(1, c.singleton_class.class_variable_get(:@@foo)) + + c = Class.new + c.extend(Olympians) + sc = Class.new(c) + assert_empty(sc.singleton_class.class_variables) + assert_raise(NameError){ sc.singleton_class.class_variable_get(:@@rule) } + c.class_variable_set(:@@foo, 1) + assert_equal([:@@foo], sc.singleton_class.class_variables) + assert_equal(1, sc.singleton_class.class_variable_get(:@@foo)) + + c = Class.new + o = c.new + o.extend(Olympians) + assert_equal([:@@rule], o.singleton_class.class_variables) + assert_equal("Zeus", o.singleton_class.class_variable_get(:@@rule)) + c.class_variable_set(:@@foo, 1) + assert_equal([:@@foo, :@@rule], o.singleton_class.class_variables.sort) + assert_equal(1, o.singleton_class.class_variable_get(:@@foo)) + end + def test_variable assert_instance_of(Integer, $$) diff --git a/variable.c b/variable.c index f3d73fac63..1627467bd9 100644 --- a/variable.c +++ b/variable.c @@ -3209,6 +3209,12 @@ static void* mod_cvar_of(VALUE mod, void *data) { VALUE tmp = mod; + if (FL_TEST(mod, FL_SINGLETON)) { + if (rb_namespace_p(rb_ivar_get(mod, id__attached__))) { + data = mod_cvar_at(tmp, data); + tmp = cvar_front_klass(tmp); + } + } for (;;) { data = mod_cvar_at(tmp, data); tmp = RCLASS_SUPER(tmp);