summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Evans <code@jeremyevans.net>2023-12-12 16:48:26 -0800
committerJeremy Evans <code@jeremyevans.net>2023-12-14 08:13:43 -0800
commita18819e65fa2dd3135909df81534937dadafb6ab (patch)
tree37719ea0262406323c3d89d1b65b7e1867a0cbe6
parent247ce712fcf93d28ceb359b66dfcf8f76e2c731d (diff)
Fix op asgn method calls passing mutable keyword splats
When passing the keyword splat to [], it cannot be mutable, because mutating the keyword splat inside [] would result in changes to the keyword splat passed to []=.
-rw-r--r--compile.c2
-rw-r--r--test/ruby/test_call.rb21
2 files changed, 22 insertions, 1 deletions
diff --git a/compile.c b/compile.c
index ae127e59c9..048a30c203 100644
--- a/compile.c
+++ b/compile.c
@@ -8908,7 +8908,7 @@ compile_op_asgn1(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
}
ADD_INSN1(ret, node, dupn, INT2FIX(dup_argn));
flag |= asgnflag;
- ADD_SEND_R(ret, node, idAREF, argc, NULL, INT2FIX(flag), keywords);
+ ADD_SEND_R(ret, node, idAREF, argc, NULL, INT2FIX(flag & ~VM_CALL_KW_SPLAT_MUT), keywords);
if (id == idOROP || id == idANDOP) {
/* a[x] ||= y or a[x] &&= y
diff --git a/test/ruby/test_call.rb b/test/ruby/test_call.rb
index f17d5848b9..5993506b5f 100644
--- a/test/ruby/test_call.rb
+++ b/test/ruby/test_call.rb
@@ -281,6 +281,27 @@ class TestCall < Test::Unit::TestCase
assert_equal([:to_a, :to_hash, :to_proc, :to_proc], ary)
end
+ def test_call_op_asgn_keywords_mutable
+ h = Class.new do
+ attr_reader :get, :set
+ def v; yield; [*@get, *@set] end
+ def [](*a, **b, &c)
+ @get = [a.dup, b.dup]
+ a << :splat_modified
+ b[:kw_splat_modified] = true
+ @set = []
+ 3
+ end
+ def []=(*a, **b) @set = [a, b] end
+ end.new
+
+ a = []
+ kw = {}
+ b = lambda{}
+
+ assert_equal([[2], {b: 5}, [2, 4], {b: 5}], h.v{h[*a, 2, b: 5, **kw] += 1})
+ end
+
def test_call_splat_order
bug12860 = '[ruby-core:77701] [Bug# 12860]'
ary = [1, 2]