@@ -67,9 +67,6 @@ public static RubyClass createBootClass(RubyLanguage language, RubyClass classCl
67
67
@ TruffleBoundary
68
68
public static RubyClass createSingletonClassOfObject (RubyContext context , SourceSection sourceSection ,
69
69
RubyClass superclass , RubyDynamicObject attached ) {
70
- // We also need to create the singleton class of a singleton class for proper lookup and consistency.
71
- // See rb_singleton_class() documentation in MRI.
72
- // Allocator is null here, we cannot create instances of singleton classes.
73
70
assert attached != null ;
74
71
final RubyClass rubyClass = createRubyClass (
75
72
context ,
@@ -136,19 +133,35 @@ public static void initialize(RubyContext context, RubyClass rubyClass) {
136
133
ensureItHasSingletonClassCreated (context , rubyClass );
137
134
}
138
135
136
+ /** We also need to create the singleton class of any class exposed to the user for proper lookup and consistency.
137
+ * This is not so intuitive, but basically it follows the rule of: "every Class object exposed to the user must have
138
+ * a singleton class", i.e., only (singleton) classes not exposed to the user can have their own singleton class
139
+ * created lazily. An example is `class K; def self.foo; end; end; sc = k.new.singleton_class`. `sc` there must have
140
+ * its singleton class created, otherwise `Primitive.class_of(sc).ancestors` would be `[Class, Module, Object,
141
+ * Kernel, BasicObject]` and `sc.foo` would not find method foo (which is defined on `#<Class:K>`). With `sc` having
142
+ * the singleton class as soon as `sc` is exposed to the user, then it's fine, and
143
+ * `Primitive.class_of(sc).ancestors` is `[#<Class:#<Class:#<K:0xc8>>>, #<Class:K>, #<Class:Object>,
144
+ * #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]`. See rb_singleton_class() documentation in
145
+ * MRI. In theory, it might be possible to do this lazily in `MetaClassNode` but it's unlikely not a good
146
+ * performance trade-off (add a check at every usage vs do it eagerly and no check). As an anecdote a single
147
+ * ruby/spec fails when not calling this in {@link #createSingletonClassOfObject}, maybe there is an opportunity?
148
+ * TODO (eregon, 23 March 2023): CRuby (3.1) seems to deal with this better, `RBASIC_CLASS(sc).ancestors` is
149
+ * `[#<Class:K>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]` so it points to
150
+ * the superclass (which is a singleton class) and then somehow knows this is not this class's singleton class
151
+ * (maybe by comparing `attached`), so on `sc.singleton_class` it actually creates it. */
139
152
private static RubyClass ensureItHasSingletonClassCreated (RubyContext context , RubyClass rubyClass ) {
140
153
getLazyCreatedSingletonClass (context , rubyClass );
141
154
return rubyClass ;
142
155
}
143
156
144
157
@ TruffleBoundary
145
- public static RubyClass getSingletonClass (RubyContext context , RubyClass rubyClass ) {
158
+ public static RubyClass getSingletonClassOfClass (RubyContext context , RubyClass rubyClass ) {
146
159
// We also need to create the singleton class of a singleton class for proper lookup and consistency.
147
160
// See rb_singleton_class() documentation in MRI.
148
161
return ensureItHasSingletonClassCreated (context , getLazyCreatedSingletonClass (context , rubyClass ));
149
162
}
150
163
151
- public static RubyClass getSingletonClassOrNull (RubyContext context , RubyClass rubyClass ) {
164
+ public static RubyClass getSingletonClassOfClassOrNull (RubyContext context , RubyClass rubyClass ) {
152
165
RubyClass metaClass = rubyClass .getMetaClass ();
153
166
if (metaClass .isSingleton ) {
154
167
return ensureItHasSingletonClassCreated (context , metaClass );
0 commit comments