Skip to content

Commit 3c251f7

Browse files
committed
Don't add singleton classes to subclasses list
Singleton classes don't need to be iterated in the subclasses list, we only need to know that a subclass exists so that we can use the "method entry" path for invalidation.
1 parent 90f74e0 commit 3c251f7

3 files changed

Lines changed: 18 additions & 3 deletions

File tree

class.c

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,15 @@ class_associate_super(VALUE klass, VALUE super, bool init)
743743
// Include/prepend inserts ICLASSes into the super chain, but T_CLASS
744744
// subclass lists should track only the immutable T_CLASS→T_CLASS link.
745745
if (RB_TYPE_P(klass, T_CLASS) && RB_TYPE_P(super, T_CLASS)) {
746-
class_switch_superclass(super, klass);
746+
if (RCLASS_SINGLETON_P(klass)) {
747+
// Instead of adding singleton classes to the subclass list,
748+
// just set a flag so that method cache invalidation takes the
749+
// tree path.
750+
FL_SET_RAW(super, RCLASS_HAS_SUBCLASSES);
751+
}
752+
else {
753+
class_switch_superclass(super, klass);
754+
}
747755
}
748756
}
749757
if (init) {
@@ -1328,9 +1336,14 @@ static inline VALUE
13281336
make_singleton_class(VALUE obj)
13291337
{
13301338
VALUE orig_class = METACLASS_OF(obj);
1331-
VALUE klass = class_boot_boxable(orig_class, FL_TEST_RAW(orig_class, RCLASS_BOXABLE));
1332-
1339+
VALUE klass = class_alloc0(T_CLASS, rb_cClass, FL_TEST_RAW(orig_class, RCLASS_BOXABLE));
13331340
FL_SET(klass, FL_SINGLETON);
1341+
class_initialize_method_table(klass);
1342+
class_associate_super(klass, orig_class, true);
1343+
if (orig_class && !UNDEF_P(orig_class)) {
1344+
rb_class_set_initialized(klass);
1345+
}
1346+
13341347
RBASIC_SET_CLASS(obj, klass);
13351348
rb_singleton_class_attached(klass, obj);
13361349
rb_yjit_invalidate_no_singleton_class(orig_class);

internal/class.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ static inline void RCLASS_WRITE_CLASSPATH(VALUE klass, VALUE classpath, bool per
256256
// 3 is RMODULE_IS_REFINEMENT for RMODULE
257257
#define RCLASS_BOXABLE FL_USER4
258258
#define RCLASS_ALLOCATOR_DEFINED FL_USER5
259+
#define RCLASS_HAS_SUBCLASSES FL_USER6
259260

260261
static inline st_table *
261262
RCLASS_CLASSEXT_TBL(VALUE klass)

vm_method.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,7 @@ clear_method_cache_by_id_in_class(VALUE klass, ID mid)
440440
rb_vm_barrier();
441441

442442
if (LIKELY(RCLASS_SUBCLASSES_FIRST(klass) == NULL) &&
443+
!FL_TEST_RAW(klass, RCLASS_HAS_SUBCLASSES) &&
443444
// Non-refinement ICLASSes (from module inclusion) previously had
444445
// subclasses reparented onto them, so they need the tree path for
445446
// broader cme-based invalidation even though they now have no subclasses.

0 commit comments

Comments
 (0)