diff --git a/src/cli/vm.c b/src/cli/vm.c index 52f9d0e2..d9a519da 100644 --- a/src/cli/vm.c +++ b/src/cli/vm.c @@ -145,6 +145,11 @@ static WrenForeignClassMethods bindForeignClass( return methods; } +static void write(WrenVM* vm, const char* text) +{ + printf("%s", text); +} + static void initVM() { WrenConfiguration config; @@ -153,6 +158,7 @@ static void initVM() config.bindForeignMethodFn = bindForeignMethod; config.bindForeignClassFn = bindForeignClass; config.loadModuleFn = readModule; + config.writeFn = write; // Since we're running in a standalone process, be generous with memory. config.initialHeapSize = 1024 * 1024 * 100; diff --git a/src/include/wren.h b/src/include/wren.h index a357c4de..40fd47c0 100644 --- a/src/include/wren.h +++ b/src/include/wren.h @@ -48,6 +48,9 @@ typedef WrenForeignMethodFn (*WrenBindForeignMethodFn)(WrenVM* vm, bool isStatic, const char* signature); +// Displays a string of text to the user. +typedef void (*WrenWriteFn)(WrenVM* vm, const char* text); + typedef struct { // The callback invoked when the foreign object is created. @@ -111,6 +114,12 @@ typedef struct // stored in the foreign object when an instance is created. WrenBindForeignClassFn bindForeignClassFn; + // The callback Wren uses to display text when `System.print()` or the other + // related functions are called. + // + // If this is `NULL`, Wren discards any printed text. + WrenWriteFn writeFn; + // The number of bytes Wren will allocate before triggering the first garbage // collection. // diff --git a/src/vm/wren_core.c b/src/vm/wren_core.c index 55503075..f1a28f9e 100644 --- a/src/vm/wren_core.c +++ b/src/vm/wren_core.c @@ -1,8 +1,6 @@ #include #include #include -#include -#include #include #include @@ -1049,7 +1047,11 @@ DEF_PRIMITIVE(system_clock) DEF_PRIMITIVE(system_writeString) { - printf("%s", AS_CSTRING(args[1])); + if (vm->config.writeFn != NULL) + { + vm->config.writeFn(vm, AS_CSTRING(args[1])); + } + RETURN_VAL(args[1]); } diff --git a/src/vm/wren_vm.c b/src/vm/wren_vm.c index 717050a2..03adf27a 100644 --- a/src/vm/wren_vm.c +++ b/src/vm/wren_vm.c @@ -1,6 +1,4 @@ #include -#include -#include #include #include "wren.h" @@ -37,40 +35,26 @@ static void* defaultReallocate(void* ptr, size_t newSize) return realloc(ptr, newSize); } -void wrenInitConfiguration(WrenConfiguration* configuration) +void wrenInitConfiguration(WrenConfiguration* config) { - configuration->reallocateFn = NULL; - configuration->loadModuleFn = NULL; - configuration->bindForeignMethodFn = NULL; - configuration->bindForeignClassFn = NULL; - configuration->initialHeapSize = 1024 * 1024 * 10; - configuration->minHeapSize = 1024 * 1024; - configuration->heapGrowthPercent = 50; + config->reallocateFn = defaultReallocate; + config->loadModuleFn = NULL; + config->bindForeignMethodFn = NULL; + config->bindForeignClassFn = NULL; + config->writeFn = NULL; + config->initialHeapSize = 1024 * 1024 * 10; + config->minHeapSize = 1024 * 1024; + config->heapGrowthPercent = 50; } -WrenVM* wrenNewVM(WrenConfiguration* configuration) +WrenVM* wrenNewVM(WrenConfiguration* config) { - WrenReallocateFn reallocate = defaultReallocate; - if (configuration->reallocateFn != NULL) - { - reallocate = configuration->reallocateFn; - } - - WrenVM* vm = (WrenVM*)reallocate(NULL, sizeof(*vm)); + WrenVM* vm = (WrenVM*)config->reallocateFn(NULL, sizeof(*vm)); memset(vm, 0, sizeof(WrenVM)); + memcpy(&vm->config, config, sizeof(WrenConfiguration)); - vm->reallocate = reallocate; - vm->bindForeignMethod = configuration->bindForeignMethodFn; - vm->bindForeignClass = configuration->bindForeignClassFn; - vm->loadModule = configuration->loadModuleFn; - vm->nextGC = configuration->initialHeapSize; - vm->minNextGC = configuration->minHeapSize; + vm->nextGC = config->initialHeapSize; - // +100 here because the configuration gives us the *additional* size of - // the heap relative to the in-use memory, while heapScalePercent is the - // *total* size of the heap relative to in-use. - vm->heapScalePercent = 100 + configuration->heapGrowthPercent; - wrenSymbolTableInit(&vm->methodNames); ObjString* name = AS_STRING(CONST_STRING(vm, "core")); @@ -188,8 +172,11 @@ void wrenCollectGarbage(WrenVM* vm) } } - vm->nextGC = vm->bytesAllocated * vm->heapScalePercent / 100; - if (vm->nextGC < vm->minNextGC) vm->nextGC = vm->minNextGC; + // +100 here because the configuration gives us the *additional* size of + // the heap relative to the in-use memory, while heapScalePercent is the + // *total* size of the heap relative to in-use. + vm->nextGC = vm->bytesAllocated * (100 + vm->config.heapGrowthPercent) / 100; + if (vm->nextGC < vm->config.minHeapSize) vm->nextGC = vm->config.minHeapSize; #if WREN_DEBUG_TRACE_MEMORY || WREN_DEBUG_TRACE_GC double elapsed = ((double)clock() / CLOCKS_PER_SEC) - startTime; @@ -227,7 +214,7 @@ void* wrenReallocate(WrenVM* vm, void* memory, size_t oldSize, size_t newSize) if (newSize > 0 && vm->bytesAllocated > vm->nextGC) wrenCollectGarbage(vm); #endif - return vm->reallocate(memory, newSize); + return vm->config.reallocateFn(memory, newSize); } // Captures the local variable [local] into an [Upvalue]. If that local is @@ -298,9 +285,9 @@ static WrenForeignMethodFn findForeignMethod(WrenVM* vm, bool isStatic, const char* signature) { - if (vm->bindForeignMethod == NULL) return NULL; + if (vm->config.bindForeignMethodFn == NULL) return NULL; - return vm->bindForeignMethod(vm, moduleName, className, isStatic, signature); + return vm->config.bindForeignMethodFn(vm, moduleName, className, isStatic, signature); } // Defines [methodValue] as a method on [classObj]. @@ -484,7 +471,7 @@ static Value importModule(WrenVM* vm, Value name) if (!IS_UNDEFINED(wrenMapGet(vm->modules, name))) return NULL_VAL; // Load the module's source code from the embedder. - char* source = vm->loadModule(vm, AS_CSTRING(name)); + char* source = vm->config.loadModuleFn(vm, AS_CSTRING(name)); if (source == NULL) { // Couldn't load the module. @@ -585,10 +572,10 @@ static Value validateSuperclass(WrenVM* vm, Value name, Value superclassValue, static void bindForeignClass(WrenVM* vm, ObjClass* classObj, ObjModule* module) { // TODO: Make this a runtime error? - ASSERT(vm->bindForeignClass != NULL, + ASSERT(vm->config.bindForeignClassFn != NULL, "Cannot declare foreign classes without a bindForeignClassFn."); - WrenForeignClassMethods methods = vm->bindForeignClass( + WrenForeignClassMethods methods = vm->config.bindForeignClassFn( vm, module->name->value, classObj->name->value); Method method; @@ -1492,7 +1479,7 @@ Value wrenImportModule(WrenVM* vm, const char* name) } // Load the module's source code from the embedder. - char* source = vm->loadModule(vm, name); + char* source = vm->config.loadModuleFn(vm, name); if (source == NULL) { wrenPopRoot(vm); // nameValue. diff --git a/src/vm/wren_vm.h b/src/vm/wren_vm.h index cef542ec..56bf5173 100644 --- a/src/vm/wren_vm.h +++ b/src/vm/wren_vm.h @@ -57,9 +57,6 @@ struct WrenVM // Memory management data: - // The externally-provided function used to allocate memory. - WrenReallocateFn reallocate; - // The number of bytes that are known to be currently allocated. Includes all // memory that was proven live after the last GC, as well as any new bytes // that were allocated since then. Does *not* include bytes for objects that @@ -69,14 +66,6 @@ struct WrenVM // The number of total allocated bytes that will trigger the next GC. size_t nextGC; - // The minimum value for [nextGC] when recalculated after a collection. - size_t minNextGC; - - // The scale factor used to calculate [nextGC] from the current number of in - // use bytes, as a percent. For example, 150 here means that nextGC will be - // 50% larger than the current number of in-use bytes. - int heapScalePercent; - // The first object in the linked list of all currently allocated objects. Obj* first; @@ -104,15 +93,8 @@ struct WrenVM // to the function. int foreignCallNumArgs; - // The function used to locate foreign functions. - WrenBindForeignMethodFn bindForeignMethod; + WrenConfiguration config; - // The function used to locate foreign classes. - WrenBindForeignClassFn bindForeignClass; - - // The function used to load modules. - WrenLoadModuleFn loadModule; - // Compiler and debugger data: // The compiler that is currently compiling code. This is used so that heap