diff --git a/gc.c b/gc.c index f7b770b4c3..4d8ced7611 100644 --- a/gc.c +++ b/gc.c @@ -7769,6 +7769,41 @@ hash_foreach_replace(st_data_t key, st_data_t value, st_data_t argp, int error) return ST_CONTINUE; } +static int +hash_replace_ref_value(st_data_t *key, st_data_t *value, st_data_t argp, int existing) +{ + rb_objspace_t *objspace = (rb_objspace_t *)argp; + + if (gc_object_moved_p(objspace, (VALUE)*value)) { + *value = rb_gc_location((VALUE)*value); + } + + return ST_CONTINUE; +} + +static int +hash_foreach_replace_value(st_data_t key, st_data_t value, st_data_t argp, int error) +{ + rb_objspace_t *objspace; + + objspace = (rb_objspace_t *)argp; + + if (gc_object_moved_p(objspace, (VALUE)value)) { + return ST_REPLACE; + } + return ST_CONTINUE; +} + +static void +gc_update_tbl_refs(rb_objspace_t * objspace, st_table *tbl) +{ + if (!tbl || tbl->num_entries == 0) return; + + if (st_foreach_with_replace(tbl, hash_foreach_replace_value, hash_replace_ref_value, (st_data_t)objspace)) { + rb_raise(rb_eRuntimeError, "hash modified during iteration"); + } +} + static void gc_update_table_refs(rb_objspace_t * objspace, st_table *tbl) { @@ -8020,7 +8055,7 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj) } if (!RCLASS_EXT(obj)) break; if (RCLASS_IV_TBL(obj)) { - gc_update_table_refs(objspace, RCLASS_IV_TBL(obj)); + gc_update_tbl_refs(objspace, RCLASS_IV_TBL(obj)); } update_class_ext(objspace, RCLASS_EXT(obj)); update_const_tbl(objspace, RCLASS_CONST_TBL(obj));