diff --git a/Makefile b/Makefile index 82cd1e3..b6a5419 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # make clean && make && ./bin/cosmo CC=clang -CFLAGS=-fPIE -Wall -Isrc -O3 -std=c99 +CFLAGS=-fPIE -Wall -Isrc -O3 -std=c99 #-g -fsanitize=address LDFLAGS=-lm #-fsanitize=address OUT=bin/cosmo diff --git a/main.c b/main.c index 33ebeb8..2993cc8 100644 --- a/main.c +++ b/main.c @@ -45,21 +45,20 @@ int cosmoB_input(CState *state, int nargs, CValue *args) 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) if (cosmoV_compileString(state, script, mod)) { // 0 args being passed, 0 results expected - if (!cosmoV_call(state, 0, 0)) - cosmoV_printError(state, state->error); + if (!cosmoV_pcall(state, 0, 0)) { + cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state))); + return false; + } } else { - cosmoV_pop(state); // pop the error off the stack - cosmoV_printError(state, state->error); + cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state))); + return false; } - ret = state->panic; - state->panic = false; // so our repl isn't broken - return !ret; + return true; } 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; cosmoD_dump(state, func, fileWriter, (void *)fout); } else { - cosmoV_pop(state); // pop the error off the stack - cosmoV_printError(state, state->error); + cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state))); } free(script); @@ -172,14 +170,13 @@ void loadScript(CState *state, const char *in) { FILE *file = fopen(in, "rb"); if (!cosmoV_undump(state, fileReader, file)) { - cosmoV_pop(state); // pop the error off the stack - cosmoV_printError(state, state->error); + cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state))); return; }; printf("[!] loaded %s!\n", in); - if (!cosmoV_call(state, 0, 0)) - cosmoV_printError(state, state->error); + if (!cosmoV_pcall(state, 0, 0)) + cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state))); fclose(file); } diff --git a/src/cmem.c b/src/cmem.c index 79e3588..c7a6665 100644 --- a/src/cmem.c +++ b/src/cmem.c @@ -288,9 +288,6 @@ static void markRoots(CState *state) // mark the user defined roots markUserRoots(state); - // mark other misc. internally reserved objects - markObject(state, (CObj *)state->error); - for (int i = 0; i < COBJ_MAX; i++) markObject(state, (CObj *)state->protoObjects[i]); diff --git a/src/cobj.c b/src/cobj.c index 19f0a6d..2a30ab1 100644 --- a/src/cobj.c +++ b/src/cobj.c @@ -164,14 +164,12 @@ _eqFail: cosmoV_pushValue(state, eq1); cosmoV_pushRef(state, obj1); cosmoV_pushRef(state, obj2); - if (!cosmoV_call(state, 2, 1)) - return false; + cosmoV_call(state, 2, 1); // check return value and make sure it's a boolean if (!IS_BOOLEAN(*cosmoV_getTop(state, 0))) { cosmoV_error(state, "__equal expected to return , got %s!", cosmoV_typeStr(*cosmoV_pop(state))); - return false; } // 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)) { cosmoV_pushValue(state, *val); // push function cosmoV_pushRef(state, (CObj *)obj); // push object - if (!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 + cosmoV_call(state, 1, 1); // call the function with the 1 argument + *val = *cosmoV_pop(state); // set value to the return value of __index return true; } @@ -530,9 +527,8 @@ bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *v cosmoV_pushValue(state, *val); // push function cosmoV_pushRef(state, (CObj *)object); // push object cosmoV_pushValue(state, key); // push key - if (!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 + cosmoV_call(state, 2, 1); // call the function with the 2 arguments + *val = *cosmoV_pop(state); // set value to the return value of __index return true; } else { // there's no __index function defined! 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_pushValue(state, key); // push key & value pair cosmoV_pushValue(state, val); - return cosmoV_call(state, 3, 0); + cosmoV_call(state, 3, 0); + return true; } else { // there's no __newindex function defined 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)) { cosmoV_pushValue(state, res); cosmoV_pushRef(state, (CObj *)obj); - if (!cosmoV_call(state, 1, 1)) - return cosmoO_copyString(state, "", 5); + cosmoV_call(state, 1, 1); // make sure the __tostring function returned a string 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)) { cosmoV_pushValue(state, res); cosmoV_pushRef(state, (CObj *)obj); - if (!cosmoV_call(state, 1, 1)) // call res, expect 1 return val of - return 0; + cosmoV_call(state, 1, 1); // call res, expect 1 return val of StkPtr temp = cosmoV_getTop(state, 0); if (!IS_NUMBER(*temp)) { cosmoV_error(state, "__tonumber expected to return , got %s!", cosmoV_typeStr(*temp)); - return 0; } // return number @@ -666,8 +660,7 @@ int cosmoO_count(CState *state, CObj *obj) if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_COUNT, &res)) { cosmoV_pushValue(state, res); cosmoV_pushRef(state, (CObj *)obj); - if (!cosmoV_call(state, 1, 1)) // call res, we expect 1 return value of type - return 0; + cosmoV_call(state, 1, 1); // call res, we expect 1 return value of type StkPtr ret = cosmoV_getTop(state, 0); if (!IS_NUMBER(*ret)) { diff --git a/src/cobj.h b/src/cobj.h index 0f0e3ba..a53362e 100644 --- a/src/cobj.h +++ b/src/cobj.h @@ -136,6 +136,7 @@ struct CObjUpval #define cosmoV_readCFunction(x) (((CObjCFunction *)cosmoV_readRef(x))->cfunc) #define cosmoV_readMethod(x) ((CObjMethod *)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_readType(x) ((CObj *)x)->type diff --git a/src/cstate.c b/src/cstate.c index 4b4cccb..08e3228 100644 --- a/src/cstate.c +++ b/src/cstate.c @@ -12,6 +12,7 @@ CPanic *cosmoV_newPanic(CState *state) CPanic *panic = cosmoM_xmalloc(state, sizeof(CPanic)); panic->prev = state->panic; state->panic = panic; + return panic; } @@ -19,6 +20,7 @@ void cosmoV_freePanic(CState *state) { CPanic *panic = state->panic; state->panic = panic->prev; + cosmoM_free(state, CPanic, panic); } @@ -32,7 +34,6 @@ CState *cosmoV_newState() exit(1); } - state->panic = false; state->freezeGC = 1; // we start frozen state->panic = NULL; @@ -50,8 +51,6 @@ CState *cosmoV_newState() state->frameCount = 0; state->openUpvalues = NULL; - state->error = NULL; - // set default proto objects for (int i = 0; i < COBJ_MAX; i++) state->protoObjects[i] = NULL; diff --git a/src/cstate.h b/src/cstate.h index d971e4f..c6840c3 100644 --- a/src/cstate.h +++ b/src/cstate.h @@ -52,7 +52,6 @@ struct CState int frameCount; CPanic *panic; - CObjError *error; // NULL, unless panic is true CObj *objects; // tracks all of our allocated objects 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 diff --git a/src/cvm.c b/src/cvm.c index e055867..aec8b49 100644 --- a/src/cvm.c +++ b/src/cvm.c @@ -10,17 +10,7 @@ #include #include -bool cosmoV_protect(CState *state) -{ - CPanic *panic = cosmoV_newPanic(state); - - if (setjmp(panic->jmp) == 0) { - return 1; - } - - cosmoV_freePanic(state); - return 0; -} +#define cosmoV_protect(panic) setjmp(panic->jmp) == 0 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; if (cosmoD_undump(state, reader, ud, &func)) { - // fail recovery - state->panic = false; - cosmoV_pushRef(state, (CObj *)state->error); return false; }; @@ -65,24 +52,29 @@ COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud) 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) { CObjFunction *func; - if (cosmoV_protect(state)) { - if ((func = cosmoP_compileString(state, src, name)) != NULL) { - // success + CPanic *panic = cosmoV_newPanic(state); + + if (cosmoV_protect(panic)) { + func = cosmoP_compileString(state, src, name); #ifdef VM_DEBUG - disasmChunk(&func->chunk, func->module->str, 0); + disasmChunk(&func->chunk, func->module->str, 0); #endif - // push function onto the stack so it doesn't it cleaned up by the GC, at the same stack - // location put our closure - cosmoV_pushRef(state, (CObj *)func); - *(cosmoV_getTop(state, 0)) = cosmoV_newRef(cosmoO_newClosure(state, func)); - return true; - } + cosmoV_freePanic(state); + + // push function onto the stack so it doesn't it cleaned up by the GC, at the same stack + // location put our closure + cosmoV_pushRef(state, (CObj *)func); + *(cosmoV_getTop(state, 0)) = cosmoV_newRef(cosmoO_newClosure(state, func)); + return true; } + cosmoV_freePanic(state); return false; } @@ -244,18 +236,16 @@ void cosmoV_concat(CState *state, int vals) } 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 - return. + return. - returns: - false: state paniced during C Function, error is at state->error - true: state->top is moved to base + offset + nresults, with nresults pushed onto the stack - from base + offset + 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); @@ -267,13 +257,8 @@ static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nre // remember where the return values are StkPtr results = cosmoV_getTop(state, nres - 1); - 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 memmove(state->top, results, sizeof(CValue) * nres); // copies the return values to the top of the stack @@ -282,20 +267,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 for (int i = nres; i < nresults; i++) cosmoV_pushValue(state, cosmoV_newNil()); - - return true; } /* calls a raw closure object with # args on the stack, nresults are pushed onto the stack upon - return. + return. - returns: - false: state paniced, error is at state->error - true: stack->top is moved to base + offset + nresults, with nresults pushed onto the stack - from base + offset + 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; @@ -319,7 +300,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, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, args); - return false; } else { // load function into callframe pushCallFrame(state, closure, func->args); @@ -337,9 +317,6 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults, // pop the callframe and return results :) popCallFrame(state, offset); - if (state->panic) // panic state - return false; - // push the return values back onto the stack for (int i = 0; i < nres; i++) { state->top[i] = results[i]; @@ -349,12 +326,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 for (int i = nres; i < nresults; i++) cosmoV_pushValue(state, cosmoV_newNil()); - - return true; } // 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 printf("\n"); @@ -365,17 +340,19 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset) if (!IS_REF(func)) { cosmoV_error(state, "Cannot call non-callable type %s!", cosmoV_typeStr(func)); - return false; } switch (cosmoV_readRef(func)->type) { case COBJ_CLOSURE: - return rawCall(state, cosmoV_readClosure(func), args, nresults, offset); + rawCall(state, cosmoV_readClosure(func), args, nresults, offset); + break; case COBJ_CFUNCTION: - return callCFunction(state, cosmoV_readCFunction(func), args, nresults, offset); + callCFunction(state, cosmoV_readCFunction(func), args, nresults, offset); + break; case COBJ_METHOD: { 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 CObjObject *protoObj = (CObjObject *)cosmoV_readRef(func); @@ -388,12 +365,10 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset) // check if they defined an initializer (we accept 0 return values) if (cosmoO_getIString(state, protoObj, ISTRING_INIT, &ret)) { - if (!invokeMethod(state, (CObj *)newObj, ret, args, 0, offset + 1)) - return false; + invokeMethod(state, (CObj *)newObj, ret, args, 0, offset + 1); } else { // no default initializer cosmoV_error(state, "Expected __init() in proto, object cannot be instantiated!"); - return false; } if (nresults > 0) { @@ -409,19 +384,16 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset) } default: 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 StkPtr temp = cosmoV_getTop(state, args); *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 @@ -429,22 +401,22 @@ bool invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults, // returns false if function call failed, true if function call succeeded 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_freePanic(state); return true; } else { - printf("caught panic!\n"); - // restore panic state - state->panic = false; + // if cosmoV_protect returns false, the error is already on the top of the stack if (nresults > 0) { - cosmoV_pushRef(state, (CObj *)state->error); - // push other expected results onto the stack for (int i = 0; i < nresults - 1; i++) cosmoV_pushValue(state, cosmoV_newNil()); } + cosmoV_freePanic(state); return false; } } @@ -452,11 +424,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 // ensuring nresults are returned // 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 - return callCValue(state, *val, args, nresults, 0); + callCValue(state, *val, args, nresults, 0); } static inline bool isFalsey(StkPtr val) @@ -732,7 +704,7 @@ int cosmoV_execute(CState *state) CCallFrame *frame = &state->callFrame[state->frameCount - 1]; // grabs the current frame CValue *constants = frame->closure->function->chunk.constants.values; // cache the pointer :) - while (!state->panic) { + for (;;) { #ifdef VM_DEBUG cosmoV_printStack(state); disasmInstr(&frame->closure->function->chunk, @@ -816,9 +788,7 @@ int cosmoV_execute(CState *state) { uint8_t args = READBYTE(frame); uint8_t nres = READBYTE(frame); - if (!cosmoV_call(state, args, nres)) { - return -1; - } + cosmoV_call(state, args, nres); } CASE(OP_CLOSURE) : { @@ -1043,10 +1013,9 @@ int cosmoV_execute(CState *state) cosmoV_pop(state); // pop the object from the stack cosmoV_pushValue(state, val); cosmoV_pushRef(state, (CObj *)obj); - if (!cosmoV_call( - state, 1, - 1)) // we expect 1 return value on the stack, the iterable object - return -1; + cosmoV_call( + state, 1, + 1); // we expect 1 return value on the stack, the iterable object StkPtr iObj = cosmoV_getTop(state, 0); @@ -1104,8 +1073,7 @@ int cosmoV_execute(CState *state) } cosmoV_pushValue(state, *temp); - if (!cosmoV_call(state, 0, nresults)) - return -1; + cosmoV_call(state, 0, nresults); if (IS_NIL(*(cosmoV_getTop( state, 0)))) { // __next returned a nil, which means to exit the loop @@ -1363,7 +1331,6 @@ int cosmoV_execute(CState *state) } } - // we'll only reach this if state->panic is true return -1; } diff --git a/src/cvm.h b/src/cvm.h index e721184..2dcb489 100644 --- a/src/cvm.h +++ b/src/cvm.h @@ -13,16 +13,19 @@ cosmoV_execute by about 20% from benchmarking. of course, if you know 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 cosmo with this enabled and reading invalid opcodes due to us just using the opcode as an index into the jump table */ -#if defined(__GNUC__) || defined(__clang__) +#if (defined(__GNUC__) || defined(__clang__)) && !defined(VM_DEBUG) # define VM_JUMPTABLE #endif // 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); // 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 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!"); return; }