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.
This commit is contained in:
parent
3935b1c401
commit
d1343e12d2
Notes:
git
2025-05-28 15:44:19 +00:00
46
class.c
46
class.c
@ -42,6 +42,8 @@
|
|||||||
* 2: RCLASS_PRIME_CLASSEXT_PRIME_WRITABLE
|
* 2: RCLASS_PRIME_CLASSEXT_PRIME_WRITABLE
|
||||||
* This class's prime classext is the only classext and writable from any namespaces.
|
* 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.
|
* If unset, the prime classext is writable only from the root namespace.
|
||||||
|
* 3: RCLASS_IS_INITIALIZED
|
||||||
|
* Class has been initialized.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Flags of T_ICLASS
|
/* Flags of T_ICLASS
|
||||||
@ -56,13 +58,13 @@
|
|||||||
* 0: RCLASS_IS_ROOT
|
* 0: RCLASS_IS_ROOT
|
||||||
* The class has been added to the VM roots. Will always be marked and pinned.
|
* 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.
|
* This is done for classes defined from C to allow storing them in global variables.
|
||||||
* 1: RMODULE_ALLOCATED_BUT_NOT_INITIALIZED
|
* 1: RMODULE_IS_REFINEMENT
|
||||||
* Module has not been initialized.
|
* Module is used for refinements.
|
||||||
* 2: RCLASS_PRIME_CLASSEXT_PRIME_WRITABLE
|
* 2: RCLASS_PRIME_CLASSEXT_PRIME_WRITABLE
|
||||||
* This module's prime classext is the only classext and writable from any namespaces.
|
* 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.
|
* If unset, the prime classext is writable only from the root namespace.
|
||||||
* 3: RMODULE_IS_REFINEMENT
|
* 3: RCLASS_IS_INITIALIZED
|
||||||
* Module is used for refinements.
|
* Module has been initialized.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define METACLASS_OF(k) RBASIC(k)->klass
|
#define METACLASS_OF(k) RBASIC(k)->klass
|
||||||
@ -746,6 +748,9 @@ rb_class_boot(VALUE super)
|
|||||||
class_initialize_method_table(klass);
|
class_initialize_method_table(klass);
|
||||||
|
|
||||||
class_associate_super(klass, super, true);
|
class_associate_super(klass, super, true);
|
||||||
|
if (super && !UNDEF_P(super)) {
|
||||||
|
rb_class_set_initialized(klass);
|
||||||
|
}
|
||||||
|
|
||||||
return (VALUE)klass;
|
return (VALUE)klass;
|
||||||
}
|
}
|
||||||
@ -903,7 +908,7 @@ class_init_copy_check(VALUE clone, VALUE orig)
|
|||||||
if (orig == rb_cBasicObject) {
|
if (orig == rb_cBasicObject) {
|
||||||
rb_raise(rb_eTypeError, "can't copy the root class");
|
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");
|
rb_raise(rb_eTypeError, "already initialized class");
|
||||||
}
|
}
|
||||||
if (RCLASS_SINGLETON_P(orig)) {
|
if (RCLASS_SINGLETON_P(orig)) {
|
||||||
@ -976,28 +981,18 @@ copy_tables(VALUE clone, VALUE orig)
|
|||||||
|
|
||||||
static bool ensure_origin(VALUE klass);
|
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
|
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 */
|
/* no more re-initialization */
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
rb_module_check_initializable(VALUE mod)
|
rb_module_check_initializable(VALUE mod)
|
||||||
{
|
{
|
||||||
if (!RMODULE_UNINITIALIZED(mod)) {
|
if (RCLASS_INITIALIZED_P(mod)) {
|
||||||
rb_raise(rb_eTypeError, "already initialized module");
|
rb_raise(rb_eTypeError, "already initialized module");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1006,9 +1001,11 @@ rb_module_check_initializable(VALUE mod)
|
|||||||
VALUE
|
VALUE
|
||||||
rb_mod_init_copy(VALUE clone, VALUE orig)
|
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)) {
|
switch (BUILTIN_TYPE(clone)) {
|
||||||
case T_CLASS:
|
case T_CLASS:
|
||||||
case T_ICLASS:
|
|
||||||
class_init_copy_check(clone, orig);
|
class_init_copy_check(clone, orig);
|
||||||
break;
|
break;
|
||||||
case T_MODULE:
|
case T_MODULE:
|
||||||
@ -1019,6 +1016,11 @@ rb_mod_init_copy(VALUE clone, VALUE orig)
|
|||||||
}
|
}
|
||||||
if (!OBJ_INIT_COPY(clone, orig)) return clone;
|
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
|
/* cloned flag is refer at constant inline cache
|
||||||
* see vm_get_const_key_cref() in vm_insnhelper.c
|
* see vm_get_const_key_cref() in vm_insnhelper.c
|
||||||
*/
|
*/
|
||||||
@ -1266,6 +1268,7 @@ make_metaclass(VALUE klass)
|
|||||||
super = RCLASS_SUPER(klass);
|
super = RCLASS_SUPER(klass);
|
||||||
while (RB_TYPE_P(super, T_ICLASS)) super = RCLASS_SUPER(super);
|
while (RB_TYPE_P(super, T_ICLASS)) super = RCLASS_SUPER(super);
|
||||||
class_associate_super(metaclass, super ? ENSURE_EIGENCLASS(super) : rb_cClass, true);
|
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.
|
// Full class ancestry may not have been filled until we reach here.
|
||||||
rb_class_update_superclasses(METACLASS_OF(metaclass));
|
rb_class_update_superclasses(METACLASS_OF(metaclass));
|
||||||
@ -1548,7 +1551,6 @@ rb_module_s_alloc(VALUE klass)
|
|||||||
{
|
{
|
||||||
VALUE mod = class_alloc(T_MODULE, klass);
|
VALUE mod = class_alloc(T_MODULE, klass);
|
||||||
class_initialize_method_table(mod);
|
class_initialize_method_table(mod);
|
||||||
FL_SET(mod, RMODULE_ALLOCATED_BUT_NOT_INITIALIZED);
|
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1672,7 +1674,7 @@ ensure_includable(VALUE klass, VALUE module)
|
|||||||
{
|
{
|
||||||
rb_class_modify_check(klass);
|
rb_class_modify_check(klass);
|
||||||
Check_Type(module, T_MODULE);
|
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))) {
|
if (!NIL_P(rb_refinement_module_get_refined_class(module))) {
|
||||||
rb_raise(rb_eArgError, "refinement module is not allowed");
|
rb_raise(rb_eArgError, "refinement module is not allowed");
|
||||||
}
|
}
|
||||||
|
3
eval.c
3
eval.c
@ -422,7 +422,8 @@ rb_class_modify_check(VALUE klass)
|
|||||||
Check_Type(klass, T_CLASS);
|
Check_Type(klass, T_CLASS);
|
||||||
}
|
}
|
||||||
if (RB_TYPE_P(klass, T_MODULE)) {
|
if (RB_TYPE_P(klass, T_MODULE)) {
|
||||||
rb_module_set_initialized(klass);
|
// TODO: shouldn't this only happen in a few places?
|
||||||
|
rb_class_set_initialized(klass);
|
||||||
}
|
}
|
||||||
if (OBJ_FROZEN(klass)) {
|
if (OBJ_FROZEN(klass)) {
|
||||||
const char *desc;
|
const char *desc;
|
||||||
|
@ -58,7 +58,7 @@ enum ruby_rmodule_flags {
|
|||||||
* rb_mod_refine() has this flag set. This is the bit which controls
|
* rb_mod_refine() has this flag set. This is the bit which controls
|
||||||
* difference between normal inclusion versus refinements.
|
* difference between normal inclusion versus refinements.
|
||||||
*/
|
*/
|
||||||
RMODULE_IS_REFINEMENT = RUBY_FL_USER3
|
RMODULE_IS_REFINEMENT = RUBY_FL_USER1
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RClass; /* Opaque, declared here for RCLASS() macro. */
|
struct RClass; /* Opaque, declared here for RCLASS() macro. */
|
||||||
|
@ -294,8 +294,9 @@ static inline void RCLASS_SET_CLASSPATH(VALUE klass, VALUE classpath, bool perma
|
|||||||
static inline void RCLASS_WRITE_CLASSPATH(VALUE klass, VALUE classpath, bool permanent);
|
static inline void RCLASS_WRITE_CLASSPATH(VALUE klass, VALUE classpath, bool permanent);
|
||||||
|
|
||||||
#define RCLASS_IS_ROOT FL_USER0
|
#define RCLASS_IS_ROOT FL_USER0
|
||||||
// 1 is for RUBY_FL_SINGLETON or RMODULE_ALLOCATED_BUT_NOT_INITIALIZED (see class.c)
|
// 1 is for RUBY_FL_SINGLETON or RMODULE_IS_REFINEMENT
|
||||||
#define RCLASS_PRIME_CLASSEXT_WRITABLE FL_USER2
|
#define RCLASS_PRIME_CLASSEXT_WRITABLE FL_USER2
|
||||||
|
#define RCLASS_IS_INITIALIZED FL_USER3
|
||||||
// 3 is RMODULE_IS_REFINEMENT for RMODULE
|
// 3 is RMODULE_IS_REFINEMENT for RMODULE
|
||||||
// 4-19: SHAPE_FLAG_MASK
|
// 4-19: SHAPE_FLAG_MASK
|
||||||
|
|
||||||
@ -485,7 +486,7 @@ VALUE rb_class_set_super(VALUE klass, VALUE super);
|
|||||||
VALUE rb_class_boot(VALUE);
|
VALUE rb_class_boot(VALUE);
|
||||||
VALUE rb_class_s_alloc(VALUE klass);
|
VALUE rb_class_s_alloc(VALUE klass);
|
||||||
VALUE rb_module_s_alloc(VALUE klass);
|
VALUE rb_module_s_alloc(VALUE klass);
|
||||||
void rb_module_set_initialized(VALUE module);
|
void rb_class_set_initialized(VALUE klass);
|
||||||
void rb_module_check_initializable(VALUE module);
|
void rb_module_check_initializable(VALUE module);
|
||||||
VALUE rb_make_metaclass(VALUE, VALUE);
|
VALUE rb_make_metaclass(VALUE, VALUE);
|
||||||
VALUE rb_include_class_new(VALUE, VALUE);
|
VALUE rb_include_class_new(VALUE, VALUE);
|
||||||
@ -796,4 +797,11 @@ RCLASS_SET_CLONED(VALUE klass, bool cloned)
|
|||||||
RCLASSEXT_CLONED(RCLASS_EXT_PRIME(klass)) = cloned;
|
RCLASSEXT_CLONED(RCLASS_EXT_PRIME(klass)) = cloned;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
RCLASS_INITIALIZED_P(VALUE klass)
|
||||||
|
{
|
||||||
|
VM_ASSERT(RB_TYPE_P(klass, T_CLASS) || RB_TYPE_P(klass, T_MODULE));
|
||||||
|
return FL_TEST_RAW(klass, RCLASS_IS_INITIALIZED);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* INTERNAL_CLASS_H */
|
#endif /* INTERNAL_CLASS_H */
|
||||||
|
4
object.c
4
object.c
@ -2069,7 +2069,7 @@ rb_class_initialize(int argc, VALUE *argv, VALUE klass)
|
|||||||
else {
|
else {
|
||||||
super = argv[0];
|
super = argv[0];
|
||||||
rb_check_inheritable(super);
|
rb_check_inheritable(super);
|
||||||
if (super != rb_cBasicObject && !RCLASS_SUPER(super)) {
|
if (!RCLASS_INITIALIZED_P(super)) {
|
||||||
rb_raise(rb_eTypeError, "can't inherit uninitialized class");
|
rb_raise(rb_eTypeError, "can't inherit uninitialized class");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2126,7 +2126,7 @@ class_get_alloc_func(VALUE klass)
|
|||||||
{
|
{
|
||||||
rb_alloc_func_t allocator;
|
rb_alloc_func_t allocator;
|
||||||
|
|
||||||
if (RCLASS_SUPER(klass) == 0 && klass != rb_cBasicObject) {
|
if (!RCLASS_INITIALIZED_P(klass)) {
|
||||||
rb_raise(rb_eTypeError, "can't instantiate uninitialized class");
|
rb_raise(rb_eTypeError, "can't instantiate uninitialized class");
|
||||||
}
|
}
|
||||||
if (RCLASS_SINGLETON_P(klass)) {
|
if (RCLASS_SINGLETON_P(klass)) {
|
||||||
|
2
yjit/src/cruby_bindings.inc.rs
generated
2
yjit/src/cruby_bindings.inc.rs
generated
@ -303,7 +303,7 @@ pub const RARRAY_EMBED_LEN_MASK: ruby_rarray_flags = 4161536;
|
|||||||
pub type ruby_rarray_flags = u32;
|
pub type ruby_rarray_flags = u32;
|
||||||
pub const RARRAY_EMBED_LEN_SHIFT: ruby_rarray_consts = 15;
|
pub const RARRAY_EMBED_LEN_SHIFT: ruby_rarray_consts = 15;
|
||||||
pub type ruby_rarray_consts = u32;
|
pub type ruby_rarray_consts = u32;
|
||||||
pub const RMODULE_IS_REFINEMENT: ruby_rmodule_flags = 32768;
|
pub const RMODULE_IS_REFINEMENT: ruby_rmodule_flags = 8192;
|
||||||
pub type ruby_rmodule_flags = u32;
|
pub type ruby_rmodule_flags = u32;
|
||||||
pub const ROBJECT_EMBED: ruby_robject_flags = 8192;
|
pub const ROBJECT_EMBED: ruby_robject_flags = 8192;
|
||||||
pub type ruby_robject_flags = u32;
|
pub type ruby_robject_flags = u32;
|
||||||
|
2
zjit/src/cruby_bindings.inc.rs
generated
2
zjit/src/cruby_bindings.inc.rs
generated
@ -152,7 +152,7 @@ pub const RARRAY_EMBED_LEN_MASK: ruby_rarray_flags = 4161536;
|
|||||||
pub type ruby_rarray_flags = u32;
|
pub type ruby_rarray_flags = u32;
|
||||||
pub const RARRAY_EMBED_LEN_SHIFT: ruby_rarray_consts = 15;
|
pub const RARRAY_EMBED_LEN_SHIFT: ruby_rarray_consts = 15;
|
||||||
pub type ruby_rarray_consts = u32;
|
pub type ruby_rarray_consts = u32;
|
||||||
pub const RMODULE_IS_REFINEMENT: ruby_rmodule_flags = 32768;
|
pub const RMODULE_IS_REFINEMENT: ruby_rmodule_flags = 8192;
|
||||||
pub type ruby_rmodule_flags = u32;
|
pub type ruby_rmodule_flags = u32;
|
||||||
pub const ROBJECT_EMBED: ruby_robject_flags = 8192;
|
pub const ROBJECT_EMBED: ruby_robject_flags = 8192;
|
||||||
pub type ruby_robject_flags = u32;
|
pub type ruby_robject_flags = u32;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user