summaryrefslogtreecommitdiff
path: root/vm_insnhelper.c
diff options
context:
space:
mode:
authorko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2012-10-14 16:59:05 +0000
committerko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2012-10-14 16:59:05 +0000
commitcbd597e9bc05eac7249e91dcdb4f1c1c75a53cf5 (patch)
tree8f570f2e813466ef8fa0ac3f564a638b69164773 /vm_insnhelper.c
parente8c234577fa7061a22f0dc515aefdc8dbc61f3b5 (diff)
* 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.c725
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);
+}