Skip to content

Commit 5c7dfe8

Browse files
authored
Initialize class dup/clone before calling initialize_dup/initialize_clone
Previously, you could override the class initialize_dup/initialize_clone method and the class hierarchy would not be set correctly inside the method before calling super. This removes Module#initialize_copy, and instead makes Object#dup/clone call the underlying C function (rb_mod_init_copy) before calling the appropriate initialize_dup/initialize_clone method. This results in the following fixes: * The appropriate initialize_dup method is called (dup on a class will respect superclass initialize_dup). * Inside class initialize_dup/initialize_clone/initialize_copy, class ancestor hierarchy is correct. * Calling singleton_class inside initialize_dup no longer raises a TypeError later in dup. * Calling singleton_class.ancestors inside initialize_dup no longer results in missing ancestors. Fixes [Bug #21538]
1 parent dd4e780 commit 5c7dfe8

File tree

3 files changed

+41
-10
lines changed

3 files changed

+41
-10
lines changed

object.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ init_copy(VALUE dest, VALUE obj)
409409
break;
410410
case T_CLASS:
411411
case T_MODULE:
412-
// noop: handled in class.c: rb_mod_init_copy
412+
rb_mod_init_copy(dest, obj);
413413
break;
414414
case T_OBJECT:
415415
rb_obj_copy_ivar(dest, obj);
@@ -4571,7 +4571,6 @@ InitVM_Object(void)
45714571
rb_define_method(rb_cModule, "<=", rb_class_inherited_p, 1);
45724572
rb_define_method(rb_cModule, ">", rb_mod_gt, 1);
45734573
rb_define_method(rb_cModule, ">=", rb_mod_ge, 1);
4574-
rb_define_method(rb_cModule, "initialize_copy", rb_mod_init_copy, 1); /* in class.c */
45754574
rb_define_method(rb_cModule, "to_s", rb_mod_to_s, 0);
45764575
rb_define_alias(rb_cModule, "inspect", "to_s");
45774576
rb_define_method(rb_cModule, "included_modules", rb_mod_included_modules, 0); /* in class.c */

test/ruby/test_class.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,46 @@ def test_initialize_copy
259259
assert_raise(TypeError) { BasicObject.dup }
260260
end
261261

262+
def test_class_hierarchy_inside_initialize_dup_bug_21538
263+
ancestors = sc_ancestors = nil
264+
b = Class.new
265+
b.define_singleton_method(:initialize_dup) do |x|
266+
ancestors = self.ancestors
267+
sc_ancestors = singleton_class.ancestors
268+
super(x)
269+
end
270+
271+
a = Class.new(b)
272+
273+
c = a.dup
274+
275+
expected_ancestors = [c, b, *Object.ancestors]
276+
expected_sc_ancestors = [c.singleton_class, b.singleton_class, *Object.singleton_class.ancestors]
277+
assert_equal expected_ancestors, ancestors
278+
assert_equal expected_sc_ancestors, sc_ancestors
279+
assert_equal expected_ancestors, c.ancestors
280+
assert_equal expected_sc_ancestors, c.singleton_class.ancestors
281+
end
282+
283+
def test_class_hierarchy_inside_initialize_clone_bug_21538
284+
ancestors = sc_ancestors = nil
285+
a = Class.new
286+
a.define_singleton_method(:initialize_clone) do |x|
287+
ancestors = self.ancestors
288+
sc_ancestors = singleton_class.ancestors
289+
super(x)
290+
end
291+
292+
c = a.clone
293+
294+
expected_ancestors = [c, *Object.ancestors]
295+
expected_sc_ancestors = [c.singleton_class, *Object.singleton_class.ancestors]
296+
assert_equal expected_ancestors, ancestors
297+
assert_equal expected_sc_ancestors, sc_ancestors
298+
assert_equal expected_ancestors, c.ancestors
299+
assert_equal expected_sc_ancestors, c.singleton_class.ancestors
300+
end
301+
262302
def test_singleton_class
263303
assert_raise(TypeError) { 1.extend(Module.new) }
264304
assert_raise(TypeError) { 1.0.extend(Module.new) }

test/ruby/test_module.rb

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -418,9 +418,6 @@ def test_initialize_copy
418418
instance = klass.new
419419
assert_equal(:first, instance.foo)
420420
new_mod = Module.new { define_method(:foo) { :second } }
421-
assert_raise(TypeError) do
422-
mod.send(:initialize_copy, new_mod)
423-
end
424421
4.times { GC.start }
425422
assert_equal(:first, instance.foo) # [BUG] unreachable
426423
end
@@ -435,11 +432,6 @@ def x
435432
assert_equal([:x], m.instance_methods)
436433
assert_equal([:@x], m.instance_variables)
437434
assert_equal([:X], m.constants)
438-
assert_raise(TypeError) do
439-
m.module_eval do
440-
initialize_copy(Module.new)
441-
end
442-
end
443435

444436
m = Class.new(Module) do
445437
def initialize_copy(other)

0 commit comments

Comments
 (0)