variable.c: handle cleared fields_obj in genfields cache

[Bug #21547]

Followup: https://github.com/ruby/ruby/pull/14201

When adding an instance variable and the IMEMO/fields need to be
larger, we allocate a new one and clear the old one.

Since the old one may still be in other ec's cache, on a hit we must
check the IMEMO/fields isn't a stale one.
This commit is contained in:
Jean Boussier 2025-08-21 13:24:45 +02:00
parent a837ec0962
commit b6bf44ae0f
4 changed files with 18 additions and 3 deletions

3
depend
View File

@ -6306,6 +6306,7 @@ imemo.$(OBJEXT): $(top_srcdir)/internal/compilers.h
imemo.$(OBJEXT): $(top_srcdir)/internal/gc.h
imemo.$(OBJEXT): $(top_srcdir)/internal/imemo.h
imemo.$(OBJEXT): $(top_srcdir)/internal/namespace.h
imemo.$(OBJEXT): $(top_srcdir)/internal/object.h
imemo.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
imemo.$(OBJEXT): $(top_srcdir)/internal/serial.h
imemo.$(OBJEXT): $(top_srcdir)/internal/set_table.h
@ -7159,7 +7160,6 @@ iseq.$(OBJEXT): $(top_srcdir)/prism/pack.h
iseq.$(OBJEXT): $(top_srcdir)/prism/parser.h
iseq.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
iseq.$(OBJEXT): $(top_srcdir)/prism/prism.h
iseq.$(OBJEXT): $(top_srcdir)/prism/prism.h
iseq.$(OBJEXT): $(top_srcdir)/prism/regexp.h
iseq.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
iseq.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
@ -11676,7 +11676,6 @@ prism/prism.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
prism/prism.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
prism/prism.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
prism/prism.$(OBJEXT): $(top_srcdir)/prism/version.h
prism/prism.$(OBJEXT): $(top_srcdir)/prism/version.h
prism/regexp.$(OBJEXT): $(top_srcdir)/prism/ast.h
prism/regexp.$(OBJEXT): $(top_srcdir)/prism/defines.h
prism/regexp.$(OBJEXT): $(top_srcdir)/prism/encoding.h

View File

@ -3,6 +3,7 @@
#include "id_table.h"
#include "internal.h"
#include "internal/imemo.h"
#include "internal/object.h"
#include "internal/st.h"
#include "vm_callinfo.h"
@ -208,6 +209,8 @@ rb_imemo_fields_clear(VALUE fields_obj)
else {
RBASIC_SET_SHAPE_ID(fields_obj, ROOT_SHAPE_ID);
}
// Invalidate the ec->gen_fields_cache.
RBASIC_CLEAR_CLASS(fields_obj);
}
/* =========================================================================

View File

@ -447,6 +447,19 @@ class TestVariable < Test::Unit::TestCase
assert_equal(%i[α b], b.local_variables)
end
def test_genivar_cache
bug21547 = '[Bug #21547]'
klass = Class.new(Array)
instance = klass.new
instance.instance_variable_set(:@a1, 1)
instance.instance_variable_set(:@a2, 2)
Fiber.new do
instance.instance_variable_set(:@a3, 3)
instance.instance_variable_set(:@a4, 4)
end.resume
assert_equal 4, instance.instance_variable_get(:@a4)
end
private
def with_kwargs_11(v1:, v2:, v3:, v4:, v5:, v6:, v7:, v8:, v9:, v10:, v11:)
local_variables

View File

@ -1247,7 +1247,7 @@ rb_obj_fields(VALUE obj, ID field_name)
generic_fields:
{
rb_execution_context_t *ec = GET_EC();
if (ec->gen_fields_cache.obj == obj) {
if (ec->gen_fields_cache.obj == obj && rb_imemo_fields_owner(ec->gen_fields_cache.fields_obj) == obj) {
fields_obj = ec->gen_fields_cache.fields_obj;
}
else {