aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNobuyoshi Nakada <nobu@ruby-lang.org>2020-07-26 11:52:19 +0900
committerNobuyoshi Nakada <nobu@ruby-lang.org>2021-09-17 11:14:04 +0900
commit178ee1e801acb33d13b3e8a630f6ca4926c68fbc (patch)
treefbc3b2dd31c4a3007580959b79ad08c4dbf36029
parent8f41c791b19a47e2dfa39b0a6d12ef964098536a (diff)
downloadruby-178ee1e801acb33d13b3e8a630f6ca4926c68fbc.tar.gz
Already initialized modules cannot be replaced [Bug #17048]
-rw-r--r--class.c43
-rw-r--r--internal/class.h3
-rw-r--r--object.c24
-rw-r--r--test/ruby/test_module.rb40
4 files changed, 80 insertions, 30 deletions
diff --git a/class.c b/class.c
index d2dd43815c..0b075a9abb 100644
--- a/class.c
+++ b/class.c
@@ -248,6 +248,12 @@ rb_class_new(VALUE super)
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)
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)
}
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)
{
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 00ee62facf..a7f7e56340 100644
--- a/internal/class.h
+++ b/internal/class.h
@@ -113,6 +113,9 @@ void rb_class_subclass_add(VALUE super, VALUE klass);
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 c6d9b58d39..5263618697 100644
--- a/object.c
+++ b/object.c
@@ -1766,20 +1766,7 @@ rb_mod_cmp(VALUE mod, VALUE arg)
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)
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)
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 98c9128406..bbe84cd9ff 100644
--- a/test/ruby/test_module.rb
+++ b/test/ruby/test_module.rb
@@ -404,19 +404,20 @@ class TestModule < Test::Unit::TestCase
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
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
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)