[Bug #21331] Prohibit hash modification during stlike loop

This commit is contained in:
Nobuyoshi Nakada 2025-05-14 10:37:59 +09:00
parent 7f5b4fb26e
commit 7793b59c8d
No known key found for this signature in database
GPG Key ID: 3582D74E1FEE4465
Notes: git 2025-05-14 02:42:50 +00:00
2 changed files with 26 additions and 2 deletions

20
hash.c
View File

@ -1404,6 +1404,7 @@ hash_foreach_ensure(VALUE hash)
return 0;
}
/* This does not manage iteration level */
int
rb_hash_stlike_foreach(VALUE hash, st_foreach_callback_func *func, st_data_t arg)
{
@ -1415,6 +1416,7 @@ rb_hash_stlike_foreach(VALUE hash, st_foreach_callback_func *func, st_data_t arg
}
}
/* This does not manage iteration level */
int
rb_hash_stlike_foreach_with_replace(VALUE hash, st_foreach_check_callback_func *func, st_update_callback_func *replace, st_data_t arg)
{
@ -3486,6 +3488,20 @@ transform_values_foreach_replace(st_data_t *key, st_data_t *value, st_data_t arg
return ST_CONTINUE;
}
static VALUE
transform_values_call(VALUE hash)
{
rb_hash_stlike_foreach_with_replace(hash, transform_values_foreach_func, transform_values_foreach_replace, hash);
return hash;
}
static void
transform_values(VALUE hash)
{
hash_iter_lev_inc(hash);
rb_ensure(transform_values_call, hash, hash_foreach_ensure, hash);
}
/*
* call-seq:
* transform_values {|value| ... } -> new_hash
@ -3514,7 +3530,7 @@ rb_hash_transform_values(VALUE hash)
SET_DEFAULT(result, Qnil);
if (!RHASH_EMPTY_P(hash)) {
rb_hash_stlike_foreach_with_replace(result, transform_values_foreach_func, transform_values_foreach_replace, result);
transform_values(result);
compact_after_delete(result);
}
@ -3549,7 +3565,7 @@ rb_hash_transform_values_bang(VALUE hash)
rb_hash_modify_check(hash);
if (!RHASH_TABLE_EMPTY_P(hash)) {
rb_hash_stlike_foreach_with_replace(hash, transform_values_foreach_func, transform_values_foreach_replace, hash);
transform_values(hash);
}
return hash;

View File

@ -1853,6 +1853,14 @@ class TestHash < Test::Unit::TestCase
end
end
assert_equal(@cls[a: 2, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10], x)
x = (1..1337).to_h {|k| [k, k]}
assert_raise_with_message(RuntimeError, /rehash during iteration/) do
x.transform_values! {|v|
x.rehash if v == 1337
v * 2
}
end
end
def hrec h, n, &b