From 034ab3c2af6940b0d64fbf69e4f15f80ffdc92a7 Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Sun, 15 Mar 2015 22:32:20 -0700 Subject: [PATCH] Clean up code for creating strings in the VM. --- script/metrics.py | 6 --- src/vm/wren_compiler.c | 7 ++- src/vm/wren_core.c | 100 +++++++++++++--------------------- src/vm/wren_value.c | 120 ++++++++++++++++++++++++++++++++++------- src/vm/wren_value.h | 25 ++++++--- src/vm/wren_vm.c | 104 +++++++++++++---------------------- 6 files changed, 194 insertions(+), 168 deletions(-) diff --git a/script/metrics.py b/script/metrics.py index 3974457e..8f897f73 100755 --- a/script/metrics.py +++ b/script/metrics.py @@ -83,9 +83,3 @@ print(" TODOs " + str(num_test_todos)) print(" expectations " + str(num_expects)) print(" non-empty lines " + str(num_test)) print(" empty lines " + str(num_test_empty)) -print("\n") -print("benchmark:") -print(" files " + str(num_benchmark_files)) -print(" TODOs " + str(num_benchmark_todos)) -print(" non-empty lines " + str(num_benchmark)) -print(" empty lines " + str(num_benchmark_empty)) diff --git a/src/vm/wren_compiler.c b/src/vm/wren_compiler.c index bdfe46e1..7afbc83c 100644 --- a/src/vm/wren_compiler.c +++ b/src/vm/wren_compiler.c @@ -3121,14 +3121,13 @@ void definition(Compiler* compiler) ObjFn* wrenCompile(WrenVM* vm, ObjModule* module, const char* sourcePath, const char* source) { - ObjString* sourcePathObj = AS_STRING(wrenNewString(vm, sourcePath, - strlen(sourcePath))); - wrenPushRoot(vm, (Obj*)sourcePathObj); + Value sourcePathValue = wrenStringFormat(vm, "$", sourcePath); + wrenPushRoot(vm, AS_OBJ(sourcePathValue)); Parser parser; parser.vm = vm; parser.module = module; - parser.sourcePath = sourcePathObj; + parser.sourcePath = AS_STRING(sourcePathValue); parser.source = source; parser.tokenStart = source; diff --git a/src/vm/wren_core.c b/src/vm/wren_core.c index 32b9e23b..741b3672 100644 --- a/src/vm/wren_core.c +++ b/src/vm/wren_core.c @@ -38,7 +38,7 @@ #define RETURN_ERROR(msg) \ do { \ - args[0] = wrenNewString(vm, msg, strlen(msg)); \ + args[0] = wrenStringFormat(vm, "$", msg); \ return PRIM_ERROR; \ } while (0); @@ -193,8 +193,7 @@ static bool validateFn(WrenVM* vm, Value* args, int index, const char* argName) { if (IS_FN(args[index]) || IS_CLOSURE(args[index])) return true; - args[0] = OBJ_VAL(wrenStringConcat(vm, argName, -1, - " must be a function.", -1)); + args[0] = wrenStringFormat(vm, "$ must be a function.", argName); return false; } @@ -204,8 +203,7 @@ static bool validateNum(WrenVM* vm, Value* args, int index, const char* argName) { if (IS_NUM(args[index])) return true; - args[0] = OBJ_VAL(wrenStringConcat(vm, argName, -1, - " must be a number.", -1)); + args[0] = wrenStringFormat(vm, "$ must be a number.", argName); return false; } @@ -216,8 +214,7 @@ static bool validateIntValue(WrenVM* vm, Value* args, double value, { if (trunc(value) == value) return true; - args[0] = OBJ_VAL(wrenStringConcat(vm, argName, -1, - " must be an integer.", -1)); + args[0] = wrenStringFormat(vm, "$ must be an integer."); return false; } @@ -247,7 +244,7 @@ static int validateIndexValue(WrenVM* vm, Value* args, int count, double value, // Check bounds. if (index >= 0 && index < count) return index; - args[0] = OBJ_VAL(wrenStringConcat(vm, argName, -1, " out of bounds.", -1)); + args[0] = wrenStringFormat(vm, "$ out of bounds."); return -1; } @@ -262,7 +259,7 @@ static bool validateKey(WrenVM* vm, Value* args, int index) return true; } - args[0] = wrenNewString(vm, "Key must be a value type.", 25); + args[0] = CONST_STRING(vm, "Key must be a value type."); return false; } @@ -284,8 +281,7 @@ static bool validateString(WrenVM* vm, Value* args, int index, { if (IS_STRING(args[index])) return true; - args[0] = OBJ_VAL(wrenStringConcat(vm, argName, -1, - " must be a string.", -1)); + args[0] = wrenStringFormat(vm, "$ must be a string.", argName); return false; } @@ -334,7 +330,7 @@ static int calculateRange(WrenVM* vm, Value* args, ObjRange* range, if (to < -1 || to > *length) { - args[0] = wrenNewString(vm, "Range end out of bounds.", 24); + args[0] = CONST_STRING(vm, "Range end out of bounds."); return -1; } @@ -354,11 +350,11 @@ DEF_PRIMITIVE(bool_toString) { if (AS_BOOL(args[0])) { - RETURN_VAL(wrenNewString(vm, "true", 4)); + RETURN_VAL(CONST_STRING(vm, "true")); } else { - RETURN_VAL(wrenNewString(vm, "false", 5)); + RETURN_VAL(CONST_STRING(vm, "false")); } } @@ -658,7 +654,7 @@ DEF_PRIMITIVE(fn_call16) { return callFunction(vm, args, 16); } DEF_PRIMITIVE(fn_toString) { - RETURN_VAL(wrenNewString(vm, "", 4)); + RETURN_VAL(CONST_STRING(vm, "")); } DEF_PRIMITIVE(list_instantiate) @@ -901,7 +897,7 @@ DEF_PRIMITIVE(null_not) DEF_PRIMITIVE(null_toString) { - RETURN_VAL(wrenNewString(vm, "null", 4)); + RETURN_VAL(CONST_STRING(vm, "null")); } DEF_PRIMITIVE(num_abs) @@ -964,34 +960,7 @@ DEF_PRIMITIVE(num_sqrt) DEF_PRIMITIVE(num_toString) { - double value = AS_NUM(args[0]); - - // Corner case: If the value is NaN, different versions of libc produce - // different outputs (some will format it signed and some won't). To get - // reliable output, handle that ourselves. - if (value != value) RETURN_VAL(wrenNewString(vm, "nan", 3)); - if (value == INFINITY) RETURN_VAL(wrenNewString(vm, "infinity", 8)); - if (value == -INFINITY) RETURN_VAL(wrenNewString(vm, "-infinity", 9)); - - // This is large enough to hold any double converted to a string using - // "%.14g". Example: - // - // -1.12345678901234e-1022 - // - // So we have: - // - // + 1 char for sign - // + 1 char for digit - // + 1 char for "." - // + 14 chars for decimal digits - // + 1 char for "e" - // + 1 char for "-" or "+" - // + 4 chars for exponent - // + 1 char for "\0" - // = 24 - char buffer[24]; - int length = sprintf(buffer, "%.14g", value); - RETURN_VAL(wrenNewString(vm, buffer, length)); + RETURN_VAL(wrenNumToString(vm, AS_NUM(args[0]))); } DEF_PRIMITIVE(num_truncate) @@ -1019,7 +988,7 @@ DEF_PRIMITIVE(num_fromString) if (errno == ERANGE) { - args[0] = wrenNewString(vm, "Number literal is too large.", 28); + args[0] = CONST_STRING(vm, "Number literal is too large."); return PRIM_ERROR; } @@ -1209,12 +1178,11 @@ DEF_PRIMITIVE(object_toString) else if (IS_INSTANCE(args[0])) { ObjInstance* instance = AS_INSTANCE(args[0]); - ObjString* name = instance->obj.classObj->name; - RETURN_OBJ(wrenStringConcat(vm, "instance of ", -1, - name->value, name->length)); + Value name = OBJ_VAL(instance->obj.classObj->name); + RETURN_VAL(wrenStringFormat(vm, "instance of @", name)); } - RETURN_VAL(wrenNewString(vm, "", 8)); + RETURN_VAL(CONST_STRING(vm, "")); } DEF_PRIMITIVE(object_type) @@ -1296,11 +1264,20 @@ DEF_PRIMITIVE(range_iteratorValue) DEF_PRIMITIVE(range_toString) { - char buffer[51]; ObjRange* range = AS_RANGE(args[0]); - int length = sprintf(buffer, "%.14g%s%.14g", range->from, - range->isInclusive ? ".." : "...", range->to); - RETURN_VAL(wrenNewString(vm, buffer, length)); + + Value from = wrenNumToString(vm, range->from); + wrenPushRoot(vm, AS_OBJ(from)); + + Value to = wrenNumToString(vm, range->to); + wrenPushRoot(vm, AS_OBJ(to)); + + Value result = wrenStringFormat(vm, "@$@", from, + range->isInclusive ? ".." : "...", to); + + wrenPopRoot(vm); + wrenPopRoot(vm); + RETURN_VAL(result); } DEF_PRIMITIVE(string_contains) @@ -1403,10 +1380,7 @@ DEF_PRIMITIVE(string_toString) DEF_PRIMITIVE(string_plus) { if (!validateString(vm, args, 1, "Right operand")) return PRIM_ERROR; - ObjString* left = AS_STRING(args[0]); - ObjString* right = AS_STRING(args[1]); - RETURN_OBJ(wrenStringConcat(vm, left->value, left->length, - right->value, right->length)); + RETURN_VAL(wrenStringFormat(vm, "@@", args[0], args[1])); } DEF_PRIMITIVE(string_subscript) @@ -1432,7 +1406,7 @@ DEF_PRIMITIVE(string_subscript) int start = calculateRange(vm, args, AS_RANGE(args[1]), &count, &step); if (start == -1) return PRIM_ERROR; - ObjString* result = AS_STRING(wrenNewUninitializedString(vm, count)); + ObjString* result = wrenNewUninitializedString(vm, count); for (int i = 0; i < count; i++) { result->value[i] = string->value[start + (i * step)]; @@ -1444,12 +1418,11 @@ DEF_PRIMITIVE(string_subscript) static ObjClass* defineSingleClass(WrenVM* vm, const char* name) { - size_t length = strlen(name); - ObjString* nameString = AS_STRING(wrenNewString(vm, name, length)); + ObjString* nameString = AS_STRING(wrenStringFormat(vm, "$", name)); wrenPushRoot(vm, (Obj*)nameString); ObjClass* classObj = wrenNewSingleClass(vm, 0, nameString); - wrenDefineVariable(vm, NULL, name, length, OBJ_VAL(classObj)); + wrenDefineVariable(vm, NULL, name, nameString->length, OBJ_VAL(classObj)); wrenPopRoot(vm); return classObj; @@ -1457,12 +1430,11 @@ static ObjClass* defineSingleClass(WrenVM* vm, const char* name) static ObjClass* defineClass(WrenVM* vm, const char* name) { - size_t length = strlen(name); - ObjString* nameString = AS_STRING(wrenNewString(vm, name, length)); + ObjString* nameString = AS_STRING(wrenStringFormat(vm, "$", name)); wrenPushRoot(vm, (Obj*)nameString); ObjClass* classObj = wrenNewClass(vm, vm->objectClass, 0, nameString); - wrenDefineVariable(vm, NULL, name, length, OBJ_VAL(classObj)); + wrenDefineVariable(vm, NULL, name, nameString->length, OBJ_VAL(classObj)); wrenPopRoot(vm); return classObj; diff --git a/src/vm/wren_value.c b/src/vm/wren_value.c index 969be6f0..a97b6245 100644 --- a/src/vm/wren_value.c +++ b/src/vm/wren_value.c @@ -1,3 +1,5 @@ +#include +#include #include #include @@ -73,11 +75,10 @@ ObjClass* wrenNewClass(WrenVM* vm, ObjClass* superclass, int numFields, wrenPushRoot(vm, (Obj*)name); // Create the metaclass. - ObjString* metaclassName = wrenStringConcat(vm, name->value, name->length, - " metaclass", -1); - wrenPushRoot(vm, (Obj*)metaclassName); + Value metaclassName = wrenStringFormat(vm, "@ metaclass", OBJ_VAL(name)); + wrenPushRoot(vm, AS_OBJ(metaclassName)); - ObjClass* metaclass = wrenNewSingleClass(vm, 0, metaclassName); + ObjClass* metaclass = wrenNewSingleClass(vm, 0, AS_STRING(metaclassName)); metaclass->obj.classObj = vm->classClass; wrenPopRoot(vm); @@ -619,7 +620,7 @@ Value wrenNewString(WrenVM* vm, const char* text, size_t length) ASSERT(length == 0 || text != NULL, "Unexpected NULL string."); // TODO: Don't allocate a heap string at all for zero-length strings. - ObjString* string = AS_STRING(wrenNewUninitializedString(vm, length)); + ObjString* string = wrenNewUninitializedString(vm, length); // Copy the string (if given one). if (length > 0) memcpy(string->value, text, length); @@ -629,28 +630,108 @@ Value wrenNewString(WrenVM* vm, const char* text, size_t length) return OBJ_VAL(string); } -Value wrenNewUninitializedString(WrenVM* vm, size_t length) +ObjString* wrenNewUninitializedString(WrenVM* vm, size_t length) { ObjString* string = ALLOCATE_FLEX(vm, ObjString, char, length + 1); initObj(vm, &string->obj, OBJ_STRING, vm->stringClass); string->length = (int)length; - return OBJ_VAL(string); + return string; } -ObjString* wrenStringConcat(WrenVM* vm, const char* left, int leftLength, - const char* right, int rightLength) +Value wrenNumToString(WrenVM* vm, double value) { - if (leftLength == -1) leftLength = (int)strlen(left); - if (rightLength == -1) rightLength = (int)strlen(right); + // Corner case: If the value is NaN, different versions of libc produce + // different outputs (some will format it signed and some won't). To get + // reliable output, handle that ourselves. + if (value != value) return CONST_STRING(vm, "nan"); + if (value == INFINITY) return CONST_STRING(vm, "infinity"); + if (value == -INFINITY) return CONST_STRING(vm, "-infinity"); - Value value = wrenNewUninitializedString(vm, leftLength + rightLength); - ObjString* string = AS_STRING(value); - memcpy(string->value, left, leftLength); - memcpy(string->value + leftLength, right, rightLength); - string->value[leftLength + rightLength] = '\0'; + // This is large enough to hold any double converted to a string using + // "%.14g". Example: + // + // -1.12345678901234e-1022 + // + // So we have: + // + // + 1 char for sign + // + 1 char for digit + // + 1 char for "." + // + 14 chars for decimal digits + // + 1 char for "e" + // + 1 char for "-" or "+" + // + 4 chars for exponent + // + 1 char for "\0" + // = 24 + char buffer[24]; + int length = sprintf(buffer, "%.14g", value); + return wrenNewString(vm, buffer, length); +} - return string; +Value wrenStringFormat(WrenVM* vm, const char* format, ...) +{ + va_list argList; + + // Calculate the length of the result string. Do this up front so we can + // create the final string with a single allocation. + va_start(argList, format); + size_t totalLength = 0; + for (const char* c = format; *c != '\0'; c++) + { + switch (*c) + { + case '$': + totalLength += strlen(va_arg(argList, const char*)); + break; + + case '@': + totalLength += AS_STRING(va_arg(argList, Value))->length; + break; + + default: + // Any other character is interpreted literally. + totalLength++; + } + } + va_end(argList); + + // Concatenate the string. + ObjString* result = wrenNewUninitializedString(vm, totalLength); + + va_start(argList, format); + char* start = result->value; + for (const char* c = format; *c != '\0'; c++) + { + switch (*c) + { + case '$': + { + const char* string = va_arg(argList, const char*); + size_t length = strlen(string); + memcpy(start, string, length); + start += length; + break; + } + + case '@': + { + ObjString* string = AS_STRING(va_arg(argList, Value)); + memcpy(start, string->value, string->length); + start += string->length; + break; + } + + default: + // Any other character is interpreted literally. + *start++ = *c; + } + } + va_end(argList); + + *start = '\0'; + + return OBJ_VAL(result); } Value wrenStringCodePointAt(WrenVM* vm, ObjString* string, uint32_t index) @@ -669,11 +750,10 @@ Value wrenStringCodePointAt(WrenVM* vm, ObjString* string, uint32_t index) else if ((first & 0xe0) == 0xc0) numBytes = 2; else numBytes = 1; - Value value = wrenNewUninitializedString(vm, numBytes); - ObjString* result = AS_STRING(value); + ObjString* result = wrenNewUninitializedString(vm, numBytes); memcpy(result->value, string->value + index, numBytes); result->value[numBytes] = '\0'; - return value; + return OBJ_VAL(result); } // Uses the Boyer-Moore-Horspool string matching algorithm. diff --git a/src/vm/wren_value.h b/src/vm/wren_value.h index b4009190..9b148cad 100644 --- a/src/vm/wren_value.h +++ b/src/vm/wren_value.h @@ -697,6 +697,11 @@ ObjModule* wrenNewModule(WrenVM* vm); // Creates a new range from [from] to [to]. Value wrenNewRange(WrenVM* vm, double from, double to, bool isInclusive); +// Creates a new string object from [text], which should be a bare C string +// literal. This determines the length of the string automatically at compile +// time based on the size of the character array -1 for the terminating '\0'. +#define CONST_STRING(vm, text) wrenNewString((vm), (text), sizeof(text) - 1) + // Creates a new string object of [length] and copies [text] into it. // // [text] may be NULL if [length] is zero. @@ -706,13 +711,21 @@ Value wrenNewString(WrenVM* vm, const char* text, size_t length); // [length] but does no initialization of the buffer. // // The caller is expected to fully initialize the buffer after calling. -Value wrenNewUninitializedString(WrenVM* vm, size_t length); +ObjString* wrenNewUninitializedString(WrenVM* vm, size_t length); -// Creates a new string that is the concatenation of [left] and [right] (with -// length [leftLength] and [rightLength], respectively). If -1 is passed -// the string length is automatically calculated. -ObjString* wrenStringConcat(WrenVM* vm, const char* left, int leftLength, - const char* right, int rightLength); +// Produces a string representation of [value]. +Value wrenNumToString(WrenVM* vm, double value); + +// Creates a new formatted string from [format] and any additional arguments +// used in the format string. +// +// This is a very restricted flavor of formatting, intended only for internal +// use by the VM. Two formatting characters are supported, each of which reads +// the next argument as a certain type: +// +// $ - A C string. +// @ - A Wren string object. +Value wrenStringFormat(WrenVM* vm, const char* format, ...); // Creates a new string containing the code point in [string] starting at byte // [index]. If [index] points into the middle of a UTF-8 sequence, returns an diff --git a/src/vm/wren_vm.c b/src/vm/wren_vm.c index 6356e981..1d199e79 100644 --- a/src/vm/wren_vm.c +++ b/src/vm/wren_vm.c @@ -316,12 +316,12 @@ static void callForeign(WrenVM* vm, ObjFiber* fiber, // // Returns the fiber that should receive the error or `NULL` if no fiber // caught it. -static ObjFiber* runtimeError(WrenVM* vm, ObjFiber* fiber, ObjString* error) +static ObjFiber* runtimeError(WrenVM* vm, ObjFiber* fiber, Value error) { ASSERT(fiber->error == NULL, "Can only fail once."); // Store the error in the fiber so it can be accessed later. - fiber->error = error; + fiber->error = AS_STRING(error); // If the caller ran this fiber using "try", give it the error. if (fiber->callerIsTrying) @@ -340,14 +340,10 @@ static ObjFiber* runtimeError(WrenVM* vm, ObjFiber* fiber, ObjString* error) // Creates a string containing an appropriate method not found error for a // method with [symbol] on [classObj]. -static ObjString* methodNotFound(WrenVM* vm, ObjClass* classObj, int symbol) +static Value methodNotFound(WrenVM* vm, ObjClass* classObj, int symbol) { - char message[MAX_VARIABLE_NAME + MAX_METHOD_NAME + 24]; - sprintf(message, "%s does not implement '%s'.", - classObj->name->value, - vm->methodNames.data[symbol].buffer); - - return AS_STRING(wrenNewString(vm, message, strlen(message))); + return wrenStringFormat(vm, "@ does not implement '$'.", + OBJ_VAL(classObj->name), vm->methodNames.data[symbol].buffer); } // Pushes [function] onto [fiber]'s callstack and invokes it. Expects [numArgs] @@ -435,10 +431,7 @@ static Value importModule(WrenVM* vm, Value name) if (source == NULL) { // Couldn't load the module. - Value error = wrenNewUninitializedString(vm, 25 + AS_STRING(name)->length); - sprintf(AS_STRING(error)->value, "Could not find module '%s'.", - AS_CSTRING(name)); - return error; + return wrenStringFormat(vm, "Could not find module '@'.", name); } ObjFiber* moduleFiber = loadModule(vm, name, source); @@ -468,41 +461,24 @@ static bool importVariable(WrenVM* vm, Value moduleName, Value variableName, return true; } - // TODO: This is pretty verbose. Do something cleaner? - ObjString* moduleString = AS_STRING(moduleName); - ObjString* variableString = AS_STRING(variableName); - int length = 48 + variableString->length + moduleString->length; - ObjString* error = AS_STRING(wrenNewUninitializedString(vm, length)); - - char* start = error->value; - memcpy(start, "Could not find a variable named '", 33); - start += 33; - memcpy(start, variableString->value, variableString->length); - start += variableString->length; - memcpy(start, "' in module '", 13); - start += 13; - memcpy(start, moduleString->value, moduleString->length); - start += moduleString->length; - memcpy(start, "'.", 2); - start += 2; - *start = '\0'; - - *result = OBJ_VAL(error); + *result = wrenStringFormat(vm, + "Could not find a variable named '@' in module '@'.", + variableName, moduleName); return false; } // Verifies that [superclass] is a valid object to inherit from. That means it // must be a class and cannot be the class of any built-in type. // -// If successful, returns NULL. Otherwise, returns a string for the runtime +// If successful, returns null. Otherwise, returns a string for the runtime // error message. -static ObjString* validateSuperclass(WrenVM* vm, ObjString* name, +static Value validateSuperclass(WrenVM* vm, Value name, Value superclassValue) { // Make sure the superclass is a class. if (!IS_CLASS(superclassValue)) { - return AS_STRING(wrenNewString(vm, "Must inherit from a class.", 26)); + return CONST_STRING(vm, "Must inherit from a class."); } // Make sure it doesn't inherit from a sealed built-in type. Primitive methods @@ -517,13 +493,11 @@ static ObjString* validateSuperclass(WrenVM* vm, ObjString* name, superclass == vm->rangeClass || superclass == vm->stringClass) { - char message[70 + MAX_VARIABLE_NAME]; - sprintf(message, "%s cannot inherit from %s.", - name->value, superclass->name->value); - return AS_STRING(wrenNewString(vm, message, strlen(message))); + return wrenStringFormat(vm, "@ cannot inherit from @.", + name, OBJ_VAL(superclass->name)); } - return NULL; + return NULL_VAL; } // The main bytecode interpreter loop. This is where the magic happens. It is @@ -789,7 +763,7 @@ static bool runInterpreter(WrenVM* vm) break; case PRIM_ERROR: - RUNTIME_ERROR(AS_STRING(args[0])); + RUNTIME_ERROR(args[0]); case PRIM_CALL: STORE_FRAME(); @@ -888,7 +862,7 @@ static bool runInterpreter(WrenVM* vm) break; case PRIM_ERROR: - RUNTIME_ERROR(AS_STRING(args[0])); + RUNTIME_ERROR(args[0]); case PRIM_CALL: STORE_FRAME(); @@ -1047,8 +1021,7 @@ static bool runInterpreter(WrenVM* vm) Value expected = POP(); if (!IS_CLASS(expected)) { - const char* message = "Right operand must be a class."; - RUNTIME_ERROR(AS_STRING(wrenNewString(vm, message, strlen(message)))); + RUNTIME_ERROR(CONST_STRING(vm, "Right operand must be a class.")); } ObjClass* actual = wrenGetClass(vm, POP()); @@ -1149,20 +1122,21 @@ static bool runInterpreter(WrenVM* vm) CASE_CODE(CLASS): { - ObjString* name = AS_STRING(PEEK2()); + Value name = PEEK2(); ObjClass* superclass = vm->objectClass; // Use implicit Object superclass if none given. if (!IS_NULL(PEEK())) { - ObjString* error = validateSuperclass(vm, name, PEEK()); - if (error != NULL) RUNTIME_ERROR(error); + Value error = validateSuperclass(vm, name, PEEK()); + if (!IS_NULL(error)) RUNTIME_ERROR(error); superclass = AS_CLASS(PEEK()); } int numFields = READ_BYTE(); - ObjClass* classObj = wrenNewClass(vm, superclass, numFields, name); + ObjClass* classObj = wrenNewClass(vm, superclass, numFields, + AS_STRING(name)); // Don't pop the superclass and name off the stack until the subclass is // done being created, to make sure it doesn't get collected. @@ -1173,12 +1147,9 @@ static bool runInterpreter(WrenVM* vm) // overflow. if (superclass->numFields + numFields > MAX_FIELDS) { - char message[70 + MAX_VARIABLE_NAME]; - sprintf(message, - "Class '%s' may not have more than %d fields, including inherited " - "ones.", name->value, MAX_FIELDS); - - RUNTIME_ERROR(AS_STRING(wrenNewString(vm, message, strlen(message)))); + RUNTIME_ERROR(wrenStringFormat(vm, + "Class '@' may not have more than 255 fields, including inherited " + "ones.", name)); } PUSH(OBJ_VAL(classObj)); @@ -1203,7 +1174,7 @@ static bool runInterpreter(WrenVM* vm) Value result = importModule(vm, name); // If it returned a string, it was an error message. - if (IS_STRING(result)) RUNTIME_ERROR(AS_STRING(result)); + if (IS_STRING(result)) RUNTIME_ERROR(result); // Make a slot that the module's fiber can use to store its result in. // It ends up getting discarded, but CODE_RETURN expects to be able to @@ -1236,7 +1207,7 @@ static bool runInterpreter(WrenVM* vm) } else { - RUNTIME_ERROR(AS_STRING(result)); + RUNTIME_ERROR(result); } DISPATCH(); } @@ -1285,7 +1256,7 @@ static ObjFn* makeCallStub(WrenVM* vm, ObjModule* module, const char* signature) WrenMethod* wrenGetMethod(WrenVM* vm, const char* module, const char* variable, const char* signature) { - Value moduleName = wrenNewString(vm, module, strlen(module)); + Value moduleName = wrenStringFormat(vm, "$", module); wrenPushRoot(vm, AS_OBJ(moduleName)); Value moduleValue = wrenMapGet(vm->modules, moduleName); @@ -1343,8 +1314,7 @@ void wrenCall(WrenVM* vm, WrenMethod* method, const char* argTypes, ...) case 'n': value = NULL_VAL; va_arg(argList, void*); break; case 's': { - const char* text = va_arg(argList, const char*); - value = wrenNewString(vm, text, strlen(text)); + value = wrenStringFormat(vm, "$", va_arg(argList, const char*)); break; } @@ -1413,7 +1383,7 @@ WrenInterpretResult wrenInterpret(WrenVM* vm, const char* sourcePath, if (strlen(sourcePath) == 0) return loadIntoCore(vm, source); // TODO: Better module name. - Value name = wrenNewString(vm, "main", 4); + Value name = CONST_STRING(vm, "main"); wrenPushRoot(vm, AS_OBJ(name)); ObjFiber* fiber = loadModule(vm, name, source); @@ -1434,7 +1404,7 @@ WrenInterpretResult wrenInterpret(WrenVM* vm, const char* sourcePath, Value wrenImportModule(WrenVM* vm, const char* name) { - Value nameValue = wrenNewString(vm, name, strlen(name)); + Value nameValue = wrenStringFormat(vm, "$", name); wrenPushRoot(vm, AS_OBJ(nameValue)); // If the module is already loaded, we don't need to do anything. @@ -1451,9 +1421,7 @@ Value wrenImportModule(WrenVM* vm, const char* name) wrenPopRoot(vm); // nameValue. // Couldn't load the module. - Value error = wrenNewUninitializedString(vm, 25 + strlen(name)); - sprintf(AS_STRING(error)->value, "Could not find module '%s'.", name); - return error; + return wrenStringFormat(vm, "Could not find module '$'.", name); } ObjFiber* moduleFiber = loadModule(vm, nameValue, source); @@ -1559,14 +1527,14 @@ static void defineMethod(WrenVM* vm, const char* className, else { // The class doesn't already exist, so create it. - size_t length = strlen(className); - ObjString* nameString = AS_STRING(wrenNewString(vm, className, length)); + ObjString* nameString = AS_STRING(wrenStringFormat(vm, "$", className)); wrenPushRoot(vm, (Obj*)nameString); // TODO: Allow passing in name for superclass? classObj = wrenNewClass(vm, vm->objectClass, 0, nameString); - wrenDefineVariable(vm, coreModule, className, length, OBJ_VAL(classObj)); + wrenDefineVariable(vm, coreModule, className, nameString->length, + OBJ_VAL(classObj)); wrenPopRoot(vm); }