From 2ec01c558fe0872bbffd8fb058c6b0f57b22b57c Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Sat, 21 Dec 2013 20:44:37 -0800 Subject: [PATCH] Clean up the root of the class/metaclass hierarchy. --- src/wren_core.c | 64 +++++++++++++++++++++++++++++++---------- src/wren_value.c | 68 +++++++++++++++++++++++--------------------- src/wren_value.h | 12 +++++++- test/class/type.wren | 10 +++++++ 4 files changed, 105 insertions(+), 49 deletions(-) create mode 100644 test/class/type.wren diff --git a/src/wren_core.c b/src/wren_core.c index a1ab6315..e8d62fb2 100644 --- a/src/wren_core.c +++ b/src/wren_core.c @@ -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. diff --git a/src/wren_value.c b/src/wren_value.c index bd81fd77..114e6d2a 100644 --- a/src/wren_value.c +++ b/src/wren_value.c @@ -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); diff --git a/src/wren_value.h b/src/wren_value.h index 82c64c9f..b3d701ee 100644 --- a/src/wren_value.h +++ b/src/wren_value.h @@ -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 diff --git a/test/class/type.wren b/test/class/type.wren new file mode 100644 index 00000000..dea1ff0b --- /dev/null +++ b/test/class/type.wren @@ -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