Skip to content

Commit

Permalink
Copy cvar table on clone
Browse files Browse the repository at this point in the history
When a class with a class variable is cloned we need to also copy the
cvar cache table from the original table to the clone. I found this bug
while working on fixing [Bug #19379]. While this does not fix that bug
directly it is still a required change to fix another bug revealed by
the fix in #7265

This needs to be backported to 3.2.x and 3.1.x.

Co-authored-by: Aaron Patterson <tenderlove@ruby-lang.org>
  • Loading branch information
eileencodes and tenderlove committed Feb 9, 2023
1 parent 0ddf29f commit b9e6580
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 0 deletions.
31 changes: 31 additions & 0 deletions class.c
Expand Up @@ -404,13 +404,44 @@ class_init_copy_check(VALUE clone, VALUE orig)
}
}

struct cvc_table_copy_ctx {
VALUE clone;
struct rb_id_table * new_table;
};

static enum rb_id_table_iterator_result
cvc_table_copy(ID id, VALUE val, void *data) {
struct cvc_table_copy_ctx *ctx = (struct cvc_table_copy_ctx *)data;
struct rb_cvar_class_tbl_entry * orig_entry;
orig_entry = (struct rb_cvar_class_tbl_entry *)val;

struct rb_cvar_class_tbl_entry *ent;

ent = ALLOC(struct rb_cvar_class_tbl_entry);
ent->class_value = ctx->clone;
ent->global_cvar_state = orig_entry->global_cvar_state;
rb_id_table_insert(ctx->new_table, id, (VALUE)ent);

return ID_TABLE_CONTINUE;
}

static void
copy_tables(VALUE clone, VALUE orig)
{
if (RCLASS_CONST_TBL(clone)) {
rb_free_const_table(RCLASS_CONST_TBL(clone));
RCLASS_CONST_TBL(clone) = 0;
}
if (RCLASS_CVC_TBL(orig)) {
struct rb_id_table *rb_cvc_tbl = RCLASS_CVC_TBL(orig);
struct rb_id_table *rb_cvc_tbl_dup = rb_id_table_create(rb_id_table_size(rb_cvc_tbl));

struct cvc_table_copy_ctx ctx;
ctx.clone = clone;
ctx.new_table = rb_cvc_tbl_dup;
rb_id_table_foreach(rb_cvc_tbl, cvc_table_copy, &ctx);
RCLASS_CVC_TBL(clone) = rb_cvc_tbl_dup;
}
RCLASS_M_TBL(clone) = 0;
if (!RB_TYPE_P(clone, T_ICLASS)) {
st_data_t id;
Expand Down
6 changes: 6 additions & 0 deletions test/ruby/test_variable.rb
Expand Up @@ -33,6 +33,12 @@ def ruler4
end
end

Athena = Gods.clone

def test_cloned_classes_copy_cvar_cache
assert_equal "Cronus", Athena.new.ruler0
end

def test_setting_class_variable_on_module_through_inheritance
mod = Module.new
mod.class_variable_set(:@@foo, 1)
Expand Down

0 comments on commit b9e6580

Please sign in to comment.