[前][次][番号順一覧][スレッド一覧]

ruby-changes:68022

From: Nobuyoshi <ko1@a...>
Date: Fri, 17 Sep 2021 19:05:21 +0900 (JST)
Subject: [ruby-changes:68022] 178ee1e801 (master): Already initialized modules cannot be replaced [Bug #17048]

https://git.ruby-lang.org/ruby.git/commit/?id=178ee1e801

From 178ee1e801acb33d13b3e8a630f6ca4926c68fbc Mon Sep 17 00:00:00 2001
From: Nobuyoshi Nakada <nobu@r...>
Date: Sun, 26 Jul 2020 11:52:19 +0900
Subject: Already initialized modules cannot be replaced [Bug #17048]

---
 class.c                  | 43 ++++++++++++++++++++++++++++++++++++++++++-
 internal/class.h         |  3 +++
 object.c                 | 24 +++++++++---------------
 test/ruby/test_module.rb | 40 ++++++++++++++++++++++++++--------------
 4 files changed, 80 insertions(+), 30 deletions(-)

diff --git a/class.c b/class.c
index d2dd438..0b075a9 100644
--- a/class.c
+++ b/class.c
@@ -248,6 +248,12 @@ rb_class_new(VALUE super) https://github.com/ruby/ruby/blob/trunk/class.c#L248
     return rb_class_boot(super);
 }
 
+VALUE
+rb_class_s_alloc(VALUE klass)
+{
+    return rb_class_boot(0);
+}
+
 static void
 clone_method(VALUE old_klass, VALUE new_klass, ID mid, const rb_method_entry_t *me)
 {
@@ -345,12 +351,35 @@ copy_tables(VALUE clone, VALUE orig) https://github.com/ruby/ruby/blob/trunk/class.c#L351
 
 static bool ensure_origin(VALUE klass);
 
+static inline bool
+RMODULE_UNINITIALIZED(VALUE module)
+{
+    return RCLASS_SUPER(module) == rb_cBasicObject;
+}
+
+void
+rb_module_check_initialiable(VALUE mod)
+{
+    if (!RMODULE_UNINITIALIZED(mod)) {
+        rb_raise(rb_eTypeError, "already initialized module");
+    }
+    RB_OBJ_WRITE(mod, &RCLASS(mod)->super, 0);
+}
+
 /* :nodoc: */
 VALUE
 rb_mod_init_copy(VALUE clone, VALUE orig)
 {
-    if (RB_TYPE_P(clone, T_CLASS)) {
+    switch (BUILTIN_TYPE(clone)) {
+      case T_CLASS:
+      case T_ICLASS:
         class_init_copy_check(clone, orig);
+        break;
+      case T_MODULE:
+        rb_module_check_initialiable(clone);
+        break;
+      default:
+        break;
     }
     if (!OBJ_INIT_COPY(clone, orig)) return clone;
 
@@ -775,6 +804,15 @@ rb_define_class_id_under(VALUE outer, ID id, VALUE super) https://github.com/ruby/ruby/blob/trunk/class.c#L804
 }
 
 VALUE
+rb_module_s_alloc(VALUE klass)
+{
+    VALUE mod = class_alloc(T_MODULE, klass);
+    RCLASS_M_TBL_INIT(mod);
+    RB_OBJ_WRITE(mod, &RCLASS(mod)->super, rb_cBasicObject);
+    return mod;
+}
+
+VALUE
 rb_module_new(void)
 {
     VALUE mdl = class_alloc(T_MODULE, rb_cModule);
@@ -878,6 +916,9 @@ ensure_includable(VALUE klass, VALUE module) https://github.com/ruby/ruby/blob/trunk/class.c#L916
 {
     rb_class_modify_check(klass);
     Check_Type(module, T_MODULE);
+    if (RMODULE_UNINITIALIZED(module)) {
+        rb_raise(rb_eArgError, "uninitialized module");
+    }
     if (!NIL_P(rb_refinement_module_get_refined_class(module))) {
 	rb_raise(rb_eArgError, "refinement module is not allowed");
     }
diff --git a/internal/class.h b/internal/class.h
index 00ee62f..a7f7e56 100644
--- a/internal/class.h
+++ b/internal/class.h
@@ -113,6 +113,9 @@ void rb_class_subclass_add(VALUE super, VALUE klass); https://github.com/ruby/ruby/blob/trunk/internal/class.h#L113
 void rb_class_remove_from_super_subclasses(VALUE);
 int rb_singleton_class_internal_p(VALUE sklass);
 VALUE rb_class_boot(VALUE);
+VALUE rb_class_s_alloc(VALUE klass);
+VALUE rb_module_s_alloc(VALUE klass);
+void rb_module_check_initialiable(VALUE module);
 VALUE rb_make_metaclass(VALUE, VALUE);
 VALUE rb_include_class_new(VALUE, VALUE);
 void rb_class_foreach_subclass(VALUE klass, void (*f)(VALUE, VALUE), VALUE);
diff --git a/object.c b/object.c
index c6d9b58..5263618 100644
--- a/object.c
+++ b/object.c
@@ -1766,20 +1766,7 @@ rb_mod_cmp(VALUE mod, VALUE arg) https://github.com/ruby/ruby/blob/trunk/object.c#L1766
     return INT2FIX(1);
 }
 
-static VALUE
-rb_module_s_alloc(VALUE klass)
-{
-    VALUE mod = rb_module_new();
-
-    RBASIC_SET_CLASS(mod, klass);
-    return mod;
-}
-
-static VALUE
-rb_class_s_alloc(VALUE klass)
-{
-    return rb_class_boot(0);
-}
+static VALUE rb_mod_initialize_exec(VALUE module);
 
 /*
  *  call-seq:
@@ -1810,6 +1797,13 @@ rb_class_s_alloc(VALUE klass) https://github.com/ruby/ruby/blob/trunk/object.c#L1797
 static VALUE
 rb_mod_initialize(VALUE module)
 {
+    rb_module_check_initialiable(module);
+    return rb_mod_initialize_exec(module);
+}
+
+static VALUE
+rb_mod_initialize_exec(VALUE module)
+{
     if (rb_block_given_p()) {
 	rb_mod_module_exec(1, &module, module);
     }
@@ -1879,7 +1873,7 @@ rb_class_initialize(int argc, VALUE *argv, VALUE klass) https://github.com/ruby/ruby/blob/trunk/object.c#L1873
     RCLASS_SET_SUPER(klass, super);
     rb_make_metaclass(klass, RBASIC(super)->klass);
     rb_class_inherited(super, klass);
-    rb_mod_initialize(klass);
+    rb_mod_initialize_exec(klass);
 
     return klass;
 }
diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb
index 98c9128..bbe84cd 100644
--- a/test/ruby/test_module.rb
+++ b/test/ruby/test_module.rb
@@ -404,19 +404,20 @@ class TestModule < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_module.rb#L404
     assert_equal([:MIXIN, :USER], User.constants.sort)
   end
 
-  def test_self_initialize_copy
-    bug9535 = '[ruby-dev:47989] [Bug #9535]'
-    m = Module.new do
-      def foo
-        :ok
-      end
-      initialize_copy(self)
+  def test_initialize_copy
+    mod = Module.new { define_method(:foo) {:first} }
+    klass = Class.new { include mod }
+    instance = klass.new
+    assert_equal(:first, instance.foo)
+    new_mod = Module.new { define_method(:foo) { :second } }
+    assert_raise(TypeError) do
+      mod.send(:initialize_copy, new_mod)
     end
-    assert_equal(:ok, Object.new.extend(m).foo, bug9535)
+    4.times { GC.start }
+    assert_equal(:first, instance.foo) # [BUG] unreachable
   end
 
   def test_initialize_copy_empty
-    bug9813 = '[ruby-dev:48182] [Bug #9813]'
     m = Module.new do
       def x
       end
@@ -426,12 +427,11 @@ class TestModule < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_module.rb#L427
     assert_equal([:x], m.instance_methods)
     assert_equal([:@x], m.instance_variables)
     assert_equal([:X], m.constants)
-    m.module_eval do
-      initialize_copy(Module.new)
+    assert_raise(TypeError) do
+      m.module_eval do
+        initialize_copy(Module.new)
+      end
     end
-    assert_empty(m.instance_methods, bug9813)
-    assert_empty(m.instance_variables, bug9813)
-    assert_empty(m.constants, bug9813)
   end
 
   def test_dup
@@ -3094,6 +3094,18 @@ class TestModule < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_module.rb#L3094
     assert_match(/::Foo$/, mod.name, '[Bug #14895]')
   end
 
+  def test_include_allocated
+    assert_raise(ArgumentError) do
+      Module.new {include Module.allocate}
+    end
+    assert_raise(ArgumentError) do
+      Module.new {prepend Module.allocate}
+    end
+    assert_raise(ArgumentError) do
+      Object.new.extend Module.allocate
+    end
+  end
+
   private
 
   def assert_top_method_is_private(method)
-- 
cgit v1.1


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]