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:
parent
7fd53eeb46
commit
6462c1a042
Notes:
git
2023-03-15 09:05:39 +00:00
14
compile.c
14
compile.c
@ -5789,6 +5789,16 @@ check_keyword(const NODE *node)
|
|||||||
}
|
}
|
||||||
#endif
|
#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
|
static int
|
||||||
setup_args_core(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
|
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)
|
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) {
|
if (kwnode) {
|
||||||
// f(*a, k:1)
|
// f(*a, k:1)
|
||||||
*flag_ptr |= VM_CALL_KW_SPLAT;
|
*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);
|
compile_hash(iseq, args, kwnode, TRUE, FALSE);
|
||||||
argc += 1;
|
argc += 1;
|
||||||
}
|
}
|
||||||
|
@ -447,6 +447,16 @@ class TestKeywordArguments < Test::Unit::TestCase
|
|||||||
assert_equal(false, public_send(:yo, **{}).frozen?)
|
assert_equal(false, public_send(:yo, **{}).frozen?)
|
||||||
assert_equal_not_same(kw, public_send(:yo, **kw))
|
assert_equal_not_same(kw, public_send(:yo, **kw))
|
||||||
assert_equal_not_same(h, public_send(:yo, **h))
|
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
|
end
|
||||||
|
|
||||||
def test_regular_kwsplat
|
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);
|
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
|
static int
|
||||||
setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * const iseq,
|
setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * const iseq,
|
||||||
struct rb_calling_info *const calling,
|
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;
|
keyword_hash = Qnil;
|
||||||
}
|
}
|
||||||
else if (UNLIKELY(ISEQ_BODY(iseq)->param.flags.ruby2_keywords)) {
|
else if (UNLIKELY(ISEQ_BODY(iseq)->param.flags.ruby2_keywords)) {
|
||||||
flag_keyword_hash = keyword_hash;
|
converted_keyword_hash = check_kwrestarg(converted_keyword_hash, &kw_flag);
|
||||||
rb_ary_push(args->rest, keyword_hash);
|
flag_keyword_hash = converted_keyword_hash;
|
||||||
|
rb_ary_push(args->rest, converted_keyword_hash);
|
||||||
keyword_hash = Qnil;
|
keyword_hash = Qnil;
|
||||||
}
|
}
|
||||||
else if (!ISEQ_BODY(iseq)->param.flags.has_kwrest && !ISEQ_BODY(iseq)->param.flags.has_kw) {
|
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;
|
keyword_hash = Qnil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user