Skip to content

Commit 4e925df

Browse files
authored
Merge pull request jruby#8534 from enebo/last_primal
Last boostrapping classes/modules decoupled from non-boot
2 parents 65dec63 + ab15259 commit 4e925df

File tree

16 files changed

+282
-157
lines changed

16 files changed

+282
-157
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: 87 additions & 75 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 = RubyClass.newClassBootstrap(this, objectClass, "NilClass");
366+
falseClass = RubyClass.newClassBootstrap(this, objectClass, "FalseClass");
367+
trueClass = RubyClass.newClassBootstrap(this, objectClass, "TrueClass");
368368

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

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

14271414
/**
@@ -1442,15 +1429,20 @@ public RubyClass defineClass(String name, RubyClass superClass, ObjectAllocator
14421429
@Extension
14431430
@Deprecated(since = "10.0")
14441431
public RubyClass defineClassUnder(String name, RubyClass superClass, ObjectAllocator allocator, RubyModule parent) {
1445-
return defineClassUnder(name, superClass, allocator, parent, null);
1432+
return defineClassUnder(runtimeError.getCurrentContext(), name, superClass, allocator, parent, null);
1433+
}
1434+
1435+
@Deprecated(since = "10.0")
1436+
public RubyClass defineClassUnder(String id, RubyClass superClass, ObjectAllocator allocator, RubyModule parent, CallSite[] callSites) {
1437+
return defineClassUnder(getCurrentContext(), id, superClass, allocator, parent, callSites);
14461438
}
14471439

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

14771459
boolean parentIsObject = parent == objectClass;
14781460

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

1484-
superClass = objectClass;
1477+
if (klazz.getSuperClass().getRealClass() != superClass) {
1478+
throw newNameError(str(this, ids(this, id), " is already defined"), asSymbol(context, id));
14851479
}
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);
14861482

1487-
return RubyClass.newClass(this, superClass, id, allocator, parent, !parentIsObject, callSites);
1483+
return klazz;
14881484
}
14891485

14901486
/**
@@ -1495,9 +1491,23 @@ public RubyClass defineClassUnder(String id, RubyClass superClass, ObjectAllocat
14951491
* @return The new module
14961492
*/
14971493
@Deprecated(since = "10.0")
1498-
@Extension
14991494
public RubyModule defineModule(String name) {
1500-
return defineModuleUnder(name, objectClass);
1495+
return defineModuleUnder(getCurrentContext(), name, objectClass);
1496+
}
1497+
1498+
/**
1499+
* This is only for defining kernel. The reason we have this is so all other define methods
1500+
* can count on ThreadContext being available. These are defined before that point.
1501+
* @param name The name for the new class
1502+
* @return The new module
1503+
*/
1504+
public RubyModule defineModuleBootstrap(String name) {
1505+
return RubyModule.newModuleBootstrap(this, name, objectClass);
1506+
}
1507+
1508+
@Deprecated(since = "10.0")
1509+
public RubyModule defineModuleUnder(String name, RubyModule parent) {
1510+
return defineModuleUnder(getCurrentContext(), name, parent);
15011511
}
15021512

15031513
/**
@@ -1511,22 +1521,24 @@ public RubyModule defineModule(String name) {
15111521
* module
15121522
* @return The new module
15131523
*/
1514-
@Extension
1515-
public RubyModule defineModuleUnder(String name, RubyModule parent) {
1524+
public RubyModule defineModuleUnder(ThreadContext context, String name, RubyModule parent) {
15161525
IRubyObject moduleObj = parent.getConstantAt(name);
15171526

15181527
boolean parentIsObject = parent == objectClass;
15191528

1520-
if (moduleObj != null) {
1521-
if (moduleObj.isModule()) return (RubyModule) moduleObj;
1529+
return moduleObj != null ?
1530+
foundExistingModule(context, parent, moduleObj, parentIsObject) :
1531+
RubyModule.newModule(context, name, parent, !parentIsObject, null, -1);
1532+
}
15221533

1523-
RubyString typeName = parentIsObject ?
1524-
types(this, moduleObj.getMetaClass()) : types(this, parent, moduleObj.getMetaClass());
1534+
private RubyModule foundExistingModule(ThreadContext context, RubyModule parent, IRubyObject moduleObj,
1535+
boolean parentIsObject) {
1536+
if (moduleObj.isModule()) return (RubyModule) moduleObj;
15251537

1526-
throw typeError(getCurrentContext(), str(this, typeName, " is not a module"));
1527-
}
1538+
RubyString typeName = parentIsObject ?
1539+
types(this, moduleObj.getMetaClass()) : types(this, parent, moduleObj.getMetaClass());
15281540

1529-
return RubyModule.newModule(this, name, parent, !parentIsObject);
1541+
throw typeError(context, str(this, typeName, " is not a module"));
15301542
}
15311543

15321544
/**
@@ -3037,7 +3049,7 @@ public void loadScope(IRScope scope, boolean wrap) {
30373049

30383050
if (wrap) {
30393051
// toss an anonymous module into the search path
3040-
scope.getStaticScope().setModule(RubyModule.newModule(this));
3052+
scope.getStaticScope().setModule(new RubyModule(this));
30413053
}
30423054

30433055
runInterpreter(getCurrentContext(), scope, self);
@@ -3070,7 +3082,7 @@ public StaticScope setupWrappedToplevel(IRubyObject self, StaticScope top) {
30703082
RubyModule wrapper = loadService.getWrapperSelf();
30713083

30723084
if (wrapper == null || wrapper.isNil()) {
3073-
wrapper = RubyModule.newModule(this);
3085+
wrapper = new RubyModule(this);
30743086
}
30753087

30763088
// toss an anonymous module into the search path

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

0 commit comments

Comments
 (0)