Allocate singleton classes as namespaceable if their parent are

This commit is contained in:
Jean Boussier 2025-06-20 11:23:10 +01:00
parent 32ee3fab0a
commit c6dd07d66f

52
class.c
View file

@ -644,14 +644,18 @@ class_switch_superclass(VALUE super, VALUE klass)
* @note this function is not Class#allocate.
*/
static VALUE
class_alloc(enum ruby_value_type type, VALUE klass)
class_alloc0(enum ruby_value_type type, VALUE klass, bool namespaceable)
{
rb_ns_subclasses_t *ns_subclasses;
rb_subclass_anchor_t *anchor;
const rb_namespace_t *ns = rb_definition_namespace();
size_t alloc_size = sizeof(struct RClass_and_rb_classext_t);
if (!ruby_namespace_init_done) {
namespaceable = true;
}
size_t alloc_size = sizeof(struct RClass_and_rb_classext_t);
if (namespaceable) {
alloc_size = sizeof(struct RClass_namespaceable);
}
@ -672,7 +676,7 @@ class_alloc(enum ruby_value_type type, VALUE klass)
VALUE flags = type;
if (RGENGC_WB_PROTECTED_CLASS) flags |= FL_WB_PROTECTED;
if (!ruby_namespace_init_done) flags |= RCLASS_NAMESPACEABLE;
if (namespaceable) flags |= RCLASS_NAMESPACEABLE;
NEWOBJ_OF(obj, struct RClass, klass, flags, alloc_size, 0);
@ -688,7 +692,7 @@ class_alloc(enum ruby_value_type type, VALUE klass)
RCLASS_PRIME_NS((VALUE)obj) = ns;
// Classes/Modules defined in user namespaces are
// writable directly because it exists only in a namespace.
RCLASS_SET_PRIME_CLASSEXT_WRITABLE((VALUE)obj, ruby_namespace_init_done || NAMESPACE_USER_P(ns));
RCLASS_SET_PRIME_CLASSEXT_WRITABLE((VALUE)obj, !namespaceable || NAMESPACE_USER_P(ns));
RCLASS_SET_ORIGIN((VALUE)obj, (VALUE)obj);
RCLASS_SET_REFINED_CLASS((VALUE)obj, Qnil);
@ -698,6 +702,12 @@ class_alloc(enum ruby_value_type type, VALUE klass)
return (VALUE)obj;
}
static VALUE
class_alloc(enum ruby_value_type type, VALUE klass)
{
return class_alloc0(type, klass, false);
}
static VALUE
class_associate_super(VALUE klass, VALUE super, bool init)
{
@ -733,6 +743,23 @@ class_clear_method_table(VALUE c)
RCLASS_WRITE_M_TBL_EVEN_WHEN_PROMOTED(c, rb_id_table_create(0));
}
static VALUE
class_boot_namespaceable(VALUE super, bool namespaceable)
{
VALUE klass = class_alloc0(T_CLASS, rb_cClass, namespaceable);
// initialize method table prior to class_associate_super()
// because class_associate_super() may cause GC and promote klass
class_initialize_method_table(klass);
class_associate_super(klass, super, true);
if (super && !UNDEF_P(super)) {
rb_class_set_initialized(klass);
}
return (VALUE)klass;
}
/**
* A utility function that wraps class_alloc.
*
@ -745,18 +772,7 @@ class_clear_method_table(VALUE c)
VALUE
rb_class_boot(VALUE super)
{
VALUE klass = class_alloc(T_CLASS, rb_cClass);
// initialize method table prior to class_associate_super()
// because class_associate_super() may cause GC and promote klass
class_initialize_method_table(klass);
class_associate_super(klass, super, true);
if (super && !UNDEF_P(super)) {
rb_class_set_initialized(klass);
}
return (VALUE)klass;
return class_boot_namespaceable(super, false);
}
static VALUE *
@ -1254,7 +1270,7 @@ static inline VALUE
make_metaclass(VALUE klass)
{
VALUE super;
VALUE metaclass = rb_class_boot(Qundef);
VALUE metaclass = class_boot_namespaceable(Qundef, FL_TEST_RAW(klass, RCLASS_NAMESPACEABLE));
FL_SET(metaclass, FL_SINGLETON);
rb_singleton_class_attached(metaclass, klass);
@ -1290,7 +1306,7 @@ static inline VALUE
make_singleton_class(VALUE obj)
{
VALUE orig_class = METACLASS_OF(obj);
VALUE klass = rb_class_boot(orig_class);
VALUE klass = class_boot_namespaceable(orig_class, FL_TEST_RAW(orig_class, RCLASS_NAMESPACEABLE));
FL_SET(klass, FL_SINGLETON);
RBASIC_SET_CLASS(obj, klass);