ruby-changes:25936
From: shugo <ko1@a...>
Date: Thu, 29 Nov 2012 21:50:37 +0900 (JST)
Subject: [ruby-changes:25936] shugo:r37993 (trunk): * vm_method.c (rb_method_entry_make): add a method entry with
shugo 2012-11-29 21:50:10 +0900 (Thu, 29 Nov 2012) New Revision: 37993 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=37993 Log: * vm_method.c (rb_method_entry_make): add a method entry with VM_METHOD_TYPE_REFINED to the class refined by the refinement if the target module is a refinement. When a method entry with VM_METHOD_TYPE_UNDEF is invoked by vm_call_method(), a method with the same name is searched in refinements. If such a method is found, the method is invoked. Otherwise, the original method in the refined class (rb_method_definition_t::body.orig_def) is invoked. This change is made to simplify the normal method lookup and to improve the performance of normal method calls. * vm_method.c (EXPR1, search_method, rb_method_entry), vm_eval.c (rb_call0, rb_search_method_entry): do not use refinements for method lookup. * vm_insnhelper.c (vm_call_method): search methods in refinements if ci->me is VM_METHOD_TYPE_REFINED. If the method is called by super (i.e., ci->call == vm_call_super_method), skip the same method entry as the current method to avoid infinite call of the same method. * class.c (include_modules_at): add a refined method entry for each method defined in a module included in a refinement. * class.c (rb_prepend_module): set an empty table to RCLASS_M_TBL(klass) to add refined method entries, because refinements should have priority over prepended modules. * proc.c (mnew): use rb_method_entry_with_refinements() to get a refined method. * test/ruby/test_refinement.rb (test_inline_method_cache): do not skip the test because it should pass successfully. * test/ruby/test_refinement.rb (test_redefine_refined_method): new test for the case a refined method is redefined. Modified files: trunk/ChangeLog trunk/class.c trunk/eval.c trunk/gc.c trunk/internal.h trunk/method.h trunk/object.c trunk/proc.c trunk/test/ruby/test_refinement.rb trunk/vm_eval.c trunk/vm_insnhelper.c trunk/vm_method.c Index: method.h =================================================================== --- method.h (revision 37992) +++ method.h (revision 37993) @@ -42,7 +42,8 @@ VM_METHOD_TYPE_NOTIMPLEMENTED, VM_METHOD_TYPE_OPTIMIZED, /* Kernel#send, Proc#call, etc */ VM_METHOD_TYPE_MISSING, /* wrapper for method_missing(id) */ - VM_METHOD_TYPE_CFUNC_FRAMELESS + VM_METHOD_TYPE_CFUNC_FRAMELESS, + VM_METHOD_TYPE_REFINED, } rb_method_type_t; struct rb_call_info_struct; @@ -72,6 +73,7 @@ OPTIMIZED_METHOD_TYPE_SEND, OPTIMIZED_METHOD_TYPE_CALL } optimize_type; + struct rb_method_definition_struct *orig_def; } body; int alias_count; } rb_method_definition_t; @@ -94,9 +96,16 @@ 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_method_entry(VALUE klass, ID id, VALUE *define_class_ptr); +void rb_add_refined_method_entry(VALUE refined_class, ID mid); +rb_method_entry_t *rb_resolve_refined_method(VALUE refinements, + rb_method_entry_t *me, + rb_method_entry_t *me_buf, + VALUE *defined_class_ptr); +rb_method_entry_t *rb_method_entry_with_refinements(VALUE klass, ID id, + rb_method_entry_t *me_buf, + VALUE *defined_class_ptr); -rb_method_entry_t *rb_method_entry_get_with_refinements(VALUE refinements, VALUE klass, ID id, VALUE *define_class_ptr); -rb_method_entry_t *rb_method_entry_get_without_cache(VALUE klass, VALUE refinements, ID id, VALUE *define_class_ptr); +rb_method_entry_t *rb_method_entry_get_without_cache(VALUE klass, ID id, VALUE *define_class_ptr); rb_method_entry_t *rb_method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *, rb_method_flag_t noex); int rb_method_entry_arity(const rb_method_entry_t *me); Index: ChangeLog =================================================================== --- ChangeLog (revision 37992) +++ ChangeLog (revision 37993) @@ -1,3 +1,41 @@ +Thu Nov 29 21:42:16 2012 Shugo Maeda <shugo@r...> + + * vm_method.c (rb_method_entry_make): add a method entry with + VM_METHOD_TYPE_REFINED to the class refined by the refinement if + the target module is a refinement. When a method entry with + VM_METHOD_TYPE_UNDEF is invoked by vm_call_method(), a method with + the same name is searched in refinements. If such a method is + found, the method is invoked. Otherwise, the original method in + the refined class (rb_method_definition_t::body.orig_def) is + invoked. This change is made to simplify the normal method lookup + and to improve the performance of normal method calls. + + * vm_method.c (EXPR1, search_method, rb_method_entry), + vm_eval.c (rb_call0, rb_search_method_entry): do not use + refinements for method lookup. + + * vm_insnhelper.c (vm_call_method): search methods in refinements if + ci->me is VM_METHOD_TYPE_REFINED. If the method is called by + super (i.e., ci->call == vm_call_super_method), skip the same + method entry as the current method to avoid infinite call of the + same method. + + * class.c (include_modules_at): add a refined method entry for each + method defined in a module included in a refinement. + + * class.c (rb_prepend_module): set an empty table to + RCLASS_M_TBL(klass) to add refined method entries, because + refinements should have priority over prepended modules. + + * proc.c (mnew): use rb_method_entry_with_refinements() to get + a refined method. + + * test/ruby/test_refinement.rb (test_inline_method_cache): do not skip + the test because it should pass successfully. + + * test/ruby/test_refinement.rb (test_redefine_refined_method): new + test for the case a refined method is redefined. + Thu Nov 29 17:45:10 2012 Nobuyoshi Nakada <nobu@r...> * variable.c (rb_const_set): show namespace in warning messages. Index: object.c =================================================================== --- object.c (revision 37992) +++ object.c (revision 37993) @@ -1337,7 +1337,7 @@ static VALUE rb_mod_to_s(VALUE klass) { - ID id_refined_class, id_defined_at; + ID id_defined_at; VALUE refined_class, defined_at; if (FL_TEST(klass, FL_SINGLETON)) { @@ -1357,8 +1357,7 @@ return s; } - CONST_ID(id_refined_class, "__refined_class__"); - refined_class = rb_attr_get(klass, id_refined_class); + refined_class = rb_refinement_module_get_refined_class(klass); if (!NIL_P(refined_class)) { VALUE s = rb_usascii_str_new2("#<refinement:"); Index: vm_eval.c =================================================================== --- vm_eval.c (revision 37992) +++ vm_eval.c (revision 37993) @@ -179,7 +179,17 @@ case VM_METHOD_TYPE_BMETHOD: return vm_call_bmethod_body(th, ci, argv); case VM_METHOD_TYPE_ZSUPER: + case VM_METHOD_TYPE_REFINED: { + if (ci->me->def->type == VM_METHOD_TYPE_REFINED && + ci->me->def->body.orig_def) { + rb_method_entry_t orig_me; + orig_me = *ci->me; + orig_me.def = ci->me->def->body.orig_def; + ci->me = &orig_me; + goto again; + } + ci->defined_class = RCLASS_SUPER(ci->defined_class); if (!ci->defined_class || !(ci->me = rb_method_entry(ci->defined_class, ci->mid, &ci->defined_class))) { @@ -274,8 +284,7 @@ } static inline rb_method_entry_t * - rb_search_method_entry(VALUE refinements, VALUE recv, ID mid, - VALUE *defined_class_ptr); + rb_search_method_entry(VALUE recv, ID mid, VALUE *defined_class_ptr); static inline int rb_method_call_status(rb_thread_t *th, const rb_method_entry_t *me, call_type scope, VALUE self); #define NOEX_OK NOEX_NOSUPER @@ -295,11 +304,11 @@ */ static inline VALUE rb_call0(VALUE recv, ID mid, int argc, const VALUE *argv, - call_type scope, VALUE self, VALUE refinements) + call_type scope, VALUE self) { VALUE defined_class; rb_method_entry_t *me = - rb_search_method_entry(refinements, recv, mid, &defined_class); + rb_search_method_entry(recv, mid, &defined_class); rb_thread_t *th = GET_THREAD(); int call_status = rb_method_call_status(th, me, scope, self); @@ -363,7 +372,7 @@ } } - me = rb_search_method_entry(Qnil, recv, mid, &defined_class); + me = rb_search_method_entry(recv, mid, &defined_class); call_status = rb_method_call_status(th, me, CALL_FCALL, Qundef); if (call_status != NOEX_OK) { if (rb_method_basic_definition_p(klass, idMethodMissing)) { @@ -428,8 +437,7 @@ } static inline rb_method_entry_t * -rb_search_method_entry(VALUE refinements, VALUE recv, ID mid, - VALUE *defined_class_ptr) +rb_search_method_entry(VALUE recv, ID mid, VALUE *defined_class_ptr) { VALUE klass = CLASS_OF(recv); @@ -468,8 +476,7 @@ rb_id2name(mid), type, (void *)recv, flags, klass); } } - return rb_method_entry_get_with_refinements(refinements, klass, mid, - defined_class_ptr); + return rb_method_entry(klass, mid, defined_class_ptr); } static inline int @@ -533,7 +540,7 @@ static inline VALUE rb_call(VALUE recv, ID mid, int argc, const VALUE *argv, call_type scope) { - return rb_call0(recv, mid, argc, argv, scope, Qundef, Qnil); + return rb_call0(recv, mid, argc, argv, scope, Qundef); } NORETURN(static void raise_method_missing(rb_thread_t *th, int argc, const VALUE *argv, @@ -786,10 +793,24 @@ const VALUE *argv, VALUE refinements) { + VALUE defined_class; + rb_method_entry_t meb, *me = + rb_search_method_entry(recv, mid, &defined_class); + rb_thread_t *th; + int call_status; + + if (me && me->def->type == VM_METHOD_TYPE_REFINED) { + me = rb_resolve_refined_method(refinements, me, &meb, + &defined_class); + } PASS_PASSED_BLOCK_TH(GET_THREAD()); - - return rb_call0(recv, mid, argc, argv, CALL_PUBLIC, Qundef, - refinements); + th = GET_THREAD(); + call_status = rb_method_call_status(th, me, CALL_PUBLIC, Qundef); + if (call_status != NOEX_OK) { + return method_missing(recv, mid, argc, argv, call_status); + } + stack_check(); + return vm_call0(th, recv, mid, argc, argv, me, defined_class); } static VALUE @@ -816,7 +837,7 @@ id = rb_to_id(vid); } PASS_PASSED_BLOCK_TH(th); - return rb_call0(recv, id, argc, argv, scope, self, Qnil); + return rb_call0(recv, id, argc, argv, scope, self); } /* Index: proc.c =================================================================== --- proc.c (revision 37992) +++ proc.c (revision 37993) @@ -917,7 +917,7 @@ rb_method_flag_t flag = NOEX_UNDEF; again: - me = rb_method_entry(klass, id, &defined_class); + me = rb_method_entry_with_refinements(klass, id, &meb, &defined_class); if (UNDEFINED_METHOD_ENTRY_P(me)) { ID rmiss = rb_intern("respond_to_missing?"); VALUE sym = ID2SYM(id); @@ -1682,6 +1682,8 @@ default: break; } + case VM_METHOD_TYPE_REFINED: + return -1; } } rb_bug("rb_method_entry_arity: invalid method entry type (%d)", def->type); Index: vm_method.c =================================================================== --- vm_method.c (revision 37992) +++ vm_method.c (revision 37993) @@ -4,7 +4,7 @@ #define CACHE_SIZE 0x800 #define CACHE_MASK 0x7ff -#define EXPR1(c,o,m) ((((c)>>3)^((o)>>3)^(m))&CACHE_MASK) +#define EXPR1(c,m) ((((c)>>3)^(m))&CACHE_MASK) #define NOEX_NOREDEF 0 #ifndef NOEX_NOREDEF @@ -21,7 +21,6 @@ VALUE filled_version; /* filled state version */ ID mid; /* method's id */ VALUE klass; /* receiver's class */ - VALUE refinements; /* refinements */ rb_method_entry_t *me; VALUE defined_class; }; @@ -155,6 +154,12 @@ if (def) { if (def->alias_count == 0) { + if (def->type == VM_METHOD_TYPE_REFINED) { + def->body.orig_def->alias_count--; + if (def->body.orig_def->alias_count == 0) { + xfree(def->body.orig_def); + } + } xfree(def); } else if (def->alias_count > 0) { @@ -167,8 +172,51 @@ static int rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_definition_t *d2); -void rb_redefine_opt_method(VALUE, ID); +static inline rb_method_entry_t * +lookup_method_table(VALUE klass, ID id) +{ + st_data_t body; + st_table *m_tbl = RCLASS_M_TBL(klass); + if (st_lookup(m_tbl, id, &body)) { + return (rb_method_entry_t *) body; + } + else { + return 0; + } +} +static void +make_method_entry_refined(rb_method_entry_t *me) +{ + rb_method_definition_t *new_def; + + if (me->def && me->def->type == VM_METHOD_TYPE_REFINED) + return; + + new_def = ALLOC(rb_method_definition_t); + new_def->type = VM_METHOD_TYPE_REFINED; + new_def->original_id = me->called_id; + new_def->alias_count = 0; + new_def->body.orig_def = me->def; + rb_vm_check_redefinition_opt_method(me, me->klass); + if (me->def) me->def->alias_count++; + me->def = new_def; +} + +void +rb_add_refined_method_entry(VALUE refined_class, ID mid) +{ + rb_method_entry_t *me = lookup_method_table(refined_class, mid); + + if (me) { + make_method_entry_refined(me); + } + else { + rb_add_method(refined_class, mid, VM_METHOD_TYPE_REFINED, 0, + NOEX_PUBLIC); + } +} + static rb_method_entry_t * rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type, rb_method_definition_t *def, rb_method_flag_t noex) @@ -179,6 +227,7 @@ #endif st_table *mtbl; st_data_t data; + int make_refined = 0; if (NIL_P(klass)) { klass = rb_cObject; @@ -199,14 +248,19 @@ rklass = klass; #endif if (FL_TEST(klass, RMODULE_IS_REFINEMENT)) { - ID id_refined_class; - VALUE refined_class; + VALUE refined_class = + rb_refinement_module_get_refined_class(klass); - CONST_ID(id_refined_class, "__refined_class__"); - refined_class = rb_ivar_get(klass, id_refined_class); - rb_redefine_opt_method(refined_class, mid); + rb_add_refined_method_entry(refined_class, mid); } - klass = RCLASS_ORIGIN(klass); + if (type == VM_METHOD_TYPE_REFINED) { + rb_method_entry_t *old_me = + lookup_method_table(RCLASS_ORIGIN(klass), mid); + if (old_me) rb_vm_check_redefinition_opt_method(old_me, klass); + } + else { + klass = RCLASS_ORIGIN(klass); + } mtbl = RCLASS_M_TBL(klass); /* check re-definition */ @@ -222,6 +276,8 @@ } #endif rb_vm_check_redefinition_opt_method(old_me, klass); + if (old_def->type == VM_METHOD_TYPE_REFINED) + make_refined = 1; if (RTEST(ruby_verbose) && type != VM_METHOD_TYPE_UNDEF && @@ -274,6 +330,10 @@ } } + if (make_refined) { + make_method_entry_refined(me); + } + st_insert(mtbl, mid, (st_data_t) me); return me; @@ -341,7 +401,12 @@ int line; rb_method_entry_t *me = rb_method_entry_make(klass, mid, type, 0, noex); rb_method_definition_t *def = ALLOC(rb_method_definition_t); - me->def = def; + if (me->def && me->def->type == VM_METHOD_TYPE_REFINED) { + me->def->body.orig_def = def; + } + else { + me->def = def; + } def->type = type; def->original_id = mid; def->alias_count = 0; @@ -379,10 +444,13 @@ case VM_METHOD_TYPE_ZSUPER: case VM_METHOD_TYPE_UNDEF: break; + case VM_METHOD_TYPE_REFINED: + def->body.orig_def = (rb_method_definition_t *) opts; + break; default: rb_bug("rb_add_method: unsupported method type (%d)\n", type); } - if (type != VM_METHOD_TYPE_UNDEF) { + if (type != VM_METHOD_TYPE_UNDEF && type != VM_METHOD_TYPE_REFINED) { method_added(klass, mid); } return me; @@ -425,88 +493,20 @@ return 0; } -static VALUE -copy_refinement_iclass(VALUE iclass, VALUE superclass) -{ - VALUE result, c; - - Check_Type(iclass, T_ICLASS); - c = result = rb_include_class_new(RBASIC(iclass)->klass, superclass); - RCLASS_REFINED_CLASS(c) = RCLASS_REFINED_CLASS(iclass); - iclass = RCLASS_SUPER(iclass); - while (iclass && BUILTIN_TYPE(iclass) == T_ICLASS) { - c = RCLASS_SUPER(c) = rb_include_class_new(RBASIC(iclass)->klass, - RCLASS_SUPER(c)); - RCLASS_REFINED_CLASS(c) = RCLASS_REFINED_CLASS(iclass); - iclass = RCLASS_SUPER(iclass); - } - return result; -} - -static inline int -lookup_method_table(VALUE klass, ID id, st_data_t *body) -{ - st_table *m_tbl = RCLASS_M_TBL(klass); - if (!m_tbl) { - m_tbl = RCLASS_M_TBL(RCLASS_ORIGIN(RBASIC(klass)->klass)); - } - return st_lookup(m_tbl, id, body); -} - static inline rb_method_entry_t* -search_method_with_refinements(VALUE klass, ID id, VALUE refinements, - VALUE *defined_class_ptr) +search_method(VALUE klass, ID id, VALUE *defined_class_ptr) { - st_data_t body; - VALUE iclass, skipped_class = Qnil; + rb_method_entry_t *me; - for (body = 0; klass; klass = RCLASS_SUPER(klass)) { - if (klass != skipped_class) { - iclass = rb_hash_lookup(refinements, klass); - if (NIL_P(iclass) && BUILTIN_TYPE(klass) == T_ICLASS) { - iclass = rb_hash_lookup(refinements, RBASIC(klass)->klass); - if (!NIL_P(iclass)) - iclass = copy_refinement_iclass(iclass, klass); - } - if (!NIL_P(iclass)) { - skipped_class = klass; - klass = iclass; - } - } - if (lookup_method_table(klass, id, &body)) break; + for (me = 0; klass; klass = RCLASS_SUPER(klass)) { + if ((me = lookup_method_table(klass, id)) != 0) break; } if (defined_class_ptr) *defined_class_ptr = klass; - return (rb_method_entry_t *)body; + return me; } -static inline rb_method_entry_t* -search_method_without_refinements(VALUE klass, ID id, VALUE *defined_class_ptr) -{ - st_data_t body; - - for (body = 0; klass; klass = RCLASS_SUPER(klass)) { - if (lookup_method_table(klass, id, &body)) break; - } - - if (defined_class_ptr) - *defined_class_ptr = klass; - return (rb_method_entry_t *)body; -} - -static rb_method_entry_t* -search_method(VALUE klass, ID id, VALUE refinements, VALUE *defined_class_ptr) -{ - if (NIL_P(refinements)) { - return search_method_without_refinements(klass, id, defined_class_ptr); - } - else { - return search_method_with_refinements(klass, id, refinements, - defined_class_ptr); - } -} - /* * search method entry without the method cache. * @@ -514,19 +514,17 @@ * rb_method_entry() simply. */ rb_method_entry_t * -rb_method_entry_get_without_cache(VALUE klass, VALUE refinements, ID id, +rb_method_entry_get_without_cache(VALUE klass, ID id, VALUE *defined_class_ptr) { VALUE defined_class; - rb_method_entry_t *me = search_method(klass, id, refinements, - &defined_class); + rb_method_entry_t *me = search_method(klass, id, &defined_class); if (ruby_running) { struct cache_entry *ent; - ent = cache + EXPR1(klass, refinements, id); + ent = cache + EXPR1(klass, id); ent->filled_version = GET_VM_STATE_VERSION(); ent->klass = klass; - ent->refinements = refinements; ent->defined_class = defined_class; if (UNDEFINED_METHOD_ENTRY_P(me)) { @@ -546,37 +544,89 @@ } rb_method_entry_t * -rb_method_entry_get_with_refinements(VALUE refinements, VALUE klass, ID id, - VALUE *defined_class_ptr) +rb_method_entry(VALUE klass, ID id, VALUE *defined_class_ptr) { #if OPT_GLOBAL_METHOD_CACHE struct cache_entry *ent; - ent = cache + EXPR1(klass, refinements, id); + ent = cache + EXPR1(klass, id); if (ent->filled_version == GET_VM_STATE_VERSION() && - ent->mid == id && ent->klass == klass && - ent->refinements == refinements) { + ent->mid == id && ent->klass == klass) { if (defined_class_ptr) *defined_class_ptr = ent->defined_class; return ent->me; } #endif - return rb_method_entry_get_without_cache(klass, refinements, id, + return rb_method_entry_get_without_cache(klass, id, defined_class_ptr); +} + +static rb_method_entry_t * +get_original_method_entry(VALUE refinements, + rb_method_entry_t *me, rb_method_entry_t *me_buf, + VALUE *defined_class_ptr) +{ + if (me->def->body.orig_def) { + *me_buf = *me; + me_buf->def = me->def->body.orig_def; + return me_buf; + } + else { + rb_method_entry_t *tmp_me; + tmp_me = rb_method_entry(RCLASS_SUPER(me->klass), me->called_id, + defined_class_ptr); + return rb_resolve_refined_method(refinements, tmp_me, me_buf, + defined_class_ptr); + } +} + +rb_method_entry_t * +rb_resolve_refined_method(VALUE refinements, rb_method_entry_t *me, + rb_method_entry_t *me_buf, + VALUE *defined_class_ptr) +{ + if (me && me->def->type == VM_METHOD_TYPE_REFINED) { + VALUE refinement; + rb_method_entry_t *tmp_me; + + refinement = find_refinement(refinements, me->klass); + if (NIL_P(refinement)) { + return get_original_method_entry(refinements, me, me_buf, defined_class_ptr); + } + tmp_me = rb_method_entry(refinement, me->called_id, + defined_class_ptr); + if (tmp_me && tmp_me->def->type != VM_METHOD_TYPE_REFINED) { + return tmp_me; + } + else { + return get_original_method_entry(refinements, me, me_buf, + defined_class_ptr); + } + } + else { + return me; + } } rb_method_entry_t * -rb_method_entry(VALUE klass, ID id, VALUE *defined_class_ptr) +rb_method_entry_with_refinements(VALUE klass, ID id, + rb_method_entry_t *me_buf, + VALUE *defined_class_ptr) { - NODE *cref = rb_vm_cref(); - VALUE refinements = Qnil; + VALUE defined_class; + rb_method_entry_t *me = rb_method_entry(klass, id, &defined_class); - if (cref && !NIL_P(cref->nd_refinements)) { - refinements = cref->nd_refinements; + if (me && me->def->type == VM_METHOD_TYPE_REFINED) { + NODE *cref = rb_vm_cref(); + VALUE refinements = cref ? cref->nd_refinements : Qnil; + + me = rb_resolve_refined_method(refinements, me, me_buf, + &defined_class); } - return rb_method_entry_get_with_refinements(refinements, klass, id, - (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/