merge revision(s) 49222,49480,49493: [Backport #10765] [Backport #1010826]

method.h: UNDEFINED_REFINED_METHOD_P

	* method.h (UNDEFINED_REFINED_METHOD_P): macro to tell if refined
  original method is defined.
	* 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]

	* 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]


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_0_0@49738 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
usa 2015-02-25 06:01:25 +00:00
parent 24bb5756b1
commit 94ec0a6419
6 changed files with 139 additions and 25 deletions

View file

@ -1,3 +1,21 @@
Wed Feb 25 14:53:27 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 Feb 25 14:53:27 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 Feb 25 14:41:01 2015 Seiei Higa <hanachin@gmail.com> Wed Feb 25 14:41:01 2015 Seiei Higa <hanachin@gmail.com>
* vm_method.c (check_definition): Module#public_method_defined?, * vm_method.c (check_definition): Module#public_method_defined?,

45
class.c
View file

@ -986,25 +986,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); return ins_methods_push((ID)name, (long)type, (VALUE)ary, NOEX_PUBLIC);
} }
struct method_entry_arg {
st_table *list;
int recur;
};
static int static int
method_entry_i(st_data_t key, st_data_t value, st_data_t data) 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; 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; long type;
if (me && me->def->type == VM_METHOD_TYPE_REFINED) { if (me && me->def->type == VM_METHOD_TYPE_REFINED) {
VALUE klass = me->klass;
me = rb_resolve_refined_method(Qnil, me, NULL); me = rb_resolve_refined_method(Qnil, me, NULL);
if (!me) return ST_CONTINUE; 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)) { if (UNDEFINED_METHOD_ENTRY_P(me)) {
type = -1; /* none */ type = -1; /* none */
} }
else { else {
type = VISI(me->flag); type = VISI(me->flag);
} }
st_add_direct(list, key, type); st_add_direct(arg->list, key, type);
} }
return ST_CONTINUE; return ST_CONTINUE;
} }
@ -1014,7 +1021,7 @@ class_instance_method_list(int argc, VALUE *argv, VALUE mod, int obj, int (*func
{ {
VALUE ary; VALUE ary;
int recur, prepended = 0; int recur, prepended = 0;
st_table *list; struct method_entry_arg me_arg;
if (argc == 0) { if (argc == 0) {
recur = TRUE; recur = TRUE;
@ -1030,16 +1037,17 @@ class_instance_method_list(int argc, VALUE *argv, VALUE mod, int obj, int (*func
prepended = 1; prepended = 1;
} }
list = st_init_numtable(); me_arg.list = st_init_numtable();
me_arg.recur = recur;
for (; mod; mod = RCLASS_SUPER(mod)) { 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 (BUILTIN_TYPE(mod) == T_ICLASS && !prepended) continue;
if (obj && FL_TEST(mod, FL_SINGLETON)) continue; if (obj && FL_TEST(mod, FL_SINGLETON)) continue;
if (!recur) break; if (!recur) break;
} }
ary = rb_ary_new(); ary = rb_ary_new();
st_foreach(list, func, ary); st_foreach(me_arg.list, func, ary);
st_free_table(list); st_free_table(me_arg.list);
return ary; return ary;
} }
@ -1251,8 +1259,9 @@ rb_obj_public_methods(int argc, VALUE *argv, VALUE obj)
VALUE VALUE
rb_obj_singleton_methods(int argc, VALUE *argv, VALUE obj) rb_obj_singleton_methods(int argc, VALUE *argv, VALUE obj)
{ {
VALUE recur, ary, klass; VALUE recur, ary, klass, origin;
st_table *list; struct method_entry_arg me_arg;
st_table *mtbl;
if (argc == 0) { if (argc == 0) {
recur = Qtrue; recur = Qtrue;
@ -1261,22 +1270,24 @@ rb_obj_singleton_methods(int argc, VALUE *argv, VALUE obj)
rb_scan_args(argc, argv, "01", &recur); rb_scan_args(argc, argv, "01", &recur);
} }
klass = CLASS_OF(obj); klass = CLASS_OF(obj);
list = st_init_numtable(); origin = RCLASS_ORIGIN(klass);
me_arg.list = st_init_numtable();
me_arg.recur = RTEST(recur);
if (klass && FL_TEST(klass, FL_SINGLETON)) { if (klass && FL_TEST(klass, FL_SINGLETON)) {
if (RCLASS_M_TBL(klass)) if ((mtbl = RCLASS_M_TBL(origin)) != 0)
st_foreach(RCLASS_M_TBL(klass), method_entry_i, (st_data_t)list); st_foreach(mtbl, method_entry_i, (st_data_t)&me_arg);
klass = RCLASS_SUPER(klass); klass = RCLASS_SUPER(klass);
} }
if (RTEST(recur)) { if (RTEST(recur)) {
while (klass && (FL_TEST(klass, FL_SINGLETON) || RB_TYPE_P(klass, T_ICLASS))) { while (klass && (FL_TEST(klass, FL_SINGLETON) || RB_TYPE_P(klass, T_ICLASS))) {
if (RCLASS_M_TBL(klass)) if (klass != origin && (mtbl = RCLASS_M_TBL(klass)) != 0)
st_foreach(RCLASS_M_TBL(klass), method_entry_i, (st_data_t)list); st_foreach(mtbl, method_entry_i, (st_data_t)&me_arg);
klass = RCLASS_SUPER(klass); klass = RCLASS_SUPER(klass);
} }
} }
ary = rb_ary_new(); ary = rb_ary_new();
st_foreach(list, ins_methods_i, ary); st_foreach(me_arg.list, ins_methods_i, ary);
st_free_table(list); st_free_table(me_arg.list);
return ary; return ary;
} }

View file

@ -106,6 +106,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_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); 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); rb_method_entry_t *rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_flag_t noex);

