diff --git a/gc.c b/gc.c index 449dd03ec4..f5f510c979 100644 --- a/gc.c +++ b/gc.c @@ -7259,6 +7259,8 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj) gc_mark(objspace, RCLASS_IVPTR(obj)[i]); } mark_const_tbl(objspace, RCLASS_CONST_TBL(obj)); + + gc_mark(objspace, RCLASS_EXT(obj)->classpath); break; case T_ICLASS: @@ -10559,6 +10561,8 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj) update_class_ext(objspace, RCLASS_EXT(obj)); update_const_tbl(objspace, RCLASS_CONST_TBL(obj)); + + UPDATE_IF_MOVED(objspace, RCLASS_EXT(obj)->classpath); break; case T_ICLASS: diff --git a/internal/class.h b/internal/class.h index 2af1a52666..92e018f759 100644 --- a/internal/class.h +++ b/internal/class.h @@ -59,6 +59,8 @@ struct rb_classext_struct { #endif uint32_t max_iv_count; unsigned char variation_count; + bool permanent_classpath; + VALUE classpath; }; typedef struct rb_classext_struct rb_classext_t; @@ -73,8 +75,10 @@ struct RClass { #endif }; -typedef struct rb_subclass_entry rb_subclass_entry_t; -typedef struct rb_classext_struct rb_classext_t; +#if RCLASS_EXT_EMBEDDED +// Assert that classes can be embedded in size_pools[2] (which has 160B slot size) +STATIC_ASSERT(sizeof_rb_classext_t, sizeof(struct RClass) + sizeof(rb_classext_t) <= 4 * RVALUE_SIZE); +#endif #if RCLASS_EXT_EMBEDDED # define RCLASS_EXT(c) ((rb_classext_t *)((char *)(c) + sizeof(struct RClass))) @@ -184,4 +188,14 @@ RCLASS_SET_SUPER(VALUE klass, VALUE super) return super; } +static inline void +RCLASS_SET_CLASSPATH(VALUE klass, VALUE classpath, bool permanent) +{ + assert(BUILTIN_TYPE(klass) == T_CLASS || BUILTIN_TYPE(klass) == T_MODULE); + assert(classpath == 0 || BUILTIN_TYPE(classpath) == T_STRING); + + RB_OBJ_WRITE(klass, &(RCLASS_EXT(klass)->classpath), classpath); + RCLASS_EXT(klass)->permanent_classpath = permanent; +} + #endif /* INTERNAL_CLASS_H */ diff --git a/spec/ruby/optional/capi/object_spec.rb b/spec/ruby/optional/capi/object_spec.rb index 25a43d8908..9efc892202 100644 --- a/spec/ruby/optional/capi/object_spec.rb +++ b/spec/ruby/optional/capi/object_spec.rb @@ -993,13 +993,19 @@ describe "CApiObject" do end it "calls the callback function for each cvar and ivar on a class" do + exp = [:@@cvar, :foo, :@@cvar2, :bar, :@ivar, :baz] + exp.unshift(:__classpath__, 'CApiObjectSpecs::CVars') if RUBY_VERSION < "3.3" + ary = @o.rb_ivar_foreach(CApiObjectSpecs::CVars) - ary.should == [:__classpath__, 'CApiObjectSpecs::CVars', :@@cvar, :foo, :@@cvar2, :bar, :@ivar, :baz] + ary.should == exp end it "calls the callback function for each cvar and ivar on a module" do + exp = [:@@mvar, :foo, :@@mvar2, :bar, :@ivar, :baz] + exp.unshift(:__classpath__, 'CApiObjectSpecs::MVars') if RUBY_VERSION < "3.3" + ary = @o.rb_ivar_foreach(CApiObjectSpecs::MVars) - ary.should == [:__classpath__, 'CApiObjectSpecs::MVars', :@@mvar, :foo, :@@mvar2, :bar, :@ivar, :baz] + ary.should == exp end end diff --git a/variable.c b/variable.c index 0e6f449f91..f297900cf0 100644 --- a/variable.c +++ b/variable.c @@ -46,7 +46,7 @@ RUBY_EXTERN rb_serial_t ruby_vm_global_cvar_state; typedef void rb_gvar_compact_t(void *var); static struct rb_id_table *rb_global_tbl; -static ID autoload, classpath, tmp_classpath; +static ID autoload; // This hash table maps file paths to loadable features. We use this to track // autoload state until it's no longer needed. @@ -75,10 +75,6 @@ Init_var_tables(void) rb_global_tbl = rb_id_table_create(0); generic_iv_tbl_ = st_init_numtable(); autoload = rb_intern_const("__autoload__"); - /* __classpath__: fully qualified class path */ - classpath = rb_intern_const("__classpath__"); - /* __tmp_classpath__: temporary class path which contains anonymous names */ - tmp_classpath = rb_intern_const("__tmp_classpath__"); autoload_mutex = rb_mutex_new(); rb_obj_hide(autoload_mutex); @@ -109,17 +105,16 @@ rb_namespace_p(VALUE obj) * Ruby level APIs that can change a permanent +classpath+. */ static VALUE -classname(VALUE klass, int *permanent) +classname(VALUE klass, bool *permanent) { - *permanent = 0; + *permanent = false; - VALUE classpathv = rb_ivar_lookup(klass, classpath, Qnil); - if (RTEST(classpathv)) { - *permanent = 1; - return classpathv; - } + VALUE classpath = RCLASS_EXT(klass)->classpath; + if (classpath == 0) return Qnil; - return rb_ivar_lookup(klass, tmp_classpath, Qnil);; + *permanent = RCLASS_EXT(klass)->permanent_classpath; + + return classpath; } /* @@ -132,7 +127,7 @@ classname(VALUE klass, int *permanent) VALUE rb_mod_name(VALUE mod) { - int permanent; + bool permanent; return classname(mod, &permanent); } @@ -158,7 +153,7 @@ make_temporary_path(VALUE obj, VALUE klass) typedef VALUE (*fallback_func)(VALUE obj, VALUE name); static VALUE -rb_tmp_class_path(VALUE klass, int *permanent, fallback_func fallback) +rb_tmp_class_path(VALUE klass, bool *permanent, fallback_func fallback) { VALUE path = classname(klass, permanent); @@ -171,11 +166,11 @@ rb_tmp_class_path(VALUE klass, int *permanent, fallback_func fallback) path = Qfalse; } else { - int perm; + bool perm; path = rb_tmp_class_path(RBASIC(klass)->klass, &perm, fallback); } } - *permanent = 0; + *permanent = false; return fallback(klass, path); } } @@ -183,7 +178,7 @@ rb_tmp_class_path(VALUE klass, int *permanent, fallback_func fallback) VALUE rb_class_path(VALUE klass) { - int permanent; + bool permanent; VALUE path = rb_tmp_class_path(klass, &permanent, make_temporary_path); if (!NIL_P(path)) path = rb_str_dup(path); return path; @@ -204,7 +199,7 @@ no_fallback(VALUE obj, VALUE name) VALUE rb_search_class_path(VALUE klass) { - int permanent; + bool permanent; return rb_tmp_class_path(klass, &permanent, no_fallback); } @@ -226,21 +221,18 @@ build_const_path(VALUE head, ID tail) void rb_set_class_path_string(VALUE klass, VALUE under, VALUE name) { - VALUE str; - ID pathid = classpath; + bool permanent = true; + VALUE str; if (under == rb_cObject) { str = rb_str_new_frozen(name); } else { - int permanent; str = rb_tmp_class_path(under, &permanent, make_temporary_path); str = build_const_pathname(str, name); - if (!permanent) { - pathid = tmp_classpath; - } } - rb_ivar_set(klass, pathid, str); + + RCLASS_SET_CLASSPATH(klass, str, permanent); } void @@ -311,7 +303,7 @@ rb_class_name(VALUE klass) const char * rb_class2name(VALUE klass) { - int permanent; + bool permanent; VALUE path = rb_tmp_class_path(rb_class_real(klass), &permanent, make_temporary_path); if (NIL_P(path)) return NULL; return RSTRING_PTR(path); @@ -3217,17 +3209,21 @@ set_namespace_path_i(ID id, VALUE v, void *payload) { rb_const_entry_t *ce = (rb_const_entry_t *)v; VALUE value = ce->value; - int has_permanent_classpath; VALUE parental_path = *((VALUE *) payload); if (!rb_is_const_id(id) || !rb_namespace_p(value)) { return ID_TABLE_CONTINUE; } + + bool has_permanent_classpath; classname(value, &has_permanent_classpath); if (has_permanent_classpath) { return ID_TABLE_CONTINUE; } set_namespace_path(value, build_const_path(parental_path, id)); - rb_attr_delete(value, tmp_classpath); + + if (!RCLASS_EXT(value)->permanent_classpath) { + RCLASS_SET_CLASSPATH(value, 0, false); + } return ID_TABLE_CONTINUE; } @@ -3244,7 +3240,8 @@ set_namespace_path(VALUE named_namespace, VALUE namespace_path) RB_VM_LOCK_ENTER(); { - rb_class_ivar_set(named_namespace, classpath, namespace_path); + RCLASS_SET_CLASSPATH(named_namespace, namespace_path, true); + if (const_table) { rb_id_table_foreach(const_table, set_namespace_path_i, &namespace_path); } @@ -3304,24 +3301,24 @@ const_set(VALUE klass, ID id, VALUE val) * and avoid order-dependency on const_tbl */ if (rb_cObject && rb_namespace_p(val)) { - int val_path_permanent; + bool val_path_permanent; VALUE val_path = classname(val, &val_path_permanent); if (NIL_P(val_path) || !val_path_permanent) { if (klass == rb_cObject) { set_namespace_path(val, rb_id2str(id)); } else { - int parental_path_permanent; + bool parental_path_permanent; VALUE parental_path = classname(klass, &parental_path_permanent); if (NIL_P(parental_path)) { - int throwaway; + bool throwaway; parental_path = rb_tmp_class_path(klass, &throwaway, make_temporary_path); } if (parental_path_permanent && !val_path_permanent) { set_namespace_path(val, build_const_path(parental_path, id)); } else if (!parental_path_permanent && NIL_P(val_path)) { - ivar_set(val, tmp_classpath, build_const_path(parental_path, id)); + RCLASS_SET_CLASSPATH(val, build_const_path(parental_path, id), false); } } }