diff --git a/ractor.c b/ractor.c index db2d43e91e..62a0dd47e2 100644 --- a/ractor.c +++ b/ractor.c @@ -2739,6 +2739,19 @@ obj_traverse_rec(struct obj_traverse_data *data) return data->rec; } +static int +obj_traverse_ivar_foreach_i(ID key, VALUE val, st_data_t ptr) +{ + struct obj_traverse_callback_data *d = (struct obj_traverse_callback_data *)ptr; + + if (obj_traverse_i(val, d->data)) { + d->stop = true; + return ST_STOP; + } + + return ST_CONTINUE; +} + static int obj_traverse_i(VALUE obj, struct obj_traverse_data *data) { @@ -2755,14 +2768,12 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data) return 0; } - if (UNLIKELY(FL_TEST_RAW(obj, FL_EXIVAR))) { - struct gen_ivtbl *ivtbl; - rb_ivar_generic_ivtbl_lookup(obj, &ivtbl); - for (uint32_t i = 0; i < ivtbl->as.shape.numiv; i++) { - VALUE val = ivtbl->as.shape.ivptr[i]; - if (!UNDEF_P(val) && obj_traverse_i(val, data)) return 1; - } - } + struct obj_traverse_callback_data d = { + .stop = false, + .data = data, + }; + rb_ivar_foreach(obj, obj_traverse_ivar_foreach_i, (st_data_t)&d); + if (d.stop) return 1; switch (BUILTIN_TYPE(obj)) { // no child node @@ -3229,9 +3240,26 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data) if (UNLIKELY(FL_TEST_RAW(obj, FL_EXIVAR))) { struct gen_ivtbl *ivtbl; rb_ivar_generic_ivtbl_lookup(obj, &ivtbl); - for (uint32_t i = 0; i < ivtbl->as.shape.numiv; i++) { - if (!UNDEF_P(ivtbl->as.shape.ivptr[i])) { - CHECK_AND_REPLACE(ivtbl->as.shape.ivptr[i]); + + if (UNLIKELY(rb_shape_obj_too_complex(obj))) { + struct obj_traverse_replace_callback_data d = { + .stop = false, + .data = data, + .src = obj, + }; + rb_st_foreach_with_replace( + ivtbl->as.complex.table, + obj_iv_hash_traverse_replace_foreach_i, + obj_iv_hash_traverse_replace_i, + (st_data_t)&d + ); + if (d.stop) return 1; + } + else { + for (uint32_t i = 0; i < ivtbl->as.shape.numiv; i++) { + if (!UNDEF_P(ivtbl->as.shape.ivptr[i])) { + CHECK_AND_REPLACE(ivtbl->as.shape.ivptr[i]); + } } } } diff --git a/test/ruby/test_shapes.rb b/test/ruby/test_shapes.rb index c4b0d251f1..148595ac32 100644 --- a/test/ruby/test_shapes.rb +++ b/test/ruby/test_shapes.rb @@ -621,6 +621,24 @@ class TestShapes < Test::Unit::TestCase end; end + def test_too_complex_generic_ivar_ractor_share + assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") + begin; + $VERBOSE = nil + + RubyVM::Shape.exhaust_shapes + + r = Ractor.new do + o = [] + o.instance_variable_set(:@a, "hello") + Ractor.yield(o) + end + + o = r.take + assert_equal "hello", o.instance_variable_get(:@a) + end; + end + def test_read_iv_after_complex ensure_complex