From 87fe3b01d64a541b7108db82c3eea79aa0216998 Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Wed, 27 Apr 2022 08:46:00 +0530 Subject: [PATCH] runFunction() abstraction to hide fiber creation the function will create a fiber and execute the function, this is a better abstracion for the host applicaion (createFiber, runFiber are removed) and this will come in handy when it comes to execute pocket functions inside a native function (it's required to implement operator overloading). --- cli/repl.c | 7 +-- docs/wasm/main.c | 4 +- src/include/pocketlang.h | 21 +++---- src/pk_core.c | 15 ++--- src/pk_public.c | 121 +++++++++++++++++---------------------- src/pk_value.h | 6 +- src/pk_vm.c | 40 +++++++++---- src/pk_vm.h | 7 ++- 8 files changed, 107 insertions(+), 114 deletions(-) diff --git a/cli/repl.c b/cli/repl.c index 1ba8ad6..161a505 100644 --- a/cli/repl.c +++ b/cli/repl.c @@ -134,12 +134,9 @@ int repl(PKVM* vm, const PkCompileOptions* options) { // Compiled source would be the "main" function of the module. Run it. PkHandle* _main = pkModuleGetMainFunction(vm, module); - PkHandle* fiber = pkNewFiber(vm, _main); - ASSERT((_main != NULL) && (fiber != NULL), OOPS); - - result = pkRunFiber(vm, fiber, 0, NULL); + ASSERT(_main != NULL, OOPS); + result = pkRunFunction(vm, _main, 0, -1, -1); pkReleaseHandle(vm, _main); - pkReleaseHandle(vm, fiber); } while (!done); diff --git a/docs/wasm/main.c b/docs/wasm/main.c index f91dc75..79358c0 100644 --- a/docs/wasm/main.c +++ b/docs/wasm/main.c @@ -51,10 +51,8 @@ int runSource(const char* source) { if (result != PK_RESULT_SUCCESS) break; PkHandle* _main = pkModuleGetMainFunction(vm, module); - PkHandle* fiber = pkNewFiber(vm, _main); - result = pkRunFiber(vm, fiber, 0, NULL); + result = pkRunFunction(vm, _main, 0, -1, -1); pkReleaseHandle(vm, _main); - pkReleaseHandle(vm, fiber); } while (false); pkReleaseHandle(vm, module); diff --git a/src/include/pocketlang.h b/src/include/pocketlang.h index d102584..08b4ff9 100644 --- a/src/include/pocketlang.h +++ b/src/include/pocketlang.h @@ -293,20 +293,13 @@ PK_PUBLIC PkResult pkInterpretSource(PKVM* vm, PkStringPtr path, const PkCompileOptions* options); -// FIXME: -// Remove pkNewFiber and pkRunFiber functions and add interface to run closure -// with pkRunClosure or pkRunFunction. - -// Create and return a new fiber around the function [fn]. -PK_PUBLIC PkHandle* pkNewFiber(PKVM* vm, PkHandle* fn); - -// Runs the fiber's function with the provided arguments (param [arc] is the -// argument count and [argv] are the values). It'll returns it's run status -// result (success or failure) if you need the yielded or returned value use -// the pkFiberGetReturnValue() function, and use pkFiberIsDone() function to -// check if the fiber can be resumed with pkFiberResume() function. -PK_PUBLIC PkResult pkRunFiber(PKVM* vm, PkHandle* fiber, - int argc, PkHandle** argv); +// Run a function with [argc] arguments and their values are stored at the VM's +// slots starting at index [argv_slot] till the next [argc] consequent slots. +// If [argc] is 0 [argv_slot] value will be ignored. +// To store the return value set [ret_slot] to a valid slot index, if it's +// negative the return value will be ignored. +PK_PUBLIC PkResult pkRunFunction(PKVM* vm, PkHandle* fn, + int argc, int argv_slot, int ret_slot); /*****************************************************************************/ /* NATIVE / RUNTIME FUNCTION API */ diff --git a/src/pk_core.c b/src/pk_core.c index 0e7449d..2c2e8f6 100644 --- a/src/pk_core.c +++ b/src/pk_core.c @@ -734,15 +734,12 @@ DEF(_fiberRun, ASSERT(IS_OBJ_TYPE(SELF, OBJ_FIBER), OOPS); Fiber* self = (Fiber*)AS_OBJ(SELF); - // Buffer of argument to call vmPrepareFiber(). - Var* args[MAX_ARGC]; - - for (int i = 0; i < ARGC; i++) { - args[i] = &ARG(i + 1); - } - - // Switch fiber and start execution. - if (vmPrepareFiber(vm, self, ARGC, args)) { + // 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; } } diff --git a/src/pk_public.c b/src/pk_public.c index 111c61c..8692992 100644 --- a/src/pk_public.c +++ b/src/pk_public.c @@ -22,6 +22,38 @@ "Given handle is not of type " #type "."); \ } while (false) +#define VALIDATE_SLOT_INDEX(index) \ + do { \ + ASSERT(index >= 0, "Slot index was negative."); \ + ASSERT(index < pkGetSlotsCount(vm), \ + "Slot index is too large. Did you forget to call pkReserveSlots()?."); \ + } while (false) + +// ARGC won't be the real arity if any slots allocated before calling argument +// validation calling this first is the callers responsibility. +#define VALIDATE_ARGC(arg) \ + ASSERT(arg > 0 && arg <= ARGC, "Invalid argument index.") + +#define CHECK_RUNTIME() \ + do { \ + ASSERT(vm->fiber != NULL, \ + "This function can only be called at runtime."); \ + } while (false) + +// A convenient macro to get the nth (1 based) argument of the current +// function. +#define ARG(n) (vm->fiber->ret[n]) + +// Nth slot is same as Nth argument, It'll also work if we allocate more +// slots but the caller should ensure the index. +#define SLOT(n) ARG(n) + +// This will work. +#define SET_SLOT(n, val) SLOT(n) = (val); + +// Evaluates to the current function's argument count. +#define ARGC ((int)(vm->fiber->sp - vm->fiber->ret) - 1) + // The default allocator that will be used to initialize the PKVM's // configuration if the host doesn't provided any allocators for us. static void* defaultRealloc(void* memory, size_t new_size, void* _); @@ -257,77 +289,42 @@ PkResult pkInterpretSource(PKVM* vm, PkStringPtr source, PkStringPtr path, // inclusion cause a crash. module->initialized = true; - return vmRunFiber(vm, newFiber(vm, module->body)); + Fiber* fiber = newFiber(vm, module->body); + vmPushTempRef(vm, &fiber->_super); // fiber. + vmPrepareFiber(vm, fiber, 0, NULL /* TODO: get argv from user. */); + vmPopTempRef(vm); // fiber. + + return vmRunFiber(vm, fiber); } -PkHandle* pkNewFiber(PKVM* vm, PkHandle* fn) { +PK_PUBLIC PkResult pkRunFunction(PKVM* vm, PkHandle* fn, + int argc, int argv_slot, int ret_slot) { CHECK_HANDLE_TYPE(fn, OBJ_CLOSURE); + Closure* closure = (Closure*)AS_OBJ(fn->value); - Fiber* fiber = newFiber(vm, (Closure*)AS_OBJ(fn->value)); - vmPushTempRef(vm, &fiber->_super); // fiber - PkHandle* handle = vmNewHandle(vm, VAR_OBJ(fiber)); - vmPopTempRef(vm); // fiber - return handle; -} + ASSERT(argc >= 0, "argc cannot be negative."); + Var* argv = NULL; -PkResult pkRunFiber(PKVM* vm, PkHandle* fiber, - int argc, PkHandle** argv) { - __ASSERT(fiber != NULL, "Handle fiber was NULL."); - Var fb = fiber->value; - __ASSERT(IS_OBJ_TYPE(fb, OBJ_FIBER), "Given handle is not a fiber."); - Fiber* _fiber = (Fiber*)AS_OBJ(fb); - - Var* args[MAX_ARGC]; - for (int i = 0; i < argc; i++) { - args[i] = &(argv[i]->value); + if (argc != 0) { + for (int i = 0; i < argc; i++) { + VALIDATE_SLOT_INDEX(argv_slot + i); + } + argv = &SLOT(argv_slot); } - if (!vmPrepareFiber(vm, _fiber, argc, args)) { - return PK_RESULT_RUNTIME_ERROR; + Var* ret = NULL; + if (ret_slot >= 0) { + VALIDATE_SLOT_INDEX(ret_slot); + ret = &SLOT(ret_slot); } - ASSERT(_fiber->frame_count == 1, OOPS); - return vmRunFiber(vm, _fiber); -} - -// TODO: Get resume argument. -PkResult pkResumeFiber(PKVM* vm, PkHandle* fiber) { - __ASSERT(fiber != NULL, "Handle fiber was NULL."); - Var fb = fiber->value; - __ASSERT(IS_OBJ_TYPE(fb, OBJ_FIBER), "Given handle is not a fiber."); - Fiber* _fiber = (Fiber*)AS_OBJ(fb); - - if (!vmSwitchFiber(vm, _fiber, NULL /* TODO: argument */)) { - return PK_RESULT_RUNTIME_ERROR; - } - - return vmRunFiber(vm, _fiber); + return vmRunFunction(vm, closure, argc, argv, ret); } /*****************************************************************************/ /* RUNTIME */ /*****************************************************************************/ -#define CHECK_RUNTIME() \ - do { \ - ASSERT(vm->fiber != NULL, \ - "This function can only be called at runtime."); \ - } while (false) - -// A convenient macro to get the nth (1 based) argument of the current -// function. -#define ARG(n) (vm->fiber->ret[n]) - -// Nth slot is same as Nth argument, It'll also work if we allocate more -// slots but the caller should ensure the index. -#define SLOT(n) ARG(n) - -// This will work. -#define SET_SLOT(n, val) SLOT(n) = (val); - -// Evaluates to the current function's argument count. -#define ARGC ((int)(vm->fiber->sp - vm->fiber->ret) - 1) - void pkSetRuntimeError(PKVM* vm, const char* message) { CHECK_RUNTIME(); VM_SET_ERROR(vm, newString(vm, message)); @@ -365,18 +362,6 @@ bool pkCheckArgcRange(PKVM* vm, int argc, int min, int max) { return true; } -#define VALIDATE_SLOT_INDEX(index) \ - do { \ - ASSERT(index >= 0, "Slot index was negative."); \ - ASSERT(index < pkGetSlotsCount(vm), \ - "Slot index is too large. Did you forget to call pkReserveSlots()?."); \ - } while (false) - -// ARGC won't be the real arity if any slots allocated before calling argument -// validation calling this first is the callers responsibility. -#define VALIDATE_ARGC(arg) \ - ASSERT(arg > 0 && arg <= ARGC, "Invalid argument index.") - // Set error for incompatible type provided as an argument. (TODO: got type). #define ERR_INVALID_ARG_TYPE(ty_name) \ do { \ diff --git a/src/pk_value.h b/src/pk_value.h index 3cc4c31..507fee5 100644 --- a/src/pk_value.h +++ b/src/pk_value.h @@ -466,9 +466,9 @@ struct Fiber { Upvalue* open_upvalues; // The stack base pointer of the current frame. It'll be updated before - // calling a native function. (`fiber->ret` === `curr_call_frame->rbp`). And - // also updated if the stack is reallocated (that's when it's about to get - // overflowed. + // calling a native function. (`fiber->ret` === `curr_call_frame->rbp`). + // Return value of the callee fiber will be passed and return value of the + // the function that started the fiber will also be set. Var* ret; // The self pointer to of the current method. It'll be updated before diff --git a/src/pk_vm.c b/src/pk_vm.c index fa50c5f..ffb9212 100644 --- a/src/pk_vm.c +++ b/src/pk_vm.c @@ -147,7 +147,7 @@ void vmCollectGarbage(PKVM* vm) { return false; \ } while (false) -bool vmPrepareFiber(PKVM* vm, Fiber* fiber, int argc, Var** argv) { +bool vmPrepareFiber(PKVM* vm, Fiber* fiber, int argc, Var* argv) { ASSERT(fiber->closure->fn->arity >= -1, OOPS " (Forget to initialize arity.)"); @@ -172,7 +172,7 @@ bool vmPrepareFiber(PKVM* vm, Fiber* fiber, int argc, Var** argv) { } ASSERT(fiber->stack != NULL && fiber->sp == fiber->stack + 1, OOPS); - ASSERT(fiber->ret + 1 == fiber->sp, OOPS); + ASSERT(fiber->ret == fiber->stack, OOPS); // Pass the function arguments. @@ -185,14 +185,10 @@ bool vmPrepareFiber(PKVM* vm, Fiber* fiber, int argc, Var** argv) { // ARG1 is fiber, function arguments are ARG(2), ARG(3), ... ARG(argc). // And ret[0] is the return value, parameters starts at ret[1], ... for (int i = 0; i < argc; i++) { - fiber->ret[1 + i] = *argv[i]; // +1: ret[0] is return value. + fiber->ret[1 + i] = *(argv + i); // +1: ret[0] is return value. } fiber->sp += argc; // Parameters. - // Set the new fiber as the vm's fiber. - fiber->caller = vm->fiber; - vm->fiber = fiber; - // On success return true. return true; } @@ -249,6 +245,26 @@ void vmYieldFiber(PKVM* vm, Var* value) { vm->fiber = caller; } +PkResult vmRunFunction(PKVM* vm, Closure* fn, int argc, Var* argv, Var* ret) { + ASSERT(argc >= 0, "argc cannot be negative."); + ASSERT(argc == 0 || argv != NULL, "argv was NULL when argc > 0."); + + Fiber* fiber = newFiber(vm, fn); + vmPushTempRef(vm, &fiber->_super); // fiber. + vmPrepareFiber(vm, fiber, argc, argv); + vmPopTempRef(vm); // fiber. + + Fiber* last = vm->fiber; + if (last != NULL) vmPushTempRef(vm, &last->_super); // last. + PkResult result = vmRunFiber(vm, fiber); + if (last != NULL) vmPopTempRef(vm); // last. + vm->fiber = last; + + if (ret != NULL) *ret = *fiber->ret; + + return result; +} + /*****************************************************************************/ /* VM INTERNALS */ /*****************************************************************************/ @@ -489,8 +505,10 @@ static void reportError(PKVM* vm) { PkResult vmRunFiber(PKVM* vm, Fiber* fiber_) { - // Set the fiber as the vm's current fiber (another root object) to prevent + // Set the fiber as the VM's current fiber (another root object) to prevent // it from garbage collection and get the reference from native functions. + // If this is being called when running another fiber, that'll be garbage + // collected, if protected with vmPushTempRef() by the caller otherwise. vm->fiber = fiber_; ASSERT(fiber_->state == FIBER_NEW || fiber_->state == FIBER_YIELDED, OOPS); @@ -1203,12 +1221,12 @@ L_do_call: // value on the stack. //fiber->sp = fiber->stack; ?? - FIBER_SWITCH_BACK(); - - if (fiber == NULL) { + if (fiber->caller == NULL) { + *fiber->ret = ret_value; return PK_RESULT_SUCCESS; } else { + FIBER_SWITCH_BACK(); *fiber->ret = ret_value; } diff --git a/src/pk_vm.h b/src/pk_vm.h index 4ac1330..960f6b0 100644 --- a/src/pk_vm.h +++ b/src/pk_vm.h @@ -204,7 +204,7 @@ Module* vmGetModule(PKVM* vm, String* key); // Prepare a new fiber for execution with the given arguments. That can be used // different fiber_run apis. Return true on success, otherwise it'll set the // error to the vm's current fiber (if it has any). -bool vmPrepareFiber(PKVM* vm, Fiber* fiber, int argc, Var** argv); +bool vmPrepareFiber(PKVM* vm, Fiber* fiber, int argc, Var* argv); // ((Context switching - resume)) // Switch the running fiber of the vm from the current fiber to the provided @@ -221,4 +221,9 @@ void vmYieldFiber(PKVM* vm, Var* value); // till the next yield or return statement, and return result. PkResult vmRunFiber(PKVM* vm, Fiber* fiber); +// Runs the script function (if not an assertion will fail) and if the [ret] is +// not null the return value will be set. [argv] should be the first argument +// pointer following the rest of the arguments in an array. +PkResult vmRunFunction(PKVM* vm, Closure* fn, int argc, Var* argv, Var* ret); + #endif // PK_VM_H