Compare commits

...

2 Commits

Author SHA1 Message Date
37e42eb60b fixed minor memory leak in cparse
we keep track of internal values used by the parser by pushing them onto the stack
and popping them off once complete.
2023-08-29 15:27:22 -05:00
cd3047c271 WIP: removed stale error handling
currently, scripts seem to run fine. however I'm a bit worried about stack related issues. maybe i'll need to reset state->top as well? but not entirely sure
2023-08-29 14:07:45 -05:00
10 changed files with 92 additions and 131 deletions

View File

@ -1,7 +1,7 @@
# make clean && make && ./bin/cosmo # make clean && make && ./bin/cosmo
CC=clang CC=clang
CFLAGS=-fPIE -Wall -Isrc -O3 -std=c99 CFLAGS=-fPIE -Wall -Isrc -O3 -std=c99 #-g -fsanitize=address
LDFLAGS=-lm #-fsanitize=address LDFLAGS=-lm #-fsanitize=address
OUT=bin/cosmo OUT=bin/cosmo

25
main.c
View File

@ -45,21 +45,20 @@ int cosmoB_input(CState *state, int nargs, CValue *args)
static bool interpret(CState *state, const char *script, const char *mod) static bool interpret(CState *state, const char *script, const char *mod)
{ {
bool ret;
// cosmoV_compileString pushes the result onto the stack (COBJ_ERROR or COBJ_CLOSURE) // cosmoV_compileString pushes the result onto the stack (COBJ_ERROR or COBJ_CLOSURE)
if (cosmoV_compileString(state, script, mod)) { if (cosmoV_compileString(state, script, mod)) {
// 0 args being passed, 0 results expected // 0 args being passed, 0 results expected
if (!cosmoV_call(state, 0, 0)) if (!cosmoV_pcall(state, 0, 0)) {
cosmoV_printError(state, state->error); cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
return false;
}
} else { } else {
cosmoV_pop(state); // pop the error off the stack cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
cosmoV_printError(state, state->error); return false;
} }
ret = state->panic; return true;
state->panic = false; // so our repl isn't broken
return !ret;
} }
static void repl(CState *state) static void repl(CState *state)
@ -158,8 +157,7 @@ void compileScript(CState *state, const char *in, const char *out)
CObjFunction *func = cosmoV_readClosure(*cosmoV_getTop(state, 0))->function; CObjFunction *func = cosmoV_readClosure(*cosmoV_getTop(state, 0))->function;
cosmoD_dump(state, func, fileWriter, (void *)fout); cosmoD_dump(state, func, fileWriter, (void *)fout);
} else { } else {
cosmoV_pop(state); // pop the error off the stack cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
cosmoV_printError(state, state->error);
} }
free(script); free(script);
@ -172,14 +170,13 @@ void loadScript(CState *state, const char *in)
{ {
FILE *file = fopen(in, "rb"); FILE *file = fopen(in, "rb");
if (!cosmoV_undump(state, fileReader, file)) { if (!cosmoV_undump(state, fileReader, file)) {
cosmoV_pop(state); // pop the error off the stack cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
cosmoV_printError(state, state->error);
return; return;
}; };
printf("[!] loaded %s!\n", in); printf("[!] loaded %s!\n", in);
if (!cosmoV_call(state, 0, 0)) if (!cosmoV_pcall(state, 0, 0))
cosmoV_printError(state, state->error); cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
fclose(file); fclose(file);
} }

View File

@ -288,9 +288,6 @@ static void markRoots(CState *state)
// mark the user defined roots // mark the user defined roots
markUserRoots(state); markUserRoots(state);
// mark other misc. internally reserved objects
markObject(state, (CObj *)state->error);
for (int i = 0; i < COBJ_MAX; i++) for (int i = 0; i < COBJ_MAX; i++)
markObject(state, (CObj *)state->protoObjects[i]); markObject(state, (CObj *)state->protoObjects[i]);

