summaryrefslogtreecommitdiff
path: root/class.c
diff options
context:
space:
mode:
Diffstat (limited to 'class.c')
-rw-r--r--class.c856
1 files changed, 396 insertions, 460 deletions
diff --git a/class.c b/class.c
index 24f61fd023..02078cc9bc 100644
--- a/class.c
+++ b/class.c
@@ -21,16 +21,18 @@
#include "debug_counter.h"
#include "id_table.h"
#include "internal.h"
+#include "internal/box.h"
#include "internal/class.h"
#include "internal/eval.h"
#include "internal/hash.h"
-#include "internal/namespace.h"
#include "internal/object.h"
#include "internal/string.h"
#include "internal/variable.h"
#include "ruby/st.h"
#include "vm_core.h"
+#include "ruby/ractor.h"
#include "yjit.h"
+#include "zjit.h"
/* Flags of T_CLASS
*
@@ -39,22 +41,22 @@
* This is done for classes defined from C to allow storing them in global variables.
* 1: RUBY_FL_SINGLETON
* This class is a singleton class.
- * 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.
+ * 2: RCLASS_PRIME_CLASSEXT_WRITABLE
+ * This class's prime classext is the only classext and writable from any boxes.
+ * If unset, the prime classext is writable only from the root box.
* 3: RCLASS_IS_INITIALIZED
* Class has been initialized.
- * 4: RCLASS_NAMESPACEABLE
- * Is a builtin class that may be namespaced. It larger than a normal class.
+ * 4: RCLASS_BOXABLE
+ * Is a builtin class that may be boxed. It larger than a normal class.
*/
/* Flags of T_ICLASS
*
- * 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.
- * 4: RCLASS_NAMESPACEABLE
- * Is a builtin class that may be namespaced. It larger than a normal class.
+ * 2: RCLASS_PRIME_CLASSEXT_WRITABLE
+ * This module's prime classext is the only classext and writable from any boxes.
+ * If unset, the prime classext is writable only from the root box.
+ * 4: RCLASS_BOXABLE
+ * Is a builtin class that may be boxed. It larger than a normal class.
*/
/* Flags of T_MODULE
@@ -62,35 +64,140 @@
* 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_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.
+ * 1: <reserved>
+ * Ensures that RUBY_FL_SINGLETON is never set on a T_MODULE. See `rb_class_real`.
+ * 2: RCLASS_PRIME_CLASSEXT_WRITABLE
+ * This module's prime classext is the only classext and writable from any boxes.
+ * If unset, the prime classext is writable only from the root box.
* 3: RCLASS_IS_INITIALIZED
* Module has been initialized.
- * 4: RCLASS_NAMESPACEABLE
- * Is a builtin class that may be namespaced. It larger than a normal class.
+ * 4: RCLASS_BOXABLE
+ * Is a builtin class that may be boxed. It larger than a normal class.
+ * 5: RMODULE_IS_REFINEMENT
+ * Module is used for refinements.
*/
#define METACLASS_OF(k) RBASIC(k)->klass
#define SET_METACLASS_OF(k, cls) RBASIC_SET_CLASS(k, cls)
-RUBY_EXTERN rb_serial_t ruby_vm_global_cvar_state;
+rb_classext_t *
+rb_class_unlink_classext(VALUE klass, const rb_box_t *box)
+{
+ st_data_t ext;
+ st_data_t key = (st_data_t)box->box_object;
+ st_delete(box->classext_cow_classes, &klass, 0);
+ st_delete(RCLASS_CLASSEXT_TBL(klass), &key, &ext);
+ return (rb_classext_t *)ext;
+}
-struct duplicate_id_tbl_data {
+void
+rb_class_classext_free(VALUE klass, rb_classext_t *ext, bool is_prime)
+{
struct rb_id_table *tbl;
- VALUE klass;
+
+ rb_id_table_free(RCLASSEXT_M_TBL(ext));
+
+ if (!RCLASSEXT_SHARED_CONST_TBL(ext) && (tbl = RCLASSEXT_CONST_TBL(ext)) != NULL) {
+ rb_free_const_table(tbl);
+ }
+
+ if (RCLASSEXT_SUPERCLASSES_WITH_SELF(ext)) {
+ RUBY_ASSERT(is_prime); // superclasses should only be used on prime
+ size_t depth = RCLASSEXT_SUPERCLASS_DEPTH(ext);
+ if (depth != RCLASS_MAX_SUPERCLASS_DEPTH) {
+ depth++;
+ }
+ SIZED_FREE_N(RCLASSEXT_SUPERCLASSES(ext), depth);
+ }
+
+ if (!is_prime) { // the prime classext will be freed with RClass
+ SIZED_FREE(ext);
+ }
+}
+
+void
+rb_iclass_classext_free(VALUE klass, rb_classext_t *ext, bool is_prime)
+{
+ if (RCLASSEXT_ICLASS_IS_ORIGIN(ext) && !RCLASSEXT_ICLASS_ORIGIN_SHARED_MTBL(ext)) {
+ /* Method table is not shared for origin iclasses of classes */
+ rb_id_table_free(RCLASSEXT_M_TBL(ext));
+ }
+
+ if (RCLASSEXT_CALLABLE_M_TBL(ext) != NULL) {
+ rb_id_table_free(RCLASSEXT_CALLABLE_M_TBL(ext));
+ }
+
+ if (!is_prime) { // the prime classext will be freed with RClass
+ SIZED_FREE(ext);
+ }
+}
+
+static void
+iclass_free_orphan_classext(VALUE klass, rb_classext_t *ext)
+{
+ if (RCLASSEXT_ICLASS_IS_ORIGIN(ext) && !RCLASSEXT_ICLASS_ORIGIN_SHARED_MTBL(ext)) {
+ /* Method table is not shared for origin iclasses of classes */
+ rb_id_table_free(RCLASSEXT_M_TBL(ext));
+ }
+
+ if (RCLASSEXT_CALLABLE_M_TBL(ext) != NULL) {
+ rb_id_table_free(RCLASSEXT_CALLABLE_M_TBL(ext));
+ }
+
+ SIZED_FREE(ext);
+}
+
+struct rb_class_set_box_classext_args {
+ VALUE obj;
+ rb_classext_t *ext;
};
-static enum rb_id_table_iterator_result
-duplicate_classext_id_table_i(ID key, VALUE value, void *data)
+static int
+set_box_classext_update(st_data_t *key_ptr, st_data_t *val_ptr, st_data_t a, int existing)
{
- struct rb_id_table *tbl = (struct rb_id_table *)data;
- rb_id_table_insert(tbl, key, value);
- return ID_TABLE_CONTINUE;
+ struct rb_class_set_box_classext_args *args = (struct rb_class_set_box_classext_args *)a;
+
+ if (existing) {
+ if (LIKELY(BUILTIN_TYPE(args->obj) == T_ICLASS)) {
+ iclass_free_orphan_classext(args->obj, (rb_classext_t *)*val_ptr);
+ }
+ else {
+ rb_bug("Updating existing classext for non-iclass never happen");
+ }
+ }
+
+ *val_ptr = (st_data_t)args->ext;
+
+ return ST_CONTINUE;
}
+void
+rb_class_set_box_classext(VALUE obj, const rb_box_t *box, rb_classext_t *ext)
+{
+ struct rb_class_set_box_classext_args args = {
+ .obj = obj,
+ .ext = ext,
+ };
+
+ VM_ASSERT(BOX_MUTABLE_P(box));
+
+ st_update(RCLASS_CLASSEXT_TBL(obj), (st_data_t)box->box_object, set_box_classext_update, (st_data_t)&args);
+
+ // The classext references are now visible via the classext table,
+ // so we must issue the write barrier before any further allocations
+ // (e.g. st_insert below) that could trigger GC.
+ rb_gc_writebarrier_remember(obj);
+
+ st_insert(box->classext_cow_classes, (st_data_t)obj, 0);
+}
+
+RUBY_EXTERN rb_serial_t ruby_vm_global_cvar_state;
+
+struct duplicate_id_tbl_data {
+ struct rb_id_table *tbl;
+ VALUE klass;
+};
+
static enum rb_id_table_iterator_result
duplicate_classext_m_tbl_i(ID key, VALUE value, void *data)
{
@@ -119,22 +226,6 @@ duplicate_classext_m_tbl(struct rb_id_table *orig, VALUE klass, bool init_missin
return tbl;
}
-static struct rb_id_table *
-duplicate_classext_id_table(struct rb_id_table *orig, bool init_missing)
-{
- struct rb_id_table *tbl;
-
- if (!orig) {
- if (init_missing)
- return rb_id_table_create(0);
- else
- return NULL;
- }
- tbl = rb_id_table_create(rb_id_table_size(orig));
- rb_id_table_foreach(orig, duplicate_classext_id_table_i, tbl);
- return tbl;
-}
-
static rb_const_entry_t *
duplicate_classext_const_entry(rb_const_entry_t *src, VALUE klass)
{
@@ -179,73 +270,13 @@ duplicate_classext_const_tbl(struct rb_id_table *src, VALUE klass)
return dst;
}
-static VALUE
-namespace_subclasses_tbl_key(const rb_namespace_t *ns)
-{
- if (!ns){
- return 0;
- }
- return (VALUE)ns->ns_id;
-}
-
-static void
-duplicate_classext_subclasses(rb_classext_t *orig, rb_classext_t *copy)
-{
- rb_subclass_anchor_t *anchor, *orig_anchor;
- rb_subclass_entry_t *head, *cur, *cdr, *entry, *first = NULL;
- rb_ns_subclasses_t *ns_subclasses;
- struct st_table *tbl;
-
- if (RCLASSEXT_SUBCLASSES(orig)) {
- orig_anchor = RCLASSEXT_SUBCLASSES(orig);
- ns_subclasses = orig_anchor->ns_subclasses;
- tbl = ((rb_ns_subclasses_t *)ns_subclasses)->tbl;
-
- anchor = ZALLOC(rb_subclass_anchor_t);
- anchor->ns_subclasses = rb_ns_subclasses_ref_inc(ns_subclasses);
-
- head = ZALLOC(rb_subclass_entry_t);
- anchor->head = head;
-
- RCLASSEXT_SUBCLASSES(copy) = anchor;
-
- cur = head;
- entry = orig_anchor->head;
- RUBY_ASSERT(!entry->klass);
- // The head entry has NULL klass always. See rb_class_foreach_subclass().
- entry = entry->next;
- while (entry) {
- if (rb_objspace_garbage_object_p(entry->klass)) {
- entry = entry->next;
- continue;
- }
- cdr = ZALLOC(rb_subclass_entry_t);
- cdr->klass = entry->klass;
- cdr->prev = cur;
- cur->next = cdr;
- if (!first) {
- VALUE ns_id = namespace_subclasses_tbl_key(RCLASSEXT_NS(copy));
- first = cdr;
- st_insert(tbl, ns_id, (st_data_t)first);
- }
- cur = cdr;
- entry = entry->next;
- }
- }
-
- if (RCLASSEXT_NS_SUPER_SUBCLASSES(orig))
- RCLASSEXT_NS_SUPER_SUBCLASSES(copy) = rb_ns_subclasses_ref_inc(RCLASSEXT_NS_SUPER_SUBCLASSES(orig));
- if (RCLASSEXT_NS_MODULE_SUBCLASSES(orig))
- RCLASSEXT_NS_MODULE_SUBCLASSES(copy) = rb_ns_subclasses_ref_inc(RCLASSEXT_NS_MODULE_SUBCLASSES(orig));
-}
-
static void
-class_duplicate_iclass_classext(VALUE iclass, rb_classext_t *mod_ext, const rb_namespace_t *ns)
+class_duplicate_iclass_classext(VALUE iclass, rb_classext_t *mod_ext, const rb_box_t *box)
{
RUBY_ASSERT(RB_TYPE_P(iclass, T_ICLASS));
rb_classext_t *src = RCLASS_EXT_PRIME(iclass);
- rb_classext_t *ext = RCLASS_EXT_TABLE_LOOKUP_INTERNAL(iclass, ns);
+ rb_classext_t *ext = RCLASS_EXT_TABLE_LOOKUP_INTERNAL(iclass, box);
int first_set = 0;
if (ext) {
@@ -255,7 +286,7 @@ class_duplicate_iclass_classext(VALUE iclass, rb_classext_t *mod_ext, const rb_n
ext = ZALLOC(rb_classext_t);
- RCLASSEXT_NS(ext) = ns;
+ RCLASSEXT_BOX(ext) = box;
RCLASSEXT_SUPER(ext) = RCLASSEXT_SUPER(src);
@@ -274,8 +305,7 @@ class_duplicate_iclass_classext(VALUE iclass, rb_classext_t *mod_ext, const rb_n
// RCLASSEXT_CALLABLE_M_TBL(ext) = NULL;
// RCLASSEXT_CC_TBL(ext) = NULL;
- // subclasses, namespace_super_subclasses_tbl, namespace_module_subclasses_tbl
- duplicate_classext_subclasses(src, ext);
+ // Subclasses/back-pointers are only in the prime classext.
RCLASSEXT_SET_ORIGIN(ext, iclass, RCLASSEXT_ORIGIN(src));
RCLASSEXT_ICLASS_IS_ORIGIN(ext) = RCLASSEXT_ICLASS_IS_ORIGIN(src);
@@ -283,25 +313,29 @@ class_duplicate_iclass_classext(VALUE iclass, rb_classext_t *mod_ext, const rb_n
RCLASSEXT_SET_INCLUDER(ext, iclass, RCLASSEXT_INCLUDER(src));
- first_set = RCLASS_SET_NAMESPACE_CLASSEXT(iclass, ns, ext);
+ VM_ASSERT(FL_TEST_RAW(iclass, RCLASS_BOXABLE));
+
+ first_set = RCLASS_SET_BOX_CLASSEXT(iclass, box, ext);
if (first_set) {
RCLASS_SET_PRIME_CLASSEXT_WRITABLE(iclass, false);
}
}
rb_classext_t *
-rb_class_duplicate_classext(rb_classext_t *orig, VALUE klass, const rb_namespace_t *ns)
+rb_class_duplicate_classext(rb_classext_t *orig, VALUE klass, const rb_box_t *box)
{
VM_ASSERT(RB_TYPE_P(klass, T_CLASS) || RB_TYPE_P(klass, T_MODULE) || RB_TYPE_P(klass, T_ICLASS));
rb_classext_t *ext = ZALLOC(rb_classext_t);
bool dup_iclass = RB_TYPE_P(klass, T_MODULE) ? true : false;
- RCLASSEXT_NS(ext) = ns;
+ RCLASSEXT_BOX(ext) = box;
RCLASSEXT_SUPER(ext) = RCLASSEXT_SUPER(orig);
RCLASSEXT_M_TBL(ext) = duplicate_classext_m_tbl(RCLASSEXT_M_TBL(orig), klass, dup_iclass);
+ RCLASSEXT_ICLASS_IS_ORIGIN(ext) = true;
+ RCLASSEXT_ICLASS_ORIGIN_SHARED_MTBL(ext) = false;
if (orig->fields_obj) {
RB_OBJ_WRITE(klass, &ext->fields_obj, rb_imemo_fields_clone(orig->fields_obj));
@@ -320,19 +354,25 @@ rb_class_duplicate_classext(rb_classext_t *orig, VALUE klass, const rb_namespace
* so initially, it can be NULL and let it be created lazily.
* RCLASSEXT_CALLABLE_M_TBL(ext) = NULL;
*
- * cc_tbl is for method inline cache, and method calls from different namespaces never occur on
+ * cc_tbl is for method inline cache, and method calls from different boxes never occur on
* the same code, so the copied classext should have a different cc_tbl from the prime one.
* RCLASSEXT_CC_TBL(copy) = NULL
*/
- RCLASSEXT_CVC_TBL(ext) = duplicate_classext_id_table(RCLASSEXT_CVC_TBL(orig), dup_iclass);
+ VALUE cvc_table = RCLASSEXT_CVC_TBL(orig);
+ if (cvc_table) {
+ cvc_table = rb_marked_id_table_dup(cvc_table);
+ }
+ else if (dup_iclass) {
+ cvc_table = rb_marked_id_table_new(2);
+ }
+ RB_OBJ_WRITE(klass, &RCLASSEXT_CVC_TBL(ext), cvc_table);
- // subclasses, subclasses_index
- duplicate_classext_subclasses(orig, ext);
+ // Subclasses/back-pointers are only in the prime classext.
RCLASSEXT_SET_ORIGIN(ext, klass, RCLASSEXT_ORIGIN(orig));
/*
- * Members not copied to namespace classext values
+ * Members not copied to box's classext values
* * refined_class
* * as.class.allocator / as.singleton_class.attached_object
* * includer
@@ -340,30 +380,37 @@ rb_class_duplicate_classext(rb_classext_t *orig, VALUE klass, const rb_namespace
* * variation count
*/
RCLASSEXT_PERMANENT_CLASSPATH(ext) = RCLASSEXT_PERMANENT_CLASSPATH(orig);
- RCLASSEXT_CLONED(ext) = RCLASSEXT_CLONED(orig);
RCLASSEXT_CLASSPATH(ext) = RCLASSEXT_CLASSPATH(orig);
/* For the usual T_CLASS/T_MODULE, iclass flags are always false */
if (dup_iclass) {
- VALUE iclass;
/*
* ICLASS has the same m_tbl/const_tbl/cvc_tbl with the included module.
* So the module's classext is copied, its tables should be also referred
- * by the ICLASS's classext for the namespace.
+ * by the ICLASS's classext for the box.
+ *
+ * Subclasses are only in the prime classext, so read from orig.
*/
- rb_subclass_anchor_t *anchor = RCLASSEXT_SUBCLASSES(ext);
- rb_subclass_entry_t *subclass_entry = anchor->head;
- while (subclass_entry) {
- if (subclass_entry->klass && RB_TYPE_P(subclass_entry->klass, T_ICLASS)) {
- iclass = subclass_entry->klass;
- if (RBASIC_CLASS(iclass) == klass) {
- // Is the subclass an ICLASS including this module into another class
- // If so we need to re-associate it under our namespace with the new ext
- class_duplicate_iclass_classext(iclass, ext, ns);
+ VALUE subs_v = RCLASSEXT_SUBCLASSES(orig);
+ if (subs_v) {
+ struct rb_subclasses *subs = (struct rb_subclasses *)subs_v;
+ VALUE *entries = rb_imemo_subclasses_entries(subs_v);
+ for (uint32_t i = 0; i < subs->count; i++) {
+ VALUE iclass = entries[i];
+ if (!iclass) continue;
+
+ /* every node in the subclass list should be an ICLASS built from this module */
+ VM_ASSERT(RB_TYPE_P(iclass, T_ICLASS));
+ VM_ASSERT(RBASIC_CLASS(iclass) == klass);
+
+ if (FL_TEST_RAW(iclass, RCLASS_BOXABLE)) {
+ // Non-boxable ICLASSes (included by classes in main/user boxes) can't
+ // hold per-box classexts, and their includer classes also can't, so
+ // method lookup through them always uses the prime classext.
+ class_duplicate_iclass_classext(iclass, ext, box);
}
}
- subclass_entry = subclass_entry->next;
}
}
@@ -423,40 +470,45 @@ rb_class_variation_count(VALUE klass)
}
static void
-push_subclass_entry_to_list(VALUE super, VALUE klass, bool is_module)
+push_subclass_entry_to_list(VALUE super, VALUE klass)
{
- rb_subclass_entry_t *entry, *head;
- rb_subclass_anchor_t *anchor;
- rb_ns_subclasses_t *ns_subclasses;
- struct st_table *tbl;
- const rb_namespace_t *ns = rb_current_namespace();
-
- entry = ZALLOC(rb_subclass_entry_t);
- entry->klass = klass;
+ RUBY_ASSERT(
+ (RB_TYPE_P(super, T_MODULE) && RB_TYPE_P(klass, T_ICLASS)) ||
+ (RB_TYPE_P(super, T_CLASS) && RB_TYPE_P(klass, T_CLASS)) ||
+ (RB_TYPE_P(klass, T_ICLASS) && !NIL_P(RCLASS_REFINED_CLASS(klass)))
+ );
RB_VM_LOCKING() {
- anchor = RCLASS_WRITABLE_SUBCLASSES(super);
- VM_ASSERT(anchor);
- ns_subclasses = (rb_ns_subclasses_t *)anchor->ns_subclasses;
- VM_ASSERT(ns_subclasses);
- tbl = ns_subclasses->tbl;
- VM_ASSERT(tbl);
-
- head = anchor->head;
- if (head->next) {
- head->next->prev = entry;
- entry->next = head->next;
+ VALUE subs_v = RCLASS_SUBCLASSES(super);
+ struct rb_subclasses *subs = (struct rb_subclasses *)subs_v;
+
+ if (!subs || subs->count == subs->capacity) {
+ VALUE *old_entries = subs ? rb_imemo_subclasses_entries(subs_v) : NULL;
+ uint32_t live = 0;
+ for (uint32_t i = 0; subs && i < subs->count; i++) {
+ if (old_entries[i]) live++;
+ }
+
+ uint32_t cap = subs ? subs->capacity : 2;
+ if (live * 2 >= cap) cap *= 2;
+
+ VALUE new_v = rb_imemo_subclasses_new(cap);
+ struct rb_subclasses *new_subs = (struct rb_subclasses *)new_v;
+ VALUE *new_entries = rb_imemo_subclasses_entries(new_v);
+ for (uint32_t i = 0; subs && i < subs->count; i++) {
+ VALUE entry = old_entries[i];
+ if (entry) {
+ new_entries[new_subs->count++] = entry;
+ RB_OBJ_WRITTEN(new_v, Qundef, entry);
+ }
+ }
+ RCLASS_SET_SUBCLASSES(super, new_v);
+ subs_v = new_v;
+ subs = new_subs;
}
- head->next = entry;
- entry->prev = head;
- st_insert(tbl, namespace_subclasses_tbl_key(ns), (st_data_t)entry);
- }
- if (is_module) {
- RCLASS_WRITE_NS_MODULE_SUBCLASSES(klass, anchor->ns_subclasses);
- }
- else {
- RCLASS_WRITE_NS_SUPER_SUBCLASSES(klass, anchor->ns_subclasses);
+ rb_imemo_subclasses_entries(subs_v)[subs->count++] = klass;
+ RB_OBJ_WRITTEN(subs_v, Qundef, klass);
}
}
@@ -464,7 +516,9 @@ void
rb_class_subclass_add(VALUE super, VALUE klass)
{
if (super && !UNDEF_P(super)) {
- push_subclass_entry_to_list(super, klass, false);
+ RUBY_ASSERT(RB_TYPE_P(super, T_CLASS) || RB_TYPE_P(super, T_MODULE));
+ RUBY_ASSERT(RB_TYPE_P(klass, T_CLASS) || RB_TYPE_P(klass, T_ICLASS));
+ push_subclass_entry_to_list(super, klass);
}
}
@@ -472,164 +526,33 @@ static void
rb_module_add_to_subclasses_list(VALUE module, VALUE iclass)
{
if (module && !UNDEF_P(module)) {
- push_subclass_entry_to_list(module, iclass, true);
+ RUBY_ASSERT(RB_TYPE_P(module, T_MODULE));
+ RUBY_ASSERT(RB_TYPE_P(iclass, T_ICLASS));
+ push_subclass_entry_to_list(module, iclass);
}
}
void
-rb_class_remove_subclass_head(VALUE klass) // TODO: check this is still used and required
-{
- rb_classext_t *ext = RCLASS_EXT_WRITABLE(klass);
- rb_class_classext_free_subclasses(ext, klass);
-}
-
-static struct rb_subclass_entry *
-class_get_subclasses_for_ns(struct st_table *tbl, VALUE ns_id)
-{
- st_data_t value;
- if (st_lookup(tbl, (st_data_t)ns_id, &value)) {
- return (struct rb_subclass_entry *)value;
- }
- return NULL;
-}
-
-static void
-remove_class_from_subclasses(struct st_table *tbl, VALUE ns_id, VALUE klass)
+rb_class_foreach_subclass(VALUE klass, void (*f)(VALUE, VALUE), VALUE arg)
{
- rb_subclass_entry_t *entry = class_get_subclasses_for_ns(tbl, ns_id);
- bool first_entry = true;
- while (entry) {
- if (entry->klass == klass) {
- rb_subclass_entry_t *prev = entry->prev, *next = entry->next;
+ VALUE subs_v = RCLASS_SUBCLASSES(klass);
+ if (!subs_v) return;
- if (prev) {
- prev->next = next;
- }
- if (next) {
- next->prev = prev;
- }
-
- xfree(entry);
-
- if (first_entry) {
- if (next) {
- st_insert(tbl, ns_id, (st_data_t)next);
- }
- else {
- // no subclass entries in this ns
- st_delete(tbl, &ns_id, NULL);
- }
- }
- break;
+ struct rb_subclasses *subs = (struct rb_subclasses *)subs_v;
+ VALUE *entries = rb_imemo_subclasses_entries(subs_v);
+ for (uint32_t i = 0; i < subs->count; i++) {
+ VALUE curklass = entries[i];
+ if (curklass) {
+ f(curklass, arg);
}
- else if (first_entry) {
- first_entry = false;
- }
- entry = entry->next;
- }
-}
-
-void
-rb_class_remove_from_super_subclasses(VALUE klass)
-{
- rb_classext_t *ext = RCLASS_EXT_WRITABLE(klass);
- rb_ns_subclasses_t *ns_subclasses = RCLASSEXT_NS_SUPER_SUBCLASSES(ext);
-
- if (!ns_subclasses) return;
- remove_class_from_subclasses(ns_subclasses->tbl, namespace_subclasses_tbl_key(RCLASSEXT_NS(ext)), klass);
- rb_ns_subclasses_ref_dec(ns_subclasses);
- RCLASSEXT_NS_SUPER_SUBCLASSES(ext) = 0;
-}
-
-void
-rb_class_remove_from_module_subclasses(VALUE klass)
-{
- rb_classext_t *ext = RCLASS_EXT_WRITABLE(klass);
- rb_ns_subclasses_t *ns_subclasses = RCLASSEXT_NS_MODULE_SUBCLASSES(ext);
-
- if (!ns_subclasses) return;
- remove_class_from_subclasses(ns_subclasses->tbl, namespace_subclasses_tbl_key(RCLASSEXT_NS(ext)), klass);
- rb_ns_subclasses_ref_dec(ns_subclasses);
- RCLASSEXT_NS_MODULE_SUBCLASSES(ext) = 0;
-}
-
-void
-rb_class_classext_free_subclasses(rb_classext_t *ext, VALUE klass)
-{
- rb_subclass_anchor_t *anchor = RCLASSEXT_SUBCLASSES(ext);
- struct st_table *tbl = anchor->ns_subclasses->tbl;
- VALUE ns_id = namespace_subclasses_tbl_key(RCLASSEXT_NS(ext));
- rb_subclass_entry_t *next, *entry = anchor->head;
-
- while (entry) {
- next = entry->next;
- xfree(entry);
- entry = next;
- }
- VM_ASSERT(
- rb_ns_subclasses_ref_count(anchor->ns_subclasses) > 0,
- "ns_subclasses refcount (%p) %ld", anchor->ns_subclasses, rb_ns_subclasses_ref_count(anchor->ns_subclasses));
- st_delete(tbl, &ns_id, NULL);
- rb_ns_subclasses_ref_dec(anchor->ns_subclasses);
- xfree(anchor);
-
- if (RCLASSEXT_NS_SUPER_SUBCLASSES(ext)) {
- rb_ns_subclasses_t *ns_sub = RCLASSEXT_NS_SUPER_SUBCLASSES(ext);
- remove_class_from_subclasses(ns_sub->tbl, ns_id, klass);
- rb_ns_subclasses_ref_dec(ns_sub);
- }
- if (RCLASSEXT_NS_MODULE_SUBCLASSES(ext)) {
- rb_ns_subclasses_t *ns_sub = RCLASSEXT_NS_MODULE_SUBCLASSES(ext);
- remove_class_from_subclasses(ns_sub->tbl, ns_id, klass);
- rb_ns_subclasses_ref_dec(ns_sub);
- }
-}
-
-void
-rb_class_foreach_subclass(VALUE klass, void (*f)(VALUE, VALUE), VALUE arg)
-{
- rb_subclass_entry_t *tmp;
- rb_subclass_entry_t *cur = RCLASS_SUBCLASSES_FIRST(klass);
- /* do not be tempted to simplify this loop into a for loop, the order of
- operations is important here if `f` modifies the linked list */
- while (cur) {
- VALUE curklass = cur->klass;
- tmp = cur->next;
- // do not trigger GC during f, otherwise the cur will become
- // a dangling pointer if the subclass is collected
- f(curklass, arg);
- cur = tmp;
}
}
static void
-class_detach_subclasses(VALUE klass, VALUE arg)
-{
- rb_class_remove_from_super_subclasses(klass);
-}
-
-void
-rb_class_detach_subclasses(VALUE klass)
-{
- rb_class_foreach_subclass(klass, class_detach_subclasses, Qnil);
-}
-
-static void
-class_detach_module_subclasses(VALUE klass, VALUE arg)
-{
- rb_class_remove_from_module_subclasses(klass);
-}
-
-void
-rb_class_detach_module_subclasses(VALUE klass)
-{
- rb_class_foreach_subclass(klass, class_detach_module_subclasses, Qnil);
-}
-
-static void
class_switch_superclass(VALUE super, VALUE klass)
{
- class_detach_subclasses(klass, Qnil);
+ // No need to remove from old super's subclasses list — the GC
+ // will nullify the weak reference when appropriate.
rb_class_subclass_add(super, klass);
}
@@ -644,41 +567,35 @@ class_switch_superclass(VALUE super, VALUE klass)
* @note this function is not Class#allocate.
*/
static VALUE
-class_alloc0(enum ruby_value_type type, VALUE klass, bool namespaceable)
+class_alloc0(enum ruby_value_type type, VALUE klass, bool boxable)
{
- rb_ns_subclasses_t *ns_subclasses;
- rb_subclass_anchor_t *anchor;
- const rb_namespace_t *ns = rb_definition_namespace();
+ const rb_box_t *box = rb_current_box();
- if (!ruby_namespace_init_done) {
- namespaceable = true;
+ if (!ruby_box_init_done) {
+ boxable = true;
}
size_t alloc_size = sizeof(struct RClass_and_rb_classext_t);
- if (namespaceable) {
- alloc_size = sizeof(struct RClass_namespaceable);
- }
-
- // class_alloc is supposed to return a new object that is not promoted yet.
- // So, we need to avoid GC after NEWOBJ_OF.
- // To achieve that, we allocate subclass lists before NEWOBJ_OF.
- //
- // TODO: Note that this could cause memory leak.
- // If NEWOBJ_OF fails with out of memory, these buffers will leak.
- ns_subclasses = ZALLOC(rb_ns_subclasses_t);
- ns_subclasses->refcount = 1;
- ns_subclasses->tbl = st_init_numtable();
- anchor = ZALLOC(rb_subclass_anchor_t);
- anchor->ns_subclasses = ns_subclasses;
- anchor->head = ZALLOC(rb_subclass_entry_t);
+ if (boxable) {
+ alloc_size = sizeof(struct RClass_boxable);
+ }
RUBY_ASSERT(type == T_CLASS || type == T_ICLASS || type == T_MODULE);
- VALUE flags = type;
- if (RGENGC_WB_PROTECTED_CLASS) flags |= FL_WB_PROTECTED;
- if (namespaceable) flags |= RCLASS_NAMESPACEABLE;
+ VALUE flags = type | FL_SHAREABLE;
+ if (boxable) flags |= RCLASS_BOXABLE;
+
+ shape_id_t shape_id = ROOT_SHAPE_ID;
+ if (boxable) {
+ shape_id |= SHAPE_ID_LAYOUT_OTHER;
+ }
+ else {
+ shape_id |= SHAPE_ID_LAYOUT_RCLASS;
+ }
+
+ struct RClass *obj = (struct RClass *)rb_newobj(GET_EC(), klass, flags, shape_id, true, alloc_size);
- NEWOBJ_OF(obj, struct RClass, klass, flags, alloc_size, 0);
+ obj->object_id = 0;
memset(RCLASS_EXT_PRIME(obj), 0, sizeof(rb_classext_t));
@@ -689,30 +606,46 @@ class_alloc0(enum ruby_value_type type, VALUE klass, bool namespaceable)
RCLASS_SET_SUPER((VALUE)obj, 0);
*/
- 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, !namespaceable || NAMESPACE_USER_P(ns));
+ if (boxable) {
+ ((struct RClass_boxable *)obj)->box_classext_tbl = NULL;
+ }
+
+ RCLASS_PRIME_BOX((VALUE)obj) = box;
+ // Classes/Modules defined in user boxes are
+ // writable directly because it exists only in a box.
+ RCLASS_SET_PRIME_CLASSEXT_WRITABLE((VALUE)obj, !boxable || BOX_USER_P(box));
RCLASS_SET_ORIGIN((VALUE)obj, (VALUE)obj);
RCLASS_SET_REFINED_CLASS((VALUE)obj, Qnil);
- RCLASS_SET_SUBCLASSES((VALUE)obj, anchor);
-
return (VALUE)obj;
}
static VALUE
class_alloc(enum ruby_value_type type, VALUE klass)
{
- return class_alloc0(type, klass, false);
+ bool boxable = rb_box_available() && BOX_MASTER_P(rb_current_box());
+ return class_alloc0(type, klass, boxable);
}
static VALUE
class_associate_super(VALUE klass, VALUE super, bool init)
{
if (super && !UNDEF_P(super)) {
- class_switch_superclass(super, klass);
+ // Only maintain subclass lists for T_CLASS→T_CLASS relationships.
+ // Include/prepend inserts ICLASSes into the super chain, but T_CLASS
+ // subclass lists should track only the immutable T_CLASS→T_CLASS link.
+ if (RB_TYPE_P(klass, T_CLASS) && RB_TYPE_P(super, T_CLASS)) {
+ if (RCLASS_SINGLETON_P(klass)) {
+ // Instead of adding singleton classes to the subclass list,
+ // just set a flag so that method cache invalidation takes the
+ // tree path.
+ FL_SET_RAW(super, RCLASS_HAS_SUBCLASSES);
+ }
+ else {
+ class_switch_superclass(super, klass);
+ }
+ }
}
if (init) {
RCLASS_SET_SUPER(klass, super);
@@ -744,9 +677,9 @@ class_clear_method_table(VALUE c)
}
static VALUE
-class_boot_namespaceable(VALUE super, bool namespaceable)
+class_boot_boxable(VALUE super, bool boxable)
{
- VALUE klass = class_alloc0(T_CLASS, rb_cClass, namespaceable);
+ VALUE klass = class_alloc0(T_CLASS, rb_cClass, boxable);
// initialize method table prior to class_associate_super()
// because class_associate_super() may cause GC and promote klass
@@ -754,6 +687,7 @@ class_boot_namespaceable(VALUE super, bool namespaceable)
class_associate_super(klass, super, true);
if (super && !UNDEF_P(super)) {
+ RCLASS_SET_ALLOCATOR(klass, RCLASS_ALLOCATOR(super));
rb_class_set_initialized(klass);
}
@@ -772,7 +706,7 @@ class_boot_namespaceable(VALUE super, bool namespaceable)
VALUE
rb_class_boot(VALUE super)
{
- return class_boot_namespaceable(super, false);
+ return class_boot_boxable(super, false);
}
static VALUE *
@@ -858,11 +792,8 @@ rb_class_new(VALUE super)
rb_check_inheritable(super);
VALUE klass = rb_class_boot(super);
- if (super != rb_cObject && super != rb_cBasicObject) {
- RCLASS_SET_MAX_IV_COUNT(klass, RCLASS_MAX_IV_COUNT(super));
- }
-
- RUBY_ASSERT(getenv("RUBY_NAMESPACE") || RCLASS_PRIME_CLASSEXT_WRITABLE_P(klass));
+ RCLASS_SET_MAX_IV_COUNT(klass, RCLASS_MAX_IV_COUNT(super));
+ RUBY_ASSERT(getenv("RUBY_BOX") || RCLASS_PRIME_CLASSEXT_WRITABLE_P(klass));
return klass;
}
@@ -874,27 +805,20 @@ rb_class_s_alloc(VALUE klass)
}
static void
-clone_method(VALUE old_klass, VALUE new_klass, ID mid, const rb_method_entry_t *me)
+clone_method(VALUE new_klass, ID mid, const rb_method_entry_t *me)
{
- if (me->def->type == VM_METHOD_TYPE_ISEQ) {
- rb_cref_t *new_cref = rb_vm_rewrite_cref(me->def->body.iseq.cref, old_klass, new_klass);
- rb_add_method_iseq(new_klass, mid, me->def->body.iseq.iseqptr, new_cref, METHOD_ENTRY_VISI(me));
- }
- else {
- rb_method_entry_set(new_klass, mid, me, METHOD_ENTRY_VISI(me));
- }
+ rb_method_entry_set(new_klass, mid, me, METHOD_ENTRY_VISI(me));
}
struct clone_method_arg {
VALUE new_klass;
- VALUE old_klass;
};
static enum rb_id_table_iterator_result
clone_method_i(ID key, VALUE value, void *data)
{
const struct clone_method_arg *arg = (struct clone_method_arg *)data;
- clone_method(arg->old_klass, arg->new_klass, key, (const rb_method_entry_t *)value);
+ clone_method(arg->new_klass, key, (const rb_method_entry_t *)value);
return ID_TABLE_CONTINUE;
}
@@ -937,9 +861,15 @@ class_init_copy_check(VALUE clone, VALUE orig)
struct cvc_table_copy_ctx {
VALUE clone;
- struct rb_id_table * new_table;
+ VALUE new_table;
};
+static struct rb_cvar_class_tbl_entry *
+cvc_table_entry_alloc(void)
+{
+ return (struct rb_cvar_class_tbl_entry *)SHAREABLE_IMEMO_NEW(struct rb_cvar_class_tbl_entry, imemo_cvar_entry, 0);
+}
+
static enum rb_id_table_iterator_result
cvc_table_copy(ID id, VALUE val, void *data)
{
@@ -949,13 +879,11 @@ cvc_table_copy(ID id, VALUE val, void *data)
struct rb_cvar_class_tbl_entry *ent;
- ent = ALLOC(struct rb_cvar_class_tbl_entry);
- ent->class_value = ctx->clone;
- ent->cref = orig_entry->cref;
+ ent = cvc_table_entry_alloc();
+ RB_OBJ_WRITE((VALUE)ent, &ent->class_value, ctx->clone);
+ RB_OBJ_WRITE(ctx->clone, &ent->cref, orig_entry->cref);
ent->global_cvar_state = orig_entry->global_cvar_state;
- rb_id_table_insert(ctx->new_table, id, (VALUE)ent);
-
- RB_OBJ_WRITTEN(ctx->clone, Qundef, ent->cref);
+ rb_marked_id_table_insert(ctx->new_table, id, (VALUE)ent);
return ID_TABLE_CONTINUE;
}
@@ -968,13 +896,13 @@ copy_tables(VALUE clone, VALUE orig)
RCLASS_WRITE_CONST_TBL(clone, 0, false);
}
if (RCLASS_CVC_TBL(orig)) {
- struct rb_id_table *rb_cvc_tbl = RCLASS_CVC_TBL(orig);
- struct rb_id_table *rb_cvc_tbl_dup = rb_id_table_create(rb_id_table_size(rb_cvc_tbl));
+ VALUE rb_cvc_tbl = RCLASS_CVC_TBL(orig);
+ VALUE rb_cvc_tbl_dup = rb_marked_id_table_new(rb_marked_id_table_size(rb_cvc_tbl));
struct cvc_table_copy_ctx ctx;
ctx.clone = clone;
ctx.new_table = rb_cvc_tbl_dup;
- rb_id_table_foreach(rb_cvc_tbl, cvc_table_copy, &ctx);
+ rb_marked_id_table_foreach(rb_cvc_tbl, cvc_table_copy, &ctx);
RCLASS_WRITE_CVC_TBL(clone, rb_cvc_tbl_dup);
}
rb_id_table_free(RCLASS_M_TBL(clone));
@@ -990,6 +918,7 @@ copy_tables(VALUE clone, VALUE orig)
arg.klass = clone;
rb_id_table_foreach(orig_tbl, clone_const_i, &arg);
RCLASS_WRITE_CONST_TBL(clone, const_tbl, false);
+ rb_gc_writebarrier_remember(clone);
}
}
@@ -1035,12 +964,6 @@ rb_mod_init_copy(VALUE clone, VALUE orig)
rb_class_set_initialized(clone);
- /* cloned flag is refer at constant inline cache
- * see vm_get_const_key_cref() in vm_insnhelper.c
- */
- RCLASS_SET_CLONED(clone, true);
- RCLASS_SET_CLONED(orig, true);
-
if (!RCLASS_SINGLETON_P(CLASS_OF(clone))) {
RBASIC_SET_CLASS(clone, rb_singleton_class_clone(orig));
rb_singleton_class_attached(METACLASS_OF(clone), (VALUE)clone);
@@ -1051,7 +974,6 @@ rb_mod_init_copy(VALUE clone, VALUE orig)
copy_tables(clone, orig);
if (RCLASS_M_TBL(orig)) {
struct clone_method_arg arg;
- arg.old_klass = orig;
arg.new_klass = clone;
class_initialize_method_table(clone);
rb_id_table_foreach(RCLASS_M_TBL(orig), clone_method_i, &arg);
@@ -1113,7 +1035,6 @@ rb_mod_init_copy(VALUE clone, VALUE orig)
copy_tables(clone_origin, orig_origin);
if (RCLASS_M_TBL(orig_origin)) {
struct clone_method_arg arg;
- arg.old_klass = orig;
arg.new_klass = clone;
class_initialize_method_table(clone_origin);
rb_id_table_foreach(RCLASS_M_TBL(orig_origin), clone_method_i, &arg);
@@ -1175,7 +1096,7 @@ rb_singleton_class_clone_and_attach(VALUE obj, VALUE attach)
if (RCLASS_CONST_TBL(klass)) {
struct clone_const_arg arg;
struct rb_id_table *table;
- arg.tbl = table = rb_id_table_create(0);
+ arg.tbl = table = rb_id_table_create(rb_id_table_size(RCLASS_CONST_TBL(klass)));
arg.klass = clone;
rb_id_table_foreach(RCLASS_CONST_TBL(klass), clone_const_i, &arg);
RCLASS_SET_CONST_TBL(clone, table, false);
@@ -1185,7 +1106,6 @@ rb_singleton_class_clone_and_attach(VALUE obj, VALUE attach)
}
{
struct clone_method_arg arg;
- arg.old_klass = klass;
arg.new_klass = clone;
rb_id_table_foreach(RCLASS_M_TBL(klass), clone_method_i, &arg);
}
@@ -1259,7 +1179,7 @@ static inline VALUE
make_metaclass(VALUE klass)
{
VALUE super;
- VALUE metaclass = class_boot_namespaceable(Qundef, FL_TEST_RAW(klass, RCLASS_NAMESPACEABLE));
+ VALUE metaclass = class_boot_boxable(Qundef, FL_TEST_RAW(klass, RCLASS_BOXABLE));
FL_SET(metaclass, FL_SINGLETON);
rb_singleton_class_attached(metaclass, klass);
@@ -1295,12 +1215,18 @@ static inline VALUE
make_singleton_class(VALUE obj)
{
VALUE orig_class = METACLASS_OF(obj);
- VALUE klass = class_boot_namespaceable(orig_class, FL_TEST_RAW(orig_class, RCLASS_NAMESPACEABLE));
-
+ VALUE klass = class_alloc0(T_CLASS, rb_cClass, FL_TEST_RAW(orig_class, RCLASS_BOXABLE));
FL_SET(klass, FL_SINGLETON);
+ class_initialize_method_table(klass);
+ class_associate_super(klass, orig_class, true);
+ if (orig_class && !UNDEF_P(orig_class)) {
+ rb_class_set_initialized(klass);
+ }
+
RBASIC_SET_CLASS(obj, klass);
rb_singleton_class_attached(klass, obj);
rb_yjit_invalidate_no_singleton_class(orig_class);
+ rb_zjit_invalidate_no_singleton_class(orig_class);
SET_METACLASS_OF(klass, METACLASS_OF(rb_class_real(orig_class)));
return klass;
@@ -1395,8 +1321,12 @@ void
Init_class_hierarchy(void)
{
rb_cBasicObject = boot_defclass("BasicObject", 0);
+ RCLASS_SET_ALLOCATOR(rb_cBasicObject, rb_class_allocate_instance);
+ FL_SET_RAW(rb_cBasicObject, RCLASS_ALLOCATOR_DEFINED);
+ RCLASS_SET_EXPECT_NO_IVAR(rb_cBasicObject);
+
rb_cObject = boot_defclass("Object", rb_cBasicObject);
- rb_vm_register_global_object(rb_cObject);
+ RCLASS_SET_EXPECT_NO_IVAR(rb_cObject);
/* resolve class name ASAP for order-independence */
rb_set_class_path_string(rb_cObject, rb_cObject, rb_fstring_lit("Object"));
@@ -1476,13 +1406,8 @@ VALUE
rb_define_class(const char *name, VALUE super)
{
VALUE klass;
- ID id;
- const rb_namespace_t *ns = rb_current_namespace();
+ ID id = rb_intern(name);
- id = rb_intern(name);
- if (NAMESPACE_OPTIONAL_P(ns)) {
- return rb_define_class_id_under(ns->ns_object, id, super);
- }
if (rb_const_defined(rb_cObject, id)) {
klass = rb_const_get(rb_cObject, id);
if (!RB_TYPE_P(klass, T_CLASS)) {
@@ -1594,13 +1519,8 @@ VALUE
rb_define_module(const char *name)
{
VALUE module;
- ID id;
- const rb_namespace_t *ns = rb_current_namespace();
+ ID id = rb_intern(name);
- id = rb_intern(name);
- if (NAMESPACE_OPTIONAL_P(ns)) {
- return rb_define_module_id_under(ns->ns_object, id);
- }
if (rb_const_defined(rb_cObject, id)) {
module = rb_const_get(rb_cObject, id);
if (!RB_TYPE_P(module, T_MODULE)) {
@@ -1701,29 +1621,34 @@ rb_include_module(VALUE klass, VALUE module)
rb_raise(rb_eArgError, "cyclic include detected");
if (RB_TYPE_P(klass, T_MODULE)) {
- rb_subclass_entry_t *iclass = RCLASS_SUBCLASSES_FIRST(klass);
- while (iclass) {
- int do_include = 1;
- VALUE check_class = iclass->klass;
- /* During lazy sweeping, iclass->klass could be a dead object that
- * has not yet been swept. */
- if (!rb_objspace_garbage_object_p(check_class)) {
- while (check_class) {
- RUBY_ASSERT(!rb_objspace_garbage_object_p(check_class));
-
- if (RB_TYPE_P(check_class, T_ICLASS) &&
- (METACLASS_OF(check_class) == module)) {
- do_include = 0;
+ VALUE subs_v = RCLASS_SUBCLASSES(klass);
+ if (subs_v) {
+ struct rb_subclasses *subs = (struct rb_subclasses *)subs_v;
+ VALUE *entries = rb_imemo_subclasses_entries(subs_v);
+ for (uint32_t i = 0; i < subs->count; i++) {
+ VALUE check_class = entries[i];
+ if (!check_class) continue;
+
+ int do_include = 1;
+ /* During lazy sweeping, the entry could be a dead object that
+ * has not yet been swept. */
+ if (!rb_objspace_garbage_object_p(check_class)) {
+ VALUE walk = check_class;
+ while (walk) {
+ RUBY_ASSERT(!rb_objspace_garbage_object_p(walk));
+
+ if (RB_TYPE_P(walk, T_ICLASS) &&
+ (METACLASS_OF(walk) == module)) {
+ do_include = 0;
+ }
+ walk = RCLASS_SUPER(walk);
}
- check_class = RCLASS_SUPER(check_class);
- }
- if (do_include) {
- include_modules_at(iclass->klass, RCLASS_ORIGIN(iclass->klass), module, TRUE);
+ if (do_include) {
+ include_modules_at(check_class, RCLASS_ORIGIN(check_class), module, TRUE);
+ }
}
}
-
- iclass = iclass->next;
}
}
}
@@ -1956,29 +1881,32 @@ rb_prepend_module(VALUE klass, VALUE module)
rb_vm_check_redefinition_by_prepend(klass);
}
if (RB_TYPE_P(klass, T_MODULE)) {
- rb_subclass_entry_t *iclass = RCLASS_SUBCLASSES_FIRST(klass);
+ VALUE subs_v = RCLASS_SUBCLASSES(klass);
VALUE klass_origin = RCLASS_ORIGIN(klass);
struct rb_id_table *klass_m_tbl = RCLASS_M_TBL(klass);
struct rb_id_table *klass_origin_m_tbl = RCLASS_M_TBL(klass_origin);
- while (iclass) {
- /* During lazy sweeping, iclass->klass could be a dead object that
- * has not yet been swept. */
- if (!rb_objspace_garbage_object_p(iclass->klass)) {
- const VALUE subclass = iclass->klass;
- if (klass_had_no_origin && klass_origin_m_tbl == RCLASS_M_TBL(subclass)) {
- // backfill an origin iclass to handle refinements and future prepends
- rb_id_table_foreach(RCLASS_M_TBL(subclass), clear_module_cache_i, (void *)subclass);
- RCLASS_WRITE_M_TBL(subclass, klass_m_tbl);
- VALUE origin = rb_include_class_new(klass_origin, RCLASS_SUPER(subclass));
- rb_class_set_super(subclass, origin);
- RCLASS_SET_INCLUDER(origin, RCLASS_INCLUDER(subclass));
- RCLASS_WRITE_ORIGIN(subclass, origin);
- RICLASS_SET_ORIGIN_SHARED_MTBL(origin);
+ if (subs_v) {
+ struct rb_subclasses *subs = (struct rb_subclasses *)subs_v;
+ VALUE *entries = rb_imemo_subclasses_entries(subs_v);
+ for (uint32_t i = 0; i < subs->count; i++) {
+ const VALUE subclass = entries[i];
+ if (!subclass) continue;
+ /* During lazy sweeping, the entry could be a dead object that
+ * has not yet been swept. */
+ if (!rb_objspace_garbage_object_p(subclass)) {
+ if (klass_had_no_origin && klass_origin_m_tbl == RCLASS_M_TBL(subclass)) {
+ // backfill an origin iclass to handle refinements and future prepends
+ rb_id_table_foreach(RCLASS_M_TBL(subclass), clear_module_cache_i, (void *)subclass);
+ RCLASS_WRITE_M_TBL(subclass, klass_m_tbl);
+ VALUE origin = rb_include_class_new(klass_origin, RCLASS_SUPER(subclass));
+ rb_class_set_super(subclass, origin);
+ RCLASS_SET_INCLUDER(origin, RCLASS_INCLUDER(subclass));
+ RCLASS_WRITE_ORIGIN(subclass, origin);
+ RICLASS_SET_ORIGIN_SHARED_MTBL(origin);
+ }
+ include_modules_at(subclass, subclass, module, FALSE);
}
- include_modules_at(subclass, subclass, module, FALSE);
}
-
- iclass = iclass->next;
}
}
}
@@ -2108,19 +2036,17 @@ class_descendants_recursive(VALUE klass, VALUE v)
{
struct subclass_traverse_data *data = (struct subclass_traverse_data *) v;
- if (BUILTIN_TYPE(klass) == T_CLASS && !RCLASS_SINGLETON_P(klass)) {
+ if (RB_TYPE_P(klass, T_ICLASS)) return; // skip refinement ICLASSes
+
+ if (!RCLASS_SINGLETON_P(klass)) {
if (data->buffer && data->count < data->maxcount && !rb_objspace_garbage_object_p(klass)) {
// assumes that this does not cause GC as long as the length does not exceed the capacity
rb_ary_push(data->buffer, klass);
}
data->count++;
- if (!data->immediate_only) {
- rb_class_foreach_subclass(klass, class_descendants_recursive, v);
- }
- }
- else {
- rb_class_foreach_subclass(klass, class_descendants_recursive, v);
+ if (data->immediate_only) return;
}
+ rb_class_foreach_subclass(klass, class_descendants_recursive, v);
}
static VALUE
@@ -2593,7 +2519,7 @@ rb_obj_singleton_methods(int argc, const VALUE *argv, VALUE obj)
int recur = TRUE;
if (rb_check_arity(argc, 0, 1)) recur = RTEST(argv[0]);
- if (RCLASS_SINGLETON_P(obj)) {
+ if (RB_TYPE_P(obj, T_CLASS) && RCLASS_SINGLETON_P(obj)) {
rb_singleton_class(obj);
}
klass = CLASS_OF(obj);
@@ -2719,7 +2645,7 @@ rb_special_singleton_class(VALUE obj)
* consistency of the metaclass hierarchy.
*/
static VALUE
-singleton_class_of(VALUE obj)
+singleton_class_of(VALUE obj, bool ensure_eigenclass)
{
VALUE klass;
@@ -2747,28 +2673,43 @@ singleton_class_of(VALUE obj)
}
}
- klass = METACLASS_OF(obj);
- if (!(RCLASS_SINGLETON_P(klass) &&
- RCLASS_ATTACHED_OBJECT(klass) == obj)) {
- klass = rb_make_metaclass(obj, klass);
+ bool needs_lock = rb_multi_ractor_p() && rb_ractor_shareable_p(obj);
+ unsigned int lev;
+ if (needs_lock) {
+ RB_VM_LOCK_ENTER_LEV(&lev);
+ }
+ {
+ klass = METACLASS_OF(obj);
+ if (!(RCLASS_SINGLETON_P(klass) &&
+ RCLASS_ATTACHED_OBJECT(klass) == obj)) {
+ klass = rb_make_metaclass(obj, klass);
+ }
+ RB_FL_SET_RAW(klass, RB_OBJ_FROZEN_RAW(obj));
+ if (ensure_eigenclass && RB_TYPE_P(obj, T_CLASS)) {
+ /* ensures an exposed class belongs to its own eigenclass */
+ (void)ENSURE_EIGENCLASS(klass);
+ }
+ }
+ if (needs_lock) {
+ RB_VM_LOCK_LEAVE_LEV(&lev);
}
-
- RB_FL_SET_RAW(klass, RB_OBJ_FROZEN_RAW(obj));
return klass;
}
void
-rb_freeze_singleton_class(VALUE x)
-{
- /* should not propagate to meta-meta-class, and so on */
- if (!RCLASS_SINGLETON_P(x)) {
- VALUE klass = RBASIC_CLASS(x);
- if (klass && // no class when hidden from ObjectSpace
- FL_TEST_RAW(klass, FL_SINGLETON) &&
- !OBJ_FROZEN_RAW(klass)) {
- OBJ_FREEZE(klass);
- }
+rb_freeze_singleton_class(VALUE attached_object)
+{
+ VALUE klass;
+
+ /* Freeze singleton classes of singleton class, as singleton class is frozen, and so on */
+ /* In each iteration, check the current object's class pointer is the singleton class of the object. */
+ while ((klass = RBASIC_CLASS(attached_object)) &&
+ FL_TEST_RAW(klass, FL_SINGLETON) &&
+ !OBJ_FROZEN_RAW(klass) &&
+ (RCLASS_ATTACHED_OBJECT(klass) == attached_object)) {
+ attached_object = klass;
+ OBJ_FREEZE(attached_object);
}
}
@@ -2796,12 +2737,7 @@ rb_singleton_class_get(VALUE obj)
VALUE
rb_singleton_class(VALUE obj)
{
- VALUE klass = singleton_class_of(obj);
-
- /* ensures an exposed class belongs to its own eigenclass */
- if (RB_TYPE_P(obj, T_CLASS)) (void)ENSURE_EIGENCLASS(klass);
-
- return klass;
+ return singleton_class_of(obj, true);
}
/*!
@@ -2819,7 +2755,7 @@ rb_singleton_class(VALUE obj)
void
rb_define_singleton_method(VALUE obj, const char *name, VALUE (*func)(ANYARGS), int argc)
{
- rb_define_method(singleton_class_of(obj), name, func, argc);
+ rb_define_method(singleton_class_of(obj, false), name, func, argc);
}
#ifdef rb_define_module_function