Skip to content

Commit e3b37d4

Browse files
committed
Reclaim one VALUE from rb_classext_t by shrinking super_classdepth
By making `super_classdepth` `uint16_t`, classes and modules can now fit in 160B slots again. The downside of course is that before `super_classdepth` was large enough we never had to care about overflow, as you couldn't realistically create enough classes to ever go over it. With this change, while it is stupid, you could realistically create an ancestor chain containing 65k classes and modules.
1 parent bb180b8 commit e3b37d4

2 files changed

Lines changed: 12 additions & 4 deletions

File tree

class.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -827,7 +827,9 @@ rb_class_update_superclasses(VALUE klass)
827827
superclasses = class_superclasses_including_self(super);
828828
RCLASS_WRITE_SUPERCLASSES(super, super_depth, superclasses, true, true);
829829
}
830-
RCLASS_WRITE_SUPERCLASSES(klass, super_depth + 1, superclasses, false, false);
830+
831+
size_t depth = super_depth == RCLASS_MAX_SUPERCLASS_DEPTH ? super_depth : super_depth + 1;
832+
RCLASS_WRITE_SUPERCLASSES(klass, depth, superclasses, false, false);
831833
}
832834

833835
void

internal/class.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ struct rb_classext_struct {
8585
struct rb_id_table *callable_m_tbl;
8686
struct rb_id_table *cc_tbl; /* ID -> [[ci1, cc1], [ci2, cc2] ...] */
8787
struct rb_id_table *cvc_tbl;
88-
size_t superclass_depth;
8988
VALUE *superclasses;
9089
/**
9190
* The head of subclasses is a blank (w/o klass) entry to be referred from anchor (and be never deleted).
@@ -121,6 +120,7 @@ struct rb_classext_struct {
121120
} iclass;
122121
} as;
123122
attr_index_t max_iv_count;
123+
uint16_t superclass_depth;
124124
unsigned char variation_count;
125125
bool permanent_classpath : 1;
126126
bool cloned : 1;
@@ -145,14 +145,18 @@ struct RClass {
145145
};
146146

147147
// Assert that classes can be embedded in heaps[2] (which has 160B slot size)
148-
// TODO: restore this assertion after removing several fields from rb_classext_t
149-
// STATIC_ASSERT(sizeof_rb_classext_t, sizeof(struct RClass) + sizeof(rb_classext_t) <= 4 * RVALUE_SIZE);
148+
// On 32bit platforms there is now variable width allocation so it doesn't matter.
149+
#if SIZEOF_VALUE >= SIZEOF_LONG
150+
STATIC_ASSERT(sizeof_rb_classext_t, sizeof(struct RClass) + sizeof(rb_classext_t) <= 4 * RVALUE_SIZE);
151+
#endif
150152

151153
struct RClass_and_rb_classext_t {
152154
struct RClass rclass;
153155
rb_classext_t classext;
154156
};
155157

158+
static const uint16_t RCLASS_MAX_SUPERCLASS_DEPTH = ((uint16_t)-1);
159+
156160
static inline bool RCLASS_SINGLETON_P(VALUE klass);
157161

158162
static inline bool RCLASS_PRIME_CLASSEXT_READABLE_P(VALUE obj);
@@ -712,6 +716,8 @@ RCLASS_SET_INCLUDER(VALUE iclass, VALUE klass)
712716
static inline void
713717
RCLASS_WRITE_SUPERCLASSES(VALUE klass, size_t depth, VALUE *superclasses, bool owns_it, bool with_self)
714718
{
719+
RUBY_ASSERT(depth <= RCLASS_MAX_SUPERCLASS_DEPTH);
720+
715721
rb_classext_t *ext = RCLASS_EXT_WRITABLE(klass);
716722
RCLASSEXT_SUPERCLASS_DEPTH(ext) = depth;
717723
RCLASSEXT_SUPERCLASSES(ext) = superclasses;

0 commit comments

Comments
 (0)