summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYusuke Endoh <[email protected]>2019-03-18 14:25:47 +0900
committerJeremy Evans <[email protected]>2019-08-30 12:39:31 -0700
commit16c6984bb97409029e213154ac4f633ae04af3d8 ()
tree53839e4d596e4016320097530ff5d7fcf19d11e6
parentb0a291f6f6a5834fd84807eb48be906ade429871 (diff)
Separate keyword arguments from positional arguments
And, allow non-symbol keys as a keyword arugment
Notes: Merged: https://.com/ruby/ruby/pull/2395
-rw-r--r--class.c3
-rw-r--r--compile.c7
-rw-r--r--hash.c8
-rw-r--r--internal.h1
-rw-r--r--parse.y12
-rw-r--r--proc.c2
-rw-r--r--vm.c16
-rw-r--r--vm_args.c118
-rw-r--r--vm_insnhelper.c36
9 files changed, 152 insertions, 51 deletions
@@ -1830,8 +1830,7 @@ rb_keyword_error_new(const char *error, VALUE keys)
rb_str_cat_cstr(error_message, ": ");
while (1) {
const VALUE k = RARRAY_AREF(keys, i);
- Check_Type(k, T_SYMBOL); /* wrong hash is given to rb_get_kwargs */
- rb_str_append(error_message, rb_sym2str(k));
if (++i >= len) break;
rb_str_cat_cstr(error_message, ", ");
}
@@ -3798,7 +3798,7 @@ compile_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
{
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) {
const NODE *node = root_node->nd_head;
while (node) {
@@ -3806,13 +3806,14 @@ compile_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
assert(nd_type(node) == NODE_ARRAY);
if (!key_node) {
- if (flag && !root_node->nd_brace) *flag |= VM_CALL_KW_SPLAT;
return FALSE;
}
else if (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 */
@@ -7351,6 +7352,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, in
ADD_INSN (args, line, concatarray);
--argc;
}
}
else if (local_body->param.flags.has_kwrest) {
int idx = local_body->local_table_size - local_kwd->rest_start;
@@ -7364,6 +7366,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, in
else {
argc++;
}
}
}
@@ -30,6 +30,8 @@
# endif
#endif
#ifndef HASH_DEBUG
#define HASH_DEBUG 0
#endif
@@ -3274,6 +3276,8 @@ inspect_hash(VALUE hash, VALUE dummy, int recur)
static VALUE
rb_hash_inspect(VALUE hash)
{
if (RHASH_EMPTY_P(hash))
return rb_usascii_str_new2("{}");
return rb_exec_recursive(inspect_hash, hash, 0);
@@ -6273,6 +6277,10 @@ Init_Hash(void)
*/
rb_define_global_const("ENV", envtbl);
/* for callcc */
ruby_register_rollback_func_for_ensure(hash_foreach_ensure, hash_foreach_ensure_rollback);
@@ -2397,6 +2397,7 @@ VALUE rb_str_normalize_ospath(const char *ptr, long len);
/* hash.c (export) */
VALUE rb_hash_delete_entry(VALUE hash, VALUE key);
VALUE rb_ident_hash_new(void);
/* io.c (export) */
void rb_maygvl_fd_fix_cloexec(int fd);
@@ -5134,12 +5134,6 @@ assocs : assoc
assocs = tail;
}
else if (tail) {
- if (assocs->nd_head &&
- !tail->nd_head && nd_type(tail->nd_next) == NODE_ARRAY &&
- nd_type(tail->nd_next->nd_head) == NODE_HASH) {
- /* DSTAR */
- tail = tail->nd_next->nd_head->nd_head;
- }
assocs = list_concat(assocs, tail);
}
$$ = assocs;
@@ -5177,11 +5171,7 @@ assoc : arg_value tASSOC arg_value
| tDSTAR arg_value
{
/*%%%*/
- if (nd_type($2) == NODE_HASH &&
- !($2->nd_head && $2->nd_head->nd_alen))
- $$ = 0;
- else
- $$ = list_append(p, NEW_LIST(0, &@$), $2);
/*% %*/
/*% ripper: assoc_splat!($2) %*/
}
@@ -2683,7 +2683,7 @@ rb_mod_method_location(VALUE mod, ID id)
return rb_method_entry_location(me);
}
-VALUE
rb_obj_method_location(VALUE obj, ID id)
{
return rb_mod_method_location(CLASS_OF(obj), id);
@@ -2779,6 +2779,9 @@ static VALUE core_hash_merge_kwd(VALUE hash, VALUE kw);
static VALUE
core_hash_merge(VALUE hash, long argc, const VALUE *argv)
{
Check_Type(hash, T_HASH);
VM_ASSERT(argc % 2 == 0);
rb_hash_bulk_insert(argc, argv, hash);
@@ -2790,23 +2793,14 @@ m_core_hash_merge_ptr(int argc, VALUE *argv, VALUE recv)
{
VALUE hash = argv[0];
- REWIND_CFP(core_hash_merge(hash, argc-1, argv+1));
return hash;
}
-static void
-kw_check_symbol(VALUE key)
-{
- if (!SYMBOL_P(key)) {
- rb_raise(rb_eTypeError, "hash key %+"PRIsVALUE" is not a Symbol",
- key);
- }
-}
static int
kwmerge_i(VALUE key, VALUE value, VALUE hash)
{
- kw_check_symbol(key);
rb_hash_aset(hash, key, value);
return ST_CONTINUE;
}
@@ -2821,6 +2815,8 @@ m_core_hash_merge_kwd(VALUE recv, VALUE hash, VALUE kw)
static VALUE
core_hash_merge_kwd(VALUE hash, VALUE kw)
{
rb_hash_foreach(rb_to_hash_type(kw), kwmerge_i, hash);
return hash;
}
@@ -186,12 +186,16 @@ args_rest_array(struct args_info *args)
static int
keyword_hash_p(VALUE *kw_hash_ptr, VALUE *rest_hash_ptr)
{
*rest_hash_ptr = rb_check_hash_type(*kw_hash_ptr);
if (!NIL_P(*rest_hash_ptr)) {
- VALUE hash = rb_extract_keywords(rest_hash_ptr);
- if (!hash) hash = Qnil;
- *kw_hash_ptr = hash;
return TRUE;
}
else {
@@ -483,7 +487,7 @@ args_setup_kw_parameters(rb_execution_context_t *const ec, const rb_iseq_t *cons
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
@@ -509,6 +513,40 @@ fill_keys_values(st_data_t key, st_data_t val, st_data_t ptr)
return ST_CONTINUE;
}
static int
setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * const iseq,
struct rb_calling_info *const calling,
@@ -520,10 +558,12 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
int opt_pc = 0;
int given_argc;
int kw_splat = FALSE;
struct args_info args_body, *args;
VALUE keyword_hash = Qnil;
VALUE * const orig_sp = ec->cfp->sp;
unsigned int i;
vm_check_canary(ec, orig_sp);
/*
@@ -551,7 +591,7 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
args->argv = locals;
args->rest_dupped = FALSE;
- if (ci->flag & VM_CALL_KWARG) {
args->kw_arg = ((struct rb_call_info_with_kwarg *)ci)->kw_arg;
if (iseq->body->param.flags.has_kw) {
@@ -565,6 +605,7 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
else {
args->kw_argv = NULL;
given_argc = args_kw_argv_to_hash(args);
}
}
else {
@@ -576,8 +617,24 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
args->rest = locals[--args->argc];
args->rest_index = 0;
given_argc += RARRAY_LENINT(args->rest) - 1;
}
else {
args->rest = Qfalse;
}
@@ -598,6 +655,12 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
/* 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);
}
@@ -613,16 +676,41 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
}
}
- if (ci->flag & VM_CALL_KW_SPLAT) {
kw_splat = !iseq->body->param.flags.has_rest;
}
if (given_argc > min_argc &&
(iseq->body->param.flags.has_kw || iseq->body->param.flags.has_kwrest ||
(kw_splat && given_argc > max_argc)) &&
args->kw_argv == NULL) {
- if (args_pop_keyword_hash(args, &keyword_hash)) {
- given_argc--;
}
}
if (given_argc > max_argc && max_argc != UNLIMITED_ARGUMENTS) {
@@ -683,7 +771,7 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
else if (iseq->body->param.flags.has_kwrest) {
args_setup_kw_rest_parameter(keyword_hash, locals + iseq->body->param.keyword->rest_start);
}
- else if (!NIL_P(keyword_hash) && RHASH_SIZE(keyword_hash) > 0) {
argument_kw_error(ec, iseq, "unknown", rb_hash_keys(keyword_hash));
}
else if (kw_splat && NIL_P(keyword_hash)) {
@@ -792,6 +880,8 @@ vm_caller_setup_arg_splat(rb_control_frame_t *cfp, struct rb_calling_info *calli
CHECK_VM_STACK_OVERFLOW(cfp, len);
for (i = 0; i < len; i++) {
*cfp->sp++ = ptr[i];
}
@@ -800,7 +890,7 @@ vm_caller_setup_arg_splat(rb_control_frame_t *cfp, struct rb_calling_info *calli
}
static inline void
-vm_caller_setup_arg_kw(rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci)
{
struct rb_call_info_with_kwarg *ci_kw = (struct rb_call_info_with_kwarg *)ci;
const VALUE *const passed_keywords = ci_kw->kw_arg->keywords;
@@ -809,6 +899,14 @@ vm_caller_setup_arg_kw(rb_control_frame_t *cfp, struct rb_calling_info *calling,
VALUE *sp = cfp->sp;
int i;
for (i=0; i<kw_len; i++) {
rb_hash_aset(h, passed_keywords[i], (sp - kw_len)[i]);
}
@@ -1740,13 +1740,13 @@ rb_iseq_only_kwparam_p(const rb_iseq_t *iseq)
static inline void
CALLER_SETUP_ARG(struct rb_control_frame_struct *restrict cfp,
struct rb_calling_info *restrict calling,
- const struct rb_call_info *restrict ci)
{
if (UNLIKELY(IS_ARGS_SPLAT(ci))) {
vm_caller_setup_arg_splat(cfp, calling);
}
if (UNLIKELY(IS_ARGS_KEYWORD(ci))) {
- vm_caller_setup_arg_kw(cfp, calling, ci);
}
}
@@ -1856,7 +1856,7 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling,
if (LIKELY(!(ci->flag & VM_CALL_KW_SPLAT))) {
if (LIKELY(rb_simple_iseq_p(iseq))) {
rb_control_frame_t *cfp = ec->cfp;
- CALLER_SETUP_ARG(cfp, calling, ci); /* splat arg */
if (calling->argc != iseq->body->param.lead_num) {
argument_arity_error(ec, iseq, calling->argc, iseq->body->param.lead_num, iseq->body->param.lead_num);
@@ -1867,7 +1867,7 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling,
}
else if (rb_iseq_only_optparam_p(iseq)) {
rb_control_frame_t *cfp = ec->cfp;
- CALLER_SETUP_ARG(cfp, calling, ci); /* splat arg */
const int lead_num = iseq->body->param.lead_num;
const int opt_num = iseq->body->param.opt_num;
@@ -2214,7 +2214,7 @@ vm_call_cfunc(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb
{
RB_DEBUG_COUNTER_INC(ccf_cfunc);
- CALLER_SETUP_ARG(reg_cfp, calling, ci);
return vm_call_cfunc_with_frame(ec, reg_cfp, calling, ci, cc);
}
@@ -2256,7 +2256,7 @@ vm_call_bmethod(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_c
VALUE *argv;
int argc;
- CALLER_SETUP_ARG(cfp, calling, ci);
argc = calling->argc;
argv = ALLOCA_N(VALUE, argc);
MEMCPY(argv, cfp->sp - argc, VALUE, argc);
@@ -2286,7 +2286,7 @@ vm_call_opt_send(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct
struct rb_call_info_with_kwarg ci_entry;
struct rb_call_cache cc_entry, *cc;
- CALLER_SETUP_ARG(reg_cfp, calling, orig_ci);
i = calling->argc - 1;
@@ -2303,7 +2303,12 @@ vm_call_opt_send(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct
ci = &ci_entry.ci;
ci_entry.ci = *orig_ci;
}
- ci->flag = ci->flag & ~VM_CALL_KWARG; /* TODO: delegate kw_arg without making a Hash object */
/* setup new cc */
cc_entry = *orig_cc;
@@ -2333,7 +2338,7 @@ vm_call_opt_send(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct
}
cc->me = rb_callable_method_entry_with_refinements(CLASS_OF(calling->recv), ci->mid, NULL);
- ci->flag = VM_CALL_FCALL | VM_CALL_OPT_SEND;
return vm_call_method(ec, reg_cfp, calling, ci, cc);
}
@@ -2392,7 +2397,7 @@ vm_call_method_missing(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
struct rb_call_cache cc_entry, *cc;
unsigned int argc;
- CALLER_SETUP_ARG(reg_cfp, calling, orig_ci);
argc = calling->argc+1;
ci_entry.flag = VM_CALL_FCALL | VM_CALL_OPT_SEND;
@@ -2597,14 +2602,14 @@ vm_call_method_each_type(rb_execution_context_t *ec, rb_control_frame_t *cfp, st
return vm_call_cfunc(ec, cfp, calling, ci, cc);
case VM_METHOD_TYPE_ATTRSET:
- CALLER_SETUP_ARG(cfp, calling, ci);
rb_check_arity(calling->argc, 1, 1);
cc->aux.index = 0;
CC_SET_FASTPATH(cc, vm_call_attrset, !((ci->flag & VM_CALL_ARGS_SPLAT) || (ci->flag & VM_CALL_KWARG)));
return vm_call_attrset(ec, cfp, calling, ci, cc);
case VM_METHOD_TYPE_IVAR:
- CALLER_SETUP_ARG(cfp, calling, ci);
rb_check_arity(calling->argc, 0, 0);
cc->aux.index = 0;
CC_SET_FASTPATH(cc, vm_call_ivar, !(ci->flag & VM_CALL_ARGS_SPLAT));
@@ -2905,7 +2910,7 @@ vm_callee_setup_block_arg(rb_execution_context_t *ec, struct rb_calling_info *ca
rb_control_frame_t *cfp = ec->cfp;
VALUE arg0;
- CALLER_SETUP_ARG(cfp, calling, ci); /* splat arg */
if (arg_setup_type == arg_setup_block &&
calling->argc == 1 &&
@@ -2948,6 +2953,7 @@ vm_yield_setup_args(rb_execution_context_t *ec, const rb_iseq_t *iseq, const int
calling = &calling_entry;
calling->argc = argc;
calling->block_handler = block_handler;
ci_entry.flag = 0;
ci = &ci_entry;
@@ -2987,7 +2993,7 @@ vm_invoke_symbol_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
{
VALUE val;
int argc;
- CALLER_SETUP_ARG(ec->cfp, calling, ci);
argc = calling->argc;
val = vm_yield_with_symbol(ec, symbol, argc, STACK_ADDR_FROM_TOP(argc), calling->block_handler);
POPN(argc);
@@ -3001,7 +3007,7 @@ vm_invoke_ifunc_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
{
VALUE val;
int argc;
- CALLER_SETUP_ARG(ec->cfp, calling, ci);
argc = calling->argc;
val = vm_yield_with_cfunc(ec, captured, captured->self, argc, STACK_ADDR_FROM_TOP(argc), calling->block_handler, NULL);
POPN(argc); /* TODO: should put before C/yield? */