Get rid of PrimitiveResult.

Primitives still have a return value to indicate normal value returning
versus runtime errors or fiber switching since it minimizes branches
in the common case of a normal value result.
This commit is contained in:
Bob Nystrom
2015-10-03 20:43:12 -07:00
parent 812bc15380
commit 76d188e018
5 changed files with 77 additions and 92 deletions

View File

@ -50,7 +50,7 @@ DEF_PRIMITIVE(class_toString)
DEF_PRIMITIVE(fiber_new)
{
if (!validateFn(vm, args[1], "Argument")) return PRIM_FIBER;
if (!validateFn(vm, args[1], "Argument")) return false;
ObjFiber* newFiber = wrenNewFiber(vm, AS_OBJ(args[1]));
@ -70,7 +70,7 @@ DEF_PRIMITIVE(fiber_abort)
vm->fiber->error = args[1];
// If the error is explicitly null, it's not really an abort.
return IS_NULL(args[1]) ? PRIM_VALUE : PRIM_FIBER;
return IS_NULL(args[1]);
}
// Transfer execution to [fiber] coming from the current fiber whose stack has
@ -80,8 +80,8 @@ DEF_PRIMITIVE(fiber_abort)
//
// [hasValue] is true if a value in [args] is being passed to the new fiber.
// Otherwise, `null` is implicitly being passed.
static PrimitiveResult runFiber(WrenVM* vm, ObjFiber* fiber, Value* args,
bool isCall, bool hasValue)
static bool runFiber(WrenVM* vm, ObjFiber* fiber, Value* args, bool isCall,
bool hasValue)
{
if (isCall)
{
@ -95,14 +95,14 @@ static PrimitiveResult runFiber(WrenVM* vm, ObjFiber* fiber, Value* args,
{
vm->fiber->error = wrenStringFormat(vm, "Cannot $ a finished fiber.",
isCall ? "call" : "transfer to");
return PRIM_FIBER;
return false;
}
if (!IS_NULL(fiber->error))
{
vm->fiber->error = wrenStringFormat(vm, "Cannot $ an aborted fiber.",
isCall ? "call" : "transfer to");
return PRIM_FIBER;
return false;
}
// When the calling fiber resumes, we'll store the result of the call in its
@ -117,8 +117,7 @@ static PrimitiveResult runFiber(WrenVM* vm, ObjFiber* fiber, Value* args,
}
vm->fiber = fiber;
return PRIM_FIBER;
return false;
}
DEF_PRIMITIVE(fiber_call)
@ -151,7 +150,7 @@ DEF_PRIMITIVE(fiber_suspend)
{
// Switching to a null fiber tells the interpreter to stop and exit.
vm->fiber = NULL;
return PRIM_FIBER;
return false;
}
DEF_PRIMITIVE(fiber_transfer)
@ -166,10 +165,9 @@ DEF_PRIMITIVE(fiber_transfer1)
DEF_PRIMITIVE(fiber_transferError)
{
PrimitiveResult result = runFiber(vm, AS_FIBER(args[0]), args, false, true);
if (result == PRIM_FIBER) vm->fiber->error = args[1];
return result;
runFiber(vm, AS_FIBER(args[0]), args, false, true);
vm->fiber->error = args[1];
return false;
}
DEF_PRIMITIVE(fiber_try)
@ -192,7 +190,7 @@ DEF_PRIMITIVE(fiber_try)
vm->fiber->stackTop[-1] = NULL_VAL;
}
return PRIM_FIBER;
return false;
}
DEF_PRIMITIVE(fiber_yield)
@ -210,7 +208,7 @@ DEF_PRIMITIVE(fiber_yield)
vm->fiber->stackTop[-1] = NULL_VAL;
}
return PRIM_FIBER;
return false;
}
DEF_PRIMITIVE(fiber_yield1)
@ -234,12 +232,12 @@ DEF_PRIMITIVE(fiber_yield1)
current->stackTop--;
}
return PRIM_FIBER;
return false;
}
DEF_PRIMITIVE(fn_new)
{
if (!validateFn(vm, args[1], "Argument")) return PRIM_FIBER;
if (!validateFn(vm, args[1], "Argument")) return false;
// The block argument is already a function, so just return it.
RETURN_VAL(args[1]);
@ -284,7 +282,7 @@ DEF_PRIMITIVE(list_insert)
// count + 1 here so you can "insert" at the very end.
uint32_t index = validateIndex(vm, args[1], list->elements.count + 1,
"Index");
if (index == UINT32_MAX) return PRIM_FIBER;
if (index == UINT32_MAX) return false;
wrenListInsert(vm, list, args[2], index);
RETURN_VAL(args[2]);
@ -301,7 +299,7 @@ DEF_PRIMITIVE(list_iterate)
RETURN_NUM(0);
}
if (!validateInt(vm, args[1], "Iterator")) return PRIM_FIBER;
if (!validateInt(vm, args[1], "Iterator")) return false;
// Stop if we're out of bounds.
double index = AS_NUM(args[1]);
@ -315,7 +313,7 @@ DEF_PRIMITIVE(list_iteratorValue)
{
ObjList* list = AS_LIST(args[0]);
uint32_t index = validateIndex(vm, args[1], list->elements.count, "Iterator");
if (index == UINT32_MAX) return PRIM_FIBER;
if (index == UINT32_MAX) return false;
RETURN_VAL(list->elements.data[index]);
}
@ -324,7 +322,7 @@ DEF_PRIMITIVE(list_removeAt)
{
ObjList* list = AS_LIST(args[0]);
uint32_t index = validateIndex(vm, args[1], list->elements.count, "Index");
if (index == UINT32_MAX) return PRIM_FIBER;
if (index == UINT32_MAX) return false;
RETURN_VAL(wrenListRemoveAt(vm, list, index));
}
@ -337,7 +335,7 @@ DEF_PRIMITIVE(list_subscript)
{
uint32_t index = validateIndex(vm, args[1], list->elements.count,
"Subscript");
if (index == UINT32_MAX) return PRIM_FIBER;
if (index == UINT32_MAX) return false;
RETURN_VAL(list->elements.data[index]);
}
@ -350,7 +348,7 @@ DEF_PRIMITIVE(list_subscript)
int step;
uint32_t count = list->elements.count;
uint32_t start = calculateRange(vm, AS_RANGE(args[1]), &count, &step);
if (start == UINT32_MAX) return PRIM_FIBER;
if (start == UINT32_MAX) return false;
ObjList* result = wrenNewList(vm, count);
for (uint32_t i = 0; i < count; i++)
@ -366,7 +364,7 @@ DEF_PRIMITIVE(list_subscriptSetter)
ObjList* list = AS_LIST(args[0]);
uint32_t index = validateIndex(vm, args[1], list->elements.count,
"Subscript");
if (index == UINT32_MAX) return PRIM_FIBER;
if (index == UINT32_MAX) return false;
list->elements.data[index] = args[2];
RETURN_VAL(args[2]);
@ -379,7 +377,7 @@ DEF_PRIMITIVE(map_new)
DEF_PRIMITIVE(map_subscript)
{
if (!validateKey(vm, args[1])) return PRIM_FIBER;
if (!validateKey(vm, args[1])) return false;
ObjMap* map = AS_MAP(args[0]);
Value value = wrenMapGet(map, args[1]);
@ -390,7 +388,7 @@ DEF_PRIMITIVE(map_subscript)
DEF_PRIMITIVE(map_subscriptSetter)
{
if (!validateKey(vm, args[1])) return PRIM_FIBER;
if (!validateKey(vm, args[1])) return false;
wrenMapSet(vm, AS_MAP(args[0]), args[1], args[2]);
RETURN_VAL(args[2]);
@ -404,7 +402,7 @@ DEF_PRIMITIVE(map_clear)
DEF_PRIMITIVE(map_containsKey)
{
if (!validateKey(vm, args[1])) return PRIM_FIBER;
if (!validateKey(vm, args[1])) return false;
RETURN_BOOL(!IS_UNDEFINED(wrenMapGet(AS_MAP(args[0]), args[1])));
}
@ -426,7 +424,7 @@ DEF_PRIMITIVE(map_iterate)
// Otherwise, start one past the last entry we stopped at.
if (!IS_NULL(args[1]))
{
if (!validateInt(vm, args[1], "Iterator")) return PRIM_FIBER;
if (!validateInt(vm, args[1], "Iterator")) return false;
if (AS_NUM(args[1]) < 0) RETURN_FALSE;
index = (uint32_t)AS_NUM(args[1]);
@ -449,7 +447,7 @@ DEF_PRIMITIVE(map_iterate)
DEF_PRIMITIVE(map_remove)
{
if (!validateKey(vm, args[1])) return PRIM_FIBER;
if (!validateKey(vm, args[1])) return false;
RETURN_VAL(wrenMapRemoveKey(vm, AS_MAP(args[0]), args[1]));
}
@ -458,7 +456,7 @@ DEF_PRIMITIVE(map_keyIteratorValue)
{
ObjMap* map = AS_MAP(args[0]);
uint32_t index = validateIndex(vm, args[1], map->capacity, "Iterator");
if (index == UINT32_MAX) return PRIM_FIBER;
if (index == UINT32_MAX) return false;
MapEntry* entry = &map->entries[index];
if (IS_UNDEFINED(entry->key))
@ -473,7 +471,7 @@ DEF_PRIMITIVE(map_valueIteratorValue)
{
ObjMap* map = AS_MAP(args[0]);
uint32_t index = validateIndex(vm, args[1], map->capacity, "Iterator");
if (index == UINT32_MAX) return PRIM_FIBER;
if (index == UINT32_MAX) return false;
MapEntry* entry = &map->entries[index];
if (IS_UNDEFINED(entry->key))
@ -496,7 +494,7 @@ DEF_PRIMITIVE(null_toString)
DEF_PRIMITIVE(num_fromString)
{
if (!validateString(vm, args[1], "Argument")) return PRIM_FIBER;
if (!validateString(vm, args[1], "Argument")) return false;
ObjString* string = AS_STRING(args[1]);
@ -513,7 +511,7 @@ DEF_PRIMITIVE(num_fromString)
if (errno == ERANGE)
{
vm->fiber->error = CONST_STRING(vm, "Number literal is too large.");
return PRIM_FIBER;
return false;
}
// We must have consumed the entire string. Otherwise, it contains non-number
@ -532,7 +530,7 @@ DEF_PRIMITIVE(num_pi)
#define DEF_NUM_INFIX(name, op, type) \
DEF_PRIMITIVE(num_##name) \
{ \
if (!validateNum(vm, args[1], "Right operand")) return PRIM_FIBER; \
if (!validateNum(vm, args[1], "Right operand")) return false; \
RETURN_##type(AS_NUM(args[0]) op AS_NUM(args[1])); \
}
@ -549,7 +547,7 @@ DEF_NUM_INFIX(gte, >=, BOOL)
#define DEF_NUM_BITWISE(name, op) \
DEF_PRIMITIVE(num_bitwise##name) \
{ \
if (!validateNum(vm, args[1], "Right operand")) return PRIM_FIBER; \
if (!validateNum(vm, args[1], "Right operand")) return false; \
uint32_t left = (uint32_t)AS_NUM(args[0]); \
uint32_t right = (uint32_t)AS_NUM(args[1]); \
RETURN_NUM(left op right); \
@ -582,7 +580,7 @@ DEF_NUM_FN(tan, tan)
DEF_PRIMITIVE(num_mod)
{
if (!validateNum(vm, args[1], "Right operand")) return PRIM_FIBER;
if (!validateNum(vm, args[1], "Right operand")) return false;
RETURN_NUM(fmod(AS_NUM(args[0]), AS_NUM(args[1])));
}
@ -606,7 +604,7 @@ DEF_PRIMITIVE(num_bitwiseNot)
DEF_PRIMITIVE(num_dotDot)
{
if (!validateNum(vm, args[1], "Right hand side of range")) return PRIM_FIBER;
if (!validateNum(vm, args[1], "Right hand side of range")) return false;
double from = AS_NUM(args[0]);
double to = AS_NUM(args[1]);
@ -615,7 +613,7 @@ DEF_PRIMITIVE(num_dotDot)
DEF_PRIMITIVE(num_dotDotDot)
{
if (!validateNum(vm, args[1], "Right hand side of range")) return PRIM_FIBER;
if (!validateNum(vm, args[1], "Right hand side of range")) return false;
double from = AS_NUM(args[0]);
double to = AS_NUM(args[1]);
@ -770,7 +768,7 @@ DEF_PRIMITIVE(range_iterate)
// Start the iteration.
if (IS_NULL(args[1])) RETURN_NUM(range->from);
if (!validateNum(vm, args[1], "Iterator")) return PRIM_FIBER;
if (!validateNum(vm, args[1], "Iterator")) return false;
double iterator = AS_NUM(args[1]);
@ -817,7 +815,7 @@ DEF_PRIMITIVE(range_toString)
DEF_PRIMITIVE(string_fromCodePoint)
{
if (!validateInt(vm, args[1], "Code point")) return PRIM_FIBER;
if (!validateInt(vm, args[1], "Code point")) return false;
int codePoint = (int)AS_NUM(args[1]);
if (codePoint < 0)
@ -837,7 +835,7 @@ DEF_PRIMITIVE(string_byteAt)
ObjString* string = AS_STRING(args[0]);
uint32_t index = validateIndex(vm, args[1], string->length, "Index");
if (index == UINT32_MAX) return PRIM_FIBER;
if (index == UINT32_MAX) return false;
RETURN_NUM((uint8_t)string->value[index]);
}
@ -852,7 +850,7 @@ DEF_PRIMITIVE(string_codePointAt)
ObjString* string = AS_STRING(args[0]);
uint32_t index = validateIndex(vm, args[1], string->length, "Index");
if (index == UINT32_MAX) return PRIM_FIBER;
if (index == UINT32_MAX) return false;
// If we are in the middle of a UTF-8 sequence, indicate that.
const uint8_t* bytes = (uint8_t*)string->value;
@ -865,7 +863,7 @@ DEF_PRIMITIVE(string_codePointAt)
DEF_PRIMITIVE(string_contains)
{
if (!validateString(vm, args[1], "Argument")) return PRIM_FIBER;
if (!validateString(vm, args[1], "Argument")) return false;
ObjString* string = AS_STRING(args[0]);
ObjString* search = AS_STRING(args[1]);
@ -875,7 +873,7 @@ DEF_PRIMITIVE(string_contains)
DEF_PRIMITIVE(string_endsWith)
{
if (!validateString(vm, args[1], "Argument")) return PRIM_FIBER;
if (!validateString(vm, args[1], "Argument")) return false;
ObjString* string = AS_STRING(args[0]);
ObjString* search = AS_STRING(args[1]);
@ -889,7 +887,7 @@ DEF_PRIMITIVE(string_endsWith)
DEF_PRIMITIVE(string_indexOf)
{
if (!validateString(vm, args[1], "Argument")) return PRIM_FIBER;
if (!validateString(vm, args[1], "Argument")) return false;
ObjString* string = AS_STRING(args[0]);
ObjString* search = AS_STRING(args[1]);
@ -909,7 +907,7 @@ DEF_PRIMITIVE(string_iterate)
RETURN_NUM(0);
}
if (!validateInt(vm, args[1], "Iterator")) return PRIM_FIBER;
if (!validateInt(vm, args[1], "Iterator")) return false;
if (AS_NUM(args[1]) < 0) RETURN_FALSE;
uint32_t index = (uint32_t)AS_NUM(args[1]);
@ -935,7 +933,7 @@ DEF_PRIMITIVE(string_iterateByte)
RETURN_NUM(0);
}
if (!validateInt(vm, args[1], "Iterator")) return PRIM_FIBER;
if (!validateInt(vm, args[1], "Iterator")) return false;
if (AS_NUM(args[1]) < 0) RETURN_FALSE;
uint32_t index = (uint32_t)AS_NUM(args[1]);
@ -951,14 +949,14 @@ DEF_PRIMITIVE(string_iteratorValue)
{
ObjString* string = AS_STRING(args[0]);
uint32_t index = validateIndex(vm, args[1], string->length, "Iterator");
if (index == UINT32_MAX) return PRIM_FIBER;
if (index == UINT32_MAX) return false;
RETURN_VAL(wrenStringCodePointAt(vm, string, index));
}
DEF_PRIMITIVE(string_startsWith)
{
if (!validateString(vm, args[1], "Argument")) return PRIM_FIBER;
if (!validateString(vm, args[1], "Argument")) return false;
ObjString* string = AS_STRING(args[0]);
ObjString* search = AS_STRING(args[1]);
@ -971,7 +969,7 @@ DEF_PRIMITIVE(string_startsWith)
DEF_PRIMITIVE(string_plus)
{
if (!validateString(vm, args[1], "Right operand")) return PRIM_FIBER;
if (!validateString(vm, args[1], "Right operand")) return false;
RETURN_VAL(wrenStringFormat(vm, "@@", args[0], args[1]));
}
@ -982,7 +980,7 @@ DEF_PRIMITIVE(string_subscript)
if (IS_NUM(args[1]))
{
int index = validateIndex(vm, args[1], string->length, "Subscript");
if (index == -1) return PRIM_FIBER;
if (index == -1) return false;
RETURN_VAL(wrenStringCodePointAt(vm, string, index));
}
@ -995,7 +993,7 @@ DEF_PRIMITIVE(string_subscript)
int step;
uint32_t count = string->length;
int start = calculateRange(vm, AS_RANGE(args[1]), &count, &step);
if (start == -1) return PRIM_FIBER;
if (start == -1) return false;
RETURN_VAL(wrenNewStringFromRange(vm, string, start, count, step));
}

View File

@ -10,10 +10,10 @@
DEF_PRIMITIVE(meta_eval)
{
if (!validateString(vm, args[1], "Source code")) return PRIM_FIBER;
if (!validateString(vm, args[1], "Source code")) return false;
// Eval the code in the module where the calling function was defined.
Value callingFn = OBJ_VAL(vm->fiber->frames[vm->fiber->numFrames - 1].fn);
Value callingFn = OBJ_VAL(vm->fiber->frames[vm->fiber->numFrames - 1].fn);
ObjModule* module = IS_FN(callingFn)
? AS_FN(callingFn)->module
: AS_CLOSURE(callingFn)->fn->module;
@ -30,13 +30,13 @@ DEF_PRIMITIVE(meta_eval)
ObjFiber* evalFiber = wrenNewFiber(vm, (Obj*)fn);
// Remember what fiber to return to.
evalFiber->caller = vm->fiber;
evalFiber->caller = vm->fiber;
// Switch to the fiber.
vm->fiber = evalFiber;
wrenPopRoot(vm);
return PRIM_FIBER;
return false;
}
void wrenLoadMetaLibrary(WrenVM* vm)

View File

@ -19,9 +19,9 @@
// the actual type signature of a primitive function and makes it clear which C
// functions are invoked as primitives.
#define DEF_PRIMITIVE(name) \
static PrimitiveResult prim_##name(WrenVM* vm, Value* args)
static bool prim_##name(WrenVM* vm, Value* args)
#define RETURN_VAL(value) do { args[0] = value; return PRIM_VALUE; } while (0)
#define RETURN_VAL(value) do { args[0] = value; return true; } while (0)
#define RETURN_OBJ(obj) RETURN_VAL(OBJ_VAL(obj))
#define RETURN_BOOL(value) RETURN_VAL(BOOL_VAL(value))
@ -32,8 +32,8 @@
#define RETURN_ERROR(msg) \
do { \
vm->fiber->error = wrenStringFormat(vm, "$", msg); \
return PRIM_FIBER; \
vm->fiber->error = wrenNewString(vm, msg, sizeof(msg) - 1); \
return false; \
} while (0);
// Validates that the given [arg] is a function. Returns true if it is. If not,

View File

@ -246,18 +246,13 @@ typedef struct sObjFiber
bool callerIsTrying;
} ObjFiber;
typedef enum
{
// A normal value has been returned.
PRIM_VALUE,
// A fiber is being switched to. Also used for runtime errors (which also
// change which fiber is being executed).
PRIM_FIBER
} PrimitiveResult;
typedef PrimitiveResult (*Primitive)(WrenVM* vm, Value* args);
// The type of a primitive function.
//
// Primitives are similiar to foreign functions, but have more direct access to
// VM internals. It is passed the arguments in [args]. If it returns a value,
// it places it in `args[0]` and returns `true`. If it causes a runtime error
// or modifies the running fiber, it returns `false`.
typedef bool (*Primitive)(WrenVM* vm, Value* args);
// TODO: See if it's actually a perf improvement to have this in a separate
// struct instead of in ObjFn.

View File

@ -922,30 +922,22 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
switch (method->type)
{
case METHOD_PRIMITIVE:
{
// After calling this, the result will be in the first arg slot.
switch (method->fn.primitive(vm, args))
if (method->fn.primitive(vm, args))
{
case PRIM_VALUE:
// The result is now in the first arg slot. Discard the other
// stack slots.
fiber->stackTop -= numArgs - 1;
break;
// The result is now in the first arg slot. Discard the other
// stack slots.
fiber->stackTop -= numArgs - 1;
} else {
// An error or fiber switch occurred.
STORE_FRAME();
case PRIM_FIBER:
STORE_FRAME();
// If we don't have a fiber to switch to, stop interpreting.
if (vm->fiber == NULL) return WREN_RESULT_SUCCESS;
if (!IS_NULL(vm->fiber->error)) RUNTIME_ERROR();
fiber = vm->fiber;
LOAD_FRAME();
break;
// If we don't have a fiber to switch to, stop interpreting.
fiber = vm->fiber;
if (fiber == NULL) return WREN_RESULT_SUCCESS;
if (!IS_NULL(fiber->error)) RUNTIME_ERROR();
LOAD_FRAME();
}
break;
}
case METHOD_FOREIGN:
callForeign(vm, fiber, method->fn.foreign, numArgs);