* 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:
ko1 2014-11-02 18:02:55 +00:00
Родитель 055a465ac4
Коммит fbebd502f9
15 изменённых файлов: 1397 добавлений и 693 удалений

Просмотреть файл

@ -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
Просмотреть файл

@ -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
Просмотреть файл

@ -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
Просмотреть файл

@ -1854,7 +1854,7 @@ rb_hash_values(VALUE hash)
*
*/
static VALUE
VALUE
rb_hash_has_key(VALUE hash, VALUE key)
{
if (!RHASH(hash)->ntbl)

Просмотреть файл

@ -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
Просмотреть файл

@ -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
Просмотреть файл

@ -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
Просмотреть файл

@ -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
Просмотреть файл

@ -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
Просмотреть файл

@ -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)

Просмотреть файл

@ -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);
}

Просмотреть файл

@ -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;
}