summaryrefslogtreecommitdiff
path: root/class.c
diff options
context:
space:
mode:
authorJohn Hawthorn <[email protected]>2025-05-26 11:48:41 -0700
committerJohn Hawthorn <[email protected]>2025-05-28 11:44:07 -0400
commitd1343e12d253cef9c8af22ff58275d28c484d6f2 (patch)
tree82b03d79298f8fdf55032fe90ec8a66dfc51761d /class.c
parent3935b1c401d00f52b9ee0ce0b58767cc32e280fc (diff)
Use flag for RCLASS_IS_INITIALIZED
Previously we used a flag to set whether a module was uninitialized. When checked whether a class was initialized, we first had to check that it had a non-zero superclass, as well as that it wasn't BasicObject. With the advent of namespaces, RCLASS_SUPER is now an expensive operation, and though we could just check for the prime superclass, we might as well take this opportunity to use a flag so that we can perform the initialized check with as few instructions as possible. It's possible in the future that we could prevent uninitialized classes from being available to the user, but currently there are a few ways to do that.
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/13443
Diffstat (limited to 'class.c')
-rw-r--r--class.c46
1 files changed, 24 insertions, 22 deletions
diff --git a/class.c b/class.c
index b984dcb137..43ccb75de1 100644
--- a/class.c
+++ b/class.c
@@ -42,6 +42,8 @@
* 2: RCLASS_PRIME_CLASSEXT_PRIME_WRITABLE
* This class's prime classext is the only classext and writable from any namespaces.
* If unset, the prime classext is writable only from the root namespace.
+ * 3: RCLASS_IS_INITIALIZED
+ * Class has been initialized.
*/
/* Flags of T_ICLASS
@@ -56,13 +58,13 @@
* 0: RCLASS_IS_ROOT
* The class has been added to the VM roots. Will always be marked and pinned.
* This is done for classes defined from C to allow storing them in global variables.
- * 1: RMODULE_ALLOCATED_BUT_NOT_INITIALIZED
- * Module has not been initialized.
+ * 1: RMODULE_IS_REFINEMENT
+ * Module is used for refinements.
* 2: RCLASS_PRIME_CLASSEXT_PRIME_WRITABLE
* This module's prime classext is the only classext and writable from any namespaces.
* If unset, the prime classext is writable only from the root namespace.
- * 3: RMODULE_IS_REFINEMENT
- * Module is used for refinements.
+ * 3: RCLASS_IS_INITIALIZED
+ * Module has been initialized.
*/
#define METACLASS_OF(k) RBASIC(k)->klass
@@ -746,6 +748,9 @@ rb_class_boot(VALUE super)
class_initialize_method_table(klass);
class_associate_super(klass, super, true);
+ if (super && !UNDEF_P(super)) {
+ rb_class_set_initialized(klass);
+ }
return (VALUE)klass;
}
@@ -903,7 +908,7 @@ class_init_copy_check(VALUE clone, VALUE orig)
if (orig == rb_cBasicObject) {
rb_raise(rb_eTypeError, "can't copy the root class");
}
- if (RCLASS_SUPER(clone) != 0 || clone == rb_cBasicObject) {
+ if (RCLASS_INITIALIZED_P(clone)) {
rb_raise(rb_eTypeError, "already initialized class");
}
if (RCLASS_SINGLETON_P(orig)) {
@@ -976,28 +981,18 @@ copy_tables(VALUE clone, VALUE orig)
static bool ensure_origin(VALUE klass);
-/**
- * If this flag is set, that module is allocated but not initialized yet.
- */
-enum {RMODULE_ALLOCATED_BUT_NOT_INITIALIZED = RUBY_FL_USER1};
-
-static inline bool
-RMODULE_UNINITIALIZED(VALUE module)
-{
- return FL_TEST_RAW(module, RMODULE_ALLOCATED_BUT_NOT_INITIALIZED);
-}
-
void
-rb_module_set_initialized(VALUE mod)
+rb_class_set_initialized(VALUE klass)
{
- FL_UNSET_RAW(mod, RMODULE_ALLOCATED_BUT_NOT_INITIALIZED);
+ RUBY_ASSERT(RB_TYPE_P(klass, T_CLASS) || RB_TYPE_P(klass, T_MODULE));
+ FL_SET_RAW(klass, RCLASS_IS_INITIALIZED);
/* no more re-initialization */
}
void
rb_module_check_initializable(VALUE mod)
{
- if (!RMODULE_UNINITIALIZED(mod)) {
+ if (RCLASS_INITIALIZED_P(mod)) {
rb_raise(rb_eTypeError, "already initialized module");
}
}
@@ -1006,9 +1001,11 @@ rb_module_check_initializable(VALUE mod)
VALUE
rb_mod_init_copy(VALUE clone, VALUE orig)
{
+ /* Only class or module is valid here, but other classes may enter here and
+ * only hit an exception on the OBJ_INIT_COPY checks
+ */
switch (BUILTIN_TYPE(clone)) {
case T_CLASS:
- case T_ICLASS:
class_init_copy_check(clone, orig);
break;
case T_MODULE:
@@ -1019,6 +1016,11 @@ rb_mod_init_copy(VALUE clone, VALUE orig)
}
if (!OBJ_INIT_COPY(clone, orig)) return clone;
+ RUBY_ASSERT(RB_TYPE_P(orig, T_CLASS) || RB_TYPE_P(orig, T_MODULE));
+ RUBY_ASSERT(BUILTIN_TYPE(clone) == BUILTIN_TYPE(orig));
+
+ rb_class_set_initialized(clone);
+
/* cloned flag is refer at constant inline cache
* see vm_get_const_key_cref() in vm_insnhelper.c
*/
@@ -1266,6 +1268,7 @@ make_metaclass(VALUE klass)
super = RCLASS_SUPER(klass);
while (RB_TYPE_P(super, T_ICLASS)) super = RCLASS_SUPER(super);
class_associate_super(metaclass, super ? ENSURE_EIGENCLASS(super) : rb_cClass, true);
+ rb_class_set_initialized(klass);
// Full class ancestry may not have been filled until we reach here.
rb_class_update_superclasses(METACLASS_OF(metaclass));
@@ -1548,7 +1551,6 @@ rb_module_s_alloc(VALUE klass)
{
VALUE mod = class_alloc(T_MODULE, klass);
class_initialize_method_table(mod);
- FL_SET(mod, RMODULE_ALLOCATED_BUT_NOT_INITIALIZED);
return mod;
}
@@ -1672,7 +1674,7 @@ ensure_includable(VALUE klass, VALUE module)
{
rb_class_modify_check(klass);
Check_Type(module, T_MODULE);
- rb_module_set_initialized(module);
+ rb_class_set_initialized(module);
if (!NIL_P(rb_refinement_module_get_refined_class(module))) {
rb_raise(rb_eArgError, "refinement module is not allowed");
}