diff --git a/ChangeLog b/ChangeLog index 97e7176f1c..8efc22573d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +Thu Aug 2 20:08:02 2012 Shugo Maeda + + * class.c, insns.def, method.h, proc.c, vm.c, vm_core.h, vm_eval.c, + vm_insnhelper.c, vm_insnhelper.h, vm_method.c: add klass to + rb_control_frame_t to implement super correctly. + Thu Aug 2 13:23:08 2012 NARUSE, Yui * configure.in (AC_PROG_CC): AC_PROG_CC tries clang at first on diff --git a/class.c b/class.c index 1253ce80b7..025f02f6da 100644 --- a/class.c +++ b/class.c @@ -1444,7 +1444,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")); + const rb_method_entry_t *me = rb_method_entry(CLASS_OF(obj), rb_intern("to_s"), 0); if (me && me->def && me->def->type == VM_METHOD_TYPE_CFUNC && me->def->body.cfunc.func == rb_any_to_s) return 1; diff --git a/insns.def b/insns.def index 5a0726cf39..a36a56e112 100644 --- a/insns.def +++ b/insns.def @@ -798,7 +798,7 @@ defined break; case DEFINED_METHOD:{ VALUE klass = CLASS_OF(v); - const rb_method_entry_t *me = rb_method_entry(klass, SYM2ID(obj)); + const rb_method_entry_t *me = rb_method_entry(klass, SYM2ID(obj), 0); if (me) { if (!(me->flag & NOEX_PRIVATE)) { @@ -967,7 +967,7 @@ defineclass /* enter scope */ vm_push_frame(th, class_iseq, VM_FRAME_MAGIC_CLASS, - klass, VM_ENVVAL_BLOCK_PTR(GET_BLOCK_PTR()), + klass, 0, VM_ENVVAL_BLOCK_PTR(GET_BLOCK_PTR()), class_iseq->iseq_encoded, GET_SP(), class_iseq->local_size, 0); RESTORE_REGS(); @@ -999,7 +999,7 @@ send (VALUE val) // inc += - (int)(op_argc + ((op_flag & VM_CALL_ARGS_BLOCKARG_BIT) ? 1 : 0)); { const rb_method_entry_t *me; - VALUE recv, klass; + VALUE recv, klass, defined_class; rb_block_t *blockptr = 0; VALUE flag = op_flag; int num = caller_setup_args(th, GET_CFP(), flag, (int)op_argc, @@ -1009,8 +1009,8 @@ send /* get receiver */ recv = TOPN(num); klass = CLASS_OF(recv); - me = vm_method_search(id, klass, ic); - CALL_METHOD(num, blockptr, flag, id, me, recv); + me = vm_method_search(id, klass, ic, &defined_class); + CALL_METHOD(num, blockptr, flag, id, me, recv, defined_class); } /** @@ -1033,20 +1033,26 @@ invokesuper VALUE recv, klass; ID id; const rb_method_entry_t *me; + rb_iseq_t *ip; flag = VM_CALL_SUPER_BIT | VM_CALL_FCALL_BIT; recv = GET_SELF(); vm_search_superclass(GET_CFP(), GET_ISEQ(), recv, TOPN(num), &id, &klass); - /* temporary measure for [Bug #2402] [Bug #2502] [Bug #3136] */ - if (!rb_obj_is_kind_of(recv, klass)) { - rb_raise(rb_eNotImpError, "super from singleton method that is defined to multiple classes is not supported; this will be fixed in 1.9.3 or later"); + ip = GET_ISEQ(); + while (ip && !ip->klass) { + ip = ip->parent_iseq; + } + again: + me = rb_method_entry(klass, id, &klass); + if (me && me->def->type == VM_METHOD_TYPE_ISEQ && + me->def->body.iseq == ip) { + klass = RCLASS_SUPER(klass); + goto again; } - me = rb_method_entry(klass, id); - - CALL_METHOD(num, blockptr, flag, id, me, recv); + CALL_METHOD(num, blockptr, flag, id, me, recv, klass); } /** @@ -1652,7 +1658,7 @@ opt_neq (VALUE val) { extern VALUE rb_obj_not_equal(VALUE obj1, VALUE obj2); - const rb_method_entry_t *me = vm_method_search(idNeq, CLASS_OF(recv), ic); + const rb_method_entry_t *me = vm_method_search(idNeq, CLASS_OF(recv), ic, 0); val = Qundef; if (check_cfunc(me, rb_obj_not_equal)) { @@ -2056,7 +2062,7 @@ opt_not (VALUE val) { extern VALUE rb_obj_not(VALUE obj); - const rb_method_entry_t *me = vm_method_search(idNot, CLASS_OF(recv), ic); + const rb_method_entry_t *me = vm_method_search(idNot, CLASS_OF(recv), ic, 0); if (check_cfunc(me, rb_obj_not)) { val = RTEST(recv) ? Qfalse : Qtrue; diff --git a/method.h b/method.h index 816e10f5b1..26333b4719 100644 --- a/method.h +++ b/method.h @@ -89,9 +89,9 @@ struct unlinked_method_entry_list_entry { void rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_flag_t noex); rb_method_entry_t *rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_flag_t noex); -rb_method_entry_t *rb_method_entry(VALUE klass, ID id); +rb_method_entry_t *rb_method_entry(VALUE klass, ID id, VALUE *define_class_ptr); -rb_method_entry_t *rb_method_entry_get_without_cache(VALUE klass, ID id); +rb_method_entry_t *rb_method_entry_get_without_cache(VALUE klass, ID id, VALUE *define_class_ptr); rb_method_entry_t *rb_method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *, rb_method_flag_t noex); int rb_method_entry_arity(const rb_method_entry_t *me); diff --git a/proc.c b/proc.c index 5612f95fac..54bef3b45d 100644 --- a/proc.c +++ b/proc.c @@ -17,6 +17,7 @@ struct METHOD { VALUE recv; VALUE rclass; + VALUE defined_class; ID id; rb_method_entry_t *me; struct unlinked_method_entry_list_entry *ume; @@ -889,6 +890,7 @@ 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); if (data->me) rb_mark_method_entry(data->me); @@ -935,7 +937,7 @@ static VALUE mnew(VALUE klass, VALUE obj, ID id, VALUE mclass, int scope) { VALUE method; - VALUE rclass = klass; + VALUE rclass = klass, defined_class; ID rid = id; struct METHOD *data; rb_method_entry_t *me, meb; @@ -943,7 +945,7 @@ mnew(VALUE klass, VALUE obj, ID id, VALUE mclass, int scope) rb_method_flag_t flag = NOEX_UNDEF; again: - me = rb_method_entry(klass, id); + me = rb_method_entry(klass, id, &defined_class); if (UNDEFINED_METHOD_ENTRY_P(me)) { ID rmiss = rb_intern("respond_to_missing?"); VALUE sym = ID2SYM(id); @@ -985,12 +987,12 @@ mnew(VALUE klass, VALUE obj, ID id, VALUE mclass, int scope) } } if (def && def->type == VM_METHOD_TYPE_ZSUPER) { - klass = RCLASS_SUPER(me->klass); + klass = RCLASS_SUPER(defined_class); id = def->original_id; goto again; } - klass = me->klass; + klass = defined_class; while (rclass != klass && (FL_TEST(rclass, FL_SINGLETON) || RB_TYPE_P(rclass, T_ICLASS))) { @@ -1006,6 +1008,7 @@ mnew(VALUE klass, VALUE obj, ID id, VALUE mclass, int scope) data->recv = obj; data->rclass = rclass; + data->defined_class = defined_class; data->id = rid; data->me = ALLOC(rb_method_entry_t); *data->me = *me; @@ -1119,6 +1122,7 @@ method_unbind(VALUE obj) *data->me = *orig->me; if (orig->me->def) orig->me->def->alias_count++; data->rclass = orig->rclass; + data->defined_class = orig->defined_class; data->ume = ALLOC(struct unlinked_method_entry_list_entry); OBJ_INFECT(method, obj); @@ -1394,6 +1398,7 @@ rb_mod_define_method(int argc, VALUE *argv, VALUE mod) 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, noex); } @@ -1498,7 +1503,7 @@ rb_method_call(int argc, VALUE *argv, VALUE method) rb_thread_t *th = GET_THREAD(); PASS_PASSED_BLOCK_TH(th); - result = rb_vm_call(th, data->recv, data->id, argc, argv, data->me); + result = rb_vm_call(th, data->recv, data->id, argc, argv, data->me, data->defined_class); } POP_TAG(); if (safe >= 0) @@ -1727,7 +1732,7 @@ method_arity(VALUE method) int rb_mod_method_arity(VALUE mod, ID id) { - rb_method_entry_t *me = rb_method_entry(mod, id); + rb_method_entry_t *me = rb_method_entry(mod, id, 0); return rb_method_entry_arity(me); } diff --git a/vm.c b/vm.c index fb6affcc15..f249585dba 100644 --- a/vm.c +++ b/vm.c @@ -136,8 +136,8 @@ vm_set_top_stack(rb_thread_t * th, VALUE iseqval) /* for return */ CHECK_STACK_OVERFLOW(th->cfp, iseq->local_size + iseq->stack_max); vm_push_frame(th, iseq, VM_FRAME_MAGIC_TOP | VM_FRAME_FLAG_FINISH, - th->top_self, VM_ENVVAL_BLOCK_PTR(0), iseq->iseq_encoded, - th->cfp->sp, iseq->local_size, 0); + th->top_self, rb_cObject, VM_ENVVAL_BLOCK_PTR(0), + iseq->iseq_encoded, th->cfp->sp, iseq->local_size, 0); } static void @@ -147,7 +147,8 @@ vm_set_eval_stack(rb_thread_t * th, VALUE iseqval, const NODE *cref, rb_block_t GetISeqPtr(iseqval, iseq); CHECK_STACK_OVERFLOW(th->cfp, iseq->local_size + iseq->stack_max); - vm_push_frame(th, iseq, VM_FRAME_MAGIC_EVAL | VM_FRAME_FLAG_FINISH, base_block->self, + 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), iseq->iseq_encoded, th->cfp->sp, iseq->local_size, 0); @@ -554,6 +555,7 @@ rb_vm_make_proc(rb_thread_t *th, const rb_block_t *block, VALUE klass) GetProcPtr(procval, proc); proc->blockprocval = blockprocval; proc->block.self = block->self; + proc->block.klass = block->klass; proc->block.ep = block->ep; proc->block.iseq = block->iseq; proc->block.proc = procval; @@ -596,8 +598,10 @@ invoke_block_from_c(rb_thread_t *th, const rb_block_t *block, type == VM_FRAME_MAGIC_LAMBDA); vm_push_frame(th, iseq, type | VM_FRAME_FLAG_FINISH, - self, VM_ENVVAL_PREV_EP_PTR(block->ep), - iseq->iseq_encoded + opt_pc, cfp->sp + arg_size, iseq->local_size - arg_size, + self, block->klass, /* th->passed_defined_class, */ + VM_ENVVAL_PREV_EP_PTR(block->ep), + iseq->iseq_encoded + opt_pc, + cfp->sp + arg_size, iseq->local_size - arg_size, th->passed_me); th->passed_me = 0; th->passed_block = blockptr; @@ -1302,8 +1306,11 @@ vm_exec(rb_thread_t *th) /* push block frame */ cfp->sp[0] = err; vm_push_frame(th, catch_iseq, VM_FRAME_MAGIC_BLOCK, - cfp->self, VM_ENVVAL_PREV_EP_PTR(cfp->ep), catch_iseq->iseq_encoded, - cfp->sp + 1 /* push value */, catch_iseq->local_size - 1, + cfp->self, cfp->klass, + VM_ENVVAL_PREV_EP_PTR(cfp->ep), + catch_iseq->iseq_encoded, + cfp->sp + 1 /* push value */, + catch_iseq->local_size - 1, cfp->me); state = 0; @@ -1438,7 +1445,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, VM_ENVVAL_BLOCK_PTR(blockptr), 0, reg_cfp->sp, 1, 0); + recv, CLASS_OF(recv), VM_ENVVAL_BLOCK_PTR(blockptr), 0, reg_cfp->sp, 1, 0); val = (*func)(arg); @@ -1783,7 +1790,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_TOP | VM_FRAME_FLAG_FINISH, - Qnil /* dummy self */, VM_ENVVAL_BLOCK_PTR(0), 0 /* dummy pc */, th->stack, 1, 0); + Qnil /* dummy self */, Qnil /* dummy klass */, VM_ENVVAL_BLOCK_PTR(0), 0 /* dummy pc */, th->stack, 1, 0); th->status = THREAD_RUNNABLE; th->errinfo = Qnil; diff --git a/vm_core.h b/vm_core.h index 97d4410d73..130063a490 100644 --- a/vm_core.h +++ b/vm_core.h @@ -137,6 +137,9 @@ struct iseq_inline_cache_entry { rb_method_entry_t *method; long index; } ic_value; + union { + VALUE defined_class; + } ic_value2; }; #if 1 @@ -349,14 +352,16 @@ typedef struct { rb_iseq_t *iseq; /* cfp[3] */ VALUE flag; /* cfp[4] */ VALUE self; /* cfp[5] / block[0] */ - VALUE *ep; /* cfp[6] / block[1] */ - rb_iseq_t *block_iseq; /* cfp[7] / block[2] */ - VALUE proc; /* cfp[8] / block[3] */ - const rb_method_entry_t *me;/* cfp[9] */ + VALUE klass; /* cfp[6] / block[1] */ + VALUE *ep; /* cfp[7] / block[2] */ + rb_iseq_t *block_iseq; /* cfp[8] / block[3] */ + VALUE proc; /* cfp[9] / block[4] */ + const rb_method_entry_t *me;/* cfp[10] */ } rb_control_frame_t; 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; @@ -714,7 +719,8 @@ void rb_vm_rewrite_ep_in_errinfo(rb_thread_t *th, rb_control_frame_t *cfp); 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); + const VALUE *argv, const rb_method_entry_t *me, + VALUE defined_class); void rb_unlink_method_entry(rb_method_entry_t *me); void rb_gc_mark_unlinked_live_method_entries(void *pvm); diff --git a/vm_eval.c b/vm_eval.c index 12e4eb4e0b..58af869387 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -33,11 +33,11 @@ static VALUE send_internal(int argc, const VALUE *argv, VALUE recv, call_type sc static inline VALUE vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv, - const rb_method_entry_t *me) + const rb_method_entry_t *me, VALUE defined_class) { const rb_method_definition_t *def = me->def; VALUE val; - VALUE klass = me->klass; + VALUE klass = defined_class; const rb_block_t *blockptr = 0; if (!def) return Qnil; @@ -59,7 +59,8 @@ vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv, *reg_cfp->sp++ = argv[i]; } - vm_setup_method(th, reg_cfp, recv, argc, blockptr, 0 /* flag */, me); + vm_setup_method(th, reg_cfp, recv, argc, blockptr, 0 /* flag */, + me, klass); th->cfp->flag |= VM_FRAME_FLAG_FINISH; val = vm_exec(th); break; @@ -71,7 +72,8 @@ vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv, rb_control_frame_t *reg_cfp = th->cfp; rb_control_frame_t *cfp = vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, - recv, VM_ENVVAL_BLOCK_PTR(blockptr), 0, reg_cfp->sp, 1, me); + recv, klass, VM_ENVVAL_BLOCK_PTR(blockptr), + 0, reg_cfp->sp, 1, me); cfp->me = me; val = call_cfunc(def->body.cfunc.func, recv, def->body.cfunc.argc, argc, argv); @@ -95,12 +97,12 @@ vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv, break; } case VM_METHOD_TYPE_BMETHOD: { - val = vm_call_bmethod(th, recv, argc, argv, blockptr, me); + val = vm_call_bmethod(th, recv, argc, argv, blockptr, me, klass); break; } case VM_METHOD_TYPE_ZSUPER: { klass = RCLASS_SUPER(klass); - if (!klass || !(me = rb_method_entry(klass, id))) { + if (!klass || !(me = rb_method_entry(klass, id, &klass))) { return method_missing(recv, id, argc, argv, NOEX_SUPER); } RUBY_VM_CHECK_INTS(th); @@ -144,9 +146,9 @@ vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, 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) + const rb_method_entry_t *me, VALUE defined_class) { - return vm_call0(th, recv, id, argc, argv, me); + return vm_call0(th, recv, id, argc, argv, me, defined_class); } static inline VALUE @@ -158,9 +160,8 @@ vm_call_super(rb_thread_t *th, int argc, const VALUE *argv) rb_method_entry_t *me; rb_control_frame_t *cfp = th->cfp; - if (!cfp->iseq) { - klass = cfp->me->klass; - klass = RCLASS_SUPER(klass); + if (!cfp->iseq && !NIL_P(cfp->klass)) { + klass = RCLASS_SUPER(cfp->klass); if (klass == 0) { klass = vm_search_normal_superclass(cfp->me->klass, recv); @@ -171,12 +172,12 @@ vm_call_super(rb_thread_t *th, int argc, const VALUE *argv) rb_bug("vm_call_super: should not be reached"); } - me = rb_method_entry(klass, id); + me = rb_method_entry(klass, id, &klass); if (!me) { return method_missing(recv, id, argc, argv, NOEX_SUPER); } - return vm_call0(th, recv, id, argc, argv, me); + return vm_call0(th, recv, id, argc, argv, me, klass); } VALUE @@ -197,7 +198,8 @@ stack_check(void) } } -static inline rb_method_entry_t *rb_search_method_entry(VALUE recv, ID mid); +static inline rb_method_entry_t * + rb_search_method_entry(VALUE recv, ID mid, VALUE *defined_class_ptr); static inline int rb_method_call_status(rb_thread_t *th, const rb_method_entry_t *me, call_type scope, VALUE self); #define NOEX_OK NOEX_NOSUPER @@ -219,7 +221,8 @@ static inline VALUE rb_call0(VALUE recv, ID mid, int argc, const VALUE *argv, call_type scope, VALUE self) { - rb_method_entry_t *me = rb_search_method_entry(recv, mid); + VALUE defined_class; + rb_method_entry_t *me = rb_search_method_entry(recv, mid, &defined_class); rb_thread_t *th = GET_THREAD(); int call_status = rb_method_call_status(th, me, scope, self); @@ -227,7 +230,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); + return vm_call0(th, recv, mid, argc, argv, me, defined_class); } struct rescue_funcall_args { @@ -264,8 +267,9 @@ check_funcall(VALUE recv, ID mid, int argc, VALUE *argv) const rb_method_entry_t *me; rb_thread_t *th = GET_THREAD(); int call_status; + VALUE defined_class; - me = rb_method_entry(klass, idRespond_to); + me = rb_method_entry(klass, idRespond_to, &defined_class); if (me && !(me->flag & NOEX_BASIC)) { VALUE args[2]; int arity = rb_method_entry_arity(me); @@ -277,12 +281,13 @@ check_funcall(VALUE recv, ID mid, int argc, VALUE *argv) args[0] = ID2SYM(mid); args[1] = Qtrue; - if (!RTEST(vm_call0(th, recv, idRespond_to, arity, args, me))) { + if (!RTEST(vm_call0(th, recv, idRespond_to, arity, args, me, + defined_class))) { return Qundef; } } - me = rb_search_method_entry(recv, mid); + me = rb_search_method_entry(recv, mid, &defined_class); call_status = rb_method_call_status(th, me, CALL_FCALL, Qundef); if (call_status != NOEX_OK) { if (rb_method_basic_definition_p(klass, idMethodMissing)) { @@ -302,7 +307,7 @@ check_funcall(VALUE recv, ID mid, int argc, VALUE *argv) } } stack_check(); - return vm_call0(th, recv, mid, argc, argv, me); + return vm_call0(th, recv, mid, argc, argv, me, defined_class); } VALUE @@ -347,7 +352,7 @@ rb_type_str(enum ruby_value_type type) } static inline rb_method_entry_t * -rb_search_method_entry(VALUE recv, ID mid) +rb_search_method_entry(VALUE recv, ID mid, VALUE *defined_class_ptr) { VALUE klass = CLASS_OF(recv); @@ -386,7 +391,7 @@ rb_search_method_entry(VALUE recv, ID mid) rb_id2name(mid), type, (void *)recv, flags, klass); } } - return rb_method_entry(klass, mid); + return rb_method_entry(klass, mid, defined_class_ptr); } static inline int diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 074e24cd36..8e24a73604 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -27,6 +27,7 @@ vm_push_frame(rb_thread_t *th, const rb_iseq_t *iseq, VALUE type, VALUE self, + VALUE klass, VALUE specval, const VALUE *pc, VALUE *sp, @@ -64,6 +65,18 @@ vm_push_frame(rb_thread_t *th, cfp->block_iseq = 0; cfp->proc = 0; cfp->me = me; + 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(); @@ -415,14 +428,14 @@ call_cfunc(VALUE (*func)(), VALUE recv, static inline VALUE vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp, int num, volatile VALUE recv, const rb_block_t *blockptr, - const rb_method_entry_t *me) + const rb_method_entry_t *me, VALUE defined_class) { volatile VALUE val = 0; const rb_method_definition_t *def = me->def; EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, me->called_id, me->klass); - vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, recv, + vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, recv, defined_class, VM_ENVVAL_BLOCK_PTR(blockptr), 0, th->cfp->sp, 1, me); reg_cfp->sp -= num + 1; @@ -442,7 +455,8 @@ vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp, static inline VALUE vm_call_bmethod(rb_thread_t *th, VALUE recv, int argc, const VALUE *argv, - const rb_block_t *blockptr, const rb_method_entry_t *me) + const rb_block_t *blockptr, const rb_method_entry_t *me, + VALUE defined_class) { rb_proc_t *proc; VALUE val; @@ -483,7 +497,7 @@ vm_method_missing(rb_thread_t *th, ID id, VALUE recv, static inline void vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp, VALUE recv, int argc, const rb_block_t *blockptr, VALUE flag, - const rb_method_entry_t *me) + const rb_method_entry_t *me, VALUE defined_class) { int opt_pc, i; VALUE *sp, *rsp = cfp->sp - argc; @@ -505,7 +519,7 @@ vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp, *sp++ = Qnil; } - vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD, recv, + vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD, recv, defined_class, VM_ENVVAL_BLOCK_PTR(blockptr), iseq->iseq_encoded + opt_pc, sp, 0, me); @@ -528,7 +542,7 @@ vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp, *sp++ = Qnil; } - vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD, recv, + vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD, recv, defined_class, VM_ENVVAL_BLOCK_PTR(blockptr), iseq->iseq_encoded + opt_pc, sp, 0, me); } @@ -537,7 +551,8 @@ vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp, static inline VALUE vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, int num, const rb_block_t *blockptr, VALUE flag, - ID id, const rb_method_entry_t *me, VALUE recv) + ID id, const rb_method_entry_t *me, + VALUE recv, VALUE defined_class) { VALUE val; @@ -548,12 +563,14 @@ vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, normal_method_dispatch: switch (me->def->type) { case VM_METHOD_TYPE_ISEQ:{ - vm_setup_method(th, cfp, recv, num, blockptr, flag, me); + vm_setup_method(th, cfp, recv, num, blockptr, flag, me, + defined_class); return Qundef; } case VM_METHOD_TYPE_NOTIMPLEMENTED: case VM_METHOD_TYPE_CFUNC:{ - val = vm_call_cfunc(th, cfp, num, recv, blockptr, me); + val = vm_call_cfunc(th, cfp, num, recv, blockptr, me, + defined_class); break; } case VM_METHOD_TYPE_ATTRSET:{ @@ -581,12 +598,13 @@ vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, VALUE *argv = ALLOCA_N(VALUE, num); MEMCPY(argv, cfp->sp - num, VALUE, num); cfp->sp += - num - 1; - val = vm_call_bmethod(th, recv, num, argv, blockptr, me); + val = vm_call_bmethod(th, recv, num, argv, blockptr, me, + defined_class); break; } case VM_METHOD_TYPE_ZSUPER:{ VALUE klass = RCLASS_SUPER(me->klass); - me = rb_method_entry(klass, id); + me = rb_method_entry(klass, id, &defined_class); if (me != 0) { goto normal_method_dispatch; @@ -622,7 +640,7 @@ vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, if (i > 0) { MEMMOVE(&TOPN(i), &TOPN(i-1), VALUE, i); } - me = rb_method_entry(CLASS_OF(recv), id); + me = rb_method_entry(CLASS_OF(recv), id, &defined_class); num -= 1; DEC_SP(1); flag |= VM_CALL_FCALL_BIT | VM_CALL_OPT_SEND_BIT; @@ -665,8 +683,6 @@ vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, val = vm_method_missing(th, id, recv, num, blockptr, stat); } else if (!(flag & VM_CALL_OPT_SEND_BIT) && (me->flag & NOEX_MASK) & NOEX_PROTECTED) { - VALUE defined_class = me->klass; - if (RB_TYPE_P(defined_class, T_ICLASS)) { defined_class = RBASIC(defined_class)->klass; } @@ -759,7 +775,8 @@ vm_yield_with_cfunc(rb_thread_t *th, const rb_block_t *block, } cfp = vm_push_frame(th, (rb_iseq_t *)ifunc, VM_FRAME_MAGIC_IFUNC, self, - VM_ENVVAL_PREV_EP_PTR(block->ep), 0, th->cfp->sp, 1, 0); + 0, VM_ENVVAL_PREV_EP_PTR(block->ep), 0, + th->cfp->sp, 1, 0); if (blockargptr) { VM_CF_LEP(cfp)[0] = VM_ENVVAL_BLOCK_PTR(blockargptr); @@ -976,6 +993,7 @@ vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_num_t num, rb_n block_proc_is_lambda(block->proc)); vm_push_frame(th, iseq, VM_FRAME_MAGIC_BLOCK, block->self, + block->klass, VM_ENVVAL_PREV_EP_PTR(block->ep), iseq->iseq_encoded + opt_pc, rsp + arg_size, @@ -1396,22 +1414,28 @@ vm_setivar(VALUE obj, ID id, VALUE val, IC ic) } static inline const rb_method_entry_t * -vm_method_search(VALUE id, VALUE klass, IC ic) +vm_method_search(VALUE id, VALUE klass, IC ic, VALUE *defined_class_ptr) { rb_method_entry_t *me; #if OPT_INLINE_METHOD_CACHE if (LIKELY(klass == ic->ic_class && GET_VM_STATE_VERSION() == ic->ic_vmstat)) { me = ic->ic_value.method; + if (defined_class_ptr) + *defined_class_ptr = ic->ic_value2.defined_class; } else { - me = rb_method_entry(klass, id); + VALUE defined_class; + me = rb_method_entry(klass, id, &defined_class); + if (defined_class_ptr) + *defined_class_ptr = defined_class; ic->ic_class = klass; ic->ic_value.method = me; + ic->ic_value2.defined_class = defined_class; ic->ic_vmstat = GET_VM_STATE_VERSION(); } #else - me = rb_method_entry(klass, id); + me = rb_method_entry(klass, id, defined_class_ptr); #endif return me; } @@ -1419,7 +1443,7 @@ vm_method_search(VALUE id, VALUE klass, IC ic) static inline VALUE vm_search_normal_superclass(VALUE klass, VALUE recv) { - if (BUILTIN_TYPE(klass) == T_CLASS) { + if (BUILTIN_TYPE(klass) == T_CLASS || BUILTIN_TYPE(klass) == T_ICLASS) { klass = RCLASS_ORIGIN(klass); return RCLASS_SUPER(klass); } @@ -1488,10 +1512,10 @@ vm_search_superclass(rb_control_frame_t *reg_cfp, rb_iseq_t *iseq, } id = lcfp->me->def->original_id; - klass = vm_search_normal_superclass(lcfp->me->klass, recv); + klass = vm_search_normal_superclass(lcfp->klass, recv); } else { - klass = vm_search_normal_superclass(iseq->klass, recv); + klass = vm_search_normal_superclass(reg_cfp->klass, recv); } *idp = id; @@ -1771,7 +1795,7 @@ opt_eq_func(VALUE recv, VALUE obj, IC ic) } { - const rb_method_entry_t *me = vm_method_search(idEq, CLASS_OF(recv), ic); + const rb_method_entry_t *me = vm_method_search(idEq, CLASS_OF(recv), ic, 0); if (check_cfunc(me, rb_obj_equal)) { return recv == obj ? Qtrue : Qfalse; diff --git a/vm_insnhelper.h b/vm_insnhelper.h index ffabbda23d..fdaa818e6c 100644 --- a/vm_insnhelper.h +++ b/vm_insnhelper.h @@ -170,8 +170,8 @@ enum vm_regan_acttype { } \ } while (0) -#define CALL_METHOD(num, blockptr, flag, id, me, recv) do { \ - VALUE v = vm_call_method(th, GET_CFP(), (num), (blockptr), (flag), (id), (me), (recv)); \ +#define CALL_METHOD(num, blockptr, flag, id, me, recv, defined_class) do { \ + VALUE v = vm_call_method(th, GET_CFP(), (num), (blockptr), (flag), (id), (me), (recv), (defined_class)); \ if (v == Qundef) { \ RESTORE_REGS(); \ NEXT_INSN(); \ @@ -213,8 +213,9 @@ enum vm_regan_acttype { #if USE_IC_FOR_SPECIALIZED_METHOD #define CALL_SIMPLE_METHOD(num, id, recv) do { \ - VALUE klass = CLASS_OF(recv); \ - CALL_METHOD((num), 0, 0, (id), vm_method_search((id), klass, ic), (recv)); \ + VALUE klass = CLASS_OF(recv), defined_class; \ + const rb_method_entry_t *me = vm_method_search((id), klass, ic, &defined_class); \ + CALL_METHOD((num), 0, 0, (id), me, (recv), defined_class); \ } while (0) #else diff --git a/vm_method.c b/vm_method.c index f344355f4f..9acded6280 100644 --- a/vm_method.c +++ b/vm_method.c @@ -22,6 +22,7 @@ struct cache_entry { /* method hash table. */ ID mid; /* method's id */ VALUE klass; /* receiver's class */ rb_method_entry_t *me; + VALUE defined_class; }; static struct cache_entry cache[CACHE_SIZE]; @@ -373,7 +374,7 @@ rb_get_alloc_func(VALUE klass) { rb_method_entry_t *me; Check_Type(klass, T_CLASS); - me = rb_method_entry(CLASS_OF(klass), ID_ALLOCATOR); + me = rb_method_entry(CLASS_OF(klass), ID_ALLOCATOR, 0); if (me && me->def && me->def->type == VM_METHOD_TYPE_CFUNC) { return (rb_alloc_func_t)me->def->body.cfunc.func; @@ -384,7 +385,7 @@ rb_get_alloc_func(VALUE klass) } static rb_method_entry_t* -search_method(VALUE klass, ID id) +search_method(VALUE klass, ID id, VALUE *defined_class_ptr) { st_data_t body; @@ -396,6 +397,8 @@ search_method(VALUE klass, ID id) if (st_lookup(m_tbl, id, &body)) break; } + if (defined_class_ptr) + *defined_class_ptr = klass; return (rb_method_entry_t *)body; } @@ -406,15 +409,18 @@ search_method(VALUE klass, ID id) * rb_method_entry() simply. */ rb_method_entry_t * -rb_method_entry_get_without_cache(VALUE klass, ID id) +rb_method_entry_get_without_cache(VALUE klass, ID id, + VALUE *defined_class_ptr) { - rb_method_entry_t *me = search_method(klass, id); + VALUE defined_class; + rb_method_entry_t *me = search_method(klass, id, &defined_class); if (ruby_running) { struct cache_entry *ent; ent = cache + EXPR1(klass, id); ent->filled_version = GET_VM_STATE_VERSION(); ent->klass = klass; + ent->defined_class = defined_class; if (UNDEFINED_METHOD_ENTRY_P(me)) { ent->mid = id; @@ -427,21 +433,25 @@ rb_method_entry_get_without_cache(VALUE klass, ID id) } } + if (defined_class_ptr) + *defined_class_ptr = defined_class; return me; } rb_method_entry_t * -rb_method_entry(VALUE klass, ID id) +rb_method_entry(VALUE klass, ID id, VALUE *defined_class_ptr) { struct cache_entry *ent; ent = cache + EXPR1(klass, id); if (ent->filled_version == GET_VM_STATE_VERSION() && ent->mid == id && ent->klass == klass) { + if (defined_class_ptr) + *defined_class_ptr = ent->defined_class; return ent->me; } - return rb_method_entry_get_without_cache(klass, id); + return rb_method_entry_get_without_cache(klass, id, defined_class_ptr); } static void @@ -534,14 +544,15 @@ static void rb_export_method(VALUE klass, ID name, rb_method_flag_t noex) { rb_method_entry_t *me; + VALUE defined_class; if (klass == rb_cObject) { rb_secure(4); } - me = search_method(klass, name); + me = search_method(klass, name, &defined_class); if (!me && RB_TYPE_P(klass, T_MODULE)) { - me = search_method(rb_cObject, name); + me = search_method(rb_cObject, name, &defined_class); } if (UNDEFINED_METHOD_ENTRY_P(me)) { @@ -551,7 +562,7 @@ rb_export_method(VALUE klass, ID name, rb_method_flag_t noex) if (me->flag != noex) { rb_vm_check_redefinition_opt_method(me, klass); - if (klass == me->klass) { + if (klass == defined_class) { me->flag = noex; } else { @@ -563,7 +574,7 @@ rb_export_method(VALUE klass, ID name, rb_method_flag_t noex) int rb_method_boundp(VALUE klass, ID id, int ex) { - rb_method_entry_t *me = rb_method_entry(klass, id); + rb_method_entry_t *me = rb_method_entry(klass, id, 0); if (me != 0) { if ((ex & ~NOEX_RESPONDS) && @@ -644,7 +655,7 @@ rb_undef(VALUE klass, ID id) rb_warn("undefining `%s' may cause serious problems", rb_id2name(id)); } - me = search_method(klass, id); + me = search_method(klass, id, 0); if (UNDEFINED_METHOD_ENTRY_P(me)) { const char *s0 = " class"; @@ -773,7 +784,7 @@ check_definition(VALUE mod, VALUE mid, rb_method_flag_t noex) const rb_method_entry_t *me; ID id = rb_check_id(&mid); if (!id) return Qfalse; - me = rb_method_entry(mod, id); + me = rb_method_entry(mod, id, 0); if (me) { if (VISI_CHECK(me->flag, noex)) return Qtrue; @@ -969,11 +980,12 @@ rb_alias(VALUE klass, ID name, ID def) } again: - orig_me = search_method(klass, def); + orig_me = search_method(klass, def, 0); if (UNDEFINED_METHOD_ENTRY_P(orig_me)) { if ((!RB_TYPE_P(klass, T_MODULE)) || - (orig_me = search_method(rb_cObject, def), UNDEFINED_METHOD_ENTRY_P(orig_me))) { + (orig_me = search_method(rb_cObject, def, 0), + UNDEFINED_METHOD_ENTRY_P(orig_me))) { rb_print_undef(klass, def, 0); } } @@ -1248,9 +1260,9 @@ rb_mod_modfunc(int argc, VALUE *argv, VALUE module) id = rb_to_id(argv[i]); for (;;) { - me = search_method(m, id); + me = search_method(m, id, 0); if (me == 0) { - me = search_method(rb_cObject, id); + me = search_method(rb_cObject, id, 0); } if (UNDEFINED_METHOD_ENTRY_P(me)) { rb_print_undef(module, id, 0); @@ -1270,7 +1282,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); + const rb_method_entry_t *me = rb_method_entry(klass, id, 0); if (me && (me->flag & NOEX_BASIC)) return 1; return 0; @@ -1402,7 +1414,8 @@ Init_eval_method(void) { #define REPLICATE_METHOD(klass, id, noex) \ - rb_method_entry_set((klass), (id), rb_method_entry((klass), (id)), \ + rb_method_entry_set((klass), (id), \ + rb_method_entry((klass), (id), 0), \ (rb_method_flag_t)(noex | NOEX_BASIC | NOEX_NOREDEF)) REPLICATE_METHOD(rb_eException, idMethodMissing, NOEX_PRIVATE); REPLICATE_METHOD(rb_eException, idRespond_to, NOEX_PUBLIC);