summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean byroot Boussier <jean.boussier+github@shopify.com>2022-12-01 23:32:41 +0100
committerGitHub <noreply@github.com>2022-12-01 17:32:41 -0500
commit3d272b0fc822f5c2e9c716377b7fcecf15426b27 (patch)
tree71b970741b78968c45d9aa5758c616948148ab69
parent9da2a5204f32a4f2ce135fddde2abb6e07d647e9 (diff)
downloadruby-3d272b0fc822f5c2e9c716377b7fcecf15426b27.tar.gz
Module#remove_method: Check frozen on the right object
Previously, the frozen check happened on `RCLASS_ORIGIN(self)`, which can return an iclass. The frozen check is supposed to respond to objects that users can call methods on while iclasses are hidden from users. Other mutation methods like Module#{define_method,alias_method,public} don't do this. Check frozen status on the module itself. Fixes [Bug #19164] and [Bug #19166]. Co-authored-by: Alan Wu <XrXr@users.noreply.github.com>
-rw-r--r--test/ruby/test_class.rb9
-rw-r--r--test/ruby/test_module.rb12
-rw-r--r--vm_method.c2
3 files changed, 22 insertions, 1 deletions
diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb
index 8d9fde144e..5086e60398 100644
--- a/test/ruby/test_class.rb
+++ b/test/ruby/test_class.rb
@@ -809,4 +809,13 @@ PREP
3_000_000.times(&code)
CODE
end
+
+ def test_instance_freeze_dont_freeze_the_class_bug_19164
+ klass = Class.new
+ klass.prepend(Module.new)
+
+ klass.new.freeze
+ klass.define_method(:bar) {}
+ assert_equal klass, klass.remove_method(:bar), '[Bug #19164]'
+ end
end
diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb
index 981c1394a2..6c4defd6b1 100644
--- a/test/ruby/test_module.rb
+++ b/test/ruby/test_module.rb
@@ -2350,6 +2350,18 @@ class TestModule < Test::Unit::TestCase
assert_equal(:foo, removed)
end
+ def test_frozen_prepend_remove_method
+ [Module, Class].each do |klass|
+ mod = klass.new do
+ prepend(Module.new)
+ def foo; end
+ end
+ mod.freeze
+ assert_raise(FrozenError, '[Bug #19166]') { mod.send(:remove_method, :foo) }
+ assert_equal([:foo], mod.instance_methods(false))
+ end
+ end
+
def test_prepend_class_ancestors
bug6658 = '[ruby-core:45919]'
m = labeled_module("m")
diff --git a/vm_method.c b/vm_method.c
index f1c50b9300..2c5ccdb3d7 100644
--- a/vm_method.c
+++ b/vm_method.c
@@ -1541,8 +1541,8 @@ remove_method(VALUE klass, ID mid)
rb_method_entry_t *me = 0;
VALUE self = klass;
- klass = RCLASS_ORIGIN(klass);
rb_class_modify_check(klass);
+ klass = RCLASS_ORIGIN(klass);
if (mid == object_id || mid == id__send__ || mid == idInitialize) {
rb_warn("removing `%s' may cause serious problems", rb_id2name(mid));
}