mirror of
https://github.com/CPunch/Cosmo.git
synced 2024-11-05 08:10:05 +00:00
Minor VM refactor, added cosmoV_pcall()
This commit is contained in:
parent
4e968529dc
commit
752bd533e9
183
src/cvm.c
183
src/cvm.c
@ -138,19 +138,29 @@ void cosmoV_concat(CState *state, int vals) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int cosmoV_execute(CState *state);
|
int cosmoV_execute(CState *state);
|
||||||
|
bool invokeMethod(CState* state, CObjObject *obj, CValue func, int args, int nresults, int offset);
|
||||||
|
|
||||||
static inline bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nresults, int offset) {
|
/*
|
||||||
|
calls a native C Function with # args on the stack, nresults are pushed onto the stack upon return.
|
||||||
|
|
||||||
|
returns:
|
||||||
|
false: state paniced during C Function, stack is preserved so that the error is left on the stack
|
||||||
|
true: state->top is moved to base + offset + nresults, with nresults pushed onto the stack from base + offset
|
||||||
|
*/
|
||||||
|
static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nresults, int offset) {
|
||||||
StkPtr savedBase = cosmoV_getTop(state, args);
|
StkPtr savedBase = cosmoV_getTop(state, args);
|
||||||
|
|
||||||
cosmoM_freezeGC(state); // we don't want a GC event during c api because we don't actually trust the user to know how to evade the GC
|
// we don't want a GC event during c api because we don't actually trust the user to know how to evade the GC
|
||||||
|
cosmoM_freezeGC(state);
|
||||||
int nres = cfunc(state, args, savedBase + 1);
|
int nres = cfunc(state, args, savedBase + 1);
|
||||||
cosmoM_unfreezeGC(state);
|
cosmoM_unfreezeGC(state);
|
||||||
|
|
||||||
// if the state paniced during the c function, return false and leave the stack preserved (so the error string is left)
|
// if the state paniced during the c function, return false and leave the stack preserved (so the error is left on the stack)
|
||||||
if (state->panic)
|
if (state->panic)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (nres > nresults) // caller function wasn't expecting this many return values, cap it
|
// caller function wasn't expecting this many return values, cap it
|
||||||
|
if (nres > nresults)
|
||||||
nres = nresults;
|
nres = nresults;
|
||||||
|
|
||||||
// remember where the return values are
|
// remember where the return values are
|
||||||
@ -169,7 +179,14 @@ static inline bool callCFunction(CState *state, CosmoCFunction cfunc, int args,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool call(CState *state, CObjClosure *closure, int args, int nresults, int offset) {
|
/*
|
||||||
|
calls a raw closure object with # args on the stack, nresults are pushed onto the stack upon return.
|
||||||
|
|
||||||
|
returns:
|
||||||
|
false: state paniced, stack is preserved so that the error is left on the stack
|
||||||
|
true: stack->top is moved to base + offset + nresults, with nresults pushed onto the stack from base + offset
|
||||||
|
*/
|
||||||
|
static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults, int offset) {
|
||||||
CObjFunction *func = closure->function;
|
CObjFunction *func = closure->function;
|
||||||
|
|
||||||
// if the function is variadic and theres more args than parameters, push the args into a dictionary
|
// if the function is variadic and theres more args than parameters, push the args into a dictionary
|
||||||
@ -221,77 +238,101 @@ static inline bool call(CState *state, CObjClosure *closure, int args, int nresu
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool callCValue(CState *state, CValue func, int args, int nresults, int offset) {
|
||||||
|
if (GET_TYPE(func) != COSMO_TOBJ) {
|
||||||
|
cosmoV_error(state, "Cannot call non-function type %s!", cosmoV_typeStr(func));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cosmoV_readObj(func)->type) {
|
||||||
|
case COBJ_CLOSURE:
|
||||||
|
return rawCall(state, cosmoV_readClosure(func), args, nresults, offset);
|
||||||
|
case COBJ_METHOD: {
|
||||||
|
CObjMethod *method = (CObjMethod*)cosmoV_readObj(func);
|
||||||
|
return invokeMethod(state, method->obj, method->func, args, nresults, offset + 1);
|
||||||
|
}
|
||||||
|
case COBJ_OBJECT: { // object is being instantiated, making another object
|
||||||
|
CObjObject *protoObj = (CObjObject*)cosmoV_readObj(func);
|
||||||
|
CObjObject *newObj = cosmoO_newObject(state);
|
||||||
|
newObj->proto = protoObj;
|
||||||
|
CValue ret;
|
||||||
|
|
||||||
|
// check if they defined an initializer (we accept 0 return values)
|
||||||
|
if (cosmoO_getIString(state, protoObj, ISTRING_INIT, &ret)) {
|
||||||
|
if (!invokeMethod(state, newObj, ret, args, 0, offset + 1))
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// no default initializer
|
||||||
|
cosmoV_error(state, "Expected __init() in proto, object cannot be instantiated!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nresults > 0) {
|
||||||
|
cosmoV_pushValue(state, cosmoV_newObj(newObj));
|
||||||
|
|
||||||
|
// push the nils to fill up the expected return values
|
||||||
|
for (int i = 0; i < nresults - 1; i++) { // -1 since the we already pushed the important value
|
||||||
|
cosmoV_pushValue(state, cosmoV_newNil());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case COBJ_CFUNCTION:
|
||||||
|
return callCFunction(state, cosmoV_readCFunction(func), args, nresults, offset);
|
||||||
|
default:
|
||||||
|
cosmoV_error(state, "Cannot call non-function value %s!", cosmoV_typeStr(func));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool invokeMethod(CState* state, CObjObject *obj, CValue func, int args, int nresults, int offset) {
|
bool invokeMethod(CState* state, CObjObject *obj, CValue func, int args, int nresults, int offset) {
|
||||||
// first, set the first argument to the object
|
// first, set the first argument to the object
|
||||||
StkPtr temp = cosmoV_getTop(state, args);
|
StkPtr temp = cosmoV_getTop(state, args);
|
||||||
*temp = cosmoV_newObj(obj);
|
*temp = cosmoV_newObj(obj);
|
||||||
|
|
||||||
if (IS_CFUNCTION(func)) {
|
return callCValue(state, func, args+1, nresults, offset);
|
||||||
return callCFunction(state, cosmoV_readCFunction(func), args+1, nresults, offset);
|
|
||||||
} else if (IS_CLOSURE(func)) {
|
|
||||||
return call(state, cosmoV_readClosure(func), args+1, nresults, offset); // offset = 1 so our stack is properly reset
|
|
||||||
} else {
|
|
||||||
cosmoV_error(state, "Cannot invoke non-function type %s!", cosmoV_typeStr(func));
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// args = # of pass parameters
|
// wraps cosmoV_call in a protected state, error string will be pushed onto the stack if function call failed, else return values are passed
|
||||||
|
COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults) {
|
||||||
|
StkPtr base = cosmoV_getTop(state, args);
|
||||||
|
|
||||||
|
COSMOVMRESULT res = cosmoV_call(state, args, nresults);
|
||||||
|
|
||||||
|
if (res != COSMOVM_OK) {
|
||||||
|
// restore panic state
|
||||||
|
state->panic = false;
|
||||||
|
|
||||||
|
// error is on the stack, grab it
|
||||||
|
StkPtr err = cosmoV_getTop(state, 0);
|
||||||
|
state->top = base;
|
||||||
|
|
||||||
|
if (nresults > 0) {
|
||||||
|
cosmoV_pushValue(state, *err);
|
||||||
|
|
||||||
|
// push the nils to fill up the expected return values
|
||||||
|
for (int i = 0; i < nresults - 1; i++) { // -1 since the we already pushed the important value
|
||||||
|
cosmoV_pushValue(state, cosmoV_newNil());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
calls a callable object at stack->top - args - 1, passing the # of args to the callable, and ensuring nresults are returned
|
||||||
|
|
||||||
|
returns:
|
||||||
|
COSMOVM_OK: callable object exited normally
|
||||||
|
COSMOVM_RUNTIME_ERR: an error occurred, the error will be at the top of the stack (the rest of the stack is junk)
|
||||||
|
*/
|
||||||
COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) {
|
COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) {
|
||||||
StkPtr val = cosmoV_getTop(state, args); // function will always be right above the args
|
StkPtr val = cosmoV_getTop(state, args); // function will always be right above the args
|
||||||
|
|
||||||
if (GET_TYPE(*val) != COSMO_TOBJ) {
|
return callCValue(state, *val, args, nresults, 0) ? COSMOVM_OK : COSMOVM_RUNTIME_ERR;
|
||||||
cosmoV_error(state, "Cannot call non-function type %s!", cosmoV_typeStr(*val));
|
|
||||||
return COSMOVM_RUNTIME_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (cosmoV_readObj(*val)->type) {
|
|
||||||
case COBJ_CLOSURE: {
|
|
||||||
CObjClosure *closure = (CObjClosure*)cosmoV_readObj(*val);
|
|
||||||
if (!call(state, closure, args, nresults, 0))
|
|
||||||
return COSMOVM_RUNTIME_ERR;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case COBJ_METHOD: {
|
|
||||||
CObjMethod *method = (CObjMethod*)cosmoV_readObj(*val);
|
|
||||||
invokeMethod(state, method->obj, method->func, args, nresults, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case COBJ_OBJECT: { // object is being instantiated, making another object
|
|
||||||
CObjObject *protoObj = (CObjObject*)cosmoV_readObj(*val);
|
|
||||||
CObjObject *newObj = cosmoO_newObject(state);
|
|
||||||
newObj->proto = protoObj;
|
|
||||||
CValue ret;
|
|
||||||
|
|
||||||
// check if they defined an initializer
|
|
||||||
if (cosmoO_getIString(state, protoObj, ISTRING_INIT, &ret)) {
|
|
||||||
invokeMethod(state, newObj, ret, args, nresults, 1);
|
|
||||||
} else {
|
|
||||||
// no default initializer
|
|
||||||
cosmoV_error(state, "Expected __init() in proto, object cannot be instantiated!");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
cosmoV_pop(state); // pops the return value, it's unused
|
|
||||||
cosmoV_pushValue(state, cosmoV_newObj(newObj));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case COBJ_CFUNCTION: {
|
|
||||||
// it's a C function, so call it
|
|
||||||
CosmoCFunction cfunc = ((CObjCFunction*)cosmoV_readObj(*val))->cfunc;
|
|
||||||
if (!callCFunction(state, cfunc, args, nresults, 0))
|
|
||||||
return COSMOVM_RUNTIME_ERR;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
cosmoV_error(state, "Cannot call non-function value %s!", cosmoV_typeStr(*val));
|
|
||||||
return COSMOVM_RUNTIME_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return state->panic ? COSMOVM_RUNTIME_ERR : COSMOVM_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool isFalsey(StkPtr val) {
|
static inline bool isFalsey(StkPtr val) {
|
||||||
@ -688,16 +729,8 @@ int cosmoV_execute(CState *state) {
|
|||||||
cosmoT_get(&dict->tbl, *key, &val);
|
cosmoT_get(&dict->tbl, *key, &val);
|
||||||
|
|
||||||
// call closure/cfunction
|
// call closure/cfunction
|
||||||
if (IS_CFUNCTION(val)) {
|
if (!callCValue(state, val, args, nres, -1))
|
||||||
if (!callCFunction(state, cosmoV_readCFunction(val), args, nres, -1))
|
|
||||||
return -1;
|
return -1;
|
||||||
} else if (IS_CLOSURE(val)) {
|
|
||||||
if (!call(state, cosmoV_readClosure(val), args, nres, -1))
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
cosmoV_error(state, "Cannot call non-function value %s!", cosmoV_typeStr(val));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case COBJ_OBJECT: {
|
case COBJ_OBJECT: {
|
||||||
|
Loading…
Reference in New Issue
Block a user