From c6dd07d66fa469d963d3771e001d45c462f413e3 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Fri, 20 Jun 2025 11:23:10 +0100 Subject: [PATCH] Allocate singleton classes as namespaceable if their parent are --- class.c | 52 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/class.c b/class.c index e29478d9ee..dd0e79bfa9 100644 --- a/class.c +++ b/class.c @@ -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);