зеркало из https://github.com/github/ruby.git
`Hash#dup` for kwsplat arguments
On `f(*a, **kw)` method calls, a rest keyword parameter is identically same Hash object is passed and it should make `#dup`ed Hahs. fix https://bugs.ruby-lang.org/issues/19526
This commit is contained in:
Родитель
7fd53eeb46
Коммит
6462c1a042
14
compile.c
14
compile.c
|
@ -5789,6 +5789,16 @@ check_keyword(const NODE *node)
|
|||
}
|
||||
#endif
|
||||
|
||||
static bool
|
||||
keyword_node_single_splat_p(NODE *kwnode)
|
||||
{
|
||||
RUBY_ASSERT(keyword_node_p(kwnode));
|
||||
|
||||
NODE *node = kwnode->nd_head;
|
||||
return node->nd_head == NULL &&
|
||||
node->nd_next->nd_next == NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
setup_args_core(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
|
||||
int dup_rest, unsigned int *flag_ptr, struct rb_callinfo_kwarg **kwarg_ptr)
|
||||
|
@ -5881,7 +5891,9 @@ setup_args_core(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
|
|||
if (kwnode) {
|
||||
// f(*a, k:1)
|
||||
*flag_ptr |= VM_CALL_KW_SPLAT;
|
||||
*flag_ptr |= VM_CALL_KW_SPLAT_MUT;
|
||||
if (!keyword_node_single_splat_p(kwnode)) {
|
||||
*flag_ptr |= VM_CALL_KW_SPLAT_MUT;
|
||||
}
|
||||
compile_hash(iseq, args, kwnode, TRUE, FALSE);
|
||||
argc += 1;
|
||||
}
|
||||
|
|
|
@ -447,6 +447,16 @@ class TestKeywordArguments < Test::Unit::TestCase
|
|||
assert_equal(false, public_send(:yo, **{}).frozen?)
|
||||
assert_equal_not_same(kw, public_send(:yo, **kw))
|
||||
assert_equal_not_same(h, public_send(:yo, **h))
|
||||
|
||||
def self.yo(*a, **kw) = kw
|
||||
assert_equal_not_same kw, yo(**kw)
|
||||
assert_equal_not_same kw, yo(**kw, **kw)
|
||||
|
||||
singleton_class.send(:remove_method, :yo)
|
||||
def self.yo(opts) = opts
|
||||
assert_equal_not_same h, yo(*[], **h)
|
||||
a = []
|
||||
assert_equal_not_same h, yo(*a, **h)
|
||||
end
|
||||
|
||||
def test_regular_kwsplat
|
||||
|
|
20
vm_args.c
20
vm_args.c
|
@ -450,6 +450,18 @@ ignore_keyword_hash_p(VALUE keyword_hash, const rb_iseq_t * const iseq, unsigned
|
|||
RHASH_EMPTY_P(keyword_hash);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
check_kwrestarg(VALUE keyword_hash, unsigned int *kw_flag)
|
||||
{
|
||||
if (!(*kw_flag & VM_CALL_KW_SPLAT_MUT)) {
|
||||
*kw_flag |= VM_CALL_KW_SPLAT_MUT;
|
||||
return rb_hash_dup(keyword_hash);
|
||||
}
|
||||
else {
|
||||
return keyword_hash;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * const iseq,
|
||||
struct rb_calling_info *const calling,
|
||||
|
@ -528,12 +540,14 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
|
|||
keyword_hash = Qnil;
|
||||
}
|
||||
else if (UNLIKELY(ISEQ_BODY(iseq)->param.flags.ruby2_keywords)) {
|
||||
flag_keyword_hash = keyword_hash;
|
||||
rb_ary_push(args->rest, keyword_hash);
|
||||
converted_keyword_hash = check_kwrestarg(converted_keyword_hash, &kw_flag);
|
||||
flag_keyword_hash = converted_keyword_hash;
|
||||
rb_ary_push(args->rest, converted_keyword_hash);
|
||||
keyword_hash = Qnil;
|
||||
}
|
||||
else if (!ISEQ_BODY(iseq)->param.flags.has_kwrest && !ISEQ_BODY(iseq)->param.flags.has_kw) {
|
||||
rb_ary_push(args->rest, keyword_hash);
|
||||
converted_keyword_hash = check_kwrestarg(converted_keyword_hash, &kw_flag);
|
||||
rb_ary_push(args->rest, converted_keyword_hash);
|
||||
keyword_hash = Qnil;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче