Skip to content

Commit 56588d6

Browse files
committed
[GR-35427] Set super class eagerly
PullRequest: truffleruby/3358
2 parents c52e632 + 8e8c501 commit 56588d6

File tree

12 files changed

+136
-255
lines changed

12 files changed

+136
-255
lines changed

lib/truffle/truffle/cext.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,6 +1139,10 @@ def rb_const_remove(mod, name)
11391139
Primitive.module_remove_const(mod, name)
11401140
end
11411141

1142+
def rb_class_new(superclass)
1143+
Primitive.class_new(superclass, false, nil)
1144+
end
1145+
11421146
def rb_define_class_under(mod, name, superclass)
11431147
# nil is TypeError (checked below), false is ArgumentError
11441148
if false.equal?(superclass)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
fails:Class#allocate returns a fully-formed instance of Module
2+
fails:Class#allocate throws an exception when calling a method on a new instance
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fails:Class#initialize when given the Class raises a TypeError

src/main/java/org/truffleruby/cext/CExtNodes.java

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,6 @@
102102
import org.truffleruby.language.methods.DeclarationContext;
103103
import org.truffleruby.language.methods.InternalMethod;
104104
import org.truffleruby.language.objects.AllocationTracing;
105-
import org.truffleruby.language.objects.InitializeClassNode;
106-
import org.truffleruby.language.objects.InitializeClassNodeGen;
107105
import org.truffleruby.language.objects.MetaClassNode;
108106
import org.truffleruby.language.objects.WriteObjectFieldNode;
109107
import org.truffleruby.language.supercall.CallSuperMethodNode;
@@ -1317,27 +1315,6 @@ protected int write(RubyString string, int index, int value,
13171315

13181316
}
13191317

1320-
@CoreMethod(names = "rb_class_new", onSingleton = true, required = 1)
1321-
public abstract static class ClassNewNode extends CoreMethodArrayArgumentsNode {
1322-
1323-
@Child private DispatchNode allocateNode;
1324-
@Child private InitializeClassNode initializeClassNode;
1325-
1326-
@Specialization
1327-
protected RubyClass classNew(RubyClass superclass) {
1328-
if (allocateNode == null) {
1329-
CompilerDirectives.transferToInterpreterAndInvalidate();
1330-
allocateNode = insert(DispatchNode.create());
1331-
initializeClassNode = insert(InitializeClassNodeGen.create(false));
1332-
}
1333-
1334-
RubyClass klass = (RubyClass) allocateNode
1335-
.call(getContext().getCoreLibrary().classClass, "__allocate__");
1336-
return initializeClassNode.executeInitialize(klass, superclass, nil);
1337-
}
1338-
1339-
}
1340-
13411318
@CoreMethod(names = "rb_tr_debug", onSingleton = true, rest = true)
13421319
public abstract static class DebugNode extends CoreMethodArrayArgumentsNode {
13431320

src/main/java/org/truffleruby/core/CoreLibrary.java

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -292,15 +292,10 @@ public CoreLibrary(RubyContext context, RubyLanguage language) {
292292

293293
// Create the cyclic classes and modules
294294

295-
classClass = ClassNodes.createClassClass(language);
296-
297-
basicObjectClass = ClassNodes.createBootClass(language, classClass, Nil.INSTANCE, "BasicObject");
298-
objectClass = ClassNodes.createBootClass(language, classClass, basicObjectClass, "Object");
299-
moduleClass = ClassNodes.createBootClass(language, classClass, objectClass, "Module");
300-
301-
// Close the cycles
302-
// Set superclass of Class to Module
303-
classClass.setSuperClass(moduleClass);
295+
classClass = ClassNodes.createClassClassAndBootClasses(language);
296+
moduleClass = (RubyClass) classClass.superclass;
297+
objectClass = (RubyClass) moduleClass.superclass;
298+
basicObjectClass = (RubyClass) objectClass.superclass;
304299

305300
// Set constants in Object and lexical parents
306301
classClass.fields.getAdoptedByLexicalParent(context, objectClass, "Class", node);

src/main/java/org/truffleruby/core/kernel/KernelNodes.java

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
import org.truffleruby.core.cast.ToStringOrSymbolNode;
4848
import org.truffleruby.core.cast.ToSymbolNode;
4949
import org.truffleruby.core.encoding.Encodings;
50-
import org.truffleruby.core.encoding.RubyEncoding;
5150
import org.truffleruby.core.exception.GetBacktraceException;
5251
import org.truffleruby.core.format.BytesResult;
5352
import org.truffleruby.core.format.FormatExceptionTranslator;
@@ -512,7 +511,7 @@ public abstract static class CopyNode extends RubyBaseNode {
512511

513512
public abstract RubyDynamicObject executeCopy(Object self);
514513

515-
@Specialization
514+
@Specialization(guards = "!isRubyClass(self)")
516515
protected RubyDynamicObject copyRubyDynamicObject(RubyDynamicObject self,
517516
@Cached DispatchNode allocateNode,
518517
@Cached CopyInstanceVariablesNode copyInstanceVariablesNode) {
@@ -522,15 +521,18 @@ protected RubyDynamicObject copyRubyDynamicObject(RubyDynamicObject self,
522521
}
523522

524523
@Specialization
525-
protected RubyString copyImmutableString(ImmutableRubyString string,
526-
@Cached DispatchNode allocateStringNode) {
527-
return (RubyString) allocateStringNode.call(coreLibrary().stringClass, "__allocate__");
524+
protected RubyClass copyRubyClass(RubyClass self,
525+
@Cached CopyInstanceVariablesNode copyInstanceVariablesNode) {
526+
var newClass = new RubyClass(coreLibrary().classClass, getLanguage(), getEncapsulatingSourceSection(),
527+
null, null, false, null, self.superclass);
528+
copyInstanceVariablesNode.execute(newClass, self);
529+
return newClass;
528530
}
529531

530532
@Specialization
531-
protected RubyDynamicObject copyRubyEncoding(RubyEncoding encoding) {
532-
throw new RaiseException(getContext(),
533-
coreExceptions().typeErrorAllocatorUndefinedFor(coreLibrary().encodingClass, this));
533+
protected RubyString copyImmutableString(ImmutableRubyString string,
534+
@Cached DispatchNode allocateStringNode) {
535+
return (RubyString) allocateStringNode.call(coreLibrary().stringClass, "__allocate__");
534536
}
535537
}
536538

@@ -575,10 +577,6 @@ protected RubyDynamicObject clone(RubyDynamicObject object, Object freeze,
575577
rubyLibraryFreeze.freeze(newObject);
576578
}
577579

578-
if (isRubyClass.profile(object instanceof RubyClass)) {
579-
((RubyClass) newObject).superclass = ((RubyClass) object).superclass;
580-
}
581-
582580
return newObject;
583581
}
584582

src/main/java/org/truffleruby/core/klass/ClassNodes.java

Lines changed: 60 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
import org.truffleruby.builtins.CoreMethod;
1818
import org.truffleruby.builtins.CoreMethodArrayArgumentsNode;
1919
import org.truffleruby.builtins.CoreModule;
20+
import org.truffleruby.builtins.Primitive;
21+
import org.truffleruby.builtins.PrimitiveArrayArgumentsNode;
22+
import org.truffleruby.builtins.UnaryCoreMethodNode;
2023
import org.truffleruby.core.CoreLibrary;
2124
import org.truffleruby.core.inlined.AlwaysInlinedMethodNode;
2225
import org.truffleruby.core.module.RubyModule;
23-
import org.truffleruby.language.Nil;
2426
import org.truffleruby.language.RubyDynamicObject;
2527
import org.truffleruby.language.Visibility;
2628
import org.truffleruby.language.arguments.RubyArguments;
@@ -37,12 +39,14 @@
3739
import com.oracle.truffle.api.profiles.BranchProfile;
3840
import com.oracle.truffle.api.source.SourceSection;
3941

42+
import static org.truffleruby.language.RubyBaseNode.nil;
43+
4044
@CoreModule(value = "Class", isClass = true)
4145
public abstract class ClassNodes {
4246

4347
/** Special constructor for class Class */
4448
@TruffleBoundary
45-
public static RubyClass createClassClass(RubyLanguage language) {
49+
public static RubyClass createClassClassAndBootClasses(RubyLanguage language) {
4650
final RubyClass rubyClass = new RubyClass(language, language.classShape);
4751

4852
assert rubyClass.getLogicalClass() == rubyClass;
@@ -56,22 +60,7 @@ public static RubyClass createClassClass(RubyLanguage language) {
5660
@TruffleBoundary
5761
public static RubyClass createBootClass(RubyLanguage language, RubyClass classClass, Object superclass,
5862
String name) {
59-
final RubyClass rubyClass = new RubyClass(
60-
classClass,
61-
language,
62-
null,
63-
null,
64-
name,
65-
false,
66-
null,
67-
superclass);
68-
rubyClass.fields.setFullName(name);
69-
70-
if (superclass != Nil.INSTANCE) {
71-
rubyClass.setSuperClass((RubyClass) superclass);
72-
}
73-
74-
return rubyClass;
63+
return new RubyClass(classClass, language, null, null, name, false, null, superclass);
7564
}
7665

7766
@TruffleBoundary
@@ -131,46 +120,15 @@ private static RubyClass createRubyClass(RubyContext context,
131120

132121
if (lexicalParent != null) {
133122
rubyClass.fields.getAdoptedByLexicalParent(context, lexicalParent, name, null);
134-
} else if (name != null) { // bootstrap module
135-
rubyClass.fields.setFullName(name);
136-
}
137-
138-
rubyClass.setSuperClass(superclass);
139-
140-
return rubyClass;
141-
}
142-
143-
@TruffleBoundary
144-
public static RubyClass createUninitializedRubyClass(RubyContext context,
145-
SourceSection sourceSection,
146-
RubyClass classClass) {
147-
if (classClass != context.getCoreLibrary().classClass) {
148-
throw CompilerDirectives.shouldNotReachHere("Subclasses of class Class are forbidden in Ruby");
149123
}
150124

151-
final RubyClass rubyClass = new RubyClass(
152-
classClass,
153-
context.getLanguageSlow(),
154-
sourceSection,
155-
null,
156-
null,
157-
false,
158-
null,
159-
null);
160-
161-
// For Class.allocate, set it in the fields but not in RubyClass#superclass to mark as not yet initialized
162-
rubyClass.fields.setSuperClass(context.getCoreLibrary().objectClass);
163-
164-
assert !rubyClass.isInitialized();
165125
return rubyClass;
166126
}
167127

168128
@TruffleBoundary
169-
public static void initialize(RubyContext context, RubyClass rubyClass, RubyClass superclass) {
129+
public static void initialize(RubyContext context, RubyClass rubyClass) {
170130
assert !rubyClass.isSingleton : "Singleton classes can only be created internally";
171131

172-
rubyClass.setSuperClass(superclass);
173-
174132
ensureItHasSingletonClassCreated(context, rubyClass);
175133
}
176134

@@ -209,11 +167,11 @@ private static RubyClass getLazyCreatedSingletonClass(RubyContext context, RubyC
209167
@TruffleBoundary
210168
private static RubyClass createSingletonClass(RubyContext context, RubyClass rubyClass) {
211169
final RubyClass singletonSuperclass;
212-
final RubyClass superclass = getSuperClass(rubyClass);
213-
if (superclass == null) {
170+
final Object superclass = rubyClass.superclass;
171+
if (superclass == nil) {
214172
singletonSuperclass = rubyClass.getLogicalClass();
215173
} else {
216-
singletonSuperclass = getLazyCreatedSingletonClass(context, superclass);
174+
singletonSuperclass = getLazyCreatedSingletonClass(context, (RubyClass) superclass);
217175
}
218176

219177
RubyClass metaClass = ClassNodes.createRubyClass(
@@ -236,15 +194,47 @@ private static RubyClass getClassClass(RubyClass rubyClass) {
236194
return rubyClass.getLogicalClass();
237195
}
238196

239-
@TruffleBoundary
240-
public static RubyClass getSuperClass(RubyClass rubyClass) {
241-
for (RubyModule ancestor : rubyClass.fields.ancestors()) {
242-
if (ancestor instanceof RubyClass && ancestor != rubyClass) {
243-
return (RubyClass) ancestor;
197+
@Primitive(name = "class_new")
198+
public abstract static class NewClassNode extends PrimitiveArrayArgumentsNode {
199+
200+
@Child private InitializeClassNode initializeClassNode;
201+
private final BranchProfile errorProfile = BranchProfile.create();
202+
203+
@Specialization
204+
protected RubyClass newClass(RubyClass superclass, boolean callInherited, Object maybeBlock) {
205+
if (superclass.isSingleton) {
206+
errorProfile.enter();
207+
throw new RaiseException(getContext(), coreExceptions().typeErrorSubclassSingletonClass(this));
208+
}
209+
if (superclass == coreLibrary().classClass) {
210+
errorProfile.enter();
211+
throw new RaiseException(getContext(), coreExceptions().typeErrorSubclassClass(this));
212+
}
213+
214+
final RubyClass newRubyClass = new RubyClass(
215+
coreLibrary().classClass,
216+
getLanguage(),
217+
getEncapsulatingSourceSection(),
218+
null,
219+
null,
220+
false,
221+
null,
222+
superclass);
223+
224+
if (initializeClassNode == null) {
225+
CompilerDirectives.transferToInterpreterAndInvalidate();
226+
initializeClassNode = insert(InitializeClassNodeGen.create());
244227
}
228+
229+
initializeClassNode.executeInitialize(newRubyClass, superclass, callInherited, maybeBlock);
230+
231+
return newRubyClass;
245232
}
246233

247-
return null;
234+
@Specialization(guards = "!isRubyClass(superclass)")
235+
protected RubyClass newClass(Object superclass, boolean callInherited, Object maybeBlock) {
236+
throw new RaiseException(getContext(), coreExceptions().typeErrorSuperclassMustBeClass(this));
237+
}
248238
}
249239

250240
/** #allocate should only be defined as an instance method of Class (Class#allocate), which is required for
@@ -293,78 +283,38 @@ protected RubyClass newSingletonInstance(
293283
}
294284
}
295285

296-
@CoreMethod(names = "initialize", optional = 1, needsBlock = true)
297-
public abstract static class InitializeNode extends CoreMethodArrayArgumentsNode {
298-
299-
@Child private InitializeClassNode initializeClassNode;
300286

287+
@CoreMethod(names = "initialize", optional = 1)
288+
public abstract static class InitializeNode extends CoreMethodArrayArgumentsNode {
301289
@Specialization
302-
protected RubyClass initialize(RubyClass rubyClass, Object maybeSuperclass, Object maybeBlock) {
303-
if (initializeClassNode == null) {
304-
CompilerDirectives.transferToInterpreterAndInvalidate();
305-
initializeClassNode = insert(InitializeClassNodeGen.create(true));
306-
}
307-
308-
return initializeClassNode.executeInitialize(rubyClass, maybeSuperclass, maybeBlock);
290+
protected Object initialize(RubyClass rubyClass, Object maybeSuperclass) {
291+
throw new RaiseException(
292+
getContext(),
293+
getContext().getCoreExceptions().typeErrorAlreadyInitializedClass(this));
309294
}
310-
311295
}
312296

313297
@CoreMethod(names = "inherited", needsSelf = false, required = 1, visibility = Visibility.PRIVATE)
314298
public abstract static class InheritedNode extends CoreMethodArrayArgumentsNode {
315-
316299
@Specialization
317300
protected Object inherited(Object subclass) {
318301
return nil;
319302
}
320-
321303
}
322304

323305
@CoreMethod(names = "superclass")
324306
public abstract static class SuperClassNode extends CoreMethodArrayArgumentsNode {
325-
326-
@Specialization(
327-
guards = { "rubyClass == cachedRubyClass", "cachedSuperclass != null" },
328-
limit = "getCacheLimit()")
329-
protected Object getSuperClass(RubyClass rubyClass,
330-
@Cached("rubyClass") RubyClass cachedRubyClass,
331-
@Cached("fastLookUp(cachedRubyClass)") Object cachedSuperclass) {
332-
// caches only initialized classes, just allocated will go through slow look up
333-
return cachedSuperclass;
334-
}
335-
336-
@Specialization(replaces = "getSuperClass")
337-
protected Object getSuperClassUncached(RubyClass rubyClass,
338-
@Cached BranchProfile errorProfile) {
339-
final Object superclass = fastLookUp(rubyClass);
340-
if (superclass != null) {
341-
return superclass;
342-
} else {
343-
errorProfile.enter();
344-
throw new RaiseException(
345-
getContext(),
346-
getContext().getCoreExceptions().typeError("uninitialized class", this));
347-
}
348-
}
349-
350-
protected Object fastLookUp(RubyClass rubyClass) {
307+
@Specialization
308+
protected Object getSuperClass(RubyClass rubyClass) {
351309
return rubyClass.superclass;
352310
}
353-
354-
protected int getCacheLimit() {
355-
return getLanguage().options.CLASS_CACHE;
356-
}
357311
}
358312

359313
@CoreMethod(names = { "__allocate__", "__layout_allocate__" }, constructor = true, visibility = Visibility.PRIVATE)
360-
public abstract static class AllocateClassNode extends CoreMethodArrayArgumentsNode {
361-
314+
public abstract static class AllocateNode extends UnaryCoreMethodNode {
362315
@Specialization
363-
protected RubyClass allocate(RubyClass classClass) {
364-
return createUninitializedRubyClass(
365-
getContext(),
366-
getEncapsulatingSourceSection(),
367-
classClass);
316+
protected Object allocate(RubyClass rubyClass) {
317+
throw new RaiseException(getContext(), coreExceptions().typeErrorAllocatorUndefinedFor(rubyClass, this));
368318
}
369319
}
370320
}

0 commit comments

Comments
 (0)