Skip to content

Commit 970b18e

Browse files
committed
Clear fields obj when removing
This fixes a bug where the gen_fields_cache could become invalid when the last ivar was removed. Also adds more assertions.
1 parent db94a79 commit 970b18e

File tree

2 files changed

+30
-5
lines changed

2 files changed

+30
-5
lines changed

test/ruby/test_variable.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,20 @@ def test_genivar_cache
515515
assert_equal 4, instance.instance_variable_get(:@a4), bug21547
516516
end
517517

518+
def test_genivar_cache_free
519+
str = +"hello"
520+
str.instance_variable_set(:@x, :old_value)
521+
522+
str.instance_variable_get(:@x) # populate cache
523+
524+
Fiber.new {
525+
str.remove_instance_variable(:@x)
526+
str.instance_variable_set(:@x, :new_value)
527+
}.resume
528+
529+
assert_equal :new_value, str.instance_variable_get(:@x)
530+
end
531+
518532
private
519533
def with_kwargs_11(v1:, v2:, v3:, v4:, v5:, v6:, v7:, v8:, v9:, v10:, v11:)
520534
local_variables

variable.c

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,6 +1236,18 @@ rb_mark_generic_ivar(VALUE obj)
12361236
}
12371237
}
12381238

1239+
VALUE
1240+
rb_obj_fields_generic_uncached(VALUE obj)
1241+
{
1242+
VALUE fields_obj = 0;
1243+
RB_VM_LOCKING() {
1244+
if (!st_lookup(generic_fields_tbl_, (st_data_t)obj, (st_data_t *)&fields_obj)) {
1245+
rb_bug("Object is missing entry in generic_fields_tbl");
1246+
}
1247+
}
1248+
return fields_obj;
1249+
}
1250+
12391251
VALUE
12401252
rb_obj_fields(VALUE obj, ID field_name)
12411253
{
@@ -1263,13 +1275,10 @@ rb_obj_fields(VALUE obj, ID field_name)
12631275
rb_execution_context_t *ec = GET_EC();
12641276
if (ec->gen_fields_cache.obj == obj && rb_imemo_fields_owner(ec->gen_fields_cache.fields_obj) == obj) {
12651277
fields_obj = ec->gen_fields_cache.fields_obj;
1278+
RUBY_ASSERT(fields_obj == rb_obj_fields_generic_uncached(obj));
12661279
}
12671280
else {
1268-
RB_VM_LOCKING() {
1269-
if (!st_lookup(generic_fields_tbl_, (st_data_t)obj, (st_data_t *)&fields_obj)) {
1270-
rb_bug("Object is missing entry in generic_fields_tbl");
1271-
}
1272-
}
1281+
fields_obj = rb_obj_fields_generic_uncached(obj);
12731282
ec->gen_fields_cache.fields_obj = fields_obj;
12741283
ec->gen_fields_cache.obj = obj;
12751284
}
@@ -1320,7 +1329,9 @@ rb_obj_set_fields(VALUE obj, VALUE fields_obj, ID field_name, VALUE original_fie
13201329
ivar_ractor_check(obj, field_name);
13211330

13221331
if (!fields_obj) {
1332+
RUBY_ASSERT(original_fields_obj);
13231333
rb_free_generic_ivar(obj);
1334+
rb_imemo_fields_clear(original_fields_obj);
13241335
return;
13251336
}
13261337

0 commit comments

Comments
 (0)