mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* 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
This commit is contained in:
parent
055a465ac4
commit
fbebd502f9
15 changed files with 1397 additions and 693 deletions
84
ChangeLog
84
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
|
||||
|
|
10
class.c
10
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;
|
||||
|
|
|
@ -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 \
|
||||
|
|
403
compile.c
403
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;
|
||||
|
||||
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;
|
||||
}
|
||||
for (j = 0; j < i; j++) {
|
||||
iseq->arg_keyword_table[j] = SYM2ID(RARRAY_AREF(keywords, j));
|
||||
}
|
||||
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;
|
||||
|
|
2
hash.c
2
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)
|
||||
|
|
40
insns.def
40
insns.def
|
@ -832,6 +832,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
|
||||
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
40
iseq.c
40
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");
|
||||
|
|
64
parse.y
64
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;
|
||||
|
|
6
proc.c
6
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
|
||||
|
|
2
vm.c
2
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 */
|
||||
|
|
783
vm_args.c
Normal file
783
vm_args.c
Normal file
|
@ -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)
|
46
vm_core.h
46
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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
605
vm_insnhelper.c
605
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;
|
||||
|
||||
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;
|
||||
|
||||
CHECK_VM_STACK_OVERFLOW(cfp, len);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
*cfp->sp++ = ptr[i];
|
||||
}
|
||||
ci->argc += i-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
if (!keyword_hash) {
|
||||
keyword_hash = rb_hash_new();
|
||||
}
|
||||
|
||||
th->cfp->sp = sp;
|
||||
th->mark_stack_len = mark_stack_len;
|
||||
|
||||
*kwd = keyword_hash;
|
||||
|
||||
return argc;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)) {
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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 {
|
||||
blockval = blockptr->proc;
|
||||
}
|
||||
}
|
||||
|
||||
orig_argv[iseq->arg_block] = blockval; /* Proc or nil */
|
||||
}
|
||||
|
||||
th->mark_stack_len = 0;
|
||||
return (int)opt_pc;
|
||||
}
|
||||
#include "vm_args.c"
|
||||
|
||||
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 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_block_arg_arg0_splat(rb_control_frame_t *cfp, const rb_iseq_t *iseq, VALUE *argv, VALUE ary)
|
||||
{
|
||||
int i;
|
||||
long len = RARRAY_LEN(ary);
|
||||
|
||||
CHECK_VM_STACK_OVERFLOW(cfp, iseq->argc);
|
||||
|
||||
for (i=0; i<len && i<iseq->argc; i++) {
|
||||
argv[i] = RARRAY_AREF(ary, i);
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
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_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)
|
||||
{
|
||||
if (LIKELY(iseq->arg_simple & 0x01)) {
|
||||
/* simple check */
|
||||
rb_control_frame_t *cfp = th->cfp;
|
||||
VALUE arg0;
|
||||
|
||||
CALLER_SETUP_ARG(cfp, ci); /* splat arg */
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
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 */
|
||||
}
|
||||
}
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
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_type);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
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)) {
|
||||
rb_control_frame_t *cfp = th->cfp;
|
||||
|
||||
CALLER_SETUP_ARG(cfp, ci); /* splat arg */
|
||||
|
||||
if (ci->argc != iseq->argc) {
|
||||
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_ARGS_SPLAT(ci) && !IS_ARGS_KEYWORD(ci) && !(ci->me->flag & NOEX_PROTECTED)));
|
||||
}
|
||||
else {
|
||||
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)
|
||||
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)
|
||||
{
|
||||
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;
|
||||
vm_callee_setup_block_arg(th, ci, iseq, argv, arg_setup_type);
|
||||
return ci->aux.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_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)
|
||||
{
|
||||
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;
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue