Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Enhancement] add experimental error handling: Fiber.try() and built-in raise() #269

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions scripts/run_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@
"lang/fibers.pk",
"lang/functions.pk",
"lang/import.pk",
"lang/try.pk",
),

"Modules Test" : (
"modules/dummy.pk",
"modules/math.pk",
Expand Down Expand Up @@ -65,7 +66,7 @@ def main():
def run_all_tests():
## get the interpreter.
pocket = get_pocket_binary()

for suite in TEST_SUITE:
print_title(suite)
for test in TEST_SUITE[suite]:
Expand Down Expand Up @@ -142,7 +143,7 @@ def print_title(title):
print("----------------------------------")
print(" %s " % title)
print("----------------------------------")

if __name__ == '__main__':
## This will enable ANSI codes in windows terminal.
os.system('')
Expand Down
59 changes: 53 additions & 6 deletions src/core/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -200,15 +200,21 @@ String* varToString(PKVM* vm, Var self, bool repr) {
if (has_method) {
Var ret = VAR_NULL;
PkResult result = vmCallMethod(vm, self, closure, 0, NULL, &ret);
if (result != PK_RESULT_SUCCESS) return NULL;

if (!IS_OBJ_TYPE(ret, OBJ_STRING)) {
VM_SET_ERROR(vm, newString(vm, "method " LITS__str " returned "
"non-string type."));
return NULL;
if (!VM_HAS_ERROR(vm)) {
if (result != PK_RESULT_SUCCESS) return NULL;
if (!IS_OBJ_TYPE(ret, OBJ_STRING)) {
VM_SET_ERROR(vm, newString(vm, "method " LITS__str " returned "
"non-string type."));
return NULL;
}
return (String*)AS_OBJ(ret);
}

return (String*)AS_OBJ(ret);
// already has error -> dealing with error message
if (result == PK_RESULT_SUCCESS && IS_OBJ_TYPE(ret, OBJ_STRING)) {
return (String*)AS_OBJ(ret);
}
}

// If we reached here, it doesn't have a to string override. just
Expand Down Expand Up @@ -437,6 +443,22 @@ DEF(coreAssert,
}
}

DEF(coreRaise,
"raise([message:String]) -> Null",
"Raise a runtime error.") {

int argc = ARGC;
if (argc > 1) { // raise() or raise(message).
RET_ERR(newString(vm, "Invalid argument count."));
}

if (argc == 1 && ARG(1) != VAR_NULL) {
vm->fiber->error = ARG(1);
return;
}
VM_SET_ERROR(vm, newString(vm, "No exception to reraise."));
}

DEF(coreBin,
"bin(value:Number) -> String",
"Returns as a binary value string with '0b' prefix.") {
Expand Down Expand Up @@ -698,6 +720,7 @@ static void initializeBuiltinFunctions(PKVM* vm) {
INITIALIZE_BUILTIN_FN("help", coreHelp, -1);
INITIALIZE_BUILTIN_FN("dir", coreDir, 1);
INITIALIZE_BUILTIN_FN("assert", coreAssert, -1);
INITIALIZE_BUILTIN_FN("raise", coreRaise, -1);
INITIALIZE_BUILTIN_FN("bin", coreBin, 1);
INITIALIZE_BUILTIN_FN("hex", coreHex, 1);
INITIALIZE_BUILTIN_FN("yield", coreYield, -1);
Expand Down Expand Up @@ -1377,6 +1400,26 @@ DEF(_fiberRun,
self->caller = vm->fiber;
vm->fiber = self;
self->state = FIBER_RUNNING;
self->trying = false;
}
}

DEF(_fiberTry,
"Fiber.try(...) -> Var",
"The same as Fiber.run() but store the error message in 'error' attrib "
"instead of exiting.") {

ASSERT(IS_OBJ_TYPE(SELF, OBJ_FIBER), OOPS);
Fiber* self = (Fiber*) AS_OBJ(SELF);

// Switch fiber and start execution. New fibers are marked as running in
// either it's stats running with vmRunFiber() or here -- inserting a
// fiber over a running (callee) fiber.
if (vmPrepareFiber(vm, self, ARGC, &ARG(1))) {
self->caller = vm->fiber;
vm->fiber = self;
self->state = FIBER_RUNNING;
self->trying = true;
}
}

Expand Down Expand Up @@ -1484,6 +1527,7 @@ static void initializePrimitiveClasses(PKVM* vm) {
ADD_METHOD(PK_MODULE, "globals", _moduleGlobals, 0);

ADD_METHOD(PK_FIBER, "run", _fiberRun, -1);
ADD_METHOD(PK_FIBER, "try", _fiberTry, -1);
ADD_METHOD(PK_FIBER, "resume", _fiberResume, -1);

#undef ADD_METHOD
Expand Down Expand Up @@ -2087,6 +2131,9 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {

case CHECK_HASH("function", 0x9ed64249):
return VAR_OBJ(fb->closure);

case CHECK_HASH("error", 0x21918751):
return fb->error;
}
} break;

Expand Down
18 changes: 14 additions & 4 deletions src/core/debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,15 +214,25 @@ static void _reportStackFrame(PKVM* vm, CallFrame* frame) {
}
}

void reportRuntimeError(PKVM* vm, Fiber* fiber) {
void reportRuntimeError(PKVM* vm, Fiber* fiber, bool* is_first) {

pkWriteFn writefn = vm->config.stderr_write;
if (writefn == NULL) return;

// Error message.
_printRed(vm, "Error: ");
writefn(vm, fiber->error->data);
writefn(vm, "\n");
if (*is_first) {
_printRed(vm, "Error: ");
String* msg = NULL;
if (IS_OBJ_TYPE(fiber->error, OBJ_STRING)) {
msg = (String*)AS_OBJ(fiber->error);
} else {
msg = varToString(vm, fiber->error, false);
}

writefn(vm, msg->data);
writefn(vm, "\n");
*is_first = false;
}

// If the stack frames are greater than 2 * max_dump_frames + 1,
// we're only print the first [max_dump_frames] and last [max_dump_frames]
Expand Down
2 changes: 1 addition & 1 deletion src/core/debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ void reportCompileTimeError(PKVM* vm, const char* path, int line,
const char* fmt, va_list args);

// Pretty print runtime error.
void reportRuntimeError(PKVM* vm, Fiber* fiber);
void reportRuntimeError(PKVM* vm, Fiber* fiber, bool* first);

// Dump opcodes of the given function to the stdout.
void dumpFunctionCode(PKVM* vm, Function* func);
Expand Down
27 changes: 26 additions & 1 deletion src/core/public.c
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,11 @@ PkResult pkRunREPL(PKVM* vm) {

void pkSetRuntimeError(PKVM* vm, const char* message) {
CHECK_FIBER_EXISTS(vm);
VM_SET_ERROR(vm, newString(vm, message));
if (message != NULL) {
VM_SET_ERROR(vm, newString(vm, message));
} else {
VM_RESET_ERROR(vm);
}
}

void pkSetRuntimeErrorFmt(PKVM* vm, const char* fmt, ...) {
Expand All @@ -578,6 +582,18 @@ void pkSetRuntimeErrorFmt(PKVM* vm, const char* fmt, ...) {
va_end(args);
}

void pkSetRuntimeErrorObj(PKVM* vm, int slot) {
CHECK_FIBER_EXISTS(vm);
VALIDATE_SLOT_INDEX(slot);
vm->fiber->error = ARG(slot);
}

void pkGetRuntimeError(PKVM* vm, int slot) {
CHECK_FIBER_EXISTS(vm);
VALIDATE_SLOT_INDEX(slot);
SET_SLOT(slot, vm->fiber->error);
}

void* pkGetSelf(const PKVM* vm) {
CHECK_FIBER_EXISTS(vm);
ASSERT(IS_OBJ_TYPE(vm->fiber->self, OBJ_INST), OOPS);
Expand Down Expand Up @@ -697,6 +713,7 @@ bool pkValidateSlotInstanceOf(PKVM* vm, int slot, int cls) {
CHECK_FIBER_EXISTS(vm);
VALIDATE_SLOT_INDEX(slot);
VALIDATE_SLOT_INDEX(cls);
VM_RESET_ERROR(vm);

Var instance = ARG(slot), class_ = SLOT(cls);
if (!varIsType(vm, instance, class_)) {
Expand All @@ -710,9 +727,11 @@ bool pkValidateSlotInstanceOf(PKVM* vm, int slot, int cls) {
}

bool pkIsSlotInstanceOf(PKVM* vm, int inst, int cls, bool* val) {
CHECK_FIBER_EXISTS(vm);
CHECK_ARG_NULL(val);
VALIDATE_SLOT_INDEX(inst);
VALIDATE_SLOT_INDEX(cls);
VM_RESET_ERROR(vm);

*val = varIsType(vm, inst, cls);
return !VM_HAS_ERROR(vm);
Expand Down Expand Up @@ -839,6 +858,7 @@ bool pkSetAttribute(PKVM* vm, int instance, const char* name, int value) {
CHECK_ARG_NULL(name);
VALIDATE_SLOT_INDEX(instance);
VALIDATE_SLOT_INDEX(value);
VM_RESET_ERROR(vm);

String* sname = newString(vm, name);
vmPushTempRef(vm, &sname->_super); // sname.
Expand All @@ -854,6 +874,7 @@ bool pkGetAttribute(PKVM* vm, int instance, const char* name,
CHECK_ARG_NULL(name);
VALIDATE_SLOT_INDEX(instance);
VALIDATE_SLOT_INDEX(index);
VM_RESET_ERROR(vm);

String* sname = newString(vm, name);
vmPushTempRef(vm, &sname->_super); // sname.
Expand Down Expand Up @@ -885,6 +906,7 @@ static Var _newInstance(PKVM* vm, Class* cls, int argc, Var* argv) {
bool pkNewInstance(PKVM* vm, int cls, int index, int argc, int argv) {
CHECK_FIBER_EXISTS(vm);
VALIDATE_SLOT_INDEX(index);
VM_RESET_ERROR(vm);

if (argc != 0) {
VALIDATE_SLOT_INDEX(argv);
Expand Down Expand Up @@ -969,6 +991,7 @@ uint32_t pkListLength(PKVM* vm, int list) {

bool pkCallFunction(PKVM* vm, int fn, int argc, int argv, int ret) {
CHECK_FIBER_EXISTS(vm);
VM_RESET_ERROR(vm);
ASSERT(IS_OBJ_TYPE(SLOT(fn), OBJ_CLOSURE), "Slot value wasn't a function");
if (argc != 0) {
VALIDATE_SLOT_INDEX(argv);
Expand Down Expand Up @@ -1009,6 +1032,7 @@ bool pkCallMethod(PKVM* vm, int instance, const char* method,
CHECK_FIBER_EXISTS(vm);
CHECK_ARG_NULL(method);
VALIDATE_SLOT_INDEX(instance);
VM_RESET_ERROR(vm);
if (argc != 0) {
VALIDATE_SLOT_INDEX(argv);
VALIDATE_SLOT_INDEX(argv + argc - 1);
Expand Down Expand Up @@ -1054,6 +1078,7 @@ void pkPlaceSelf(PKVM* vm, int index) {
bool pkImportModule(PKVM* vm, const char* path, int index) {
CHECK_FIBER_EXISTS(vm);
VALIDATE_SLOT_INDEX(index);
VM_RESET_ERROR(vm);

String* path_ = newString(vm, path);
vmPushTempRef(vm, &path_->_super); // path_
Expand Down
5 changes: 3 additions & 2 deletions src/core/value.c
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,8 @@ static void popMarkedObjectsInternal(Object* obj, PKVM* vm) {

markObject(vm, &fiber->caller->_super);
markObject(vm, &fiber->native->_super);
markObject(vm, &fiber->error->_super);

markValue(vm, fiber->error);
markValue(vm, fiber->self);

} break;
Expand Down Expand Up @@ -495,6 +495,7 @@ Fiber* newFiber(PKVM* vm, Closure* closure) {

fiber->open_upvalues = NULL;
fiber->self = VAR_UNDEFINED;
fiber->error = VAR_NULL;

// Initialize the return value to null (doesn't really have to do that here
// but if we're trying to debut it may crash when dumping the return value).
Expand Down Expand Up @@ -1127,7 +1128,7 @@ Var mapRemoveKey(PKVM* vm, Map* self, Var key) {
}

bool fiberHasError(Fiber* fiber) {
return fiber->error != NULL;
return fiber->error != VAR_NULL;
}

void freeObject(PKVM* vm, Object* self) {
Expand Down
4 changes: 3 additions & 1 deletion src/core/value.h
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,8 @@ typedef enum {
struct Fiber {
Object _super;

bool trying;

FiberState state;

// The root closure of the fiber.
Expand Down Expand Up @@ -514,7 +516,7 @@ struct Fiber {
Fiber *caller, *native;

// Runtime error initially NULL, heap allocated.
String* error;
Var error;
};

struct Class {
Expand Down
Loading