forked from Mirror/wren
Clean up the root of the class/metaclass hierarchy.
This commit is contained in:
@ -448,9 +448,9 @@ DEF_NATIVE(os_clock)
|
||||
return NUM_VAL(time);
|
||||
}
|
||||
|
||||
static ObjClass* defineClass(WrenVM* vm, const char* name, ObjClass* superclass)
|
||||
static ObjClass* defineClass(WrenVM* vm, const char* name)
|
||||
{
|
||||
ObjClass* classObj = wrenNewClass(vm, superclass, 0);
|
||||
ObjClass* classObj = wrenNewClass(vm, vm->objectClass, 0);
|
||||
int symbol = addSymbol(&vm->globalSymbols, name, strlen(name));
|
||||
vm->globals[symbol] = OBJ_VAL(classObj);
|
||||
return classObj;
|
||||
@ -458,20 +458,54 @@ static ObjClass* defineClass(WrenVM* vm, const char* name, ObjClass* superclass)
|
||||
|
||||
void wrenInitializeCore(WrenVM* vm)
|
||||
{
|
||||
vm->objectClass = defineClass(vm, "Object", NULL);
|
||||
// Define the root Object class. This has to be done a little specially
|
||||
// because it has no superclass and an unusual metaclass (Class).
|
||||
vm->objectClass = wrenNewSingleClass(vm, 0);
|
||||
int objectSymbol = addSymbol(&vm->globalSymbols, "Object", strlen("Object"));
|
||||
vm->globals[objectSymbol] = OBJ_VAL(vm->objectClass);
|
||||
|
||||
NATIVE(vm->objectClass, "== ", object_eqeq);
|
||||
NATIVE(vm->objectClass, "!= ", object_bangeq);
|
||||
NATIVE(vm->objectClass, "new", object_new);
|
||||
NATIVE(vm->objectClass, "type", object_type);
|
||||
|
||||
// The "Class" class is the superclass of all metaclasses.
|
||||
vm->classClass = defineClass(vm, "Class", vm->objectClass);
|
||||
// Now we can define Class, which is a subclass of Object, but Object's
|
||||
// metaclass.
|
||||
vm->classClass = wrenNewSingleClass(vm, 0);
|
||||
int classSymbol = addSymbol(&vm->globalSymbols, "Class", strlen("Class"));
|
||||
vm->globals[classSymbol] = OBJ_VAL(vm->classClass);
|
||||
|
||||
vm->boolClass = defineClass(vm, "Bool", vm->objectClass);
|
||||
// Now that Object and Class are defined, we can wire them up to each other.
|
||||
wrenBindSuperclass(vm->classClass, vm->objectClass);
|
||||
vm->objectClass->metaclass = vm->classClass;
|
||||
vm->classClass->metaclass = vm->classClass;
|
||||
|
||||
// The core class diagram ends up looking like this, where single lines point
|
||||
// to a class's superclass, and double lines point to its metaclass:
|
||||
//
|
||||
// __________ /====\
|
||||
// / \ // \\
|
||||
// v \ v \\
|
||||
// .---------. .--------------. //
|
||||
// | Object |==>| Class |==/
|
||||
// '---------' '--------------'
|
||||
// ^ ^
|
||||
// | |
|
||||
// .---------. .--------------. \
|
||||
// | Base |==>| Base.type | |
|
||||
// '---------' '--------------' |
|
||||
// ^ ^ | Hypothetical example classes
|
||||
// | | |
|
||||
// .---------. .--------------. |
|
||||
// | Derived |==>| Derived.type | |
|
||||
// '---------' '--------------' /
|
||||
|
||||
// The rest of the classes can not be defined normally.
|
||||
vm->boolClass = defineClass(vm, "Bool");
|
||||
NATIVE(vm->boolClass, "toString", bool_toString);
|
||||
NATIVE(vm->boolClass, "!", bool_not);
|
||||
|
||||
vm->fnClass = defineClass(vm, "Function", vm->objectClass);
|
||||
vm->fnClass = defineClass(vm, "Function");
|
||||
FIBER_NATIVE(vm->fnClass, "call", fn_call0);
|
||||
FIBER_NATIVE(vm->fnClass, "call ", fn_call1);
|
||||
FIBER_NATIVE(vm->fnClass, "call ", fn_call2);
|
||||
@ -490,7 +524,7 @@ void wrenInitializeCore(WrenVM* vm)
|
||||
FIBER_NATIVE(vm->fnClass, "call ", fn_call15);
|
||||
FIBER_NATIVE(vm->fnClass, "call ", fn_call16);
|
||||
|
||||
vm->listClass = defineClass(vm, "List", vm->objectClass);
|
||||
vm->listClass = defineClass(vm, "List");
|
||||
NATIVE(vm->listClass, "add ", list_add);
|
||||
NATIVE(vm->listClass, "clear", list_clear);
|
||||
NATIVE(vm->listClass, "count", list_count);
|
||||
@ -499,10 +533,10 @@ void wrenInitializeCore(WrenVM* vm)
|
||||
NATIVE(vm->listClass, "[ ]", list_subscript);
|
||||
NATIVE(vm->listClass, "[ ]=", list_subscriptSetter);
|
||||
|
||||
vm->nullClass = defineClass(vm, "Null", vm->objectClass);
|
||||
vm->nullClass = defineClass(vm, "Null");
|
||||
NATIVE(vm->nullClass, "toString", null_toString);
|
||||
|
||||
vm->numClass = defineClass(vm, "Num", vm->objectClass);
|
||||
vm->numClass = defineClass(vm, "Num");
|
||||
NATIVE(vm->numClass, "abs", num_abs);
|
||||
NATIVE(vm->numClass, "toString", num_toString)
|
||||
NATIVE(vm->numClass, "-", num_negate);
|
||||
@ -517,12 +551,12 @@ void wrenInitializeCore(WrenVM* vm)
|
||||
NATIVE(vm->numClass, ">= ", num_gte);
|
||||
NATIVE(vm->numClass, "~", num_bitwiseNot);
|
||||
|
||||
// TODO: The only reason there are here is so that 0 != -0. Is that what we
|
||||
// want?
|
||||
// These are defined just so that 0 and -0 are equal, which is specified by
|
||||
// IEEE 754 even though they have different bit representations.
|
||||
NATIVE(vm->numClass, "== ", num_eqeq);
|
||||
NATIVE(vm->numClass, "!= ", num_bangeq);
|
||||
|
||||
vm->stringClass = defineClass(vm, "String", vm->objectClass);
|
||||
vm->stringClass = defineClass(vm, "String");
|
||||
NATIVE(vm->stringClass, "contains ", string_contains);
|
||||
NATIVE(vm->stringClass, "count", string_count);
|
||||
NATIVE(vm->stringClass, "toString", string_toString)
|
||||
@ -531,10 +565,10 @@ void wrenInitializeCore(WrenVM* vm)
|
||||
NATIVE(vm->stringClass, "!= ", string_bangeq);
|
||||
NATIVE(vm->stringClass, "[ ]", string_subscript);
|
||||
|
||||
ObjClass* ioClass = defineClass(vm, "IO", vm->objectClass);
|
||||
ObjClass* ioClass = defineClass(vm, "IO");
|
||||
NATIVE(ioClass->metaclass, "write ", io_write);
|
||||
|
||||
ObjClass* osClass = defineClass(vm, "OS", vm->objectClass);
|
||||
ObjClass* osClass = defineClass(vm, "OS");
|
||||
NATIVE(osClass->metaclass, "clock", os_clock);
|
||||
|
||||
// TODO: Make this a distinct object type.
|
||||
|
||||
@ -17,63 +17,65 @@ static void initObj(WrenVM* vm, Obj* obj, ObjType type)
|
||||
vm->first = obj;
|
||||
}
|
||||
|
||||
static ObjClass* newClass(WrenVM* vm, ObjClass* metaclass,
|
||||
ObjClass* superclass, int numFields)
|
||||
ObjClass* wrenNewSingleClass(WrenVM* vm, int numFields)
|
||||
{
|
||||
ObjClass* obj = allocate(vm, sizeof(ObjClass));
|
||||
initObj(vm, &obj->obj, OBJ_CLASS);
|
||||
obj->metaclass = metaclass;
|
||||
obj->superclass = superclass;
|
||||
ObjClass* classObj = allocate(vm, sizeof(ObjClass));
|
||||
initObj(vm, &classObj->obj, OBJ_CLASS);
|
||||
classObj->metaclass = NULL;
|
||||
classObj->superclass = NULL;
|
||||
classObj->numFields = numFields;
|
||||
|
||||
if (superclass != NULL)
|
||||
// Clear out the method table.
|
||||
for (int i = 0; i < MAX_SYMBOLS; i++)
|
||||
{
|
||||
obj->numFields = superclass->numFields + numFields;
|
||||
|
||||
// Inherit methods from its superclass.
|
||||
for (int i = 0; i < MAX_SYMBOLS; i++)
|
||||
{
|
||||
obj->methods[i] = superclass->methods[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
obj->numFields = numFields;
|
||||
|
||||
// Object has no superclass, so just clear these out.
|
||||
for (int i = 0; i < MAX_SYMBOLS; i++)
|
||||
{
|
||||
obj->methods[i].type = METHOD_NONE;
|
||||
}
|
||||
classObj->methods[i].type = METHOD_NONE;
|
||||
}
|
||||
|
||||
return obj;
|
||||
return classObj;
|
||||
}
|
||||
|
||||
void wrenBindSuperclass(ObjClass* subclass, ObjClass* superclass)
|
||||
{
|
||||
ASSERT(superclass != NULL, "Must have superclass.");
|
||||
|
||||
subclass->superclass = superclass;
|
||||
|
||||
// Include the superclass in the total number of fields.
|
||||
subclass->numFields += superclass->numFields;
|
||||
|
||||
// Inherit methods from its superclass.
|
||||
for (int i = 0; i < MAX_SYMBOLS; i++)
|
||||
{
|
||||
subclass->methods[i] = superclass->methods[i];
|
||||
}
|
||||
}
|
||||
|
||||
ObjClass* wrenNewClass(WrenVM* vm, ObjClass* superclass, int numFields)
|
||||
{
|
||||
// Make the metaclass.
|
||||
// TODO: What is the metaclass's metaclass?
|
||||
// Create the metaclass.
|
||||
// TODO: Handle static fields.
|
||||
ObjClass* metaclass = wrenNewSingleClass(vm, 0);
|
||||
metaclass->metaclass = vm->classClass;
|
||||
|
||||
// The metaclass inheritance chain mirrors the class's inheritance chain
|
||||
// except that when the latter bottoms out at "Object", the metaclass one
|
||||
// bottoms out at "Class".
|
||||
ObjClass* metaclassSuperclass;
|
||||
if (superclass == vm->objectClass)
|
||||
{
|
||||
metaclassSuperclass = vm->classClass;
|
||||
wrenBindSuperclass(metaclass, vm->classClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
metaclassSuperclass = superclass->metaclass;
|
||||
wrenBindSuperclass(metaclass, superclass->metaclass);
|
||||
}
|
||||
|
||||
ObjClass* metaclass = newClass(vm, NULL, metaclassSuperclass, 0);
|
||||
|
||||
// Make sure it isn't collected when we allocate the metaclass.
|
||||
PinnedObj pinned;
|
||||
pinObj(vm, (Obj*)metaclass, &pinned);
|
||||
|
||||
ObjClass* classObj = newClass(vm, metaclass, superclass, numFields);
|
||||
ObjClass* classObj = wrenNewSingleClass(vm, numFields);
|
||||
classObj->metaclass = metaclass;
|
||||
wrenBindSuperclass(classObj, superclass);
|
||||
|
||||
unpinObj(vm);
|
||||
|
||||
|
||||
@ -408,7 +408,17 @@ typedef struct
|
||||
|
||||
#endif
|
||||
|
||||
// Creates a new class object.
|
||||
// Creates a new "raw" class. It has no metaclass or superclass whatsoever.
|
||||
// This is only used for bootstrapping the initial Object and Class classes,
|
||||
// which are a little special.
|
||||
ObjClass* wrenNewSingleClass(WrenVM* vm, int numFields);
|
||||
|
||||
// Makes [superclass] the superclass of [subclass], and causes subclass to
|
||||
// inherit its methods. This should be called before any methods are defined
|
||||
// on subclass.
|
||||
void wrenBindSuperclass(ObjClass* subclass, ObjClass* superclass);
|
||||
|
||||
// Creates a new class object as well as its associated metaclass.
|
||||
ObjClass* wrenNewClass(WrenVM* vm, ObjClass* superclass, int numFields);
|
||||
|
||||
// Creates a new closure object that invokes [fn]. Allocates room for its
|
||||
|
||||
10
test/class/type.wren
Normal file
10
test/class/type.wren
Normal file
@ -0,0 +1,10 @@
|
||||
class Foo {}
|
||||
|
||||
// A class is a class.
|
||||
IO.write(Foo is Class) // expect: true
|
||||
|
||||
// It's metatype is also a class.
|
||||
IO.write(Foo.type is Class) // expect: true
|
||||
|
||||
// The metatype's metatype is Class.
|
||||
IO.write(Foo.type.type == Class) // expect: true
|
||||
Reference in New Issue
Block a user