Allow passing a value when first starting a fiber.

If the function the fiber is created from takes a parameter, the value
passed to the first call() or transfer() gets bound to that parameter.

Also, this now correctly handles fibers with functions that take
parameters. It used to leave the stack in a busted state. Now, it's a
runtime error to create a fiber with a function that takes any more
than one parameter.
This commit is contained in:
Bob Nystrom
2017-10-19 20:45:13 -07:00
parent e043a95e4e
commit 1661f5368f
12 changed files with 182 additions and 86 deletions
+21 -4
View File
@@ -53,14 +53,20 @@ DEF_PRIMITIVE(fiber_new)
{
if (!validateFn(vm, args[1], "Argument")) return false;
ObjFiber* newFiber = wrenNewFiber(vm, AS_CLOSURE(args[1]));
ObjClosure* closure = AS_CLOSURE(args[1]);
if (closure->fn->arity > 1)
{
RETURN_ERROR("Function cannot take more than one parameter.");
}
ObjFiber* newFiber = wrenNewFiber(vm, closure);
// The compiler expects the first slot of a function to hold the receiver.
// Since a fiber's stack is invoked directly, it doesn't have one, so put it
// in here.
newFiber->stack[0] = NULL_VAL;
newFiber->stackTop++;
RETURN_OBJ(newFiber);
}
@@ -106,9 +112,20 @@ static bool runFiber(WrenVM* vm, ObjFiber* fiber, Value* args, bool isCall,
// need one slot for the result, so discard the other slot now.
if (hasValue) vm->fiber->stackTop--;
// If the fiber was paused, make yield() or transfer() return the result.
if (fiber->stackTop > fiber->stack)
if (fiber->numFrames == 1 &&
fiber->frames[0].ip == fiber->frames[0].closure->fn->code.data)
{
// The fiber is being started for the first time. If its function takes a
// parameter, bind an argument to it.
if (fiber->frames[0].closure->fn->arity == 1)
{
fiber->stackTop[0] = hasValue ? args[1] : NULL_VAL;
fiber->stackTop++;
}
}
else
{
// The fiber is being resumed, make yield() or transfer() return the result.
fiber->stackTop[-1] = hasValue ? args[1] : NULL_VAL;
}
+1 -1
View File
@@ -170,7 +170,7 @@ ObjFiber* wrenNewFiber(WrenVM* vm, ObjClosure* closure)
void wrenResetFiber(WrenVM* vm, ObjFiber* fiber, ObjClosure* closure)
{
// Push the stack frame for the function.
// Reset everything.
fiber->stackTop = fiber->stack;
fiber->openUpvalues = NULL;
fiber->caller = NULL;