diff options
author | nobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2012-07-02 08:06:37 +0000 |
---|---|---|
committer | nobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2012-07-02 08:06:37 +0000 |
commit | edb1fc4eb2f9b0bd69398362ae40100adfd5577e (patch) | |
tree | 48a12ca98a1780f14164a016b6da7c4707b5a2de | |
parent | bd52bed97be25e8f3344033ac01416349ec8be67 (diff) | |
download | ruby-edb1fc4eb2f9b0bd69398362ae40100adfd5577e.tar.gz |
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
-rw-r--r-- | ChangeLog | 14 | ||||
-rw-r--r-- | class.c | 54 | ||||
-rw-r--r-- | test/ruby/test_module.rb | 43 | ||||
-rw-r--r-- | vm_method.c | 12 |
4 files changed, 68 insertions, 55 deletions
@@ -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]. @@ -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); } } diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb index 8e3dbbc34f..06ab6cba9f 100644 --- a/test/ruby/test_module.rb +++ b/test/ruby/test_module.rb @@ -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) diff --git a/vm_method.c b/vm_method.c index 7546a1c31c..af7c99a95f 100644 --- a/vm_method.c +++ b/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; |