View File

@ -164,14 +164,12 @@ _eqFail:
cosmoV_pushValue(state, eq1); cosmoV_pushValue(state, eq1);
cosmoV_pushRef(state, obj1); cosmoV_pushRef(state, obj1);
cosmoV_pushRef(state, obj2); cosmoV_pushRef(state, obj2);
if (!cosmoV_call(state, 2, 1)) cosmoV_call(state, 2, 1);
return false;
// check return value and make sure it's a boolean // check return value and make sure it's a boolean
if (!IS_BOOLEAN(*cosmoV_getTop(state, 0))) { if (!IS_BOOLEAN(*cosmoV_getTop(state, 0))) {
cosmoV_error(state, "__equal expected to return <boolean>, got %s!", cosmoV_error(state, "__equal expected to return <boolean>, got %s!",
cosmoV_typeStr(*cosmoV_pop(state))); cosmoV_typeStr(*cosmoV_pop(state)));
return false;
} }
// return the result // return the result
@ -409,9 +407,8 @@ bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *v
cosmoT_get(state, &cosmoV_readTable(*val)->tbl, key, val)) { cosmoT_get(state, &cosmoV_readTable(*val)->tbl, key, val)) {
cosmoV_pushValue(state, *val); // push function cosmoV_pushValue(state, *val); // push function
cosmoV_pushRef(state, (CObj *)obj); // push object cosmoV_pushRef(state, (CObj *)obj); // push object
if (!cosmoV_call(state, 1, 1)) // call the function with the 1 argument cosmoV_call(state, 1, 1); // call the function with the 1 argument
return false; *val = *cosmoV_pop(state); // set value to the return value of __index
*val = *cosmoV_pop(state); // set value to the return value of __index
return true; return true;
} }
@ -530,9 +527,8 @@ bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *v
cosmoV_pushValue(state, *val); // push function cosmoV_pushValue(state, *val); // push function
cosmoV_pushRef(state, (CObj *)object); // push object cosmoV_pushRef(state, (CObj *)object); // push object
cosmoV_pushValue(state, key); // push key cosmoV_pushValue(state, key); // push key
if (!cosmoV_call(state, 2, 1)) // call the function with the 2 arguments cosmoV_call(state, 2, 1); // call the function with the 2 arguments
return false; *val = *cosmoV_pop(state); // set value to the return value of __index
*val = *cosmoV_pop(state); // set value to the return value of __index
return true; return true;
} else { // there's no __index function defined! } else { // there's no __index function defined!
cosmoV_error(state, "Couldn't index object without __index function!"); cosmoV_error(state, "Couldn't index object without __index function!");
@ -550,7 +546,8 @@ bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue
cosmoV_pushRef(state, (CObj *)object); // push object cosmoV_pushRef(state, (CObj *)object); // push object
cosmoV_pushValue(state, key); // push key & value pair cosmoV_pushValue(state, key); // push key & value pair
cosmoV_pushValue(state, val); cosmoV_pushValue(state, val);
return cosmoV_call(state, 3, 0); cosmoV_call(state, 3, 0);
return true;
} else { // there's no __newindex function defined } else { // there's no __newindex function defined
cosmoV_error(state, "Couldn't set index on object without __newindex function!"); cosmoV_error(state, "Couldn't set index on object without __newindex function!");
} }
@ -567,8 +564,7 @@ CObjString *cosmoO_toString(CState *state, CObj *obj)
if (protoObject != NULL && cosmoO_getIString(state, protoObject, ISTRING_TOSTRING, &res)) { if (protoObject != NULL && cosmoO_getIString(state, protoObject, ISTRING_TOSTRING, &res)) {
cosmoV_pushValue(state, res); cosmoV_pushValue(state, res);
cosmoV_pushRef(state, (CObj *)obj); cosmoV_pushRef(state, (CObj *)obj);
if (!cosmoV_call(state, 1, 1)) cosmoV_call(state, 1, 1);
return cosmoO_copyString(state, "<err>", 5);
// make sure the __tostring function returned a string // make sure the __tostring function returned a string
StkPtr ret = cosmoV_getTop(state, 0); StkPtr ret = cosmoV_getTop(state, 0);
@ -633,14 +629,12 @@ cosmo_Number cosmoO_toNumber(CState *state, CObj *obj)
if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_TONUMBER, &res)) { if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_TONUMBER, &res)) {
cosmoV_pushValue(state, res); cosmoV_pushValue(state, res);
cosmoV_pushRef(state, (CObj *)obj); cosmoV_pushRef(state, (CObj *)obj);
if (!cosmoV_call(state, 1, 1)) // call res, expect 1 return val of <number> cosmoV_call(state, 1, 1); // call res, expect 1 return val of <number>
return 0;
StkPtr temp = cosmoV_getTop(state, 0); StkPtr temp = cosmoV_getTop(state, 0);
if (!IS_NUMBER(*temp)) { if (!IS_NUMBER(*temp)) {
cosmoV_error(state, "__tonumber expected to return <number>, got %s!", cosmoV_error(state, "__tonumber expected to return <number>, got %s!",
cosmoV_typeStr(*temp)); cosmoV_typeStr(*temp));
return 0;
} }
// return number // return number
@ -666,8 +660,7 @@ int cosmoO_count(CState *state, CObj *obj)
if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_COUNT, &res)) { if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_COUNT, &res)) {
cosmoV_pushValue(state, res); cosmoV_pushValue(state, res);
cosmoV_pushRef(state, (CObj *)obj); cosmoV_pushRef(state, (CObj *)obj);
if (!cosmoV_call(state, 1, 1)) // call res, we expect 1 return value of type <number> cosmoV_call(state, 1, 1); // call res, we expect 1 return value of type <number>
return 0;
StkPtr ret = cosmoV_getTop(state, 0); StkPtr ret = cosmoV_getTop(state, 0);
if (!IS_NUMBER(*ret)) { if (!IS_NUMBER(*ret)) {

View File

@ -136,6 +136,7 @@ struct CObjUpval
#define cosmoV_readCFunction(x) (((CObjCFunction *)cosmoV_readRef(x))->cfunc) #define cosmoV_readCFunction(x) (((CObjCFunction *)cosmoV_readRef(x))->cfunc)
#define cosmoV_readMethod(x) ((CObjMethod *)cosmoV_readRef(x)) #define cosmoV_readMethod(x) ((CObjMethod *)cosmoV_readRef(x))
#define cosmoV_readClosure(x) ((CObjClosure *)cosmoV_readRef(x)) #define cosmoV_readClosure(x) ((CObjClosure *)cosmoV_readRef(x))
#define cosmoV_readError(x) ((CObjError *)cosmoV_readRef(x))
#define cosmoO_readCString(x) ((CObjString *)x)->str #define cosmoO_readCString(x) ((CObjString *)x)->str
#define cosmoO_readType(x) ((CObj *)x)->type #define cosmoO_readType(x) ((CObj *)x)->type

View File

@ -65,6 +65,8 @@ typedef struct
CObjString *module; // name of the module CObjString *module; // name of the module
CToken current; CToken current;
CToken previous; // token right after the current token CToken previous; // token right after the current token
int workingStackCount; // we push CValues of objects we need onto the stack so the garbage collector can see them.
// this is the count of those values so we'll know how many to pop off when we're done
} CParseState; } CParseState;
typedef enum typedef enum
@ -107,6 +109,12 @@ static CObjFunction *endCompiler(CParseState *pstate);
// ================================================================ [FRONT END/TALK TO LEXER] // ================================================================ [FRONT END/TALK TO LEXER]
static void keepTrackOf(CParseState *pstate, CValue val)
{
pstate->workingStackCount++;
cosmoV_pushValue(pstate->state, val);
}
static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, FunctionType type, static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, FunctionType type,
CCompilerState *enclosing) CCompilerState *enclosing)
{ {
@ -122,7 +130,7 @@ static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, Func
ccstate->function = cosmoO_newFunction(pstate->state); ccstate->function = cosmoO_newFunction(pstate->state);
ccstate->function->module = pstate->module; ccstate->function->module = pstate->module;
cosmoV_pushRef(pstate->state, (CObj *)ccstate->function); keepTrackOf(pstate, cosmoV_newRef((CObj *)ccstate->function));
ccstate->loop.scope = -1; // there is no loop yet ccstate->loop.scope = -1; // there is no loop yet
@ -151,13 +159,18 @@ static void initParseState(CParseState *pstate, CCompilerState *ccstate, CState
pstate->state = s; pstate->state = s;
pstate->compiler = ccstate; pstate->compiler = ccstate;
pstate->module = cosmoO_copyString(s, module, strlen(module)); pstate->module = cosmoO_copyString(s, module, strlen(module));
pstate->workingStackCount = 0;
keepTrackOf(pstate, cosmoV_newRef((CObj *)pstate->module));
initCompilerState(pstate, ccstate, FTYPE_SCRIPT, NULL); // enclosing starts as NULL initCompilerState(pstate, ccstate, FTYPE_SCRIPT, NULL); // enclosing starts as NULL
} }
static void freeParseState(CParseState *pstate) static void freeParseState(CParseState *pstate)
{ {
cosmoL_cleanupLexState(pstate->state, &pstate->lex); cosmoL_cleanupLexState(pstate->state, &pstate->lex);
// pop our working values off the stack
cosmoV_setTop(pstate->state, pstate->workingStackCount);
} }
static void errorAt(CParseState *pstate, CToken *token, const char *format, va_list args) static void errorAt(CParseState *pstate, CToken *token, const char *format, va_list args)

View File

@ -12,6 +12,7 @@ CPanic *cosmoV_newPanic(CState *state)
CPanic *panic = cosmoM_xmalloc(state, sizeof(CPanic)); CPanic *panic = cosmoM_xmalloc(state, sizeof(CPanic));
panic->prev = state->panic; panic->prev = state->panic;
state->panic = panic; state->panic = panic;
return panic; return panic;
} }
@ -19,6 +20,7 @@ void cosmoV_freePanic(CState *state)
{ {
CPanic *panic = state->panic; CPanic *panic = state->panic;
state->panic = panic->prev; state->panic = panic->prev;
cosmoM_free(state, CPanic, panic); cosmoM_free(state, CPanic, panic);
} }
@ -32,7 +34,6 @@ CState *cosmoV_newState()
exit(1); exit(1);
} }
state->panic = false;
state->freezeGC = 1; // we start frozen state->freezeGC = 1; // we start frozen
state->panic = NULL; state->panic = NULL;
@ -50,8 +51,6 @@ CState *cosmoV_newState()
state->frameCount = 0; state->frameCount = 0;
state->openUpvalues = NULL; state->openUpvalues = NULL;
state->error = NULL;
// set default proto objects // set default proto objects
for (int i = 0; i < COBJ_MAX; i++) for (int i = 0; i < COBJ_MAX; i++)
state->protoObjects[i] = NULL; state->protoObjects[i] = NULL;

