From f26376e6f511b298e9116865c047191632818fbc Mon Sep 17 00:00:00 2001 From: CPunch Date: Mon, 28 Aug 2023 21:13:00 -0500 Subject: [PATCH 1/9] WIP: major error handling refactoring switching to setjmp instead of the really bad global 'panic' flag --- src/clex.c | 9 +++---- src/clex.h | 4 +-- src/cparse.c | 73 ++++++++-------------------------------------------- src/cstate.c | 16 ++++++++++++ src/cstate.h | 17 ++++++++++-- src/cvm.c | 69 +++++++++++++++++++++++++++---------------------- src/cvm.h | 2 +- 7 files changed, 86 insertions(+), 104 deletions(-) diff --git a/src/clex.c b/src/clex.c index fbf0b3d..811d48e 100644 --- a/src/clex.c +++ b/src/clex.c @@ -389,9 +389,8 @@ static CToken parseIdentifier(CLexState *state) return makeToken(state, identifierType(state)); // is it a reserved word? } -CLexState *cosmoL_newLexState(CState *cstate, const char *source) +void cosmoL_initLexState(CState *cstate, CLexState *state, const char *source) { - CLexState *state = cosmoM_xmalloc(cstate, sizeof(CLexState)); state->startChar = (char *)source; state->currentChar = (char *)source; state->line = 1; @@ -400,13 +399,11 @@ CLexState *cosmoL_newLexState(CState *cstate, const char *source) state->cstate = cstate; resetBuffer(state); - - return state; } -void cosmoL_freeLexState(CState *state, CLexState *lstate) +void cosmoL_cleanupLexState(CState *state, CLexState *lstate) { - cosmoM_free(state, CLexState, lstate); + // stubbed } CToken cosmoL_scanToken(CLexState *state) diff --git a/src/clex.h b/src/clex.h index dab608e..a8d9ead 100644 --- a/src/clex.h +++ b/src/clex.h @@ -103,8 +103,8 @@ typedef struct CState *cstate; } CLexState; -CLexState *cosmoL_newLexState(CState *state, const char *source); -void cosmoL_freeLexState(CState *state, CLexState *lstate); +void cosmoL_initLexState(CState *cstate, CLexState *state, const char *source); +void cosmoL_cleanupLexState(CState *state, CLexState *lstate); CToken cosmoL_scanToken(CLexState *state); diff --git a/src/cparse.c b/src/cparse.c index dc578c3..c7099a3 100644 --- a/src/cparse.c +++ b/src/cparse.c @@ -59,14 +59,12 @@ typedef struct CCompilerState typedef struct { + CLexState lex; CState *state; - CLexState *lex; CCompilerState *compiler; CObjString *module; // name of the module CToken current; CToken previous; // token right after the current token - bool hadError; - bool panic; } CParseState; typedef enum @@ -124,6 +122,8 @@ static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, Func ccstate->function = cosmoO_newFunction(pstate->state); ccstate->function->module = pstate->module; + cosmoV_pushRef(pstate->state, (CObj *)ccstate->function); + ccstate->loop.scope = -1; // there is no loop yet if (type != FTYPE_SCRIPT) { @@ -146,11 +146,9 @@ static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, Func static void initParseState(CParseState *pstate, CCompilerState *ccstate, CState *s, const char *source, const char *module) { - pstate->lex = cosmoL_newLexState(s, source); + cosmoL_initLexState(s, &pstate->lex, source); pstate->state = s; - pstate->hadError = false; - pstate->panic = false; pstate->compiler = ccstate; pstate->module = cosmoO_copyString(s, module, strlen(module)); @@ -159,14 +157,11 @@ static void initParseState(CParseState *pstate, CCompilerState *ccstate, CState static void freeParseState(CParseState *pstate) { - cosmoL_freeLexState(pstate->state, pstate->lex); + cosmoL_cleanupLexState(pstate->state, &pstate->lex); } static void errorAt(CParseState *pstate, CToken *token, const char *format, va_list args) { - if (pstate->hadError) - return; - if (token->type == TOKEN_EOF) { cosmoV_pushString(pstate->state, "At end: "); } else if (!(token->type == TOKEN_ERROR)) { @@ -177,14 +172,9 @@ static void errorAt(CParseState *pstate, CToken *token, const char *format, va_l cosmoO_pushVFString(pstate->state, format, args); - cosmoV_concat(pstate->state, 2); // concats the two strings together - - CObjError *err = cosmoV_throw(pstate->state); - err->line = token->line; - err->parserError = true; - - pstate->hadError = true; - pstate->panic = true; + // throw complete error string + cosmoV_concat(pstate->state, 2); + cosmoV_throw(pstate->state); } static void errorAtCurrent(CParseState *pstate, const char *format, ...) @@ -206,7 +196,7 @@ static void error(CParseState *pstate, const char *format, ...) static void advance(CParseState *pstate) { pstate->previous = pstate->current; - pstate->current = cosmoL_scanToken(pstate->lex); + pstate->current = cosmoL_scanToken(&pstate->lex); if (pstate->current.type == TOKEN_ERROR) { errorAtCurrent(pstate, pstate->current.start); @@ -280,7 +270,6 @@ uint16_t makeConstant(CParseState *pstate, CValue val) int indx = addConstant(pstate->state, getChunk(pstate), val); if (indx > UINT16_MAX) { error(pstate, "UInt overflow! Too many constants in one chunk!"); - return 0; } return (uint16_t)indx; @@ -350,7 +339,6 @@ static void addLocal(CParseState *pstate, CToken name) { if (pstate->compiler->localCount > UINT8_MAX) { error(pstate, "UInt overflow! Too many locals in scope!"); - return; } Local *local = &pstate->compiler->locals[pstate->compiler->localCount++]; @@ -365,7 +353,6 @@ static int addUpvalue(CParseState *pstate, CCompilerState *ccstate, uint8_t indx if (upvals > UINT8_MAX) { error(pstate, "UInt overflow! Too many upvalues in scope!"); - return -1; } // check and make sure we haven't already captured it @@ -768,7 +755,6 @@ static void table(CParseState *pstate, bool canAssign, Precedence prec) tblType = 1; // array-like } else { error(pstate, "Can't change table description type mid-definition!"); - return; } entries++; @@ -818,7 +804,7 @@ static void object(CParseState *pstate, bool canAssign, Precedence prec) // "pop" the 1 value valuePopped(pstate, 1); entries++; - } while (match(pstate, TOKEN_COMMA) && !pstate->hadError); + } while (match(pstate, TOKEN_COMMA)); consume(pstate, TOKEN_RIGHT_BRACE, "Expected '}' to end object definition."); } @@ -1158,9 +1144,6 @@ static uint16_t parseVariable(CParseState *pstate, const char *errorMessage, boo static void defineVariable(CParseState *pstate, uint16_t global, bool forceLocal) { - if (pstate->hadError) - return; - if (pstate->compiler->scopeDepth > 0 || forceLocal) { markInitialized(pstate, global); valuePopped(pstate, 1); // the local stays on the stack! @@ -1177,7 +1160,7 @@ static void _proto(CParseState *pstate) { int entries = 0; - while (!match(pstate, TOKEN_END) && !match(pstate, TOKEN_EOF) && !pstate->hadError) { + while (!match(pstate, TOKEN_END) && !match(pstate, TOKEN_EOF)) { if (match(pstate, TOKEN_FUNC)) { // define method consume(pstate, TOKEN_IDENTIFIER, "Expected identifier for method!"); @@ -1224,9 +1207,6 @@ static void localProto(CParseState *pstate) static void popLocals(CParseState *pstate, int toScope) { - if (pstate->hadError) - return; - // count the locals in scope to pop int localsToPop = 0; @@ -1686,18 +1666,6 @@ static void continueStatement(CParseState *pstate) writeJmpBack(pstate, pstate->compiler->loop.startBytecode); } -static void synchronize(CParseState *pstate) -{ - pstate->panic = false; - - while (pstate->current.type != TOKEN_EOF) { - if (pstate->previous.type == TOKEN_EOS) - return; - - advance(pstate); - } -} - static int expressionPrecedence(CParseState *pstate, int needed, Precedence prec, bool forceNeeded) { int lastExpected = pstate->compiler->expectedValues; @@ -1787,10 +1755,6 @@ static void statement(CParseState *pstate) static void declaration(CParseState *pstate) { statement(pstate); - - // if we paniced, skip the whole statement! - if (pstate->panic) - synchronize(pstate); } static CObjFunction *endCompiler(CParseState *pstate) @@ -1812,7 +1776,6 @@ CObjFunction *cosmoP_compileString(CState *state, const char *source, const char { CParseState parser; CCompilerState compiler; - cosmoM_freezeGC(state); // ignore all GC events while compiling initParseState(&parser, &compiler, state, source, module); advance(&parser); @@ -1825,24 +1788,10 @@ CObjFunction *cosmoP_compileString(CState *state, const char *source, const char popLocals(&parser, 0); - if (parser.hadError) { // we don't free the function, the state already has a reference to it in - // it's linked list of objects! - endCompiler(&parser); - freeParseState(&parser); - - cosmoM_unfreezeGC(state); - return NULL; - } - CObjFunction *resFunc = compiler.function; // finally free out parser states endCompiler(&parser); freeParseState(&parser); - - // push the funciton onto the stack so if we cause an GC event, it won't be free'd - cosmoV_pushRef(state, (CObj *)resFunc); - cosmoM_unfreezeGC(state); - cosmoV_pop(state); return resFunc; } diff --git a/src/cstate.c b/src/cstate.c index 6b241b8..4b4cccb 100644 --- a/src/cstate.c +++ b/src/cstate.c @@ -7,6 +7,21 @@ #include +CPanic *cosmoV_newPanic(CState *state) +{ + CPanic *panic = cosmoM_xmalloc(state, sizeof(CPanic)); + panic->prev = state->panic; + state->panic = panic; + return panic; +} + +void cosmoV_freePanic(CState *state) +{ + CPanic *panic = state->panic; + state->panic = panic->prev; + cosmoM_free(state, CPanic, panic); +} + CState *cosmoV_newState() { // we use C's malloc because we don't want to trigger a GC with an invalid state @@ -19,6 +34,7 @@ CState *cosmoV_newState() state->panic = false; state->freezeGC = 1; // we start frozen + state->panic = NULL; // GC state->objects = NULL; diff --git a/src/cstate.h b/src/cstate.h index 711d62f..d971e4f 100644 --- a/src/cstate.h +++ b/src/cstate.h @@ -6,6 +6,8 @@ #include "ctable.h" #include "cvalue.h" +#include + struct CCallFrame { CObjClosure *closure; @@ -38,11 +40,17 @@ typedef struct ArrayCObj int capacity; } ArrayCObj; +typedef struct CPanic +{ + jmp_buf jmp; + struct CPanic *prev; +} CPanic; + struct CState { - bool panic; int freezeGC; // when > 0, GC events will be ignored (for internal use) int frameCount; + CPanic *panic; CObjError *error; // NULL, unless panic is true CObj *objects; // tracks all of our allocated objects @@ -64,10 +72,15 @@ struct CState CValue stack[STACK_MAX]; // stack }; +CPanic *cosmoV_newPanic(CState *state); +void cosmoV_freePanic(CState *state); + COSMO_API CState *cosmoV_newState(); +COSMO_API void cosmoV_freeState(CState *state); + // expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value COSMO_API void cosmoV_register(CState *state, int pairs); -COSMO_API void cosmoV_freeState(CState *state); + COSMO_API void cosmoV_printStack(CState *state); #endif diff --git a/src/cvm.c b/src/cvm.c index 865e634..e055867 100644 --- a/src/cvm.c +++ b/src/cvm.c @@ -10,6 +10,18 @@ #include #include +bool cosmoV_protect(CState *state) +{ + 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, ...) { va_list args; @@ -57,21 +69,20 @@ COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char * { CObjFunction *func; - if ((func = cosmoP_compileString(state, src, name)) != NULL) { - // success + if (cosmoV_protect(state)) { + if ((func = cosmoP_compileString(state, src, name)) != NULL) { + // success #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; + // 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; + } } - // fail recovery - state->panic = false; - cosmoV_pushRef(state, (CObj *)state->error); return false; } @@ -109,30 +120,26 @@ COSMO_API void cosmoV_printError(CState *state, CObjError *err) } /* - takes value on top of the stack and wraps an CObjError around it, state->error is set to that - value the value on the stack is *expected* to be a string, but not required, so yes, this means - you could throw a nil value if you really wanted too.. + takes value on top of the stack and wraps an CObjError around it, then throws it */ -CObjError *cosmoV_throw(CState *state) +void cosmoV_throw(CState *state) { StkPtr temp = cosmoV_getTop(state, 0); - CObjError *error = cosmoO_newError(state, *temp); - state->error = error; - state->panic = true; - cosmoV_pop(state); // pops thrown value off the stack - return error; + // replace the value on the stack with the error + *temp = cosmoV_newRef((CObj *)cosmoO_newError(state, *temp)); + if (state->panic) { + longjmp(state->panic->jmp, 1); + } else { + fprintf(stderr, "Unhandled panic! "); + cosmoV_printError(state, error); + exit(1); + } } void cosmoV_error(CState *state, const char *format, ...) { - if (state->panic) - return; - - // i set panic before calling cosmoO_pushVFString, since that can also call cosmoV_error - state->panic = true; - // format the error string and push it onto the stack va_list args; va_start(args, format); @@ -422,9 +429,11 @@ 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) { - StkPtr base = cosmoV_getTop(state, args); - - if (!callCValue(state, *base, args, nresults, 0)) { + if (cosmoV_protect(state)) { + cosmoV_call(state, args, nresults); + return true; + } else { + printf("caught panic!\n"); // restore panic state state->panic = false; @@ -438,8 +447,6 @@ bool cosmoV_pcall(CState *state, int args, int nresults) return false; } - - return true; } // calls a callable object at stack->top - args - 1, passing the # of args to the callable, and diff --git a/src/cvm.h b/src/cvm.h index 96fab8a..e721184 100644 --- a/src/cvm.h +++ b/src/cvm.h @@ -31,7 +31,7 @@ COSMO_API void cosmoV_makeTable(CState *state, int pairs); COSMO_API void cosmoV_concat(CState *state, int vals); COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...); COSMO_API void cosmoV_printError(CState *state, CObjError *err); -COSMO_API CObjError *cosmoV_throw(CState *state); +COSMO_API void cosmoV_throw(CState *state); COSMO_API void cosmoV_error(CState *state, const char *format, ...); COSMO_API void cosmo_insert(CState *state, int indx, CValue val); From cd3047c2710d05ad3a375291d3df2287532955e0 Mon Sep 17 00:00:00 2001 From: CPunch Date: Tue, 29 Aug 2023 14:07:45 -0500 Subject: [PATCH 2/9] 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 --- Makefile | 2 +- main.c | 25 +++++----- src/cmem.c | 3 -- src/cobj.c | 27 ++++------- src/cobj.h | 1 + src/cstate.c | 5 +- src/cstate.h | 1 - src/cvm.c | 131 +++++++++++++++++++-------------------------------- src/cvm.h | 14 ++---- 9 files changed, 79 insertions(+), 130 deletions(-) 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; } From 37e42eb60b74318077eabe7cc8c2637623961da3 Mon Sep 17 00:00:00 2001 From: CPunch Date: Tue, 29 Aug 2023 15:27:22 -0500 Subject: [PATCH 3/9] 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. --- src/cparse.c | 15 ++++++++++++++- src/cvm.c | 1 - 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/cparse.c b/src/cparse.c index c7099a3..8b8ec18 100644 --- a/src/cparse.c +++ b/src/cparse.c @@ -65,6 +65,8 @@ typedef struct CObjString *module; // name of the module CToken current; 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; typedef enum @@ -107,6 +109,12 @@ static CObjFunction *endCompiler(CParseState *pstate); // ================================================================ [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, CCompilerState *enclosing) { @@ -122,7 +130,7 @@ static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, Func ccstate->function = cosmoO_newFunction(pstate->state); 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 @@ -151,13 +159,18 @@ static void initParseState(CParseState *pstate, CCompilerState *ccstate, CState pstate->state = s; pstate->compiler = ccstate; 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 } static void freeParseState(CParseState *pstate) { 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) diff --git a/src/cvm.c b/src/cvm.c index aec8b49..c7bc95a 100644 --- a/src/cvm.c +++ b/src/cvm.c @@ -57,7 +57,6 @@ COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud) COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name) { CObjFunction *func; - CPanic *panic = cosmoV_newPanic(state); if (cosmoV_protect(panic)) { From 27818b37880c6bd9282dfaffb308e407464fb85b Mon Sep 17 00:00:00 2001 From: CPunch Date: Tue, 29 Aug 2023 16:48:38 -0500 Subject: [PATCH 4/9] cosmoV_throw() now resets the vm stack as well also a minor GC bug in cosmoO_newError was fixed. i'm going to try to phase out cosmoM_freezeGC & friends since that would cause hell with this new error handling solution. the only thing still using it is the GC. --- src/cbaselib.c | 7 ------- src/cobj.c | 7 ++++--- src/cparse.h | 3 +-- src/cstate.c | 3 ++- src/cstate.h | 2 ++ src/cvm.c | 7 +++++-- 6 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/cbaselib.c b/src/cbaselib.c index 7cadc9b..e173938 100644 --- a/src/cbaselib.c +++ b/src/cbaselib.c @@ -948,16 +948,9 @@ int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args) // vm.collect() int cosmoB_vcollect(CState *state, int nargs, CValue *args) { - // first, unfreeze the state (we start frozen on entry to any C Function) - cosmoM_unfreezeGC(state); - // now force a garbage collection cosmoM_collectGarbage(state); - // and re-freeze the state - cosmoM_freezeGC(state); - - // the end! return 0; } diff --git a/src/cobj.c b/src/cobj.c index 2a30ab1..6badbdb 100644 --- a/src/cobj.c +++ b/src/cobj.c @@ -229,14 +229,13 @@ CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func) CObjError *cosmoO_newError(CState *state, CValue err) { + CCallFrame *frames = cosmoM_xmalloc(state, sizeof(CCallFrame) * state->frameCount); CObjError *cerror = (CObjError *)cosmoO_allocateBase(state, sizeof(CObjError), COBJ_ERROR); cerror->err = err; cerror->frameCount = state->frameCount; + cerror->frames = frames; cerror->parserError = false; - // allocate the callframe - cerror->frames = cosmoM_xmalloc(state, sizeof(CCallFrame) * cerror->frameCount); - // clone the call frame for (int i = 0; i < state->frameCount; i++) cerror->frames[i] = state->callFrame[i]; @@ -762,6 +761,8 @@ const char *cosmoO_typeStr(CObj *obj) return ""; case COBJ_CFUNCTION: return ""; + case COBJ_ERROR: + return ""; case COBJ_METHOD: return ""; case COBJ_CLOSURE: diff --git a/src/cparse.h b/src/cparse.h index 3c0da88..24e131f 100644 --- a/src/cparse.h +++ b/src/cparse.h @@ -4,8 +4,7 @@ #include "clex.h" #include "cosmo.h" -// compiles source into CChunk, if NULL is returned, a syntaxical error has occurred and pushed onto -// the stack +// compiles source into CChunk CObjFunction *cosmoP_compileString(CState *state, const char *source, const char *module); #endif diff --git a/src/cstate.c b/src/cstate.c index 08e3228..6233366 100644 --- a/src/cstate.c +++ b/src/cstate.c @@ -10,6 +10,8 @@ CPanic *cosmoV_newPanic(CState *state) { CPanic *panic = cosmoM_xmalloc(state, sizeof(CPanic)); + panic->top = state->top; + panic->frameCount = state->frameCount; panic->prev = state->panic; state->panic = panic; @@ -95,7 +97,6 @@ void cosmoV_freeState(CState *state) #ifdef GC_DEBUG printf("state %p is being free'd!\n", state); #endif - cosmoM_freezeGC(state); // frees all the objects CObj *objs = state->objects; diff --git a/src/cstate.h b/src/cstate.h index c6840c3..df79a86 100644 --- a/src/cstate.h +++ b/src/cstate.h @@ -43,6 +43,8 @@ typedef struct ArrayCObj typedef struct CPanic { jmp_buf jmp; + StkPtr top; + int frameCount; struct CPanic *prev; } CPanic; diff --git a/src/cvm.c b/src/cvm.c index c7bc95a..89d085e 100644 --- a/src/cvm.c +++ b/src/cvm.c @@ -118,11 +118,14 @@ void cosmoV_throw(CState *state) StkPtr temp = cosmoV_getTop(state, 0); CObjError *error = cosmoO_newError(state, *temp); - // replace the value on the stack with the error - *temp = cosmoV_newRef((CObj *)cosmoO_newError(state, *temp)); + CValue val = cosmoV_newRef((CObj *)cosmoO_newError(state, *temp)); if (state->panic) { + state->top = state->panic->top; + state->frameCount = state->panic->frameCount; + cosmoV_pushValue(state, val); longjmp(state->panic->jmp, 1); } else { + cosmoV_pushValue(state, val); fprintf(stderr, "Unhandled panic! "); cosmoV_printError(state, error); exit(1); From 1408a07b2328eb2461e688bf9fdb93924770c0dd Mon Sep 17 00:00:00 2001 From: CPunch Date: Tue, 29 Aug 2023 16:51:04 -0500 Subject: [PATCH 5/9] fix this test script --- examples/test.cosmo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/test.cosmo b/examples/test.cosmo index e7fa530..3fc3d65 100644 --- a/examples/test.cosmo +++ b/examples/test.cosmo @@ -6,7 +6,7 @@ end // instance of test let obj = test() -test.__index = function(self, key) +test.__index = func(self, key) print("__index called!") if (key == "lol") then return 9001 From 6ed55895134dc46ce3f1ea4f0af8726f0a739431 Mon Sep 17 00:00:00 2001 From: CPunch Date: Tue, 29 Aug 2023 23:01:47 -0500 Subject: [PATCH 6/9] fix cparse.c gc bug --- src/cparse.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cparse.c b/src/cparse.c index 8b8ec18..84e4d8b 100644 --- a/src/cparse.c +++ b/src/cparse.c @@ -481,6 +481,7 @@ static void string(CParseState *pstate, bool canAssign, Precedence prec) { CObjString *strObj = cosmoO_takeString(pstate->state, pstate->previous.start, pstate->previous.length); + keepTrackOf(pstate, cosmoV_newRef((CObj *)strObj)); writeConstant(pstate, cosmoV_newRef((CObj *)strObj)); } From 1ae473383de9374a9bfe92f415673e457c80bb98 Mon Sep 17 00:00:00 2001 From: CPunch Date: Tue, 29 Aug 2023 23:21:52 -0500 Subject: [PATCH 7/9] fix more GC bugs --- src/cchunk.c | 8 ++++---- src/clex.c | 6 +++--- src/cmem.c | 22 ++++++++++++---------- src/cmem.h | 33 ++++++++++++++++++++------------- src/cobj.c | 8 ++++---- src/cparse.c | 11 ++++++----- src/cstate.c | 2 +- src/cstate.h | 6 +++--- src/ctable.c | 4 ++-- src/cvalue.c | 4 ++-- 10 files changed, 57 insertions(+), 47 deletions(-) diff --git a/src/cchunk.c b/src/cchunk.c index 4ef4ad6..04db498 100644 --- a/src/cchunk.c +++ b/src/cchunk.c @@ -27,9 +27,9 @@ void initChunk(CState *state, CChunk *chunk, size_t startCapacity) void cleanChunk(CState *state, CChunk *chunk) { // first, free the chunk buffer - cosmoM_freearray(state, INSTRUCTION, chunk->buf, chunk->capacity); + cosmoM_freeArray(state, INSTRUCTION, chunk->buf, chunk->capacity); // then the line info - cosmoM_freearray(state, int, chunk->lineInfo, chunk->capacity); + cosmoM_freeArray(state, int, chunk->lineInfo, chunk->capacity); // free the constants cleanValArray(state, &chunk->constants); } @@ -61,8 +61,8 @@ int addConstant(CState *state, CChunk *chunk, CValue value) void writeu8Chunk(CState *state, CChunk *chunk, INSTRUCTION i, int line) { // does the buffer need to be reallocated? - cosmoM_growarray(state, INSTRUCTION, chunk->buf, chunk->count, chunk->capacity); - cosmoM_growarray(state, int, chunk->lineInfo, chunk->count, chunk->lineCapacity); + cosmoM_growArray(state, INSTRUCTION, chunk->buf, chunk->count, chunk->capacity); + cosmoM_growArray(state, int, chunk->lineInfo, chunk->count, chunk->lineCapacity); // write data to the chunk :) chunk->lineInfo[chunk->count] = line; diff --git a/src/clex.c b/src/clex.c index 811d48e..e2993df 100644 --- a/src/clex.c +++ b/src/clex.c @@ -54,7 +54,7 @@ static void resetBuffer(CLexState *state) // cancels the token heap buffer and frees it static void freeBuffer(CLexState *state) { - cosmoM_freearray(state->cstate, char, state->buffer, state->bufCap); + cosmoM_freeArray(state->cstate, char, state->buffer, state->bufCap); resetBuffer(state); } @@ -62,7 +62,7 @@ static void freeBuffer(CLexState *state) // adds character to buffer static void appendBuffer(CLexState *state, char c) { - cosmoM_growarray(state->cstate, char, state->buffer, state->bufCount, state->bufCap); + cosmoM_growArray(state->cstate, char, state->buffer, state->bufCount, state->bufCap); state->buffer[state->bufCount++] = c; } @@ -90,7 +90,7 @@ static char *cutBuffer(CLexState *state, int *length) resetBuffer(state); // shrink the buffer to only use what we need - return cosmoM_reallocate(state->cstate, buf, cap, count); + return cosmoM_reallocate(state->cstate, buf, cap, count, true); } static CToken makeToken(CLexState *state, CTokenType type) diff --git a/src/cmem.c b/src/cmem.c index c7a6665..71b8514 100644 --- a/src/cmem.c +++ b/src/cmem.c @@ -8,7 +8,7 @@ #include "cvalue.h" // realloc wrapper -void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize) +void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize, bool isGC) { if (buf == NULL) oldSize = 0; @@ -34,18 +34,20 @@ void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize return NULL; } + if (isGC) { #ifdef GC_STRESS - if (!(cosmoM_isFrozen(state)) && newSize > oldSize) { - cosmoM_collectGarbage(state); - } + if (!(cosmoM_isFrozen(state)) && newSize > oldSize) { + cosmoM_collectGarbage(state); + } # ifdef GC_DEBUG - else { - printf("GC event ignored! state frozen! [%d]\n", state->freezeGC); - } + else { + printf("GC event ignored! state frozen! [%d]\n", state->freezeGC); + } # endif #else - cosmoM_checkGarbage(state, 0); + cosmoM_checkGarbage(state, 0); #endif + } // if NULL is passed, realloc() acts like malloc() void *newBuf = realloc(buf, newSize); @@ -206,8 +208,8 @@ static void markObject(CState *state, CObj *obj) return; // we can use cosmoM_growarray because we lock the GC when we entered in cosmoM_collectGarbage - cosmoM_growarray(state, CObj *, state->grayStack.array, state->grayStack.count, - state->grayStack.capacity); + cosmoM_growArrayNonGC(state, CObj *, state->grayStack.array, state->grayStack.count, + state->grayStack.capacity); state->grayStack.array[state->grayStack.count++] = obj; } diff --git a/src/cmem.h b/src/cmem.h index c00aa30..ba38087 100644 --- a/src/cmem.h +++ b/src/cmem.h @@ -12,28 +12,37 @@ #define ARRAY_START 8 #ifdef GC_DEBUG -# define cosmoM_freearray(state, type, buf, capacity) \ +# define cosmoM_freeArray(state, type, buf, capacity) \ printf("freeing array %p [size %lu] at %s:%d\n", buf, sizeof(type) * capacity, __FILE__, \ __LINE__); \ - cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0) + cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0, true) #else -# define cosmoM_freearray(state, type, buf, capacity) \ - cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0) +# define cosmoM_freeArray(state, type, buf, capacity) \ + cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0, true) #endif -#define cosmoM_growarray(state, type, buf, count, capacity) \ +#define cosmoM_growArray(state, type, buf, count, capacity) \ if (count >= capacity || buf == NULL) { \ int old = capacity; \ capacity = old * GROW_FACTOR; \ - buf = (type *)cosmoM_reallocate(state, buf, sizeof(type) * old, sizeof(type) * capacity); \ + buf = (type *)cosmoM_reallocate(state, buf, sizeof(type) * old, sizeof(type) * capacity, \ + true); \ + } + +#define cosmoM_growArrayNonGC(state, type, buf, count, capacity) \ + if (count >= capacity || buf == NULL) { \ + int old = capacity; \ + capacity = old * GROW_FACTOR; \ + buf = (type *)cosmoM_reallocate(state, buf, sizeof(type) * old, sizeof(type) * capacity, \ + false); \ } #ifdef GC_DEBUG # define cosmoM_free(state, type, x) \ printf("freeing %p [size %lu] at %s:%d\n", x, sizeof(type), __FILE__, __LINE__); \ - cosmoM_reallocate(state, x, sizeof(type), 0) + cosmoM_reallocate(state, x, sizeof(type), 0, true) #else -# define cosmoM_free(state, type, x) cosmoM_reallocate(state, x, sizeof(type), 0) +# define cosmoM_free(state, type, x) cosmoM_reallocate(state, x, sizeof(type), 0, true) #endif #define cosmoM_isFrozen(state) (state->freezeGC > 0) @@ -60,7 +69,8 @@ #endif -COSMO_API void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize); +COSMO_API void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize, + bool isGC); COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed); // returns true if GC event was triggered COSMO_API void cosmoM_collectGarbage(CState *state); @@ -76,10 +86,7 @@ COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot); // wrapper for cosmoM_reallocate so we can track our memory usage static inline void *cosmoM_xmalloc(CState *state, size_t sz) { - return cosmoM_reallocate(state, NULL, 0, sz); + return cosmoM_reallocate(state, NULL, 0, sz, true); } -// #define cosmoM_xmalloc(state, sz) \ -// (printf("allocating new buffer at %s:%d of size %ld\n", __FILE__, __LINE__, sz), cosmoM_reallocate(state, NULL, 0, sz)) - #endif diff --git a/src/cobj.c b/src/cobj.c index 6badbdb..81bead8 100644 --- a/src/cobj.c +++ b/src/cobj.c @@ -46,7 +46,7 @@ void cosmoO_free(CState *state, CObj *obj) switch (obj->type) { case COBJ_STRING: { CObjString *objStr = (CObjString *)obj; - cosmoM_freearray(state, char, objStr->str, objStr->length + 1); + cosmoM_freeArray(state, char, objStr->str, objStr->length + 1); cosmoM_free(state, CObjString, objStr); break; } @@ -82,13 +82,13 @@ void cosmoO_free(CState *state, CObj *obj) } case COBJ_ERROR: { CObjError *err = (CObjError *)obj; - cosmoM_freearray(state, CCallFrame, err->frames, err->frameCount); + cosmoM_freeArray(state, CCallFrame, err->frames, err->frameCount); cosmoM_free(state, CObjError, obj); break; } case COBJ_CLOSURE: { CObjClosure *closure = (CObjClosure *)obj; - cosmoM_freearray(state, CObjUpval *, closure->upvalues, closure->upvalueCount); + cosmoM_freeArray(state, CObjUpval *, closure->upvalues, closure->upvalueCount); cosmoM_free(state, CObjClosure, closure); break; } @@ -305,7 +305,7 @@ CObjString *cosmoO_takeString(CState *state, char *str, size_t length) // have we already interned this string? if (lookup != NULL) { - cosmoM_freearray(state, char, str, + cosmoM_freeArray(state, char, str, length + 1); // free our passed character array, it's unneeded! return lookup; } diff --git a/src/cparse.c b/src/cparse.c index 84e4d8b..7bc9cf8 100644 --- a/src/cparse.c +++ b/src/cparse.c @@ -64,9 +64,10 @@ typedef struct CCompilerState *compiler; CObjString *module; // name of the module CToken current; - 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 + 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; typedef enum @@ -1370,7 +1371,7 @@ static void endLoop(CParseState *pstate) patchJmp(pstate, pstate->compiler->loop.breaks[--pstate->compiler->loop.breakCount]); } - cosmoM_freearray(pstate->state, int, pstate->compiler->loop.breaks, + cosmoM_freeArray(pstate->state, int, pstate->compiler->loop.breaks, pstate->compiler->loop.breakCapacity); } @@ -1659,7 +1660,7 @@ static void breakStatement(CParseState *pstate) pstate->compiler->localCount = savedLocals; // add break to loop - cosmoM_growarray(pstate->state, int, pstate->compiler->loop.breaks, + cosmoM_growArray(pstate->state, int, pstate->compiler->loop.breaks, pstate->compiler->loop.breakCount, pstate->compiler->loop.breakCapacity); pstate->compiler->loop.breaks[pstate->compiler->loop.breakCount++] = writeJmp(pstate, OP_JMP); } diff --git a/src/cstate.c b/src/cstate.c index 6233366..3ed618f 100644 --- a/src/cstate.c +++ b/src/cstate.c @@ -120,7 +120,7 @@ void cosmoV_freeState(CState *state) cosmoT_clearTable(state, &state->strings); // free our gray stack & finally free the state structure - cosmoM_freearray(state, CObj *, state->grayStack.array, state->grayStack.capacity); + cosmoM_freeArray(state, CObj *, state->grayStack.array, state->grayStack.capacity); #ifdef GC_DEBUG if (state->allocatedBytes != 0) { diff --git a/src/cstate.h b/src/cstate.h index df79a86..f895957 100644 --- a/src/cstate.h +++ b/src/cstate.h @@ -54,9 +54,9 @@ struct CState int frameCount; CPanic *panic; - 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 + 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 ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but // *have been* found size_t allocatedBytes; diff --git a/src/ctable.c b/src/ctable.c index 8c9a0ff..46a3e6b 100644 --- a/src/ctable.c +++ b/src/ctable.c @@ -61,7 +61,7 @@ void cosmoT_addTable(CState *state, CTable *from, CTable *to) void cosmoT_clearTable(CState *state, CTable *tbl) { - cosmoM_freearray(state, CTableEntry, tbl->table, cosmoT_getCapacity(tbl)); + cosmoM_freeArray(state, CTableEntry, tbl->table, cosmoT_getCapacity(tbl)); } static uint32_t getObjectHash(CObj *obj) @@ -165,7 +165,7 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin } // free the old table - cosmoM_freearray(state, CTableEntry, tbl->table, oldCap); + cosmoM_freeArray(state, CTableEntry, tbl->table, oldCap); tbl->table = entries; tbl->capacityMask = newCapacity - 1; diff --git a/src/cvalue.c b/src/cvalue.c index 52052cc..245436c 100644 --- a/src/cvalue.c +++ b/src/cvalue.c @@ -13,12 +13,12 @@ void initValArray(CState *state, CValueArray *val, size_t startCapacity) void cleanValArray(CState *state, CValueArray *array) { - cosmoM_freearray(state, CValue, array->values, array->capacity); + cosmoM_freeArray(state, CValue, array->values, array->capacity); } void appendValArray(CState *state, CValueArray *array, CValue val) { - cosmoM_growarray(state, CValue, array->values, array->count, array->capacity); + cosmoM_growArray(state, CValue, array->values, array->count, array->capacity); array->values[array->count++] = val; } From 37e4653d40bd1bea26496c9ebd9e6c3f71845f15 Mon Sep 17 00:00:00 2001 From: CPunch Date: Tue, 29 Aug 2023 23:32:25 -0500 Subject: [PATCH 8/9] don't freezeGC during GC cycle --- src/cmem.c | 5 ----- src/cobj.c | 3 +-- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/cmem.c b/src/cmem.c index 71b8514..491fc96 100644 --- a/src/cmem.c +++ b/src/cmem.c @@ -302,8 +302,6 @@ COSMO_API void cosmoM_collectGarbage(CState *state) printf("-- GC start\n"); size_t start = state->allocatedBytes; #endif - cosmoM_freezeGC(state); // we don't want a recursive garbage collection event! - markRoots(state); tableRemoveWhite( @@ -314,9 +312,6 @@ COSMO_API void cosmoM_collectGarbage(CState *state) // set our next GC event cosmoM_updateThreshhold(state); - - state->freezeGC--; // we don't want to use cosmoM_unfreezeGC because that might trigger a GC - // event (if GC_STRESS is defined) #ifdef GC_DEBUG printf("-- GC end, reclaimed %ld bytes (started at %ld, ended at %ld), next garbage collection " "scheduled at %ld bytes\n", diff --git a/src/cobj.c b/src/cobj.c index 81bead8..8bfb72d 100644 --- a/src/cobj.c +++ b/src/cobj.c @@ -321,8 +321,7 @@ CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uin strObj->length = sz; strObj->hash = hash; - // we push & pop the string so our GC can find it (we don't use freezeGC/unfreezeGC because we - // *want* a GC event to happen) + // push/pop to make sure GC doesn't collect it cosmoV_pushRef(state, (CObj *)strObj); cosmoT_insert(state, &state->strings, cosmoV_newRef((CObj *)strObj)); cosmoV_pop(state); From 97d40765ced8b4c0595af17ebd6794bc71cb4276 Mon Sep 17 00:00:00 2001 From: CPunch Date: Wed, 30 Aug 2023 12:00:52 -0500 Subject: [PATCH 9/9] more refactoring; things seem to work fine all example scripts run fine with GC_STRESS enabled --- src/cbaselib.c | 74 ++------------------------------------------------ src/clex.c | 2 +- src/cmem.c | 24 ++++++++-------- src/cmem.h | 25 ++++++----------- src/cstate.h | 31 ++++++++++----------- 5 files changed, 38 insertions(+), 118 deletions(-) diff --git a/src/cbaselib.c b/src/cbaselib.c index e173938..3e3e076 100644 --- a/src/cbaselib.c +++ b/src/cbaselib.c @@ -30,7 +30,6 @@ int cosmoB_assert(CState *state, int nargs, CValue *args) { if (nargs < 1 || nargs > 2) { cosmoV_error(state, "assert() expected 1 or 2 arguments, got %d!", nargs); - return 0; // nothing pushed onto the stack to return } if (!IS_BOOLEAN(args[0]) || (nargs == 2 && !IS_STRING(args[1]))) { @@ -40,7 +39,6 @@ int cosmoB_assert(CState *state, int nargs, CValue *args) } else { cosmoV_typeError(state, "assert()", "", "%s", cosmoV_typeStr(args[0])); } - return 0; } if (!cosmoV_readBoolean(args[0])) // expression passed was false, error! @@ -53,7 +51,6 @@ int cosmoB_type(CState *state, int nargs, CValue *args) { if (nargs != 1) { cosmoV_error(state, "type() expected 1 argument, got %d!", nargs); - return 0; } // push the type string to the stack @@ -65,7 +62,6 @@ int cosmoB_pcall(CState *state, int nargs, CValue *args) { if (nargs < 1) { cosmoV_error(state, "pcall() expected at least 1 argument!"); - return 0; } // call the passed callable, the passed arguments are already in the @@ -81,7 +77,6 @@ int cosmoB_tonumber(CState *state, int nargs, CValue *args) { if (nargs != 1) { cosmoV_error(state, "tonumber() expected 1 argument, got %d!", nargs); - return 0; } cosmoV_pushNumber(state, cosmoV_toNumber(state, args[0])); @@ -90,10 +85,8 @@ int cosmoB_tonumber(CState *state, int nargs, CValue *args) int cosmoB_tostring(CState *state, int nargs, CValue *args) { - if (nargs != 1) { + if (nargs != 1) cosmoV_error(state, "tostring() expected 1 argument, got %d!", nargs); - return 0; - } cosmoV_pushRef(state, (CObj *)cosmoV_toString(state, args[0])); return 1; @@ -103,12 +96,10 @@ int cosmoB_loadstring(CState *state, int nargs, CValue *args) { if (nargs != 1) { cosmoV_error(state, "loadstring() expected 1 argument, got %d!", nargs); - return 0; } if (!IS_STRING(args[0])) { cosmoV_typeError(state, "loadstring()", "", "%s", cosmoV_typeStr(args[0])); - return 0; } CObjString *str = cosmoV_readString(args[0]); @@ -122,12 +113,10 @@ int cosmoB_error(CState *state, int nargs, CValue *args) { if (nargs != 1) { cosmoV_error(state, "error() expected 1 argument, got %d!", nargs); - return 0; } if (!IS_STRING(args[0])) { cosmoV_typeError(state, "error()", "", "%s", cosmoV_typeStr(args[0])); - return 0; } cosmoV_error(state, "%s", cosmoV_readCString(args[0])); @@ -176,10 +165,8 @@ int cosmoB_osetProto(CState *state, int nargs, CValue *args) int cosmoB_ogetProto(CState *state, int nargs, CValue *args) { - if (nargs != 1) { + if (nargs != 1) cosmoV_error(state, "Expected 1 argument, got %d!", nargs); - return 0; - } cosmoV_pushRef(state, (CObj *)cosmoV_readObject(args[0])->_obj.proto); // just return the proto @@ -190,13 +177,11 @@ int cosmoB_oisChild(CState *state, int nargs, CValue *args) { if (nargs != 2) { cosmoV_error(state, "object.ischild() expected 2 arguments, got %d!", nargs); - return 0; } if (!IS_REF(args[0]) || !IS_OBJECT(args[1])) { cosmoV_typeError(state, "object.ischild()", ", ", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); - return 0; } CObj *obj = cosmoV_readRef(args[0]); @@ -255,12 +240,10 @@ int cosmoB_osRead(CState *state, int nargs, CValue *args) { if (nargs != 1) { cosmoV_error(state, "os.read() expected 1 argument, got %d!", nargs); - return 0; } if (!IS_STRING(args[0])) { cosmoV_typeError(state, "os.read()", "", "%s", cosmoV_typeStr(args[0])); - return 0; } CObjString *str = cosmoV_readString(args[0]); @@ -301,7 +284,6 @@ int cosmoB_osTime(CState *state, int nargs, CValue *args) struct timeval time; if (nargs > 0) { cosmoV_error(state, "os.time() expected no arguments, got %d!", nargs); - return 0; } gettimeofday(&time, NULL); @@ -314,12 +296,10 @@ int cosmoB_osSystem(CState *state, int nargs, CValue *args) { if (nargs != 1) { cosmoV_error(state, "os.system() expects 1 argument, got %d!", nargs); - return 0; } if (!IS_STRING(args[0])) { cosmoV_typeError(state, "os.system()", "", "%s", cosmoV_typeStr(args[0])); - return 0; } // run the command and return the exit code @@ -354,7 +334,6 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) { cosmoV_typeError(state, "string.sub()", ", ", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); - return 0; } CObjString *str = cosmoV_readString(args[0]); @@ -364,7 +343,6 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) if (indx < 0 || indx >= str->length) { cosmoV_error(state, "string.sub() expected index to be 0-%d, got %d!", str->length - 1, indx); - return 0; } cosmoV_pushLString(state, str->str + ((int)indx), str->length - ((int)indx)); @@ -373,7 +351,6 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) cosmoV_typeError(state, "string.sub()", ", , ", "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[2])); - return 0; } CObjString *str = cosmoV_readString(args[0]); @@ -385,13 +362,11 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) cosmoV_error( state, "string.sub() expected subbed string goes out of bounds, max length is %d!", str->length); - return 0; } cosmoV_pushLString(state, str->str + ((int)indx), ((int)length)); } else { cosmoV_error(state, "string.sub() expected 2 or 3 arguments, got %d!", nargs); - return 0; } return 1; @@ -404,7 +379,6 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args) if (!IS_STRING(args[0]) || !IS_STRING(args[1])) { cosmoV_typeError(state, "string.find()", ", ", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); - return 0; } CObjString *str = cosmoV_readString(args[0]); @@ -425,7 +399,6 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args) cosmoV_typeError(state, "string.find()", ", , ", "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[2])); - return 0; } CObjString *str = cosmoV_readString(args[0]); @@ -444,7 +417,6 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args) cosmoV_pushNumber(state, (cosmo_Number)(indx - str->str)); } else { cosmoV_error(state, "string.find() expected 2 or 3 arguments, got %d!", nargs); - return 0; } return 1; @@ -455,13 +427,11 @@ int cosmoB_sSplit(CState *state, int nargs, CValue *args) { if (nargs != 2) { cosmoV_error(state, "string.split() expected 2 arguments, got %d!", nargs); - return 0; } if (!IS_STRING(args[0]) || !IS_STRING(args[1])) { cosmoV_typeError(state, "string.split()", ", ", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); - return 0; } CObjString *str = cosmoV_readString(args[0]); @@ -492,12 +462,10 @@ int cosmoB_sByte(CState *state, int nargs, CValue *args) { if (nargs != 1) { cosmoV_error(state, "string.byte() expected 1 argument, got %d!", nargs); - return 0; } if (!IS_STRING(args[0])) { cosmoV_typeError(state, "string.byte", "", "%s", cosmoV_typeStr(args[0])); - return 0; } CObjString *str = cosmoV_readString(args[0]); @@ -518,12 +486,10 @@ int cosmoB_sChar(CState *state, int nargs, CValue *args) { if (nargs != 1) { cosmoV_error(state, "string.char() expected 1 argument, got %d!", nargs); - return 0; } if (!IS_NUMBER(args[0])) { cosmoV_typeError(state, "string.char", "", "%s", cosmoV_typeStr(args[0])); - return 0; } // small side effect of truncating the number, but ignoring the decimal instead of throwing an @@ -533,7 +499,6 @@ int cosmoB_sChar(CState *state, int nargs, CValue *args) if (num > 255 || num < 0) { cosmoV_error(state, "Character expected to be in range 0-255, got %d!", num); - return 0; } // basically, treat the character value on the C stack as an """"array"""" with a length of 1 @@ -545,12 +510,10 @@ int cosmoB_sLen(CState *state, int nargs, CValue *args) { if (nargs < 1) { cosmoV_error(state, "string.len() expected 1 argument, got %d!", nargs); - return 0; } if (!IS_STRING(args[0])) { cosmoV_typeError(state, "string.len", "", "%s", cosmoV_typeStr(args[0])); - return 0; } cosmoV_pushNumber(state, (cosmo_Number)strlen(cosmoV_readCString(args[0]))); @@ -562,14 +525,12 @@ int cosmoB_sRep(CState *state, int nargs, CValue *args) { if (nargs != 2) { cosmoV_error(state, "string.rep() expected 2 arguments, got %d!", nargs); - return 0; } // expects , if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) { cosmoV_typeError(state, "string.rep", ", ", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); - return 0; } CObjString *str = cosmoV_readString(args[0]); @@ -627,12 +588,10 @@ int cosmoB_mAbs(CState *state, int nargs, CValue *args) { if (nargs != 1) { cosmoV_error(state, "math.abs() expected 1 argument, got %d!", nargs); - return 0; } if (!IS_NUMBER(args[0])) { cosmoV_typeError(state, "math.abs", "", "%s", cosmoV_typeStr(args[0])); - return 0; } cosmoV_pushNumber(state, fabs(cosmoV_readNumber(args[0]))); @@ -644,12 +603,10 @@ int cosmoB_mFloor(CState *state, int nargs, CValue *args) { if (nargs != 1) { cosmoV_error(state, "math.floor() expected 1 argument, got %d!", nargs); - return 0; } if (!IS_NUMBER(args[0])) { cosmoV_typeError(state, "math.floor", "", "%s", cosmoV_typeStr(args[0])); - return 0; } cosmoV_pushNumber(state, (int)cosmoV_readNumber(args[0])); @@ -661,12 +618,10 @@ int cosmoB_mCeil(CState *state, int nargs, CValue *args) { if (nargs != 1) { cosmoV_error(state, "math.ceil() expected 1 argument, got %d!", nargs); - return 0; } if (!IS_NUMBER(args[0])) { cosmoV_typeError(state, "math.ceil", "", "%s", cosmoV_typeStr(args[0])); - return 0; } int roundedDown = (int)cosmoV_readNumber(args[0]); @@ -685,12 +640,10 @@ int cosmoB_mSin(CState *state, int nargs, CValue *args) { if (nargs != 1) { cosmoV_error(state, "math.sin() expected 1 argument, got %d!", nargs); - return 0; } if (!IS_NUMBER(args[0])) { cosmoV_typeError(state, "math.sin", "", "%s", cosmoV_typeStr(args[0])); - return 0; } cosmoV_pushNumber(state, sin(cosmoV_readNumber(args[0]))); @@ -701,12 +654,10 @@ int cosmoB_mCos(CState *state, int nargs, CValue *args) { if (nargs != 1) { cosmoV_error(state, "math.cos() expected 1 argument, got %d!", nargs); - return 0; } if (!IS_NUMBER(args[0])) { cosmoV_typeError(state, "math.cos", "", "%s", cosmoV_typeStr(args[0])); - return 0; } cosmoV_pushNumber(state, cos(cosmoV_readNumber(args[0]))); @@ -717,12 +668,10 @@ int cosmoB_mTan(CState *state, int nargs, CValue *args) { if (nargs != 1) { cosmoV_error(state, "math.tan() expected 1 argument, got %d!", nargs); - return 0; } if (!IS_NUMBER(args[0])) { cosmoV_typeError(state, "math.tan", "", "%s", cosmoV_typeStr(args[0])); - return 0; } cosmoV_pushNumber(state, tan(cosmoV_readNumber(args[0]))); @@ -733,12 +682,10 @@ int cosmoB_mASin(CState *state, int nargs, CValue *args) { if (nargs != 1) { cosmoV_error(state, "math.asin() expected 1 argument, got %d!", nargs); - return 0; } if (!IS_NUMBER(args[0])) { cosmoV_typeError(state, "math.asin", "", "%s", cosmoV_typeStr(args[0])); - return 0; } cosmoV_pushNumber(state, asin(cosmoV_readNumber(args[0]))); @@ -749,12 +696,10 @@ int cosmoB_mACos(CState *state, int nargs, CValue *args) { if (nargs != 1) { cosmoV_error(state, "math.acos() expected 1 argument, got %d!", nargs); - return 0; } if (!IS_NUMBER(args[0])) { cosmoV_typeError(state, "math.acos", "", "%s", cosmoV_typeStr(args[0])); - return 0; } cosmoV_pushNumber(state, acos(cosmoV_readNumber(args[0]))); @@ -765,12 +710,10 @@ int cosmoB_mATan(CState *state, int nargs, CValue *args) { if (nargs != 1) { cosmoV_error(state, "math.atan() expected 1 argument, got %d!", nargs); - return 0; } if (!IS_NUMBER(args[0])) { cosmoV_typeError(state, "math.atan", "", "%s", cosmoV_typeStr(args[0])); - return 0; } cosmoV_pushNumber(state, atan(cosmoV_readNumber(args[0]))); @@ -781,12 +724,10 @@ int cosmoB_mRad(CState *state, int nargs, CValue *args) { if (nargs != 1) { cosmoV_error(state, "math.rad() expected 1 argument, got %d!", nargs); - return 0; } if (!IS_NUMBER(args[0])) { cosmoV_typeError(state, "math.rad", "", "%s", cosmoV_typeStr(args[0])); - return 0; } // convert the degree to radians @@ -798,12 +739,10 @@ int cosmoB_mDeg(CState *state, int nargs, CValue *args) { if (nargs != 1) { cosmoV_error(state, "math.deg() expected 1 argument, got %d!", nargs); - return 0; } if (!IS_NUMBER(args[0])) { cosmoV_typeError(state, "math.deg", "", "%s", cosmoV_typeStr(args[0])); - return 0; } // convert the degree to radians @@ -852,13 +791,11 @@ int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args) { if (nargs != 2) { cosmoV_error(state, "Expected 2 argumenst, got %d!", nargs); - return 0; } if (!IS_TABLE(args[1])) { cosmoV_typeError(state, "vm.__setter[\"globals\"]", ", ", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); - return 0; } // this makes me very nervous ngl @@ -874,13 +811,11 @@ int cosmoB_vdisassemble(CState *state, int nargs, CValue *args) if (nargs != 1) { cosmoV_error(state, "Expected 1 argument, got %d!", nargs); - return 0; } // get the closure if (!IS_CLOSURE(args[0])) { cosmoV_typeError(state, "vm.disassemble", "", "%s", cosmoV_typeStr(args[0])); - return 0; } closure = cosmoV_readClosure(args[0]); @@ -895,13 +830,11 @@ int cosmoB_vindexBProto(CState *state, int nargs, CValue *args) { if (nargs != 2) { cosmoV_error(state, "Expected 2 arguments, got %d!", nargs); - return 0; } if (!IS_NUMBER(args[1])) { cosmoV_typeError(state, "baseProtos.__index", ", ", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); - return 0; } int indx = (int)cosmoV_readNumber(args[1]); @@ -923,14 +856,12 @@ int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args) { if (nargs != 3) { cosmoV_error(state, "Expected 3 arguments, got %d!", nargs); - return 0; } if (!IS_NUMBER(args[1]) || !IS_OBJECT(args[2])) { cosmoV_typeError(state, "baseProtos.__newindex", ", , ", "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[2])); - return 0; } int indx = (int)cosmoV_readNumber(args[1]); @@ -938,7 +869,6 @@ int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args) if (indx >= COBJ_MAX || indx < 0) { cosmoV_error(state, "index out of range! expected 0 - %d, got %d!", COBJ_MAX, indx); - return 0; } cosmoV_registerProtoObject(state, indx, proto); diff --git a/src/clex.c b/src/clex.c index e2993df..ed8360e 100644 --- a/src/clex.c +++ b/src/clex.c @@ -90,7 +90,7 @@ static char *cutBuffer(CLexState *state, int *length) resetBuffer(state); // shrink the buffer to only use what we need - return cosmoM_reallocate(state->cstate, buf, cap, count, true); + return cosmoM_reallocate(state->cstate, buf, cap, count); } static CToken makeToken(CLexState *state, CTokenType type) diff --git a/src/cmem.c b/src/cmem.c index 491fc96..20d8e41 100644 --- a/src/cmem.c +++ b/src/cmem.c @@ -8,7 +8,7 @@ #include "cvalue.h" // realloc wrapper -void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize, bool isGC) +void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize) { if (buf == NULL) oldSize = 0; @@ -34,20 +34,18 @@ void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize return NULL; } - if (isGC) { #ifdef GC_STRESS - if (!(cosmoM_isFrozen(state)) && newSize > oldSize) { - cosmoM_collectGarbage(state); - } + if (!(cosmoM_isFrozen(state)) && newSize > oldSize) { + cosmoM_collectGarbage(state); + } # ifdef GC_DEBUG - else { - printf("GC event ignored! state frozen! [%d]\n", state->freezeGC); - } + else { + printf("GC event ignored! state frozen! [%d]\n", state->freezeGC); + } # endif #else - cosmoM_checkGarbage(state, 0); + cosmoM_checkGarbage(state, 0); #endif - } // if NULL is passed, realloc() acts like malloc() void *newBuf = realloc(buf, newSize); @@ -208,8 +206,8 @@ static void markObject(CState *state, CObj *obj) return; // we can use cosmoM_growarray because we lock the GC when we entered in cosmoM_collectGarbage - cosmoM_growArrayNonGC(state, CObj *, state->grayStack.array, state->grayStack.count, - state->grayStack.capacity); + cosmoM_growArray(state, CObj *, state->grayStack.array, state->grayStack.count, + state->grayStack.capacity); state->grayStack.array[state->grayStack.count++] = obj; } @@ -298,6 +296,7 @@ static void markRoots(CState *state) COSMO_API void cosmoM_collectGarbage(CState *state) { + cosmoM_freezeGC(state); #ifdef GC_DEBUG printf("-- GC start\n"); size_t start = state->allocatedBytes; @@ -317,6 +316,7 @@ COSMO_API void cosmoM_collectGarbage(CState *state) "scheduled at %ld bytes\n", start - state->allocatedBytes, start, state->allocatedBytes, state->nextGC); #endif + cosmoM_unfreezeGC(state); } COSMO_API void cosmoM_updateThreshhold(CState *state) diff --git a/src/cmem.h b/src/cmem.h index ba38087..0063892 100644 --- a/src/cmem.h +++ b/src/cmem.h @@ -15,38 +15,30 @@ # define cosmoM_freeArray(state, type, buf, capacity) \ printf("freeing array %p [size %lu] at %s:%d\n", buf, sizeof(type) * capacity, __FILE__, \ __LINE__); \ - cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0, true) + cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0) #else # define cosmoM_freeArray(state, type, buf, capacity) \ - cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0, true) + cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0) #endif #define cosmoM_growArray(state, type, buf, count, capacity) \ if (count >= capacity || buf == NULL) { \ int old = capacity; \ capacity = old * GROW_FACTOR; \ - buf = (type *)cosmoM_reallocate(state, buf, sizeof(type) * old, sizeof(type) * capacity, \ - true); \ - } - -#define cosmoM_growArrayNonGC(state, type, buf, count, capacity) \ - if (count >= capacity || buf == NULL) { \ - int old = capacity; \ - capacity = old * GROW_FACTOR; \ - buf = (type *)cosmoM_reallocate(state, buf, sizeof(type) * old, sizeof(type) * capacity, \ - false); \ + buf = (type *)cosmoM_reallocate(state, buf, sizeof(type) * old, sizeof(type) * capacity); \ } #ifdef GC_DEBUG # define cosmoM_free(state, type, x) \ printf("freeing %p [size %lu] at %s:%d\n", x, sizeof(type), __FILE__, __LINE__); \ - cosmoM_reallocate(state, x, sizeof(type), 0, true) + cosmoM_reallocate(state, x, sizeof(type), 0) #else -# define cosmoM_free(state, type, x) cosmoM_reallocate(state, x, sizeof(type), 0, true) +# define cosmoM_free(state, type, x) cosmoM_reallocate(state, x, sizeof(type), 0) #endif #define cosmoM_isFrozen(state) (state->freezeGC > 0) +// cosmoM_freezeGC should only be used in the garbage collector ! // if debugging, print the locations of when the state is frozen/unfrozen #ifdef GC_DEBUG # define cosmoM_freezeGC(state) \ @@ -69,8 +61,7 @@ #endif -COSMO_API void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize, - bool isGC); +COSMO_API void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize); COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed); // returns true if GC event was triggered COSMO_API void cosmoM_collectGarbage(CState *state); @@ -86,7 +77,7 @@ COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot); // wrapper for cosmoM_reallocate so we can track our memory usage static inline void *cosmoM_xmalloc(CState *state, size_t sz) { - return cosmoM_reallocate(state, NULL, 0, sz, true); + return cosmoM_reallocate(state, NULL, 0, sz); } #endif diff --git a/src/cstate.h b/src/cstate.h index f895957..f000672 100644 --- a/src/cstate.h +++ b/src/cstate.h @@ -44,33 +44,32 @@ typedef struct CPanic { jmp_buf jmp; StkPtr top; - int frameCount; struct CPanic *prev; + int frameCount; } CPanic; struct CState { - int freezeGC; // when > 0, GC events will be ignored (for internal use) - int frameCount; - CPanic *panic; + CCallFrame callFrame[FRAME_MAX]; // call frames + CValue stack[STACK_MAX]; // stack + CObjObject *protoObjects[COBJ_MAX]; // proto object for each COBJ type [NULL = no default proto] + CObjString *iStrings[ISTRING_MAX]; // strings used internally by the VM, eg. __init, __index + CTable strings; + ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but + // *have been* found + CObjUpval *openUpvalues; // tracks all of our still open (meaning still on the stack) upvalues + CObjTable *globals; + CValue *top; // top of the stack 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 - ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but - // *have been* found + CPanic *panic; + + int freezeGC; // when > 0, GC events will be ignored (for internal use) + int frameCount; size_t allocatedBytes; size_t nextGC; // when allocatedBytes reaches this threshhold, trigger a GC event - - CObjUpval *openUpvalues; // tracks all of our still open (meaning still on the stack) upvalues - CTable strings; - CObjTable *globals; - - CValue *top; // top of the stack - CObjObject *protoObjects[COBJ_MAX]; // proto object for each COBJ type [NULL = no default proto] - CObjString *iStrings[ISTRING_MAX]; // strings used internally by the VM, eg. __init, __index - CCallFrame callFrame[FRAME_MAX]; // call frames - CValue stack[STACK_MAX]; // stack }; CPanic *cosmoV_newPanic(CState *state);