summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNobuyoshi Nakada <nobu@ruby-lang.org>2024-02-27 01:38:11 +0900
committerNobuyoshi Nakada <nobu@ruby-lang.org>2024-03-14 17:49:37 +0900
commitf36a71e26995b69ff72bc132bbcf40ad89571414 (patch)
treed494a8119b7f1c0f0e3d384c7128b3c2ffad93b4
parentcd774f4ab916290128924a3eb121b9e56fb587d2 (diff)
[Bug #20307] Fix `Hash#update` to make frozen copy of string keys
-rw-r--r--hash.c14
-rw-r--r--test/ruby/test_hash.rb17
2 files changed, 17 insertions, 14 deletions
diff --git a/hash.c b/hash.c
index f8c84652cd..f34f64065b 100644
--- a/hash.c
+++ b/hash.c
@@ -3906,18 +3906,9 @@ rb_hash_invert(VALUE hash)
}
static int
-rb_hash_update_callback(st_data_t *key, st_data_t *value, struct update_arg *arg, int existing)
-{
- *value = arg->arg;
- return ST_CONTINUE;
-}
-
-NOINSERT_UPDATE_CALLBACK(rb_hash_update_callback)
-
-static int
rb_hash_update_i(VALUE key, VALUE value, VALUE hash)
{
- RHASH_UPDATE(hash, key, rb_hash_update_callback, value);
+ rb_hash_aset(hash, key, value);
return ST_CONTINUE;
}
@@ -3929,6 +3920,9 @@ rb_hash_update_block_callback(st_data_t *key, st_data_t *value, struct update_ar
if (existing) {
newvalue = (st_data_t)rb_yield_values(3, (VALUE)*key, (VALUE)*value, (VALUE)newvalue);
}
+ else if (RHASH_STRING_KEY_P(arg->hash, *key) && !RB_OBJ_FROZEN(*key)) {
+ *key = rb_hash_key_str(*key);
+ }
*value = newvalue;
return ST_CONTINUE;
}
diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb
index ba0def0988..f9312e1c3f 100644
--- a/test/ruby/test_hash.rb
+++ b/test/ruby/test_hash.rb
@@ -1268,6 +1268,15 @@ class TestHash < Test::Unit::TestCase
assert_equal(@cls[a: 10, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10], h)
end
+ def test_update_on_identhash
+ key = +'a'
+ i = @cls[].compare_by_identity
+ i[key] = 0
+ h = @cls[].update(i)
+ key.upcase!
+ assert_equal(0, h.fetch('a'))
+ end
+
def test_merge
h1 = @cls[1=>2, 3=>4]
h2 = {1=>3, 5=>7}
@@ -1290,10 +1299,10 @@ class TestHash < Test::Unit::TestCase
expected[7] = 8
h2 = h.merge(7=>8)
assert_equal(expected, h2)
- assert_equal(true, h2.compare_by_identity?)
+ assert_predicate(h2, :compare_by_identity?)
h2 = h.merge({})
assert_equal(h, h2)
- assert_equal(true, h2.compare_by_identity?)
+ assert_predicate(h2, :compare_by_identity?)
h = @cls[]
h.compare_by_identity
@@ -1301,10 +1310,10 @@ class TestHash < Test::Unit::TestCase
h1.compare_by_identity
h2 = h.merge(7=>8)
assert_equal(h1, h2)
- assert_equal(true, h2.compare_by_identity?)
+ assert_predicate(h2, :compare_by_identity?)
h2 = h.merge({})
assert_equal(h, h2)
- assert_equal(true, h2.compare_by_identity?)
+ assert_predicate(h2, :compare_by_identity?)
end
def test_merge!