diff options
-rw-r--r-- | ChangeLog | 115 | ||||
-rw-r--r-- | class.c | 8 | ||||
-rw-r--r-- | cont.c | 1 | ||||
-rw-r--r-- | eval.c | 14 | ||||
-rw-r--r-- | gc.c | 8 | ||||
-rw-r--r-- | insns.def | 3 | ||||
-rw-r--r-- | internal.h | 2 | ||||
-rw-r--r-- | method.h | 51 | ||||
-rw-r--r-- | proc.c | 203 | ||||
-rw-r--r-- | vm.c | 99 | ||||
-rw-r--r-- | vm_args.c | 2 | ||||
-rw-r--r-- | vm_backtrace.c | 2 | ||||
-rw-r--r-- | vm_core.h | 20 | ||||
-rw-r--r-- | vm_dump.c | 2 | ||||
-rw-r--r-- | vm_eval.c | 111 | ||||
-rw-r--r-- | vm_insnhelper.c | 250 | ||||
-rw-r--r-- | vm_method.c | 310 |
17 files changed, 748 insertions, 453 deletions
@@ -1,3 +1,118 @@ +Fri Jul 03 20:05:10 2015 Koichi Sasada <[email protected]> + + * 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. + Fri Jul 3 14:30:18 2015 Nobuyoshi Nakada <[email protected]> * win32/file.c: some mingw compilers need a tweek for the @@ -1119,10 +1119,10 @@ method_entry_i(st_data_t key, st_data_t value, st_data_t data) rb_method_visibility_t type; if (me->def->type == VM_METHOD_TYPE_REFINED) { - VALUE klass = me->klass; - me = rb_resolve_refined_method(Qnil, me, NULL); + VALUE owner = me->owner; + me = rb_resolve_refined_method(Qnil, me); if (!me) return ST_CONTINUE; - if (!arg->recur && me->klass != klass) return ST_CONTINUE; + if (!arg->recur && me->owner != owner) return ST_CONTINUE; } if (!st_lookup(arg->list, key, 0)) { if (UNDEFINED_METHOD_ENTRY_P(me)) { @@ -1718,7 +1718,7 @@ rb_define_attr(VALUE klass, const char *name, int read, int write) int rb_obj_basic_to_s_p(VALUE obj) { - const rb_method_entry_t *me = rb_method_entry(CLASS_OF(obj), rb_intern("to_s"), 0); + const rb_method_entry_t *me = rb_method_entry(CLASS_OF(obj), rb_intern("to_s")); if (me && me->def && me->def->type == VM_METHOD_TYPE_CFUNC && me->def->body.cfunc.func == rb_any_to_s) return 1; @@ -1203,7 +1203,6 @@ fiber_init(VALUE fibval, VALUE proc) th->cfp->ep = th->stack; *th->cfp->ep = VM_ENVVAL_BLOCK_PTR(0); th->cfp->self = Qnil; - th->cfp->klass = Qnil; th->cfp->flag = 0; th->cfp->iseq = 0; th->cfp->proc = 0; @@ -748,8 +748,8 @@ rb_raise_jump(VALUE mesg, VALUE cause) { rb_thread_t *th = GET_THREAD(); const rb_control_frame_t *cfp = th->cfp; - const rb_method_entry_t *me = rb_vm_frame_method_entry(cfp); - VALUE klass = me->klass; + const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp); + VALUE klass = me->owner; VALUE self = cfp->self; ID mid = me->called_id; @@ -922,7 +922,7 @@ rb_ensure(VALUE (*b_proc)(ANYARGS), VALUE data1, VALUE (*e_proc)(ANYARGS), VALUE return result; } -static const rb_method_entry_t * +static const rb_callable_method_entry_t * method_entry_of_iseq(const rb_control_frame_t *cfp, const rb_iseq_t *iseq) { rb_thread_t *th = GET_THREAD(); @@ -939,9 +939,9 @@ method_entry_of_iseq(const rb_control_frame_t *cfp, const rb_iseq_t *iseq) static ID frame_func_id(rb_control_frame_t *cfp) { - const rb_method_entry_t *me_local; const rb_iseq_t *iseq = cfp->iseq; - const rb_method_entry_t *me = rb_vm_frame_method_entry(cfp); + const rb_callable_method_entry_t *me_local; + const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp); if (me) { return me->def->original_id; @@ -970,9 +970,9 @@ frame_func_id(rb_control_frame_t *cfp) static ID frame_called_id(rb_control_frame_t *cfp) { - const rb_method_entry_t *me_local; const rb_iseq_t *iseq = cfp->iseq; - const rb_method_entry_t *me = rb_vm_frame_method_entry(cfp); + const rb_callable_method_entry_t *me_local; + const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp); if (me) { return me->called_id; @@ -2105,6 +2105,7 @@ obj_free(rb_objspace_t *objspace, VALUE obj) if (FL_TEST(obj, RICLASS_IS_ORIGIN)) { rb_free_m_tbl(RCLASS_M_TBL(obj)); } + rb_free_m_tbl(RCLASS_CALLABLE_M_TBL(obj)); if (RCLASS_EXT(obj)->subclasses) { rb_class_detach_subclasses(obj); RCLASS_EXT(obj)->subclasses = NULL; @@ -3926,7 +3927,8 @@ mark_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me) { const rb_method_definition_t *def = me->def; - gc_mark(objspace, me->klass); + gc_mark(objspace, me->owner); + gc_mark(objspace, me->defined_class); if (def) { switch (def->type) { @@ -3946,6 +3948,7 @@ mark_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me) return; case VM_METHOD_TYPE_REFINED: gc_mark(objspace, (VALUE)def->body.refined.orig_me); + gc_mark(objspace, (VALUE)def->body.refined.owner); break; case VM_METHOD_TYPE_CFUNC: case VM_METHOD_TYPE_ZSUPER: @@ -4324,6 +4327,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj) if (FL_TEST(obj, RICLASS_IS_ORIGIN)) { mark_m_tbl(objspace, RCLASS_M_TBL(obj)); } + mark_m_tbl(objspace, RCLASS_CALLABLE_M_TBL(obj)); if (!RCLASS_EXT(obj)) break; gc_mark(objspace, RCLASS_SUPER((VALUE)obj)); break; @@ -8953,7 +8957,7 @@ rb_raw_obj_info(char *buff, const int buff_size, VALUE obj) if (imemo_type(obj) == imemo_ment) { const rb_method_entry_t *me = &RANY(obj)->as.imemo.ment; snprintf(buff, buff_size, "%s (called_id: %s, type: %s, alias: %d, class: %s)", buff, - rb_id2name(me->called_id), method_type_name(me->def->type), me->def->alias_count, obj_info(me->klass)); + rb_id2name(me->called_id), method_type_name(me->def->type), me->def->alias_count, obj_info(me->defined_class)); } } default: @@ -914,8 +914,7 @@ defineclass } /* enter scope */ - vm_push_frame(th, class_iseq, VM_FRAME_MAGIC_CLASS, - klass, 0, + vm_push_frame(th, class_iseq, VM_FRAME_MAGIC_CLASS, klass, VM_ENVVAL_BLOCK_PTR(GET_BLOCK_PTR()), (VALUE)vm_cref_push(th, klass, NULL), class_iseq->iseq_encoded, GET_SP(), diff --git a/internal.h b/internal.h index 32ad346e07..86bf5fd9cc 100644 --- a/internal.h +++ b/internal.h @@ -456,6 +456,7 @@ struct rb_classext_struct { struct st_table *iv_index_tbl; struct st_table *iv_tbl; struct st_table *const_tbl; + struct st_table *callable_m_tbl; rb_subclass_entry_t *subclasses; rb_subclass_entry_t **parent_subclasses; /** @@ -477,6 +478,7 @@ void rb_class_remove_from_super_subclasses(VALUE); #define RCLASS_IV_TBL(c) (RCLASS_EXT(c)->iv_tbl) #define RCLASS_CONST_TBL(c) (RCLASS_EXT(c)->const_tbl) #define RCLASS_M_TBL(c) (RCLASS(c)->m_tbl) +#define RCLASS_CALLABLE_M_TBL(c) (RCLASS_EXT(c)->callable_m_tbl) #define RCLASS_IV_INDEX_TBL(c) (RCLASS_EXT(c)->iv_index_tbl) #define RCLASS_ORIGIN(c) (RCLASS_EXT(c)->origin_) #define RCLASS_REFINED_CLASS(c) (RCLASS_EXT(c)->refined_class) @@ -47,12 +47,20 @@ typedef struct rb_cref_struct { typedef struct rb_method_entry_struct { VALUE flags; - VALUE dummy; + const VALUE defined_class; struct rb_method_definition_struct * const def; ID called_id; - const VALUE klass; /* should be marked */ + const VALUE owner; } rb_method_entry_t; +typedef struct rb_callable_method_entry_struct { /* same fields with rb_method_entry_t */ + VALUE flags; + const VALUE defined_class; + struct rb_method_definition_struct * const def; + ID called_id; + const VALUE owner; +} rb_callable_method_entry_t; + #define METHOD_ENTRY_VISI(me) (rb_method_visibility_t)(((me)->flags & (IMEMO_FL_USER0 | IMEMO_FL_USER1)) >> (IMEMO_FL_USHIFT+0)) #define METHOD_ENTRY_BASIC(me) (int) (((me)->flags & (IMEMO_FL_USER2 )) >> (IMEMO_FL_USHIFT+2)) #define METHOD_ENTRY_SAFE(me) (int) (((me)->flags & (IMEMO_FL_USER3 | IMEMO_FL_USER4)) >> (IMEMO_FL_USHIFT+3)) @@ -85,6 +93,13 @@ METHOD_ENTRY_FLAGS_SET(rb_method_entry_t *me, rb_method_visibility_t visi, unsig (me->flags & ~(IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2|IMEMO_FL_USER3|IMEMO_FL_USER4)) | ((visi << IMEMO_FL_USHIFT+0) | (basic << (IMEMO_FL_USHIFT+2)) | (safe << IMEMO_FL_USHIFT+3)); } +static inline void +METHOD_ENTRY_FLAGS_COPY(rb_method_entry_t *dst, const rb_method_entry_t *src) +{ + dst->flags = + (dst->flags & ~(IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2|IMEMO_FL_USER3|IMEMO_FL_USER4)) | + (src->flags & (IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2|IMEMO_FL_USER3|IMEMO_FL_USER4)); +} typedef enum { VM_METHOD_TYPE_ISEQ, @@ -127,6 +142,7 @@ typedef struct rb_method_alias_struct { typedef struct rb_method_refined_struct { const struct rb_method_entry_struct * const orig_me; + const VALUE owner; } rb_method_refined_t; typedef struct rb_method_definition_struct { @@ -159,20 +175,23 @@ typedef struct rb_method_definition_struct { void rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_visibility_t visi); void rb_add_method_iseq(VALUE klass, ID mid, VALUE iseqval, rb_cref_t *cref, rb_method_visibility_t visi); -rb_method_entry_t *rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_visibility_t visi); -rb_method_entry_t *rb_method_entry(VALUE klass, ID id, VALUE *define_class_ptr); -rb_method_entry_t *rb_method_entry_at(VALUE obj, ID id); void rb_add_refined_method_entry(VALUE refined_class, ID mid); -const rb_method_entry_t *rb_resolve_refined_method(VALUE refinements, - const rb_method_entry_t *me, - VALUE *defined_class_ptr); -const rb_method_entry_t *rb_method_entry_with_refinements(VALUE klass, ID id, - VALUE *defined_class_ptr); -const rb_method_entry_t *rb_method_entry_without_refinements(VALUE klass, ID id, - VALUE *defined_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_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_visibility_t visi); rb_method_entry_t *rb_method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *, rb_method_visibility_t noex); +rb_method_entry_t *rb_method_entry_create(ID called_id, VALUE klass, rb_method_visibility_t visi, const rb_method_definition_t *def); + +const rb_method_entry_t *rb_method_entry_at(VALUE obj, ID id); + +const rb_method_entry_t *rb_method_entry(VALUE klass, ID id); +const rb_method_entry_t *rb_method_entry_with_refinements(VALUE klass, ID id); +const rb_method_entry_t *rb_method_entry_without_refinements(VALUE klass, ID id); +const rb_method_entry_t *rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me); + +const rb_callable_method_entry_t *rb_callable_method_entry(VALUE klass, ID id); +const rb_callable_method_entry_t *rb_callable_method_entry_with_refinements(VALUE klass, ID id); +const rb_callable_method_entry_t *rb_callable_method_entry_without_refinements(VALUE klass, ID id); +const rb_callable_method_entry_t *rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me); int rb_method_entry_arity(const rb_method_entry_t *me); int rb_method_entry_eq(const rb_method_entry_t *m1, const rb_method_entry_t *m2); @@ -185,8 +204,8 @@ VALUE rb_obj_method_location(VALUE obj, ID id); void rb_free_method_entry(const rb_method_entry_t *me); void rb_sweep_method_entry(void *vm); -rb_method_entry_t *rb_method_entry_create(ID called_id, VALUE klass, rb_method_visibility_t visi, const rb_method_definition_t *def); -rb_method_entry_t *rb_method_entry_clone(const rb_method_entry_t *me); +const rb_method_entry_t *rb_method_entry_clone(const rb_method_entry_t *me); +const rb_callable_method_entry_t *rb_method_entry_complement_defined_class(const rb_method_entry_t *src_me, VALUE defined_class); void rb_method_entry_copy(rb_method_entry_t *dst, const rb_method_entry_t *src); void rb_scope_visibility_set(rb_method_visibility_t); @@ -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); } /* @@ -8,6 +8,8 @@ **********************************************************************/ +#define VM_CHECK_MODE 0 + #include "internal.h" #include "ruby/vm.h" #include "ruby/st.h" @@ -128,10 +130,10 @@ static void vm_collect_usage_register(int reg, int isset); #endif static VALUE -vm_invoke_bmethod(rb_thread_t *th, rb_proc_t *proc, VALUE self, VALUE defined_class, +vm_invoke_bmethod(rb_thread_t *th, rb_proc_t *proc, VALUE self, int argc, const VALUE *argv, const rb_block_t *blockptr); static VALUE -vm_invoke_proc(rb_thread_t *th, rb_proc_t *proc, VALUE self, VALUE defined_class, +vm_invoke_proc(rb_thread_t *th, rb_proc_t *proc, VALUE self, int argc, const VALUE *argv, const rb_block_t *blockptr); static rb_serial_t ruby_vm_global_method_state = 1; @@ -253,8 +255,7 @@ vm_set_top_stack(rb_thread_t *th, VALUE iseqval) } /* for return */ - vm_push_frame(th, iseq, VM_FRAME_MAGIC_TOP | VM_FRAME_FLAG_FINISH, - th->top_self, rb_cObject, + vm_push_frame(th, iseq, VM_FRAME_MAGIC_TOP | VM_FRAME_FLAG_FINISH, th->top_self, VM_ENVVAL_BLOCK_PTR(0), (VALUE)vm_cref_new_toplevel(th), /* cref or me */ iseq->iseq_encoded, th->cfp->sp, iseq->local_size, iseq->stack_max); @@ -267,8 +268,7 @@ vm_set_eval_stack(rb_thread_t * th, VALUE iseqval, const rb_cref_t *cref, rb_blo GetISeqPtr(iseqval, iseq); vm_push_frame(th, iseq, VM_FRAME_MAGIC_EVAL | VM_FRAME_FLAG_FINISH, - base_block->self, base_block->klass, - VM_ENVVAL_PREV_EP_PTR(base_block->ep), + base_block->self, VM_ENVVAL_PREV_EP_PTR(base_block->ep), (VALUE)cref, /* cref or me */ iseq->iseq_encoded, th->cfp->sp, iseq->local_size, iseq->stack_max); @@ -343,10 +343,10 @@ void rb_vm_pop_cfunc_frame(void) { rb_thread_t *th = GET_THREAD(); - const rb_method_entry_t *me = rb_vm_frame_method_entry(th->cfp); + const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(th->cfp); - EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, th->cfp->self, me->called_id, me->klass, Qnil); - RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, me->klass, me->called_id); + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, th->cfp->self, me->called_id, me->owner, Qnil); + RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, me->owner, me->called_id); vm_pop_frame(th); } @@ -557,7 +557,6 @@ vm_make_env_each(const rb_thread_t *const th, rb_control_frame_t *const cfp, /* as Binding */ env->block.self = cfp->self; - env->block.klass = 0; env->block.ep = cfp->ep; env->block.iseq = cfp->iseq; env->block.proc = 0; @@ -809,7 +808,7 @@ static inline VALUE invoke_block_from_c(rb_thread_t *th, const rb_block_t *block, VALUE self, int argc, const VALUE *argv, const rb_block_t *blockptr, const rb_cref_t *cref, - VALUE defined_class, int splattable) + int splattable) { if (SPECIAL_CONST_P(block->iseq)) { return Qnil; @@ -820,7 +819,7 @@ invoke_block_from_c(rb_thread_t *th, const rb_block_t *block, const rb_control_frame_t *cfp; int i, opt_pc, arg_size = iseq->param.size; int type = block_proc_is_lambda(block->proc) ? VM_FRAME_MAGIC_LAMBDA : VM_FRAME_MAGIC_BLOCK; - const rb_method_entry_t *me = th->passed_bmethod_me; + const rb_callable_method_entry_t *me = th->passed_bmethod_me; th->passed_bmethod_me = NULL; cfp = th->cfp; @@ -833,20 +832,18 @@ invoke_block_from_c(rb_thread_t *th, const rb_block_t *block, if (me != 0) { /* bmethod */ - vm_push_frame(th, iseq, type | VM_FRAME_FLAG_FINISH | VM_FRAME_FLAG_BMETHOD, - self, defined_class, + vm_push_frame(th, iseq, type | VM_FRAME_FLAG_FINISH | VM_FRAME_FLAG_BMETHOD, self, VM_ENVVAL_PREV_EP_PTR(block->ep), (VALUE)me, /* cref or method (TODO: can we ignore cref?) */ iseq->iseq_encoded + opt_pc, cfp->sp + arg_size, iseq->local_size - arg_size, iseq->stack_max); - RUBY_DTRACE_METHOD_ENTRY_HOOK(th, me->klass, me->called_id); - EXEC_EVENT_HOOK(th, RUBY_EVENT_CALL, self, me->called_id, me->klass, Qnil); + RUBY_DTRACE_METHOD_ENTRY_HOOK(th, me->owner, me->called_id); + EXEC_EVENT_HOOK(th, RUBY_EVENT_CALL, self, me->called_id, me->owner, Qnil); } else { - vm_push_frame(th, iseq, type | VM_FRAME_FLAG_FINISH, - self, defined_class, + vm_push_frame(th, iseq, type | VM_FRAME_FLAG_FINISH, self, VM_ENVVAL_PREV_EP_PTR(block->ep), (VALUE)cref, /* cref or method */ iseq->iseq_encoded + opt_pc, @@ -858,15 +855,14 @@ invoke_block_from_c(rb_thread_t *th, const rb_block_t *block, if (me) { /* bmethod */ - EXEC_EVENT_HOOK(th, RUBY_EVENT_RETURN, self, me->called_id, me->klass, ret); - RUBY_DTRACE_METHOD_RETURN_HOOK(th, me->klass, me->called_id); + EXEC_EVENT_HOOK(th, RUBY_EVENT_RETURN, self, me->called_id, me->owner, ret); + RUBY_DTRACE_METHOD_RETURN_HOOK(th, me->owner, me->called_id); } return ret; } else { - return vm_yield_with_cfunc(th, block, self, defined_class, - argc, argv, blockptr); + return vm_yield_with_cfunc(th, block, self, argc, argv, blockptr); } } @@ -886,28 +882,25 @@ static inline VALUE vm_yield_with_cref(rb_thread_t *th, int argc, const VALUE *argv, const rb_cref_t *cref) { const rb_block_t *blockptr = check_block(th); - return invoke_block_from_c(th, blockptr, blockptr->self, argc, argv, 0, cref, - blockptr->klass, 1); + return invoke_block_from_c(th, blockptr, blockptr->self, argc, argv, 0, cref, 1); } static inline VALUE vm_yield(rb_thread_t *th, int argc, const VALUE *argv) { const rb_block_t *blockptr = check_block(th); - return invoke_block_from_c(th, blockptr, blockptr->self, argc, argv, 0, 0, - blockptr->klass, 1); + return invoke_block_from_c(th, blockptr, blockptr->self, argc, argv, 0, 0, 1); } static inline VALUE vm_yield_with_block(rb_thread_t *th, int argc, const VALUE *argv, const rb_block_t *blockargptr) { const rb_block_t *blockptr = check_block(th); - return invoke_block_from_c(th, blockptr, blockptr->self, argc, argv, blockargptr, 0, - blockptr->klass, 1); + return invoke_block_from_c(th, blockptr, blockptr->self, argc, argv, blockargptr, 0, 1); } static VALUE -vm_invoke_proc(rb_thread_t *th, rb_proc_t *proc, VALUE self, VALUE defined_class, +vm_invoke_proc(rb_thread_t *th, rb_proc_t *proc, VALUE self, int argc, const VALUE *argv, const rb_block_t *blockptr) { VALUE val = Qundef; @@ -917,8 +910,7 @@ vm_invoke_proc(rb_thread_t *th, rb_proc_t *proc, VALUE self, VALUE defined_class TH_PUSH_TAG(th); if ((state = EXEC_TAG()) == 0) { th->safe_level = proc->safe_level; - val = invoke_block_from_c(th, &proc->block, self, argc, argv, blockptr, 0, - defined_class, 0); + val = invoke_block_from_c(th, &proc->block, self, argc, argv, blockptr, 0, 0); } TH_POP_TAG(); @@ -931,11 +923,10 @@ vm_invoke_proc(rb_thread_t *th, rb_proc_t *proc, VALUE self, VALUE defined_class } static VALUE -vm_invoke_bmethod(rb_thread_t *th, rb_proc_t *proc, VALUE self, VALUE defined_class, +vm_invoke_bmethod(rb_thread_t *th, rb_proc_t *proc, VALUE self, int argc, const VALUE *argv, const rb_block_t *blockptr) { - return invoke_block_from_c(th, &proc->block, self, argc, argv, blockptr, 0, - defined_class, 0); + return invoke_block_from_c(th, &proc->block, self, argc, argv, blockptr, 0, 0); } VALUE @@ -943,12 +934,11 @@ rb_vm_invoke_proc(rb_thread_t *th, rb_proc_t *proc, int argc, const VALUE *argv, const rb_block_t *blockptr) { VALUE self = proc->block.self; - VALUE defined_class = proc->block.klass; if (proc->is_from_method) { - return vm_invoke_bmethod(th, proc, self, defined_class, argc, argv, blockptr); + return vm_invoke_bmethod(th, proc, self, argc, argv, blockptr); } else { - return vm_invoke_proc(th, proc, self, defined_class, argc, argv, blockptr); + return vm_invoke_proc(th, proc, self, argc, argv, blockptr); } } @@ -1260,12 +1250,12 @@ static int check_redefined_method(st_data_t key, st_data_t value, st_data_t data) { ID mid = (ID)key; - rb_method_entry_t *me = (rb_method_entry_t *)value; VALUE klass = (VALUE)data; - rb_method_entry_t *newme = rb_method_entry(klass, mid, NULL); + const rb_method_entry_t *me = (rb_method_entry_t *)value; + const rb_method_entry_t *newme = rb_method_entry(klass, mid); + + if (newme != me) rb_vm_check_redefinition_opt_method(me, me->owner); - if (newme != me) - rb_vm_check_redefinition_opt_method(me, me->klass); return ST_CONTINUE; } @@ -1280,7 +1270,7 @@ rb_vm_check_redefinition_by_prepend(VALUE klass) static void add_opt_method(VALUE klass, ID mid, VALUE bop) { - rb_method_entry_t *me = rb_method_entry_at(klass, mid); + const rb_method_entry_t *me = rb_method_entry_at(klass, mid); if (me && me->def->type == VM_METHOD_TYPE_CFUNC) { st_insert(vm_opt_method_table, (st_data_t)me, (st_data_t)bop); @@ -1361,7 +1351,7 @@ hook_before_rewind(rb_thread_t *th, rb_control_frame_t *cfp) EXEC_EVENT_HOOK(th, RUBY_EVENT_B_RETURN, th->cfp->self, 0, 0, Qnil); EXEC_EVENT_HOOK_AND_POP_FRAME(th, RUBY_EVENT_RETURN, th->cfp->self, rb_vm_frame_method_entry(th->cfp)->called_id, - rb_vm_frame_method_entry(th->cfp)->klass, Qnil); + rb_vm_frame_method_entry(th->cfp)->owner, Qnil); } else { EXEC_EVENT_HOOK_AND_POP_FRAME(th, RUBY_EVENT_B_RETURN, th->cfp->self, 0, 0, Qnil); @@ -1508,8 +1498,10 @@ vm_exec(rb_thread_t *th) while (th->cfp->pc == 0 || th->cfp->iseq == 0) { if (UNLIKELY(VM_FRAME_TYPE(th->cfp) == VM_FRAME_MAGIC_CFUNC)) { EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, th->cfp->self, - rb_vm_frame_method_entry(th->cfp)->called_id, rb_vm_frame_method_entry(th->cfp)->klass, Qnil); - RUBY_DTRACE_METHOD_RETURN_HOOK(th, rb_vm_frame_method_entry(th->cfp)->klass, + rb_vm_frame_method_entry(th->cfp)->called_id, + rb_vm_frame_method_entry(th->cfp)->owner, Qnil); + RUBY_DTRACE_METHOD_RETURN_HOOK(th, + rb_vm_frame_method_entry(th->cfp)->owner, rb_vm_frame_method_entry(th->cfp)->called_id); } th->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp); @@ -1673,7 +1665,7 @@ vm_exec(rb_thread_t *th) /* push block frame */ cfp->sp[0] = (VALUE)err; vm_push_frame(th, catch_iseq, VM_FRAME_MAGIC_RESCUE, - cfp->self, cfp->klass, + cfp->self, VM_ENVVAL_PREV_EP_PTR(cfp->ep), 0, /* cref or me */ catch_iseq->iseq_encoded, @@ -1738,11 +1730,11 @@ rb_iseq_eval_main(VALUE iseqval) int rb_vm_control_frame_id_and_class(const rb_control_frame_t *cfp, ID *idp, VALUE *klassp) { - const rb_method_entry_t *me = rb_vm_frame_method_entry(cfp); + const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp); if (me) { if (idp) *idp = me->def->original_id; - if (klassp) *klassp = me->klass; + if (klassp) *klassp = me->owner; return TRUE; } else { @@ -1766,7 +1758,7 @@ VALUE rb_thread_current_status(const rb_thread_t *th) { const rb_control_frame_t *cfp = th->cfp; - const rb_method_entry_t *me; + const rb_callable_method_entry_t *me; VALUE str = Qnil; if (cfp->iseq != 0) { @@ -1779,7 +1771,7 @@ rb_thread_current_status(const rb_thread_t *th) } else if ((me = rb_vm_frame_method_entry(cfp)) && me->def->original_id) { str = rb_sprintf("`%"PRIsVALUE"#%"PRIsVALUE"' (cfunc)", - rb_class_path(me->klass), + rb_class_path(me->owner), rb_id2str(me->def->original_id)); } @@ -1796,7 +1788,7 @@ rb_vm_call_cfunc(VALUE recv, VALUE (*func)(VALUE), VALUE arg, VALUE val; vm_push_frame(th, DATA_PTR(iseqval), VM_FRAME_MAGIC_TOP | VM_FRAME_FLAG_FINISH, - recv, CLASS_OF(recv), VM_ENVVAL_BLOCK_PTR(blockptr), + recv, VM_ENVVAL_BLOCK_PTR(blockptr), (VALUE)vm_cref_new_toplevel(th), /* cref or me */ 0, reg_cfp->sp, 1, 0); @@ -2073,7 +2065,6 @@ rb_thread_mark(void *ptr) rb_iseq_t *iseq = cfp->iseq; rb_gc_mark(cfp->proc); rb_gc_mark(cfp->self); - rb_gc_mark(cfp->klass); if (iseq) { rb_gc_mark(RUBY_VM_NORMAL_ISEQ_P(iseq) ? iseq->self : (VALUE)iseq); } @@ -2231,7 +2222,7 @@ th_init(rb_thread_t *th, VALUE self) th->cfp = (void *)(th->stack + th->stack_size); vm_push_frame(th, 0 /* dummy iseq */, VM_FRAME_MAGIC_DUMMY | VM_FRAME_FLAG_FINISH /* dummy frame */, - Qnil /* dummy self */, Qnil /* dummy klass */, VM_ENVVAL_BLOCK_PTR(0) /* dummy block ptr */, + Qnil /* dummy self */, VM_ENVVAL_BLOCK_PTR(0) /* dummy block ptr */, 0 /* dummy cref/me */, 0 /* dummy pc */, th->stack, 1, 0); @@ -2544,7 +2535,6 @@ Init_VM(void) rb_define_method_id(klass, idLambda, rb_block_lambda, 0); rb_obj_freeze(fcore); RBASIC_CLEAR_CLASS(klass); - RCLASS_SET_SUPER(klass, 0); rb_obj_freeze(klass); rb_gc_register_mark_object(fcore); rb_mRubyVMFrozenCore = fcore; @@ -2793,7 +2783,6 @@ Init_VM(void) th->cfp->iseq = iseq; th->cfp->pc = iseq->iseq_encoded; th->cfp->self = th->top_self; - th->cfp->klass = Qnil; th->cfp->ep[-1] = (VALUE)vm_cref_new(rb_cObject, METHOD_VISI_PRIVATE, NULL); @@ -689,7 +689,7 @@ raise_argument_error(rb_thread_t *th, const rb_iseq_t *iseq, const VALUE exc) VALUE at; if (iseq) { - vm_push_frame(th, iseq, VM_FRAME_MAGIC_DUMMY, Qnil /* self */, Qnil /* klass */, + vm_push_frame(th, iseq, VM_FRAME_MAGIC_DUMMY, Qnil /* self */, VM_ENVVAL_BLOCK_PTR(0) /* specval*/, Qfalse /* me or cref */, iseq->iseq_encoded, th->cfp->sp, 1 /* local_size (cref/me) */, 0 /* stack_max */); at = rb_vm_backtrace_object(); diff --git a/vm_backtrace.c b/vm_backtrace.c index 801bf1100c..99d3e7f3d3 100644 --- a/vm_backtrace.c +++ b/vm_backtrace.c @@ -465,7 +465,7 @@ backtrace_each(rb_thread_t *th, } } else if (RUBYVM_CFUNC_FRAME_P(cfp)) { - const rb_method_entry_t *me = rb_vm_frame_method_entry(cfp); + const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp); ID mid = me->def->original_id; iter_cfunc(arg, cfp, mid); @@ -183,8 +183,7 @@ typedef struct rb_call_info_struct { VALUE klass; /* inline cache: values */ - const rb_method_entry_t *me; - VALUE defined_class; + const rb_callable_method_entry_t *me; /* temporary values for method calling */ struct rb_block_struct *blockptr; @@ -524,7 +523,7 @@ typedef struct rb_vm_struct { #endif #ifndef VM_DEBUG_VERIFY_METHOD_CACHE -#define VM_DEBUG_VERIFY_METHOD_CACHE 0 +#define VM_DEBUG_VERIFY_METHOD_CACHE (VM_DEBUG_MODE != 0) #endif typedef struct rb_control_frame_struct { @@ -533,10 +532,9 @@ typedef struct rb_control_frame_struct { rb_iseq_t *iseq; /* cfp[2] */ VALUE flag; /* cfp[3] */ VALUE self; /* cfp[4] / block[0] */ - VALUE klass; /* cfp[5] / block[1] */ - VALUE *ep; /* cfp[6] / block[2] */ - rb_iseq_t *block_iseq; /* cfp[7] / block[3] */ - VALUE proc; /* cfp[8] / block[4] */ + VALUE *ep; /* cfp[6] / block[1] */ + rb_iseq_t *block_iseq; /* cfp[7] / block[2] */ + VALUE proc; /* cfp[8] / block[3] */ #if VM_DEBUG_BP_CHECK VALUE *bp_check; /* cfp[9] */ @@ -545,7 +543,6 @@ typedef struct rb_control_frame_struct { typedef struct rb_block_struct { VALUE self; /* share with method frame if it's only block */ - VALUE klass; /* share with method frame if it's only block */ VALUE *ep; /* share with method frame if it's only block */ rb_iseq_t *iseq; VALUE proc; @@ -631,7 +628,7 @@ typedef struct rb_thread_struct { const rb_block_t *passed_block; /* for bmethod */ - const rb_method_entry_t *passed_bmethod_me; + const rb_callable_method_entry_t *passed_bmethod_me; /* for cfunc */ rb_call_info_t *passed_ci; @@ -976,8 +973,7 @@ VALUE *rb_binding_add_dynavars(rb_binding_t *bind, int dyncount, const ID *dynva void rb_vm_inc_const_missing_count(void); void rb_vm_gvl_destroy(rb_vm_t *vm); VALUE rb_vm_call(rb_thread_t *th, VALUE recv, VALUE id, int argc, - const VALUE *argv, const rb_method_entry_t *me, - VALUE defined_class); + const VALUE *argv, const rb_callable_method_entry_t *me); void rb_thread_start_timer_thread(void); void rb_thread_stop_timer_thread(int); @@ -1024,7 +1020,7 @@ int rb_autoloading_value(VALUE mod, ID id, VALUE* value); void rb_vm_rewrite_cref(rb_cref_t *node, VALUE old_klass, VALUE new_klass, rb_cref_t **new_cref_ptr); -const rb_method_entry_t *rb_vm_frame_method_entry(const rb_control_frame_t *cfp); +const rb_callable_method_entry_t *rb_vm_frame_method_entry(const rb_control_frame_t *cfp); #define sysstack_error GET_VM()->special_exceptions[ruby_error_sysstack] @@ -36,7 +36,7 @@ control_frame_dump(rb_thread_t *th, rb_control_frame_t *cfp) const char *magic, *iseq_name = "-", *selfstr = "-", *biseq_name = "-"; VALUE tmp; - const rb_method_entry_t *me; + const rb_callable_method_entry_t *me; if (cfp->block_iseq != 0 && !RUBY_VM_IFUNC_P(cfp->block_iseq)) { biseq_name = ""; /* RSTRING(cfp->block_iseq->location.label)->ptr; */ @@ -42,15 +42,13 @@ static VALUE send_internal(int argc, const VALUE *argv, VALUE recv, call_type sc static VALUE vm_call0_body(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv); static VALUE -vm_call0(rb_thread_t* th, VALUE recv, ID id, int argc, const VALUE *argv, - const rb_method_entry_t *me, VALUE defined_class) +vm_call0(rb_thread_t* th, VALUE recv, ID id, int argc, const VALUE *argv, const rb_callable_method_entry_t *me) { rb_call_info_t ci_entry, *ci = &ci_entry; ci->flag = 0; ci->mid = id; ci->recv = recv; - ci->defined_class = defined_class; ci->argc = argc; ci->me = me; ci->kw_arg = NULL; @@ -64,11 +62,11 @@ vm_call0_cfunc(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv) { VALUE val; - RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, ci->defined_class, ci->mid); - EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, ci->recv, ci->mid, ci->defined_class, Qnil); + RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, ci->me->owner, ci->mid); + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, ci->recv, ci->mid, ci->me->owner, Qnil); { rb_control_frame_t *reg_cfp = th->cfp; - const rb_method_entry_t *me = ci->me; + const rb_callable_method_entry_t *me = ci->me; const rb_method_cfunc_t *cfunc = &me->def->body.cfunc; int len = cfunc->argc; VALUE recv = ci->recv; @@ -95,8 +93,8 @@ vm_call0_cfunc(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv) vm_pop_frame(th); } } - EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, ci->recv, ci->mid, ci->defined_class, val); - RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, ci->defined_class, ci->mid); + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, ci->recv, ci->mid, ci->me->owner, val); + RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, ci->me->owner, ci->mid); return val; } @@ -105,21 +103,20 @@ static VALUE vm_call0_cfunc_with_frame(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv) { VALUE val; - const rb_method_entry_t *me = ci->me; + const rb_callable_method_entry_t *me = ci->me; const rb_method_cfunc_t *cfunc = &me->def->body.cfunc; int len = cfunc->argc; VALUE recv = ci->recv; - VALUE defined_class = ci->defined_class; int argc = ci->argc; ID mid = ci->mid; rb_block_t *blockptr = ci->blockptr; - RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, defined_class, mid); - EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, mid, defined_class, Qnil); + RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, me->owner, mid); + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, mid, me->owner, Qnil); { rb_control_frame_t *reg_cfp = th->cfp; - vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, recv, defined_class, + vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, recv, VM_ENVVAL_BLOCK_PTR(blockptr), (VALUE)me, 0, reg_cfp->sp, 1, 0); @@ -134,8 +131,8 @@ vm_call0_cfunc_with_frame(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv VM_PROFILE_UP(3); vm_pop_frame(th); } - EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, mid, defined_class, val); - RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, defined_class, mid); + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, mid, me->owner, val); + RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, me->owner, mid); return val; } @@ -198,14 +195,16 @@ vm_call0_body(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv) case VM_METHOD_TYPE_REFINED: { const rb_method_type_t type = ci->me->def->type; + VALUE super_class; + if (type == VM_METHOD_TYPE_REFINED && ci->me->def->body.refined.orig_me) { - ci->me = ci->me->def->body.refined.orig_me; + ci->me = refined_method_callable_without_refinement(ci->me); goto again; } - ci->defined_class = RCLASS_SUPER(ci->defined_class); + super_class = RCLASS_SUPER(ci->me->defined_class); - if (!ci->defined_class || !(ci->me = rb_method_entry(ci->defined_class, ci->mid, &ci->defined_class))) { + if (!super_class || !(ci->me = rb_callable_method_entry(super_class, ci->mid))) { enum method_missing_reason ex = (type == VM_METHOD_TYPE_ZSUPER) ? MISSING_SUPER : 0; ret = method_missing(ci->recv, ci->mid, ci->argc, argv, ex); goto success; @@ -214,11 +213,8 @@ vm_call0_body(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv) goto again; } case VM_METHOD_TYPE_ALIAS: - { - ci->me = ci->me->def->body.alias.original_me; - ci->defined_class = find_defined_class_by_owner(ci->defined_class, ci->me->klass); - goto again; - } + ci->me = aliased_callable_method_entry(ci->me); + goto again; case VM_METHOD_TYPE_MISSING: { VALUE new_args = rb_ary_new4(ci->argc, argv); @@ -258,10 +254,9 @@ vm_call0_body(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv) } VALUE -rb_vm_call(rb_thread_t *th, VALUE recv, VALUE id, int argc, const VALUE *argv, - const rb_method_entry_t *me, VALUE defined_class) +rb_vm_call(rb_thread_t *th, VALUE recv, VALUE id, int argc, const VALUE *argv, const rb_callable_method_entry_t *me) { - return vm_call0(th, recv, id, argc, argv, me, defined_class); + return vm_call0(th, recv, id, argc, argv, me); } static inline VALUE @@ -270,22 +265,24 @@ vm_call_super(rb_thread_t *th, int argc, const VALUE *argv) VALUE recv = th->cfp->self; VALUE klass; ID id; - rb_method_entry_t *me; rb_control_frame_t *cfp = th->cfp; + const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp); - if (RUBY_VM_NORMAL_ISEQ_P(cfp->iseq) || NIL_P(cfp->klass)) { + if (RUBY_VM_NORMAL_ISEQ_P(cfp->iseq)) { rb_bug("vm_call_super: should not be reached"); } - klass = RCLASS_ORIGIN(cfp->klass); + klass = RCLASS_ORIGIN(me->defined_class); klass = RCLASS_SUPER(klass); - id = rb_vm_frame_method_entry(cfp)->def->original_id; - me = rb_method_entry(klass, id, &klass); + id = me->def->original_id; + me = rb_callable_method_entry(klass, id); + if (!me) { return method_missing(recv, id, argc, argv, MISSING_SUPER); } - - return vm_call0(th, recv, id, argc, argv, me, klass); + else { + return vm_call0(th, recv, id, argc, argv, me); + } } VALUE @@ -316,9 +313,8 @@ stack_check(void) } } -static inline rb_method_entry_t * - rb_search_method_entry(VALUE recv, ID mid, VALUE *defined_class_ptr); -static inline enum method_missing_reason rb_method_call_status(rb_thread_t *th, const rb_method_entry_t *me, call_type scope, VALUE self); +static inline const rb_callable_method_entry_t *rb_search_method_entry(VALUE recv, ID mid); +static inline enum method_missing_reason rb_method_call_status(rb_thread_t *th, const rb_callable_method_entry_t *me, call_type scope, VALUE self); /*! * \internal @@ -339,9 +335,7 @@ static inline VALUE rb_call0(VALUE recv, ID mid, int argc, const VALUE *argv, call_type scope, VALUE self) { - VALUE defined_class; - rb_method_entry_t *me = - rb_search_method_entry(recv, mid, &defined_class); + const rb_callable_method_entry_t *me = rb_search_method_entry(recv, mid); rb_thread_t *th = GET_THREAD(); enum method_missing_reason call_status = rb_method_call_status(th, me, scope, self); @@ -349,7 +343,7 @@ rb_call0(VALUE recv, ID mid, int argc, const VALUE *argv, return method_missing(recv, mid, argc, argv, call_status); } stack_check(); - return vm_call0(th, recv, mid, argc, argv, me, defined_class); + return vm_call0(th, recv, mid, argc, argv, me); } struct rescue_funcall_args { @@ -384,13 +378,12 @@ check_funcall_failed(struct rescue_funcall_args *args, VALUE e) static int check_funcall_respond_to(rb_thread_t *th, VALUE klass, VALUE recv, ID mid) { - VALUE defined_class; - const rb_method_entry_t *me = rb_method_entry(klass, idRespond_to, &defined_class); + const rb_callable_method_entry_t *me = rb_callable_method_entry(klass, idRespond_to); if (me && !METHOD_ENTRY_BASIC(me)) { const rb_block_t *passed_block = th->passed_block; VALUE args[2], result; - int arity = rb_method_entry_arity(me); + int arity = rb_method_entry_arity((const rb_method_entry_t *)me); if (arity > 2) rb_raise(rb_eArgError, "respond_to? must accept 1 or 2 arguments (requires %d)", arity); @@ -399,7 +392,7 @@ check_funcall_respond_to(rb_thread_t *th, VALUE klass, VALUE recv, ID mid) args[0] = ID2SYM(mid); args[1] = Qtrue; - result = vm_call0(th, recv, idRespond_to, arity, args, me, defined_class); + result = vm_call0(th, recv, idRespond_to, arity, args, me); th->passed_block = passed_block; if (!RTEST(result)) { return FALSE; @@ -409,7 +402,7 @@ check_funcall_respond_to(rb_thread_t *th, VALUE klass, VALUE recv, ID mid) } static int -check_funcall_callable(rb_thread_t *th, const rb_method_entry_t *me) +check_funcall_callable(rb_thread_t *th, const rb_callable_method_entry_t *me) { return rb_method_call_status(th, me, CALL_FCALL, th->cfp->self) == MISSING_NONE; } @@ -438,19 +431,18 @@ VALUE rb_check_funcall(VALUE recv, ID mid, int argc, const VALUE *argv) { VALUE klass = CLASS_OF(recv); - const rb_method_entry_t *me; + const rb_callable_method_entry_t *me; rb_thread_t *th = GET_THREAD(); - VALUE defined_class; if (!check_funcall_respond_to(th, klass, recv, mid)) return Qundef; - me = rb_search_method_entry(recv, mid, &defined_class); + me = rb_search_method_entry(recv, mid); if (!check_funcall_callable(th, me)) { return check_funcall_missing(th, klass, recv, mid, argc, argv); } stack_check(); - return vm_call0(th, recv, mid, argc, argv, me, defined_class); + return vm_call0(th, recv, mid, argc, argv, me); } VALUE @@ -458,21 +450,20 @@ rb_check_funcall_with_hook(VALUE recv, ID mid, int argc, const VALUE *argv, rb_check_funcall_hook *hook, VALUE arg) { VALUE klass = CLASS_OF(recv); - const rb_method_entry_t *me; + const rb_callable_method_entry_t *me; rb_thread_t *th = GET_THREAD(); - VALUE defined_class; if (!check_funcall_respond_to(th, klass, recv, mid)) return Qundef; - me = rb_search_method_entry(recv, mid, &defined_class); + me = rb_search_method_entry(recv, mid); if (!check_funcall_callable(th, me)) { (*hook)(FALSE, recv, mid, argc, argv, arg); return check_funcall_missing(th, klass, recv, mid, argc, argv); } stack_check(); (*hook)(TRUE, recv, mid, argc, argv, arg); - return vm_call0(th, recv, mid, argc, argv, me, defined_class); + return vm_call0(th, recv, mid, argc, argv, me); } static const char * @@ -511,8 +502,8 @@ rb_type_str(enum ruby_value_type type) #undef type_case } -static inline rb_method_entry_t * -rb_search_method_entry(VALUE recv, ID mid, VALUE *defined_class_ptr) +static inline const rb_callable_method_entry_t * +rb_search_method_entry(VALUE recv, ID mid) { VALUE klass = CLASS_OF(recv); @@ -550,11 +541,11 @@ rb_search_method_entry(VALUE recv, ID mid, VALUE *defined_class_ptr) rb_id2str(mid), type, (void *)recv, flags); } } - return rb_method_entry(klass, mid, defined_class_ptr); + return rb_callable_method_entry(klass, mid); } static inline enum method_missing_reason -rb_method_call_status(rb_thread_t *th, const rb_method_entry_t *me, call_type scope, VALUE self) +rb_method_call_status(rb_thread_t *th, const rb_callable_method_entry_t *me, call_type scope, VALUE self) { VALUE klass; ID oid; @@ -565,10 +556,11 @@ rb_method_call_status(rb_thread_t *th, const rb_method_entry_t *me, call_type sc return scope == CALL_VCALL ? MISSING_VCALL : MISSING_NOENTRY; } if (me->def->type == VM_METHOD_TYPE_REFINED) { - me = rb_resolve_refined_method(Qnil, me, NULL); + me = rb_resolve_refined_method_callable(Qnil, me); if (UNDEFINED_METHOD_ENTRY_P(me)) goto undefined; } - klass = me->klass; + + klass = me->owner; oid = me->def->original_id; visi = METHOD_ENTRY_VISI(me); @@ -1306,7 +1298,6 @@ eval_string_with_cref(VALUE self, VALUE src, VALUE scope, rb_cref_t *const cref_ } } vm_set_eval_stack(th, iseqval, cref, base_block); - th->cfp->klass = CLASS_OF(base_block->self); RB_GC_GUARD(crefval); if (0) { /* for debug */ diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 76f5bbe42a..639efeef1c 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -22,8 +22,7 @@ #define INLINE inline #endif -static rb_control_frame_t * -vm_get_ruby_level_caller_cfp(const rb_thread_t *th, const rb_control_frame_t *cfp); +static rb_control_frame_t *vm_get_ruby_level_caller_cfp(const rb_thread_t *th, const rb_control_frame_t *cfp); VALUE ruby_vm_sysstack_error_copy(void) @@ -40,6 +39,34 @@ vm_stackoverflow(void) } #if VM_CHECK_MODE > 0 + +static int +callable_class_p(VALUE klass) +{ +#if VM_CHECK_MODE >= 2 + while (klass) { + if (klass == rb_cBasicObject) { + return TRUE; + } + klass = RCLASS_SUPER(klass); + } + return FALSE; +#else + return klass != 0; +#endif +} + +static int +callable_method_entry_p(const rb_callable_method_entry_t *me) +{ + if (me == NULL || callable_class_p(me->defined_class)) { + return TRUE; + } + else { + return FALSE; + } +} + static void check_frame(int magic, int req_block, int req_me, int req_cref, VALUE specval, VALUE cref_or_me) { @@ -76,6 +103,14 @@ check_frame(int magic, int req_block, int req_me, int req_cref, VALUE specval, V } } } + + if (cref_or_me_type == imemo_ment) { + const rb_callable_method_entry_t *me = (const rb_callable_method_entry_t *)cref_or_me; + + if (!callable_method_entry_p(me)) { + rb_bug("vm_push_frame: ment (%s) should be callable on %x frame.", rb_obj_info(cref_or_me), magic); + } + } } #endif @@ -84,7 +119,6 @@ vm_push_frame(rb_thread_t *th, const rb_iseq_t *iseq, VALUE type, VALUE self, - VALUE klass, VALUE specval, VALUE cref_or_me, const VALUE *pc, @@ -147,19 +181,6 @@ vm_push_frame(rb_thread_t *th, cfp->block_iseq = 0; cfp->proc = 0; - if (klass) { - cfp->klass = klass; - } - else { - rb_control_frame_t *prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); - if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, prev_cfp)) { - cfp->klass = Qnil; - } - else { - cfp->klass = prev_cfp->klass; - } - } - if (VMDEBUG == 2) { SDR(); } @@ -334,7 +355,7 @@ vm_getspecial(rb_thread_t *th, VALUE *lep, rb_num_t key, rb_num_t type) return val; } -static rb_method_entry_t * +static rb_callable_method_entry_t * check_method_entry(VALUE obj, int can_be_svar) { if (obj == Qfalse) return NULL; @@ -345,7 +366,7 @@ check_method_entry(VALUE obj, int can_be_svar) switch (imemo_type(obj)) { case imemo_ment: - return (rb_method_entry_t *)obj; + return (rb_callable_method_entry_t *)obj; case imemo_cref: return NULL; case imemo_svar: @@ -360,11 +381,11 @@ check_method_entry(VALUE obj, int can_be_svar) } } -const rb_method_entry_t * +const rb_callable_method_entry_t * rb_vm_frame_method_entry(const rb_control_frame_t *cfp) { VALUE *ep = cfp->ep; - rb_method_entry_t *me; + rb_callable_method_entry_t *me; while (!VM_EP_LEP_P(ep)) { if ((me = check_method_entry(ep[-1], FALSE)) != NULL) return me; @@ -375,7 +396,7 @@ rb_vm_frame_method_entry(const rb_control_frame_t *cfp) } static rb_cref_t * -method_entry_cref(rb_method_entry_t *me) +method_entry_cref(rb_callable_method_entry_t *me) { switch (me->def->type) { case VM_METHOD_TYPE_ISEQ: @@ -396,7 +417,7 @@ check_cref(VALUE obj, int can_be_svar) switch (imemo_type(obj)) { case imemo_ment: - return method_entry_cref((rb_method_entry_t *)obj); + return method_entry_cref((rb_callable_method_entry_t *)obj); case imemo_cref: return (rb_cref_t *)obj; case imemo_svar: @@ -558,15 +579,7 @@ vm_check_if_namespace(VALUE klass) static inline VALUE vm_get_iclass(rb_control_frame_t *cfp, VALUE klass) { - if (RB_TYPE_P(klass, T_MODULE) && - FL_TEST(klass, RMODULE_IS_OVERLAID) && - RB_TYPE_P(cfp->klass, T_ICLASS) && - RBASIC(cfp->klass)->klass == klass) { - return cfp->klass; - } - else { - return klass; - } + return klass; } static inline VALUE @@ -1040,7 +1053,8 @@ vm_search_method(rb_call_info_t *ci, VALUE recv) } #endif - ci->me = rb_method_entry(klass, ci->mid, &ci->defined_class); + ci->me = rb_callable_method_entry(klass, ci->mid); + VM_ASSERT(callable_method_entry_p(ci->me)); ci->klass = klass; ci->call = vm_call_general; #if OPT_INLINE_METHOD_CACHE @@ -1050,7 +1064,7 @@ vm_search_method(rb_call_info_t *ci, VALUE recv) } static inline int -check_cfunc(const rb_method_entry_t *me, VALUE (*func)()) +check_cfunc(const rb_callable_method_entry_t *me, VALUE (*func)()) { if (me && me->def->type == VM_METHOD_TYPE_CFUNC && me->def->body.cfunc.func == func) { @@ -1114,12 +1128,11 @@ rb_equal_opt(VALUE obj1, VALUE obj2) ci.klass = 0; ci.method_state = 0; ci.me = NULL; - ci.defined_class = 0; + ci.class_serial = 0; return opt_eq_func(obj1, obj2, &ci); } -static VALUE -vm_call0(rb_thread_t*, VALUE, ID, int, const VALUE*, const rb_method_entry_t*, VALUE); +static VALUE vm_call0(rb_thread_t*, VALUE, ID, int, const VALUE*, const rb_callable_method_entry_t *); static VALUE check_match(VALUE pattern, VALUE target, enum vm_check_match_type type) @@ -1133,10 +1146,9 @@ check_match(VALUE pattern, VALUE target, enum vm_check_match_type type) } /* fall through */ case VM_CHECKMATCH_TYPE_CASE: { - VALUE defined_class; - const rb_method_entry_t *me = rb_method_entry_with_refinements(CLASS_OF(pattern), idEqq, &defined_class); + const rb_callable_method_entry_t *me = rb_callable_method_entry_with_refinements(CLASS_OF(pattern), idEqq); if (me) { - return vm_call0(GET_THREAD(), pattern, idEqq, 1, &target, me, defined_class); + return vm_call0(GET_THREAD(), pattern, idEqq, 1, &target, me); } else { /* fallback to funcall (e.g. method_missing) */ @@ -1352,7 +1364,7 @@ vm_call_iseq_setup_normal(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info { int i, local_size; VALUE *argv = cfp->sp - ci->argc; - const rb_method_entry_t *me = ci->me; + const rb_callable_method_entry_t *me = ci->me; rb_iseq_t *iseq = def_iseq_ptr(me->def); VALUE *sp = argv + iseq->param.size; @@ -1361,7 +1373,7 @@ vm_call_iseq_setup_normal(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info *sp++ = Qnil; } - vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD, ci->recv, ci->defined_class, + vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD, ci->recv, VM_ENVVAL_BLOCK_PTR(ci->blockptr), (VALUE)me, iseq->iseq_encoded + ci->aux.opt_pc, sp, 0, iseq->stack_max); @@ -1374,7 +1386,7 @@ vm_call_iseq_setup_tailcall(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_in { int i; VALUE *argv = cfp->sp - ci->argc; - const rb_method_entry_t *me = ci->me; + const rb_callable_method_entry_t *me = ci->me; rb_iseq_t *iseq = def_iseq_ptr(me->def); VALUE *src_argv = argv; VALUE *sp_orig, *sp; @@ -1401,8 +1413,7 @@ vm_call_iseq_setup_tailcall(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_in } vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD | finish_flag, - ci->recv, ci->defined_class, - VM_ENVVAL_BLOCK_PTR(ci->blockptr), (VALUE)me, + ci->recv, VM_ENVVAL_BLOCK_PTR(ci->blockptr), (VALUE)me, iseq->iseq_encoded + ci->aux.opt_pc, sp, 0, iseq->stack_max); cfp->sp = sp_orig; @@ -1541,7 +1552,7 @@ vm_profile_show_result(void) static inline const rb_method_cfunc_t * -vm_method_cfunc_entry(const rb_method_entry_t *me) +vm_method_cfunc_entry(const rb_callable_method_entry_t *me) { #if VM_DEBUG_VERIFY_METHOD_CACHE switch (me->def->type) { @@ -1571,20 +1582,19 @@ static VALUE vm_call_cfunc_with_frame(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci) { VALUE val; - const rb_method_entry_t *me = ci->me; + const rb_callable_method_entry_t *me = ci->me; const rb_method_cfunc_t *cfunc = vm_method_cfunc_entry(me); int len = cfunc->argc; /* don't use `ci' after EXEC_EVENT_HOOK because ci can be override */ VALUE recv = ci->recv; - VALUE defined_class = ci->defined_class; rb_block_t *blockptr = ci->blockptr; int argc = ci->argc; - RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, me->klass, me->called_id); - EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, me->called_id, me->klass, Qundef); + RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, me->owner, me->called_id); + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, me->called_id, me->owner, Qundef); - vm_push_frame(th, NULL, VM_FRAME_MAGIC_CFUNC, recv, defined_class, + vm_push_frame(th, NULL, VM_FRAME_MAGIC_CFUNC, recv, VM_ENVVAL_BLOCK_PTR(blockptr), (VALUE)me, 0, th->cfp->sp, 1, 0); @@ -1600,8 +1610,8 @@ vm_call_cfunc_with_frame(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_i vm_pop_frame(th); - EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, me->called_id, me->klass, val); - RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, me->klass, me->called_id); + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, me->called_id, me->owner, val); + RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, me->owner, me->called_id); return val; } @@ -1644,15 +1654,15 @@ static VALUE vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci) { VALUE val; - const rb_method_entry_t *me = ci->me; + const rb_callable_method_entry_t *me = ci->me; int len = vm_method_cfunc_entry(me)->argc; VALUE recv = ci->recv; CALLER_SETUP_ARG(reg_cfp, ci); if (len >= 0) rb_check_arity(ci->argc, len, len); - RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, me->klass, me->called_id); - EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, me->called_id, me->klass, Qnil); + RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, me->owner, me->called_id); + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, me->called_id, me->owner, Qnil); if (!(ci->me->def->flag & METHOD_VISI_PROTECTED) && !(ci->flag & VM_CALL_ARGS_SPLAT) && @@ -1661,8 +1671,8 @@ vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci) } val = vm_call_cfunc_latter(th, reg_cfp, ci); - EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, me->called_id, me->klass, val); - RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, me->klass, me->called_id); + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, me->called_id, me->owner, val); + RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, me->owner, me->called_id); return val; } @@ -1671,11 +1681,11 @@ void rb_vm_call_cfunc_push_frame(rb_thread_t *th) { rb_call_info_t *ci = th->passed_ci; - const rb_method_entry_t *me = ci->me; + const rb_callable_method_entry_t *me = ci->me; th->passed_ci = 0; - vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, ci->recv, ci->defined_class, - VM_ENVVAL_BLOCK_PTR(ci->blockptr), (VALUE)me /* cref */, + vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, + ci->recv, VM_ENVVAL_BLOCK_PTR(ci->blockptr), (VALUE)me /* cref */, 0, th->cfp->sp + ci->aux.inc_sp, 1, 0); if (ci->call != vm_call_general) { @@ -1716,7 +1726,7 @@ vm_call_bmethod_body(rb_thread_t *th, rb_call_info_t *ci, const VALUE *argv) /* control block frame */ th->passed_bmethod_me = ci->me; GetProcPtr(ci->me->def->body.proc, proc); - val = vm_invoke_bmethod(th, proc, ci->recv, ci->defined_class, ci->argc, argv, ci->blockptr); + val = vm_invoke_bmethod(th, proc, ci->recv, ci->argc, argv, ci->blockptr); return val; } @@ -1790,7 +1800,7 @@ vm_call_opt_send(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *c ci->argc -= 1; DEC_SP(1); } - ci->me = rb_method_entry_without_refinements(CLASS_OF(ci->recv), ci->mid, &ci->defined_class); + ci->me = rb_callable_method_entry_without_refinements(CLASS_OF(ci->recv), ci->mid); ci->flag = VM_CALL_FCALL | VM_CALL_OPT_SEND; @@ -1828,7 +1838,7 @@ vm_call_method_missing(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_inf ci_entry.mid = idMethodMissing; ci_entry.blockptr = ci->blockptr; ci_entry.recv = ci->recv; - ci_entry.me = rb_method_entry(CLASS_OF(ci_entry.recv), idMethodMissing, &ci_entry.defined_class); + ci_entry.me = rb_callable_method_entry(CLASS_OF(ci_entry.recv), idMethodMissing); ci_entry.kw_arg = NULL; /* shift arguments: m(a, b, c) #=> method_missing(:m, a, b, c) */ @@ -1892,6 +1902,55 @@ find_defined_class_by_owner(VALUE current_class, VALUE target_owner) return current_class; /* maybe module function */ } +static rb_method_definition_t *method_definition_create(rb_method_type_t type, ID mid); +static void method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *def, void *opts); + +static const rb_callable_method_entry_t * +aliased_callable_method_entry(const rb_callable_method_entry_t *me) +{ + const rb_method_entry_t *orig_me = me->def->body.alias.original_me; + const rb_callable_method_entry_t *cme; + + if (orig_me->defined_class == 0) { + VALUE defined_class = find_defined_class_by_owner(me->defined_class, orig_me->owner); + VM_ASSERT(RB_TYPE_P(orig_me->owner, T_MODULE)); + cme = rb_method_entry_complement_defined_class(orig_me, defined_class); + + if (me->def->alias_count == 0) { + RB_OBJ_WRITE(me, &me->def->body.alias.original_me, cme); + } + else { + method_definition_set((rb_method_entry_t *)me, + method_definition_create(VM_METHOD_TYPE_ALIAS, me->def->original_id), + (void *)cme); + } + } + else { + cme = (const rb_callable_method_entry_t *)orig_me; + } + + VM_ASSERT(callable_method_entry_p(cme)); + return cme; +} + +static const rb_callable_method_entry_t * +refined_method_callable_without_refinement(const rb_callable_method_entry_t *me) +{ + const rb_method_entry_t *orig_me = me->def->body.refined.orig_me; + const rb_callable_method_entry_t *cme; + + if (orig_me->defined_class == 0) { + cme = NULL; + rb_notimplement(); + } + else { + cme = (const rb_callable_method_entry_t *)orig_me; + } + + VM_ASSERT(callable_method_entry_p(cme)); + return cme; +} + static #ifdef _MSC_VER __forceinline @@ -1904,12 +1963,16 @@ vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci) int enable_fastpath = 1; rb_call_info_t ci_temp; + VM_ASSERT(callable_method_entry_p(ci->me)); + start_method_dispatch: + VM_ASSERT(callable_method_entry_p(ci->me)); if (ci->me != 0) { if (LIKELY(METHOD_ENTRY_VISI(ci->me) == METHOD_VISI_PUBLIC && METHOD_ENTRY_SAFE(ci->me) == 0)) { VALUE klass; normal_method_dispatch: + VM_ASSERT(callable_method_entry_p(ci->me)); switch (ci->me->def->type) { case VM_METHOD_TYPE_ISEQ:{ CI_SET_FASTPATH(ci, vm_call_iseq_setup, enable_fastpath); @@ -1943,7 +2006,7 @@ vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci) return vm_call_bmethod(th, cfp, ci); } case VM_METHOD_TYPE_ZSUPER:{ - klass = ci->me->klass; + klass = ci->me->owner; klass = RCLASS_ORIGIN(klass); zsuper_method_dispatch: klass = RCLASS_SUPER(klass); @@ -1954,7 +2017,7 @@ vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci) ci_temp = *ci; ci = &ci_temp; - ci->me = rb_method_entry(klass, ci->mid, &ci->defined_class); + ci->me = rb_callable_method_entry(klass, ci->mid); if (ci->me != 0) { goto normal_method_dispatch; @@ -1963,11 +2026,9 @@ vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci) goto start_method_dispatch; } } - case VM_METHOD_TYPE_ALIAS: { - ci->me = ci->me->def->body.alias.original_me; - ci->defined_class = find_defined_class_by_owner(ci->defined_class, ci->me->klass /* owner */); + case VM_METHOD_TYPE_ALIAS: + ci->me = aliased_callable_method_entry(ci->me); goto normal_method_dispatch; - } case VM_METHOD_TYPE_OPTIMIZED:{ switch (ci->me->def->body.optimize_type) { case OPTIMIZED_METHOD_TYPE_SEND: @@ -1987,26 +2048,24 @@ vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci) case VM_METHOD_TYPE_REFINED:{ const rb_cref_t *cref = rb_vm_get_cref(cfp->ep); VALUE refinements = cref ? CREF_REFINEMENTS(cref) : Qnil; - VALUE refinement, defined_class; - rb_method_entry_t *me; + VALUE refinement; + const rb_callable_method_entry_t *me; - refinement = find_refinement(refinements, - ci->defined_class); + refinement = find_refinement(refinements, ci->me->owner); if (NIL_P(refinement)) { goto no_refinement_dispatch; } - me = rb_method_entry(refinement, ci->mid, &defined_class); + me = rb_callable_method_entry(refinement, ci->mid); if (me) { if (ci->call == vm_call_super_method) { const rb_control_frame_t *top_cfp = current_method_entry(th, cfp); - const rb_method_entry_t *top_me = rb_vm_frame_method_entry(top_cfp); + const rb_callable_method_entry_t *top_me = rb_vm_frame_method_entry(top_cfp); if (top_me && rb_method_definition_eq(me->def, top_me->def)) { goto no_refinement_dispatch; } } ci->me = me; - ci->defined_class = defined_class; if (me->def->type != VM_METHOD_TYPE_REFINED) { goto start_method_dispatch; } @@ -2018,14 +2077,15 @@ vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci) no_refinement_dispatch: if (ci->me->def->body.refined.orig_me) { - ci->me = ci->me->def->body.refined.orig_me; + ci->me = refined_method_callable_without_refinement(ci->me); + if (UNDEFINED_METHOD_ENTRY_P(ci->me)) { ci->me = 0; } goto start_method_dispatch; } else { - klass = ci->me->klass; + klass = ci->me->owner; goto zsuper_method_dispatch; } } @@ -2045,7 +2105,7 @@ vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci) } else if (!(ci->flag & VM_CALL_OPT_SEND) && (METHOD_ENTRY_VISI(ci->me) == METHOD_VISI_PROTECTED)) { enable_fastpath = 0; - if (!rb_obj_is_kind_of(cfp->self, ci->defined_class)) { + if (!rb_obj_is_kind_of(cfp->self, ci->me->defined_class)) { ci->aux.method_missing_reason = MISSING_PROTECTED; return vm_call_method_missing(th, cfp, ci); } @@ -2113,7 +2173,7 @@ vm_super_outside(void) static int vm_search_superclass(rb_control_frame_t *reg_cfp, rb_iseq_t *iseq, VALUE sigval, rb_call_info_t *ci) { - const rb_method_entry_t *me; + const rb_callable_method_entry_t *me; while (iseq && !iseq->klass) { iseq = iseq->parent_iseq; @@ -2157,10 +2217,11 @@ vm_search_superclass(rb_control_frame_t *reg_cfp, rb_iseq_t *iseq, VALUE sigval, } ci->mid = me->def->original_id; - ci->klass = vm_search_normal_superclass(lcfp->klass); + ci->klass = vm_search_normal_superclass(me->defined_class); } else { - ci->klass = vm_search_normal_superclass(reg_cfp->klass); + me = rb_vm_frame_method_entry(reg_cfp); + ci->klass = vm_search_normal_superclass(me->defined_class); } return 0; @@ -2172,12 +2233,14 @@ vm_search_super_method(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_inf VALUE current_defined_class; rb_iseq_t *iseq = GET_ISEQ(); VALUE sigval = TOPN(ci->argc); + const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(reg_cfp); - current_defined_class = GET_CFP()->klass; - if (NIL_P(current_defined_class)) { + if (!me) { vm_super_outside(); } + current_defined_class = me->defined_class; + if (!NIL_P(RCLASS_REFINED_CLASS(current_defined_class))) { current_defined_class = RCLASS_REFINED_CLASS(current_defined_class); } @@ -2212,7 +2275,7 @@ vm_search_super_method(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_inf } /* TODO: use inline cache */ - ci->me = rb_method_entry(ci->klass, ci->mid, &ci->defined_class); + ci->me = rb_callable_method_entry(ci->klass, ci->mid); ci->call = vm_call_super_method; while (iseq && !iseq->klass) { @@ -2220,8 +2283,8 @@ vm_search_super_method(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_inf } if (ci->me && ci->me->def->type == VM_METHOD_TYPE_ISEQ && def_iseq_ptr(ci->me->def) == iseq) { - ci->klass = RCLASS_SUPER(ci->defined_class); - ci->me = rb_method_entry(ci->klass, ci->mid, &ci->defined_class); + ci->klass = RCLASS_SUPER(ci->me->defined_class); + ci->me = rb_callable_method_entry(ci->klass, ci->mid); } } @@ -2242,15 +2305,14 @@ block_proc_is_lambda(const VALUE procval) } static inline VALUE -vm_yield_with_cfunc(rb_thread_t *th, const rb_block_t *block, - VALUE self, VALUE defined_class, +vm_yield_with_cfunc(rb_thread_t *th, const rb_block_t *block, VALUE self, int argc, const VALUE *argv, const rb_block_t *blockargptr) { const struct vm_ifunc *ifunc = (struct vm_ifunc *)block->iseq; VALUE val, arg, blockarg; int lambda = block_proc_is_lambda(block->proc); - const rb_method_entry_t *me = th->passed_bmethod_me; + const rb_callable_method_entry_t *me = th->passed_bmethod_me; th->passed_bmethod_me = NULL; if (lambda) { @@ -2276,8 +2338,7 @@ vm_yield_with_cfunc(rb_thread_t *th, const rb_block_t *block, } vm_push_frame(th, (rb_iseq_t *)ifunc, VM_FRAME_MAGIC_IFUNC, - self, defined_class, - VM_ENVVAL_PREV_EP_PTR(block->ep), (VALUE)me, + self, VM_ENVVAL_PREV_EP_PTR(block->ep), (VALUE)me, 0, th->cfp->sp, 1, 0); val = (*ifunc->func) (arg, ifunc->data, argc, argv, blockarg); @@ -2332,7 +2393,6 @@ vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci vm_push_frame(th, iseq, is_lambda ? VM_FRAME_MAGIC_LAMBDA : VM_FRAME_MAGIC_BLOCK, block->self, - block->klass, VM_ENVVAL_PREV_EP_PTR(block->ep), 0, iseq->iseq_encoded + opt_pc, rsp + arg_size, @@ -2343,7 +2403,7 @@ vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci else { VALUE val; CALLER_SETUP_ARG(th->cfp, ci); - val = vm_yield_with_cfunc(th, block, block->self, block->klass, + val = vm_yield_with_cfunc(th, block, block->self, ci->argc, STACK_ADDR_FROM_TOP(ci->argc), 0); POPN(ci->argc); /* TODO: should put before C/yield? */ return val; @@ -2461,7 +2521,7 @@ vm_defined(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_num_t op_type, VALUE break; case DEFINED_METHOD:{ VALUE klass = CLASS_OF(v); - const rb_method_entry_t *me = rb_method_entry(klass, SYM2ID(obj), 0); + const rb_method_entry_t *me = rb_method_entry(klass, SYM2ID(obj)); if (me) { switch (METHOD_ENTRY_VISI(me)) { diff --git a/vm_method.c b/vm_method.c index d9c0b4fc97..0ae14c833a 100644 --- a/vm_method.c +++ b/vm_method.c @@ -93,6 +93,15 @@ rb_clear_method_cache_by_class(VALUE klass) else { rb_class_clear_method_cache(klass, Qnil); } + + if (RB_TYPE_P(klass, T_MODULE)) { + rb_subclass_entry_t *entry = RCLASS_EXT(klass)->subclasses; + + for (; entry != NULL; entry = entry->next) { + struct st_table *table = RCLASS_CALLABLE_M_TBL(entry->klass); + if (table) st_clear(table); + } + } } } @@ -201,7 +210,7 @@ setup_method_cfunc_struct(rb_method_cfunc_t *cfunc, VALUE (*func)(), int argc) } static void -method_definition_set(rb_method_entry_t *me, rb_method_definition_t *def, void *opts) +method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *def, void *opts) { *(rb_method_definition_t **)&me->def = def; @@ -263,8 +272,12 @@ method_definition_set(rb_method_entry_t *me, rb_method_definition_t *def, void * def->body.optimize_type = (enum method_optimized_type)opts; return; case VM_METHOD_TYPE_REFINED: - RB_OBJ_WRITE(me, &def->body.refined.orig_me, (rb_method_entry_t *)opts); - return; + { + const rb_method_refined_t *refined = (rb_method_refined_t *)opts; + RB_OBJ_WRITE(me, &def->body.refined.orig_me, refined->orig_me); + RB_OBJ_WRITE(me, &def->body.refined.owner, refined->owner); + return; + } case VM_METHOD_TYPE_ALIAS: RB_OBJ_WRITE(me, &def->body.alias.original_me, (rb_method_entry_t *)opts); return; @@ -273,7 +286,6 @@ method_definition_set(rb_method_entry_t *me, rb_method_definition_t *def, void * case VM_METHOD_TYPE_MISSING: return; } - rb_bug("rb_add_method: unsupported method type (%d)\n", def->type); } } @@ -296,6 +308,7 @@ method_definition_reset(const rb_method_entry_t *me) break; case VM_METHOD_TYPE_REFINED: RB_OBJ_WRITTEN(me, Qundef, def->body.refined.orig_me); + RB_OBJ_WRITTEN(me, Qundef, def->body.refined.owner); break; case VM_METHOD_TYPE_ALIAS: RB_OBJ_WRITTEN(me, Qundef, def->body.alias.original_me); @@ -328,46 +341,83 @@ method_definition_addref(rb_method_definition_t *def) return def; } +static rb_method_entry_t * +rb_method_entry_alloc(ID called_id, VALUE owner, VALUE defined_class, const rb_method_definition_t *def) +{ + rb_method_entry_t *me = (rb_method_entry_t *)rb_imemo_new(imemo_ment, (VALUE)def, (VALUE)called_id, owner, defined_class); + return me; +} + +static VALUE +filter_defined_class(VALUE klass) +{ + switch (BUILTIN_TYPE(klass)) { + case T_CLASS: + return klass; + case T_MODULE: + return 0; + case T_ICLASS: + break; + } + rb_bug("filter_defined_class: %s", rb_obj_info(klass)); +} + rb_method_entry_t * rb_method_entry_create(ID called_id, VALUE klass, rb_method_visibility_t visi, const rb_method_definition_t *def) { - rb_method_entry_t *me = (rb_method_entry_t *)rb_imemo_new(imemo_ment, (VALUE)def, (VALUE)called_id, (VALUE)klass, 0); + rb_method_entry_t *me = rb_method_entry_alloc(called_id, klass, filter_defined_class(klass), def); METHOD_ENTRY_FLAGS_SET(me, visi, ruby_running ? FALSE : TRUE, rb_safe_level()); if (def != NULL) method_definition_reset(me); return me; } -rb_method_entry_t * +const rb_method_entry_t * rb_method_entry_clone(const rb_method_entry_t *src_me) { - rb_method_entry_t *me = rb_method_entry_create(src_me->called_id, src_me->klass, - METHOD_ENTRY_VISI(src_me), - method_definition_addref(src_me->def)); + rb_method_entry_t *me = rb_method_entry_alloc(src_me->called_id, src_me->owner, src_me->defined_class, + method_definition_addref(src_me->def)); + METHOD_ENTRY_FLAGS_COPY(me, src_me); return me; } +const rb_callable_method_entry_t * +rb_method_entry_complement_defined_class(const rb_method_entry_t *src_me, VALUE defined_class) +{ + rb_method_entry_t *me = rb_method_entry_alloc(src_me->called_id, src_me->owner, defined_class, + method_definition_addref(src_me->def)); + METHOD_ENTRY_FLAGS_COPY(me, src_me); + return (rb_callable_method_entry_t *)me; +} + void rb_method_entry_copy(rb_method_entry_t *dst, const rb_method_entry_t *src) { *(rb_method_definition_t **)&dst->def = method_definition_addref(src->def); method_definition_reset(dst); dst->called_id = src->called_id; - RB_OBJ_WRITE((VALUE)dst, &dst->klass, src->klass); + RB_OBJ_WRITE((VALUE)dst, &dst->owner, src->owner); + RB_OBJ_WRITE((VALUE)dst, &dst->defined_class, src->defined_class); + METHOD_ENTRY_FLAGS_COPY(dst, src); } static void -make_method_entry_refined(rb_method_entry_t *me) +make_method_entry_refined(VALUE owner, rb_method_entry_t *me) { if (me->def->type == VM_METHOD_TYPE_REFINED) { return; } else { - rb_method_entry_t *cloned_me; + struct { + const struct rb_method_entry_struct *orig_me; + VALUE owner; + } refined; + + rb_vm_check_redefinition_opt_method(me, me->owner); - rb_vm_check_redefinition_opt_method(me, me->klass); - cloned_me = rb_method_entry_clone(me); + refined.orig_me = rb_method_entry_clone(me); + refined.owner = owner; - method_definition_set(me, method_definition_create(VM_METHOD_TYPE_REFINED, me->called_id), (void *)cloned_me); + method_definition_set(me, method_definition_create(VM_METHOD_TYPE_REFINED, me->called_id), (void *)&refined); METHOD_ENTRY_VISI_SET(me, METHOD_VISI_PUBLIC); } } @@ -378,7 +428,7 @@ 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); + make_method_entry_refined(refined_class, me); rb_clear_method_cache_by_class(refined_class); } else { @@ -490,7 +540,7 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil } if (make_refined) { - make_method_entry_refined(me); + make_method_entry_refined(klass, me); } st_insert(mtbl, mid, (st_data_t) me); @@ -607,7 +657,7 @@ search_method(VALUE klass, ID id, VALUE *defined_class_ptr) return me; } -rb_method_entry_t * +const rb_method_entry_t * rb_method_entry_at(VALUE klass, ID id) { return lookup_method_table(klass, id); @@ -619,9 +669,9 @@ rb_method_entry_at(VALUE klass, ID id) * if you need method entry with method cache (normal case), use * rb_method_entry() simply. */ -rb_method_entry_t * -rb_method_entry_get_without_cache(VALUE klass, ID id, - VALUE *defined_class_ptr) +static rb_method_entry_t * +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, &defined_class); @@ -659,7 +709,7 @@ verify_method_cache(VALUE klass, ID id, VALUE defined_class, rb_method_entry_t * { VALUE actual_defined_class; rb_method_entry_t *actual_me = - rb_method_entry_get_without_cache(klass, id, &actual_defined_class); + method_entry_get_without_cache(klass, id, &actual_defined_class); if (me != actual_me || defined_class != actual_defined_class) { rb_bug("method cache verification failed"); @@ -667,8 +717,8 @@ verify_method_cache(VALUE klass, ID id, VALUE defined_class, rb_method_entry_t * } #endif -rb_method_entry_t * -rb_method_entry(VALUE klass, ID id, VALUE *defined_class_ptr) +static rb_method_entry_t * +method_entry_get(VALUE klass, ID id, VALUE *defined_class_ptr) { #if OPT_GLOBAL_METHOD_CACHE struct cache_entry *ent; @@ -676,104 +726,179 @@ rb_method_entry(VALUE klass, ID id, VALUE *defined_class_ptr) if (ent->method_state == GET_GLOBAL_METHOD_STATE() && ent->class_serial == RCLASS_SERIAL(klass) && ent->mid == id) { - if (defined_class_ptr) - *defined_class_ptr = ent->defined_class; #if VM_DEBUG_VERIFY_METHOD_CACHE verify_method_cache(klass, id, ent->defined_class, ent->me); #endif + if (defined_class_ptr) *defined_class_ptr = ent->defined_class; return ent->me; } #endif - return rb_method_entry_get_without_cache(klass, id, defined_class_ptr); + return method_entry_get_without_cache(klass, id, defined_class_ptr); +} + +const rb_method_entry_t * +rb_method_entry(VALUE klass, ID id) +{ + return method_entry_get(klass, id, NULL); } +static const rb_callable_method_entry_t * +prepare_callable_method_entry(VALUE defined_class, ID id, const rb_method_entry_t *me) +{ + struct st_table *mtbl; + const rb_callable_method_entry_t *cme; + + if (me && me->defined_class == 0) { + VM_ASSERT(RB_TYPE_P(defined_class, T_ICLASS)); + VM_ASSERT(me->defined_class == 0); + + if ((mtbl = RCLASS_EXT(defined_class)->callable_m_tbl) == NULL) { + mtbl = RCLASS_EXT(defined_class)->callable_m_tbl = st_init_numtable(); + } + + if (st_lookup(mtbl, id, (st_data_t *)&me)) { + cme = (rb_callable_method_entry_t *)me; + VM_ASSERT(callable_method_entry_p(cme)); + } + else { + cme = rb_method_entry_complement_defined_class(me, defined_class); + st_insert(mtbl, id, (st_data_t)cme); + VM_ASSERT(callable_method_entry_p(cme)); + } + } + else { + cme = (const rb_callable_method_entry_t *)me; + } + + VM_ASSERT(callable_method_entry_p(cme)); + return cme; +} + +const rb_callable_method_entry_t * +rb_callable_method_entry(VALUE klass, ID id) +{ + VALUE defined_class; + rb_method_entry_t *me = method_entry_get(klass, id, &defined_class); + return prepare_callable_method_entry(defined_class, id, me); +} + +static const rb_method_entry_t *resolve_refined_method(VALUE refinements, const rb_method_entry_t *me, VALUE *defined_class_ptr); + static const rb_method_entry_t * -get_original_method_entry(VALUE refinements, - const rb_method_entry_t *me, - VALUE *defined_class_ptr) +method_entry_resolve_refienment(VALUE klass, ID id, int with_refinement, VALUE *defined_class_ptr) +{ + const rb_method_entry_t *me = method_entry_get(klass, id, defined_class_ptr); + + if (me) { + if (me->def->type == VM_METHOD_TYPE_REFINED) { + if (with_refinement) { + const rb_cref_t *cref = rb_vm_cref(); + VALUE refinements = cref ? CREF_REFINEMENTS(cref) : Qnil; + me = resolve_refined_method(refinements, me, defined_class_ptr); + } + else { + me = resolve_refined_method(Qnil, me, defined_class_ptr); + } + + if (UNDEFINED_METHOD_ENTRY_P(me)) me = NULL; + } + } + + return me; +} + +const rb_method_entry_t * +rb_method_entry_with_refinements(VALUE klass, ID id) +{ + return method_entry_resolve_refienment(klass, id, TRUE, NULL); +} + +const rb_callable_method_entry_t * +rb_callable_method_entry_with_refinements(VALUE klass, ID id) +{ + VALUE defined_class; + const rb_method_entry_t *me = method_entry_resolve_refienment(klass, id, TRUE, &defined_class); + return prepare_callable_method_entry(defined_class, id, me); +} + +const rb_method_entry_t * +rb_method_entry_without_refinements(VALUE klass, ID id) +{ + return method_entry_resolve_refienment(klass, id, FALSE, NULL); +} + +const rb_callable_method_entry_t * +rb_callable_method_entry_without_refinements(VALUE klass, ID id) +{ + VALUE defined_class; + const rb_method_entry_t *me = method_entry_resolve_refienment(klass, id, FALSE, &defined_class); + return prepare_callable_method_entry(defined_class, id, me); +} + +static const rb_method_entry_t * +refiend_method_original_method_entry(VALUE refinements, const rb_method_entry_t *me, VALUE *defined_class_ptr) { VALUE super; if (me->def->body.refined.orig_me) { + if (defined_class_ptr) *defined_class_ptr = me->def->body.refined.orig_me->defined_class; return me->def->body.refined.orig_me; } - else if (!(super = RCLASS_SUPER(me->klass))) { + else if (!(super = RCLASS_SUPER(me->owner))) { return 0; } else { rb_method_entry_t *tmp_me; - tmp_me = rb_method_entry(super, me->called_id, - defined_class_ptr); - return rb_resolve_refined_method(refinements, tmp_me, - defined_class_ptr); + tmp_me = method_entry_get(super, me->called_id, defined_class_ptr); + return resolve_refined_method(refinements, tmp_me, defined_class_ptr); } } -const rb_method_entry_t * -rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me, - VALUE *defined_class_ptr) +static const rb_method_entry_t * +resolve_refined_method(VALUE refinements, const rb_method_entry_t *me, 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); + refinement = find_refinement(refinements, me->owner); if (NIL_P(refinement)) { - return get_original_method_entry(refinements, me, - 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; + return refiend_method_original_method_entry(refinements, me, defined_class_ptr); } else { - return get_original_method_entry(refinements, me, - defined_class_ptr); + tmp_me = method_entry_get(refinement, me->called_id, defined_class_ptr); + + if (tmp_me && tmp_me->def->type != VM_METHOD_TYPE_REFINED) { + return tmp_me; + } + else { + return refiend_method_original_method_entry(refinements, me, defined_class_ptr); + } } } else { - return (rb_method_entry_t *)me; + return me; } } const rb_method_entry_t * -rb_method_entry_with_refinements(VALUE klass, ID id, - VALUE *defined_class_ptr) +rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me) { - VALUE defined_class; - const rb_method_entry_t *me = rb_method_entry(klass, id, &defined_class); - - if (me && me->def->type == VM_METHOD_TYPE_REFINED) { - const rb_cref_t *cref = rb_vm_cref(); - VALUE refinements = cref ? CREF_REFINEMENTS(cref) : Qnil; - - me = rb_resolve_refined_method(refinements, me, &defined_class); - } - - if (defined_class_ptr) *defined_class_ptr = defined_class; - - return me; + return resolve_refined_method(refinements, me, NULL); } -const rb_method_entry_t * -rb_method_entry_without_refinements(VALUE klass, ID id, - VALUE *defined_class_ptr) +const rb_callable_method_entry_t * +rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me) { - VALUE defined_class; - const rb_method_entry_t *me = rb_method_entry(klass, id, &defined_class); + VALUE defined_class = me->defined_class; + const rb_method_entry_t *resolved_me = resolve_refined_method(refinements, (const rb_method_entry_t *)me, &defined_class); - if (me && me->def->type == VM_METHOD_TYPE_REFINED) { - me = rb_resolve_refined_method(Qnil, me, &defined_class); - } - if (defined_class_ptr) - *defined_class_ptr = defined_class; - if (UNDEFINED_METHOD_ENTRY_P(me)) { - return 0; + if (resolved_me && resolved_me->defined_class == 0) { + return rb_method_entry_complement_defined_class(resolved_me, defined_class); } else { - return me; + return (const rb_callable_method_entry_t *)resolved_me; } } @@ -889,7 +1014,7 @@ rb_export_method(VALUE klass, ID name, rb_method_visibility_t visi) int rb_method_boundp(VALUE klass, ID id, int ex) { - const rb_method_entry_t *me = rb_method_entry_without_refinements(klass, id, 0); + const rb_method_entry_t *me = rb_method_entry_without_refinements(klass, id); if (me != 0) { if ((ex & ~BOUND_RESPONDS) && @@ -1123,7 +1248,7 @@ check_definition(VALUE mod, VALUE mid, rb_method_visibility_t visi) const rb_method_entry_t *me; ID id = rb_check_id(&mid); if (!id) return Qfalse; - me = rb_method_entry_without_refinements(mod, id, 0); + me = rb_method_entry_without_refinements(mod, id); if (me) { if (METHOD_ENTRY_VISI(me) == visi) return Qtrue; } @@ -1354,7 +1479,7 @@ rb_alias(VALUE klass, ID alias_name, ID original_name) again: orig_me = search_method(klass, original_name, &defined_class); if (orig_me && orig_me->def->type == VM_METHOD_TYPE_REFINED) { - orig_me = rb_resolve_refined_method(Qnil, orig_me, &defined_class); + orig_me = rb_resolve_refined_method(Qnil, orig_me); } if (UNDEFINED_METHOD_ENTRY_P(orig_me) || @@ -1375,26 +1500,19 @@ rb_alias(VALUE klass, ID alias_name, ID original_name) if (visi == METHOD_VISI_UNDEF) visi = METHOD_ENTRY_VISI(orig_me); - if (defined_class != target_klass) { /* inter class/module alias */ - VALUE real_owner; + if (orig_me->defined_class == 0) { rb_method_entry_t *alias_me; - if (RB_TYPE_P(defined_class, T_ICLASS)) { - defined_class = real_owner = RBASIC_CLASS(defined_class); - } - else { - real_owner = defined_class; - } - - /* make mthod entry */ - alias_me = rb_add_method(target_klass, alias_name, VM_METHOD_TYPE_ALIAS, rb_method_entry_clone(orig_me), visi); - RB_OBJ_WRITE(alias_me, &alias_me->klass, defined_class); + alias_me = rb_add_method(target_klass, alias_name, VM_METHOD_TYPE_ALIAS, (void *)rb_method_entry_clone(orig_me), visi); alias_me->def->original_id = orig_me->called_id; - *(ID *)&alias_me->def->body.alias.original_me->called_id = alias_name; METHOD_ENTRY_SAFE_SET(alias_me, METHOD_ENTRY_SAFE(orig_me)); } else { - method_entry_set(target_klass, alias_name, orig_me, visi, defined_class); + rb_method_entry_t *alias_me; + + alias_me = method_entry_set(target_klass, alias_name, orig_me, visi, orig_me->owner); + RB_OBJ_WRITE(alias_me, &alias_me->owner, target_klass); + RB_OBJ_WRITE(alias_me, &alias_me->defined_class, defined_class); } } @@ -1690,7 +1808,7 @@ rb_mod_modfunc(int argc, VALUE *argv, VALUE module) int rb_method_basic_definition_p(VALUE klass, ID id) { - const rb_method_entry_t *me = rb_method_entry(klass, id, 0); + const rb_method_entry_t *me = rb_method_entry(klass, id); return (me && METHOD_ENTRY_BASIC(me)) ? TRUE : FALSE; } @@ -1876,7 +1994,7 @@ Init_eval_method(void) { #define REPLICATE_METHOD(klass, id) do { \ - const rb_method_entry_t *me = rb_method_entry((klass), (id), 0); \ + const rb_method_entry_t *me = rb_method_entry((klass), (id)); \ rb_method_entry_set((klass), (id), me, METHOD_ENTRY_VISI(me)); \ } while (0) |