summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2014-11-02 18:02:55 +0000
committerko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2014-11-02 18:02:55 +0000
commitfbebd502f9f374d1eef31c63c10c7d8adcd63280 (patch)
tree69dc3dbcdd12509100d01fce6e5d95343b3337c9
parent055a465ac4222f314917c1d682fbd9d0d73535af (diff)
* rewrite method/block parameter fitting logic to optimize
keyword arguments/parameters and a splat argument. [Feature #10440] (Details are described in this ticket) Most of complex part is moved to vm_args.c. Now, ISeq#to_a does not catch up new instruction format. * vm_core.h: change iseq data structures. * introduce rb_call_info_kw_arg_t to represent keyword arguments. * add rb_call_info_t::kw_arg. * rename rb_iseq_t::arg_post_len to rb_iseq_t::arg_post_num. * rename rb_iseq_t::arg_keywords to arg_keyword_num. * rename rb_iseq_t::arg_keyword to rb_iseq_t::arg_keyword_bits. to represent keyword bitmap parameter index. This bitmap parameter shows that which keyword parameters are given or not given (0 for given). It is refered by `checkkeyword' instruction described bellow. * rename rb_iseq_t::arg_keyword_check to rb_iseq_t::arg_keyword_rest to represent keyword rest parameter index. * add rb_iseq_t::arg_keyword_default_values to represent default keyword values. * rename VM_CALL_ARGS_SKIP_SETUP to VM_CALL_ARGS_SIMPLE to represent (ci->flag & (SPLAT|BLOCKARG)) && ci->blockiseq == NULL && ci->kw_arg == NULL. * vm_insnhelper.c, vm_args.c: rewrite with refactoring. * rewrite splat argument code. * rewrite keyword arguments/parameters code. * merge method and block parameter fitting code into one code base. * vm.c, vm_eval.c: catch up these changes. * compile.c (new_callinfo): callinfo requires kw_arg parameter. * compile.c (compile_array_): check the last argument Hash object or not. If Hash object and all keys are Symbol literals, they are compiled to keyword arguments. * insns.def (checkkeyword): add new instruction. This instruction check the availability of corresponding keyword. For example, a method "def foo k1: 'v1'; end" is cimpiled to the following instructions. 0000 checkkeyword 2, 0 # check k1 is given. 0003 branchif 9 # if given, jump to address #9 0005 putstring "v1" 0007 setlocal_OP__WC__0 3 # k1 = 'v1' 0009 trace 8 0011 putnil 0012 trace 16 0014 leave * insns.def (opt_send_simple): removed and add new instruction "opt_send_without_block". * parse.y (new_args_tail_gen): reorder variables. Before this patch, a method "def foo(k1: 1, kr1:, k2: 2, **krest, &b)" has parameter variables "k1, kr1, k2, &b, internal_id, krest", but this patch reorders to "kr1, k1, k2, internal_id, krest, &b". (locate a block variable at last) * parse.y (vtable_pop): added. This function remove latest `n' variables from vtable. * iseq.c: catch up iseq data changes. * proc.c: ditto. * class.c (keyword_error): export as rb_keyword_error(). * common.mk: depend vm_args.c for vm.o. * hash.c (rb_hash_has_key): export. * internal.h: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@48239 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog84
-rw-r--r--class.c10
-rw-r--r--common.mk2
-rw-r--r--compile.c403
-rw-r--r--hash.c2
-rw-r--r--insns.def40
-rw-r--r--internal.h2
-rw-r--r--iseq.c40
-rw-r--r--parse.y64
-rw-r--r--proc.c6
-rw-r--r--vm.c2
-rw-r--r--vm_args.c783
-rw-r--r--vm_core.h46
-rw-r--r--vm_eval.c1
-rw-r--r--vm_insnhelper.c561
15 files changed, 1375 insertions, 671 deletions
diff --git a/ChangeLog b/ChangeLog
index 46e89329b6..2f22004339 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,87 @@
+Mon Nov 03 03:02:38 2014 Koichi Sasada <ko1@atdot.net>
+
+ * rewrite method/block parameter fitting logic to optimize
+ keyword arguments/parameters and a splat argument.
+ [Feature #10440] (Details are described in this ticket)
+
+ Most of complex part is moved to vm_args.c.
+
+ Now, ISeq#to_a does not catch up new instruction format.
+
+ * vm_core.h: change iseq data structures.
+
+ * introduce rb_call_info_kw_arg_t to represent keyword arguments.
+ * add rb_call_info_t::kw_arg.
+ * rename rb_iseq_t::arg_post_len to rb_iseq_t::arg_post_num.
+ * rename rb_iseq_t::arg_keywords to arg_keyword_num.
+ * rename rb_iseq_t::arg_keyword to rb_iseq_t::arg_keyword_bits.
+ to represent keyword bitmap parameter index.
+ This bitmap parameter shows that which keyword parameters are given
+ or not given (0 for given).
+ It is refered by `checkkeyword' instruction described bellow.
+ * rename rb_iseq_t::arg_keyword_check to rb_iseq_t::arg_keyword_rest
+ to represent keyword rest parameter index.
+ * add rb_iseq_t::arg_keyword_default_values to represent default
+ keyword values.
+ * rename VM_CALL_ARGS_SKIP_SETUP to VM_CALL_ARGS_SIMPLE
+ to represent
+ (ci->flag & (SPLAT|BLOCKARG)) &&
+ ci->blockiseq == NULL &&
+ ci->kw_arg == NULL.
+
+ * vm_insnhelper.c, vm_args.c: rewrite with refactoring.
+
+ * rewrite splat argument code.
+ * rewrite keyword arguments/parameters code.
+ * merge method and block parameter fitting code into one code base.
+
+ * vm.c, vm_eval.c: catch up these changes.
+
+ * compile.c (new_callinfo): callinfo requires kw_arg parameter.
+
+ * compile.c (compile_array_): check the last argument Hash object or
+ not. If Hash object and all keys are Symbol literals, they are
+ compiled to keyword arguments.
+
+ * insns.def (checkkeyword): add new instruction.
+ This instruction check the availability of corresponding keyword.
+
+ For example, a method "def foo k1: 'v1'; end" is cimpiled to the
+ following instructions.
+
+ 0000 checkkeyword 2, 0 # check k1 is given.
+ 0003 branchif 9 # if given, jump to address #9
+ 0005 putstring "v1"
+ 0007 setlocal_OP__WC__0 3 # k1 = 'v1'
+ 0009 trace 8
+ 0011 putnil
+ 0012 trace 16
+ 0014 leave
+
+ * insns.def (opt_send_simple): removed and add new instruction
+ "opt_send_without_block".
+
+ * parse.y (new_args_tail_gen): reorder variables.
+ Before this patch, a method "def foo(k1: 1, kr1:, k2: 2, **krest, &b)"
+ has parameter variables "k1, kr1, k2, &b, internal_id, krest",
+ but this patch reorders to "kr1, k1, k2, internal_id, krest, &b".
+ (locate a block variable at last)
+
+ * parse.y (vtable_pop): added.
+ This function remove latest `n' variables from vtable.
+
+ * iseq.c: catch up iseq data changes.
+
+ * proc.c: ditto.
+
+ * class.c (keyword_error): export as rb_keyword_error().
+
+ * common.mk: depend vm_args.c for vm.o.
+
+ * hash.c (rb_hash_has_key): export.
+
+ * internal.h: ditto.
+
Mon Nov 3 02:35:32 2014 Koichi Sasada <ko1@atdot.net>
* sample/simple-bench.rb: added to measure performance of simple
diff --git a/class.c b/class.c
index 3ebff44d55..c6b659f28f 100644
--- a/class.c
+++ b/class.c
@@ -1862,9 +1862,9 @@ rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...)
return argc;
}
-NORETURN(static void keyword_error(const char *error, VALUE keys));
-static void
-keyword_error(const char *error, VALUE keys)
+NORETURN(void rb_keyword_error(const char *error, VALUE keys));
+void
+rb_keyword_error(const char *error, VALUE keys)
{
const char *msg = "";
if (RARRAY_LEN(keys) == 1) {
@@ -1890,7 +1890,7 @@ unknown_keyword_error(VALUE hash, const ID *table, int keywords)
}
keys = rb_funcall(hash, rb_intern("keys"), 0, 0);
if (!RB_TYPE_P(keys, T_ARRAY)) rb_raise(rb_eArgError, "unknown keyword");
- keyword_error("unknown", keys);
+ rb_keyword_error("unknown", keys);
}
static int
@@ -1957,7 +1957,7 @@ rb_get_kwargs(VALUE keyword_hash, const ID *table, int required, int optional, V
rb_ary_push(missing, keyword);
}
if (!NIL_P(missing)) {
- keyword_error("missing", missing);
+ rb_keyword_error("missing", missing);
}
}
j = i;
diff --git a/common.mk b/common.mk
index 6a9bba3575..75c6a7a646 100644
--- a/common.mk
+++ b/common.mk
@@ -824,7 +824,7 @@ iseq.$(OBJEXT): {$(VPATH)}iseq.c {$(VPATH)}gc.h {$(VPATH)}iseq.h \
{$(VPATH)}insns_info.inc {$(VPATH)}node_name.inc {$(VPATH)}internal.h \
{$(VPATH)}vm_opts.h {$(VPATH)}ruby_atomic.h {$(VPATH)}eval_intern.h \
{$(VPATH)}util.h
-vm.$(OBJEXT): {$(VPATH)}vm.c {$(VPATH)}gc.h {$(VPATH)}iseq.h \
+vm.$(OBJEXT): {$(VPATH)}vm.c {$(VPATH)}gc.h {$(VPATH)}iseq.h {$(VPATH)}vm_args.c \
{$(VPATH)}eval_intern.h $(RUBY_H_INCLUDES) $(ENCODING_H_INCLUDES) \
$(VM_CORE_H_INCLUDES) {$(VPATH)}vm_method.c {$(VPATH)}vm_eval.c \
{$(VPATH)}vm_insnhelper.c {$(VPATH)}vm_insnhelper.h {$(VPATH)}vm_exec.c \
diff --git a/compile.c b/compile.c
index 8df7acfd32..860fe13759 100644
--- a/compile.c
+++ b/compile.c
@@ -202,21 +202,25 @@ r_value(VALUE value)
/* Specific Insn factory */
#define ADD_SEND(seq, line, id, argc) \
- ADD_SEND_R((seq), (line), (id), (argc), (VALUE)Qfalse, (VALUE)INT2FIX(0))
+ ADD_SEND_R((seq), (line), (id), (argc), (VALUE)Qfalse, (VALUE)INT2FIX(0), NULL)
+
+#define ADD_SEND_WITH_FLAG(seq, line, id, argc, flag) \
+ ADD_SEND_R((seq), (line), (id), (argc), (VALUE)Qfalse, (VALUE)(flag), NULL)
+
+#define ADD_SEND_WITH_BLOCK(seq, line, id, argc, block) \
+ ADD_SEND_R((seq), (line), (id), (argc), (VALUE)(block), (VALUE)INT2FIX(0), NULL)
#define ADD_CALL_RECEIVER(seq, line) \
ADD_INSN((seq), (line), putself)
#define ADD_CALL(seq, line, id, argc) \
- ADD_SEND_R((seq), (line), (id), (argc), (VALUE)Qfalse, (VALUE)INT2FIX(VM_CALL_FCALL))
+ ADD_SEND_R((seq), (line), (id), (argc), (VALUE)Qfalse, (VALUE)INT2FIX(VM_CALL_FCALL), NULL)
#define ADD_CALL_WITH_BLOCK(seq, line, id, argc, block) \
- ADD_SEND_R((seq), (line), (id), (argc), (block), (VALUE)INT2FIX(VM_CALL_FCALL))
+ ADD_SEND_R((seq), (line), (id), (argc), (block), (VALUE)INT2FIX(VM_CALL_FCALL), NULL)
-#define ADD_SEND_R(seq, line, id, argc, block, flag) \
- ADD_ELEM((seq), (LINK_ELEMENT *) \
- new_insn_send(iseq, (line), \
- (id), (VALUE)(argc), (VALUE)(block), (VALUE)(flag)))
+#define ADD_SEND_R(seq, line, id, argc, block, flag, keywords) \
+ ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_send(iseq, (line), (id), (VALUE)(argc), (VALUE)(block), (VALUE)(flag), (keywords)))
#define ADD_TRACE(seq, line, event) \
do { \
@@ -870,23 +874,33 @@ new_insn_body(rb_iseq_t *iseq, int line_no, enum ruby_vminsn_type insn_id, int a
}
static rb_call_info_t *
-new_callinfo(rb_iseq_t *iseq, ID mid, int argc, VALUE block, unsigned int flag)
+new_callinfo(rb_iseq_t *iseq, ID mid, int argc, VALUE block, unsigned int flag, rb_call_info_kw_arg_t *kw_arg)
{
rb_call_info_t *ci = (rb_call_info_t *)compile_data_alloc(iseq, sizeof(rb_call_info_t));
+
ci->mid = mid;
ci->flag = flag;
ci->orig_argc = argc;
ci->argc = argc;
+ ci->kw_arg = kw_arg;
+
+ if (kw_arg) {
+ ci->argc += kw_arg->keyword_len;
+ ci->orig_argc += kw_arg->keyword_len;
+ }
if (block) {
GetISeqPtr(block, ci->blockiseq);
}
else {
ci->blockiseq = 0;
- if (!(ci->flag & (VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_BLOCKARG))) {
- ci->flag |= VM_CALL_ARGS_SKIP_SETUP;
- }
}
+
+ if (!(ci->flag & (VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_BLOCKARG)) &&
+ ci->blockiseq == NULL && ci->kw_arg == NULL) {
+ ci->flag |= VM_CALL_ARGS_SIMPLE;
+ }
+
ci->method_state = 0;
ci->class_serial = 0;
ci->blockptr = 0;
@@ -899,10 +913,10 @@ new_callinfo(rb_iseq_t *iseq, ID mid, int argc, VALUE block, unsigned int flag)
}
static INSN *
-new_insn_send(rb_iseq_t *iseq, int line_no, ID id, VALUE argc, VALUE block, VALUE flag)
+new_insn_send(rb_iseq_t *iseq, int line_no, ID id, VALUE argc, VALUE block, VALUE flag, rb_call_info_kw_arg_t *keywords)
{
VALUE *operands = (VALUE *)compile_data_alloc(iseq, sizeof(VALUE) * 1);
- operands[0] = (VALUE)new_callinfo(iseq, id, FIX2INT(argc), block, FIX2INT(flag));
+ operands[0] = (VALUE)new_callinfo(iseq, id, FIX2INT(argc), block, FIX2INT(flag), keywords);
return new_insn_core(iseq, line_no, BIN(send), 1, operands);
}
@@ -1074,7 +1088,7 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *optargs, NODE *node_args)
if (args->first_post_arg) {
iseq->arg_post_start = get_dyna_var_idx_at_raw(iseq, args->first_post_arg);
- iseq->arg_post_len = args->post_args_num;
+ iseq->arg_post_num = args->post_args_num;
}
if (args->opt_args) {
@@ -1112,44 +1126,59 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *optargs, NODE *node_args)
if (args->kw_args) {
NODE *node = args->kw_args;
- VALUE keywords = rb_ary_tmp_new(1);
- VALUE required = 0;
- int i = 0, j, r = 0;
+ const VALUE default_values = rb_ary_tmp_new(1);
+ const VALUE complex_mark = rb_str_tmp_new(0);
+ int kw = 0, rkw = 0, di = 0, i;
+
+ iseq->arg_keyword_bits = get_dyna_var_idx_at_raw(iseq, args->kw_rest_arg->nd_vid);
- iseq->arg_keyword = get_dyna_var_idx_at_raw(iseq, args->kw_rest_arg->nd_vid);
- COMPILE(optargs, "kwarg", args->kw_rest_arg);
while (node) {
- VALUE list = keywords;
- if (node->nd_body->nd_value == (NODE *)-1) {
- ++r;
- if (!required) required = rb_ary_tmp_new(1);
- list = required;
+ NODE *val_node = node->nd_body->nd_value;
+ VALUE dv;
+
+ if (val_node == (NODE *)-1) {
+ ++rkw;
}
- rb_ary_push(list, ID2SYM(node->nd_body->nd_vid));
- COMPILE_POPED(optargs, "kwarg", node); /* nd_type(node) == NODE_KW_ARG */
+ else {
+ if (nd_type(val_node) == NODE_LIT) {
+ dv = val_node->nd_lit;
+ iseq_add_mark_object(iseq, dv);
+ }
+ else if (nd_type(val_node) == NODE_NIL) {
+ dv = Qnil;
+ }
+ else {
+ COMPILE_POPED(optargs, "kwarg", node); /* nd_type(node) == NODE_KW_ARG */
+ dv = complex_mark;
+ }
+
+ iseq->arg_keyword_num = ++di;
+ rb_ary_push(default_values, dv);
+ }
+
+ kw++;
node = node->nd_next;
- i += 1;
}
- iseq->arg_keyword_check = args->kw_rest_arg->nd_cflag != 0;
- iseq->arg_keywords = i;
- iseq->arg_keyword_required = r;
- iseq->arg_keyword_table = ALLOC_N(ID, i);
- if (r) {
- rb_ary_concat(required, keywords);
- keywords = required;
- }
- for (j = 0; j < i; j++) {
- iseq->arg_keyword_table[j] = SYM2ID(RARRAY_AREF(keywords, j));
+
+ iseq->arg_keyword_num = kw;
+ iseq->arg_keyword_rest = args->kw_rest_arg->nd_cflag != 0 ? get_dyna_var_idx_at_raw(iseq, args->kw_rest_arg->nd_cflag) : -1;
+ iseq->arg_keyword_required = rkw;
+ iseq->arg_keyword_table = &iseq->local_table[iseq->arg_keyword_bits - iseq->arg_keyword_num];
+ iseq->arg_keyword_default_values = ALLOC_N(VALUE, RARRAY_LEN(default_values));
+
+ for (i = 0; i < RARRAY_LEN(default_values); i++) {
+ VALUE dv = RARRAY_AREF(default_values, i);
+ if (dv == complex_mark) dv = Qundef;
+ iseq->arg_keyword_default_values[i] = dv;
}
- ADD_INSN(optargs, nd_line(args->kw_args), pop);
}
else if (args->kw_rest_arg) {
- iseq->arg_keyword = get_dyna_var_idx_at_raw(iseq, args->kw_rest_arg->nd_vid);
- COMPILE(optargs, "kwarg", args->kw_rest_arg);
- ADD_INSN(optargs, nd_line(args->kw_rest_arg), pop);
+ iseq->arg_keyword_bits = -1;
+ iseq->arg_keyword_rest = get_dyna_var_idx_at_raw(iseq, args->kw_rest_arg->nd_vid);
}
else {
- iseq->arg_keyword = -1;
+ iseq->arg_keyword_bits = -1;
+ iseq->arg_keyword_rest = -1;
}
if (args->pre_init) { /* m_init */
@@ -1175,20 +1204,26 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *optargs, NODE *node_args)
iseq->arg_block = get_dyna_var_idx_at_raw(iseq, block_id);
}
- if (iseq->arg_opts != 0 || iseq->arg_post_len != 0 ||
- iseq->arg_rest != -1 || iseq->arg_block != -1 ||
- iseq->arg_keyword != -1) {
+ if (iseq->arg_opts != 0 ||
+ iseq->arg_post_num != 0 ||
+ iseq->arg_rest != -1 ||
+ iseq->arg_block != -1 ||
+ iseq->arg_keyword_bits != -1 ||
+ iseq->arg_keyword_rest != -1) {
iseq->arg_simple = 0;
/* set arg_size: size of arguments */
- if (iseq->arg_keyword != -1) {
- iseq->arg_size = iseq->arg_keyword + 1;
- }
- else if (iseq->arg_block != -1) {
+ if (iseq->arg_block != -1) {
iseq->arg_size = iseq->arg_block + 1;
}
- else if (iseq->arg_post_len) {
- iseq->arg_size = iseq->arg_post_start + iseq->arg_post_len;
+ else if (iseq->arg_keyword_rest != -1) {
+ iseq->arg_size = iseq->arg_keyword_rest + 1;
+ }
+ else if (iseq->arg_keyword_bits != -1) {
+ iseq->arg_size = iseq->arg_keyword_bits + 1;
+ }
+ else if (iseq->arg_post_num) {
+ iseq->arg_size = iseq->arg_post_start + iseq->arg_post_num;
}
else if (iseq->arg_rest != -1) {
iseq->arg_size = iseq->arg_rest + 1;
@@ -1206,8 +1241,14 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *optargs, NODE *node_args)
}
if (iseq->type == ISEQ_TYPE_BLOCK) {
- if (iseq->arg_opts == 0 && iseq->arg_post_len == 0 &&
- iseq->arg_rest == -1 && iseq->arg_keyword == -1) {
+ if (iseq->arg_opts == 0 &&
+ iseq->arg_post_num == 0 &&
+ iseq->arg_rest == -1 &&
+ iseq->arg_keyword_bits == -1 &&
+ iseq->arg_keyword_rest == -1) {
+
+ /* TODO: why not check block? */
+
if (iseq->argc == 1 && last_comma == 0) {
/* {|a|} */
iseq->arg_simple |= 0x02;
@@ -1812,7 +1853,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
INSN *piobj = (INSN *)get_prev_insn((INSN *)list);
enum ruby_vminsn_type previ = piobj->insn_id;
- if (previ == BIN(send) || previ == BIN(opt_send_simple) || previ == BIN(invokesuper)) {
+ if (previ == BIN(send) || previ == BIN(opt_send_without_block) || previ == BIN(invokesuper)) {
rb_call_info_t *ci = (rb_call_info_t *)piobj->operands[0];
if (ci->blockiseq == 0) {
ci->flag |= VM_CALL_TAILCALL;
@@ -1836,7 +1877,7 @@ insn_set_specialized_instruction(rb_iseq_t *iseq, INSN *iobj, int insn_id)
}
iobj->operands = (VALUE *)compile_data_alloc(iseq, iobj->operand_size * sizeof(VALUE));
iobj->operands[0] = old_operands[0];
- iobj->operands[1] = (VALUE)new_callinfo(iseq, idEq, 1, 0, 0);
+ iobj->operands[1] = (VALUE)new_callinfo(iseq, idEq, 1, 0, 0, NULL);
}
return COMPILE_OK;
@@ -1849,7 +1890,7 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
rb_call_info_t *ci = (rb_call_info_t *)OPERAND_AT(iobj, 0);
#define SP_INSN(opt) insn_set_specialized_instruction(iseq, iobj, BIN(opt_##opt))
- if (ci->blockiseq == 0 && (ci->flag & ~VM_CALL_ARGS_SKIP_SETUP) == 0) {
+ if (ci->flag & VM_CALL_ARGS_SIMPLE) {
switch (ci->orig_argc) {
case 0:
switch (ci->mid) {
@@ -1884,8 +1925,9 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
break;
}
}
- if (ci->flag & VM_CALL_ARGS_SKIP_SETUP) {
- iobj->insn_id = BIN(opt_send_simple);
+
+ if ((ci->flag & VM_CALL_ARGS_BLOCKARG) == 0 && ci->blockiseq == NULL) {
+ iobj->insn_id = BIN(opt_send_without_block);
}
}
#undef SP_INSN
@@ -2266,6 +2308,52 @@ compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * cond,
return COMPILE_OK;
}
+static int
+compile_array_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE * const root_node, rb_call_info_kw_arg_t ** const kw_arg_ptr)
+{
+ if (kw_arg_ptr == NULL) return FALSE;
+
+ if (nd_type(root_node) == NODE_HASH && root_node->nd_head && nd_type(root_node->nd_head) == NODE_ARRAY) {
+ NODE *node = root_node->nd_head;
+
+ while (node) {
+ NODE *key_node = node->nd_head;
+
+ assert(nd_type(node) == NODE_ARRAY);
+ if (key_node && nd_type(key_node) == NODE_LIT && RB_TYPE_P(key_node->nd_lit, T_SYMBOL)) {
+ /* can be keywords */
+ }
+ else {
+ return FALSE;
+ }
+ node = node->nd_next; /* skip value node */
+ node = node->nd_next;
+ }
+
+ /* may be keywords */
+ node = root_node->nd_head;
+ {
+ int len = (int)node->nd_alen / 2;
+ rb_call_info_kw_arg_t *kw_arg = (rb_call_info_kw_arg_t *)ruby_xmalloc(sizeof(rb_call_info_kw_arg_t) + sizeof(VALUE) * (len - 1));
+ ID *keywords = kw_arg->keywords;
+ int i = 0;
+ kw_arg->keyword_len = len;
+
+ *kw_arg_ptr = kw_arg;
+
+ for (i=0; node != NULL; i++, node = node->nd_next->nd_next) {
+ NODE *key_node = node->nd_head;
+ NODE *val_node = node->nd_next->nd_head;
+ keywords[i] = SYM2ID(key_node->nd_lit);
+ COMPILE(ret, "keyword values", val_node);
+ }
+ assert(i == len);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
enum compile_array_type_t {
COMPILE_ARRAY_TYPE_ARRAY,
COMPILE_ARRAY_TYPE_HASH,
@@ -2274,7 +2362,7 @@ enum compile_array_type_t {
static int
compile_array_(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE* node_root,
- enum compile_array_type_t type, int poped)
+ enum compile_array_type_t type, rb_call_info_kw_arg_t **keywords_ptr, int poped)
{
NODE *node = node_root;
int line = (int)nd_line(node);
@@ -2316,7 +2404,12 @@ compile_array_(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE* node_root,
opt_p = 0;
}
- COMPILE_(anchor, "array element", node->nd_head, poped);
+ if (type == COMPILE_ARRAY_TYPE_ARGS && node->nd_next == NULL /* last node */ && compile_array_keyword_arg(iseq, anchor, node->nd_head, keywords_ptr)) {
+ len--;
+ }
+ else {
+ COMPILE_(anchor, "array element", node->nd_head, poped);
+ }
}
if (opt_p && type != COMPILE_ARRAY_TYPE_ARGS) {
@@ -2423,7 +2516,7 @@ compile_array_(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE* node_root,
static VALUE
compile_array(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE* node_root, enum compile_array_type_t type)
{
- return compile_array_(iseq, ret, node_root, type, 0);
+ return compile_array_(iseq, ret, node_root, type, NULL, 0);
}
static VALUE
@@ -3001,7 +3094,7 @@ add_ensure_iseq(LINK_ANCHOR *ret, rb_iseq_t *iseq, int is_return)
}
static VALUE
-setup_args(rb_iseq_t *iseq, LINK_ANCHOR *args, NODE *argn, unsigned int *flag)
+setup_args(rb_iseq_t *iseq, LINK_ANCHOR *args, NODE *argn, unsigned int *flag, rb_call_info_kw_arg_t **keywords)
{
VALUE argc = INT2FIX(0);
int nsplat = 0;
@@ -3021,6 +3114,7 @@ setup_args(rb_iseq_t *iseq, LINK_ANCHOR *args, NODE *argn, unsigned int *flag)
switch (nd_type(argn)) {
case NODE_SPLAT: {
COMPILE(args, "args (splat)", argn->nd_head);
+ ADD_INSN1(args, nd_line(argn), splatarray, Qfalse);
argc = INT2FIX(1);
nsplat++;
*flag |= VM_CALL_ARGS_SPLAT;
@@ -3033,16 +3127,11 @@ setup_args(rb_iseq_t *iseq, LINK_ANCHOR *args, NODE *argn, unsigned int *flag)
INIT_ANCHOR(tmp);
COMPILE(tmp, "args (cat: splat)", argn->nd_body);
- if (next_is_array && nsplat == 0) {
- /* none */
+ if (nd_type(argn) == NODE_ARGSCAT) {
+ ADD_INSN1(tmp, nd_line(argn), splatarray, Qfalse);
}
else {
- if (nd_type(argn) == NODE_ARGSCAT) {
- ADD_INSN1(tmp, nd_line(argn), splatarray, Qfalse);
- }
- else {
- ADD_INSN1(tmp, nd_line(argn), newarray, INT2FIX(1));
- }
+ ADD_INSN1(tmp, nd_line(argn), newarray, INT2FIX(1));
}
INSERT_LIST(args_splat, tmp);
nsplat++;
@@ -3057,10 +3146,11 @@ setup_args(rb_iseq_t *iseq, LINK_ANCHOR *args, NODE *argn, unsigned int *flag)
}
break;
}
- case NODE_ARRAY: {
- argc = INT2FIX(compile_array(iseq, args, argn, COMPILE_ARRAY_TYPE_ARGS));
- break;
- }
+ case NODE_ARRAY:
+ {
+ argc = INT2FIX(compile_array_(iseq, args, argn, COMPILE_ARRAY_TYPE_ARGS, keywords, FALSE));
+ break;
+ }
default: {
rb_bug("setup_arg: unknown node: %s\n", ruby_node_name(nd_type(argn)));
}
@@ -3425,8 +3515,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
NEW_CHILD_ISEQVAL(node->nd_body, make_name_for_block(iseq),
ISEQ_TYPE_BLOCK, line);
- ADD_SEND_R(ret, line, idEach, INT2FIX(0),
- iseq->compile_data->current_block, INT2FIX(0));
+ ADD_SEND_WITH_BLOCK(ret, line, idEach, INT2FIX(0), iseq->compile_data->current_block);
}
else {
iseq->compile_data->current_block =
@@ -3937,12 +4026,12 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
boff = 1;
default:
INIT_ANCHOR(args);
- argc = setup_args(iseq, args, node->nd_args->nd_head, &flag);
+ argc = setup_args(iseq, args, node->nd_args->nd_head, &flag, NULL);
ADD_SEQ(ret, args);
}
ADD_INSN1(ret, line, dupn, FIXNUM_INC(argc, 1 + boff));
flag |= asgnflag;
- ADD_SEND_R(ret, line, idAREF, argc, Qfalse, INT2FIX(flag));
+ ADD_SEND_WITH_FLAG(ret, line, idAREF, argc, INT2FIX(flag));
if (id == 0 || id == 1) {
/* 0: or, 1: and
@@ -3985,14 +4074,12 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
ADD_INSN(ret, line, pop);
ADD_INSN(ret, line, pop);
}
- ADD_SEND_R(ret, line, idASET,
- argc, Qfalse, INT2FIX(flag));
+ ADD_SEND_WITH_FLAG(ret, line, idASET, argc, INT2FIX(flag));
}
else {
if (boff > 0)
ADD_INSN(ret, line, swap);
- ADD_SEND_R(ret, line, idASET,
- FIXNUM_INC(argc, 1), Qfalse, INT2FIX(flag));
+ ADD_SEND_WITH_FLAG(ret, line, idASET, FIXNUM_INC(argc, 1), INT2FIX(flag));
}
ADD_INSN(ret, line, pop);
ADD_INSNL(ret, line, jump, lfin);
@@ -4022,14 +4109,12 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
ADD_INSN(ret, line, pop);
ADD_INSN(ret, line, pop);
}
- ADD_SEND_R(ret, line, idASET,
- argc, Qfalse, INT2FIX(flag));
+ ADD_SEND_WITH_FLAG(ret, line, idASET, argc, INT2FIX(flag));
}
else {
if (boff > 0)
ADD_INSN(ret, line, swap);
- ADD_SEND_R(ret, line, idASET,
- FIXNUM_INC(argc, 1), Qfalse, INT2FIX(flag));
+ ADD_SEND_WITH_FLAG(ret, line, idASET, FIXNUM_INC(argc, 1), INT2FIX(flag));
}
ADD_INSN(ret, line, pop);
}
@@ -4085,8 +4170,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
asgnflag = COMPILE_RECV(ret, "NODE_OP_ASGN2#recv", node);
ADD_INSN(ret, line, dup);
- ADD_SEND_R(ret, line, node->nd_next->nd_vid,
- INT2FIX(0), Qfalse, INT2FIX(asgnflag));
+ ADD_SEND_WITH_FLAG(ret, line, node->nd_next->nd_vid, INT2FIX(0), INT2FIX(asgnflag));
if (atype == 0 || atype == 1) { /* 0: OR or 1: AND */
ADD_INSN(ret, line, dup);
@@ -4100,8 +4184,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value);
ADD_INSN(ret, line, swap);
ADD_INSN1(ret, line, topn, INT2FIX(1));
- ADD_SEND_R(ret, line, node->nd_next->nd_aid,
- INT2FIX(1), Qfalse, INT2FIX(asgnflag));
+ ADD_SEND_WITH_FLAG(ret, line, node->nd_next->nd_aid, INT2FIX(1), INT2FIX(asgnflag));
ADD_INSNL(ret, line, jump, lfin);
ADD_LABEL(ret, lcfin);
@@ -4122,8 +4205,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
ADD_INSN(ret, line, swap);
ADD_INSN1(ret, line, topn, INT2FIX(1));
}
- ADD_SEND_R(ret, line, node->nd_next->nd_aid,
- INT2FIX(1), Qfalse, INT2FIX(asgnflag));
+ ADD_SEND_WITH_FLAG(ret, line, node->nd_next->nd_aid, INT2FIX(1), INT2FIX(asgnflag));
ADD_INSN(ret, line, pop);
}
break;
@@ -4263,7 +4345,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
node->nd_args->nd_head->nd_lit = str;
COMPILE(ret, "recv", node->nd_recv);
ADD_INSN2(ret, line, opt_aref_with,
- new_callinfo(iseq, idAREF, 1, 0, 0), str);
+ new_callinfo(iseq, idAREF, 1, 0, 0, NULL), str);
if (poped) {
ADD_INSN(ret, line, pop);
}
@@ -4281,6 +4363,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
ID mid = node->nd_mid;
VALUE argc;
unsigned int flag = 0;
+ rb_call_info_kw_arg_t *keywords = NULL;
VALUE parent_block = iseq->compile_data->current_block;
iseq->compile_data->current_block = Qfalse;
@@ -4360,7 +4443,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
/* args */
if (nd_type(node) != NODE_VCALL) {
- argc = setup_args(iseq, args, node->nd_args, &flag);
+ argc = setup_args(iseq, args, node->nd_args, &flag, &keywords);
}
else {
argc = INT2FIX(0);
@@ -4380,8 +4463,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
flag |= VM_CALL_FCALL;
}
- ADD_SEND_R(ret, line, mid,
- argc, parent_block, INT2FIX(flag));
+ ADD_SEND_R(ret, line, mid, argc, parent_block, INT2FIX(flag), keywords);
if (poped) {
ADD_INSN(ret, line, pop);
@@ -4393,12 +4475,13 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
DECL_ANCHOR(args);
int argc;
unsigned int flag = 0;
+ rb_call_info_kw_arg_t *keywords = NULL;
VALUE parent_block = iseq->compile_data->current_block;
INIT_ANCHOR(args);
iseq->compile_data->current_block = Qfalse;
if (nd_type(node) == NODE_SUPER) {
- VALUE vargc = setup_args(iseq, args, node->nd_args, &flag);
+ VALUE vargc = setup_args(iseq, args, node->nd_args, &flag, &keywords);
argc = FIX2INT(vargc);
}
else {
@@ -4435,9 +4518,9 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
flag |= VM_CALL_ARGS_SPLAT;
}
- if (liseq->arg_post_len) {
+ if (liseq->arg_post_num > 0) {
/* post arguments */
- int post_len = liseq->arg_post_len;
+ int post_len = liseq->arg_post_num;
int post_start = liseq->arg_post_start;
if (liseq->arg_rest != -1) {
@@ -4460,16 +4543,22 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
}
}
- if (liseq->arg_keyword >= 0) {
+ if (liseq->arg_keyword_bits >= 0) { /* TODO: support keywords */
int local_size = liseq->local_size;
- int idx = local_size - liseq->arg_keyword;
argc++;
+
ADD_INSN1(args, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
- ADD_INSN2(args, line, getlocal, INT2FIX(idx), INT2FIX(lvar_level));
- ADD_SEND (args, line, rb_intern("dup"), INT2FIX(0));
- for (i = 0; i < liseq->arg_keywords; ++i) {
+
+ if (liseq->arg_keyword_rest > 0) {
+ ADD_INSN2(args, line, getlocal, INT2FIX(liseq->local_size - liseq->arg_keyword_rest), INT2FIX(lvar_level));
+ ADD_SEND (args, line, rb_intern("dup"), INT2FIX(0));
+ }
+ else {
+ ADD_INSN1(args, line, newhash, INT2FIX(0));
+ }
+ for (i = 0; i < liseq->arg_keyword_num; ++i) {
ID id = liseq->arg_keyword_table[i];
- idx = local_size - get_local_var_idx(liseq, id);
+ int idx = local_size - get_local_var_idx(liseq, id);
ADD_INSN1(args, line, putobject, ID2SYM(id));
ADD_INSN2(args, line, getlocal, INT2FIX(idx), INT2FIX(lvar_level));
}
@@ -4480,6 +4569,17 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
--argc;
}
}
+ else if (liseq->arg_keyword_rest >= 0) {
+ ADD_INSN2(args, line, getlocal, INT2FIX(liseq->local_size - liseq->arg_keyword_rest), INT2FIX(lvar_level));
+ ADD_SEND (args, line, rb_intern("dup"), INT2FIX(0));
+ if (liseq->arg_rest != -1) {
+ ADD_INSN1(args, line, newarray, INT2FIX(1));
+ ADD_INSN (args, line, concatarray);
+ }
+ else {
+ argc++;
+ }
+ }
}
}
@@ -4487,7 +4587,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
ADD_INSN1(ret, line, putobject, nd_type(node) == NODE_ZSUPER ? Qfalse : Qtrue);
ADD_SEQ(ret, args);
ADD_INSN1(ret, line, invokesuper, new_callinfo(iseq, 0, argc, parent_block,
- flag | VM_CALL_SUPER | VM_CALL_FCALL));
+ flag | VM_CALL_SUPER | VM_CALL_FCALL, keywords));
if (poped) {
ADD_INSN(ret, line, pop);
@@ -4495,7 +4595,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
break;
}
case NODE_ARRAY:{
- compile_array_(iseq, ret, node, COMPILE_ARRAY_TYPE_ARRAY, poped);
+ compile_array_(iseq, ret, node, COMPILE_ARRAY_TYPE_ARRAY, NULL, poped);
break;
}
case NODE_ZARRAY:{
@@ -4582,6 +4682,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
DECL_ANCHOR(args);
VALUE argc;
unsigned int flag = 0;
+ rb_call_info_kw_arg_t *keywords = NULL;
INIT_ANCHOR(args);
if (iseq->type == ISEQ_TYPE_TOP) {
@@ -4589,14 +4690,14 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
}
if (node->nd_head) {
- argc = setup_args(iseq, args, node->nd_head, &flag);
+ argc = setup_args(iseq, args, node->nd_head, &flag, &keywords);
}
else {
argc = INT2FIX(0);
}
ADD_SEQ(ret, args);
- ADD_INSN1(ret, line, invokeblock, new_callinfo(iseq, 0, FIX2INT(argc), 0, flag));
+ ADD_INSN1(ret, line, invokeblock, new_callinfo(iseq, 0, FIX2INT(argc), 0, flag, keywords));
if (poped) {
ADD_INSN(ret, line, pop);
@@ -4720,7 +4821,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
else {
ADD_SEQ(ret, recv);
ADD_SEQ(ret, val);
- ADD_INSN1(ret, line, opt_regexpmatch2, new_callinfo(iseq, idEqTilde, 1, 0, 0));
+ ADD_INSN1(ret, line, opt_regexpmatch2, new_callinfo(iseq, idEqTilde, 1, 0, 0, NULL));
}
}
else {
@@ -5012,8 +5113,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
/* function call */
ADD_CALL_RECEIVER(ret, line);
COMPILE(ret, "colon2#nd_head", node->nd_head);
- ADD_CALL(ret, line, node->nd_mid,
- INT2FIX(1));
+ ADD_CALL(ret, line, node->nd_mid, INT2FIX(1));
}
if (poped) {
ADD_INSN(ret, line, pop);
@@ -5186,43 +5286,34 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
}
break;
}
- case NODE_KW_ARG:{
- LABEL *default_label = NEW_LABEL(line);
- LABEL *end_label = 0;
- int idx, lv, ls;
- ID id = node->nd_body->nd_vid;
+ case NODE_KW_ARG:
+ {
+ LABEL *end_label = NEW_LABEL(nd_line(node));
+ NODE *default_value = node->nd_body->nd_value;
+
+ if (default_value == (NODE *)-1) {
+ /* required argument. do nothing */
+ rb_bug("unreachable");
+ }
+ else if (nd_type(default_value) == NODE_LIT) {
+ rb_bug("unreachable");
+ }
+ else {
+ /* if keywordcheck(_kw_bits, nth_keyword)
+ * kw = default_value
+ * end
+ */
+ int kw_bits_idx = iseq->local_size - iseq->arg_keyword_bits;
+ int keyword_idx = iseq->arg_keyword_num;
+
+ ADD_INSN2(ret, line, checkkeyword, INT2FIX(kw_bits_idx), INT2FIX(keyword_idx));
+ ADD_INSNL(ret, line, branchif, end_label);
+ COMPILE_POPED(ret, "keyword default argument", node->nd_body);
+ ADD_LABEL(ret, end_label);
+ }
- ADD_INSN(ret, line, dup);
- ADD_INSN1(ret, line, putobject, ID2SYM(id));
- ADD_SEND(ret, line, rb_intern("key?"), INT2FIX(1));
- ADD_INSNL(ret, line, branchunless, default_label);
- ADD_INSN(ret, line, dup);
- ADD_INSN1(ret, line, putobject, ID2SYM(id));
- ADD_SEND(ret, line, rb_intern("delete"), INT2FIX(1));
- switch (nd_type(node->nd_body)) {
- case NODE_LASGN:
- idx = iseq->local_iseq->local_size - get_local_var_idx(iseq, id);
- ADD_INSN2(ret, line, setlocal, INT2FIX(idx), INT2FIX(get_lvar_level(iseq)));
- break;
- case NODE_DASGN:
- case NODE_DASGN_CURR:
- idx = get_dyna_var_idx(iseq, id, &lv, &ls);
- ADD_INSN2(ret, line, setlocal, INT2FIX(ls - idx), INT2FIX(lv));
break;
- default:
- rb_bug("iseq_compile_each (NODE_KW_ARG): unknown node: %s", ruby_node_name(nd_type(node->nd_body)));
- }
- if (node->nd_body->nd_value != (NODE *)-1) {
- end_label = NEW_LABEL(nd_line(node));
- ADD_INSNL(ret, nd_line(node), jump, end_label);
- }
- ADD_LABEL(ret, default_label);
- if (node->nd_body->nd_value != (NODE *)-1) {
- COMPILE_POPED(ret, "keyword default argument", node->nd_body);
- ADD_LABEL(ret, end_label);
}
- break;
- }
case NODE_DSYM:{
compile_dstr(iseq, ret, node);
if (!poped) {
@@ -5257,14 +5348,14 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
ADD_INSN1(ret, line, topn, INT2FIX(1));
}
ADD_INSN2(ret, line, opt_aset_with,
- new_callinfo(iseq, idASET, 2, 0, 0), str);
+ new_callinfo(iseq, idASET, 2, 0, 0, NULL), str);
ADD_INSN(ret, line, pop);
break;
}
INIT_ANCHOR(recv);
INIT_ANCHOR(args);
- argc = setup_args(iseq, args, node->nd_args, &flag);
+ argc = setup_args(iseq, args, node->nd_args, &flag, NULL);
flag |= (asgnflag = COMPILE_RECV(recv, "recv", node));
@@ -5280,7 +5371,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
ADD_INSN1(ret, line, topn, INT2FIX(1));
if (flag & VM_CALL_ARGS_SPLAT) {
ADD_INSN1(ret, line, putobject, INT2FIX(-1));
- ADD_SEND_R(ret, line, idAREF, INT2FIX(1), Qfalse, INT2FIX(asgnflag));
+ ADD_SEND_WITH_FLAG(ret, line, idAREF, INT2FIX(1), INT2FIX(asgnflag));
}
ADD_INSN1(ret, line, setn, FIXNUM_INC(argc, 3));
ADD_INSN (ret, line, pop);
@@ -5288,7 +5379,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
else if (flag & VM_CALL_ARGS_SPLAT) {
ADD_INSN(ret, line, dup);
ADD_INSN1(ret, line, putobject, INT2FIX(-1));
- ADD_SEND_R(ret, line, idAREF, INT2FIX(1), Qfalse, INT2FIX(asgnflag));
+ ADD_SEND_WITH_FLAG(ret, line, idAREF, INT2FIX(1), INT2FIX(asgnflag));
ADD_INSN1(ret, line, setn, FIXNUM_INC(argc, 2));
ADD_INSN (ret, line, pop);
}
@@ -5300,7 +5391,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
ADD_SEQ(ret, recv);
ADD_SEQ(ret, args);
}
- ADD_SEND_R(ret, line, node->nd_mid, argc, 0, INT2FIX(flag));
+ ADD_SEND_WITH_FLAG(ret, line, node->nd_mid, argc, INT2FIX(flag));
ADD_INSN(ret, line, pop);
break;
@@ -5731,7 +5822,7 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *anchor,
if (!NIL_P(vorig_argc)) orig_argc = FIX2INT(vorig_argc);
if (!NIL_P(vblock)) block = iseq_build_load_iseq(iseq, vblock);
}
- argv[j] = (VALUE)new_callinfo(iseq, mid, orig_argc, block, flag);
+ argv[j] = (VALUE)new_callinfo(iseq, mid, orig_argc, block, flag, NULL /* TODO: support keywords */);
}
break;
case TS_ID:
@@ -5805,7 +5896,7 @@ rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE locals, VALUE args,
int i = 0;
VALUE argc = CHECK_INTEGER(rb_ary_entry(args, i++));
VALUE arg_opt_labels = CHECK_ARRAY(rb_ary_entry(args, i++));
- VALUE arg_post_len = CHECK_INTEGER(rb_ary_entry(args, i++));
+ VALUE arg_post_num = CHECK_INTEGER(rb_ary_entry(args, i++));
VALUE arg_post_start = CHECK_INTEGER(rb_ary_entry(args, i++));
VALUE arg_rest = CHECK_INTEGER(rb_ary_entry(args, i++));
VALUE arg_block = CHECK_INTEGER(rb_ary_entry(args, i++));
@@ -5813,7 +5904,7 @@ rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE locals, VALUE args,
iseq->argc = FIX2INT(argc);
iseq->arg_rest = FIX2INT(arg_rest);
- iseq->arg_post_len = FIX2INT(arg_post_len);
+ iseq->arg_post_num = FIX2INT(arg_post_num);
iseq->arg_post_start = FIX2INT(arg_post_start);
iseq->arg_block = FIX2INT(arg_block);
iseq->arg_opts = RARRAY_LENINT(arg_opt_labels);
@@ -5822,8 +5913,8 @@ rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE locals, VALUE args,
if (iseq->arg_block != -1) {
iseq->arg_size = iseq->arg_block + 1;
}
- else if (iseq->arg_post_len) {
- iseq->arg_size = iseq->arg_post_start + iseq->arg_post_len;
+ else if (iseq->arg_post_num) {
+ iseq->arg_size = iseq->arg_post_start + iseq->arg_post_num;
}
else if (iseq->arg_rest != -1) {
iseq->arg_size = iseq->arg_rest + 1;
diff --git a/hash.c b/hash.c
index 4933205d67..d7bd4c8355 100644
--- a/hash.c
+++ b/hash.c
@@ -1854,7 +1854,7 @@ rb_hash_values(VALUE hash)
*
*/
-static VALUE
+VALUE
rb_hash_has_key(VALUE hash, VALUE key)
{
if (!RHASH(hash)->ntbl)
diff --git a/insns.def b/insns.def
index b4f67afa9b..9e1c16be4c 100644
--- a/insns.def
+++ b/insns.def
@@ -834,6 +834,30 @@ checkmatch
/**
@c setting
+ @e check keywords are specified or not.
+ @j キーワードが指定されているかどうかチェックする
+ */
+DEFINE_INSN
+checkkeyword
+(lindex_t kw_bits_index, rb_num_t keyword_index)
+()
+(VALUE ret)
+{
+ const VALUE *ep = GET_EP();
+ const VALUE kw_bits = *(ep - kw_bits_index);
+
+ if (FIXNUM_P(kw_bits)) {
+ int bits = FIX2INT(kw_bits);
+ ret = (bits & (0x01 << keyword_index)) ? Qfalse : Qtrue;
+ }
+ else {
+ assert(RB_TYPE_P(kw_bits, T_HASH));
+ ret = rb_hash_has_key(kw_bits, INT2FIX(keyword_index)) ? Qfalse : Qtrue;
+ }
+}
+
+/**
+ @c setting
@e trace
@j trace 用の命令。
*/
@@ -995,8 +1019,7 @@ send
(VALUE val) // inc += - (int)(ci->orig_argc + ((ci->flag & VM_CALL_ARGS_BLOCKARG) ? 1 : 0));
{
ci->argc = ci->orig_argc;
- ci->blockptr = 0;
- vm_caller_setup_args(th, reg_cfp, ci);
+ vm_caller_setup_arg_block(th, reg_cfp, ci, FALSE);
vm_search_method(ci, ci->recv = TOPN(ci->argc));
CALL_METHOD(ci);
}
@@ -1017,15 +1040,16 @@ opt_str_freeze
/**
@c optimize
- @e Invoke method without block, splat
- @j Invoke method without block, splat
+ @e Invoke method without block
+ @j Invoke method without block
*/
DEFINE_INSN
-opt_send_simple
+opt_send_without_block
(CALL_INFO ci)
(...)
(VALUE val) // inc += -ci->orig_argc;
{
+ ci->argc = ci->orig_argc;
vm_search_method(ci, ci->recv = TOPN(ci->argc));
CALL_METHOD(ci);
}
@@ -1042,11 +1066,7 @@ invokesuper
(VALUE val) // inc += - (int)(ci->orig_argc + ((ci->flag & VM_CALL_ARGS_BLOCKARG) ? 1 : 0));
{
ci->argc = ci->orig_argc;
- ci->blockptr = !(ci->flag & VM_CALL_ARGS_BLOCKARG) ? GET_BLOCK_PTR() : 0;
-
- if (UNLIKELY(!(ci->flag & VM_CALL_ARGS_SKIP_SETUP))) {
- vm_caller_setup_args(th, reg_cfp, ci);
- }
+ vm_caller_setup_arg_block(th, reg_cfp, ci, TRUE);
ci->recv = GET_SELF();
vm_search_super_method(th, GET_CFP(), ci);
CALL_METHOD(ci);
diff --git a/internal.h b/internal.h
index a076abf4e7..ca3649d0f6 100644
--- a/internal.h
+++ b/internal.h
@@ -668,6 +668,8 @@ void rb_gc_resurrect(VALUE ptr);
/* hash.c */
struct st_table *rb_hash_tbl_raw(VALUE hash);
+VALUE rb_hash_has_key(VALUE hash, VALUE key);
+
#define RHASH_TBL_RAW(h) rb_hash_tbl_raw(h)
VALUE rb_hash_keys(VALUE hash);
VALUE rb_hash_values(VALUE hash);
diff --git a/iseq.c b/iseq.c
index 2bfdf7c9ed..4af1426a54 100644
--- a/iseq.c
+++ b/iseq.c
@@ -67,6 +67,7 @@ iseq_free(void *ptr)
RUBY_FREE_ENTER("iseq");
if (ptr) {
+ int i;
iseq = ptr;
if (!iseq->orig) {
/* It's possible that strings are freed */
@@ -79,10 +80,14 @@ iseq_free(void *ptr)
RUBY_FREE_UNLESS_NULL(iseq->line_info_table);
RUBY_FREE_UNLESS_NULL(iseq->local_table);
RUBY_FREE_UNLESS_NULL(iseq->is_entries);
+ for (i=0; i<iseq->callinfo_size; i++) {
+ /* TODO: revisit callinfo data structure */
+ rb_call_info_kw_arg_t *kw_arg = iseq->callinfo_entries[i].kw_arg;
+ RUBY_FREE_UNLESS_NULL(kw_arg);
+ }
RUBY_FREE_UNLESS_NULL(iseq->callinfo_entries);
RUBY_FREE_UNLESS_NULL(iseq->catch_table);
RUBY_FREE_UNLESS_NULL(iseq->arg_opt_table);
- RUBY_FREE_UNLESS_NULL(iseq->arg_keyword_table);
compile_data_free(iseq->compile_data);
RUBY_FREE_UNLESS_NULL(iseq->iseq);
}
@@ -259,7 +264,8 @@ prepare_iseq_build(rb_iseq_t *iseq,
iseq->type = type;
iseq->arg_rest = -1;
iseq->arg_block = -1;
- iseq->arg_keyword = -1;
+ iseq->arg_keyword_bits = -1;
+ iseq->arg_keyword_rest = -1;
RB_OBJ_WRITE(iseq->self, &iseq->klass, 0);
set_relation(iseq, parent);
@@ -1239,6 +1245,9 @@ rb_insn_operand_intern(const rb_iseq_t *iseq,
rb_ary_push(ary, rb_sprintf("argc:%d", ci->orig_argc));
+ if (ci->kw_arg) {
+ rb_ary_push(ary, rb_sprintf("kw:%d", ci->kw_arg->keyword_len));
+ }
if (ci->blockiseq) {
if (child) {
rb_ary_push(child, ci->blockiseq->self);
@@ -1255,7 +1264,7 @@ rb_insn_operand_intern(const rb_iseq_t *iseq,
if (ci->flag & VM_CALL_TAILCALL) rb_ary_push(flags, rb_str_new2("TAILCALL"));
if (ci->flag & VM_CALL_SUPER) rb_ary_push(flags, rb_str_new2("SUPER"));
if (ci->flag & VM_CALL_OPT_SEND) rb_ary_push(flags, rb_str_new2("SNED")); /* maybe not reachable */
- if (ci->flag & VM_CALL_ARGS_SKIP_SETUP) rb_ary_push(flags, rb_str_new2("ARGS_SKIP")); /* maybe not reachable */
+ if (ci->flag & VM_CALL_ARGS_SIMPLE) rb_ary_push(flags, rb_str_new2("ARGS_SIMPLE")); /* maybe not reachable */
rb_ary_push(ary, rb_ary_join(flags, rb_str_new2("|")));
}
ret = rb_sprintf("<callinfo!%"PRIsVALUE">", rb_ary_join(ary, rb_str_new2(", ")));
@@ -1424,11 +1433,12 @@ rb_iseq_disasm(VALUE self)
if (tbl) {
rb_str_catf(str,
"local table (size: %d, argc: %d "
- "[opts: %d, rest: %d, post: %d, block: %d, keyword: %d@%d] s%d)\n",
+ "[opts: %d, rest: %d, post: %d, block: %d, kw: %d@%d, kwrest: %d] s%d)\n",
iseqdat->local_size, iseqdat->argc,
iseqdat->arg_opts, iseqdat->arg_rest,
- iseqdat->arg_post_len, iseqdat->arg_block,
- iseqdat->arg_keywords, iseqdat->local_size-iseqdat->arg_keyword,
+ iseqdat->arg_post_num, iseqdat->arg_block,
+ iseqdat->arg_keyword_num, iseqdat->local_size - iseqdat->arg_keyword_bits,
+ iseqdat->arg_keyword_rest,
iseqdat->arg_simple);
for (i = 0; i < iseqdat->local_table_size; i++) {
@@ -1451,7 +1461,7 @@ rb_iseq_disasm(VALUE self)
opti,
iseqdat->arg_rest == i ? "Rest" : "",
(iseqdat->arg_post_start <= i &&
- i < iseqdat->arg_post_start + iseqdat->arg_post_len) ? "Post" : "",
+ i < iseqdat->arg_post_start + iseqdat->arg_post_num) ? "Post" : "",
iseqdat->arg_block == i ? "Block" : "");
rb_str_catf(str, "[%2d] ", iseqdat->local_size - i);
@@ -1746,7 +1756,7 @@ iseq_data_to_ary(rb_iseq_t *iseq)
else {
rb_ary_push(args, INT2FIX(iseq->argc));
rb_ary_push(args, arg_opt_labels);
- rb_ary_push(args, INT2FIX(iseq->arg_post_len));
+ rb_ary_push(args, INT2FIX(iseq->arg_post_num));
rb_ary_push(args, INT2FIX(iseq->arg_post_start));
rb_ary_push(args, INT2FIX(iseq->arg_rest));
rb_ary_push(args, INT2FIX(iseq->arg_block));
@@ -1985,7 +1995,7 @@ rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc)
CONST_ID(rest, "rest");
rb_ary_push(args, PARAM(iseq->arg_rest, rest));
}
- r = iseq->arg_post_start + iseq->arg_post_len;
+ r = iseq->arg_post_start + iseq->arg_post_num;
if (is_proc) {
for (i = iseq->arg_post_start; i < r; i++) {
PARAM_TYPE(opt);
@@ -1998,7 +2008,7 @@ rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc)
rb_ary_push(args, PARAM(i, req));
}
}
- if (iseq->arg_keyword != -1) {
+ if (iseq->arg_keyword_bits != -1) {
i = 0;
if (iseq->arg_keyword_required) {
ID keyreq;
@@ -2012,17 +2022,17 @@ rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc)
}
}
CONST_ID(key, "key");
- for (; i < iseq->arg_keywords; i++) {
+ for (; i < iseq->arg_keyword_num; i++) {
PARAM_TYPE(key);
if (rb_id2str(iseq->arg_keyword_table[i])) {
rb_ary_push(a, ID2SYM(iseq->arg_keyword_table[i]));
}
rb_ary_push(args, a);
}
- if (!iseq->arg_keyword_check) {
- CONST_ID(keyrest, "keyrest");
- rb_ary_push(args, PARAM(iseq->arg_keyword, keyrest));
- }
+ }
+ if (iseq->arg_keyword_rest >= 0) {
+ CONST_ID(keyrest, "keyrest");
+ rb_ary_push(args, PARAM(iseq->arg_keyword_rest, keyrest));
}
if (iseq->arg_block != -1) {
CONST_ID(block, "block");
diff --git a/parse.y b/parse.y
index 18c08db394..861ff4945a 100644
--- a/parse.y
+++ b/parse.y
@@ -174,6 +174,15 @@ vtable_add(struct vtable *tbl, ID id)
tbl->tbl[tbl->pos++] = id;
}
+#ifndef RIPPER
+static void
+vtable_pop(struct vtable *tbl, int n)
+{
+ if (tbl->pos < n) rb_bug("vtable_pop: unreachable");
+ tbl->pos -= n;
+}
+#endif
+
static int
vtable_included(const struct vtable * tbl, ID id)
{
@@ -9614,25 +9623,62 @@ new_args_tail_gen(struct parser_params *parser, NODE *k, ID kr, ID b)
{
int saved_line = ruby_sourceline;
struct rb_args_info *args;
- NODE *kw_rest_arg = 0;
NODE *node;
- int check = 0;
args = ZALLOC(struct rb_args_info);
node = NEW_NODE(NODE_ARGS, 0, 0, args);
args->block_arg = b;
args->kw_args = k;
- if (k && !kr) {
- check = 1;
- kr = internal_id();
+
+ if (k) {
+ /*
+ * def foo(k1: 1, kr1:, k2: 2, **krest, &b)
+ * variable order: k1, kr1, k2, &b, internal_id, krest
+ * #=> <reorder>
+ * variable order: kr1, k1, k2, internal_id, krest, &b
+ */
+ ID kw_bits;
+ NODE *kwn = k;
+ struct vtable *required_kw_vars = vtable_alloc(NULL);
+ struct vtable *kw_vars = vtable_alloc(NULL);
+ int i;
+
+ while (kwn) {
+ NODE *val_node = kwn->nd_body->nd_value;
+ ID vid = kwn->nd_body->nd_vid;
+
+ if (val_node == (NODE *)-1) {
+ vtable_add(required_kw_vars, vid);
+ }
+ else {
+ vtable_add(kw_vars, vid);
+ }
+
+ kwn = kwn->nd_next;
+ }
+
+ vtable_pop(lvtbl->args, vtable_size(required_kw_vars) + vtable_size(kw_vars) + (b != 0));
+
+ for (i=0; i<vtable_size(required_kw_vars); i++) arg_var(required_kw_vars->tbl[i]);
+ for (i=0; i<vtable_size(kw_vars); i++) arg_var(kw_vars->tbl[i]);
+ vtable_free(required_kw_vars);
+ vtable_free(kw_vars);
+
+ kw_bits = internal_id();
+ arg_var(kw_bits);
+ if (kr) arg_var(kr);
+ if (b) arg_var(b);
+
+ args->kw_rest_arg = NEW_DVAR(kw_bits);
+ args->kw_rest_arg->nd_cflag = kr;
}
- if (kr) {
+ else if (kr) {
+ if (b) vtable_pop(lvtbl->args, 1); /* reorder */
arg_var(kr);
- kw_rest_arg = NEW_DVAR(kr);
- kw_rest_arg->nd_cflag = check;
+ if (b) arg_var(b);
+ args->kw_rest_arg = NEW_DVAR(kr);
}
- args->kw_rest_arg = kw_rest_arg;
ruby_sourceline = saved_line;
return node;
diff --git a/proc.c b/proc.c
index a4b8c82a25..d0856ec40d 100644
--- a/proc.c
+++ b/proc.c
@@ -848,10 +848,10 @@ static inline int
rb_iseq_min_max_arity(const rb_iseq_t *iseq, int *max)
{
*max = iseq->arg_rest == -1 ?
- iseq->argc + iseq->arg_post_len + iseq->arg_opts -
- (iseq->arg_opts > 0) + (iseq->arg_keyword != -1)
+ iseq->argc + iseq->arg_post_num + iseq->arg_opts -
+ (iseq->arg_opts > 0) + (iseq->arg_keyword_num > 0) + (iseq->arg_keyword_rest >= 0)
: UNLIMITED_ARGUMENTS;
- return iseq->argc + iseq->arg_post_len + (iseq->arg_keyword_required > 0);
+ return iseq->argc + iseq->arg_post_num + (iseq->arg_keyword_required > 0);
}
static int
diff --git a/vm.c b/vm.c
index 34b2103981..f478d26291 100644
--- a/vm.c
+++ b/vm.c
@@ -787,7 +787,7 @@ invoke_block_from_c(rb_thread_t *th, const rb_block_t *block,
}
opt_pc = vm_yield_setup_args(th, iseq, argc, cfp->sp, blockptr,
- (type == VM_FRAME_MAGIC_LAMBDA) ? splattable+1 : 0);
+ (type == VM_FRAME_MAGIC_LAMBDA ? (splattable ? arg_setup_lambda : arg_setup_method) : arg_setup_block));
if (me != 0) {
/* bmethod */
diff --git a/vm_args.c b/vm_args.c
new file mode 100644
index 0000000000..fd77bfa4d0
--- /dev/null
+++ b/vm_args.c
@@ -0,0 +1,783 @@
+/**********************************************************************
+
+ vm_args.c - process method call arguments.
+
+ $Author$
+
+ Copyright (C) 2014- Yukihiro Matsumoto
+
+**********************************************************************/
+
+struct args_info {
+ /* basic args info */
+ rb_call_info_t *ci;
+ int argc;
+ VALUE *argv;
+
+ /* additional args info */
+ VALUE *kw_argv;
+ int rest_index;
+ VALUE rest;
+};
+
+enum arg_setup_type {
+ arg_setup_method,
+ arg_setup_block,
+ arg_setup_lambda
+};
+
+static inline int
+args_argc(struct args_info *args)
+{
+ if (args->rest == Qfalse) {
+ return args->argc;
+ }
+ else {
+ return args->argc + RARRAY_LENINT(args->rest) - args->rest_index;
+ }
+}
+
+static inline void
+args_extend(struct args_info *args, const int min_argc)
+{
+ int i;
+
+ if (args->rest) {
+ args->rest = rb_ary_dup(args->rest);
+ assert(args->rest_index == 0);
+ for (i=args->argc + RARRAY_LENINT(args->rest); i<min_argc; i++) {
+ rb_ary_push(args->rest, Qnil);
+ }
+ }
+ else {
+ for (i=args->argc; i<min_argc; i++) {
+ args->argv[args->argc++] = Qnil;
+ }
+ }
+}
+
+static inline void
+args_reduce(struct args_info *args, int over_argc)
+{
+ if (args->rest) {
+ const long len = RARRAY_LEN(args->rest);
+
+ if (len > over_argc) {
+ args->rest = rb_ary_dup(args->rest);
+ rb_ary_resize(args->rest, len - over_argc);
+ return;
+ }
+ else {
+ args->rest = Qfalse;
+ over_argc -= len;
+ }
+ }
+
+ assert(args->argc >= over_argc);
+ args->argc -= over_argc;
+}
+
+static inline int
+args_check_block_arg0(struct args_info *args, rb_thread_t *th, const int msl)
+{
+ VALUE ary = Qnil;
+
+ if (args->rest && RARRAY_LEN(args->rest) == 1) {
+ VALUE arg0 = RARRAY_AREF(args->rest, 0);
+ ary = rb_check_array_type(arg0);
+ th->mark_stack_len = msl;
+ }
+ else if (args->argc == 1) {
+ VALUE arg0 = args->argv[0];
+ ary = rb_check_array_type(arg0);
+ th->mark_stack_len = msl;
+ args->argv[0] = arg0; /* see: https://bugs.ruby-lang.org/issues/8484 */
+ }
+
+ if (!NIL_P(ary)) {
+ args->rest = ary;
+ args->rest_index = 0;
+ args->argc = 0;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static inline void
+args_copy(struct args_info *args)
+{
+ if (args->rest != Qfalse) {
+ int argc = args->argc;
+ args->argc = 0;
+ args->rest = rb_ary_dup(args->rest); /* make dup */
+
+ /*
+ * argv: [m0, m1, m2, m3]
+ * rest: [a0, a1, a2, a3, a4, a5]
+ * ^
+ * rest_index
+ *
+ * #=> first loop
+ *
+ * argv: [m0, m1]
+ * rest: [m2, m3, a2, a3, a4, a5]
+ * ^
+ * rest_index
+ *
+ * #=> 2nd loop
+ *
+ * argv: [] (argc == 0)
+ * rest: [m0, m1, m2, m3, a2, a3, a4, a5]
+ * ^
+ * rest_index
+ */
+ while (args->rest_index > 0 && argc > 0) {
+ RARRAY_ASET(args->rest, --args->rest_index, args->argv[--argc]);
+ }
+ while (argc > 0) {
+ rb_ary_unshift(args->rest, args->argv[--argc]);
+ }
+ }
+ else if (args->argc > 0) {
+ args->rest = rb_ary_new_from_values(args->argc, args->argv);
+ args->rest_index = 0;
+ args->argc = 0;
+ }
+}
+
+static inline const VALUE *
+args_rest_argv(struct args_info *args)
+{
+ return RARRAY_CONST_PTR(args->rest) + args->rest_index;
+}
+
+static inline VALUE
+args_rest_array(struct args_info *args)
+{
+ VALUE ary;
+
+ if (args->rest) {
+ ary = rb_ary_subseq(args->rest, args->rest_index, RARRAY_LEN(args->rest) - args->rest_index);
+ args->rest = 0;
+ }
+ else {
+ ary = rb_ary_new();
+ }
+ return ary;
+}
+
+static int
+keyword_hash_p(VALUE *kw_hash_ptr, VALUE *rest_hash_ptr, rb_thread_t *th, const int msl)
+{
+ *rest_hash_ptr = rb_check_hash_type(*kw_hash_ptr);
+ th->mark_stack_len = msl;
+
+ if (!NIL_P(*rest_hash_ptr)) {
+ *kw_hash_ptr = rb_extract_keywords(rest_hash_ptr);
+ return TRUE;
+ }
+ else {
+ *kw_hash_ptr = Qnil;
+ return FALSE;
+ }
+}
+
+static VALUE
+args_pop_keyword_hash(struct args_info *args, VALUE *kw_hash_ptr, rb_thread_t *th, const int msl)
+{
+ VALUE rest_hash;
+
+ if (args->rest == Qfalse) {
+ from_argv:
+ assert(args->argc > 0);
+ *kw_hash_ptr = args->argv[args->argc-1];
+
+ if (keyword_hash_p(kw_hash_ptr, &rest_hash, th, msl)) {
+ if (rest_hash) {
+ args->argv[args->argc-1] = rest_hash;
+ }
+ else {
+ args->argc--;
+ return TRUE;
+ }
+ }
+ }
+ else {
+ long len = RARRAY_LEN(args->rest);
+
+ if (len > 0) {
+ *kw_hash_ptr = RARRAY_AREF(args->rest, len - 1);
+
+ if (keyword_hash_p(kw_hash_ptr, &rest_hash, th, msl)) {
+ if (rest_hash) {
+ RARRAY_ASET(args->rest, len - 1, rest_hash);
+ }
+ else {
+ args->rest = rb_ary_dup(args->rest);
+ rb_ary_pop(args->rest);
+ return TRUE;
+ }
+ }
+ }
+ else {
+ goto from_argv;
+ }
+ }
+
+ return FALSE;
+}
+
+static int
+args_kw_argv_to_hash(struct args_info *args)
+{
+ const ID * const passed_keywords = args->ci->kw_arg->keywords;
+ const int kw_len = args->ci->kw_arg->keyword_len;
+ VALUE h = rb_hash_new();
+ const int kw_start = args->argc - kw_len;
+ const VALUE * const kw_argv = args->argv + kw_start;
+ int i;
+
+ args->argc = kw_start + 1;
+ for (i=0; i<kw_len; i++) {
+ rb_hash_aset(h, ID2SYM(passed_keywords[i]), kw_argv[i]);
+ }
+
+ args->argv[args->argc - 1] = h;
+
+ return args->argc;
+}
+
+static void
+args_stored_kw_argv_to_hash(struct args_info *args)
+{
+ VALUE h = rb_hash_new();
+ int i;
+ const ID * const passed_keywords = args->ci->kw_arg->keywords;
+ const int passed_keyword_len = args->ci->kw_arg->keyword_len;
+
+ for (i=0; i<passed_keyword_len; i++) {
+ rb_hash_aset(h, ID2SYM(passed_keywords[i]), args->kw_argv[i]);
+ }
+ args->kw_argv = NULL;
+
+ if (args->rest) {
+ args->rest = rb_ary_dup(args->rest);
+ rb_ary_push(args->rest, h);
+ }
+ else {
+ args->argv[args->argc++] = h;
+ }
+}
+
+static inline void
+args_setup_lead_parameters(struct args_info *args, int argc, VALUE *locals)
+{
+ if (args->argc >= argc) {
+ /* do noting */
+ args->argc -= argc;
+ args->argv += argc;
+ }
+ else {
+ int i, j;
+ const VALUE *argv = args_rest_argv(args);
+
+ for (i=args->argc, j=0; i<argc; i++, j++) {
+ locals[i] = argv[j];
+ }
+ args->rest_index += argc - args->argc;
+ args->argc = 0;
+ }
+}
+
+static inline void
+args_setup_post_parameters(struct args_info *args, int argc, VALUE *locals)
+{
+ long len;
+ args_copy(args);
+ len = RARRAY_LEN(args->rest);
+ MEMCPY(locals, RARRAY_CONST_PTR(args->rest) + len - argc, VALUE, argc);
+ rb_ary_resize(args->rest, len - argc);
+}
+
+static inline int
+args_setup_opt_parameters(struct args_info *args, int opt_max, VALUE *locals)
+{
+ int i;
+
+ if (args->argc >= opt_max) {
+ args->argc -= opt_max;
+ args->argv += opt_max;
+ i = opt_max;
+ }
+ else {
+ int j;
+ i = args->argc;
+ args->argc = 0;
+
+ if (args->rest) {
+ int len = RARRAY_LENINT(args->rest);
+ const VALUE *argv = RARRAY_CONST_PTR(args->rest);
+
+ for (; i<opt_max && args->rest_index < len; i++, args->rest_index++) {
+ locals[i] = argv[args->rest_index];
+ }
+ }
+
+ /* initialize by nil */
+ for (j=i; j<opt_max; j++) {
+ locals[j] = Qnil;
+ }
+ }
+
+ return i;
+}
+
+static inline void
+args_setup_rest_parameter(struct args_info *args, VALUE *locals)
+{
+ args_copy(args);
+ *locals = args_rest_array(args);
+}
+
+static VALUE
+make_unused_kw_hash(const ID *passed_keywords, int passed_keyword_len, const VALUE *kw_argv, const int key_only)
+{
+ int i;
+ VALUE obj = key_only ? rb_ary_tmp_new(1) : rb_hash_new();
+
+ for (i=0; i<passed_keyword_len; i++) {
+ if (kw_argv[i] != Qundef) {
+ if (key_only) {
+ rb_ary_push(obj, ID2SYM(passed_keywords[i]));
+ }
+ else {
+ rb_hash_aset(obj, ID2SYM(passed_keywords[i]), kw_argv[i]);
+ }
+ }
+ }
+ return obj;
+}
+
+void rb_keyword_error(const char *error, VALUE keys);
+
+static inline int
+args_setup_kw_parameters_lookup(const ID key, VALUE *ptr, const ID * const passed_keywords, VALUE *passed_values, const int passed_keyword_len)
+{
+ int i;
+
+ for (i=0; i<passed_keyword_len; i++) {
+ if (key == passed_keywords[i]) {
+ *ptr = passed_values[i];
+ passed_values[i] = Qundef;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+args_setup_kw_parameters(VALUE* const passed_values, const int passed_keyword_len, const ID * const passed_keywords,
+ const rb_iseq_t * const iseq, VALUE * const locals)
+{
+ const ID *acceptable_keywords = iseq->arg_keyword_table;
+ const int req_key_num = iseq->arg_keyword_required;
+ const int key_num = iseq->arg_keyword_num;
+ const VALUE * const default_values = iseq->arg_keyword_default_values;
+ VALUE missing = 0;
+ int i, di, found = 0;
+ int unspecified_bits = 0;
+ VALUE unspecified_bits_value = Qnil;
+
+ for (i=0; i<req_key_num; i++) {
+ ID key = acceptable_keywords[i];
+ if (args_setup_kw_parameters_lookup(key, &locals[i], passed_keywords, passed_values, passed_keyword_len)) {
+ found++;
+ }
+ else {
+ if (!missing) missing = rb_ary_tmp_new(1);
+ rb_ary_push(missing, ID2SYM(key));
+ }
+ }
+
+ if (missing) rb_keyword_error("missing", missing);
+
+ for (di=0; i<key_num; i++, di++) {
+ if (args_setup_kw_parameters_lookup(acceptable_keywords[i], &locals[i], passed_keywords, passed_values, passed_keyword_len)) {
+ found++;
+ }
+ else {
+ if (default_values[di] == Qundef) {
+ locals[i] = Qnil;
+
+ if (LIKELY(i < 32)) { /* TODO: 32 -> Fixnum's max bits */
+ unspecified_bits |= 0x01 << di;
+ }
+ else {
+ if (NIL_P(unspecified_bits_value)) {
+ /* fixnum -> hash */
+ int j;
+ unspecified_bits_value = rb_hash_new();
+
+ for (j=0; j<32; j++) {
+ if (unspecified_bits & (0x01 << j)) {
+ rb_hash_aset(unspecified_bits_value, INT2FIX(j), Qtrue);
+ }
+ }
+ }
+ rb_hash_aset(unspecified_bits_value, INT2FIX(di), Qtrue);
+ }
+ }
+ else {
+ locals[i] = default_values[di];
+ }
+ }
+ }
+
+ if (iseq->arg_keyword_rest >= 0) {
+ const int rest_hash_index = key_num + 1;
+ locals[rest_hash_index] = make_unused_kw_hash(passed_keywords, passed_keyword_len, passed_values, FALSE);
+ }
+ else {
+ if (found != passed_keyword_len) {
+ VALUE keys = make_unused_kw_hash(passed_keywords, passed_keyword_len, passed_values, TRUE);
+ rb_keyword_error("unknown", keys);
+ }
+ }
+
+ if (NIL_P(unspecified_bits_value)) {
+ unspecified_bits_value = INT2FIX(unspecified_bits);
+ }
+ locals[key_num] = unspecified_bits_value;
+}
+
+static inline void
+args_setup_kw_rest_parameter(VALUE keyword_hash, VALUE *locals)
+{
+ locals[0] = NIL_P(keyword_hash) ? rb_hash_new() : rb_hash_dup(keyword_hash);
+}
+
+static inline void
+args_setup_block_parameter(rb_thread_t *th, rb_call_info_t *ci, VALUE *locals)
+{
+ VALUE blockval = Qnil;
+ const rb_block_t *blockptr = ci->blockptr;
+
+ if (blockptr) {
+ /* make Proc object */
+ if (blockptr->proc == 0) {
+ rb_proc_t *proc;
+ blockval = rb_vm_make_proc(th, blockptr, rb_cProc);
+ GetProcPtr(blockval, proc);
+ ci->blockptr = &proc->block;
+ }
+ else {
+ blockval = blockptr->proc;
+ }
+ }
+ *locals = blockval;
+}
+
+struct fill_values_arg {
+ VALUE *keys;
+ VALUE *vals;
+ int argc;
+};
+
+static int
+fill_keys_values(st_data_t key, st_data_t val, st_data_t ptr)
+{
+ struct fill_values_arg *arg = (struct fill_values_arg *)ptr;
+ int i = arg->argc++;
+ arg->keys[i] = SYM2ID((VALUE)key);
+ arg->vals[i] = (VALUE)val;
+ return ST_CONTINUE;
+}
+
+NORETURN(static void argument_error(const rb_iseq_t *iseq, int miss_argc, int min_argc, int max_argc));
+static void
+argument_error(const rb_iseq_t *iseq, int miss_argc, int min_argc, int max_argc)
+{
+ rb_thread_t *th = GET_THREAD();
+ VALUE exc = rb_arg_error_new(miss_argc, min_argc, max_argc);
+ VALUE at;
+
+ if (iseq) {
+ vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD, Qnil /* self */, Qnil /* klass */, Qnil /* specval*/,
+ iseq->iseq_encoded, th->cfp->sp, 0 /* local_size */, 0 /* me */, 0 /* stack_max */);
+ at = rb_vm_backtrace_object();
+ vm_pop_frame(th);
+ }
+ else {
+ at = rb_vm_backtrace_object();
+ }
+
+ rb_iv_set(exc, "bt_locations", at);
+ rb_funcall(exc, rb_intern("set_backtrace"), 1, at);
+ rb_exc_raise(exc);
+}
+
+static int
+setup_parameters_complex(rb_thread_t * const th, const rb_iseq_t * const iseq, rb_call_info_t * const ci,
+ VALUE * const locals, const enum arg_setup_type arg_setup_type)
+{
+ const int min_argc = iseq->argc + iseq->arg_post_num;
+ const int max_argc = (iseq->arg_rest == -1) ? min_argc + (iseq->arg_opts - (iseq->arg_opts > 0)) : UNLIMITED_ARGUMENTS;
+ int opt_pc = 0;
+ int given_argc;
+ struct args_info args_body, *args;
+ VALUE keyword_hash = Qnil;
+ const int msl = ci->argc + iseq->arg_size;
+
+ th->mark_stack_len = msl;
+
+ /* setup args */
+ args = &args_body;
+ args->ci = ci;
+ given_argc = args->argc = ci->argc;
+ args->argv = locals;
+
+ if (ci->kw_arg) {
+ if (iseq->arg_keyword_bits >= 0) {
+ int kw_len = ci->kw_arg->keyword_len;
+ /* copy kw_argv */
+ args->kw_argv = ALLOCA_N(VALUE, kw_len);
+ args->argc -= kw_len;
+ given_argc -= kw_len;
+ MEMCPY(args->kw_argv, locals + args->argc, VALUE, kw_len);
+ }
+ else {
+ args->kw_argv = NULL;
+ given_argc = args_kw_argv_to_hash(args);
+ }
+ }
+ else {
+ args->kw_argv = NULL;
+ }
+
+ if (ci->flag & VM_CALL_ARGS_SPLAT) {
+ args->rest = locals[--args->argc];
+ args->rest_index = 0;
+ given_argc += RARRAY_LENINT(args->rest) - 1;
+ }
+ else {
+ args->rest = Qfalse;
+ }
+
+ switch (arg_setup_type) {
+ case arg_setup_method:
+ break; /* do nothing special */
+ case arg_setup_block:
+ if (given_argc == 1 &&
+ (min_argc > 0 ||
+ iseq->arg_opts > 2 || iseq->arg_keyword_bits >= 0 || iseq->arg_keyword_rest >= 0) && /* TODO: can be shrink with flags */
+ !(iseq->arg_simple & 0x02) &&
+ args_check_block_arg0(args, th, msl)) {
+ given_argc = RARRAY_LEN(args->rest);
+ }
+ break;
+ case arg_setup_lambda:
+ if (given_argc == 1 &&
+ given_argc != iseq->argc &&
+ !(iseq->arg_rest >= 0) &&
+ args_check_block_arg0(args, th, msl)) {
+ given_argc = RARRAY_LEN(args->rest);
+ }
+ }
+
+ /* argc check */
+ if (given_argc < min_argc) {
+ if (given_argc == min_argc - 1 && args->kw_argv) {
+ args_stored_kw_argv_to_hash(args);
+ given_argc = args_argc(args);
+ }
+ else {
+ if (arg_setup_type == arg_setup_block) {
+ CHECK_VM_STACK_OVERFLOW(th->cfp, min_argc);
+ given_argc = min_argc;
+ args_extend(args, min_argc);
+ }
+ else {
+ argument_error(iseq, given_argc, min_argc, max_argc);
+ }
+ }
+ }
+
+ if (given_argc > min_argc &&
+ (iseq->arg_keyword_bits >= 0 || iseq->arg_keyword_rest >= 0) &&
+ args->kw_argv == NULL) {
+ if (args_pop_keyword_hash(args, &keyword_hash, th, msl)) {
+ given_argc--;
+ }
+ }
+
+ if (given_argc > max_argc && max_argc != UNLIMITED_ARGUMENTS) {
+ if (arg_setup_type == arg_setup_block) {
+ /* truncate */
+ args_reduce(args, given_argc - max_argc);
+ given_argc = max_argc;
+ }
+ else {
+ argument_error(iseq, given_argc, min_argc, max_argc);
+ }
+ }
+
+ if (iseq->argc > 0) {
+ args_setup_lead_parameters(args, iseq->argc, locals + 0);
+ }
+
+ if (iseq->arg_post_num > 0) {
+ args_setup_post_parameters(args, iseq->arg_post_num, locals + iseq->arg_post_start);
+ }
+
+ if (iseq->arg_opts > 0) {
+ int opt = args_setup_opt_parameters(args, iseq->arg_opts - 1, locals + iseq->argc);
+ opt_pc = (int)iseq->arg_opt_table[opt];
+ }
+
+ if (iseq->arg_rest >= 0) {
+ args_setup_rest_parameter(args, locals + iseq->arg_rest);
+ }
+
+ if (iseq->arg_keyword_bits >= 0) {
+ VALUE * const klocals = locals + iseq->arg_keyword_bits - iseq->arg_keyword_num;
+
+ if (args->kw_argv != NULL) {
+ args_setup_kw_parameters(args->kw_argv, args->ci->kw_arg->keyword_len, args->ci->kw_arg->keywords, iseq, klocals);
+ }
+ else if (!NIL_P(keyword_hash)) {
+ int kw_len = rb_long2int(RHASH_SIZE(keyword_hash));
+ struct fill_values_arg arg;
+ /* copy kw_argv */
+ arg.keys = args->kw_argv = ALLOCA_N(VALUE, kw_len * 2);
+ arg.vals = arg.keys + kw_len;
+ arg.argc = 0;
+ rb_hash_foreach(keyword_hash, fill_keys_values, (VALUE)&arg);
+ assert(arg.argc == kw_len);
+ args_setup_kw_parameters(arg.vals, kw_len, arg.keys, iseq, klocals);
+ }
+ else {
+ assert(args_argc(args) == 0);
+ args_setup_kw_parameters(NULL, 0, NULL, iseq, klocals);
+ }
+ }
+ else if (iseq->arg_keyword_rest >= 0) {
+ args_setup_kw_rest_parameter(keyword_hash, locals + iseq->arg_keyword_rest);
+ }
+
+ if (iseq->arg_block >= 0) {
+ args_setup_block_parameter(th, ci, locals + iseq->arg_block);
+ }
+
+#if 0
+ {
+ int i;
+ for (i=0; i<iseq->arg_size; i++) {
+ fprintf(stderr, "local[%d] = %p\n", i, (void *)locals[i]);
+ }
+ }
+#endif
+
+ th->mark_stack_len = 0;
+
+ return opt_pc;
+}
+
+static inline void
+vm_caller_setup_arg_splat(rb_control_frame_t *cfp, rb_call_info_t *ci)
+{
+ VALUE *argv = cfp->sp - ci->argc;
+ VALUE ary = argv[ci->argc-1];
+
+ cfp->sp--;
+
+ if (!NIL_P(ary)) {
+ const VALUE *ptr = RARRAY_CONST_PTR(ary);
+ long len = RARRAY_LEN(ary), i;
+
+ CHECK_VM_STACK_OVERFLOW(cfp, len);
+
+ for (i = 0; i < len; i++) {
+ *cfp->sp++ = ptr[i];
+ }
+ ci->argc += i - 1;
+ }
+}
+
+static inline void
+vm_caller_setup_arg_kw(rb_control_frame_t *cfp, rb_call_info_t *ci)
+{
+ const ID * const passed_keywords = ci->kw_arg->keywords;
+ const int kw_len = ci->kw_arg->keyword_len;
+ const VALUE h = rb_hash_new();
+ VALUE *sp = cfp->sp;
+ int i;
+
+ for (i=0; i<kw_len; i++) {
+ rb_hash_aset(h, ID2SYM(passed_keywords[i]), (sp - kw_len)[i]);
+ }
+ (sp-kw_len)[0] = h;
+
+ cfp->sp -= kw_len - 1;
+ ci->argc -= kw_len - 1;
+}
+
+#define SAVE_RESTORE_CI(expr, ci) do { \
+ int saved_argc = (ci)->argc; rb_block_t *saved_blockptr = (ci)->blockptr; /* save */ \
+ expr; \
+ (ci)->argc = saved_argc; (ci)->blockptr = saved_blockptr; /* restore */ \
+} while (0)
+
+static void
+vm_caller_setup_arg_block(const rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci, const int is_super)
+{
+ if (ci->flag & VM_CALL_ARGS_BLOCKARG) {
+ rb_proc_t *po;
+ VALUE proc;
+
+ proc = *(--reg_cfp->sp);
+
+ if (proc != Qnil) {
+ if (!rb_obj_is_proc(proc)) {
+ VALUE b;
+
+ SAVE_RESTORE_CI(b = rb_check_convert_type(proc, T_DATA, "Proc", "to_proc"), ci);
+
+ 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;
+ }
+ GetProcPtr(proc, po);
+ ci->blockptr = &po->block;
+ RUBY_VM_GET_BLOCK_PTR_IN_CFP(reg_cfp)->proc = proc;
+ }
+ else {
+ ci->blockptr = NULL;
+ }
+ }
+ else if (ci->blockiseq != 0) { /* likely */
+ ci->blockptr = RUBY_VM_GET_BLOCK_PTR_IN_CFP(reg_cfp);
+ ci->blockptr->iseq = ci->blockiseq;
+ ci->blockptr->proc = 0;
+ }
+ else {
+ if (is_super) {
+ ci->blockptr = GET_BLOCK_PTR();
+ }
+ else {
+ ci->blockptr = NULL;
+ }
+ }
+}
+
+#define IS_ARGS_SPLAT(ci) ((ci)->flag & VM_CALL_ARGS_SPLAT)
+#define IS_ARGS_KEYWORD(ci) ((ci)->kw_arg != NULL)
+
+#define CALLER_SETUP_ARG(cfp, ci) do { \
+ if (UNLIKELY(IS_ARGS_SPLAT(ci))) vm_caller_setup_arg_splat((cfp), (ci)); \
+ if (UNLIKELY(IS_ARGS_KEYWORD(ci))) vm_caller_setup_arg_kw((cfp), (ci)); \
+} while (0)
diff --git a/vm_core.h b/vm_core.h
index 6dd66ff6b8..f8494d07e6 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -135,13 +135,20 @@ union iseq_inline_storage_entry {
struct rb_thread_struct;
struct rb_control_frame_struct;
+typedef struct rb_call_info_kw_arg_struct {
+ int keyword_len;
+ ID keywords[1];
+} rb_call_info_kw_arg_t;
+
/* rb_call_info_t contains calling information including inline cache */
typedef struct rb_call_info_struct {
/* fixed at compile time */
ID mid;
+
unsigned int flag;
int orig_argc;
rb_iseq_t *blockiseq;
+ rb_call_info_kw_arg_t *kw_arg;
/* inline cache: keys */
rb_serial_t method_state;
@@ -240,23 +247,23 @@ struct rb_iseq_struct {
* *c, # rest
* d1, d2, ..., dO, # post
* e1:(...), e2:(...), ..., eK:(...), # keyword
- * **f, # keyword rest
+ * **f, # keyword_rest
* &g) # block
* =>
*
- * argc = M // or 0 if no mandatory arg
- * arg_opts = N+1 // or 0 if no optional arg
- * arg_rest = M+N // or -1 if no rest arg
- * arg_opt_table = [ (arg_opts entries) ]
- * arg_post_start = M+N+(*1) // or 0 if no post arguments
- * arg_post_len = O // or 0 if no post arguments
- * arg_keywords = K // or 0 if no keyword arg
- * arg_block = M+N+(*1)+O+K // or -1 if no block arg
- * arg_keyword = M+N+(*1)+O+K+(&1) // or -1 if no keyword arg/rest
- * arg_simple = 0 if not simple arguments.
- * = 1 if no opt, rest, post, block.
- * = 2 if ambiguous block parameter ({|a|}).
- * arg_size = M+N+O+(*1)+K+(&1)+(**1) argument size.
+ * argc = M // or 0 if no mandatory arg
+ * arg_opts = N+1 // or 0 if no optional arg
+ * arg_rest = M+N // or -1 if no rest arg
+ * arg_opt_table = [ (arg_opts entries) ]
+ * arg_post_start = M+N+(*1) // or 0 if no post arguments
+ * arg_post_num = O // or 0 if no post arguments
+ * arg_keyword_num = K // or 0 if no keyword arg
+ * arg_block = M+N+(*1)+O+K // or -1 if no block arg
+ * arg_keyword_bits = M+N+(*1)+O+K+(&1) // or -1 if no keyword arg/rest
+ * arg_simple = 0 if not simple arguments.
+ * = 1 if no opt, rest, post, block.
+ * = 2 if ambiguous block parameter ({|a|}).
+ * arg_size = M+N+O+(*1)+K+(&1)+(**1) argument size.
*/
int argc;
@@ -264,15 +271,16 @@ struct rb_iseq_struct {
int arg_rest;
int arg_block;
int arg_opts;
- int arg_post_len;
+ int arg_post_num;
int arg_post_start;
int arg_size;
VALUE *arg_opt_table;
- int arg_keyword;
- int arg_keyword_check; /* if this is true, raise an ArgumentError when unknown keyword argument is passed */
- int arg_keywords;
+ int arg_keyword_num;
+ int arg_keyword_bits;
+ int arg_keyword_rest;
int arg_keyword_required;
ID *arg_keyword_table;
+ VALUE *arg_keyword_default_values;
/* catch table */
struct iseq_catch_table *catch_table;
@@ -793,7 +801,7 @@ enum vm_check_match_type {
#define VM_CALL_TAILCALL (0x01 << 5) /* located at tail position */
#define VM_CALL_SUPER (0x01 << 6) /* super */
#define VM_CALL_OPT_SEND (0x01 << 7) /* internal flag */
-#define VM_CALL_ARGS_SKIP_SETUP (0x01 << 8) /* (flag & (SPLAT|BLOCKARG)) && blockiseq == 0 */
+#define VM_CALL_ARGS_SIMPLE (0x01 << 8) /* (ci->flag & (SPLAT|BLOCKARG)) && ci->blockiseq == NULL && ci->kw_arg == NULL */
enum vm_special_object_type {
VM_SPECIAL_OBJECT_VMCORE = 1,
diff --git a/vm_eval.c b/vm_eval.c
index 22b3e1eabd..14e5414f6d 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -50,6 +50,7 @@ vm_call0(rb_thread_t* th, VALUE recv, ID id, int argc, const VALUE *argv,
ci->defined_class = defined_class;
ci->argc = argc;
ci->me = me;
+ ci->kw_arg = NULL;
return vm_call0_body(th, ci, argv);
}
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index e3e91729b0..95d232e740 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -130,29 +130,6 @@ rb_arg_error_new(int argc, int min, int max)
return rb_exc_new3(rb_eArgError, err_mess);
}
-NORETURN(static void argument_error(const rb_iseq_t *iseq, int miss_argc, int min_argc, int max_argc));
-static void
-argument_error(const rb_iseq_t *iseq, int miss_argc, int min_argc, int max_argc)
-{
- rb_thread_t *th = GET_THREAD();
- VALUE exc = rb_arg_error_new(miss_argc, min_argc, max_argc);
- VALUE at;
-
- if (iseq) {
- vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD, Qnil /* self */, Qnil /* klass */, Qnil /* specval*/,
- iseq->iseq_encoded, th->cfp->sp, 0 /* local_size */, 0 /* me */, 0 /* stack_max */);
- at = rb_vm_backtrace_object();
- vm_pop_frame(th);
- }
- else {
- at = rb_vm_backtrace_object();
- }
-
- rb_iv_set(exc, "bt_locations", at);
- rb_funcall(exc, rb_intern("set_backtrace"), 1, at);
- rb_exc_raise(exc);
-}
-
void
rb_error_arity(int argc, int min, int max)
{
@@ -1009,261 +986,110 @@ vm_base_ptr(rb_control_frame_t *cfp)
/* method call processes with call_info */
-static void
-vm_caller_setup_args(const rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci)
-{
-#define SAVE_RESTORE_CI(expr, ci) do { \
- int saved_argc = (ci)->argc; rb_block_t *saved_blockptr = (ci)->blockptr; /* save */ \
- expr; \
- (ci)->argc = saved_argc; (ci)->blockptr = saved_blockptr; /* restore */ \
-} while (0)
-
- if (UNLIKELY(ci->flag & VM_CALL_ARGS_BLOCKARG)) {
- rb_proc_t *po;
- VALUE proc;
-
- proc = *(--cfp->sp);
-
- if (proc != Qnil) {
- if (!rb_obj_is_proc(proc)) {
- VALUE b;
+#include "vm_args.c"
- SAVE_RESTORE_CI(b = rb_check_convert_type(proc, T_DATA, "Proc", "to_proc"), ci);
-
- 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;
- }
- GetProcPtr(proc, po);
- ci->blockptr = &po->block;
- RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp)->proc = proc;
- }
- }
- else if (ci->blockiseq != 0) { /* likely */
- ci->blockptr = RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp);
- ci->blockptr->iseq = ci->blockiseq;
- ci->blockptr->proc = 0;
- }
-
- /* expand top of stack? */
-
- if (UNLIKELY(ci->flag & VM_CALL_ARGS_SPLAT)) {
- VALUE ary = *(cfp->sp - 1);
- const VALUE *ptr;
- int i;
- VALUE tmp;
-
- SAVE_RESTORE_CI(tmp = rb_check_convert_type(ary, T_ARRAY, "Array", "to_a"), ci);
-
- if (NIL_P(tmp)) {
- /* do nothing */
- }
- else {
- long len = RARRAY_LEN(tmp);
- ptr = RARRAY_CONST_PTR(tmp);
- cfp->sp -= 1;
+static VALUE vm_call_iseq_setup_2(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci);
+static inline VALUE vm_call_iseq_setup_normal(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci);
+static inline VALUE vm_call_iseq_setup_tailcall(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci);
- CHECK_VM_STACK_OVERFLOW(cfp, len);
- for (i = 0; i < len; i++) {
- *cfp->sp++ = ptr[i];
- }
- ci->argc += i-1;
- }
- }
+static inline VALUE
+vm_callee_setup_block_arg_arg0_check(VALUE *argv)
+{
+ VALUE ary, arg0 = argv[0];
+ ary = rb_check_array_type(arg0);
+ argv[0] = arg0;
+ return ary;
}
static inline int
-vm_callee_setup_keyword_arg(rb_thread_t *th, const rb_iseq_t *iseq, int argc, int m, VALUE *orig_argv, VALUE *kwd)
+vm_callee_setup_block_arg_arg0_splat(rb_control_frame_t *cfp, const rb_iseq_t *iseq, VALUE *argv, VALUE ary)
{
- VALUE keyword_hash = 0, orig_hash;
- int optional = iseq->arg_keywords - iseq->arg_keyword_required;
- VALUE *const sp = th->cfp->sp;
- const int mark_stack_len = th->mark_stack_len;
-
- th->cfp->sp += argc;
- th->mark_stack_len -= argc;
+ int i;
+ long len = RARRAY_LEN(ary);
- if (argc > m &&
- !NIL_P(orig_hash = rb_check_hash_type(orig_argv[argc-1])) &&
- (keyword_hash = rb_extract_keywords(&orig_hash)) != 0) {
- if (!orig_hash) {
- argc--;
- }
- else {
- orig_argv[argc-1] = orig_hash;
- }
- }
- rb_get_kwargs(keyword_hash, iseq->arg_keyword_table, iseq->arg_keyword_required,
- (iseq->arg_keyword_check ? optional : -1-optional),
- NULL);
+ CHECK_VM_STACK_OVERFLOW(cfp, iseq->argc);
- if (!keyword_hash) {
- keyword_hash = rb_hash_new();
+ for (i=0; i<len && i<iseq->argc; i++) {
+ argv[i] = RARRAY_AREF(ary, i);
}
- th->cfp->sp = sp;
- th->mark_stack_len = mark_stack_len;
-
- *kwd = keyword_hash;
-
- return argc;
+ return i;
}
-static inline int
-vm_callee_setup_arg_complex(rb_thread_t *th, rb_call_info_t *ci, const rb_iseq_t *iseq, VALUE *orig_argv,
- int splattable)
+static inline void
+vm_callee_setup_block_arg(rb_thread_t *th, rb_call_info_t *ci, const rb_iseq_t *iseq, VALUE *argv, const enum arg_setup_type arg_setup_type)
{
- 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;
- int orig_argc = ci->argc;
- int argc = orig_argc;
- VALUE *argv = orig_argv;
- VALUE keyword_hash = Qnil;
- rb_num_t opt_pc = 0;
-
- th->mark_stack_len = argc + iseq->arg_size;
-
- /* keyword argument */
- if (iseq->arg_keyword != -1) {
- argc = vm_callee_setup_keyword_arg(th, iseq, argc, min, orig_argv, &keyword_hash);
- }
-
- /* mandatory */
- if ((argc < min) || (argc > max && max != UNLIMITED_ARGUMENTS)) {
+ if (LIKELY(iseq->arg_simple & 0x01)) {
+ rb_control_frame_t *cfp = th->cfp;
VALUE arg0;
- long len;
- if (!splattable ||
- argc != 1 ||
- NIL_P(arg0 = rb_check_array_type(argv[0])) ||
- (len = RARRAY_LEN(arg0)) < (long)min ||
- (len > (long)max && max != UNLIMITED_ARGUMENTS)) {
- argument_error(iseq, argc, min, max);
- }
- CHECK_VM_STACK_OVERFLOW(th->cfp, len - 1);
- MEMCPY(argv, RARRAY_CONST_PTR(arg0), VALUE, len);
- ci->argc = argc = orig_argc = (int)len;
- }
- argv += m;
- argc -= m;
+ CALLER_SETUP_ARG(cfp, ci); /* splat arg */
- /* post arguments */
- if (iseq->arg_post_len) {
- if (!(orig_argc < iseq->arg_post_start)) {
- VALUE *new_argv = ALLOCA_N(VALUE, argc);
- MEMCPY(new_argv, argv, VALUE, argc);
- argv = new_argv;
+ if (arg_setup_type == arg_setup_block &&
+ ci->argc == 1 &&
+ iseq->argc > 0 && !(iseq->arg_simple & 0x02) &&
+ !NIL_P(arg0 = vm_callee_setup_block_arg_arg0_check(argv))) {
+ ci->argc = vm_callee_setup_block_arg_arg0_splat(cfp, iseq, argv, arg0);
}
- MEMCPY(&orig_argv[iseq->arg_post_start], &argv[argc -= iseq->arg_post_len],
- VALUE, iseq->arg_post_len);
- }
-
- /* opt arguments */
- if (iseq->arg_opts) {
- if (argc > opts) {
- argc -= opts;
- argv += opts;
- opt_pc = iseq->arg_opt_table[opts]; /* no opt */
- }
- else {
- int i;
- for (i = argc; i<opts; i++) {
- orig_argv[i + m] = Qnil;
+ if (ci->argc != iseq->argc) {
+ if (arg_setup_type == arg_setup_block) {
+ if (ci->argc < iseq->argc) {
+ int i;
+ CHECK_VM_STACK_OVERFLOW(cfp, iseq->argc);
+ for (i=ci->argc; i<iseq->argc; i++) argv[i] = Qnil;
+ ci->argc = iseq->argc; /* fill rest parameters */
+ }
+ else if (ci->argc > iseq->argc) {
+ ci->argc = iseq->argc; /* simply truncate arguments */
+ }
}
- opt_pc = iseq->arg_opt_table[argc];
- argc = 0;
- }
- }
-
- /* rest arguments */
- if (iseq->arg_rest != -1) {
- orig_argv[iseq->arg_rest] = rb_ary_new4(argc, argv);
- argc = 0;
- }
-
- /* keyword argument */
- if (iseq->arg_keyword != -1) {
- int i;
- int arg_keywords_end = iseq->arg_keyword - (iseq->arg_block != -1);
- for (i = iseq->arg_keywords; 0 < i; i--) {
- orig_argv[arg_keywords_end - i] = Qnil;
- }
- orig_argv[iseq->arg_keyword] = keyword_hash;
- }
-
- /* block arguments */
- if (iseq->arg_block != -1) {
- VALUE blockval = Qnil;
- const rb_block_t *blockptr = ci->blockptr;
-
- if (blockptr) {
- /* make Proc object */
- if (blockptr->proc == 0) {
- rb_proc_t *proc;
- blockval = rb_vm_make_proc(th, blockptr, rb_cProc);
- GetProcPtr(blockval, proc);
- ci->blockptr = &proc->block;
+ else if (arg_setup_type == arg_setup_lambda &&
+ ci->argc == 1 &&
+ !NIL_P(arg0 = vm_callee_setup_block_arg_arg0_check(argv)) &&
+ RARRAY_LEN(arg0) == iseq->argc) {
+ ci->argc = vm_callee_setup_block_arg_arg0_splat(cfp, iseq, argv, arg0);
}
else {
- blockval = blockptr->proc;
+ argument_error(iseq, ci->argc, iseq->argc, iseq->argc);
}
}
- orig_argv[iseq->arg_block] = blockval; /* Proc or nil */
+ ci->aux.opt_pc = 0;
+ }
+ else {
+ ci->aux.opt_pc = setup_parameters_complex(th, iseq, ci, argv, arg_setup_type);
}
-
- th->mark_stack_len = 0;
- return (int)opt_pc;
}
-static VALUE vm_call_iseq_setup_2(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci);
-static inline VALUE vm_call_iseq_setup_normal(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci);
-static inline VALUE vm_call_iseq_setup_tailcall(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci);
-
static inline void
-vm_callee_setup_arg(rb_thread_t *th, rb_call_info_t *ci, const rb_iseq_t *iseq,
- VALUE *argv, int is_lambda)
+vm_callee_setup_arg(rb_thread_t *th, rb_call_info_t *ci, const rb_iseq_t *iseq, VALUE *argv)
{
if (LIKELY(iseq->arg_simple & 0x01)) {
- /* simple check */
+ rb_control_frame_t *cfp = th->cfp;
+
+ CALLER_SETUP_ARG(cfp, ci); /* splat arg */
+
if (ci->argc != iseq->argc) {
- VALUE arg0;
- long len;
- if (!(is_lambda > 1) ||
- ci->argc != 1 ||
- NIL_P(arg0 = rb_check_array_type(argv[0])) ||
- (len = RARRAY_LEN(arg0)) != (long)iseq->argc) {
- argument_error(iseq, ci->argc, iseq->argc, iseq->argc);
- }
- CHECK_VM_STACK_OVERFLOW(th->cfp, len - 1);
- MEMCPY(argv, RARRAY_CONST_PTR(arg0), VALUE, len);
- ci->argc = (int)len;
+ argument_error(iseq, ci->argc, iseq->argc, iseq->argc);
}
+
ci->aux.opt_pc = 0;
+
CI_SET_FASTPATH(ci,
- (UNLIKELY(ci->flag & VM_CALL_TAILCALL) ?
- vm_call_iseq_setup_tailcall :
- vm_call_iseq_setup_normal),
- (!is_lambda &&
- !(ci->flag & VM_CALL_ARGS_SPLAT) && /* argc may differ for each calls */
- !(ci->me->flag & NOEX_PROTECTED)));
+ (UNLIKELY(ci->flag & VM_CALL_TAILCALL) ? vm_call_iseq_setup_tailcall : vm_call_iseq_setup_normal),
+ (!IS_ARGS_SPLAT(ci) && !IS_ARGS_KEYWORD(ci) && !(ci->me->flag & NOEX_PROTECTED)));
}
else {
- ci->aux.opt_pc = vm_callee_setup_arg_complex(th, ci, iseq, argv, is_lambda > 1);
+ ci->aux.opt_pc = setup_parameters_complex(th, iseq, ci, argv, arg_setup_method);
}
}
static VALUE
vm_call_iseq_setup(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci)
{
- vm_callee_setup_arg(th, ci, ci->me->def->body.iseq, cfp->sp - ci->argc, 0);
+ vm_callee_setup_arg(th, ci, ci->me->def->body.iseq, cfp->sp - ci->argc);
return vm_call_iseq_setup_2(th, cfp, ci);
}
@@ -1573,13 +1399,15 @@ vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci)
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);
if (!(ci->me->flag & NOEX_PROTECTED) &&
- !(ci->flag & VM_CALL_ARGS_SPLAT)) {
+ !(ci->flag & VM_CALL_ARGS_SPLAT) &&
+ !(ci->kw_arg != NULL)) {
CI_SET_FASTPATH(ci, vm_call_cfunc_latter, 1);
}
val = vm_call_cfunc_latter(th, reg_cfp, ci);
@@ -1608,6 +1436,7 @@ vm_call_cfunc_push_frame(rb_thread_t *th)
static VALUE
vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci)
{
+ CALLER_SETUP_ARG(reg_cfp, ci);
return vm_call_cfunc_with_frame(th, reg_cfp, ci);
}
#endif
@@ -1645,7 +1474,11 @@ vm_call_bmethod_body(rb_thread_t *th, rb_call_info_t *ci, const VALUE *argv)
static VALUE
vm_call_bmethod(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci)
{
- VALUE *argv = ALLOCA_N(VALUE, ci->argc);
+ VALUE *argv;
+
+ CALLER_SETUP_ARG(cfp, ci);
+
+ argv = ALLOCA_N(VALUE, ci->argc);
MEMCPY(argv, cfp->sp - ci->argc, VALUE, ci->argc);
cfp->sp += - ci->argc - 1;
@@ -1663,16 +1496,21 @@ VALUE vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *c
static VALUE
vm_call_opt_send(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci)
{
- int i = ci->argc - 1;
+ int i;
VALUE sym;
rb_call_info_t ci_entry;
+ CALLER_SETUP_ARG(reg_cfp, ci);
+
+ i = ci->argc - 1;
+
if (ci->argc == 0) {
rb_raise(rb_eArgError, "no method name given");
}
ci_entry = *ci; /* copy ci entry */
ci = &ci_entry;
+ ci->kw_arg = NULL; /* TODO: delegate kw_arg without making a Hash object */
sym = TOPN(i);
@@ -1691,9 +1529,7 @@ vm_call_opt_send(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *c
if (i > 0) {
MEMMOVE(&TOPN(i), &TOPN(i-1), VALUE, i);
}
- ci->me =
- rb_method_entry_without_refinements(CLASS_OF(ci->recv),
- ci->mid, &ci->defined_class);
+ ci->me = rb_method_entry_without_refinements(CLASS_OF(ci->recv), ci->mid, &ci->defined_class);
ci->argc -= 1;
DEC_SP(1);
@@ -1706,8 +1542,13 @@ static VALUE
vm_call_opt_call(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci)
{
rb_proc_t *proc;
- int argc = ci->argc;
- VALUE *argv = ALLOCA_N(VALUE, argc);
+ int argc;
+ VALUE *argv;
+
+ CALLER_SETUP_ARG(cfp, ci);
+
+ argc = ci->argc;
+ argv = ALLOCA_N(VALUE, argc);
GetProcPtr(ci->recv, proc);
MEMCPY(argv, cfp->sp - argc, VALUE, argc);
cfp->sp -= argc + 1;
@@ -1721,12 +1562,15 @@ vm_call_method_missing(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_inf
VALUE *argv = STACK_ADDR_FROM_TOP(ci->argc);
rb_call_info_t ci_entry;
+ CALLER_SETUP_ARG(reg_cfp, ci);
+
ci_entry.flag = VM_CALL_FCALL | VM_CALL_OPT_SEND;
ci_entry.argc = ci->argc+1;
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.kw_arg = NULL;
/* shift arguments: m(a, b, c) #=> method_missing(:m, a, b, c) */
CHECK_VM_STACK_OVERFLOW(reg_cfp, 1);
@@ -1798,12 +1642,14 @@ vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci)
CI_SET_FASTPATH(ci, vm_call_cfunc, enable_fastpath);
return vm_call_cfunc(th, cfp, ci);
case VM_METHOD_TYPE_ATTRSET:{
+ CALLER_SETUP_ARG(cfp, ci);
rb_check_arity(ci->argc, 1, 1);
ci->aux.index = 0;
CI_SET_FASTPATH(ci, vm_call_attrset, enable_fastpath && !(ci->flag & VM_CALL_ARGS_SPLAT));
return vm_call_attrset(th, cfp, ci);
}
case VM_METHOD_TYPE_IVAR:{
+ CALLER_SETUP_ARG(cfp, ci);
rb_check_arity(ci->argc, 0, 0);
ci->aux.index = 0;
CI_SET_FASTPATH(ci, vm_call_ivar, enable_fastpath && !(ci->flag & VM_CALL_ARGS_SPLAT));
@@ -2150,214 +1996,27 @@ vm_yield_with_cfunc(rb_thread_t *th, const rb_block_t *block,
return val;
}
-
-/*--
- * @brief on supplied all of optional, rest and post parameters.
- * @pre iseq is block style (not lambda style)
- */
-static inline int
-vm_yield_setup_block_args_complex(rb_thread_t *th, const rb_iseq_t *iseq,
- int argc, VALUE *argv)
-{
- rb_num_t opt_pc = 0;
- int i;
- const int m = iseq->argc;
- const int r = iseq->arg_rest;
- int len = iseq->arg_post_len;
- int start = iseq->arg_post_start;
- int rsize = argc > m ? argc - m : 0; /* # of arguments which did not consumed yet */
- int psize = rsize > len ? len : rsize; /* # of post arguments */
- int osize = 0; /* # of opt arguments */
- VALUE ary;
-
- /* reserves arguments for post parameters */
- rsize -= psize;
-
- if (iseq->arg_opts) {
- const int opts = iseq->arg_opts - 1;
- if (rsize > opts) {
- osize = opts;
- opt_pc = iseq->arg_opt_table[opts];
- }
- else {
- osize = rsize;
- opt_pc = iseq->arg_opt_table[rsize];
- }
- }
- rsize -= osize;
-
- if (0) {
- printf(" argc: %d\n", argc);
- printf(" len: %d\n", len);
- printf("start: %d\n", start);
- printf("rsize: %d\n", rsize);
- }
-
- if (r == -1) {
- /* copy post argument */
- MEMMOVE(&argv[start], &argv[m+osize], VALUE, psize);
- }
- else {
- ary = rb_ary_new4(rsize, &argv[r]);
-
- /* copy post argument */
- MEMMOVE(&argv[start], &argv[m+rsize+osize], VALUE, psize);
- argv[r] = ary;
- }
-
- for (i=psize; i<len; i++) {
- argv[start + i] = Qnil;
- }
-
- return (int)opt_pc;
-}
-
-static inline int
-vm_yield_setup_block_args(rb_thread_t *th, const rb_iseq_t * iseq,
- int orig_argc, VALUE *argv,
- const rb_block_t *blockptr)
+static int
+vm_yield_callee_setup_arg(rb_thread_t *th, rb_call_info_t *ci, const rb_iseq_t *iseq, VALUE *argv, enum arg_setup_type arg_setup_type)
{
- int i;
- int argc = orig_argc;
- const int m = iseq->argc;
- const int min = m + iseq->arg_post_len;
- VALUE ary, arg0;
- VALUE keyword_hash = Qnil;
- int opt_pc = 0;
-
- th->mark_stack_len = argc;
-
- /*
- * yield [1, 2]
- * => {|a|} => a = [1, 2]
- * => {|a, b|} => a, b = [1, 2]
- */
- arg0 = argv[0];
- if (!(iseq->arg_simple & 0x02) && /* exclude {|a|} */
- (min > 0 || /* positional arguments exist */
- iseq->arg_opts > 2 || /* multiple optional arguments exist */
- iseq->arg_keyword != -1 || /* any keyword arguments */
- 0) &&
- argc == 1 && !NIL_P(ary = rb_check_array_type(arg0))) { /* rhs is only an array */
- th->mark_stack_len = argc = RARRAY_LENINT(ary);
-
- CHECK_VM_STACK_OVERFLOW(th->cfp, argc);
-
- MEMCPY(argv, RARRAY_CONST_PTR(ary), VALUE, argc);
- }
- else {
- /* vm_push_frame current argv is at the top of sp because vm_invoke_block
- * set sp at the first element of argv.
- * Therefore when rb_check_array_type(arg0) called to_ary and called to_ary
- * or method_missing run vm_push_frame, it initializes local variables.
- * see also https://bugs.ruby-lang.org/issues/8484
- */
- argv[0] = arg0;
- }
-
- /* keyword argument */
- if (iseq->arg_keyword != -1) {
- argc = vm_callee_setup_keyword_arg(th, iseq, argc, min, argv, &keyword_hash);
- }
-
- for (i=argc; i<m; i++) {
- argv[i] = Qnil;
- }
-
- if (iseq->arg_rest == -1 && iseq->arg_opts == 0) {
- const int arg_size = iseq->arg_size;
- if (arg_size < argc) {
- /*
- * yield 1, 2
- * => {|a|} # truncate
- */
- th->mark_stack_len = argc = arg_size;
- }
- }
- else {
- int r = iseq->arg_rest;
-
- if (iseq->arg_post_len ||
- iseq->arg_opts) { /* TODO: implement simple version for (iseq->arg_post_len==0 && iseq->arg_opts > 0) */
- opt_pc = vm_yield_setup_block_args_complex(th, iseq, argc, argv);
- }
- else {
- if (argc < r) {
- /* yield 1
- * => {|a, b, *r|}
- */
- for (i=argc; i<r; i++) {
- argv[i] = Qnil;
- }
- argv[r] = rb_ary_new();
- }
- else {
- argv[r] = rb_ary_new4(argc-r, &argv[r]);
- }
- }
-
- th->mark_stack_len = iseq->arg_size;
- }
-
- /* keyword argument */
- if (iseq->arg_keyword != -1) {
- int arg_keywords_end = iseq->arg_keyword - (iseq->arg_block != -1);
- for (i = iseq->arg_keywords; 0 < i; i--) {
- argv[arg_keywords_end - i] = Qnil;
- }
- argv[iseq->arg_keyword] = keyword_hash;
- }
-
- /* {|&b|} */
- if (iseq->arg_block != -1) {
- VALUE procval = Qnil;
-
- if (blockptr) {
- if (blockptr->proc == 0) {
- procval = rb_vm_make_proc(th, blockptr, rb_cProc);
- }
- else {
- procval = blockptr->proc;
- }
- }
-
- argv[iseq->arg_block] = procval;
- }
-
- th->mark_stack_len = 0;
- return opt_pc;
+ vm_callee_setup_block_arg(th, ci, iseq, argv, arg_setup_type);
+ return ci->aux.opt_pc;
}
-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)
+static int
+vm_yield_setup_args(rb_thread_t *th, const rb_iseq_t *iseq, const int argc, VALUE *argv, const rb_block_t *blockptr, enum arg_setup_type arg_setup_type)
{
- if (0) { /* for debug */
- printf(" argc: %d\n", argc);
- printf("iseq argc: %d\n", iseq->argc);
- printf("iseq opts: %d\n", iseq->arg_opts);
- printf("iseq rest: %d\n", iseq->arg_rest);
- printf("iseq post: %d\n", iseq->arg_post_len);
- printf("iseq blck: %d\n", iseq->arg_block);
- printf("iseq smpl: %d\n", iseq->arg_simple);
- printf(" lambda: %s\n", lambda ? "true" : "false");
- }
+ rb_call_info_t ci_entry;
+ ci_entry.argc = argc;
+ ci_entry.blockptr = (rb_block_t *)blockptr;
+ ci_entry.flag = 0;
+ ci_entry.kw_arg = NULL;
+ ci_entry.me = NULL;
- if (lambda) {
- /* call as method */
- 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(th, &ci_entry, iseq, argv, lambda);
- return ci_entry.aux.opt_pc;
- }
- else {
- return vm_yield_setup_block_args(th, iseq, argc, argv, blockptr);
- }
+ return vm_yield_callee_setup_arg(th, &ci_entry, iseq, argv, arg_setup_type);
}
+/* ruby iseq -> ruby block iseq */
static VALUE
vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci)
{
@@ -2370,18 +2029,15 @@ vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci
}
iseq = block->iseq;
- if (UNLIKELY(ci->flag & VM_CALL_ARGS_SPLAT)) {
- vm_caller_setup_args(th, GET_CFP(), ci);
- }
-
if (BUILTIN_TYPE(iseq) != T_NODE) {
int opt_pc;
const int arg_size = iseq->arg_size;
int is_lambda = block_proc_is_lambda(block->proc);
VALUE * const rsp = GET_SP() - ci->argc;
- SET_SP(rsp);
- opt_pc = vm_yield_setup_args(th, iseq, ci->argc, rsp, 0, is_lambda * 2);
+ opt_pc = vm_yield_callee_setup_arg(th, ci, iseq, rsp, is_lambda ? arg_setup_lambda : arg_setup_block);
+
+ SET_SP(rsp);
vm_push_frame(th, iseq,
is_lambda ? VM_FRAME_MAGIC_LAMBDA : VM_FRAME_MAGIC_BLOCK,
@@ -2395,7 +2051,9 @@ vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci
return Qundef;
}
else {
- VALUE val = vm_yield_with_cfunc(th, block, block->self, ci->argc, STACK_ADDR_FROM_TOP(ci->argc), 0);
+ VALUE val;
+ CALLER_SETUP_ARG(th->cfp, ci);
+ 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;
}
@@ -2433,3 +2091,4 @@ vm_once_clear(VALUE data)
is->once.running_thread = NULL;
return Qnil;
}
+