diff options
author | Jeremy Evans <[email protected]> | 2022-03-11 13:49:36 -0800 |
---|---|---|
committer | Benoit Daloze <[email protected]> | 2022-04-05 11:42:02 +0200 |
commit | 752c3dad989bb66e4be61911a82fed992067bdc3 (patch) | |
tree | 7e45be9773104da5d4cfcebaa53052e4e631dd80 /vm_args.c | |
parent | 5e7ebc7e6e626db01766294edbe41019b98b2e84 (diff) |
Unflag a splatted flagged hash if the method doesn't use ruby2_keywords
For a method such as:
def foo(*callee_args) end
If this method is called with a flagged hash (created by a method
flagged with ruby2_keywords), this previously passed the hash
through without modification. With this change, it acts as if the
last hash was passed as keywords, so a call to:
foo(*caller_args)
where the last element of caller_args is a flagged hash, will be
treated as:
foo(*caller_args[0...-1], **caller_args[-1])
As a result, inside foo, callee_args[-1] is an unflagged duplicate
of caller_args[-1] (all other elements of callee_args match
caller_args).
Fixes [Bug #18625]
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/5684
Diffstat (limited to 'vm_args.c')
-rw-r--r-- | vm_args.c | 14 |
1 files changed, 13 insertions, 1 deletions
@@ -468,7 +468,9 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co VALUE * const orig_sp = ec->cfp->sp; unsigned int i; VALUE flag_keyword_hash = 0; + VALUE splat_flagged_keyword_hash = 0; VALUE converted_keyword_hash = 0; + VALUE rest_last = 0; vm_check_canary(ec, orig_sp); /* @@ -519,7 +521,6 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co } if (vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT) { - VALUE rest_last = 0; int len; args->rest = locals[--args->argc]; args->rest_index = 0; @@ -530,6 +531,7 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co if (!kw_flag && len > 0) { if (RB_TYPE_P(rest_last, T_HASH) && (((struct RHash *)rest_last)->basic.flags & RHASH_PASS_AS_KEYWORDS)) { + splat_flagged_keyword_hash = rest_last; rest_last = rb_hash_dup(rest_last); kw_flag |= VM_CALL_KW_SPLAT | VM_CALL_KW_SPLAT_MUT; } @@ -658,6 +660,16 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co if (ISEQ_BODY(iseq)->param.flags.has_rest) { args_setup_rest_parameter(args, locals + ISEQ_BODY(iseq)->param.rest_start); + VALUE ary = *(locals + ISEQ_BODY(iseq)->param.rest_start); + VALUE index = RARRAY_LEN(ary) - 1; + if (splat_flagged_keyword_hash && + !ISEQ_BODY(iseq)->param.flags.ruby2_keywords && + !ISEQ_BODY(iseq)->param.flags.has_kw && + !ISEQ_BODY(iseq)->param.flags.has_kwrest && + RARRAY_AREF(ary, index) == splat_flagged_keyword_hash) { + ((struct RHash *)rest_last)->basic.flags &= ~RHASH_PASS_AS_KEYWORDS; + RARRAY_ASET(ary, index, rest_last); + } } if (ISEQ_BODY(iseq)->param.flags.has_kw) { |