View File

@ -52,7 +52,6 @@ struct CState
int frameCount; int frameCount;
CPanic *panic; CPanic *panic;
CObjError *error; // NULL, unless panic is true
CObj *objects; // tracks all of our allocated objects CObj *objects; // tracks all of our allocated objects
CObj *userRoots; // user definable roots, this holds CObjs that should be considered "roots", CObj *userRoots; // user definable roots, this holds CObjs that should be considered "roots",
// lets the VM know you are holding a reference to a CObj in your code // lets the VM know you are holding a reference to a CObj in your code

130
src/cvm.c
View File

@ -10,17 +10,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <string.h> #include <string.h>
bool cosmoV_protect(CState *state) #define cosmoV_protect(panic) setjmp(panic->jmp) == 0
{
CPanic *panic = cosmoV_newPanic(state);
if (setjmp(panic->jmp) == 0) {
return 1;
}
cosmoV_freePanic(state);
return 0;
}
COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...) COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...)
{ {
@ -48,9 +38,6 @@ COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud)
CObjFunction *func; CObjFunction *func;
if (cosmoD_undump(state, reader, ud, &func)) { if (cosmoD_undump(state, reader, ud, &func)) {
// fail recovery
state->panic = false;
cosmoV_pushRef(state, (CObj *)state->error);
return false; return false;
}; };
@ -65,24 +52,28 @@ COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud)
return true; return true;
} }
// returns false if failed, error will be on the top of the stack. true if successful, closure will
// be on the top of the stack
COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name) COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name)
{ {
CObjFunction *func; CObjFunction *func;
CPanic *panic = cosmoV_newPanic(state);
if (cosmoV_protect(state)) { if (cosmoV_protect(panic)) {
if ((func = cosmoP_compileString(state, src, name)) != NULL) { func = cosmoP_compileString(state, src, name);
// success
#ifdef VM_DEBUG #ifdef VM_DEBUG
disasmChunk(&func->chunk, func->module->str, 0); disasmChunk(&func->chunk, func->module->str, 0);
#endif #endif
// push function onto the stack so it doesn't it cleaned up by the GC, at the same stack cosmoV_freePanic(state);
// location put our closure
cosmoV_pushRef(state, (CObj *)func); // push function onto the stack so it doesn't it cleaned up by the GC, at the same stack
*(cosmoV_getTop(state, 0)) = cosmoV_newRef(cosmoO_newClosure(state, func)); // location put our closure
return true; cosmoV_pushRef(state, (CObj *)func);
} *(cosmoV_getTop(state, 0)) = cosmoV_newRef(cosmoO_newClosure(state, func));
return true;
} }
cosmoV_freePanic(state);
return false; return false;
} }
@ -244,18 +235,16 @@ void cosmoV_concat(CState *state, int vals)
} }
int cosmoV_execute(CState *state); int cosmoV_execute(CState *state);
bool invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults, int offset); void invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults, int offset);
/* /*
calls a native C Function with # args on the stack, nresults are pushed onto the stack upon calls a native C Function with # args on the stack, nresults are pushed onto the stack upon
return. return.
returns: state->top is moved to base + offset + nresults, with nresults pushed onto the stack
false: state paniced during C Function, error is at state->error from base + offset
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) static void callCFunction(CState *state, CosmoCFunction cfunc, int args, int nresults, int offset)
{ {
StkPtr savedBase = cosmoV_getTop(state, args); StkPtr savedBase = cosmoV_getTop(state, args);
@ -267,13 +256,8 @@ static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nre
// remember where the return values are // remember where the return values are
StkPtr results = cosmoV_getTop(state, nres - 1); StkPtr results = cosmoV_getTop(state, nres - 1);
state->top = savedBase + offset; // set stack state->top = savedBase + offset; // set stack
// if the state paniced during the c function, return false
if (state->panic)
return false;
// push the return value back onto the stack // push the return value back onto the stack
memmove(state->top, results, memmove(state->top, results,
sizeof(CValue) * nres); // copies the return values to the top of the stack sizeof(CValue) * nres); // copies the return values to the top of the stack
@ -282,20 +266,16 @@ static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nre
// now, if the caller function expected more return values, push nils onto the stack // now, if the caller function expected more return values, push nils onto the stack
for (int i = nres; i < nresults; i++) for (int i = nres; i < nresults; i++)
cosmoV_pushValue(state, cosmoV_newNil()); cosmoV_pushValue(state, cosmoV_newNil());
return true;
} }
/* /*
calls a raw closure object with # args on the stack, nresults are pushed onto the stack upon calls a raw closure object with # args on the stack, nresults are pushed onto the stack upon
return. return.
returns: stack->top is moved to base + offset + nresults, with nresults pushed onto the stack
false: state paniced, error is at state->error from base + offset
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) static void rawCall(CState *state, CObjClosure *closure, int args, int nresults, int offset)
{ {
CObjFunction *func = closure->function; CObjFunction *func = closure->function;
@ -319,7 +299,6 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults,
cosmoV_error(state, "Expected %d arguments for %s, got %d!", closure->function->args, cosmoV_error(state, "Expected %d arguments for %s, got %d!", closure->function->args,
closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str,
args); args);
return false;
} else { } else {
// load function into callframe // load function into callframe
pushCallFrame(state, closure, func->args); pushCallFrame(state, closure, func->args);
@ -337,9 +316,6 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults,
// pop the callframe and return results :) // pop the callframe and return results :)
popCallFrame(state, offset); popCallFrame(state, offset);
if (state->panic) // panic state
return false;
// push the return values back onto the stack // push the return values back onto the stack
for (int i = 0; i < nres; i++) { for (int i = 0; i < nres; i++) {
state->top[i] = results[i]; state->top[i] = results[i];
@ -349,12 +325,10 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults,
// now, if the caller function expected more return values, push nils onto the stack // now, if the caller function expected more return values, push nils onto the stack
for (int i = nres; i < nresults; i++) for (int i = nres; i < nresults; i++)
cosmoV_pushValue(state, cosmoV_newNil()); cosmoV_pushValue(state, cosmoV_newNil());
return true;
} }
// returns true if successful, false if error // returns true if successful, false if error
bool callCValue(CState *state, CValue func, int args, int nresults, int offset) void callCValue(CState *state, CValue func, int args, int nresults, int offset)
{ {
#ifdef VM_DEBUG #ifdef VM_DEBUG
printf("\n"); printf("\n");
@ -365,17 +339,19 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset)
if (!IS_REF(func)) { if (!IS_REF(func)) {
cosmoV_error(state, "Cannot call non-callable type %s!", cosmoV_typeStr(func)); cosmoV_error(state, "Cannot call non-callable type %s!", cosmoV_typeStr(func));
return false;
} }
switch (cosmoV_readRef(func)->type) { switch (cosmoV_readRef(func)->type) {
case COBJ_CLOSURE: case COBJ_CLOSURE:
return rawCall(state, cosmoV_readClosure(func), args, nresults, offset); rawCall(state, cosmoV_readClosure(func), args, nresults, offset);
break;
case COBJ_CFUNCTION: case COBJ_CFUNCTION:
return callCFunction(state, cosmoV_readCFunction(func), args, nresults, offset); callCFunction(state, cosmoV_readCFunction(func), args, nresults, offset);
break;
case COBJ_METHOD: { case COBJ_METHOD: {
CObjMethod *method = (CObjMethod *)cosmoV_readRef(func); CObjMethod *method = (CObjMethod *)cosmoV_readRef(func);
return invokeMethod(state, method->obj, method->func, args, nresults, offset + 1); invokeMethod(state, method->obj, method->func, args, nresults, offset + 1);
break;
} }
case COBJ_OBJECT: { // object is being instantiated, making another object case COBJ_OBJECT: { // object is being instantiated, making another object
CObjObject *protoObj = (CObjObject *)cosmoV_readRef(func); CObjObject *protoObj = (CObjObject *)cosmoV_readRef(func);
@ -388,12 +364,10 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset)
// check if they defined an initializer (we accept 0 return values) // check if they defined an initializer (we accept 0 return values)
if (cosmoO_getIString(state, protoObj, ISTRING_INIT, &ret)) { if (cosmoO_getIString(state, protoObj, ISTRING_INIT, &ret)) {
if (!invokeMethod(state, (CObj *)newObj, ret, args, 0, offset + 1)) invokeMethod(state, (CObj *)newObj, ret, args, 0, offset + 1);
return false;
} else { } else {
// no default initializer // no default initializer
cosmoV_error(state, "Expected __init() in proto, object cannot be instantiated!"); cosmoV_error(state, "Expected __init() in proto, object cannot be instantiated!");
return false;
} }
if (nresults > 0) { if (nresults > 0) {
@ -409,19 +383,16 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset)
} }
default: default:
cosmoV_error(state, "Cannot call non-callable type %s!", cosmoV_typeStr(func)); cosmoV_error(state, "Cannot call non-callable type %s!", cosmoV_typeStr(func));
return false;
} }
return true;
} }
bool invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults, int offset) void invokeMethod(CState *state, CObj *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_newRef(obj); *temp = cosmoV_newRef(obj);
return callCValue(state, func, args + 1, nresults, offset); callCValue(state, func, args + 1, nresults, offset);
} }
// wraps cosmoV_call in a protected state, CObjError will be pushed onto the stack if function call // wraps cosmoV_call in a protected state, CObjError will be pushed onto the stack if function call
@ -429,22 +400,22 @@ bool invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults,
// returns false if function call failed, true if function call succeeded // returns false if function call failed, true if function call succeeded
bool cosmoV_pcall(CState *state, int args, int nresults) bool cosmoV_pcall(CState *state, int args, int nresults)
{ {
if (cosmoV_protect(state)) { CPanic *panic = cosmoV_newPanic(state);
if (cosmoV_protect(panic)) {
cosmoV_call(state, args, nresults); cosmoV_call(state, args, nresults);
cosmoV_freePanic(state);
return true; return true;
} else { } else {
printf("caught panic!\n"); // if cosmoV_protect returns false, the error is already on the top of the stack
// restore panic state
state->panic = false;
if (nresults > 0) { if (nresults > 0) {
cosmoV_pushRef(state, (CObj *)state->error);
// push other expected results onto the stack // push other expected results onto the stack
for (int i = 0; i < nresults - 1; i++) for (int i = 0; i < nresults - 1; i++)
cosmoV_pushValue(state, cosmoV_newNil()); cosmoV_pushValue(state, cosmoV_newNil());
} }
cosmoV_freePanic(state);
return false; return false;
} }
} }
@ -452,11 +423,11 @@ bool cosmoV_pcall(CState *state, int args, int nresults)
// calls a callable object at stack->top - args - 1, passing the # of args to the callable, and // calls a callable object at stack->top - args - 1, passing the # of args to the callable, and
// ensuring nresults are returned // ensuring nresults are returned
// returns false if an error was thrown, else true if successful // returns false if an error was thrown, else true if successful
bool cosmoV_call(CState *state, int args, int nresults) void 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
return callCValue(state, *val, args, nresults, 0); callCValue(state, *val, args, nresults, 0);
} }
static inline bool isFalsey(StkPtr val) static inline bool isFalsey(StkPtr val)
@ -732,7 +703,7 @@ int cosmoV_execute(CState *state)
CCallFrame *frame = &state->callFrame[state->frameCount - 1]; // grabs the current frame CCallFrame *frame = &state->callFrame[state->frameCount - 1]; // grabs the current frame
CValue *constants = frame->closure->function->chunk.constants.values; // cache the pointer :) CValue *constants = frame->closure->function->chunk.constants.values; // cache the pointer :)
while (!state->panic) { for (;;) {
#ifdef VM_DEBUG #ifdef VM_DEBUG
cosmoV_printStack(state); cosmoV_printStack(state);
disasmInstr(&frame->closure->function->chunk, disasmInstr(&frame->closure->function->chunk,
@ -816,9 +787,7 @@ int cosmoV_execute(CState *state)
{ {
uint8_t args = READBYTE(frame); uint8_t args = READBYTE(frame);
uint8_t nres = READBYTE(frame); uint8_t nres = READBYTE(frame);
if (!cosmoV_call(state, args, nres)) { cosmoV_call(state, args, nres);
return -1;
}
} }
CASE(OP_CLOSURE) : CASE(OP_CLOSURE) :
{ {
@ -1043,10 +1012,9 @@ int cosmoV_execute(CState *state)
cosmoV_pop(state); // pop the object from the stack cosmoV_pop(state); // pop the object from the stack
cosmoV_pushValue(state, val); cosmoV_pushValue(state, val);
cosmoV_pushRef(state, (CObj *)obj); cosmoV_pushRef(state, (CObj *)obj);
if (!cosmoV_call( cosmoV_call(
state, 1, state, 1,
1)) // we expect 1 return value on the stack, the iterable object 1); // we expect 1 return value on the stack, the iterable object
return -1;
StkPtr iObj = cosmoV_getTop(state, 0); StkPtr iObj = cosmoV_getTop(state, 0);
@ -1104,8 +1072,7 @@ int cosmoV_execute(CState *state)
} }
cosmoV_pushValue(state, *temp); cosmoV_pushValue(state, *temp);
if (!cosmoV_call(state, 0, nresults)) cosmoV_call(state, 0, nresults);
return -1;
if (IS_NIL(*(cosmoV_getTop( if (IS_NIL(*(cosmoV_getTop(
state, 0)))) { // __next returned a nil, which means to exit the loop state, 0)))) { // __next returned a nil, which means to exit the loop
@ -1363,7 +1330,6 @@ int cosmoV_execute(CState *state)
} }
} }
// we'll only reach this if state->panic is true
return -1; return -1;
} }

View File

@ -13,16 +13,19 @@
cosmoV_execute by about 20% from benchmarking. of course, if you know cosmoV_execute by about 20% from benchmarking. of course, if you know
your compiler supports computed gotos, you can define VM_JUMPTABLE your compiler supports computed gotos, you can define VM_JUMPTABLE
although, this is disabled when VM_DEBUG is defined, since it can cause
issues with debugging
BTW: be weary of maliciously crafted cosmo dumps!! it's very easy to crash BTW: be weary of maliciously crafted cosmo dumps!! it's very easy to crash
cosmo with this enabled and reading invalid opcodes due to us just using the cosmo with this enabled and reading invalid opcodes due to us just using the
opcode as an index into the jump table opcode as an index into the jump table
*/ */
#if defined(__GNUC__) || defined(__clang__) #if (defined(__GNUC__) || defined(__clang__)) && !defined(VM_DEBUG)
# define VM_JUMPTABLE # define VM_JUMPTABLE
#endif #endif
// args = # of pass parameters, nresults = # of expected results // args = # of pass parameters, nresults = # of expected results
COSMO_API bool cosmoV_call(CState *state, int args, int nresults); COSMO_API void cosmoV_call(CState *state, int args, int nresults);
COSMO_API bool cosmoV_pcall(CState *state, int args, int nresults); COSMO_API bool cosmoV_pcall(CState *state, int args, int nresults);
// pushes new object onto the stack & returns a pointer to the new object // pushes new object onto the stack & returns a pointer to the new object
@ -97,13 +100,6 @@ static inline void cosmoV_pushValue(CState *state, CValue val)
// we reserve 8 slots for the error string and whatever c api we might be in // we reserve 8 slots for the error string and whatever c api we might be in
if (stackSize >= STACK_MAX - 8) { if (stackSize >= STACK_MAX - 8) {
if (state->panic) { // we're in a panic state, let the 8 reserved slots be filled
if (stackSize < STACK_MAX)
*(state->top++) = val;
return;
}
cosmoV_error(state, "Stack overflow!"); cosmoV_error(state, "Stack overflow!");
return; return;
} }