Bug #22475 ยป fix_prepend_duplicate.patch
| class.c | ||
|---|---|---|
|
/* During lazy sweeping, the entry could be a dead object that
|
||
|
* has not yet been swept. */
|
||
|
if (!rb_objspace_garbage_object_p(subclass)) {
|
||
|
/* An origin iclass backfilled by a previous prepend is
|
||
|
* registered in the subclasses list only so that method
|
||
|
* cache invalidation can reach it. The module is inserted
|
||
|
* between the includer's iclass and that origin when the
|
||
|
* includer's iclass is visited, so visiting the origin too
|
||
|
* would insert the module a second time. */
|
||
|
if (RICLASS_IS_ORIGIN_P(subclass)) continue;
|
||
|
if (klass_had_no_origin && klass_origin_m_tbl == RCLASS_M_TBL(subclass)) {
|
||
|
// backfill an origin iclass to handle refinements and future prepends
|
||
|
rb_id_table_foreach(RCLASS_M_TBL(subclass), clear_module_cache_i, (void *)subclass);
|
||
| test/ruby/test_module.rb | ||
|---|---|---|
|
assert_equal([m, c], c.ancestors[0, 2], "should never duplicate")
|
||
|
end
|
||
|
def test_prepend_to_included_module_multiple_times
|
||
|
m = labeled_module("M")
|
||
|
c = labeled_class("C") {include m}
|
||
|
m0 = labeled_module("M0")
|
||
|
m1 = labeled_module("M1")
|
||
|
m2 = labeled_module("M2")
|
||
|
m.prepend m0
|
||
|
m.prepend m1
|
||
|
m.prepend m2
|
||
|
assert_equal([c, m2, m1, m0, m, Object], c.ancestors[0, 6], "should never duplicate")
|
||
|
base = Module.new {def m; [:base] end}
|
||
|
h = Module.new {include base}
|
||
|
view = Class.new {include h}
|
||
|
p1 = Module.new {def m; [:p1] + super end}
|
||
|
p2 = Module.new {def m; [:p2] + super end}
|
||
|
h.prepend p1
|
||
|
h.prepend p2
|
||
|
assert_equal([:p2, :p1, :base], view.new.m)
|
||
|
end
|
||
|
def test_prepend_in_superclass
|
||
|
m = labeled_module("M")
|
||
|
c1 = labeled_class("C1")
|
||