diff options
Diffstat (limited to 'vm_args.c')
-rw-r--r-- | vm_args.c | 85 |
1 files changed, 74 insertions, 11 deletions
@@ -524,10 +524,9 @@ static inline int ignore_keyword_hash_p(VALUE keyword_hash, const rb_iseq_t * const iseq, unsigned int * kw_flag, VALUE * converted_keyword_hash) { if (keyword_hash == Qnil) { - return 1; + goto ignore; } - - if (!RB_TYPE_P(keyword_hash, T_HASH)) { + else if (!RB_TYPE_P(keyword_hash, T_HASH)) { keyword_hash = rb_to_hash_type(keyword_hash); } else if (UNLIKELY(ISEQ_BODY(iseq)->param.flags.anon_kwrest)) { @@ -543,9 +542,17 @@ ignore_keyword_hash_p(VALUE keyword_hash, const rb_iseq_t * const iseq, unsigned keyword_hash = rb_hash_dup(keyword_hash); } *converted_keyword_hash = keyword_hash; - return !(ISEQ_BODY(iseq)->param.flags.has_kw) && - !(ISEQ_BODY(iseq)->param.flags.has_kwrest) && - RHASH_EMPTY_P(keyword_hash); + + if (!(ISEQ_BODY(iseq)->param.flags.has_kw) && + !(ISEQ_BODY(iseq)->param.flags.has_kwrest) && + RHASH_EMPTY_P(keyword_hash)) { + ignore: + *kw_flag &= ~(VM_CALL_KW_SPLAT | VM_CALL_KW_SPLAT_MUT); + return 1; + } + else { + return 0; + } } static VALUE @@ -684,17 +691,21 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co if (RB_TYPE_P(rest_last, T_HASH) && FL_TEST_RAW(rest_last, RHASH_PASS_AS_KEYWORDS)) { // def f(**kw); a = [..., kw]; g(*a) splat_flagged_keyword_hash = rest_last; - rest_last = rb_hash_dup(rest_last); + if (!RHASH_EMPTY_P(rest_last) || (ISEQ_BODY(iseq)->param.flags.has_kwrest)) { + rest_last = rb_hash_dup(rest_last); + } kw_flag |= VM_CALL_KW_SPLAT | VM_CALL_KW_SPLAT_MUT; // Unset rest_dupped set by anon_rest as we may need to modify splat in this case args->rest_dupped = false; if (ignore_keyword_hash_p(rest_last, iseq, &kw_flag, &converted_keyword_hash)) { - arg_rest_dup(args); - rb_ary_pop(args->rest); + if (ISEQ_BODY(iseq)->param.flags.has_rest || arg_setup_type != arg_setup_method) { + // Only duplicate/modify splat array if it will be used + arg_rest_dup(args); + rb_ary_pop(args->rest); + } given_argc--; - kw_flag &= ~(VM_CALL_KW_SPLAT | VM_CALL_KW_SPLAT_MUT); } else { if (rest_last != converted_keyword_hash) { @@ -725,7 +736,6 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co if (ignore_keyword_hash_p(last_arg, iseq, &kw_flag, &converted_keyword_hash)) { args->argc--; given_argc--; - kw_flag &= ~(VM_CALL_KW_SPLAT | VM_CALL_KW_SPLAT_MUT); } else { if (!(kw_flag & VM_CALL_KW_SPLAT_MUT) && !ISEQ_BODY(iseq)->param.flags.has_kw) { @@ -1047,3 +1057,56 @@ vm_caller_setup_arg_block(const rb_execution_context_t *ec, rb_control_frame_t * } } } + +static void vm_adjust_stack_forwarding(const struct rb_execution_context_struct *ec, struct rb_control_frame_struct *cfp, int argc, VALUE splat); + +static VALUE +vm_caller_setup_fwd_args(const rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, + CALL_DATA cd, const rb_iseq_t *blockiseq, const int is_super, + struct rb_forwarding_call_data *adjusted_cd, struct rb_callinfo *adjusted_ci) +{ + CALL_INFO site_ci = cd->ci; + VALUE bh = Qundef; + + RUBY_ASSERT(ISEQ_BODY(ISEQ_BODY(GET_ISEQ())->local_iseq)->param.flags.forwardable); + CALL_INFO caller_ci = (CALL_INFO)TOPN(0); + + unsigned int site_argc = vm_ci_argc(site_ci); + unsigned int site_flag = vm_ci_flag(site_ci); + ID site_mid = vm_ci_mid(site_ci); + + unsigned int caller_argc = vm_ci_argc(caller_ci); + unsigned int caller_flag = vm_ci_flag(caller_ci); + const struct rb_callinfo_kwarg * kw = vm_ci_kwarg(caller_ci); + + VALUE splat = Qfalse; + + if (site_flag & VM_CALL_ARGS_SPLAT) { + // If we're called with args_splat, the top 1 should be an array + splat = TOPN(1); + site_argc += (RARRAY_LEN(splat) - 1); + } + + // Need to setup the block in case of e.g. `super { :block }` + if (is_super && blockiseq) { + bh = vm_caller_setup_arg_block(ec, GET_CFP(), site_ci, blockiseq, is_super); + } + else { + bh = VM_ENV_BLOCK_HANDLER(GET_LEP()); + } + + vm_adjust_stack_forwarding(ec, GET_CFP(), caller_argc, splat); + + *adjusted_ci = VM_CI_ON_STACK( + site_mid, + (caller_flag | (site_flag & (VM_CALL_FCALL | VM_CALL_FORWARDING))), + site_argc + caller_argc, + kw + ); + + adjusted_cd->cd.ci = adjusted_ci; + adjusted_cd->cd.cc = cd->cc; + adjusted_cd->caller_ci = caller_ci; + + return bh; +} |