summaryrefslogtreecommitdiff
path: root/vm_args.c
diff options
context:
space:
mode:
Diffstat (limited to 'vm_args.c')
-rw-r--r--vm_args.c85
1 files changed, 74 insertions, 11 deletions
diff --git a/vm_args.c b/vm_args.c
index 1a78e96776..b4a8fcb8fb 100644
--- a/vm_args.c
+++ b/vm_args.c
@@ -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;
+}