diff options
Diffstat (limited to 'variable.c')
-rw-r--r-- | variable.c | 92 |
1 files changed, 84 insertions, 8 deletions
diff --git a/variable.c b/variable.c index 5e5f5c4bf4..778778c866 100644 --- a/variable.c +++ b/variable.c @@ -39,6 +39,9 @@ #include "ractor_core.h" #include "vm_sync.h" +RUBY_EXTERN rb_serial_t ruby_vm_global_cvar_state; +#define GET_GLOBAL_CVAR_STATE() (ruby_vm_global_cvar_state) + typedef void rb_gvar_compact_t(void *var); static struct rb_id_table *rb_global_tbl; @@ -3325,6 +3328,30 @@ cvar_overtaken(VALUE front, VALUE target, ID id) } } +static VALUE +find_cvar(VALUE klass, VALUE * front, VALUE * target, ID id) +{ + VALUE v = Qundef; + CVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR(); + if (cvar_lookup_at(klass, id, (&v))) { + if (!*front) { + *front = klass; + } + *target = klass; + } + + for (klass = cvar_front_klass(klass); klass; klass = RCLASS_SUPER(klass)) { + if (cvar_lookup_at(klass, id, (&v))) { + if (!*front) { + *front = klass; + } + *target = klass; + } + } + + return v; +} + #define CVAR_FOREACH_ANCESTORS(klass, v, r) \ for (klass = cvar_front_klass(klass); klass; klass = RCLASS_SUPER(klass)) { \ if (cvar_lookup_at(klass, id, (v))) { \ @@ -3338,6 +3365,20 @@ cvar_overtaken(VALUE front, VALUE target, ID id) CVAR_FOREACH_ANCESTORS(klass, v, r);\ } while(0) +static void +check_for_cvar_table(VALUE subclass, VALUE key) +{ + st_table *tbl = RCLASS_IV_TBL(subclass); + + if (tbl && st_lookup(tbl, key, NULL)) { + RB_DEBUG_COUNTER_INC(cvar_class_invalidate); + ruby_vm_global_cvar_state++; + return; + } + + rb_class_foreach_subclass(subclass, check_for_cvar_table, key); +} + void rb_cvar_set(VALUE klass, ID id, VALUE val) { @@ -3357,26 +3398,61 @@ rb_cvar_set(VALUE klass, ID id, VALUE val) } check_before_mod_set(target, id, val, "class variable"); - rb_class_ivar_set(target, id, val); + int result = rb_class_ivar_set(target, id, val); + + struct rb_id_table *rb_cvc_tbl = RCLASS_CVC_TBL(target); + + if (!rb_cvc_tbl) { + rb_cvc_tbl = RCLASS_CVC_TBL(target) = rb_id_table_create(2); + } + + struct rb_cvar_class_tbl_entry *ent; + + if (!rb_id_table_lookup(rb_cvc_tbl, id, (VALUE*)&ent)) { + ent = ALLOC(struct rb_cvar_class_tbl_entry); + ent->class_value = target; + ent->global_cvar_state = GET_GLOBAL_CVAR_STATE(); + rb_id_table_insert(rb_cvc_tbl, id, (VALUE)ent); + RB_DEBUG_COUNTER_INC(cvar_inline_miss); + } else { + ent->global_cvar_state = GET_GLOBAL_CVAR_STATE(); + } + + // Break the cvar cache if this is a new class variable + // and target is a module or a subclass with the same + // cvar in this lookup. + if (result == 0) { + if (RB_TYPE_P(target, T_CLASS)) { + if (RCLASS_SUBCLASSES(target)) { + rb_class_foreach_subclass(target, check_for_cvar_table, id); + } + } + } } VALUE -rb_cvar_get(VALUE klass, ID id) +rb_cvar_find(VALUE klass, ID id, VALUE *front) { - VALUE tmp, front = 0, target = 0; - st_data_t value; + VALUE target = 0; + VALUE value; - tmp = klass; - CVAR_LOOKUP(&value, {if (!front) front = klass; target = klass;}); + value = find_cvar(klass, front, &target, id); if (!target) { rb_name_err_raise("uninitialized class variable %1$s in %2$s", - tmp, ID2SYM(id)); + klass, ID2SYM(id)); } - cvar_overtaken(front, target, id); + cvar_overtaken(*front, target, id); return (VALUE)value; } VALUE +rb_cvar_get(VALUE klass, ID id) +{ + VALUE front = 0; + return rb_cvar_find(klass, id, &front); +} + +VALUE rb_cvar_defined(VALUE klass, ID id) { if (!klass) return Qfalse; |