Let the host application control how System prints text.

This commit is contained in:
Bob Nystrom
2015-09-15 23:01:43 -07:00
parent 58e4d26648
commit efceee4320
5 changed files with 46 additions and 60 deletions

View File

@ -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;

View File

@ -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.
//

View File

@ -1,8 +1,6 @@
#include <ctype.h>
#include <errno.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
@ -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]);
}

View File

@ -1,6 +1,4 @@
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#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.

View File

@ -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