diff --git a/src/compiler.c b/src/compiler.c index f8d874ae..ec031fa5 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -717,7 +717,7 @@ static void function(Compiler* compiler, int allowAssignment) // Add the function to the constant table. Do this immediately so that it's // reachable by the GC. - compiler->fn->constants[compiler->fn->numConstants++] = (Value)fnCompiler.fn; + compiler->fn->constants[compiler->fn->numConstants++] = OBJ_VAL(fnCompiler.fn); // TODO(bob): Hackish. // Define a fake local slot for the receiver (the function object itself) so @@ -837,7 +837,7 @@ static void number(Compiler* compiler, int allowAssignment) // Define a constant for the literal. int constant = addConstant(compiler, - (Value)newNum(compiler->parser->vm, (double)value)); + newNum(compiler->parser->vm, (double)value)); // Compile the code to load the constant. emit(compiler, CODE_CONSTANT); @@ -1167,7 +1167,7 @@ void method(Compiler* compiler, int isStatic, SignatureFn signature) // Add the block to the constant table. Do this eagerly so it's reachable by // the GC. - int constant = addConstant(compiler, (Value)methodCompiler.fn); + int constant = addConstant(compiler, OBJ_VAL(methodCompiler.fn)); // TODO(bob): Hackish. // Define a fake local slot for the receiver so that later locals have the @@ -1314,7 +1314,7 @@ ObjFn* compile(VM* vm, const char* source) Compiler compiler; initCompiler(&compiler, &parser, NULL, 0); - pinObj(vm, (Value)compiler.fn); + pinObj(vm, OBJ_VAL(compiler.fn)); for (;;) { @@ -1335,7 +1335,7 @@ ObjFn* compile(VM* vm, const char* source) emit(&compiler, CODE_END); - unpinObj(vm, (Value)compiler.fn); + unpinObj(vm, OBJ_VAL(compiler.fn)); return parser.hasError ? NULL : compiler.fn; } diff --git a/src/primitives.c b/src/primitives.c index 36c62232..72ee990f 100644 --- a/src/primitives.c +++ b/src/primitives.c @@ -23,7 +23,7 @@ DEF_PRIMITIVE(bool_not) DEF_PRIMITIVE(bool_eqeq) { - if (args[1]->type != OBJ_FALSE && args[1]->type != OBJ_TRUE) + if (!(IS_BOOL(args[1]))) { return newBool(vm, 0); } @@ -33,7 +33,7 @@ DEF_PRIMITIVE(bool_eqeq) DEF_PRIMITIVE(bool_bangeq) { - if (args[1]->type != OBJ_FALSE && args[1]->type != OBJ_TRUE) + if (!(IS_BOOL(args[1]))) { return newBool(vm, 1); } @@ -62,66 +62,66 @@ DEF_PRIMITIVE(bool_toString) DEF_PRIMITIVE(fn_call0) { callFunction(fiber, AS_FN(args[0]), 1); - return NULL; + return NO_VAL; } DEF_PRIMITIVE(fn_call1) { callFunction(fiber, AS_FN(args[0]), 2); - return NULL; + return NO_VAL; } DEF_PRIMITIVE(fn_call2) { callFunction(fiber, AS_FN(args[0]), 3); - return NULL; + return NO_VAL; } DEF_PRIMITIVE(fn_call3) { callFunction(fiber, AS_FN(args[0]), 4); - return NULL; + return NO_VAL; } DEF_PRIMITIVE(fn_call4) { callFunction(fiber, AS_FN(args[0]), 5); - return NULL; + return NO_VAL; } DEF_PRIMITIVE(fn_call5) { callFunction(fiber, AS_FN(args[0]), 6); - return NULL; + return NO_VAL; } DEF_PRIMITIVE(fn_call6) { callFunction(fiber, AS_FN(args[0]), 7); - return NULL; + return NO_VAL; } DEF_PRIMITIVE(fn_call7) { callFunction(fiber, AS_FN(args[0]), 8); - return NULL; + return NO_VAL; } DEF_PRIMITIVE(fn_call8) { callFunction(fiber, AS_FN(args[0]), 9); - return NULL; + return NO_VAL; } DEF_PRIMITIVE(fn_eqeq) { - if (args[1]->type != OBJ_FN) return newBool(vm, 0); + if (!IS_FN(args[1])) return newBool(vm, 0); return newBool(vm, AS_FN(args[0]) == AS_FN(args[1])); } DEF_PRIMITIVE(fn_bangeq) { - if (args[1]->type != OBJ_FN) return newBool(vm, 1); + if (!IS_FN(args[1])) return newBool(vm, 1); return newBool(vm, AS_FN(args[0]) != AS_FN(args[1])); } @@ -145,68 +145,68 @@ DEF_PRIMITIVE(num_negate) DEF_PRIMITIVE(num_minus) { - if (args[1]->type != OBJ_NUM) return vm->unsupported; + if (!IS_NUM(args[1])) return vm->unsupported; return (Value)newNum(vm, AS_NUM(args[0]) - AS_NUM(args[1])); } DEF_PRIMITIVE(num_plus) { - if (args[1]->type != OBJ_NUM) return vm->unsupported; + if (!IS_NUM(args[1])) return vm->unsupported; // TODO(bob): Handle coercion to string if RHS is a string. return (Value)newNum(vm, AS_NUM(args[0]) + AS_NUM(args[1])); } DEF_PRIMITIVE(num_multiply) { - if (args[1]->type != OBJ_NUM) return vm->unsupported; + if (!IS_NUM(args[1])) return vm->unsupported; return (Value)newNum(vm, AS_NUM(args[0]) * AS_NUM(args[1])); } DEF_PRIMITIVE(num_divide) { - if (args[1]->type != OBJ_NUM) return vm->unsupported; + if (!IS_NUM(args[1])) return vm->unsupported; return (Value)newNum(vm, AS_NUM(args[0]) / AS_NUM(args[1])); } DEF_PRIMITIVE(num_mod) { - if (args[1]->type != OBJ_NUM) return vm->unsupported; + if (!IS_NUM(args[1])) return vm->unsupported; return (Value)newNum(vm, fmod(AS_NUM(args[0]), AS_NUM(args[1]))); } DEF_PRIMITIVE(num_lt) { - if (args[1]->type != OBJ_NUM) return vm->unsupported; + if (!IS_NUM(args[1])) return vm->unsupported; return newBool(vm, AS_NUM(args[0]) < AS_NUM(args[1])); } DEF_PRIMITIVE(num_gt) { - if (args[1]->type != OBJ_NUM) return vm->unsupported; + if (!IS_NUM(args[1])) return vm->unsupported; return newBool(vm, AS_NUM(args[0]) > AS_NUM(args[1])); } DEF_PRIMITIVE(num_lte) { - if (args[1]->type != OBJ_NUM) return vm->unsupported; + if (!IS_NUM(args[1])) return vm->unsupported; return newBool(vm, AS_NUM(args[0]) <= AS_NUM(args[1])); } DEF_PRIMITIVE(num_gte) { - if (args[1]->type != OBJ_NUM) return vm->unsupported; + if (!IS_NUM(args[1])) return vm->unsupported; return newBool(vm, AS_NUM(args[0]) >= AS_NUM(args[1])); } DEF_PRIMITIVE(num_eqeq) { - if (args[1]->type != OBJ_NUM) return newBool(vm, 0); + if (!IS_NUM(args[1])) return newBool(vm, 0); return newBool(vm, AS_NUM(args[0]) == AS_NUM(args[1])); } DEF_PRIMITIVE(num_bangeq) { - if (args[1]->type != OBJ_NUM) return newBool(vm, 1); + if (!IS_NUM(args[1])) return newBool(vm, 1); return newBool(vm, AS_NUM(args[0]) != AS_NUM(args[1])); } @@ -236,7 +236,7 @@ DEF_PRIMITIVE(string_toString) DEF_PRIMITIVE(string_plus) { - if (args[1]->type != OBJ_STRING) return vm->unsupported; + if (!IS_STRING(args[1])) return vm->unsupported; // TODO(bob): Handle coercion to string of RHS. const char* left = AS_STRING(args[0]); @@ -245,17 +245,19 @@ DEF_PRIMITIVE(string_plus) size_t leftLength = strlen(left); size_t rightLength = strlen(right); - ObjString* string = newString(vm, NULL, leftLength + rightLength); + Value value = newString(vm, NULL, leftLength + rightLength); + // TODO(bob): Cast here is lame. + ObjString* string = (ObjString*)(value.obj); strcpy(string->value, left); strcpy(string->value + leftLength, right); string->value[leftLength + rightLength] = '\0'; - return (Value)string; + return value; } DEF_PRIMITIVE(string_eqeq) { - if (args[1]->type != OBJ_STRING) return newBool(vm, 0); + if (!IS_STRING(args[1])) return newBool(vm, 0); const char* a = AS_STRING(args[0]); const char* b = AS_STRING(args[1]); return newBool(vm, strcmp(a, b) == 0); @@ -263,7 +265,7 @@ DEF_PRIMITIVE(string_eqeq) DEF_PRIMITIVE(string_bangeq) { - if (args[1]->type != OBJ_STRING) return newBool(vm, 1); + if (!IS_STRING(args[1])) return newBool(vm, 1); const char* a = AS_STRING(args[0]); const char* b = AS_STRING(args[1]); return newBool(vm, strcmp(a, b) != 0); @@ -331,8 +333,6 @@ void loadCore(VM* vm) PRIMITIVE(vm->numClass, "== ", num_eqeq); PRIMITIVE(vm->numClass, "!= ", num_bangeq); - vm->objectClass = AS_CLASS(findGlobal(vm, "Object")); - vm->stringClass = AS_CLASS(findGlobal(vm, "String")); PRIMITIVE(vm->stringClass, "contains ", string_contains); PRIMITIVE(vm->stringClass, "count", string_count); diff --git a/src/vm.c b/src/vm.c index b34218b7..28436bd4 100644 --- a/src/vm.c +++ b/src/vm.c @@ -8,6 +8,14 @@ static Value primitive_metaclass_new(VM* vm, Fiber* fiber, Value* args); +Value objectToValue(Obj* obj) +{ + Value value; + value.type = VAL_OBJ; + value.obj = obj; + return value; +} + VM* newVM() { // TODO(bob): Get rid of explicit malloc() here. @@ -28,14 +36,7 @@ VM* newVM() // initialized in case we do a garbage collection before one gets initialized. for (int i = 0; i < MAX_SYMBOLS; i++) { - vm->globals[i] = NULL; - } - - // Clear out the pinned list. We look for NULL empty slots explicitly. - vm->numPinned = 0; - for (int j = 0; j < MAX_PINNED; j++) - { - vm->pinned[j] = NULL; + vm->globals[i] = NULL_VAL; } loadCore(vm); @@ -50,10 +51,10 @@ void freeVM(VM* vm) free(vm); } -void markObj(Value value) -{ - Obj* obj = (Obj*)value; +static void markValue(Value value); +static void markObj(Obj* obj) +{ // Don't recurse if already marked. Avoids getting stuck in a loop on cycles. if (obj->flags & FLAG_MARKED) return; @@ -73,17 +74,17 @@ void markObj(Value value) { case OBJ_CLASS: { - ObjClass* classObj = AS_CLASS(obj); + ObjClass* classObj = (ObjClass*)obj; // The metaclass. - if (classObj->metaclass != NULL) markObj((Value)classObj->metaclass); + if (classObj->metaclass != NULL) markObj((Obj*)classObj->metaclass); // Method function objects. for (int i = 0; i < MAX_SYMBOLS; i++) { if (classObj->methods[i].type == METHOD_BLOCK) { - markObj((Value)classObj->methods[i].fn); + markObj((Obj*)classObj->methods[i].fn); } } break; @@ -92,10 +93,10 @@ void markObj(Value value) case OBJ_FN: { // Mark the constants. - ObjFn* fn = AS_FN(obj); + ObjFn* fn = (ObjFn*)obj; for (int i = 0; i < fn->numConstants; i++) { - markObj(fn->constants[i]); + markValue(fn->constants[i]); } break; } @@ -118,6 +119,15 @@ void markObj(Value value) #endif } +void markValue(Value value) +{ + // TODO(bob): Temp-ish hack. NULL_VAL doesn't have an Obj. + if (value.obj == NULL) return; + + //if (!IS_OBJ(value)) return; + markObj(value.obj); +} + void freeObj(VM* vm, Obj* obj) { #ifdef TRACE_MEMORY @@ -134,17 +144,20 @@ void freeObj(VM* vm, Obj* obj) { // TODO(bob): Don't hardcode array sizes. size = sizeof(ObjFn) + sizeof(Code) * 1024 + sizeof(Value) * 256; - ObjFn* fn = AS_FN(obj); + ObjFn* fn = (ObjFn*)obj; free(fn->bytecode); free(fn->constants); break; } case OBJ_STRING: + { // TODO(bob): O(n) calculation here is lame! - size = sizeof(ObjString) + strlen(AS_STRING(obj)); - free(AS_STRING(obj)); + ObjString* string = (ObjString*)obj; + size = sizeof(ObjString) + strlen(string->value); + free(string->value); break; + } case OBJ_CLASS: size = sizeof(ObjClass); @@ -171,6 +184,9 @@ void freeObj(VM* vm, Obj* obj) void collectGarbage(VM* vm) { + // TODO(bob): Instead of casting to Obj* and calling markObj(), split out + // marking functions for different types. + // Mark all reachable objects. #ifdef TRACE_MEMORY printf("-- gc --\n"); @@ -181,25 +197,25 @@ void collectGarbage(VM* vm) { // Check for NULL to handle globals that have been defined (at compile time) // but not yet initialized. - if (vm->globals[i] != NULL) markObj(vm->globals[i]); + if (!IS_NULL(vm->globals[i])) markValue(vm->globals[i]); } // Pinned objects. for (int j = 0; j < vm->numPinned; j++) { - if (vm->pinned[j] != NULL) markObj(vm->pinned[j]); + if (!IS_NULL(vm->pinned[j])) markValue(vm->pinned[j]); } // Stack functions. for (int k = 0; k < vm->fiber->numFrames; k++) { - markObj((Value)vm->fiber->frames[k].fn); + markObj((Obj*)vm->fiber->frames[k].fn); } // Stack variables. for (int l = 0; l < vm->fiber->stackSize; l++) { - markObj(vm->fiber->stack[l]); + markValue(vm->fiber->stack[l]); } // Collect any unmarked objects. @@ -258,11 +274,14 @@ void initObj(VM* vm, Obj* obj, ObjType type) vm->first = obj; } -Value newBool(VM* vm, int value) +Value newBool(VM* vm, int b) { - Obj* obj = allocate(vm, sizeof(Obj)); - initObj(vm, obj, value ? OBJ_TRUE : OBJ_FALSE); - return obj; + // TODO(bob): Get rid of Obj here. + Value value; + value.type = b ? VAL_TRUE : VAL_FALSE; + value.obj = allocate(vm, sizeof(Obj)); + initObj(vm, value.obj, b ? OBJ_TRUE : OBJ_FALSE); + return value; } static ObjClass* newSingleClass(VM* vm, ObjClass* metaclass, @@ -288,11 +307,12 @@ ObjClass* newClass(VM* vm, ObjClass* superclass) ObjClass* metaclass = newSingleClass(vm, NULL, NULL); // Make sure it isn't collected when we allocate the metaclass. - pinObj(vm, (Value)metaclass); + pinObj(vm, OBJ_VAL(metaclass)); ObjClass* classObj = newSingleClass(vm, metaclass, superclass); - unpinObj(vm, (Value)metaclass); + // TODO(bob): Make pin list just Obj* instead of Value. + unpinObj(vm, OBJ_VAL(metaclass)); // Inherit methods from its superclass (unless it's Object, which has none). // TODO(bob): If we want BETA-style inheritance, we'll need to do this after @@ -325,31 +345,40 @@ ObjFn* newFunction(VM* vm) return fn; } -ObjInstance* newInstance(VM* vm, ObjClass* classObj) +Value newInstance(VM* vm, ObjClass* classObj) { + Value value; + value.type = VAL_OBJ; ObjInstance* instance = allocate(vm, sizeof(ObjInstance)); + value.obj = (Obj*)instance; initObj(vm, &instance->obj, OBJ_INSTANCE); instance->classObj = classObj; - return instance; + return value; } Value newNull(VM* vm) { - Obj* obj = allocate(vm, sizeof(Obj)); - initObj(vm, obj, OBJ_NULL); - return obj; + // TODO(bob): Get rid of Obj here. + Value value; + value.type = VAL_NULL; + value.obj = allocate(vm, sizeof(Obj)); + initObj(vm, value.obj, OBJ_NULL); + return value; } -ObjNum* newNum(VM* vm, double number) +Value newNum(VM* vm, double number) { + Value value; + value.type = VAL_NUM; ObjNum* num = allocate(vm, sizeof(ObjNum)); + value.obj = (Obj*)num; initObj(vm, &num->obj, OBJ_NUM); num->value = number; - return num; + return value; } -ObjString* newString(VM* vm, const char* text, size_t length) +Value newString(VM* vm, const char* text, size_t length) { // Allocate before the string object in case this triggers a GC which would // free the string object. @@ -366,7 +395,10 @@ ObjString* newString(VM* vm, const char* text, size_t length) } string->value = heapText; - return string; + Value value; + value.type = VAL_OBJ; + value.obj = (Obj*)string; + return value; } void initSymbolTable(SymbolTable* symbols) @@ -474,7 +506,7 @@ void dumpCode(VM* vm, ObjFn* fn) case CODE_CLASS: printf("CLASS\n"); break; - + case CODE_SUBCLASS: printf("SUBCLASS\n"); break; @@ -590,11 +622,11 @@ void dumpCode(VM* vm, ObjFn* fn) */ // Returns the class of [object]. -static ObjClass* getClass(VM* vm, Value object) +static ObjClass* getClass(VM* vm, Value value) { - switch (object->type) + switch (value.obj->type) { - case OBJ_CLASS: return AS_CLASS(object)->metaclass; + case OBJ_CLASS: return AS_CLASS(value)->metaclass; case OBJ_FALSE: case OBJ_TRUE: return vm->boolClass; @@ -603,7 +635,7 @@ static ObjClass* getClass(VM* vm, Value object) case OBJ_NULL: return vm->nullClass; case OBJ_NUM: return vm->numClass; case OBJ_STRING: return vm->stringClass; - case OBJ_INSTANCE: return AS_INSTANCE(object)->classObj; + case OBJ_INSTANCE: return AS_INSTANCE(value)->classObj; } } @@ -655,6 +687,12 @@ Value interpret(VM* vm, ObjFn* fn) ObjClass* classObj = newClass(vm, superclass); + // Assume the first class being defined is Object. + if (vm->objectClass == NULL) + { + vm->objectClass = classObj; + } + // Define a "new" method on the metaclass. // TODO(bob): Can this be inherited? int newSymbol = ensureSymbol(&vm->methods, "new", strlen("new")); @@ -662,14 +700,14 @@ Value interpret(VM* vm, ObjFn* fn) classObj->metaclass->methods[newSymbol].primitive = primitive_metaclass_new; - PUSH((Value)classObj); + PUSH(OBJ_VAL(classObj)); break; } case CODE_METACLASS: { ObjClass* classObj = AS_CLASS(PEEK()); - PUSH((Value)classObj->metaclass); + PUSH(OBJ_VAL(classObj->metaclass)); break; } @@ -753,7 +791,7 @@ Value interpret(VM* vm, ObjFn* fn) Value result = method->primitive(vm, fiber, args); // If the primitive pushed a call frame, it returns NULL. - if (result != NULL) + if (result.type != VAL_NO_VALUE) { fiber->stack[fiber->stackSize - numArgs] = result; @@ -779,7 +817,7 @@ Value interpret(VM* vm, ObjFn* fn) Value condition = POP(); // False is the only falsey value. - if (condition->type == OBJ_FALSE) + if (!AS_BOOL(condition)) { frame->ip += offset; } @@ -831,10 +869,10 @@ void callFunction(Fiber* fiber, ObjFn* fn, int numArgs) void printValue(Value value) { // TODO(bob): Do more useful stuff here. - switch (value->type) + switch (value.obj->type) { case OBJ_CLASS: - printf("[class %p]", value); + printf("[class %p]", value.obj); break; case OBJ_FALSE: @@ -842,11 +880,11 @@ void printValue(Value value) break; case OBJ_FN: - printf("[fn %p]", value); + printf("[fn %p]", value.obj); break; case OBJ_INSTANCE: - printf("[instance %p]", value); + printf("[instance %p]", value.obj); break; case OBJ_NULL: @@ -875,7 +913,8 @@ void pinObj(VM* vm, Value value) void unpinObj(VM* vm, Value value) { - ASSERT(vm->pinned[vm->numPinned - 1] == value, + // TODO(bob): Do real equivalance check here. + ASSERT(vm->pinned[vm->numPinned - 1].type == value.type, "Unpinning object out of stack order."); vm->numPinned--; } @@ -883,5 +922,5 @@ void unpinObj(VM* vm, Value value) Value primitive_metaclass_new(VM* vm, Fiber* fiber, Value* args) { // TODO(bob): Invoke initializer method. - return (Value)newInstance(vm, AS_CLASS(args[0])); + return newInstance(vm, AS_CLASS(args[0])); } diff --git a/src/vm.h b/src/vm.h index 2127e5b4..acb3749d 100644 --- a/src/vm.h +++ b/src/vm.h @@ -7,33 +7,54 @@ #define MAX_SYMBOLS 256 #define MAX_PINNED 16 -// Get the class value of [obj] (0 or 1), which must be a boolean. -#define AS_CLASS(obj) ((ObjClass*)obj) +// Get the class value of [value] (0 or 1), which must be a boolean. +#define AS_CLASS(value) ((ObjClass*)(value).obj) // Get the bool value of [obj] (0 or 1), which must be a boolean. -#define AS_BOOL(obj) (((Obj*)obj)->type == OBJ_TRUE) +#define AS_BOOL(value) (((Obj*)(value).obj)->type == OBJ_TRUE) // Get the function value of [obj] (0 or 1), which must be a function. -#define AS_FN(obj) ((ObjFn*)obj) +#define AS_FN(value) ((ObjFn*)(value).obj) // Get the double value of [obj], which must be a number. -#define AS_INSTANCE(obj) ((ObjInstance*)obj) +#define AS_INSTANCE(value) ((ObjInstance*)(value).obj) -// Get the double value of [obj], which must be a number. -#define AS_NUM(obj) (((ObjNum*)obj)->value) +// Get the double value of [value], which must be a number. +#define AS_NUM(v) (((ObjNum*)(v).obj)->value) -// Get the const char* value of [obj], which must be a string. -#define AS_STRING(obj) (((ObjString*)obj)->value) +// Get the const char* value of [v], which must be a string. +#define AS_STRING(v) (((ObjString*)(v).obj)->value) + +// Determines if [value] is a garbage-collected object or not. +#define IS_OBJ(value) ((value).type == VAL_OBJ) + +#define IS_NULL(value) ((value).type == VAL_NULL) +#define IS_NUM(value) ((value).type == VAL_NUM) +#define IS_BOOL(value) ((value).type == VAL_FALSE || (value).type == VAL_TRUE) + +// TODO(bob): Evaluating value here twice sucks. +#define IS_FN(value) ((value).type == VAL_OBJ && (value).obj->type == OBJ_FN) +#define IS_STRING(value) ((value).type == VAL_OBJ && (value).obj->type == OBJ_STRING) + +typedef enum +{ + VAL_FALSE, + VAL_NULL, + VAL_NUM, + VAL_TRUE, + VAL_NO_VALUE, // TODO(bob): Hack remove. + VAL_OBJ +} ValueType; typedef enum { - OBJ_CLASS, OBJ_FALSE, - OBJ_FN, - OBJ_INSTANCE, OBJ_NULL, OBJ_NUM, - OBJ_STRING, - OBJ_TRUE + OBJ_TRUE, + OBJ_CLASS, + OBJ_FN, + OBJ_INSTANCE, + OBJ_STRING } ObjType; typedef enum @@ -51,7 +72,12 @@ typedef struct sObj struct sObj* next; } Obj; -typedef Obj* Value; +// TODO(bob): Temp. +typedef struct +{ + ValueType type; + Obj* obj; +} Value; typedef struct sVM VM; typedef struct sFiber Fiber; @@ -257,6 +283,15 @@ struct sFiber int numFrames; }; +// TODO(bob): Temp. +#define OBJ_VAL(obj) (objectToValue((Obj*)(obj))) +Value objectToValue(Obj* obj); + +// TODO(bob): Not C89! +#define NULL_VAL ((Value){ VAL_NULL, NULL }) +// TODO(bob): Gross. +#define NO_VAL ((Value){ VAL_NO_VALUE, NULL }) + VM* newVM(); void freeVM(VM* vm); @@ -272,16 +307,16 @@ ObjFn* newFunction(VM* vm); ObjClass* newClass(VM* vm, ObjClass* superclass); // Creates a new instance of the given [classObj]. -ObjInstance* newInstance(VM* vm, ObjClass* classObj); +Value newInstance(VM* vm, ObjClass* classObj); // Creates a new null object. Value newNull(VM* vm); // Creates a new number object. -ObjNum* newNum(VM* vm, double number); +Value newNum(VM* vm, double number); // Creates a new string object and copies [text] into it. -ObjString* newString(VM* vm, const char* text, size_t length); +Value newString(VM* vm, const char* text, size_t length); // Initializes the symbol table. void initSymbolTable(SymbolTable* symbols);