diff --git a/Makefile b/Makefile index c402486a..493b5868 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,7 @@ test: debug benchmark: release @ $(MAKE) -f util/wren.mk test - @ ./util/benchmark.py $(suite) + @ ./util/benchmark.py -l wren $(suite) # Generate the Wren site. docs: diff --git a/src/vm/wren_value.c b/src/vm/wren_value.c index ea576b9a..ab3c8f1f 100644 --- a/src/vm/wren_value.c +++ b/src/vm/wren_value.c @@ -152,7 +152,9 @@ ObjFiber* wrenNewFiber(WrenVM* vm, Obj* fn) // Add one slot for the unused implicit receiver slot that the compiler // assumes all functions have. - int stackCapacity = wrenPowerOf2Ceil(wrenUpwrapClosure(fn)->maxSlots + 1); + int stackCapacity = fn == NULL + ? 1 + : wrenPowerOf2Ceil(wrenUpwrapClosure(fn)->maxSlots + 1); Value* stack = ALLOCATE_ARRAY(vm, Value, stackCapacity); ObjFiber* fiber = ALLOCATE(vm, ObjFiber); @@ -174,10 +176,10 @@ void wrenResetFiber(WrenVM* vm, ObjFiber* fiber, Obj* fn) fiber->caller = NULL; fiber->error = NULL_VAL; fiber->callerIsTrying = false; + fiber->numFrames = 0; // Initialize the first call frame. - fiber->numFrames = 0; - wrenAppendCallFrame(vm, fiber, fn, fiber->stack); + if (fn != NULL) wrenAppendCallFrame(vm, fiber, fn, fiber->stack); } ObjForeign* wrenNewForeign(WrenVM* vm, ObjClass* classObj, size_t size) diff --git a/src/vm/wren_vm.c b/src/vm/wren_vm.c index e517fb65..83ee85e5 100644 --- a/src/vm/wren_vm.c +++ b/src/vm/wren_vm.c @@ -65,6 +65,8 @@ WrenVM* wrenNewVM(WrenConfiguration* config) vm->modules = wrenNewMap(vm); wrenInitializeCore(vm); + + vm->finalizerFiber = wrenNewFiber(vm, NULL); // TODO: Lazy load these. #if WREN_OPT_META @@ -137,8 +139,9 @@ void wrenCollectGarbage(WrenVM* vm) wrenGrayObj(vm, vm->tempRoots[i]); } - // The current fiber. + // The rooted fibers. wrenGrayObj(vm, (Obj*)vm->fiber); + wrenGrayObj(vm, (Obj*)vm->finalizerFiber); // The value handles. for (WrenValue* value = vm->valueHandles; @@ -740,10 +743,16 @@ void wrenFinalizeForeign(WrenVM* vm, ObjForeign* foreign) ASSERT(method->type == METHOD_FOREIGN, "Finalizer should be foreign."); // Pass the foreign object to the finalizer. - Value slot = OBJ_VAL(foreign); - vm->foreignStackStart = &slot; + *vm->finalizerFiber->stackTop++ = OBJ_VAL(foreign); + ObjFiber* previousFiber = vm->fiber; + vm->fiber = vm->finalizerFiber; + vm->foreignStackStart = vm->finalizerFiber->stack; method->fn.foreign(vm); + + vm->fiber = previousFiber; + vm->foreignStackStart = NULL; + wrenResetFiber(vm, vm->finalizerFiber, NULL); } // The main bytecode interpreter loop. This is where the magic happens. It is @@ -1637,28 +1646,17 @@ void wrenPopRoot(WrenVM* vm) vm->numTempRoots--; } -// Returns true if the VM is in a foreign method that's a finalizer. -// -// Finalizers don't run in the context of a fiber and have a single magic stack -// slot, so need to be handled a little specially. -static bool isInFinalizer(WrenVM* vm) -{ - return vm->fiber == NULL || - vm->foreignStackStart < vm->fiber->stack || - vm->foreignStackStart > vm->fiber->stackTop; -} - int wrenGetSlotCount(WrenVM* vm) { ASSERT(vm->foreignStackStart != NULL, "Must be in foreign call."); - if (isInFinalizer(vm)) return 1; return (int)(vm->fiber->stackTop - vm->foreignStackStart); } void wrenEnsureSlots(WrenVM* vm, int numSlots) { - ASSERT(!isInFinalizer(vm), "Cannot grow the stack in a finalizer."); + ASSERT(vm->fiber != vm->finalizerFiber, + "Cannot grow the stack in a finalizer."); int currentSize = (int)(vm->fiber->stackTop - vm->foreignStackStart); if (currentSize >= numSlots) return; diff --git a/src/vm/wren_vm.h b/src/vm/wren_vm.h index 1d0b0776..5508e7d3 100644 --- a/src/vm/wren_vm.h +++ b/src/vm/wren_vm.h @@ -102,6 +102,12 @@ struct WrenVM // There is a single global symbol table for all method names on all classes. // Method calls are dispatched directly by index in this table. SymbolTable methodNames; + + // A special fiber whose stack is used for communicating with foreign + // finalizer methods. Finalizers run during GC, so we don't want to do any + // allocation in order to execute one, so we set up this fiber ahead of time + // to ensure it's available. + ObjFiber* finalizerFiber; }; // A generic allocation function that handles all explicit memory management.