Skip to content

Commit

Permalink
[Bug #20307] Fix Hash#update to make frozen copy of string keys
Browse files Browse the repository at this point in the history
  • Loading branch information
nobu committed Mar 14, 2024
1 parent cd774f4 commit f36a71e
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 14 deletions.
14 changes: 4 additions & 10 deletions hash.c
Expand Up @@ -3905,19 +3905,10 @@ rb_hash_invert(VALUE hash)
return h;
}

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;
}

Expand All @@ -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;
}
Expand Down
17 changes: 13 additions & 4 deletions test/ruby/test_hash.rb
Expand Up @@ -1268,6 +1268,15 @@ def test_update5
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}
Expand All @@ -1290,21 +1299,21 @@ def test_merge_on_identhash
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
h1 = @cls[7=>8]
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!
Expand Down

0 comments on commit f36a71e

Please sign in to comment.