Add Fiber.try().
This commit is contained in:
+32
-1
@@ -269,10 +269,17 @@ DEF_NATIVE(fiber_call1)
|
||||
return PRIM_RUN_FIBER;
|
||||
}
|
||||
|
||||
DEF_NATIVE(fiber_error)
|
||||
{
|
||||
ObjFiber* runFiber = AS_FIBER(args[0]);
|
||||
if (runFiber->error == NULL) RETURN_NULL;
|
||||
RETURN_OBJ(runFiber->error);
|
||||
}
|
||||
|
||||
DEF_NATIVE(fiber_isDone)
|
||||
{
|
||||
ObjFiber* runFiber = AS_FIBER(args[0]);
|
||||
RETURN_BOOL(runFiber->numFrames == 0);
|
||||
RETURN_BOOL(runFiber->numFrames == 0 || runFiber->error != NULL);
|
||||
}
|
||||
|
||||
DEF_NATIVE(fiber_run)
|
||||
@@ -320,12 +327,33 @@ DEF_NATIVE(fiber_run1)
|
||||
return PRIM_RUN_FIBER;
|
||||
}
|
||||
|
||||
DEF_NATIVE(fiber_try)
|
||||
{
|
||||
ObjFiber* runFiber = AS_FIBER(args[0]);
|
||||
|
||||
if (runFiber->numFrames == 0) RETURN_ERROR("Cannot try a finished fiber.");
|
||||
if (runFiber->caller != NULL) RETURN_ERROR("Fiber has already been called.");
|
||||
|
||||
// Remember who ran it.
|
||||
runFiber->caller = fiber;
|
||||
runFiber->callerIsTrying = true;
|
||||
|
||||
// If the fiber was yielded, make the yield call return null.
|
||||
if (runFiber->stackSize > 0)
|
||||
{
|
||||
runFiber->stack[runFiber->stackSize - 1] = NULL_VAL;
|
||||
}
|
||||
|
||||
return PRIM_RUN_FIBER;
|
||||
}
|
||||
|
||||
DEF_NATIVE(fiber_yield)
|
||||
{
|
||||
if (fiber->caller == NULL) RETURN_ERROR("No fiber to yield to.");
|
||||
|
||||
ObjFiber* caller = fiber->caller;
|
||||
fiber->caller = NULL;
|
||||
fiber->callerIsTrying = false;
|
||||
|
||||
// Make the caller's run method return null.
|
||||
caller->stack[caller->stackSize - 1] = NULL_VAL;
|
||||
@@ -341,6 +369,7 @@ DEF_NATIVE(fiber_yield1)
|
||||
|
||||
ObjFiber* caller = fiber->caller;
|
||||
fiber->caller = NULL;
|
||||
fiber->callerIsTrying = false;
|
||||
|
||||
// Make the caller's run method return the argument passed to yield.
|
||||
caller->stack[caller->stackSize - 1] = args[1];
|
||||
@@ -1027,9 +1056,11 @@ void wrenInitializeCore(WrenVM* vm)
|
||||
NATIVE(vm->fiberClass->metaclass, "yield ", fiber_yield1);
|
||||
NATIVE(vm->fiberClass, "call", fiber_call);
|
||||
NATIVE(vm->fiberClass, "call ", fiber_call1);
|
||||
NATIVE(vm->fiberClass, "error", fiber_error);
|
||||
NATIVE(vm->fiberClass, "isDone", fiber_isDone);
|
||||
NATIVE(vm->fiberClass, "run", fiber_run);
|
||||
NATIVE(vm->fiberClass, "run ", fiber_run1);
|
||||
NATIVE(vm->fiberClass, "try", fiber_try);
|
||||
|
||||
vm->fnClass = defineClass(vm, "Fn");
|
||||
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
|
||||
void wrenDebugPrintStackTrace(WrenVM* vm, ObjFiber* fiber)
|
||||
{
|
||||
fprintf(stderr, "%s\n", fiber->error);
|
||||
fprintf(stderr, "%s\n", fiber->error->value);
|
||||
|
||||
for (int i = fiber->numFrames - 1; i >= 0; i--)
|
||||
{
|
||||
|
||||
+4
-7
@@ -140,6 +140,7 @@ ObjFiber* wrenNewFiber(WrenVM* vm, Obj* fn)
|
||||
fiber->openUpvalues = NULL;
|
||||
fiber->caller = NULL;
|
||||
fiber->error = NULL;
|
||||
fiber->callerIsTrying = false;
|
||||
|
||||
CallFrame* frame = &fiber->frames[0];
|
||||
frame->fn = fn;
|
||||
@@ -512,6 +513,8 @@ static void markFiber(WrenVM* vm, ObjFiber* fiber)
|
||||
// The caller.
|
||||
if (fiber->caller != NULL) markFiber(vm, fiber->caller);
|
||||
|
||||
if (fiber->error != NULL) markString(vm, fiber->error);
|
||||
|
||||
// Keep track of how much memory is still in use.
|
||||
vm->bytesAllocated += sizeof(ObjFiber);
|
||||
// TODO: Count size of error message buffer.
|
||||
@@ -586,13 +589,6 @@ void wrenFreeObj(WrenVM* vm, Obj* obj)
|
||||
wrenMethodBufferClear(vm, &((ObjClass*)obj)->methods);
|
||||
break;
|
||||
|
||||
case OBJ_FIBER:
|
||||
{
|
||||
ObjFiber* fiber = ((ObjFiber*)obj);
|
||||
if (fiber->error != NULL) wrenReallocate(vm, fiber->error, 0, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case OBJ_FN:
|
||||
{
|
||||
ObjFn* fn = (ObjFn*)obj;
|
||||
@@ -613,6 +609,7 @@ void wrenFreeObj(WrenVM* vm, Obj* obj)
|
||||
break;
|
||||
|
||||
case OBJ_CLOSURE:
|
||||
case OBJ_FIBER:
|
||||
case OBJ_INSTANCE:
|
||||
case OBJ_RANGE:
|
||||
case OBJ_UPVALUE:
|
||||
|
||||
+14
-9
@@ -94,6 +94,14 @@ typedef struct
|
||||
|
||||
DECLARE_BUFFER(Value, Value);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Obj obj;
|
||||
char* value;
|
||||
|
||||
// TODO: Flexible array.
|
||||
} ObjString;
|
||||
|
||||
// The dynamically allocated data structure for a variable that has been used
|
||||
// by a closure. Whenever a function accesses a variable declared in an
|
||||
// enclosing function, it will get to it through this.
|
||||
@@ -160,7 +168,12 @@ typedef struct sObjFiber
|
||||
|
||||
// If the fiber failed because of a runtime error, this will contain the
|
||||
// error message. Otherwise, it will be NULL.
|
||||
char* error;
|
||||
ObjString* error;
|
||||
|
||||
// This will be true if the caller that called this fiber did so using "try".
|
||||
// In that case, if this fiber fails with an error, the error will be given
|
||||
// to the caller.
|
||||
bool callerIsTrying;
|
||||
} ObjFiber;
|
||||
|
||||
typedef enum
|
||||
@@ -179,14 +192,6 @@ typedef enum
|
||||
|
||||
} PrimitiveResult;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Obj obj;
|
||||
char* value;
|
||||
|
||||
// TODO: Flexible array.
|
||||
} ObjString;
|
||||
|
||||
typedef PrimitiveResult (*Primitive)(WrenVM* vm, ObjFiber* fiber, Value* args);
|
||||
|
||||
// TODO: See if it's actually a perf improvement to have this in a separate
|
||||
|
||||
+32
-16
@@ -303,19 +303,30 @@ static void callForeign(WrenVM* vm, ObjFiber* fiber,
|
||||
}
|
||||
|
||||
// Puts [fiber] into a runtime failed state because of [error].
|
||||
static void runtimeError(WrenVM* vm, ObjFiber* fiber, const char* error)
|
||||
//
|
||||
// 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)
|
||||
{
|
||||
// Copy the error onto the heap.
|
||||
size_t length = strlen(error) + 1;
|
||||
char* heapError = wrenReallocate(vm, NULL, 0, length);
|
||||
strncpy(heapError, error, length);
|
||||
|
||||
ASSERT(fiber->error == NULL, "Can only fail once.");
|
||||
fiber->error = heapError;
|
||||
|
||||
// TODO: If the calling fiber is going to handle the error, we shouldn't dump
|
||||
// a stack trace.
|
||||
// Store the error in the fiber so it can be accessed later.
|
||||
fiber->error = AS_STRING(wrenNewString(vm, error, strlen(error)));
|
||||
|
||||
// If the caller ran this fiber using "try", give it the error.
|
||||
if (fiber->callerIsTrying)
|
||||
{
|
||||
ObjFiber* caller = fiber->caller;
|
||||
|
||||
// Make the caller's try method return the error message.
|
||||
caller->stack[caller->stackSize - 1] = OBJ_VAL(fiber->error);
|
||||
|
||||
return caller;
|
||||
}
|
||||
|
||||
// If we got here, nothing caught the error, so show the stack trace.
|
||||
wrenDebugPrintStackTrace(vm, fiber);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Pushes [function] onto [fiber]'s callstack and invokes it. Expects [numArgs]
|
||||
@@ -384,11 +395,16 @@ static bool runInterpreter(WrenVM* vm)
|
||||
upvalues = ((ObjClosure*)frame->fn)->upvalues; \
|
||||
}
|
||||
|
||||
// Terminates the current fiber with error string [error]. If another calling
|
||||
// fiber is willing to catch the error, transfers control to it, otherwise
|
||||
// exits the interpreter.
|
||||
#define RUNTIME_ERROR(error) \
|
||||
do { \
|
||||
STORE_FRAME(); \
|
||||
runtimeError(vm, fiber, error); \
|
||||
return false; \
|
||||
fiber = runtimeError(vm, fiber, error); \
|
||||
if (fiber == NULL) return false; \
|
||||
LOAD_FRAME(); \
|
||||
DISPATCH(); \
|
||||
} \
|
||||
while (false)
|
||||
|
||||
@@ -953,6 +969,11 @@ static bool runInterpreter(WrenVM* vm)
|
||||
|
||||
ObjClass* classObj = wrenNewClass(vm, superclass, numFields, name);
|
||||
|
||||
// Don't pop the superclass and name off the stack until the subclass is
|
||||
// done being created, to make sure it doesn't get collected.
|
||||
DROP();
|
||||
DROP();
|
||||
|
||||
// Now that we know the total number of fields, make sure we don't
|
||||
// overflow.
|
||||
if (superclass->numFields + numFields > MAX_FIELDS)
|
||||
@@ -964,11 +985,6 @@ static bool runInterpreter(WrenVM* vm)
|
||||
RUNTIME_ERROR(message);
|
||||
}
|
||||
|
||||
// Don't pop the superclass and name off the stack until the subclass is
|
||||
// done being created, to make sure it doesn't get collected.
|
||||
DROP();
|
||||
DROP();
|
||||
|
||||
PUSH(OBJ_VAL(classObj));
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user