diff options
author | ko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2012-10-14 16:59:05 +0000 |
---|---|---|
committer | ko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2012-10-14 16:59:05 +0000 |
commit | cbd597e9bc05eac7249e91dcdb4f1c1c75a53cf5 (patch) | |
tree | 8f570f2e813466ef8fa0ac3f564a638b69164773 /vm_insnhelper.c | |
parent | e8c234577fa7061a22f0dc515aefdc8dbc61f3b5 (diff) | |
download | ruby-cbd597e9bc05eac7249e91dcdb4f1c1c75a53cf5.tar.gz |
* insns.def (send, invokesuper, invokeblock, opt_*), vm_core.h:
use only a `ci' (rb_call_info_t) parameter instead of using
parameters such as `op_id', 'op_argc', `blockiseq' and flag.
These information are stored in rb_call_info_t at the compile
time.
This technique simplifies parameter passings at related
function calls (~10% speedups for simple mehtod invocation at
my machine).
`rb_call_info_t' also has new function pointer variable `call'.
This `call' variable enables to customize method (block)
invocation process for each place. However, it always call
`vm_call_general()' at this changes.
`rb_call_info_t' also has temporary variables for method
(block) invocation.
* vm_core.h, compile.c, insns.def: introduce VM_CALL_ARGS_SKIP_SETUP
VM_CALL macro. This flag indicates that this call can skip
caller_setup (block arg and splat arg).
* compile.c: catch up above changes.
* iseq.c: catch up above changes (especially for TS_CALLINFO).
* tool/instruction.rb: catch up above chagnes.
* vm_insnhelper.c, vm_insnhelper.h: ditto. Macros and functions
parameters are changed.
* vm_eval.c (vm_call0): ditto (it will be rewriten soon).
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37180 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'vm_insnhelper.c')
-rw-r--r-- | vm_insnhelper.c | 725 |
1 files changed, 372 insertions, 353 deletions
diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 06cc85934a..921e41ecae 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -158,27 +158,26 @@ rb_error_arity(int argc, int min, int max) rb_exc_raise(rb_arg_error_new(argc, min, max)); } -#define VM_CALLEE_SETUP_ARG(ret, th, iseq, orig_argc, orig_argv, block) \ +#define VM_CALLEE_SETUP_ARG(ret, th, ci, iseq, argv) \ if (LIKELY((iseq)->arg_simple & 0x01)) { \ /* simple check */ \ - if ((orig_argc) != (iseq)->argc) { \ - argument_error((iseq), (orig_argc), (iseq)->argc, (iseq)->argc); \ + if ((ci)->argc != (iseq)->argc) { \ + argument_error((iseq), ((ci)->argc), (iseq)->argc, (iseq)->argc); \ } \ (ret) = 0; \ } \ else { \ - (ret) = vm_callee_setup_arg_complex((th), (iseq), (orig_argc), (orig_argv), (block)); \ + (ret) = vm_callee_setup_arg_complex((th), (ci), (iseq), (argv)); \ } static inline int -vm_callee_setup_arg_complex(rb_thread_t *th, const rb_iseq_t * iseq, - int orig_argc, VALUE * orig_argv, - const rb_block_t **block) +vm_callee_setup_arg_complex(rb_thread_t *th, rb_call_info_t *ci, const rb_iseq_t * iseq, VALUE *orig_argv) { const int m = iseq->argc; const int opts = iseq->arg_opts - (iseq->arg_opts > 0); const int min = m + iseq->arg_post_len; const int max = (iseq->arg_rest == -1) ? m + opts + iseq->arg_post_len : UNLIMITED_ARGUMENTS; + const int orig_argc = ci->argc; int argc = orig_argc; VALUE *argv = orig_argv; rb_num_t opt_pc = 0; @@ -255,9 +254,9 @@ vm_callee_setup_arg_complex(rb_thread_t *th, const rb_iseq_t * iseq, } /* block arguments */ - if (block && iseq->arg_block != -1) { + if (iseq->arg_block != -1) { VALUE blockval = Qnil; - const rb_block_t *blockptr = *block; + const rb_block_t *blockptr = ci->blockptr; if (blockptr) { /* make Proc object */ @@ -265,7 +264,7 @@ vm_callee_setup_arg_complex(rb_thread_t *th, const rb_iseq_t * iseq, rb_proc_t *proc; blockval = rb_vm_make_proc(th, blockptr, rb_cProc); GetProcPtr(blockval, proc); - *block = &proc->block; + ci->blockptr = &proc->block; } else { blockval = blockptr->proc; @@ -279,45 +278,39 @@ vm_callee_setup_arg_complex(rb_thread_t *th, const rb_iseq_t * iseq, return (int)opt_pc; } -static inline int -caller_setup_args(const rb_thread_t *th, rb_control_frame_t *cfp, VALUE flag, - int argc, rb_iseq_t *blockiseq, rb_block_t **block) +static void +vm_caller_setup_args(const rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci) { - rb_block_t *blockptr = 0; - - if (block) { - if (flag & VM_CALL_ARGS_BLOCKARG_BIT) { - rb_proc_t *po; - VALUE proc; - - proc = *(--cfp->sp); - - if (proc != Qnil) { - if (!rb_obj_is_proc(proc)) { - VALUE b = rb_check_convert_type(proc, T_DATA, "Proc", "to_proc"); - if (NIL_P(b) || !rb_obj_is_proc(b)) { - rb_raise(rb_eTypeError, - "wrong argument type %s (expected Proc)", - rb_obj_classname(proc)); - } - proc = b; + if (UNLIKELY(ci->flag & VM_CALL_ARGS_BLOCKARG_BIT)) { + rb_proc_t *po; + VALUE proc; + + proc = *(--cfp->sp); + + if (proc != Qnil) { + if (!rb_obj_is_proc(proc)) { + VALUE b = rb_check_convert_type(proc, T_DATA, "Proc", "to_proc"); + if (NIL_P(b) || !rb_obj_is_proc(b)) { + rb_raise(rb_eTypeError, + "wrong argument type %s (expected Proc)", + rb_obj_classname(proc)); } - GetProcPtr(proc, po); - blockptr = &po->block; - RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp)->proc = proc; - *block = blockptr; + proc = b; } + GetProcPtr(proc, po); + ci->blockptr = &po->block; + RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp)->proc = proc; } - else if (blockiseq) { - blockptr = RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp); - blockptr->iseq = blockiseq; - blockptr->proc = 0; - *block = blockptr; - } + } + else if (UNLIKELY(ci->blockiseq != 0)) { + ci->blockptr = RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp); + ci->blockptr->iseq = ci->blockiseq; + ci->blockptr->proc = 0; } /* expand top of stack? */ - if (flag & VM_CALL_ARGS_SPLAT_BIT) { + + if (UNLIKELY(ci->flag & VM_CALL_ARGS_SPLAT_BIT)) { VALUE ary = *(cfp->sp - 1); VALUE *ptr; int i; @@ -336,11 +329,9 @@ caller_setup_args(const rb_thread_t *th, rb_control_frame_t *cfp, VALUE flag, for (i = 0; i < len; i++) { *cfp->sp++ = ptr[i]; } - argc += i-1; + ci->argc += i-1; } } - - return argc; } static inline VALUE @@ -428,21 +419,20 @@ 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, VALUE defined_class) +vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci) { volatile VALUE val = 0; + const rb_method_entry_t *me = ci->me; const rb_method_definition_t *def = me->def; - EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, me->called_id, me->klass); + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, ci->recv, me->called_id, me->klass); - vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, recv, defined_class, - VM_ENVVAL_BLOCK_PTR(blockptr), 0, th->cfp->sp, 1, me); + vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, ci->recv, ci->defined_class, + VM_ENVVAL_BLOCK_PTR(ci->blockptr), 0, th->cfp->sp, 1, me); - reg_cfp->sp -= num + 1; + reg_cfp->sp -= ci->argc + 1; - val = call_cfunc(def->body.cfunc.func, recv, (int)def->body.cfunc.argc, num, reg_cfp->sp + 1); + val = call_cfunc(def->body.cfunc.func, ci->recv, (int)def->body.cfunc.argc, ci->argc, reg_cfp->sp + 1); if (reg_cfp != th->cfp + 1) { rb_bug("cfp consistency error - send"); @@ -450,7 +440,7 @@ vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp, vm_pop_frame(th); - EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, me->called_id, me->klass); + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, ci->recv, me->called_id, me->klass); return val; } @@ -491,20 +481,18 @@ vm_method_missing(rb_thread_t *th, rb_control_frame_t *const reg_cfp, } 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, VALUE defined_class) +vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci) { int opt_pc, i; - VALUE *argv = cfp->sp - argc; - rb_iseq_t *iseq = me->def->body.iseq; + VALUE *argv = cfp->sp - ci->argc; + rb_iseq_t *iseq = ci->me->def->body.iseq; - VM_CALLEE_SETUP_ARG(opt_pc, th, iseq, argc, argv, &blockptr); + VM_CALLEE_SETUP_ARG(opt_pc, th, ci, iseq, argv); /* stack overflow check */ CHECK_STACK_OVERFLOW(cfp, iseq->stack_max); - if (LIKELY(!(flag & VM_CALL_TAILCALL_BIT))) { + if (LIKELY(!(ci->flag & VM_CALL_TAILCALL_BIT))) { VALUE *sp = argv + iseq->arg_size; /* clear local variables */ @@ -512,9 +500,9 @@ vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp, *sp++ = Qnil; } - 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); + vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD, ci->recv, ci->defined_class, + VM_ENVVAL_BLOCK_PTR(ci->blockptr), + iseq->iseq_encoded + opt_pc, sp, 0, ci->me); cfp->sp = argv - 1 /* recv */; } @@ -527,7 +515,7 @@ vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp, sp_orig = sp = cfp->sp; /* push self */ - sp[0] = recv; + sp[0] = ci->recv; sp++; /* copy arguments */ @@ -541,187 +529,13 @@ vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp, } vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD | finish_flag, - recv, defined_class, VM_ENVVAL_BLOCK_PTR(blockptr), - iseq->iseq_encoded + opt_pc, sp, 0, me); + ci->recv, ci->defined_class, VM_ENVVAL_BLOCK_PTR(ci->blockptr), + iseq->iseq_encoded + opt_pc, sp, 0, ci->me); cfp->sp = sp_orig; } } -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, VALUE defined_class) -{ - VALUE val; - - start_method_dispatch: - - if (me != 0) { - if ((me->flag == 0)) { - normal_method_dispatch: - switch (me->def->type) { - case VM_METHOD_TYPE_ISEQ:{ - 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, - defined_class); - break; - } - case VM_METHOD_TYPE_ATTRSET:{ - rb_check_arity(num, 1, 1); - val = rb_ivar_set(recv, me->def->body.attr.id, *(cfp->sp - 1)); - cfp->sp -= 2; - break; - } - case VM_METHOD_TYPE_IVAR:{ - rb_check_arity(num, 0, 0); - val = rb_attr_get(recv, me->def->body.attr.id); - cfp->sp -= 1; - break; - } - case VM_METHOD_TYPE_MISSING:{ - VALUE *argv = ALLOCA_N(VALUE, num+1); - argv[0] = ID2SYM(me->def->original_id); - MEMCPY(argv+1, cfp->sp - num, VALUE, num); - cfp->sp += - num - 1; - th->passed_block = blockptr; - val = rb_funcall2(recv, rb_intern("method_missing"), num+1, argv); - break; - } - case VM_METHOD_TYPE_BMETHOD:{ - 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, - defined_class); - break; - } - case VM_METHOD_TYPE_ZSUPER:{ - VALUE klass = RCLASS_SUPER(me->klass); - me = rb_method_entry(klass, id, &defined_class); - - if (me != 0) { - goto normal_method_dispatch; - } - else { - goto start_method_dispatch; - } - } - case VM_METHOD_TYPE_OPTIMIZED:{ - switch (me->def->body.optimize_type) { - case OPTIMIZED_METHOD_TYPE_SEND: { - rb_control_frame_t *reg_cfp = cfp; - rb_num_t i = num - 1; - VALUE sym; - - if (num == 0) { - rb_raise(rb_eArgError, "no method name given"); - } - - sym = TOPN(i); - if (SYMBOL_P(sym)) { - id = SYM2ID(sym); - } - else if (!(id = rb_check_id(&sym))) { - if (rb_method_basic_definition_p(CLASS_OF(recv), idMethodMissing)) { - VALUE exc = make_no_method_exception(rb_eNoMethodError, NULL, recv, - rb_long2int(num), &TOPN(i)); - rb_exc_raise(exc); - } - id = rb_to_id(sym); - } - /* shift arguments */ - if (i > 0) { - MEMMOVE(&TOPN(i), &TOPN(i-1), VALUE, i); - } - 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; - - goto start_method_dispatch; - } - case OPTIMIZED_METHOD_TYPE_CALL: { - rb_proc_t *proc; - int argc = num; - VALUE *argv = ALLOCA_N(VALUE, num); - GetProcPtr(recv, proc); - MEMCPY(argv, cfp->sp - num, VALUE, num); - cfp->sp -= num + 1; - - val = rb_vm_invoke_proc(th, proc, argc, argv, blockptr); - break; - } - default: - rb_bug("eval_invoke_method: unsupported optimized method type (%d)", - me->def->body.optimize_type); - } - break; - } - default:{ - rb_bug("eval_invoke_method: unsupported method type (%d)", me->def->type); - break; - } - } - } - else { - int noex_safe; - - if (!(flag & VM_CALL_FCALL_BIT) && - (me->flag & NOEX_MASK) & NOEX_PRIVATE) { - int stat = NOEX_PRIVATE; - - if (flag & VM_CALL_VCALL_BIT) { - stat |= NOEX_VCALL; - } - val = vm_method_missing(th, cfp, id, recv, num, blockptr, stat); - } - else if (!(flag & VM_CALL_OPT_SEND_BIT) && (me->flag & NOEX_MASK) & NOEX_PROTECTED) { - if (!rb_obj_is_kind_of(cfp->self, defined_class)) { - val = vm_method_missing(th, cfp, id, recv, num, blockptr, NOEX_PROTECTED); - } - else { - goto normal_method_dispatch; - } - } - else if ((noex_safe = NOEX_SAFE(me->flag)) > th->safe_level && - (noex_safe > 2)) { - rb_raise(rb_eSecurityError, "calling insecure method: %s", rb_id2name(id)); - } - else { - goto normal_method_dispatch; - } - } - } - else { - /* method missing */ - int stat = 0; - if (flag & VM_CALL_VCALL_BIT) { - stat |= NOEX_VCALL; - } - if (flag & VM_CALL_SUPER_BIT) { - stat |= NOEX_SUPER; - } - if (id == idMethodMissing) { - rb_control_frame_t *reg_cfp = cfp; - VALUE *argv = STACK_ADDR_FROM_TOP(num); - rb_raise_method_missing(th, num, argv, recv, stat); - } - else { - val = vm_method_missing(th, cfp, id, recv, num, blockptr, stat); - } - } - - RUBY_VM_CHECK_INTS(th); - return val; -} - /* yield */ static inline int @@ -938,8 +752,7 @@ vm_yield_setup_block_args(rb_thread_t *th, const rb_iseq_t * iseq, static inline int vm_yield_setup_args(rb_thread_t * const th, const rb_iseq_t *iseq, - int argc, VALUE *argv, - const rb_block_t *blockptr, int lambda) + int argc, VALUE *argv, const rb_block_t *blockptr, int lambda) { if (0) { /* for debug */ printf(" argc: %d\n", argc); @@ -955,7 +768,11 @@ vm_yield_setup_args(rb_thread_t * const th, const rb_iseq_t *iseq, if (lambda) { /* call as method */ int opt_pc; - VM_CALLEE_SETUP_ARG(opt_pc, th, iseq, argc, argv, &blockptr); + rb_call_info_t ci_entry; + ci_entry.flag = 0; + ci_entry.argc = argc; + ci_entry.blockptr = (rb_block_t *)blockptr; + VM_CALLEE_SETUP_ARG(opt_pc, th, &ci_entry, iseq, argv); return opt_pc; } else { @@ -964,11 +781,10 @@ vm_yield_setup_args(rb_thread_t * const th, const rb_iseq_t *iseq, } static VALUE -vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_num_t num, rb_num_t flag) +vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci) { const rb_block_t *block = VM_CF_BLOCK_PTR(reg_cfp); rb_iseq_t *iseq; - int argc = (int)num; VALUE type = GET_ISEQ()->local_iseq->type; if ((type != ISEQ_TYPE_METHOD && type != ISEQ_TYPE_CLASS) || block == 0) { @@ -976,17 +792,16 @@ vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_num_t num, rb_n } iseq = block->iseq; - argc = caller_setup_args(th, GET_CFP(), flag, argc, 0, 0); + vm_caller_setup_args(th, GET_CFP(), ci); if (BUILTIN_TYPE(iseq) != T_NODE) { int opt_pc; const int arg_size = iseq->arg_size; - VALUE * const rsp = GET_SP() - argc; + VALUE * const rsp = GET_SP() - ci->argc; SET_SP(rsp); CHECK_STACK_OVERFLOW(GET_CFP(), iseq->stack_max); - opt_pc = vm_yield_setup_args(th, iseq, argc, rsp, 0, - block_proc_is_lambda(block->proc)); + opt_pc = vm_yield_setup_args(th, iseq, ci->argc, rsp, 0, block_proc_is_lambda(block->proc)); vm_push_frame(th, iseq, VM_FRAME_MAGIC_BLOCK, block->self, block->klass, @@ -998,8 +813,8 @@ vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_num_t num, rb_n return Qundef; } else { - VALUE val = vm_yield_with_cfunc(th, block, block->self, argc, STACK_ADDR_FROM_TOP(argc), 0); - POPN(argc); /* TODO: should put before C/yield? */ + VALUE 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; } } @@ -1429,105 +1244,6 @@ vm_setivar(VALUE obj, ID id, VALUE val, IC ic) #endif } -static inline const rb_method_entry_t * -vm_method_search(VALUE id, VALUE klass, CALL_INFO ci, VALUE *defined_class_ptr) -{ - rb_method_entry_t *me; -#if OPT_INLINE_METHOD_CACHE - if (LIKELY(klass == ci->ic_class && - GET_VM_STATE_VERSION() == ci->ic_vmstat)) { - me = ci->method; - if (defined_class_ptr) { - *defined_class_ptr = ci->defined_class; - } - } - else { - VALUE defined_class; - me = rb_method_entry(klass, id, &defined_class); - if (defined_class_ptr) { - *defined_class_ptr = defined_class; - } - ci->ic_class = klass; - ci->method = me; - ci->defined_class = defined_class; - ci->ic_vmstat = GET_VM_STATE_VERSION(); - } -#else - me = rb_method_entry(klass, id, defined_class_ptr); -#endif - return me; -} - -static inline VALUE -vm_search_normal_superclass(VALUE klass) -{ - klass = RCLASS_ORIGIN(klass); - return RCLASS_SUPER(klass); -} - -static void -vm_super_outside(void) -{ - rb_raise(rb_eNoMethodError, "super called outside of method"); -} - -static void -vm_search_superclass(rb_control_frame_t *reg_cfp, rb_iseq_t *iseq, - VALUE sigval, - ID *idp, VALUE *klassp) -{ - ID id; - VALUE klass; - - while (iseq && !iseq->klass) { - iseq = iseq->parent_iseq; - } - - if (iseq == 0) { - vm_super_outside(); - } - - id = iseq->defined_method_id; - - if (iseq != iseq->local_iseq) { - /* defined by Module#define_method() */ - rb_control_frame_t *lcfp = GET_CFP(); - - if (!sigval) { - /* zsuper */ - rb_raise(rb_eRuntimeError, "implicit argument passing of super from method defined by define_method() is not supported. Specify all arguments explicitly."); - } - - while (lcfp->iseq != iseq) { - rb_thread_t *th = GET_THREAD(); - VALUE *tep = VM_EP_PREV_EP(lcfp->ep); - while (1) { - lcfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(lcfp); - if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, lcfp)) { - vm_super_outside(); - } - if (lcfp->ep == tep) { - break; - } - } - } - - /* temporary measure for [Bug #2420] [Bug #3136] */ - if (!lcfp->me) { - vm_super_outside(); - } - - id = lcfp->me->def->original_id; - klass = vm_search_normal_superclass(lcfp->klass); - } - else { - klass = vm_search_normal_superclass(reg_cfp->klass); - } - - *idp = id; - *klassp = klass; -} - static VALUE vm_throw(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_num_t throw_state, VALUE throwobj) @@ -1758,6 +1474,32 @@ vm_expandarray(rb_control_frame_t *cfp, VALUE ary, rb_num_t num, int flag) RB_GC_GUARD(ary); } +static VALUE vm_call_general(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci); + +static void +vm_search_method(rb_call_info_t *ci, VALUE recv) +{ + VALUE klass = CLASS_OF(recv); + rb_method_entry_t *me; + +#if OPT_INLINE_METHOD_CACHE + if (LIKELY(GET_VM_STATE_VERSION() == ci->vmstat && klass == ci->klass)) { + /* cache hit! */ + } + else { + me = rb_method_entry(klass, ci->mid, &ci->defined_class); + ci->me = me; + ci->klass = klass; + ci->vmstat = GET_VM_STATE_VERSION(); + ci->call = vm_call_general; + } +#else + ci->method = rb_method_entry(klass, id, &ci->defined_class); + ci->call = vm_call_general; + ci->klass = klass; +#endif +} + static inline int check_cfunc(const rb_method_entry_t *me, VALUE (*func)()) { @@ -1805,9 +1547,9 @@ opt_eq_func(VALUE recv, VALUE obj, CALL_INFO ci) } { - const rb_method_entry_t *me = vm_method_search(idEq, CLASS_OF(recv), ci, 0); + vm_search_method(ci, recv); - if (check_cfunc(me, rb_obj_equal)) { + if (check_cfunc(ci->me, rb_obj_equal)) { return recv == obj ? Qtrue : Qfalse; } } @@ -1927,3 +1669,280 @@ vm_base_ptr(rb_control_frame_t *cfp) return bp; } + +static inline VALUE +vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci) +{ + VALUE val; + + start_method_dispatch: + if (ci->me != 0) { + if ((ci->me->flag == 0)) { + normal_method_dispatch: + switch (ci->me->def->type) { + case VM_METHOD_TYPE_ISEQ:{ + vm_setup_method(th, cfp, ci); + return Qundef; + } + case VM_METHOD_TYPE_NOTIMPLEMENTED: + case VM_METHOD_TYPE_CFUNC:{ + val = vm_call_cfunc(th, cfp, ci); + break; + } + case VM_METHOD_TYPE_ATTRSET:{ + rb_check_arity(ci->argc, 1, 1); + val = rb_ivar_set(ci->recv, ci->me->def->body.attr.id, *(cfp->sp - 1)); + cfp->sp -= 2; + break; + } + case VM_METHOD_TYPE_IVAR:{ + rb_check_arity(ci->argc, 0, 0); + val = rb_attr_get(ci->recv, ci->me->def->body.attr.id); + cfp->sp -= 1; + break; + } + case VM_METHOD_TYPE_MISSING:{ + VALUE *argv = ALLOCA_N(VALUE, ci->argc+1); + argv[0] = ID2SYM(ci->me->def->original_id); + MEMCPY(argv+1, cfp->sp - ci->argc, VALUE, ci->argc); + cfp->sp += - ci->argc - 1; + th->passed_block = ci->blockptr; + val = rb_funcall2(ci->recv, rb_intern("method_missing"), ci->argc+1, argv); + break; + } + case VM_METHOD_TYPE_BMETHOD:{ + VALUE *argv = ALLOCA_N(VALUE, ci->argc); + MEMCPY(argv, cfp->sp - ci->argc, VALUE, ci->argc); + cfp->sp += - ci->argc - 1; + val = vm_call_bmethod(th, ci->recv, ci->argc, argv, + ci->blockptr, ci->me, ci->defined_class); + break; + } + case VM_METHOD_TYPE_ZSUPER:{ + VALUE klass = RCLASS_SUPER(ci->me->klass); + rb_call_info_t cie = *ci; + ci = &cie; + + ci->me = rb_method_entry(klass, ci->mid, &ci->defined_class); + + if (ci->me != 0) { + goto normal_method_dispatch; + } + else { + goto start_method_dispatch; + } + } + case VM_METHOD_TYPE_OPTIMIZED:{ + switch (ci->me->def->body.optimize_type) { + case OPTIMIZED_METHOD_TYPE_SEND: { + rb_control_frame_t *reg_cfp = cfp; + int i = ci->argc - 1; + VALUE sym; + rb_call_info_t ci_entry; + + if (ci->argc == 0) { + rb_raise(rb_eArgError, "no method name given"); + } + + ci_entry = *ci; /* copy ci entry */ + ci = &ci_entry; + + sym = TOPN(i); + + if (SYMBOL_P(sym)) { + ci->mid = SYM2ID(sym); + } + else if (!(ci->mid = rb_check_id(&sym))) { + if (rb_method_basic_definition_p(CLASS_OF(ci->recv), idMethodMissing)) { + VALUE exc = make_no_method_exception(rb_eNoMethodError, NULL, ci->recv, rb_long2int(ci->argc), &TOPN(i)); + rb_exc_raise(exc); + } + ci->mid = rb_to_id(sym); + } + /* shift arguments */ + if (i > 0) { + MEMMOVE(&TOPN(i), &TOPN(i-1), VALUE, i); + } + ci->me = rb_method_entry(CLASS_OF(ci->recv), ci->mid, &ci->defined_class); + ci->argc -= 1; + DEC_SP(1); + /* TODO: fixme */ + ci->flag |= VM_CALL_FCALL_BIT | VM_CALL_OPT_SEND_BIT; + + goto start_method_dispatch; + } + case OPTIMIZED_METHOD_TYPE_CALL: { + rb_proc_t *proc; + int argc = ci->argc; + VALUE *argv = ALLOCA_N(VALUE, argc); + GetProcPtr(ci->recv, proc); + MEMCPY(argv, cfp->sp - argc, VALUE, argc); + cfp->sp -= argc + 1; + + val = rb_vm_invoke_proc(th, proc, argc, argv, ci->blockptr); + break; + } + default: + rb_bug("eval_invoke_method: unsupported optimized method type (%d)", + ci->me->def->body.optimize_type); + } + break; + } + default:{ + rb_bug("eval_invoke_method: unsupported method type (%d)", ci->me->def->type); + break; + } + } + } + else { + int noex_safe; + if (!(ci->flag & VM_CALL_FCALL_BIT) && (ci->me->flag & NOEX_MASK) & NOEX_PRIVATE) { + int stat = NOEX_PRIVATE; + + if (ci->flag & VM_CALL_VCALL_BIT) { + stat |= NOEX_VCALL; + } + val = vm_method_missing(th, cfp, ci->mid, ci->recv, ci->argc, ci->blockptr, stat); + } + else if (!(ci->flag & VM_CALL_OPT_SEND_BIT) && (ci->me->flag & NOEX_MASK) & NOEX_PROTECTED) { + if (!rb_obj_is_kind_of(cfp->self, ci->defined_class)) { + val = vm_method_missing(th, cfp, ci->mid, ci->recv, ci->argc, ci->blockptr, NOEX_PROTECTED); + } + else { + goto normal_method_dispatch; + } + } + else if ((noex_safe = NOEX_SAFE(ci->me->flag)) > th->safe_level && (noex_safe > 2)) { + rb_raise(rb_eSecurityError, "calling insecure method: %s", rb_id2name(ci->mid)); + } + else { + goto normal_method_dispatch; + } + } + } + else { + /* method missing */ + int stat = 0; + if (ci->flag & VM_CALL_VCALL_BIT) { + stat |= NOEX_VCALL; + } + if (ci->flag & VM_CALL_SUPER_BIT) { + stat |= NOEX_SUPER; + } + if (ci->mid == idMethodMissing) { + rb_control_frame_t *reg_cfp = cfp; + VALUE *argv = STACK_ADDR_FROM_TOP(ci->argc); + rb_raise_method_missing(th, ci->argc, argv, ci->recv, stat); + } + else { + val = vm_method_missing(th, cfp, ci->mid, ci->recv, ci->argc, ci->blockptr, stat); + } + } + + RUBY_VM_CHECK_INTS(th); + return val; +} + +static inline VALUE +vm_search_normal_superclass(VALUE klass) +{ + klass = RCLASS_ORIGIN(klass); + return RCLASS_SUPER(klass); +} + +static void +vm_super_outside(void) +{ + rb_raise(rb_eNoMethodError, "super called outside of method"); +} + +static void +vm_search_superclass(rb_control_frame_t *reg_cfp, rb_iseq_t *iseq, VALUE sigval, rb_call_info_t *ci) +{ + while (iseq && !iseq->klass) { + iseq = iseq->parent_iseq; + } + + if (iseq == 0) { + vm_super_outside(); + } + + ci->mid = iseq->defined_method_id; + + if (iseq != iseq->local_iseq) { + /* defined by Module#define_method() */ + rb_control_frame_t *lcfp = GET_CFP(); + + if (!sigval) { + /* zsuper */ + rb_raise(rb_eRuntimeError, "implicit argument passing of super from method defined by define_method() is not supported. Specify all arguments explicitly."); + } + + while (lcfp->iseq != iseq) { + rb_thread_t *th = GET_THREAD(); + VALUE *tep = VM_EP_PREV_EP(lcfp->ep); + while (1) { + lcfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(lcfp); + if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, lcfp)) { + vm_super_outside(); + } + if (lcfp->ep == tep) { + break; + } + } + } + + /* temporary measure for [Bug #2420] [Bug #3136] */ + if (!lcfp->me) { + vm_super_outside(); + } + + ci->mid = lcfp->me->def->original_id; + ci->klass = vm_search_normal_superclass(lcfp->klass); + } + else { + ci->klass = vm_search_normal_superclass(reg_cfp->klass); + } +} + +static void +vm_search_super_method(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci) +{ + VALUE current_defind_class; + rb_iseq_t *iseq = GET_ISEQ(); + VALUE sigval = TOPN(ci->orig_argc); + + current_defind_class = GET_CFP()->klass; + if (NIL_P(current_defind_class)) { + vm_super_outside(); + } + + if (!NIL_P(RCLASS_REFINED_CLASS(current_defind_class))) { + current_defind_class = RCLASS_REFINED_CLASS(current_defind_class); + } + + if (!rb_obj_is_kind_of(ci->recv, current_defind_class)) { + rb_raise(rb_eNotImpError, "super from singleton method that is defined to multiple classes is not supported; this will be fixed in 2.0.0 or later"); + } + + vm_search_superclass(GET_CFP(), iseq, sigval, ci); + + /* TODO: use inline cache */ + ci->me = rb_method_entry(ci->klass, ci->mid, &ci->defined_class); + ci->call = vm_call_general; + + while (iseq && !iseq->klass) { + iseq = iseq->parent_iseq; + } + + if (ci->me && ci->me->def->type == VM_METHOD_TYPE_ISEQ && ci->me->def->body.iseq == iseq) { + ci->klass = RCLASS_SUPER(ci->defined_class); + ci->me = rb_method_entry_get_with_refinements(Qnil, ci->klass, ci->mid, &ci->defined_class); + } +} + +static VALUE +vm_call_general(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci) +{ + return vm_call_method(th, reg_cfp, ci); +} |