From cb49400d680894d69534b0b4cd10b085de325e2a Mon Sep 17 00:00:00 2001 From: nagachika Date: Sat, 17 May 2025 15:57:05 +0900 Subject: [PATCH] merge revision(s) 7793b59c8d2a13c124fe276e11723db23facce04: [Backport #21331] [Bug #21331] Prohibit hash modification during stlike loop --- hash.c | 20 ++++++++++++++++++-- test/ruby/test_hash.rb | 8 ++++++++ version.h | 2 +- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/hash.c b/hash.c index 18b0fdb6dc..ec7bd2c9ac 100644 --- a/hash.c +++ b/hash.c @@ -1391,6 +1391,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) { @@ -1402,6 +1403,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) { @@ -3326,6 +3328,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: * hash.transform_values {|value| ... } -> new_hash @@ -3356,7 +3372,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); } @@ -3385,7 +3401,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; diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb index 2d36556953..49aa30b735 100644 --- a/test/ruby/test_hash.rb +++ b/test/ruby/test_hash.rb @@ -1816,6 +1816,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 diff --git a/version.h b/version.h index 1fd402aa40..ffac97951d 100644 --- a/version.h +++ b/version.h @@ -11,7 +11,7 @@ # define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR #define RUBY_VERSION_TEENY 8 #define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR -#define RUBY_PATCHLEVEL 148 +#define RUBY_PATCHLEVEL 149 #include "ruby/version.h" #include "ruby/internal/abi.h"