From a1ee11d0bd0d5908d57a88fdcb1687ba0eec9350 Mon Sep 17 00:00:00 2001 From: ko1 Date: Fri, 3 Jul 2015 11:24:50 +0000 Subject: * method.h: introduce rb_callable_method_entry_t to remove rb_control_frame_t::klass. [Bug #11278], [Bug #11279] rb_method_entry_t data belong to modules/classes. rb_method_entry_t::owner points defined module or class. module M def foo; end end In this case, owner is M. rb_callable_method_entry_t data belong to only classes. For modules, MRI creates corresponding T_ICLASS internally. rb_callable_method_entry_t can also belong to T_ICLASS. rb_callable_method_entry_t::defined_class points T_CLASS or T_ICLASS. rb_method_entry_t data for classes (not for modules) are also rb_callable_method_entry_t data because it is completely same data. In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class. For example, there are classes C and D, and incldues M, class C; include M; end class D; include M; end then, two T_ICLASS objects for C's super class and D's super class will be created. When C.new.foo is called, then M#foo is searcheed and rb_callable_method_t data is used by VM to invoke M#foo. rb_method_entry_t data is only one for M#foo. However, rb_callable_method_entry_t data are two (and can be more). It is proportional to the number of including (and prepending) classes (the number of T_ICLASS which point to the module). Now, created rb_callable_method_entry_t are collected when the original module M was modified. We can think it is a cache. We need to select what kind of method entry data is needed. To operate definition, then you need to use rb_method_entry_t. You can access them by the following functions. * rb_method_entry(VALUE klass, ID id); * rb_method_entry_with_refinements(VALUE klass, ID id); * rb_method_entry_without_refinements(VALUE klass, ID id); * rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me); To invoke methods, then you need to use rb_callable_method_entry_t which you can get by the following APIs corresponding to the above listed functions. * rb_callable_method_entry(VALUE klass, ID id); * rb_callable_method_entry_with_refinements(VALUE klass, ID id); * rb_callable_method_entry_without_refinements(VALUE klass, ID id); * rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me); VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry() returns rb_callable_method_entry_t. You can check a super class of current method by rb_callable_method_entry_t::defined_class. * method.h: renamed from rb_method_entry_t::klass to rb_method_entry_t::owner. * internal.h: add rb_classext_struct::callable_m_tbl to cache rb_callable_method_entry_t data. We need to consider abotu this field again because it is only active for T_ICLASS. * class.c (method_entry_i): ditto. * class.c (rb_define_attr): rb_method_entry() does not takes defiend_class_ptr. * gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS. * cont.c (fiber_init): rb_control_frame_t::klass is removed. * proc.c: fix `struct METHOD' data structure because rb_callable_method_t has all information. * vm_core.h: remove several fields. * rb_control_frame_t::klass. * rb_block_t::klass. And catch up changes. * eval.c: catch up changes. * gc.c: ditto. * insns.def: ditto. * vm.c: ditto. * vm_args.c: ditto. * vm_backtrace.c: ditto. * vm_dump.c: ditto. * vm_eval.c: ditto. * vm_insnhelper.c: ditto. * vm_method.c: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51126 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- proc.c | 203 +++++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 103 insertions(+), 100 deletions(-) (limited to 'proc.c') diff --git a/proc.c b/proc.c index adb9f674a2..15a5cef433 100644 --- a/proc.c +++ b/proc.c @@ -21,11 +21,10 @@ const rb_cref_t *rb_vm_cref_in_context(VALUE self, VALUE cbase); struct METHOD { - VALUE recv; - VALUE rclass; - VALUE defined_class; - ID id; - rb_method_entry_t * const me; + const VALUE recv; + const VALUE klass; + const rb_method_entry_t * const me; + /* for bound methods, `me' should be rb_callable_method_entry_t * */ }; VALUE rb_cUnboundMethod; @@ -1105,9 +1104,8 @@ static void bm_mark(void *ptr) { struct METHOD *data = ptr; - rb_gc_mark(data->defined_class); - rb_gc_mark(data->rclass); rb_gc_mark(data->recv); + rb_gc_mark(data->klass); rb_gc_mark((VALUE)data->me); } @@ -1157,17 +1155,15 @@ respond_to_missing_p(VALUE klass, VALUE obj, VALUE sym, int scope) static VALUE -mnew_missing(VALUE rclass, VALUE klass, VALUE obj, ID id, ID rid, VALUE mclass) +mnew_missing(VALUE klass, VALUE obj, ID id, ID rid, VALUE mclass) { struct METHOD *data; VALUE method = TypedData_Make_Struct(mclass, struct METHOD, &method_data_type, data); rb_method_entry_t *me; rb_method_definition_t *def; - data->recv = obj; - data->rclass = rclass; - data->defined_class = klass; - data->id = rid; + RB_OBJ_WRITE(method, &data->recv, obj); + RB_OBJ_WRITE(method, &data->klass, klass); def = ZALLOC(rb_method_definition_t); def->type = VM_METHOD_TYPE_MISSING; @@ -1183,11 +1179,10 @@ mnew_missing(VALUE rclass, VALUE klass, VALUE obj, ID id, ID rid, VALUE mclass) } static VALUE -mnew_internal(const rb_method_entry_t *me, VALUE defined_class, VALUE klass, +mnew_internal(const rb_method_entry_t *me, VALUE klass, VALUE obj, ID id, VALUE mclass, int scope, int error) { struct METHOD *data; - VALUE rclass = klass; VALUE method; ID rid = id; rb_method_visibility_t visi = METHOD_VISI_UNDEF; @@ -1195,7 +1190,7 @@ mnew_internal(const rb_method_entry_t *me, VALUE defined_class, VALUE klass, again: if (UNDEFINED_METHOD_ENTRY_P(me)) { if (respond_to_missing_p(klass, obj, ID2SYM(id), scope)) { - return mnew_missing(rclass, klass, obj, id, rid, mclass); + return mnew_missing(klass, obj, id, rid, mclass); } if (!error) return Qnil; rb_print_undef(klass, id, 0); @@ -1208,44 +1203,52 @@ mnew_internal(const rb_method_entry_t *me, VALUE defined_class, VALUE klass, } } if (me->def->type == VM_METHOD_TYPE_ZSUPER) { - klass = RCLASS_SUPER(defined_class); - id = me->def->original_id; - me = rb_method_entry_without_refinements(klass, id, &defined_class); + if (me->defined_class) { + VALUE klass = RCLASS_SUPER(me->defined_class); + id = me->def->original_id; + me = (rb_method_entry_t *)rb_callable_method_entry_without_refinements(klass, id); + } + else { + VALUE klass = RCLASS_SUPER(me->owner); + id = me->def->original_id; + me = rb_method_entry_without_refinements(klass, id); + } goto again; } - klass = defined_class; - - while (rclass != klass && - (FL_TEST(rclass, FL_SINGLETON) || RB_TYPE_P(rclass, T_ICLASS))) { - rclass = RCLASS_SUPER(rclass); + while (klass != me->owner && (FL_TEST(klass, FL_SINGLETON) || RB_TYPE_P(klass, T_ICLASS))) { + klass = RCLASS_SUPER(klass); } method = TypedData_Make_Struct(mclass, struct METHOD, &method_data_type, data); - data->recv = obj; - data->rclass = rclass; - data->defined_class = defined_class; - data->id = rid; - RB_OBJ_WRITE(method, &data->me, rb_method_entry_clone(me)); + RB_OBJ_WRITE(method, &data->recv, obj); + RB_OBJ_WRITE(method, &data->klass, klass); + RB_OBJ_WRITE(method, &data->me, me); + OBJ_INFECT(method, klass); return method; } static VALUE -mnew_from_me(const rb_method_entry_t *me, VALUE defined_class, VALUE klass, +mnew_from_me(const rb_method_entry_t *me, VALUE klass, VALUE obj, ID id, VALUE mclass, int scope) { - return mnew_internal(me, defined_class, klass, obj, id, mclass, scope, TRUE); + return mnew_internal(me, klass, obj, id, mclass, scope, TRUE); } static VALUE mnew(VALUE klass, VALUE obj, ID id, VALUE mclass, int scope) { - VALUE defined_class; - const rb_method_entry_t *me = - rb_method_entry_without_refinements(klass, id, &defined_class); - return mnew_from_me(me, defined_class, klass, obj, id, mclass, scope); + const rb_method_entry_t *me; + + if (obj == Qundef) { /* UnboundMethod */ + me = rb_method_entry_without_refinements(klass, id); + } + else { + me = (rb_method_entry_t *)rb_callable_method_entry_without_refinements(klass, id); + } + return mnew_from_me(me, klass, obj, id, mclass, scope); } @@ -1287,6 +1290,7 @@ static VALUE method_eq(VALUE method, VALUE other) { struct METHOD *m1, *m2; + VALUE klass1, klass2; if (!rb_obj_is_method(other)) return Qfalse; @@ -1297,8 +1301,12 @@ method_eq(VALUE method, VALUE other) m1 = (struct METHOD *)DATA_PTR(method); m2 = (struct METHOD *)DATA_PTR(other); + klass1 = m1->me->defined_class ? m1->me->defined_class : m1->me->owner; + klass2 = m2->me->defined_class ? m2->me->defined_class : m2->me->owner; + if (!rb_method_entry_eq(m1->me, m2->me) || - m1->rclass != m2->rclass || + klass1 != klass2 || + m1->klass != m2->klass || m1->recv != m2->recv) { return Qfalse; } @@ -1322,8 +1330,7 @@ method_hash(VALUE method) st_index_t hash; TypedData_Get_Struct(method, struct METHOD, &method_data_type, m); - hash = rb_hash_start((st_index_t)m->rclass); - hash = rb_hash_uint(hash, (st_index_t)m->recv); + hash = rb_hash_start((st_index_t)m->recv); hash = rb_hash_method_entry(hash, m->me); hash = rb_hash_end(hash); @@ -1348,11 +1355,9 @@ method_unbind(VALUE obj) TypedData_Get_Struct(obj, struct METHOD, &method_data_type, orig); method = TypedData_Make_Struct(rb_cUnboundMethod, struct METHOD, &method_data_type, data); - data->recv = Qundef; - data->id = orig->id; + RB_OBJ_WRITE(method, &data->recv, Qundef); + RB_OBJ_WRITE(method, &data->klass, orig->klass); RB_OBJ_WRITE(method, &data->me, rb_method_entry_clone(orig->me)); - data->rclass = orig->rclass; - data->defined_class = orig->defined_class; OBJ_INFECT(method, obj); return method; @@ -1387,7 +1392,7 @@ method_name(VALUE obj) struct METHOD *data; TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data); - return ID2SYM(data->id); + return ID2SYM(data->me->called_id); } /* @@ -1417,16 +1422,8 @@ static VALUE method_owner(VALUE obj) { struct METHOD *data; - VALUE defined_class; - TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data); - defined_class = data->defined_class; - - if (RB_TYPE_P(defined_class, T_ICLASS)) { - defined_class = RBASIC_CLASS(defined_class); - } - - return defined_class; + return data->me->owner; } void @@ -1462,7 +1459,7 @@ obj_method(VALUE obj, VALUE vid, int scope) if (!id) { if (respond_to_missing_p(klass, obj, vid, scope)) { id = rb_intern_str(vid); - return mnew_missing(klass, klass, obj, id, id, mclass); + return mnew_missing(klass, obj, id, id, mclass); } rb_method_name_error(klass, vid); } @@ -1551,7 +1548,7 @@ rb_obj_singleton_method(VALUE obj, VALUE vid) if (!NIL_P(klass = rb_singleton_class_get(obj)) && respond_to_missing_p(klass, obj, vid, FALSE)) { id = rb_intern_str(vid); - return mnew_missing(klass, klass, obj, id, id, rb_cMethod); + return mnew_missing(klass, obj, id, id, rb_cMethod); } rb_name_error_str(vid, "undefined singleton method `%"PRIsVALUE"' for `%"PRIsVALUE"'", QUOTE(vid), obj); @@ -1562,7 +1559,7 @@ rb_obj_singleton_method(VALUE obj, VALUE vid) rb_name_error(id, "undefined singleton method `%"PRIsVALUE"' for `%"PRIsVALUE"'", QUOTE_ID(id), obj); } - return mnew_from_me(me, klass, klass, obj, id, rb_cMethod, FALSE); + return mnew_from_me(me, klass, obj, id, rb_cMethod, FALSE); } /* @@ -1704,17 +1701,16 @@ rb_mod_define_method(int argc, VALUE *argv, VALUE mod) if (is_method) { struct METHOD *method = (struct METHOD *)DATA_PTR(body); - VALUE rclass = method->rclass; - if (rclass != mod && !RB_TYPE_P(rclass, T_MODULE) && - !RTEST(rb_class_inherited_p(mod, rclass))) { - if (FL_TEST(rclass, FL_SINGLETON)) { + if (method->me->owner != mod && !RB_TYPE_P(method->me->owner, T_MODULE) && + !RTEST(rb_class_inherited_p(mod, method->me->owner))) { + if (FL_TEST(method->me->owner, FL_SINGLETON)) { rb_raise(rb_eTypeError, "can't bind singleton method to a different class"); } else { rb_raise(rb_eTypeError, "bind argument must be a subclass of % "PRIsVALUE, - rb_class_name(rclass)); + rb_class_name(method->me->owner)); } } rb_method_entry_set(mod, id, method->me, scope_visi->method_visi); @@ -1732,7 +1728,6 @@ rb_mod_define_method(int argc, VALUE *argv, VALUE mod) RB_OBJ_WRITE(proc->block.iseq->self, &proc->block.iseq->klass, mod); proc->is_lambda = TRUE; proc->is_from_method = TRUE; - proc->block.klass = mod; } rb_add_method(mod, id, VM_METHOD_TYPE_BMETHOD, (void *)body, scope_visi->method_visi); if (scope_visi->module_func) { @@ -1826,10 +1821,8 @@ method_clone(VALUE self) TypedData_Get_Struct(self, struct METHOD, &method_data_type, orig); clone = TypedData_Make_Struct(CLASS_OF(self), struct METHOD, &method_data_type, data); CLONESETUP(clone, self); - data->recv = orig->recv; - data->rclass = orig->rclass; - data->defined_class = orig->defined_class; - data->id = orig->id; + RB_OBJ_WRITE(clone, &data->recv, orig->recv); + RB_OBJ_WRITE(clone, &data->klass, orig->klass); RB_OBJ_WRITE(clone, &data->me, rb_method_entry_clone(orig->me)); return clone; } @@ -1854,6 +1847,13 @@ rb_method_call(int argc, const VALUE *argv, VALUE method) return rb_method_call_with_block(argc, argv, method, proc); } +static const rb_callable_method_entry_t * +method_callable_method_entry(struct METHOD *data) +{ + if (data->me && data->me->defined_class == 0) rb_bug("method_callable_method_entry: not callable."); + return (const rb_callable_method_entry_t *)data->me; +} + VALUE rb_method_call_with_block(int argc, const VALUE *argv, VALUE method, VALUE pass_procval) { @@ -1877,7 +1877,6 @@ rb_method_call_with_block(int argc, const VALUE *argv, VALUE method, VALUE pass_ if ((state = EXEC_TAG()) == 0) { rb_thread_t *th = GET_THREAD(); rb_block_t *block = 0; - VALUE defined_class; if (!NIL_P(pass_procval)) { rb_proc_t *pass_proc; @@ -1887,9 +1886,7 @@ rb_method_call_with_block(int argc, const VALUE *argv, VALUE method, VALUE pass_ th->passed_block = block; VAR_INITIALIZED(data); - defined_class = data->defined_class; - if (BUILTIN_TYPE(defined_class) == T_MODULE) defined_class = data->rclass; - result = rb_vm_call(th, data->recv, data->id, argc, argv, data->me, defined_class); + result = rb_vm_call(th, data->recv, data->me->called_id, argc, argv, method_callable_method_entry(data)); } POP_TAG(); if (safe >= 0) @@ -1994,12 +1991,12 @@ static VALUE umethod_bind(VALUE method, VALUE recv) { struct METHOD *data, *bound; - VALUE methclass; - VALUE rclass; + VALUE methclass, klass; TypedData_Get_Struct(method, struct METHOD, &method_data_type, data); - methclass = data->rclass; + methclass = data->me->owner; + if (!RB_TYPE_P(methclass, T_MODULE) && methclass != CLASS_OF(recv) && !rb_obj_is_kind_of(recv, methclass)) { if (FL_TEST(methclass, FL_SINGLETON)) { @@ -2012,24 +2009,23 @@ umethod_bind(VALUE method, VALUE recv) } } + klass = CLASS_OF(recv); + method = TypedData_Make_Struct(rb_cMethod, struct METHOD, &method_data_type, bound); - bound->recv = data->recv; - bound->rclass = data->rclass; - bound->defined_class = data->defined_class; - bound->id = data->id; + RB_OBJ_WRITE(method, &bound->recv, recv); + RB_OBJ_WRITE(method, &bound->klass, data->klass); RB_OBJ_WRITE(method, &bound->me, rb_method_entry_clone(data->me)); - rclass = CLASS_OF(recv); - if (BUILTIN_TYPE(bound->defined_class) == T_MODULE) { - VALUE ic = rb_class_search_ancestor(rclass, bound->defined_class); + + if (RB_TYPE_P(bound->me->owner, T_MODULE)) { + VALUE ic = rb_class_search_ancestor(klass, bound->me->owner); if (ic) { - rclass = ic; + klass = ic; } else { - rclass = rb_include_class_new(methclass, rclass); + klass = rb_include_class_new(methclass, klass); } + RB_OBJ_WRITE(method, &bound->me, rb_method_entry_complement_defined_class(bound->me, klass)); } - bound->recv = recv; - bound->rclass = rclass; return method; } @@ -2150,14 +2146,12 @@ method_arity(VALUE method) static const rb_method_entry_t * original_method_entry(VALUE mod, ID id) { - VALUE rclass; const rb_method_entry_t *me; - while ((me = rb_method_entry(mod, id, &rclass)) != 0) { + while ((me = rb_method_entry(mod, id)) != 0) { const rb_method_definition_t *def = me->def; - if (!def) break; if (def->type != VM_METHOD_TYPE_ZSUPER) break; - mod = RCLASS_SUPER(rclass); + mod = RCLASS_SUPER(me->owner); id = def->original_id; } return me; @@ -2322,6 +2316,7 @@ method_inspect(VALUE method) const char *s; const char *sharp = "#"; VALUE mklass; + VALUE defined_class; TypedData_Get_Struct(method, struct METHOD, &method_data_type, data); str = rb_str_buf_new2("#<"); @@ -2329,7 +2324,19 @@ method_inspect(VALUE method) rb_str_buf_cat2(str, s); rb_str_buf_cat2(str, ": "); - mklass = data->me->klass; + mklass = data->klass; + + if (data->me && data->me->def->type == VM_METHOD_TYPE_ALIAS) { + defined_class = data->me->def->body.alias.original_me->owner; + } + else { + defined_class = data->me->defined_class ? data->me->defined_class : data->me->owner; + } + + if (RB_TYPE_P(defined_class, T_ICLASS)) { + defined_class = RBASIC_CLASS(defined_class); + } + if (FL_TEST(mklass, FL_SINGLETON)) { VALUE v = rb_ivar_get(mklass, attached); @@ -2349,16 +2356,16 @@ method_inspect(VALUE method) } } else { - rb_str_buf_append(str, rb_class_name(data->rclass)); - if (data->rclass != mklass) { + rb_str_buf_append(str, rb_class_name(mklass)); + if (defined_class != mklass) { rb_str_buf_cat2(str, "("); - rb_str_buf_append(str, rb_class_name(mklass)); + rb_str_buf_append(str, rb_class_name(defined_class)); rb_str_buf_cat2(str, ")"); } } rb_str_buf_cat2(str, sharp); - rb_str_append(str, rb_id2str(data->id)); - if (data->id != data->me->def->original_id) { + rb_str_append(str, rb_id2str(data->me->called_id)); + if (data->me->called_id != data->me->def->original_id) { rb_str_catf(str, "(%"PRIsVALUE")", rb_id2str(data->me->def->original_id)); } @@ -2444,19 +2451,15 @@ static VALUE method_super_method(VALUE method) { const struct METHOD *data; - VALUE defined_class, super_class; + VALUE super_class; const rb_method_entry_t *me; TypedData_Get_Struct(method, struct METHOD, &method_data_type, data); - defined_class = data->defined_class; - if (BUILTIN_TYPE(defined_class) == T_MODULE) defined_class = data->rclass; - super_class = RCLASS_SUPER(defined_class); + super_class = RCLASS_SUPER(data->me->defined_class); if (!super_class) return Qnil; - me = rb_method_entry_without_refinements(super_class, data->id, &defined_class); + me = (rb_method_entry_t *)rb_callable_method_entry_without_refinements(super_class, data->me->called_id); if (!me) return Qnil; - return mnew_internal(me, defined_class, - super_class, data->recv, data->id, - rb_obj_class(method), FALSE, FALSE); + return mnew_internal(me, super_class, data->recv, data->me->called_id, rb_obj_class(method), FALSE, FALSE); } /* -- cgit v1.2.3