Skip to content

Commit 4324ccd

Browse files
committed
Last boostrapping classes/modules decoupled from non-boot
At this point all classes and modules not happening before first threadcontext can now depend on TC being available. This will lead to a lot more lower classes using TC rather than the classes runtime field.
1 parent 54ad99c commit 4324ccd

File tree

9 files changed

+184
-114
lines changed

9 files changed

+184
-114
lines changed

core/src/main/java/org/jruby/MetaClass.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,33 @@ public MetaClass(Ruby runtime, RubyClass superClass, IRubyObject attached) {
5050
*/
5151
MetaClass(Ruby runtime, RubyClass superClass, RubyBasicObject attached) {
5252
super(runtime, superClass, false);
53-
this.attached = attached;
54-
// use same ClassIndex as metaclass, since we're technically still of that type
55-
classIndex(superClass.getClassIndex());
56-
superClass.addSubclass(this);
53+
metaClassInit(superClass, attached);
5754

5855
if (LOG_SINGLETONS || LOG_SINGLETONS_VERBOSE) {
5956
logSingleton(runtime, superClass, attached);
6057
}
6158
}
6259

60+
/**
61+
* This is an internal API only used by Ruby constructor before ThreadContext exists. This is for bootstrapping
62+
* only.
63+
* @param runtime
64+
* @param superClass
65+
* @param Class
66+
* @param attached
67+
*/
68+
MetaClass(Ruby runtime, RubyClass superClass, RubyClass Class, RubyBasicObject attached) {
69+
super(runtime, superClass, Class);
70+
metaClassInit(superClass, attached);
71+
}
72+
73+
private void metaClassInit(RubyClass superClass, RubyBasicObject attached) {
74+
this.attached = attached;
75+
// use same ClassIndex as metaclass, since we're technically still of that type
76+
classIndex(superClass.getClassIndex());
77+
superClass.addSubclass(this);
78+
}
79+
6380
private static void logSingleton(Ruby runtime, RubyClass superClass, RubyBasicObject attached) {
6481
if (runtime.isBooting()) return; // don't log singleton created during boot
6582

core/src/main/java/org/jruby/Ruby.java

Lines changed: 97 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@
217217
import static org.jruby.api.Access.errnoModule;
218218
import static org.jruby.api.Access.loadService;
219219
import static org.jruby.api.Convert.asFixnum;
220+
import static org.jruby.api.Convert.asSymbol;
220221
import static org.jruby.api.Create.newEmptyString;
221222
import static org.jruby.api.Create.newFrozenString;
222223
import static org.jruby.api.Error.*;
@@ -335,36 +336,35 @@ private Ruby(RubyInstanceConfig config) {
335336
classClass.setMetaClass(classClass);
336337
refinementClass.setMetaClass(classClass);
337338

338-
RubyClass metaClass;
339-
metaClass = basicObjectClass.makeMetaClass(classClass); // runtime needed for 3 things:
340-
metaClass = objectClass.makeMetaClass(metaClass); // 1. ObjectSpace
341-
metaClass = moduleClass.makeMetaClass(metaClass); // 2. Access 'Class' for makeMetaClass
342-
classClass.makeMetaClass(metaClass); // 3. booting check to ignore logging singletons
343-
refinementClass.makeMetaClass(metaClass);
339+
var metaClass = basicObjectClass.makeMetaClassBootstrap(classClass, classClass);
340+
metaClass = objectClass.makeMetaClassBootstrap(metaClass, classClass);
341+
metaClass = moduleClass.makeMetaClassBootstrap(metaClass, classClass);
342+
classClass.makeMetaClassBootstrap(metaClass, classClass);
343+
refinementClass.makeMetaClassBootstrap(metaClass, classClass);
344344

345-
RubyObject.createObjectClass(objectClass);
346-
RubyModule.createModuleClass(moduleClass);
347-
RubyClass.createClassClass(this, classClass);
345+
RubyObject.finishObjectClass(objectClass);
346+
RubyModule.finishModuleClass(moduleClass);
347+
RubyClass.finishClassClass(this, classClass);
348348

349349
// set constants now that they're initialized
350-
basicObjectClass.setConstant("BasicObject", basicObjectClass); // FIXME: We know these sets will work so make something primal
351-
objectClass.setConstant("BasicObject", basicObjectClass); // just for these (to remove all checks which would require
352-
objectClass.setConstant("Object", objectClass); // runtime.
353-
objectClass.setConstant("Class", classClass);
354-
objectClass.setConstant("Module", moduleClass);
355-
objectClass.setConstant("Refinement", refinementClass);
350+
basicObjectClass.defineConstantBootstrap("BasicObject", basicObjectClass);
351+
objectClass.defineConstantBootstrap("BasicObject", basicObjectClass);
352+
objectClass.defineConstantBootstrap("Object", objectClass);
353+
objectClass.defineConstantBootstrap("Class", classClass);
354+
objectClass.defineConstantBootstrap("Module", moduleClass);
355+
objectClass.defineConstantBootstrap("Refinement", refinementClass);
356356

357357
// specializer for RubyObject subclasses
358358
objectSpecializer = new RubyObjectSpecializer(this);
359359

360-
kernelModule = defineModuleUnder("Kernel", objectClass); // Initialize Kernel and include into Object
360+
kernelModule = defineModuleBootstrap("Kernel"); // Initialize Kernel and include into Object
361361
topSelf = new RubyObject(this, objectClass); // Object is ready, create top self
362362

363363
// nil, true, and false all are set in TC so they need to be created above (both class and instances).
364-
// their methods are added afterwards since no dispatch happens until after first TC is defined.
365-
nilClass = defineClass("NilClass", objectClass, NOT_ALLOCATABLE_ALLOCATOR);
366-
falseClass = defineClass("FalseClass", objectClass, NOT_ALLOCATABLE_ALLOCATOR);
367-
trueClass = defineClass("TrueClass", objectClass, NOT_ALLOCATABLE_ALLOCATOR);
364+
// their methods are added afterward since no dispatch happens until after first TC is defined.
365+
nilClass = defineClassBootstrap("NilClass");
366+
falseClass = defineClassBootstrap("FalseClass");
367+
trueClass = defineClassBootstrap("TrueClass");
368368

369369
nilObject = new RubyNil(this, nilClass);
370370
nilPrefilledArray = new IRubyObject[NIL_PREFILLED_ARRAY_SIZE];
@@ -1389,24 +1389,6 @@ public RubyClass fastGetClass(String internedName) {
13891389
return getClass(internedName);
13901390
}
13911391

1392-
/**
1393-
* Define a new class under the Object namespace. Roughly equivalent to
1394-
* rb_define_class in MRI. This is an internal API. Please use
1395-
* {@link org.jruby.api.Define#defineClass(ThreadContext, String, RubyClass, ObjectAllocator)} instead.
1396-
* For bootstrappin we still need a non-ThreadContext define for nil and boolean class definition
1397-
* as they are required before we can initialize our topmost ThreadContext.
1398-
*
1399-
* @param name The name for the new class
1400-
* @param superClass The super class for the new class
1401-
* @param allocator An ObjectAllocator instance that can construct
1402-
* instances of the new class.
1403-
* @return The new class
1404-
*/
1405-
@Extension
1406-
public RubyClass defineClass(String name, RubyClass superClass, ObjectAllocator allocator) {
1407-
return defineClassUnder(name, superClass, allocator, objectClass, null);
1408-
}
1409-
14101392
/**
14111393
* A variation of defineClass that allows passing in an array of supplementary
14121394
* call sites for improving dynamic invocation performance.
@@ -1419,7 +1401,12 @@ public RubyClass defineClass(String name, RubyClass superClass, ObjectAllocator
14191401
*/
14201402
@Deprecated(since = "10.0")
14211403
public RubyClass defineClass(String name, RubyClass superClass, ObjectAllocator allocator, CallSite[] callSites) {
1422-
return defineClassUnder(name, superClass, allocator, objectClass, callSites);
1404+
return defineClassUnder(getCurrentContext(), name, superClass, allocator, objectClass, callSites);
1405+
}
1406+
1407+
@Deprecated(since = "10.0")
1408+
public RubyClass defineClass(String name, RubyClass superClass, ObjectAllocator allocator) {
1409+
return defineClassUnder(getCurrentContext(), name, superClass, allocator, objectClass, null);
14231410
}
14241411

14251412
/**
@@ -1440,15 +1427,20 @@ public RubyClass defineClass(String name, RubyClass superClass, ObjectAllocator
14401427
@Extension
14411428
@Deprecated(since = "10.0")
14421429
public RubyClass defineClassUnder(String name, RubyClass superClass, ObjectAllocator allocator, RubyModule parent) {
1443-
return defineClassUnder(name, superClass, allocator, parent, null);
1430+
return defineClassUnder(runtimeError.getCurrentContext(), name, superClass, allocator, parent, null);
1431+
}
1432+
1433+
@Deprecated(since = "10.0")
1434+
public RubyClass defineClassUnder(String id, RubyClass superClass, ObjectAllocator allocator, RubyModule parent, CallSite[] callSites) {
1435+
return defineClassUnder(getCurrentContext(), id, superClass, allocator, parent, callSites);
14441436
}
14451437

14461438
/**
14471439
* A variation of defineClassUnder that allows passing in an array of
14481440
* supplementary call sites to improve dynamic invocation. This is an internal API. Please
1449-
* use {@link org.jruby.api.Define#defineClassUnder(ThreadContext, String, RubyClass, ObjectAllocator, RubyModule)}
1450-
* or {@link org.jruby.RubyModule#defineClassUnder(ThreadContext, String, RubyClass, ObjectAllocator)} instead.
1441+
* use {@link org.jruby.RubyModule#defineClassUnder(ThreadContext, String, RubyClass, ObjectAllocator)} instead.
14511442
*
1443+
* @param context the current thread context
14521444
* @param id The name for the new class as an ISO-8859_1 String (id-value)
14531445
* @param superClass The super class for the new class
14541446
* @param allocator An ObjectAllocator instance that can construct
@@ -1457,32 +1449,38 @@ public RubyClass defineClassUnder(String name, RubyClass superClass, ObjectAlloc
14571449
* @param callSites The array of call sites to add
14581450
* @return The new class
14591451
*/
1460-
public RubyClass defineClassUnder(String id, RubyClass superClass, ObjectAllocator allocator, RubyModule parent, CallSite[] callSites) {
1461-
IRubyObject classObj = parent.getConstantAt(id);
1462-
1463-
if (classObj != null) {
1464-
if (!(classObj instanceof RubyClass)) throw typeError(getCurrentContext(), str(this, ids(this, id), " is not a class"));
1465-
RubyClass klazz = (RubyClass)classObj;
1466-
if (klazz.getSuperClass().getRealClass() != superClass) {
1467-
throw newNameError(str(this, ids(this, id), " is already defined"), newSymbol(id));
1468-
}
1469-
// If we define a class in Ruby, but later want to allow it to be defined in Java,
1470-
// the allocator needs to be updated
1471-
if (klazz.getAllocator() != allocator) klazz.allocator(allocator);
1472-
return klazz;
1473-
}
1452+
public RubyClass defineClassUnder(ThreadContext context, String id, RubyClass superClass, ObjectAllocator allocator,
1453+
RubyModule parent, CallSite[] callSites) {
1454+
IRubyObject object = parent.getConstantAt(id);
1455+
if (object != null) return foundExistingClass(context, id, superClass, allocator, object);
14741456

14751457
boolean parentIsObject = parent == objectClass;
14761458

1477-
if (superClass == null) {
1478-
IRubyObject className = parentIsObject ? ids(this, id) :
1479-
parent.toRubyString(getCurrentContext()).append(newString("::")).append(ids(this, id));
1480-
warn(getCurrentContext(), str(this, "no super class for '", className, "', Object assumed"));
1459+
if (superClass == null) superClass = determineSuperClass(context, id, parent, parentIsObject);
1460+
1461+
return RubyClass.newClass(this, superClass, id, allocator, parent, !parentIsObject, callSites);
1462+
}
1463+
1464+
private RubyClass determineSuperClass(ThreadContext context, String id, RubyModule parent, boolean parentIsObject) {
1465+
RubyClass superClass;
1466+
IRubyObject className = parentIsObject ? ids(this, id) :
1467+
parent.toRubyString(context).append(newString("::")).append(ids(this, id));
1468+
warn(context, str(this, "no super class for '", className, "', Object assumed"));
1469+
1470+
superClass = objectClass;
1471+
return superClass;
1472+
}
14811473

1482-
superClass = objectClass;
1474+
private RubyClass foundExistingClass(ThreadContext context, String id, RubyClass superClass, ObjectAllocator allocator, IRubyObject obj) {
1475+
if (!(obj instanceof RubyClass klazz)) throw typeError(context, str(this, ids(this, id), " is not a class"));
1476+
1477+
if (klazz.getSuperClass().getRealClass() != superClass) {
1478+
throw newNameError(str(this, ids(this, id), " is already defined"), asSymbol(context, id));
14831479
}
1480+
// If we define a class in Ruby, but later want to allow it to be defined in Java, the allocator needs to be updated
1481+
if (klazz.getAllocator() != allocator) klazz.allocator(allocator);
14841482

1485-
return RubyClass.newClass(this, superClass, id, allocator, parent, !parentIsObject, callSites);
1483+
return klazz;
14861484
}
14871485

14881486
/**
@@ -1495,7 +1493,32 @@ public RubyClass defineClassUnder(String id, RubyClass superClass, ObjectAllocat
14951493
@Deprecated(since = "10.0")
14961494
@Extension
14971495
public RubyModule defineModule(String name) {
1498-
return defineModuleUnder(name, objectClass);
1496+
return defineModuleUnder(getCurrentContext(), name, objectClass);
1497+
}
1498+
1499+
/**
1500+
* This is only for defining nil,true,false. The reason we have this is so all other define methods
1501+
* can count on ThreadContext being available. These are defined before that point.
1502+
* @param name The name for the new class
1503+
* @return The new class
1504+
*/
1505+
public RubyClass defineClassBootstrap(String name) {
1506+
return RubyClass.newClass(this, objectClass, name, NOT_ALLOCATABLE_ALLOCATOR, objectClass, false, null);
1507+
}
1508+
1509+
/**
1510+
* This is only for defining kernel. The reason we have this is so all other define methods
1511+
* can count on ThreadContext being available. These are defined before that point.
1512+
* @param name The name for the new class
1513+
* @return The new module
1514+
*/
1515+
public RubyModule defineModuleBootstrap(String name) {
1516+
return RubyModule.newModule(this, name, objectClass, false);
1517+
}
1518+
1519+
@Deprecated(since = "10.0")
1520+
public RubyModule defineModuleUnder(String name, RubyModule parent) {
1521+
return defineModuleUnder(getCurrentContext(), name, parent);
14991522
}
15001523

15011524
/**
@@ -1509,22 +1532,24 @@ public RubyModule defineModule(String name) {
15091532
* module
15101533
* @return The new module
15111534
*/
1512-
@Extension
1513-
public RubyModule defineModuleUnder(String name, RubyModule parent) {
1535+
public RubyModule defineModuleUnder(ThreadContext context, String name, RubyModule parent) {
15141536
IRubyObject moduleObj = parent.getConstantAt(name);
15151537

15161538
boolean parentIsObject = parent == objectClass;
15171539

1518-
if (moduleObj != null) {
1519-
if (moduleObj.isModule()) return (RubyModule) moduleObj;
1540+
return moduleObj != null ?
1541+
foundExistingModule(context, parent, moduleObj, parentIsObject) :
1542+
RubyModule.newModule(this, name, parent, !parentIsObject);
1543+
}
15201544

1521-
RubyString typeName = parentIsObject ?
1522-
types(this, moduleObj.getMetaClass()) : types(this, parent, moduleObj.getMetaClass());
1545+
private RubyModule foundExistingModule(ThreadContext context, RubyModule parent, IRubyObject moduleObj,
1546+
boolean parentIsObject) {
1547+
if (moduleObj.isModule()) return (RubyModule) moduleObj;
15231548

1524-
throw typeError(getCurrentContext(), str(this, typeName, " is not a module"));
1525-
}
1549+
RubyString typeName = parentIsObject ?
1550+
types(this, moduleObj.getMetaClass()) : types(this, parent, moduleObj.getMetaClass());
15261551

1527-
return RubyModule.newModule(this, name, parent, !parentIsObject);
1552+
throw typeError(context, str(this, typeName, " is not a module"));
15281553
}
15291554

15301555
/**

core/src/main/java/org/jruby/RubyBasicObject.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,23 @@ public RubyClass makeMetaClass(RubyClass superClass) {
500500
return klass;
501501
}
502502

503+
/**
504+
* This will create a new metaclass. This is only used during bootstrapping before
505+
* the initial ThreadContext is defined. Normal needs of making a metaclass should use
506+
* {@link RubyBasicObject#makeMetaClass(RubyClass)}
507+
* @param superClass
508+
* @param Class
509+
* @return
510+
*/
511+
public RubyClass makeMetaClassBootstrap(RubyClass superClass, RubyClass Class) {
512+
MetaClass klass = new MetaClass(getRuntime(), superClass, Class, this); // rb_class_boot
513+
setMetaClass(klass);
514+
515+
klass.setMetaClass(superClass.getRealClass().metaClass);
516+
517+
return klass;
518+
}
519+
503520
/**
504521
* Makes it possible to change the metaclass of an object. In
505522
* practice, this is a simple version of Smalltalks Become, except

core/src/main/java/org/jruby/RubyClass.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public class RubyClass extends RubyModule {
120120
private static final Logger LOG = LoggerFactory.getLogger(RubyClass.class);
121121
private static final double SUBCLASSES_CLEAN_FACTOR = 0.25;
122122

123-
public static void createClassClass(Ruby runtime, RubyClass Class) {
123+
public static void finishClassClass(Ruby runtime, RubyClass Class) {
124124
Class.reifiedClass(RubyClass.class).
125125
kindOf(new RubyModule.JavaClassKindOf(RubyClass.class)).
126126
classIndex(ClassIndex.CLASS);
@@ -431,6 +431,25 @@ protected RubyClass(Ruby runtime, RubyClass superClass, boolean objectSpace) {
431431
superClass(superClass); // this is the only case it might be null here (in MetaClass construction)
432432
}
433433

434+
/**
435+
* This is an internal API only used by Ruby constructor before ThreadContext exists.
436+
* @param runtime
437+
* @param superClass
438+
* @param Class
439+
*/
440+
protected RubyClass(Ruby runtime, RubyClass superClass, RubyClass Class) {
441+
super(runtime, Class, false);
442+
443+
this.runtime = runtime;
444+
if ((this.realClass = superClass.realClass) != null) {
445+
this.variableTableManager = realClass.variableTableManager;
446+
} else {
447+
this.variableTableManager = new VariableTableManager(this);
448+
}
449+
450+
superClass(superClass);
451+
}
452+
434453
/** used by CLASS_ALLOCATOR (any Class' class will be a Class!)
435454
* also used to bootstrap Object class
436455
*/

core/src/main/java/org/jruby/RubyConverter.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@
6161
import static org.jruby.api.Convert.*;
6262
import static org.jruby.api.Create.newArray;
6363
import static org.jruby.api.Create.newString;
64-
import static org.jruby.api.Define.defineClassUnder;
6564
import static org.jruby.api.Error.*;
6665
import static org.jruby.runtime.Visibility.PRIVATE;
6766

@@ -121,7 +120,7 @@ public class RubyConverter extends RubyObject {
121120
}
122121

123122
public static RubyClass createConverterClass(ThreadContext context, RubyClass Object, RubyClass Encoding) {
124-
return defineClassUnder(context, "Converter", Object, RubyConverter::new, Encoding).
123+
return Encoding.defineClassUnder(context, "Converter", Object, RubyConverter::new).
125124
reifiedClass(RubyConverter.class).
126125
kindOf(new RubyModule.JavaClassKindOf(RubyConverter.class)).
127126
classIndex(ClassIndex.CONVERTER).

0 commit comments

Comments
 (0)