diff options
author | Alan Wu <XrXr@users.noreply.github.com> | 2020-06-10 18:44:52 -0400 |
---|---|---|
committer | Alan Wu <XrXr@users.noreply.github.com> | 2020-06-11 14:46:08 -0400 |
commit | e100fcbdd1e81c23e3b4c87964aff210232903a8 (patch) | |
tree | 500c79c58a55359574d3008a8d9a0dce9cfe5492 | |
parent | 631c01f5ae64d1034ec719c9f97da906e7f508b3 (diff) | |
download | ruby-e100fcbdd1e81c23e3b4c87964aff210232903a8.tar.gz |
Prohibit setting class variable on frozen module through inheritance
Setting class varibles goes through the ancestor list which can
contain iclasses. Iclasses share a lot of information with the
module they are made from, but not the frozen status.
Check the frozen status of the module instead of the iclass.
-rw-r--r-- | test/ruby/test_variable.rb | 10 | ||||
-rw-r--r-- | variable.c | 3 |
2 files changed, 13 insertions, 0 deletions
diff --git a/test/ruby/test_variable.rb b/test/ruby/test_variable.rb index b053e11607..685a06226f 100644 --- a/test/ruby/test_variable.rb +++ b/test/ruby/test_variable.rb @@ -35,6 +35,16 @@ class TestVariable < Test::Unit::TestCase end end + def test_setting_class_variable_on_module_through_inheritance + mod = Module.new + mod.class_variable_set(:@@foo, 1) + mod.freeze + c = Class.new { include(mod) } + assert_raise(FrozenError) { c.class_variable_set(:@@foo, 2) } + assert_raise(FrozenError) { c.class_eval("@@foo = 2") } + assert_equal(1, c.class_variable_get(:@@foo)) + end + def test_singleton_class_included_class_variable c = Class.new c.extend(Olympians) diff --git a/variable.c b/variable.c index ebf23fab67..0d8a424438 100644 --- a/variable.c +++ b/variable.c @@ -3143,6 +3143,9 @@ rb_cvar_set(VALUE klass, ID id, VALUE val) target = tmp; } + if (RB_TYPE_P(target, T_ICLASS)) { + target = RBASIC(target)->klass; + } check_before_mod_set(target, id, val, "class variable"); if (!RCLASS_IV_TBL(target)) { RCLASS_IV_TBL(target) = st_init_numtable(); |