mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
prepend: fix mixing with include
* class.c (rb_include_module): include modules after the origin. * class.c (include_modules_at): skip prepended modules. * class.c (rb_prepend_module): now basic.klass in ICLASS refers the old original class/module. [ruby-dev:45868][Bug #6662] * class.c (rb_mod_ancestors): ditto. * vm_method.c (search_method): search method entry from the origin iclass. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36266 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
bd52bed97b
commit
edb1fc4eb2
4 changed files with 68 additions and 55 deletions
14
ChangeLog
14
ChangeLog
|
@ -1,3 +1,17 @@
|
|||
Mon Jul 2 17:06:32 2012 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||
|
||||
* class.c (rb_include_module): include modules after the origin.
|
||||
|
||||
* class.c (include_modules_at): skip prepended modules.
|
||||
|
||||
* class.c (rb_prepend_module): now basic.klass in ICLASS refers the
|
||||
old original class/module. [ruby-dev:45868][Bug #6662]
|
||||
|
||||
* class.c (rb_mod_ancestors): ditto.
|
||||
|
||||
* vm_method.c (search_method): search method entry from the origin
|
||||
iclass.
|
||||
|
||||
Mon Jul 2 05:54:58 2012 Tadayoshi Funaba <tadf@dotrb.org>
|
||||
|
||||
* ext/date/date_core.c: [ruby-core:46058].
|
||||
|
|
54
class.c
54
class.c
|
@ -668,7 +668,9 @@ rb_include_module(VALUE klass, VALUE module)
|
|||
|
||||
OBJ_INFECT(klass, module);
|
||||
|
||||
changed = include_modules_at(klass, klass, module);
|
||||
changed = include_modules_at(klass, RCLASS_ORIGIN(klass), module);
|
||||
if (changed < 0)
|
||||
rb_raise(rb_eArgError, "cyclic include detected");
|
||||
if (changed) rb_clear_cache();
|
||||
}
|
||||
|
||||
|
@ -681,8 +683,10 @@ include_modules_at(VALUE klass, VALUE c, VALUE module)
|
|||
while (module) {
|
||||
int superclass_seen = FALSE;
|
||||
|
||||
if (RCLASS_M_TBL(klass) == RCLASS_M_TBL(module))
|
||||
rb_raise(rb_eArgError, "cyclic include detected");
|
||||
if (RCLASS_ORIGIN(module) != module)
|
||||
goto skip;
|
||||
if (RCLASS_M_TBL(klass) && RCLASS_M_TBL(klass) == RCLASS_M_TBL(module))
|
||||
return -1;
|
||||
/* ignore if the module included already in superclasses */
|
||||
for (p = RCLASS_SUPER(klass); p; p = RCLASS_SUPER(p)) {
|
||||
switch (BUILTIN_TYPE(p)) {
|
||||
|
@ -699,8 +703,6 @@ include_modules_at(VALUE klass, VALUE c, VALUE module)
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (c == klass)
|
||||
c = RCLASS_ORIGIN(klass);
|
||||
c = RCLASS_SUPER(c) = include_class_new(module, RCLASS_SUPER(c));
|
||||
if (RMODULE_M_TBL(module) && RMODULE_M_TBL(module)->num_entries)
|
||||
changed = 1;
|
||||
|
@ -714,7 +716,7 @@ include_modules_at(VALUE klass, VALUE c, VALUE module)
|
|||
void
|
||||
rb_prepend_module(VALUE klass, VALUE module)
|
||||
{
|
||||
VALUE p, c, origin;
|
||||
VALUE origin;
|
||||
int changed = 0;
|
||||
|
||||
rb_frozen_class_p(klass);
|
||||
|
@ -725,32 +727,19 @@ rb_prepend_module(VALUE klass, VALUE module)
|
|||
Check_Type(module, T_MODULE);
|
||||
|
||||
OBJ_INFECT(klass, module);
|
||||
c = RCLASS_SUPER(klass);
|
||||
if (RCLASS_M_TBL(klass) == RCLASS_M_TBL(module))
|
||||
rb_raise(rb_eArgError, "cyclic prepend detected");
|
||||
for (p = c; p; p = RCLASS_SUPER(p)) {
|
||||
if (BUILTIN_TYPE(p) == T_ICLASS) {
|
||||
if (RCLASS_M_TBL(p) == RCLASS_M_TBL(module)) {
|
||||
rb_raise(rb_eArgError, "already prepended module");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
origin = RCLASS_ORIGIN(klass);
|
||||
if (origin == klass) {
|
||||
origin = class_alloc(T_ICLASS, rb_cClass);
|
||||
origin = class_alloc(T_ICLASS, klass);
|
||||
RCLASS_SUPER(origin) = RCLASS_SUPER(klass);
|
||||
RCLASS_SUPER(klass) = origin;
|
||||
RCLASS_ORIGIN(klass) = origin;
|
||||
RCLASS_M_TBL(origin) = RCLASS_M_TBL(klass);
|
||||
RCLASS_M_TBL(klass) = 0;
|
||||
c = origin;
|
||||
}
|
||||
RCLASS_SUPER(klass) = include_class_new(module, c);
|
||||
if (RCLASS_SUPER(module)) {
|
||||
changed = include_modules_at(klass, RCLASS_SUPER(klass), RCLASS_SUPER(module));
|
||||
}
|
||||
if (!changed)
|
||||
changed = RMODULE_M_TBL(module) && RMODULE_M_TBL(module)->num_entries;
|
||||
changed = include_modules_at(klass, klass, module);
|
||||
if (changed < 0)
|
||||
rb_raise(rb_eArgError, "cyclic prepend detected");
|
||||
if (changed) rb_clear_cache();
|
||||
}
|
||||
|
||||
|
@ -838,25 +827,14 @@ VALUE
|
|||
rb_mod_ancestors(VALUE mod)
|
||||
{
|
||||
VALUE p, ary = rb_ary_new();
|
||||
VALUE origin = RCLASS_ORIGIN(mod);
|
||||
|
||||
p = mod;
|
||||
if (origin == mod) {
|
||||
origin = 0;
|
||||
}
|
||||
else {
|
||||
p = RCLASS_SUPER(p);
|
||||
}
|
||||
for (; p; p = RCLASS_SUPER(p)) {
|
||||
for (p = mod; p; p = RCLASS_SUPER(p)) {
|
||||
if (FL_TEST(p, FL_SINGLETON))
|
||||
continue;
|
||||
if (p == origin) {
|
||||
rb_ary_push(ary, mod);
|
||||
}
|
||||
else if (BUILTIN_TYPE(p) == T_ICLASS) {
|
||||
if (BUILTIN_TYPE(p) == T_ICLASS) {
|
||||
rb_ary_push(ary, RBASIC(p)->klass);
|
||||
}
|
||||
else {
|
||||
else if (p == RCLASS_ORIGIN(p)) {
|
||||
rb_ary_push(ary, p);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1243,31 +1243,37 @@ class TestModule < Test::Unit::TestCase
|
|||
def m1; [:M0] end
|
||||
end
|
||||
module M1
|
||||
def m1; [:M1, super, :M1] end
|
||||
def m1; [:M1, *super] end
|
||||
end
|
||||
module M2
|
||||
def m1; [:M2, super, :M2] end
|
||||
def m1; [:M2, *super] end
|
||||
end
|
||||
M3 = Module.new do
|
||||
def m1; [:M3, super, :M3] end
|
||||
def m1; [:M3, *super] end
|
||||
end
|
||||
module M4
|
||||
def m1; [:M4, super, :M4] end
|
||||
def m1; [:M4, *super] end
|
||||
end
|
||||
class C0
|
||||
class C
|
||||
def m1; end
|
||||
end
|
||||
class C0 < C
|
||||
include M0
|
||||
prepend M1
|
||||
def m1; [:C0, super, :C0] end
|
||||
def m1; [:C0, *super] end
|
||||
end
|
||||
class C1 < C0
|
||||
prepend M2, M3
|
||||
include M4
|
||||
def m1; [:C1, super, :C1] end
|
||||
def m1; [:C1, *super] end
|
||||
end
|
||||
|
||||
def test_prepend
|
||||
obj = C0.new
|
||||
expected = [:M1,:C0,:M0]
|
||||
assert_equal(expected, obj.m1)
|
||||
obj = C1.new
|
||||
expected = [:M2,[:M3,[:C1,[:M4,[:M1,[:C0,[:M0],:C0],:M1],:M4],:C1],:M3],:M2]
|
||||
expected = [:M2,:M3,:C1,:M4,:M1,:C0,:M0]
|
||||
assert_equal(expected, obj.m1)
|
||||
end
|
||||
|
||||
|
@ -1305,13 +1311,30 @@ class TestModule < Test::Unit::TestCase
|
|||
m = labeled_module("m")
|
||||
c = labeled_class("c") {prepend m}
|
||||
assert_equal([m, c], c.ancestors[0, 2], bug6658)
|
||||
|
||||
bug6662 = '[ruby-dev:45868]'
|
||||
c2 = labeled_class("c2", c)
|
||||
anc = c2.ancestors
|
||||
assert_equal([c2, m, c, Object], anc[0..anc.index(Object)], bug6662)
|
||||
end
|
||||
|
||||
def test_prepend_module_ancestors
|
||||
bug6659 = '[ruby-dev:45861]'
|
||||
m0 = labeled_module("m0")
|
||||
m1 = labeled_module("m1") {prepend m0}
|
||||
m0 = labeled_module("m0") {def x; [:m0, *super] end}
|
||||
m1 = labeled_module("m1") {def x; [:m1, *super] end; prepend m0}
|
||||
m2 = labeled_module("m2") {def x; [:m2, *super] end; prepend m1}
|
||||
c0 = labeled_class("c0") {def x; [:c0] end}
|
||||
c1 = labeled_class("c1") {def x; [:c1] end; prepend m2}
|
||||
c2 = labeled_class("c2", c0) {def x; [:c2, *super] end; include m2}
|
||||
|
||||
assert_equal([m0, m1], m1.ancestors, bug6659)
|
||||
|
||||
bug6662 = '[ruby-dev:45868]'
|
||||
assert_equal([m0, m1, m2], m2.ancestors, bug6662)
|
||||
assert_equal([m0, m1, m2, c1], c1.ancestors[0, 4], bug6662)
|
||||
assert_equal([:m0, :m1, :m2, :c1], c1.new.x)
|
||||
assert_equal([c2, m0, m1, m2, c0], c2.ancestors[0, 5], bug6662)
|
||||
assert_equal([:c2, :m0, :m1, :m2, :c0], c2.new.x)
|
||||
end
|
||||
|
||||
def labeled_module(name, &block)
|
||||
|
|
12
vm_method.c
12
vm_method.c
|
@ -387,15 +387,13 @@ static rb_method_entry_t*
|
|||
search_method(VALUE klass, ID id)
|
||||
{
|
||||
st_data_t body;
|
||||
if (!klass) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (!RCLASS_M_TBL(klass) || !st_lookup(RCLASS_M_TBL(klass), id, &body)) {
|
||||
klass = RCLASS_SUPER(klass);
|
||||
if (!klass) {
|
||||
return 0;
|
||||
for (body = 0; klass; klass = RCLASS_SUPER(klass)) {
|
||||
st_table *m_tbl = RCLASS_M_TBL(klass);
|
||||
if (!m_tbl) {
|
||||
m_tbl = RCLASS_M_TBL(RCLASS_ORIGIN(RBASIC(klass)->klass));
|
||||
}
|
||||
if (st_lookup(m_tbl, id, &body)) break;
|
||||
}
|
||||
|
||||
return (rb_method_entry_t *)body;
|
||||
|
|
Loading…
Reference in a new issue