aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gc.c37
1 files changed, 36 insertions, 1 deletions
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));