View file

@ -1120,6 +1120,85 @@ class TestRefinement < Test::Unit::TestCase
end; end;
end end
def test_remove_refined_method
assert_separately([], <<-"end;")
$VERBOSE = nil
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;")
$VERBOSE = nil
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 private
def eval_using(mod, s) def eval_using(mod, s)

View file

@ -1,6 +1,6 @@
#define RUBY_VERSION "2.0.0" #define RUBY_VERSION "2.0.0"
#define RUBY_RELEASE_DATE "2015-02-25" #define RUBY_RELEASE_DATE "2015-02-25"
#define RUBY_PATCHLEVEL 640 #define RUBY_PATCHLEVEL 641
#define RUBY_RELEASE_YEAR 2015 #define RUBY_RELEASE_YEAR 2015
#define RUBY_RELEASE_MONTH 2 #define RUBY_RELEASE_MONTH 2

View file

@ -690,10 +690,12 @@ remove_method(VALUE klass, ID mid)
if (!st_lookup(RCLASS_M_TBL(klass), mid, &data) || if (!st_lookup(RCLASS_M_TBL(klass), mid, &data) ||
!(me = (rb_method_entry_t *)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_name_error(mid, "method `%s' not defined in %s",
rb_id2name(mid), rb_class2name(klass)); rb_id2name(mid), rb_class2name(klass));
} }
key = (st_data_t)mid; key = (st_data_t)mid;
st_delete(RCLASS_M_TBL(klass), &key, &data); st_delete(RCLASS_M_TBL(klass), &key, &data);
@ -701,6 +703,10 @@ remove_method(VALUE klass, ID mid)
rb_clear_cache_for_undef(klass, mid); rb_clear_cache_for_undef(klass, mid);
rb_unlink_method_entry(me); 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); CALL_METHOD_HOOK(self, removed, mid);
} }
@ -772,8 +778,7 @@ rb_export_method(VALUE klass, ID name, rb_method_flag_t noex)
} }
if (UNDEFINED_METHOD_ENTRY_P(me) || if (UNDEFINED_METHOD_ENTRY_P(me) ||
(me->def->type == VM_METHOD_TYPE_REFINED && UNDEFINED_REFINED_METHOD_P(me->def)) {
UNDEFINED_METHOD_ENTRY_P(me->def->body.orig_me))) {
rb_print_undef(klass, name, 0); rb_print_undef(klass, name, 0);
} }
@ -881,8 +886,7 @@ rb_undef(VALUE klass, ID id)
me = search_method(klass, id, 0); me = search_method(klass, id, 0);
if (UNDEFINED_METHOD_ENTRY_P(me) || if (UNDEFINED_METHOD_ENTRY_P(me) ||
(me->def->type == VM_METHOD_TYPE_REFINED && UNDEFINED_REFINED_METHOD_P(me->def)) {
UNDEFINED_METHOD_ENTRY_P(me->def->body.orig_me))) {
const char *s0 = " class"; const char *s0 = " class";
VALUE c = klass; VALUE c = klass;
@ -1222,8 +1226,7 @@ rb_alias(VALUE klass, ID name, ID def)
orig_me = search_method(klass, def, &defined_class); orig_me = search_method(klass, def, &defined_class);
if (UNDEFINED_METHOD_ENTRY_P(orig_me) || if (UNDEFINED_METHOD_ENTRY_P(orig_me) ||
(orig_me->def->type == VM_METHOD_TYPE_REFINED && UNDEFINED_REFINED_METHOD_P(orig_me->def)) {
UNDEFINED_METHOD_ENTRY_P(orig_me->def->body.orig_me))) {
if ((!RB_TYPE_P(klass, T_MODULE)) || if ((!RB_TYPE_P(klass, T_MODULE)) ||
(orig_me = search_method(rb_cObject, def, 0), (orig_me = search_method(rb_cObject, def, 0),
UNDEFINED_METHOD_ENTRY_P(orig_me))) { UNDEFINED_METHOD_ENTRY_P(orig_me))) {