aboutsummaryrefslogtreecommitdiffstats
path: root/vm_insnhelper.c
diff options
context:
space:
mode:
Diffstat (limited to 'vm_insnhelper.c')
-rw-r--r--vm_insnhelper.c44
1 files changed, 27 insertions, 17 deletions
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index 56767a4a62..49e865d96f 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -1770,15 +1770,21 @@ rb_iseq_only_kwparam_p(const rb_iseq_t *iseq)
static inline void
-CALLER_SETUP_ARG_WITHOUT_KW_SPLAT(struct rb_control_frame_struct *restrict cfp,
- struct rb_calling_info *restrict calling,
- const struct rb_call_info *restrict ci)
+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))) {
/* This expands the rest argument to the stack.
* So, ci->flag & VM_CALL_ARGS_SPLAT is now inconsistent.
*/
vm_caller_setup_arg_splat(cfp, calling);
+ if (!IS_ARGS_KW_OR_KW_SPLAT(ci) &&
+ calling->argc > 0 &&
+ RB_TYPE_P(*(cfp->sp - 1), T_HASH) &&
+ (((struct RHash *)*(cfp->sp - 1))->basic.flags & RHASH_PASS_AS_KEYWORDS)) {
+ calling->kw_splat = 1;
+ }
}
if (UNLIKELY(IS_ARGS_KEYWORD(ci))) {
/* This converts VM_CALL_KWARG style to VM_CALL_KW_SPLAT style
@@ -1790,12 +1796,10 @@ CALLER_SETUP_ARG_WITHOUT_KW_SPLAT(struct rb_control_frame_struct *restrict cfp,
}
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)
+CALLER_REMOVE_EMPTY_KW_SPLAT(struct rb_control_frame_struct *restrict cfp,
+ struct rb_calling_info *restrict calling,
+ const struct rb_call_info *restrict ci)
{
- CALLER_SETUP_ARG_WITHOUT_KW_SPLAT(cfp, calling, ci);
-
if (UNLIKELY(calling->kw_splat)) {
/* This removes the last Hash object if it is empty.
* So, ci->flag & VM_CALL_KW_SPLAT is now inconsistent.
@@ -1920,6 +1924,7 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling,
if (LIKELY(rb_simple_iseq_p(iseq))) {
rb_control_frame_t *cfp = ec->cfp;
CALLER_SETUP_ARG(cfp, calling, ci);
+ CALLER_REMOVE_EMPTY_KW_SPLAT(cfp, calling, ci);
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);
@@ -1931,6 +1936,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);
+ CALLER_REMOVE_EMPTY_KW_SPLAT(cfp, calling, ci);
const int lead_num = iseq->body->param.lead_num;
const int opt_num = iseq->body->param.opt_num;
@@ -2285,10 +2291,12 @@ vm_call_cfunc_with_frame(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp
static VALUE
vm_call_cfunc(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
{
- int empty_kw_splat = calling->kw_splat;
+ int empty_kw_splat;
RB_DEBUG_COUNTER_INC(ccf_cfunc);
CALLER_SETUP_ARG(reg_cfp, calling, ci);
+ empty_kw_splat = calling->kw_splat;
+ CALLER_REMOVE_EMPTY_KW_SPLAT(reg_cfp, calling, ci);
if (empty_kw_splat && calling->kw_splat) {
empty_kw_splat = 0;
}
@@ -2333,7 +2341,7 @@ vm_call_bmethod(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_c
VALUE *argv;
int argc;
- CALLER_SETUP_ARG_WITHOUT_KW_SPLAT(cfp, calling, ci);
+ CALLER_SETUP_ARG(cfp, calling, ci);
argc = calling->argc;
argv = ALLOCA_N(VALUE, argc);
MEMCPY(argv, cfp->sp - argc, VALUE, argc);
@@ -2363,7 +2371,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_WITHOUT_KW_SPLAT(reg_cfp, calling, orig_ci);
+ CALLER_SETUP_ARG(reg_cfp, calling, orig_ci);
i = calling->argc - 1;
@@ -2468,7 +2476,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_WITHOUT_KW_SPLAT(reg_cfp, calling, orig_ci);
+ CALLER_SETUP_ARG(reg_cfp, calling, orig_ci);
argc = calling->argc+1;
ci_entry.flag = VM_CALL_FCALL | VM_CALL_OPT_SEND | (calling->kw_splat ? VM_CALL_KW_SPLAT : 0);
@@ -2673,12 +2681,12 @@ 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);
if (calling->argc == 1 && calling->kw_splat && RHASH_EMPTY_P(cfp->sp[-1])) {
- CALLER_SETUP_ARG_WITHOUT_KW_SPLAT(cfp, calling, ci);
rb_warn_keyword_to_last_hash(calling, ci, NULL);
}
else {
- CALLER_SETUP_ARG(cfp, calling, ci);
+ CALLER_REMOVE_EMPTY_KW_SPLAT(cfp, calling, ci);
}
rb_check_arity(calling->argc, 1, 1);
@@ -2688,6 +2696,7 @@ vm_call_method_each_type(rb_execution_context_t *ec, rb_control_frame_t *cfp, st
case VM_METHOD_TYPE_IVAR:
CALLER_SETUP_ARG(cfp, calling, ci);
+ CALLER_REMOVE_EMPTY_KW_SPLAT(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));
@@ -2998,12 +3007,12 @@ 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);
if (calling->kw_splat && calling->argc == iseq->body->param.lead_num + iseq->body->param.post_num && RHASH_EMPTY_P(cfp->sp[-1])) {
- CALLER_SETUP_ARG_WITHOUT_KW_SPLAT(cfp, calling, ci);
rb_warn_keyword_to_last_hash(calling, ci, iseq);
}
else {
- CALLER_SETUP_ARG(cfp, calling, ci);
+ CALLER_REMOVE_EMPTY_KW_SPLAT(cfp, calling, ci);
}
if (arg_setup_type == arg_setup_block &&
@@ -3088,7 +3097,7 @@ vm_invoke_symbol_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
{
VALUE val;
int argc;
- CALLER_SETUP_ARG_WITHOUT_KW_SPLAT(ec->cfp, calling, ci);
+ CALLER_SETUP_ARG(ec->cfp, calling, ci);
argc = calling->argc;
val = vm_yield_with_symbol(ec, symbol, argc, STACK_ADDR_FROM_TOP(argc), calling->kw_splat, calling->block_handler);
POPN(argc);
@@ -3104,6 +3113,7 @@ vm_invoke_ifunc_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
int argc;
int kw_splat = calling->kw_splat;
CALLER_SETUP_ARG(ec->cfp, calling, ci);
+ CALLER_REMOVE_EMPTY_KW_SPLAT(ec->cfp, calling, ci);
if (kw_splat && !calling->kw_splat) {
kw_splat = 2;
}