diff options
-rw-r--r-- | ChangeLog | 18 | ||||
-rw-r--r-- | class.c | 38 | ||||
-rw-r--r-- | method.h | 3 | ||||
-rw-r--r-- | test/ruby/test_refinement.rb | 77 | ||||
-rw-r--r-- | version.h | 2 | ||||
-rw-r--r-- | vm_method.c | 17 |
6 files changed, 133 insertions, 22 deletions
@@ -1,3 +1,21 @@ +Wed Mar 18 00:58:43 2015 Shugo Maeda <shugo@ruby-lang.org> + + * class.c (method_entry_i, class_instance_method_list, + rb_obj_singleton_methods): should not include methods of + superclasses if recur is false. [ruby-dev:48854] [Bug #10826] + +Wed Mar 18 00:58:43 2015 Shugo Maeda <shugo@ruby-lang.org> + + * vm_method.c (remove_method): When remove refined + method, raise a NameError if the method is not + defined in refined class. + + But if the method is defined in refined class, + it should keep refined method and remove original + method. + + Patch by Seiei Higa. [ruby-core:67722] [Bug #10765] + Wed Mar 18 00:32:08 2015 Seiei Higa <hanachin@gmail.com> * vm_method.c (check_definition): Module#public_method_defined?, @@ -1116,25 +1116,32 @@ ins_methods_pub_i(st_data_t name, st_data_t type, st_data_t ary) return ins_methods_push((ID)name, (long)type, (VALUE)ary, NOEX_PUBLIC); } +struct method_entry_arg { + st_table *list; + int recur; +}; + static int method_entry_i(st_data_t key, st_data_t value, st_data_t data) { const rb_method_entry_t *me = (const rb_method_entry_t *)value; - st_table *list = (st_table *)data; + struct method_entry_arg *arg = (struct method_entry_arg *)data; long type; if (me && me->def->type == VM_METHOD_TYPE_REFINED) { + VALUE klass = me->klass; me = rb_resolve_refined_method(Qnil, me, NULL); if (!me) return ST_CONTINUE; + if (!arg->recur && me->klass != klass) return ST_CONTINUE; } - if (!st_lookup(list, key, 0)) { + if (!st_lookup(arg->list, key, 0)) { if (UNDEFINED_METHOD_ENTRY_P(me)) { type = -1; /* none */ } else { type = VISI(me->flag); } - st_add_direct(list, key, type); + st_add_direct(arg->list, key, type); } return ST_CONTINUE; } @@ -1144,7 +1151,7 @@ class_instance_method_list(int argc, VALUE *argv, VALUE mod, int obj, int (*func { VALUE ary; int recur, prepended = 0; - st_table *list; + struct method_entry_arg me_arg; if (argc == 0) { recur = TRUE; @@ -1160,16 +1167,17 @@ class_instance_method_list(int argc, VALUE *argv, VALUE mod, int obj, int (*func prepended = 1; } - list = st_init_numtable(); + me_arg.list = st_init_numtable(); + me_arg.recur = recur; for (; mod; mod = RCLASS_SUPER(mod)) { - if (RCLASS_M_TBL(mod)) st_foreach(RCLASS_M_TBL(mod), method_entry_i, (st_data_t)list); + if (RCLASS_M_TBL(mod)) st_foreach(RCLASS_M_TBL(mod), method_entry_i, (st_data_t)&me_arg); if (BUILTIN_TYPE(mod) == T_ICLASS && !prepended) continue; if (obj && FL_TEST(mod, FL_SINGLETON)) continue; if (!recur) break; } ary = rb_ary_new(); - st_foreach(list, func, ary); - st_free_table(list); + st_foreach(me_arg.list, func, ary); + st_free_table(me_arg.list); return ary; } @@ -1391,7 +1399,8 @@ VALUE rb_obj_singleton_methods(int argc, VALUE *argv, VALUE obj) { VALUE recur, ary, klass, origin; - st_table *list, *mtbl; + struct method_entry_arg me_arg; + st_table *mtbl; if (argc == 0) { recur = Qtrue; @@ -1401,22 +1410,23 @@ rb_obj_singleton_methods(int argc, VALUE *argv, VALUE obj) } klass = CLASS_OF(obj); origin = RCLASS_ORIGIN(klass); - list = st_init_numtable(); + me_arg.list = st_init_numtable(); + me_arg.recur = recur; if (klass && FL_TEST(klass, FL_SINGLETON)) { if ((mtbl = RCLASS_M_TBL(origin)) != 0) - st_foreach(mtbl, method_entry_i, (st_data_t)list); + st_foreach(mtbl, method_entry_i, (st_data_t)&me_arg); klass = RCLASS_SUPER(klass); } if (RTEST(recur)) { while (klass && (FL_TEST(klass, FL_SINGLETON) || RB_TYPE_P(klass, T_ICLASS))) { if (klass != origin && (mtbl = RCLASS_M_TBL(klass)) != 0) - st_foreach(mtbl, method_entry_i, (st_data_t)list); + st_foreach(mtbl, method_entry_i, (st_data_t)&me_arg); klass = RCLASS_SUPER(klass); } } ary = rb_ary_new(); - st_foreach(list, ins_methods_i, ary); - st_free_table(list); + st_foreach(me_arg.list, ins_methods_i, ary); + st_free_table(me_arg.list); return ary; } @@ -108,6 +108,9 @@ struct unlinked_method_entry_list_entry { }; #define UNDEFINED_METHOD_ENTRY_P(me) (!(me) || !(me)->def || (me)->def->type == VM_METHOD_TYPE_UNDEF) +#define UNDEFINED_REFINED_METHOD_P(def) \ + ((def)->type == VM_METHOD_TYPE_REFINED && \ + UNDEFINED_METHOD_ENTRY_P((def)->body.orig_me)) void rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_flag_t noex); rb_method_entry_t *rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_flag_t noex); diff --git a/test/ruby/test_refinement.rb b/test/ruby/test_refinement.rb index f34a7deb54..dc2d31c867 100644 --- a/test/ruby/test_refinement.rb +++ b/test/ruby/test_refinement.rb @@ -1281,6 +1281,83 @@ class TestRefinement < Test::Unit::TestCase end; end + def test_remove_refined_method + assert_separately([], <<-"end;") + bug10765 = '[ruby-core:67722] [Bug #10765]' + + class C + def foo + "C#foo" + end + end + + module RefinementBug + refine C do + def foo + "RefinementBug#foo" + end + end + end + + using RefinementBug + + class C + remove_method :foo + end + + assert_equal("RefinementBug#foo", C.new.foo, bug10765) + end; + end + + def test_remove_undefined_refined_method + assert_separately([], <<-"end;") + bug10765 = '[ruby-core:67722] [Bug #10765]' + + class C + end + + module RefinementBug + refine C do + def foo + end + end + end + + using RefinementBug + + assert_raise(NameError, bug10765) { + class C + remove_method :foo + end + } + end; + end + + module NotIncludeSuperclassMethod + class X + def foo + end + end + + class Y < X + end + + module Bar + refine Y do + def foo + end + end + end + end + + def test_instance_methods_not_include_superclass_method + bug10826 = '[ruby-dev:48854] [Bug #10826]' + assert_not_include(NotIncludeSuperclassMethod::Y.instance_methods(false), + :foo, bug10826) + assert_include(NotIncludeSuperclassMethod::Y.instance_methods(true), + :foo, bug10826) + end + private def eval_using(mod, s) @@ -1,6 +1,6 @@ #define RUBY_VERSION "2.1.5" #define RUBY_RELEASE_DATE "2015-03-18" -#define RUBY_PATCHLEVEL 313 +#define RUBY_PATCHLEVEL 314 #define RUBY_RELEASE_YEAR 2015 #define RUBY_RELEASE_MONTH 3 diff --git a/vm_method.c b/vm_method.c index c084d52e84..84b4e86734 100644 --- a/vm_method.c +++ b/vm_method.c @@ -732,10 +732,12 @@ remove_method(VALUE klass, ID mid) if (!st_lookup(RCLASS_M_TBL(klass), mid, &data) || !(me = (rb_method_entry_t *)data) || - (!me->def || me->def->type == VM_METHOD_TYPE_UNDEF)) { + (!me->def || me->def->type == VM_METHOD_TYPE_UNDEF) || + UNDEFINED_REFINED_METHOD_P(me->def)) { rb_name_error(mid, "method `%s' not defined in %s", rb_id2name(mid), rb_class2name(klass)); } + key = (st_data_t)mid; st_delete(RCLASS_M_TBL(klass), &key, &data); @@ -743,6 +745,10 @@ remove_method(VALUE klass, ID mid) rb_clear_method_cache_by_class(klass); rb_unlink_method_entry(me); + if (me->def->type == VM_METHOD_TYPE_REFINED) { + rb_add_refined_method_entry(klass, mid); + } + CALL_METHOD_HOOK(self, removed, mid); } @@ -812,8 +818,7 @@ rb_export_method(VALUE klass, ID name, rb_method_flag_t noex) } if (UNDEFINED_METHOD_ENTRY_P(me) || - (me->def->type == VM_METHOD_TYPE_REFINED && - UNDEFINED_METHOD_ENTRY_P(me->def->body.orig_me))) { + UNDEFINED_REFINED_METHOD_P(me->def)) { rb_print_undef(klass, name, 0); } @@ -912,8 +917,7 @@ rb_undef(VALUE klass, ID id) me = search_method(klass, id, 0); if (UNDEFINED_METHOD_ENTRY_P(me) || - (me->def->type == VM_METHOD_TYPE_REFINED && - UNDEFINED_METHOD_ENTRY_P(me->def->body.orig_me))) { + UNDEFINED_REFINED_METHOD_P(me->def)) { const char *s0 = " class"; VALUE c = klass; @@ -1260,8 +1264,7 @@ rb_alias(VALUE klass, ID name, ID def) orig_me = search_method(klass, def, &defined_class); if (UNDEFINED_METHOD_ENTRY_P(orig_me) || - (orig_me->def->type == VM_METHOD_TYPE_REFINED && - UNDEFINED_METHOD_ENTRY_P(orig_me->def->body.orig_me))) { + UNDEFINED_REFINED_METHOD_P(orig_me->def)) { if ((!RB_TYPE_P(klass, T_MODULE)) || (orig_me = search_method(rb_cObject, def, 0), UNDEFINED_METHOD_ENTRY_P(orig_me))) { |