From efabf20fee6cac747e2bb384efa19616b499d98f Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Thu, 8 Jan 2015 22:29:37 -0800 Subject: [PATCH] Make a few changes to methodNotFound(): - Move closer to other runtime error code. - Make it static since it's private to wren_vm.h. - Allocate a managed ObjString* for the message so the GC can handle it. That's how it ends up anyway, so this cuts out the middle man. - Use a width specifier in sprintf() to print the canonical method name. --- src/wren_vm.c | 93 +++++++++++++++++++++------------------------------ 1 file changed, 39 insertions(+), 54 deletions(-) diff --git a/src/wren_vm.c b/src/wren_vm.c index c23f2cca..65587108 100644 --- a/src/wren_vm.c +++ b/src/wren_vm.c @@ -24,34 +24,6 @@ static void* defaultReallocate(void* memory, size_t oldSize, size_t newSize) return realloc(memory, newSize); } -char* methodNotFound(char* className, char* symbolName) -{ - // Count the number of spaces to determine number of arguments for this - // symbol. - int pos = strlen(symbolName) - 1; - while (symbolName[pos] == ' ') pos--; - - int arguments = strlen(symbolName) - pos - 1; - - // Create a new string that contains only the symbol name. - char *canonicalizedSymbol = malloc(pos + 1); - strncpy(canonicalizedSymbol, symbolName, pos + 1); - - int messageLength = strlen(className) + strlen(canonicalizedSymbol) + - (arguments > 9 ? 48 : 47) + (arguments == 1 ? 0 : 1); - char *message = malloc(messageLength); - - snprintf(message, messageLength, "%s does not implement method '%s' with %d argument%s.", - className, - canonicalizedSymbol, - arguments, - arguments == 1 ? "" : "s"); - - free(canonicalizedSymbol); - - return &message[0]; -} - WrenVM* wrenNewVM(WrenConfiguration* configuration) { WrenReallocateFn reallocate = defaultReallocate; @@ -333,12 +305,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, const char* error) +static ObjFiber* runtimeError(WrenVM* vm, ObjFiber* fiber, ObjString* error) { ASSERT(fiber->error == NULL, "Can only fail once."); // Store the error in the fiber so it can be accessed later. - fiber->error = AS_STRING(wrenNewString(vm, error, strlen(error))); + fiber->error = error; // If the caller ran this fiber using "try", give it the error. if (fiber->callerIsTrying) @@ -355,6 +327,28 @@ static ObjFiber* runtimeError(WrenVM* vm, ObjFiber* fiber, const char* error) return NULL; } +// 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) +{ + // Count the number of spaces to determine the number of parameters the + // method expects. + const char* methodName = vm->methodNames.data[symbol]; + + int methodLength = (int)strlen(methodName); + int numParams = 0; + while (methodName[methodLength - numParams - 1] == ' ') numParams++; + + char message[MAX_VARIABLE_NAME + MAX_METHOD_NAME + 49]; + sprintf(message, "%s does not implement method '%.*s' with %d argument%s.", + classObj->name->value, + methodLength - numParams, methodName, + numParams, + numParams == 1 ? "" : "s"); + + return AS_STRING(wrenNewString(vm, message, strlen(message))); +} + // Pushes [function] onto [fiber]'s callstack and invokes it. Expects [numArgs] // arguments (including the receiver) to be on the top of the stack already. // [function] can be an `ObjFn` or `ObjClosure`. @@ -603,10 +597,7 @@ static bool runInterpreter(WrenVM* vm) // If the class's method table doesn't include the symbol, bail. if (symbol >= classObj->methods.count) { - char *message = methodNotFound(classObj->name->value, - vm->methodNames.data[symbol]); - RUNTIME_ERROR(message); - free(message); + RUNTIME_ERROR(methodNotFound(vm, classObj, symbol)); } Method* method = &classObj->methods.data[symbol]; @@ -626,7 +617,7 @@ static bool runInterpreter(WrenVM* vm) break; case PRIM_ERROR: - RUNTIME_ERROR(AS_CSTRING(args[0])); + RUNTIME_ERROR(AS_STRING(args[0])); case PRIM_CALL: STORE_FRAME(); @@ -654,12 +645,8 @@ static bool runInterpreter(WrenVM* vm) break; case METHOD_NONE: - { - char *message = methodNotFound(classObj->name->value, - vm->methodNames.data[symbol]); - RUNTIME_ERROR(message); - free(message); - } + RUNTIME_ERROR(methodNotFound(vm, classObj, symbol)); + break; } DISPATCH(); } @@ -705,10 +692,7 @@ static bool runInterpreter(WrenVM* vm) // If the class's method table doesn't include the symbol, bail. if (symbol >= classObj->methods.count) { - char *message = methodNotFound(classObj->name->value, - vm->methodNames.data[symbol]); - RUNTIME_ERROR(message); - free(message); + RUNTIME_ERROR(methodNotFound(vm, classObj, symbol)); } Method* method = &classObj->methods.data[symbol]; @@ -728,7 +712,7 @@ static bool runInterpreter(WrenVM* vm) break; case PRIM_ERROR: - RUNTIME_ERROR(AS_CSTRING(args[0])); + RUNTIME_ERROR(AS_STRING(args[0])); case PRIM_CALL: STORE_FRAME(); @@ -756,12 +740,8 @@ static bool runInterpreter(WrenVM* vm) break; case METHOD_NONE: - { - char *message = methodNotFound(classObj->name->value, - vm->methodNames.data[symbol]); - RUNTIME_ERROR(message); - free(message); - } + RUNTIME_ERROR(methodNotFound(vm, classObj, symbol)); + break; } DISPATCH(); } @@ -884,7 +864,11 @@ static bool runInterpreter(WrenVM* vm) CASE_CODE(IS): { Value expected = POP(); - if (!IS_CLASS(expected)) RUNTIME_ERROR("Right operand must be a class."); + if (!IS_CLASS(expected)) + { + const char* message = "Right operand must be a class."; + RUNTIME_ERROR(AS_STRING(wrenNewString(vm, message, strlen(message)))); + } ObjClass* actual = wrenGetClass(vm, POP()); bool isInstance = false; @@ -1031,7 +1015,8 @@ static bool runInterpreter(WrenVM* vm) snprintf(message, 70 + MAX_VARIABLE_NAME, "Class '%s' may not have more than %d fields, including inherited " "ones.", name->value, MAX_FIELDS); - RUNTIME_ERROR(message); + + RUNTIME_ERROR(AS_STRING(wrenNewString(vm, message, strlen(message)))); } PUSH(OBJ_VAL(classObj));