mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-05 20:26:53 +08:00
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).
This commit is contained in:
parent
590e76b19f
commit
87fe3b01d6
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
121
src/pk_public.c
121
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 { \
|
||||
|
@ -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
|
||||
|
40
src/pk_vm.c
40
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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user