Compare commits

..

17 Commits

Author SHA1 Message Date
bfde2d25cf Merge branch 'main' into error-handling 2023-08-30 12:03:01 -05:00
97d40765ce more refactoring; things seem to work fine
all example scripts run fine with GC_STRESS enabled
2023-08-30 12:00:52 -05:00
37e4653d40 don't freezeGC during GC cycle 2023-08-29 23:32:25 -05:00
1ae473383d fix more GC bugs 2023-08-29 23:21:52 -05:00
6ed5589513 fix cparse.c gc bug 2023-08-29 23:01:47 -05:00
1408a07b23 fix this test script 2023-08-29 16:51:04 -05:00
27818b3788 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.
2023-08-29 16:48:38 -05:00
37e42eb60b fixed minor memory leak in cparse
we keep track of internal values used by the parser by pushing them onto the stack
and popping them off once complete.
2023-08-29 15:27:22 -05:00
cd3047c271 WIP: removed stale error handling
currently, scripts seem to run fine. however I'm a bit worried about stack related issues. maybe i'll need to reset state->top as well? but not entirely sure
2023-08-29 14:07:45 -05:00
f26376e6f5 WIP: major error handling refactoring
switching to setjmp instead of the really bad global 'panic' flag
2023-08-28 21:13:00 -05:00
409937c1fa fix vm.collect()
we don't freeze the vm on entry to C functions now
2023-08-26 15:03:56 -05:00
1d2ba217af minor refactoring 2023-08-25 23:34:21 -05:00
e28ffe1c6c refactored cobj.c:printObject()
uses obj type strings from cosmoO_typeStr
2023-08-25 21:28:41 -05:00
9c5270124d finally fixed this memory bug
we were accidentally tracking frees of stuff that was never
allocated lol
2023-08-25 21:22:10 -05:00
5fc9af5564 more debug related refactoring 2023-08-25 20:44:24 -05:00
a1c58647ba minor CTable refactoring 2023-08-25 19:57:16 -05:00
5c3e24fc39 refactoring and cleanup
cosmoB_loadOSLib -> cosmoB_loadOS
2023-08-24 23:36:32 -05:00
22 changed files with 322 additions and 436 deletions

View File

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

View File

@@ -6,7 +6,7 @@ end
// instance of test // instance of test
let obj = test() let obj = test()
test.__index = function(self, key) test.__index = func(self, key)
print("__index called!") print("__index called!")
if (key == "lol") then if (key == "lol") then
return 9001 return 9001

36
main.c
View File

@@ -45,22 +45,20 @@ int cosmoB_input(CState *state, int nargs, CValue *args)
static bool interpret(CState *state, const char *script, const char *mod) static bool interpret(CState *state, const char *script, const char *mod)
{ {
bool ret;
// cosmoV_compileString pushes the result onto the stack (COBJ_ERROR or COBJ_CLOSURE) // cosmoV_compileString pushes the result onto the stack (COBJ_ERROR or COBJ_CLOSURE)
if (cosmoV_compileString(state, script, mod)) { if (cosmoV_compileString(state, script, mod)) {
COSMOVMRESULT res = cosmoV_call(state, 0, 0); // 0 args being passed, 0 results expected // 0 args being passed, 0 results expected
if (!cosmoV_pcall(state, 0, 0)) {
if (res == COSMOVM_RUNTIME_ERR) cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
cosmoV_printError(state, state->error); return false;
}
} else { } else {
cosmoV_pop(state); // pop the error off the stack cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
cosmoV_printError(state, state->error); return false;
} }
ret = state->panic; return true;
state->panic = false; // so our repl isn't broken
return !ret;
} }
static void repl(CState *state) static void repl(CState *state)
@@ -159,8 +157,7 @@ void compileScript(CState *state, const char *in, const char *out)
CObjFunction *func = cosmoV_readClosure(*cosmoV_getTop(state, 0))->function; CObjFunction *func = cosmoV_readClosure(*cosmoV_getTop(state, 0))->function;
cosmoD_dump(state, func, fileWriter, (void *)fout); cosmoD_dump(state, func, fileWriter, (void *)fout);
} else { } else {
cosmoV_pop(state); // pop the error off the stack cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
cosmoV_printError(state, state->error);
} }
free(script); free(script);
@@ -171,20 +168,15 @@ void compileScript(CState *state, const char *in, const char *out)
void loadScript(CState *state, const char *in) void loadScript(CState *state, const char *in)
{ {
FILE *file; FILE *file = fopen(in, "rb");
file = fopen(in, "rb");
if (!cosmoV_undump(state, fileReader, file)) { if (!cosmoV_undump(state, fileReader, file)) {
cosmoV_pop(state); // pop the error off the stack cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
cosmoV_printError(state, state->error);
return; return;
}; };
printf("[!] loaded %s!\n", in); printf("[!] loaded %s!\n", in);
COSMOVMRESULT res = cosmoV_call(state, 0, 0); // 0 args being passed, 0 results expected if (!cosmoV_pcall(state, 0, 0))
cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
if (res == COSMOVM_RUNTIME_ERR)
cosmoV_printError(state, state->error);
fclose(file); fclose(file);
} }
@@ -203,7 +195,7 @@ int main(int argc, char *const argv[])
{ {
CState *state = cosmoV_newState(); CState *state = cosmoV_newState();
cosmoB_loadLibrary(state); cosmoB_loadLibrary(state);
cosmoB_loadOSLib(state); cosmoB_loadOS(state);
cosmoB_loadVM(state); cosmoB_loadVM(state);
int opt; int opt;

View File

@@ -30,7 +30,6 @@ int cosmoB_assert(CState *state, int nargs, CValue *args)
{ {
if (nargs < 1 || nargs > 2) { if (nargs < 1 || nargs > 2) {
cosmoV_error(state, "assert() expected 1 or 2 arguments, got %d!", nargs); 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]))) { 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 { } else {
cosmoV_typeError(state, "assert()", "<boolean>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "assert()", "<boolean>", "%s", cosmoV_typeStr(args[0]));
} }
return 0;
} }
if (!cosmoV_readBoolean(args[0])) // expression passed was false, error! 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) { if (nargs != 1) {
cosmoV_error(state, "type() expected 1 argument, got %d!", nargs); cosmoV_error(state, "type() expected 1 argument, got %d!", nargs);
return 0;
} }
// push the type string to the stack // push the type string to the stack
@@ -65,20 +62,14 @@ int cosmoB_pcall(CState *state, int nargs, CValue *args)
{ {
if (nargs < 1) { if (nargs < 1) {
cosmoV_error(state, "pcall() expected at least 1 argument!"); cosmoV_error(state, "pcall() expected at least 1 argument!");
return 0;
} }
// unfreeze the state GC before calling the function // call the passed callable, the passed arguments are already in the
cosmoM_unfreezeGC(state); // proper order lol, so we can just call it
bool res = cosmoV_pcall(state, nargs - 1, 1);
// call the passed callable
COSMOVMRESULT res = cosmoV_pcall(state, nargs - 1, 1);
// insert false before the result // insert false before the result
cosmo_insert(state, 0, cosmoV_newBoolean(res == COSMOVM_OK)); cosmo_insert(state, 0, cosmoV_newBoolean(res));
// refreeze the state GC so it can be properly unfrozen
cosmoM_freezeGC(state);
return 2; return 2;
} }
@@ -86,7 +77,6 @@ int cosmoB_tonumber(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "tonumber() expected 1 argument, got %d!", nargs); cosmoV_error(state, "tonumber() expected 1 argument, got %d!", nargs);
return 0;
} }
cosmoV_pushNumber(state, cosmoV_toNumber(state, args[0])); cosmoV_pushNumber(state, cosmoV_toNumber(state, args[0]));
@@ -95,10 +85,8 @@ int cosmoB_tonumber(CState *state, int nargs, CValue *args)
int cosmoB_tostring(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); cosmoV_error(state, "tostring() expected 1 argument, got %d!", nargs);
return 0;
}
cosmoV_pushRef(state, (CObj *)cosmoV_toString(state, args[0])); cosmoV_pushRef(state, (CObj *)cosmoV_toString(state, args[0]));
return 1; return 1;
@@ -108,12 +96,10 @@ int cosmoB_loadstring(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "loadstring() expected 1 argument, got %d!", nargs); cosmoV_error(state, "loadstring() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_STRING(args[0])) { if (!IS_STRING(args[0])) {
cosmoV_typeError(state, "loadstring()", "<string>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "loadstring()", "<string>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
CObjString *str = cosmoV_readString(args[0]); CObjString *str = cosmoV_readString(args[0]);
@@ -127,12 +113,10 @@ int cosmoB_error(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "error() expected 1 argument, got %d!", nargs); cosmoV_error(state, "error() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_STRING(args[0])) { if (!IS_STRING(args[0])) {
cosmoV_typeError(state, "error()", "<string>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "error()", "<string>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
cosmoV_error(state, "%s", cosmoV_readCString(args[0])); cosmoV_error(state, "%s", cosmoV_readCString(args[0]));
@@ -181,10 +165,8 @@ int cosmoB_osetProto(CState *state, int nargs, CValue *args)
int cosmoB_ogetProto(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); 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 cosmoV_pushRef(state, (CObj *)cosmoV_readObject(args[0])->_obj.proto); // just return the proto
@@ -195,13 +177,11 @@ int cosmoB_oisChild(CState *state, int nargs, CValue *args)
{ {
if (nargs != 2) { if (nargs != 2) {
cosmoV_error(state, "object.ischild() expected 2 arguments, got %d!", nargs); cosmoV_error(state, "object.ischild() expected 2 arguments, got %d!", nargs);
return 0;
} }
if (!IS_REF(args[0]) || !IS_OBJECT(args[1])) { if (!IS_REF(args[0]) || !IS_OBJECT(args[1])) {
cosmoV_typeError(state, "object.ischild()", "<reference obj>, <object>", "%s, %s", cosmoV_typeError(state, "object.ischild()", "<reference obj>, <object>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
return 0;
} }
CObj *obj = cosmoV_readRef(args[0]); CObj *obj = cosmoV_readRef(args[0]);
@@ -260,12 +240,10 @@ int cosmoB_osRead(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "os.read() expected 1 argument, got %d!", nargs); cosmoV_error(state, "os.read() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_STRING(args[0])) { if (!IS_STRING(args[0])) {
cosmoV_typeError(state, "os.read()", "<string>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "os.read()", "<string>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
CObjString *str = cosmoV_readString(args[0]); CObjString *str = cosmoV_readString(args[0]);
@@ -306,7 +284,6 @@ int cosmoB_osTime(CState *state, int nargs, CValue *args)
struct timeval time; struct timeval time;
if (nargs > 0) { if (nargs > 0) {
cosmoV_error(state, "os.time() expected no arguments, got %d!", nargs); cosmoV_error(state, "os.time() expected no arguments, got %d!", nargs);
return 0;
} }
gettimeofday(&time, NULL); gettimeofday(&time, NULL);
@@ -319,12 +296,10 @@ int cosmoB_osSystem(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "os.system() expects 1 argument, got %d!", nargs); cosmoV_error(state, "os.system() expects 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_STRING(args[0])) { if (!IS_STRING(args[0])) {
cosmoV_typeError(state, "os.system()", "<string>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "os.system()", "<string>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
// run the command and return the exit code // run the command and return the exit code
@@ -332,7 +307,7 @@ int cosmoB_osSystem(CState *state, int nargs, CValue *args)
return 1; return 1;
} }
COSMO_API void cosmoB_loadOSLib(CState *state) COSMO_API void cosmoB_loadOS(CState *state)
{ {
const char *identifiers[] = {"read", "time", "system"}; const char *identifiers[] = {"read", "time", "system"};
@@ -359,7 +334,6 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args)
if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) { if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) {
cosmoV_typeError(state, "string.sub()", "<string>, <number>", "%s, %s", cosmoV_typeError(state, "string.sub()", "<string>, <number>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
return 0;
} }
CObjString *str = cosmoV_readString(args[0]); CObjString *str = cosmoV_readString(args[0]);
@@ -369,7 +343,6 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args)
if (indx < 0 || indx >= str->length) { if (indx < 0 || indx >= str->length) {
cosmoV_error(state, "string.sub() expected index to be 0-%d, got %d!", str->length - 1, cosmoV_error(state, "string.sub() expected index to be 0-%d, got %d!", str->length - 1,
indx); indx);
return 0;
} }
cosmoV_pushLString(state, str->str + ((int)indx), str->length - ((int)indx)); cosmoV_pushLString(state, str->str + ((int)indx), str->length - ((int)indx));
@@ -378,7 +351,6 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args)
cosmoV_typeError(state, "string.sub()", "<string>, <number>, <number>", "%s, %s, %s", cosmoV_typeError(state, "string.sub()", "<string>, <number>, <number>", "%s, %s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]),
cosmoV_typeStr(args[2])); cosmoV_typeStr(args[2]));
return 0;
} }
CObjString *str = cosmoV_readString(args[0]); CObjString *str = cosmoV_readString(args[0]);
@@ -390,13 +362,11 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args)
cosmoV_error( cosmoV_error(
state, "string.sub() expected subbed string goes out of bounds, max length is %d!", state, "string.sub() expected subbed string goes out of bounds, max length is %d!",
str->length); str->length);
return 0;
} }
cosmoV_pushLString(state, str->str + ((int)indx), ((int)length)); cosmoV_pushLString(state, str->str + ((int)indx), ((int)length));
} else { } else {
cosmoV_error(state, "string.sub() expected 2 or 3 arguments, got %d!", nargs); cosmoV_error(state, "string.sub() expected 2 or 3 arguments, got %d!", nargs);
return 0;
} }
return 1; return 1;
@@ -409,7 +379,6 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args)
if (!IS_STRING(args[0]) || !IS_STRING(args[1])) { if (!IS_STRING(args[0]) || !IS_STRING(args[1])) {
cosmoV_typeError(state, "string.find()", "<string>, <string>", "%s, %s", cosmoV_typeError(state, "string.find()", "<string>, <string>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
return 0;
} }
CObjString *str = cosmoV_readString(args[0]); CObjString *str = cosmoV_readString(args[0]);
@@ -430,7 +399,6 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args)
cosmoV_typeError(state, "string.find()", "<string>, <string>, <number>", "%s, %s, %s", cosmoV_typeError(state, "string.find()", "<string>, <string>, <number>", "%s, %s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]),
cosmoV_typeStr(args[2])); cosmoV_typeStr(args[2]));
return 0;
} }
CObjString *str = cosmoV_readString(args[0]); CObjString *str = cosmoV_readString(args[0]);
@@ -449,7 +417,6 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args)
cosmoV_pushNumber(state, (cosmo_Number)(indx - str->str)); cosmoV_pushNumber(state, (cosmo_Number)(indx - str->str));
} else { } else {
cosmoV_error(state, "string.find() expected 2 or 3 arguments, got %d!", nargs); cosmoV_error(state, "string.find() expected 2 or 3 arguments, got %d!", nargs);
return 0;
} }
return 1; return 1;
@@ -460,13 +427,11 @@ int cosmoB_sSplit(CState *state, int nargs, CValue *args)
{ {
if (nargs != 2) { if (nargs != 2) {
cosmoV_error(state, "string.split() expected 2 arguments, got %d!", nargs); cosmoV_error(state, "string.split() expected 2 arguments, got %d!", nargs);
return 0;
} }
if (!IS_STRING(args[0]) || !IS_STRING(args[1])) { if (!IS_STRING(args[0]) || !IS_STRING(args[1])) {
cosmoV_typeError(state, "string.split()", "<string>, <string>", "%s, %s", cosmoV_typeError(state, "string.split()", "<string>, <string>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
return 0;
} }
CObjString *str = cosmoV_readString(args[0]); CObjString *str = cosmoV_readString(args[0]);
@@ -497,12 +462,10 @@ int cosmoB_sByte(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "string.byte() expected 1 argument, got %d!", nargs); cosmoV_error(state, "string.byte() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_STRING(args[0])) { if (!IS_STRING(args[0])) {
cosmoV_typeError(state, "string.byte", "<string>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "string.byte", "<string>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
CObjString *str = cosmoV_readString(args[0]); CObjString *str = cosmoV_readString(args[0]);
@@ -523,12 +486,10 @@ int cosmoB_sChar(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "string.char() expected 1 argument, got %d!", nargs); cosmoV_error(state, "string.char() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[0])) { if (!IS_NUMBER(args[0])) {
cosmoV_typeError(state, "string.char", "<number>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "string.char", "<number>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
// small side effect of truncating the number, but ignoring the decimal instead of throwing an // small side effect of truncating the number, but ignoring the decimal instead of throwing an
@@ -538,7 +499,6 @@ int cosmoB_sChar(CState *state, int nargs, CValue *args)
if (num > 255 || num < 0) { if (num > 255 || num < 0) {
cosmoV_error(state, "Character expected to be in range 0-255, got %d!", num); 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 // basically, treat the character value on the C stack as an """"array"""" with a length of 1
@@ -550,12 +510,10 @@ int cosmoB_sLen(CState *state, int nargs, CValue *args)
{ {
if (nargs < 1) { if (nargs < 1) {
cosmoV_error(state, "string.len() expected 1 argument, got %d!", nargs); cosmoV_error(state, "string.len() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_STRING(args[0])) { if (!IS_STRING(args[0])) {
cosmoV_typeError(state, "string.len", "<string>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "string.len", "<string>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
cosmoV_pushNumber(state, (cosmo_Number)strlen(cosmoV_readCString(args[0]))); cosmoV_pushNumber(state, (cosmo_Number)strlen(cosmoV_readCString(args[0])));
@@ -567,14 +525,12 @@ int cosmoB_sRep(CState *state, int nargs, CValue *args)
{ {
if (nargs != 2) { if (nargs != 2) {
cosmoV_error(state, "string.rep() expected 2 arguments, got %d!", nargs); cosmoV_error(state, "string.rep() expected 2 arguments, got %d!", nargs);
return 0;
} }
// expects <string>, <number> // expects <string>, <number>
if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) { if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) {
cosmoV_typeError(state, "string.rep", "<string>, <number>", "%s, %s", cosmoV_typeError(state, "string.rep", "<string>, <number>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
return 0;
} }
CObjString *str = cosmoV_readString(args[0]); CObjString *str = cosmoV_readString(args[0]);
@@ -632,12 +588,10 @@ int cosmoB_mAbs(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.abs() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.abs() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[0])) { if (!IS_NUMBER(args[0])) {
cosmoV_typeError(state, "math.abs", "<number>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "math.abs", "<number>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
cosmoV_pushNumber(state, fabs(cosmoV_readNumber(args[0]))); cosmoV_pushNumber(state, fabs(cosmoV_readNumber(args[0])));
@@ -649,12 +603,10 @@ int cosmoB_mFloor(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.floor() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.floor() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[0])) { if (!IS_NUMBER(args[0])) {
cosmoV_typeError(state, "math.floor", "<number>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "math.floor", "<number>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
cosmoV_pushNumber(state, (int)cosmoV_readNumber(args[0])); cosmoV_pushNumber(state, (int)cosmoV_readNumber(args[0]));
@@ -666,12 +618,10 @@ int cosmoB_mCeil(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.ceil() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.ceil() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[0])) { if (!IS_NUMBER(args[0])) {
cosmoV_typeError(state, "math.ceil", "<number>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "math.ceil", "<number>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
int roundedDown = (int)cosmoV_readNumber(args[0]); int roundedDown = (int)cosmoV_readNumber(args[0]);
@@ -690,12 +640,10 @@ int cosmoB_mSin(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.sin() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.sin() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[0])) { if (!IS_NUMBER(args[0])) {
cosmoV_typeError(state, "math.sin", "<number>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "math.sin", "<number>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
cosmoV_pushNumber(state, sin(cosmoV_readNumber(args[0]))); cosmoV_pushNumber(state, sin(cosmoV_readNumber(args[0])));
@@ -706,12 +654,10 @@ int cosmoB_mCos(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.cos() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.cos() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[0])) { if (!IS_NUMBER(args[0])) {
cosmoV_typeError(state, "math.cos", "<number>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "math.cos", "<number>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
cosmoV_pushNumber(state, cos(cosmoV_readNumber(args[0]))); cosmoV_pushNumber(state, cos(cosmoV_readNumber(args[0])));
@@ -722,12 +668,10 @@ int cosmoB_mTan(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.tan() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.tan() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[0])) { if (!IS_NUMBER(args[0])) {
cosmoV_typeError(state, "math.tan", "<number>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "math.tan", "<number>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
cosmoV_pushNumber(state, tan(cosmoV_readNumber(args[0]))); cosmoV_pushNumber(state, tan(cosmoV_readNumber(args[0])));
@@ -738,12 +682,10 @@ int cosmoB_mASin(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.asin() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.asin() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[0])) { if (!IS_NUMBER(args[0])) {
cosmoV_typeError(state, "math.asin", "<number>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "math.asin", "<number>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
cosmoV_pushNumber(state, asin(cosmoV_readNumber(args[0]))); cosmoV_pushNumber(state, asin(cosmoV_readNumber(args[0])));
@@ -754,12 +696,10 @@ int cosmoB_mACos(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.acos() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.acos() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[0])) { if (!IS_NUMBER(args[0])) {
cosmoV_typeError(state, "math.acos", "<number>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "math.acos", "<number>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
cosmoV_pushNumber(state, acos(cosmoV_readNumber(args[0]))); cosmoV_pushNumber(state, acos(cosmoV_readNumber(args[0])));
@@ -770,12 +710,10 @@ int cosmoB_mATan(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.atan() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.atan() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[0])) { if (!IS_NUMBER(args[0])) {
cosmoV_typeError(state, "math.atan", "<number>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "math.atan", "<number>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
cosmoV_pushNumber(state, atan(cosmoV_readNumber(args[0]))); cosmoV_pushNumber(state, atan(cosmoV_readNumber(args[0])));
@@ -786,12 +724,10 @@ int cosmoB_mRad(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.rad() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.rad() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[0])) { if (!IS_NUMBER(args[0])) {
cosmoV_typeError(state, "math.rad", "<number>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "math.rad", "<number>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
// convert the degree to radians // convert the degree to radians
@@ -803,12 +739,10 @@ int cosmoB_mDeg(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.deg() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.deg() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[0])) { if (!IS_NUMBER(args[0])) {
cosmoV_typeError(state, "math.deg", "<number>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "math.deg", "<number>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
// convert the degree to radians // convert the degree to radians
@@ -857,13 +791,11 @@ int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args)
{ {
if (nargs != 2) { if (nargs != 2) {
cosmoV_error(state, "Expected 2 argumenst, got %d!", nargs); cosmoV_error(state, "Expected 2 argumenst, got %d!", nargs);
return 0;
} }
if (!IS_TABLE(args[1])) { if (!IS_TABLE(args[1])) {
cosmoV_typeError(state, "vm.__setter[\"globals\"]", "<object>, <table>", "%s, %s", cosmoV_typeError(state, "vm.__setter[\"globals\"]", "<object>, <table>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
return 0;
} }
// this makes me very nervous ngl // this makes me very nervous ngl
@@ -879,13 +811,11 @@ int cosmoB_vdisassemble(CState *state, int nargs, CValue *args)
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "Expected 1 argument, got %d!", nargs); cosmoV_error(state, "Expected 1 argument, got %d!", nargs);
return 0;
} }
// get the closure // get the closure
if (!IS_CLOSURE(args[0])) { if (!IS_CLOSURE(args[0])) {
cosmoV_typeError(state, "vm.disassemble", "<closure>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "vm.disassemble", "<closure>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
closure = cosmoV_readClosure(args[0]); closure = cosmoV_readClosure(args[0]);
@@ -900,13 +830,11 @@ int cosmoB_vindexBProto(CState *state, int nargs, CValue *args)
{ {
if (nargs != 2) { if (nargs != 2) {
cosmoV_error(state, "Expected 2 arguments, got %d!", nargs); cosmoV_error(state, "Expected 2 arguments, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[1])) { if (!IS_NUMBER(args[1])) {
cosmoV_typeError(state, "baseProtos.__index", "<object>, <number>", "%s, %s", cosmoV_typeError(state, "baseProtos.__index", "<object>, <number>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
return 0;
} }
int indx = (int)cosmoV_readNumber(args[1]); int indx = (int)cosmoV_readNumber(args[1]);
@@ -928,14 +856,12 @@ int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args)
{ {
if (nargs != 3) { if (nargs != 3) {
cosmoV_error(state, "Expected 3 arguments, got %d!", nargs); cosmoV_error(state, "Expected 3 arguments, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[1]) || !IS_OBJECT(args[2])) { if (!IS_NUMBER(args[1]) || !IS_OBJECT(args[2])) {
cosmoV_typeError(state, "baseProtos.__newindex", "<object>, <number>, <object>", cosmoV_typeError(state, "baseProtos.__newindex", "<object>, <number>, <object>",
"%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]),
cosmoV_typeStr(args[2])); cosmoV_typeStr(args[2]));
return 0;
} }
int indx = (int)cosmoV_readNumber(args[1]); int indx = (int)cosmoV_readNumber(args[1]);
@@ -943,7 +869,6 @@ int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args)
if (indx >= COBJ_MAX || indx < 0) { if (indx >= COBJ_MAX || indx < 0) {
cosmoV_error(state, "index out of range! expected 0 - %d, got %d!", COBJ_MAX, indx); cosmoV_error(state, "index out of range! expected 0 - %d, got %d!", COBJ_MAX, indx);
return 0;
} }
cosmoV_registerProtoObject(state, indx, proto); cosmoV_registerProtoObject(state, indx, proto);
@@ -953,16 +878,8 @@ int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args)
// vm.collect() // vm.collect()
int cosmoB_vcollect(CState *state, int nargs, CValue *args) int cosmoB_vcollect(CState *state, int nargs, CValue *args)
{ {
// first, unfreeze the state (we start frozen on entry to any C Function) // force a garbage collection
cosmoM_unfreezeGC(state);
// now force a garbage collection
cosmoM_collectGarbage(state); cosmoM_collectGarbage(state);
// and re-freeze the state
cosmoM_freezeGC(state);
// the end!
return 0; return 0;
} }

View File

@@ -22,7 +22,7 @@ COSMO_API void cosmoB_loadObjLib(CState *state);
- os.system() - os.system()
- os.time() - os.time()
*/ */
COSMO_API void cosmoB_loadOSLib(CState *state); COSMO_API void cosmoB_loadOS(CState *state);
/* loads the base string library, including: /* loads the base string library, including:
- string.sub & <string>:sub() - string.sub & <string>:sub()

View File

@@ -27,9 +27,9 @@ void initChunk(CState *state, CChunk *chunk, size_t startCapacity)
void cleanChunk(CState *state, CChunk *chunk) void cleanChunk(CState *state, CChunk *chunk)
{ {
// first, free the chunk buffer // 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 // then the line info
cosmoM_freearray(state, int, chunk->lineInfo, chunk->capacity); cosmoM_freeArray(state, int, chunk->lineInfo, chunk->capacity);
// free the constants // free the constants
cleanValArray(state, &chunk->constants); cleanValArray(state, &chunk->constants);
} }
@@ -49,9 +49,10 @@ int addConstant(CState *state, CChunk *chunk, CValue value)
return i; // we already have a matching constant! return i; // we already have a matching constant!
} }
cosmoM_freezeGC(state); // so our GC doesn't free it cosmoV_pushValue(state, value); // push the value to the stack so our GC can see it
appendValArray(state, &chunk->constants, value); appendValArray(state, &chunk->constants, value);
cosmoM_unfreezeGC(state); cosmoV_pop(state);
return chunk->constants.count - 1; // return the index of the new constants return chunk->constants.count - 1; // return the index of the new constants
} }
@@ -60,8 +61,8 @@ int addConstant(CState *state, CChunk *chunk, CValue value)
void writeu8Chunk(CState *state, CChunk *chunk, INSTRUCTION i, int line) void writeu8Chunk(CState *state, CChunk *chunk, INSTRUCTION i, int line)
{ {
// does the buffer need to be reallocated? // does the buffer need to be reallocated?
cosmoM_growarray(state, INSTRUCTION, chunk->buf, chunk->count, chunk->capacity); cosmoM_growArray(state, INSTRUCTION, chunk->buf, chunk->count, chunk->capacity);
cosmoM_growarray(state, int, chunk->lineInfo, chunk->count, chunk->lineCapacity); cosmoM_growArray(state, int, chunk->lineInfo, chunk->count, chunk->lineCapacity);
// write data to the chunk :) // write data to the chunk :)
chunk->lineInfo[chunk->count] = line; chunk->lineInfo[chunk->count] = line;

View File

@@ -21,6 +21,8 @@ void cleanChunk(CState *state, CChunk *chunk); // frees everything but the struc
void freeChunk(CState *state, CChunk *chunk); // frees everything including the struct void freeChunk(CState *state, CChunk *chunk); // frees everything including the struct
int addConstant(CState *state, CChunk *chunk, CValue value); int addConstant(CState *state, CChunk *chunk, CValue value);
bool validateChunk(CState *state, CChunk *chunk);
// write to chunk // write to chunk
void writeu8Chunk(CState *state, CChunk *chunk, INSTRUCTION i, int line); void writeu8Chunk(CState *state, CChunk *chunk, INSTRUCTION i, int line);
void writeu16Chunk(CState *state, CChunk *chunk, uint16_t i, int line); void writeu16Chunk(CState *state, CChunk *chunk, uint16_t i, int line);

View File

@@ -54,7 +54,7 @@ static void resetBuffer(CLexState *state)
// cancels the token heap buffer and frees it // cancels the token heap buffer and frees it
static void freeBuffer(CLexState *state) 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); resetBuffer(state);
} }
@@ -62,7 +62,7 @@ static void freeBuffer(CLexState *state)
// adds character to buffer // adds character to buffer
static void appendBuffer(CLexState *state, char c) 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; state->buffer[state->bufCount++] = c;
} }
@@ -389,9 +389,8 @@ static CToken parseIdentifier(CLexState *state)
return makeToken(state, identifierType(state)); // is it a reserved word? 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->startChar = (char *)source;
state->currentChar = (char *)source; state->currentChar = (char *)source;
state->line = 1; state->line = 1;
@@ -400,13 +399,11 @@ CLexState *cosmoL_newLexState(CState *cstate, const char *source)
state->cstate = cstate; state->cstate = cstate;
resetBuffer(state); 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) CToken cosmoL_scanToken(CLexState *state)

View File

@@ -103,8 +103,8 @@ typedef struct
CState *cstate; CState *cstate;
} CLexState; } CLexState;
CLexState *cosmoL_newLexState(CState *state, const char *source); void cosmoL_initLexState(CState *cstate, CLexState *state, const char *source);
void cosmoL_freeLexState(CState *state, CLexState *lstate); void cosmoL_cleanupLexState(CState *state, CLexState *lstate);
CToken cosmoL_scanToken(CLexState *state); CToken cosmoL_scanToken(CLexState *state);

View File

@@ -10,7 +10,24 @@
// realloc wrapper // 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)
{ {
if (buf == NULL)
oldSize = 0;
#ifdef GC_DEBUG
printf("old allocated bytes: %ld\n", state->allocatedBytes);
if (buf) {
if (newSize == 0) {
printf("freeing %p, reclaiming %ld bytes...\n", buf, oldSize);
} else {
printf("realloc %p, byte difference: %ld\n", buf, newSize - oldSize);
}
}
#endif
state->allocatedBytes += newSize - oldSize; state->allocatedBytes += newSize - oldSize;
#ifdef GC_DEBUG
printf("new allocated bytes: %ld\n", state->allocatedBytes);
fflush(stdout);
#endif
if (newSize == 0) { // it needs to be freed if (newSize == 0) { // it needs to be freed
free(buf); free(buf);
@@ -33,6 +50,11 @@ void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize
// if NULL is passed, realloc() acts like malloc() // if NULL is passed, realloc() acts like malloc()
void *newBuf = realloc(buf, newSize); void *newBuf = realloc(buf, newSize);
#ifdef GC_DEBUG
printf("allocating new buffer of size %ld at %p\n", newSize - oldSize, newBuf);
fflush(stdout);
#endif
if (newBuf == NULL) { if (newBuf == NULL) {
CERROR("failed to allocate memory!"); CERROR("failed to allocate memory!");
exit(1); exit(1);
@@ -59,7 +81,7 @@ static void markTable(CState *state, CTable *tbl)
if (tbl->table == NULL) // table is still being initialized if (tbl->table == NULL) // table is still being initialized
return; return;
int cap = tbl->capacityMask + 1; int cap = cosmoT_getCapacity(tbl);
for (int i = 0; i < cap; i++) { for (int i = 0; i < cap; i++) {
CTableEntry *entry = &tbl->table[i]; CTableEntry *entry = &tbl->table[i];
markValue(state, entry->key); markValue(state, entry->key);
@@ -67,13 +89,18 @@ static void markTable(CState *state, CTable *tbl)
} }
} }
// frees white members from the table // removes white members from the table
static void tableRemoveWhite(CState *state, CTable *tbl) static void tableRemoveWhite(CState *state, CTable *tbl)
{ {
if (tbl->table == NULL) // table is still being initialized if (tbl->table == NULL) // table is still being initialized
return; return;
int cap = tbl->capacityMask + 1; int cap = cosmoT_getCapacity(tbl);
#ifdef GC_DEBUG
printf("tableRemoveWhite: %p, cap: %d\n", tbl, cap);
#endif
for (int i = 0; i < cap; i++) { for (int i = 0; i < cap; i++) {
CTableEntry *entry = &tbl->table[i]; CTableEntry *entry = &tbl->table[i];
if (IS_REF(entry->key) && if (IS_REF(entry->key) &&
@@ -179,7 +206,7 @@ static void markObject(CState *state, CObj *obj)
return; return;
// we can use cosmoM_growarray because we lock the GC when we entered in cosmoM_collectGarbage // 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, cosmoM_growArray(state, CObj *, state->grayStack.array, state->grayStack.count,
state->grayStack.capacity); state->grayStack.capacity);
state->grayStack.array[state->grayStack.count++] = obj; state->grayStack.array[state->grayStack.count++] = obj;
@@ -261,9 +288,6 @@ static void markRoots(CState *state)
// mark the user defined roots // mark the user defined roots
markUserRoots(state); markUserRoots(state);
// mark other misc. internally reserved objects
markObject(state, (CObj *)state->error);
for (int i = 0; i < COBJ_MAX; i++) for (int i = 0; i < COBJ_MAX; i++)
markObject(state, (CObj *)state->protoObjects[i]); markObject(state, (CObj *)state->protoObjects[i]);
@@ -272,12 +296,11 @@ static void markRoots(CState *state)
COSMO_API void cosmoM_collectGarbage(CState *state) COSMO_API void cosmoM_collectGarbage(CState *state)
{ {
cosmoM_freezeGC(state);
#ifdef GC_DEBUG #ifdef GC_DEBUG
printf("-- GC start\n"); printf("-- GC start\n");
size_t start = state->allocatedBytes; size_t start = state->allocatedBytes;
#endif #endif
cosmoM_freezeGC(state); // we don't want a recursive garbage collection event!
markRoots(state); markRoots(state);
tableRemoveWhite( tableRemoveWhite(
@@ -288,15 +311,12 @@ COSMO_API void cosmoM_collectGarbage(CState *state)
// set our next GC event // set our next GC event
cosmoM_updateThreshhold(state); 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 #ifdef GC_DEBUG
printf("-- GC end, reclaimed %ld bytes (started at %ld, ended at %ld), next garbage collection " printf("-- GC end, reclaimed %ld bytes (started at %ld, ended at %ld), next garbage collection "
"scheduled at %ld bytes\n", "scheduled at %ld bytes\n",
start - state->allocatedBytes, start, state->allocatedBytes, state->nextGC); start - state->allocatedBytes, start, state->allocatedBytes, state->nextGC);
getchar(); // pauses execution
#endif #endif
cosmoM_unfreezeGC(state);
} }
COSMO_API void cosmoM_updateThreshhold(CState *state) COSMO_API void cosmoM_updateThreshhold(CState *state)

View File

@@ -12,16 +12,16 @@
#define ARRAY_START 8 #define ARRAY_START 8
#ifdef GC_DEBUG #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__, \ printf("freeing array %p [size %lu] at %s:%d\n", buf, sizeof(type) * capacity, __FILE__, \
__LINE__); \ __LINE__); \
cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0) cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0)
#else #else
# define cosmoM_freearray(state, type, buf, capacity) \ # define cosmoM_freeArray(state, type, buf, capacity) \
cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0) cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0)
#endif #endif
#define cosmoM_growarray(state, type, buf, count, capacity) \ #define cosmoM_growArray(state, type, buf, count, capacity) \
if (count >= capacity || buf == NULL) { \ if (count >= capacity || buf == NULL) { \
int old = capacity; \ int old = capacity; \
capacity = old * GROW_FACTOR; \ capacity = old * GROW_FACTOR; \
@@ -38,6 +38,7 @@
#define cosmoM_isFrozen(state) (state->freezeGC > 0) #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 // if debugging, print the locations of when the state is frozen/unfrozen
#ifdef GC_DEBUG #ifdef GC_DEBUG
# define cosmoM_freezeGC(state) \ # define cosmoM_freezeGC(state) \
@@ -73,7 +74,7 @@ COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot);
// lets the VM know this root is no longer held in a reference and is able to be freed // lets the VM know this root is no longer held in a reference and is able to be freed
COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot); COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot);
// wrapper for cosmoM_reallocate so we can track our memory usage (it's also safer :P) // wrapper for cosmoM_reallocate so we can track our memory usage
static inline void *cosmoM_xmalloc(CState *state, size_t sz) static inline void *cosmoM_xmalloc(CState *state, size_t sz)
{ {
return cosmoM_reallocate(state, NULL, 0, sz); return cosmoM_reallocate(state, NULL, 0, sz);

View File

@@ -33,7 +33,7 @@ CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type)
obj->nextRoot = NULL; obj->nextRoot = NULL;
#ifdef GC_DEBUG #ifdef GC_DEBUG
printf("allocated %p with OBJ_TYPE %d\n", obj, type); printf("allocated %s %p\n", cosmoO_typeStr(obj), obj);
#endif #endif
return obj; return obj;
} }
@@ -41,14 +41,12 @@ CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type)
void cosmoO_free(CState *state, CObj *obj) void cosmoO_free(CState *state, CObj *obj)
{ {
#ifdef GC_DEBUG #ifdef GC_DEBUG
printf("freeing %p [", obj); printf("freeing %s %p\n", cosmoO_typeStr(obj), obj);
printObject(obj);
printf("]\n");
#endif #endif
switch (obj->type) { switch (obj->type) {
case COBJ_STRING: { case COBJ_STRING: {
CObjString *objStr = (CObjString *)obj; 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); cosmoM_free(state, CObjString, objStr);
break; break;
} }
@@ -84,13 +82,13 @@ void cosmoO_free(CState *state, CObj *obj)
} }
case COBJ_ERROR: { case COBJ_ERROR: {
CObjError *err = (CObjError *)obj; 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); cosmoM_free(state, CObjError, obj);
break; break;
} }
case COBJ_CLOSURE: { case COBJ_CLOSURE: {
CObjClosure *closure = (CObjClosure *)obj; 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); cosmoM_free(state, CObjClosure, closure);
break; break;
} }
@@ -166,14 +164,12 @@ _eqFail:
cosmoV_pushValue(state, eq1); cosmoV_pushValue(state, eq1);
cosmoV_pushRef(state, obj1); cosmoV_pushRef(state, obj1);
cosmoV_pushRef(state, obj2); cosmoV_pushRef(state, obj2);
if (cosmoV_call(state, 2, 1) != COSMOVM_OK) cosmoV_call(state, 2, 1);
return false;
// check return value and make sure it's a boolean // check return value and make sure it's a boolean
if (!IS_BOOLEAN(*cosmoV_getTop(state, 0))) { if (!IS_BOOLEAN(*cosmoV_getTop(state, 0))) {
cosmoV_error(state, "__equal expected to return <boolean>, got %s!", cosmoV_error(state, "__equal expected to return <boolean>, got %s!",
cosmoV_typeStr(*cosmoV_pop(state))); cosmoV_typeStr(*cosmoV_pop(state)));
return false;
} }
// return the result // return the result
@@ -190,10 +186,10 @@ CObjObject *cosmoO_newObject(CState *state)
obj->userP = NULL; // reserved for C API obj->userP = NULL; // reserved for C API
obj->userT = 0; obj->userT = 0;
obj->isLocked = false; obj->isLocked = false;
cosmoV_pushRef(state, (CObj *)obj); // so our GC can keep track of it cosmoV_pushRef(state, (CObj *)obj); // so our GC can keep track of it
cosmoT_initTable(state, &obj->tbl, ARRAY_START); cosmoT_initTable(state, &obj->tbl, ARRAY_START);
cosmoV_pop(state); cosmoV_pop(state);
return obj; return obj;
} }
@@ -233,14 +229,13 @@ CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func)
CObjError *cosmoO_newError(CState *state, CValue err) 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); CObjError *cerror = (CObjError *)cosmoO_allocateBase(state, sizeof(CObjError), COBJ_ERROR);
cerror->err = err; cerror->err = err;
cerror->frameCount = state->frameCount; cerror->frameCount = state->frameCount;
cerror->frames = frames;
cerror->parserError = false; cerror->parserError = false;
// allocate the callframe
cerror->frames = cosmoM_xmalloc(state, sizeof(CCallFrame) * cerror->frameCount);
// clone the call frame // clone the call frame
for (int i = 0; i < state->frameCount; i++) for (int i = 0; i < state->frameCount; i++)
cerror->frames[i] = state->callFrame[i]; cerror->frames[i] = state->callFrame[i];
@@ -310,7 +305,7 @@ CObjString *cosmoO_takeString(CState *state, char *str, size_t length)
// have we already interned this string? // have we already interned this string?
if (lookup != NULL) { if (lookup != NULL) {
cosmoM_freearray(state, char, str, cosmoM_freeArray(state, char, str,
length + 1); // free our passed character array, it's unneeded! length + 1); // free our passed character array, it's unneeded!
return lookup; return lookup;
} }
@@ -326,8 +321,7 @@ CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uin
strObj->length = sz; strObj->length = sz;
strObj->hash = hash; strObj->hash = hash;
// we push & pop the string so our GC can find it (we don't use freezeGC/unfreezeGC because we // push/pop to make sure GC doesn't collect it
// *want* a GC event to happen)
cosmoV_pushRef(state, (CObj *)strObj); cosmoV_pushRef(state, (CObj *)strObj);
cosmoT_insert(state, &state->strings, cosmoV_newRef((CObj *)strObj)); cosmoT_insert(state, &state->strings, cosmoV_newRef((CObj *)strObj));
cosmoV_pop(state); cosmoV_pop(state);
@@ -409,11 +403,10 @@ bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *v
val)) { // if the field doesn't exist in the object, check the proto val)) { // if the field doesn't exist in the object, check the proto
if (cosmoO_getIString(state, proto, ISTRING_GETTER, val) && IS_TABLE(*val) && if (cosmoO_getIString(state, proto, ISTRING_GETTER, val) && IS_TABLE(*val) &&
cosmoT_get(state, &cosmoV_readTable(*val)->tbl, key, val)) { cosmoT_get(state, &cosmoV_readTable(*val)->tbl, key, val)) {
cosmoV_pushValue(state, *val); // push function cosmoV_pushValue(state, *val); // push function
cosmoV_pushRef(state, (CObj *)obj); // push object cosmoV_pushRef(state, (CObj *)obj); // push object
if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // call the function with the 1 argument cosmoV_call(state, 1, 1); // call the function with the 1 argument
return false; *val = *cosmoV_pop(state); // set value to the return value of __index
*val = *cosmoV_pop(state); // set value to the return value of __index
return true; return true;
} }
@@ -529,12 +522,11 @@ bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val)
bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val) bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val)
{ {
if (cosmoO_getIString(state, object, ISTRING_INDEX, val)) { if (cosmoO_getIString(state, object, ISTRING_INDEX, val)) {
cosmoV_pushValue(state, *val); // push function cosmoV_pushValue(state, *val); // push function
cosmoV_pushRef(state, (CObj *)object); // push object cosmoV_pushRef(state, (CObj *)object); // push object
cosmoV_pushValue(state, key); // push key cosmoV_pushValue(state, key); // push key
if (cosmoV_call(state, 2, 1) != COSMOVM_OK) // call the function with the 2 arguments cosmoV_call(state, 2, 1); // call the function with the 2 arguments
return false; *val = *cosmoV_pop(state); // set value to the return value of __index
*val = *cosmoV_pop(state); // set value to the return value of __index
return true; return true;
} else { // there's no __index function defined! } else { // there's no __index function defined!
cosmoV_error(state, "Couldn't index object without __index function!"); cosmoV_error(state, "Couldn't index object without __index function!");
@@ -552,7 +544,8 @@ bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue
cosmoV_pushRef(state, (CObj *)object); // push object cosmoV_pushRef(state, (CObj *)object); // push object
cosmoV_pushValue(state, key); // push key & value pair cosmoV_pushValue(state, key); // push key & value pair
cosmoV_pushValue(state, val); cosmoV_pushValue(state, val);
return cosmoV_call(state, 3, 0) == COSMOVM_OK; cosmoV_call(state, 3, 0);
return true;
} else { // there's no __newindex function defined } else { // there's no __newindex function defined
cosmoV_error(state, "Couldn't set index on object without __newindex function!"); cosmoV_error(state, "Couldn't set index on object without __newindex function!");
} }
@@ -569,8 +562,7 @@ CObjString *cosmoO_toString(CState *state, CObj *obj)
if (protoObject != NULL && cosmoO_getIString(state, protoObject, ISTRING_TOSTRING, &res)) { if (protoObject != NULL && cosmoO_getIString(state, protoObject, ISTRING_TOSTRING, &res)) {
cosmoV_pushValue(state, res); cosmoV_pushValue(state, res);
cosmoV_pushRef(state, (CObj *)obj); cosmoV_pushRef(state, (CObj *)obj);
if (cosmoV_call(state, 1, 1) != COSMOVM_OK) cosmoV_call(state, 1, 1);
return cosmoO_copyString(state, "<err>", 5);
// make sure the __tostring function returned a string // make sure the __tostring function returned a string
StkPtr ret = cosmoV_getTop(state, 0); StkPtr ret = cosmoV_getTop(state, 0);
@@ -635,14 +627,12 @@ cosmo_Number cosmoO_toNumber(CState *state, CObj *obj)
if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_TONUMBER, &res)) { if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_TONUMBER, &res)) {
cosmoV_pushValue(state, res); cosmoV_pushValue(state, res);
cosmoV_pushRef(state, (CObj *)obj); cosmoV_pushRef(state, (CObj *)obj);
if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // call res, expect 1 return val of <number> cosmoV_call(state, 1, 1); // call res, expect 1 return val of <number>
return 0;
StkPtr temp = cosmoV_getTop(state, 0); StkPtr temp = cosmoV_getTop(state, 0);
if (!IS_NUMBER(*temp)) { if (!IS_NUMBER(*temp)) {
cosmoV_error(state, "__tonumber expected to return <number>, got %s!", cosmoV_error(state, "__tonumber expected to return <number>, got %s!",
cosmoV_typeStr(*temp)); cosmoV_typeStr(*temp));
return 0;
} }
// return number // return number
@@ -668,9 +658,7 @@ int cosmoO_count(CState *state, CObj *obj)
if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_COUNT, &res)) { if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_COUNT, &res)) {
cosmoV_pushValue(state, res); cosmoV_pushValue(state, res);
cosmoV_pushRef(state, (CObj *)obj); cosmoV_pushRef(state, (CObj *)obj);
if (cosmoV_call(state, 1, 1) != cosmoV_call(state, 1, 1); // call res, we expect 1 return value of type <number>
COSMOVM_OK) // call res, we expect 1 return value of type <number>
return 0;
StkPtr ret = cosmoV_getTop(state, 0); StkPtr ret = cosmoV_getTop(state, 0);
if (!IS_NUMBER(*ret)) { if (!IS_NUMBER(*ret)) {
@@ -701,60 +689,61 @@ int cosmoO_count(CState *state, CObj *obj)
void printObject(CObj *o) void printObject(CObj *o)
{ {
printf("%s ", cosmoO_typeStr(o));
switch (o->type) { switch (o->type) {
case COBJ_STRING: { case COBJ_STRING: {
CObjString *objStr = (CObjString *)o; CObjString *objStr = (CObjString *)o;
printf("<string> \"%.*s\"", objStr->length, objStr->str); printf("\"%.*s\"", objStr->length, objStr->str);
break; break;
} }
case COBJ_OBJECT: { case COBJ_OBJECT: {
printf("<obj> %p", (void *)o); printf("%p", (void *)o);
break; break;
} }
case COBJ_TABLE: { case COBJ_TABLE: {
CObjTable *tbl = (CObjTable *)o; CObjTable *tbl = (CObjTable *)o;
printf("<tbl> %p", (void *)tbl); printf("%p", (void *)tbl);
break; break;
} }
case COBJ_FUNCTION: { case COBJ_FUNCTION: {
CObjFunction *objFunc = (CObjFunction *)o; CObjFunction *objFunc = (CObjFunction *)o;
if (objFunc->name != NULL) if (objFunc->name != NULL)
printf("<function> %.*s", objFunc->name->length, objFunc->name->str); printf("%.*s", objFunc->name->length, objFunc->name->str);
else else
printf("<function> %s", UNNAMEDCHUNK); printf("%s", UNNAMEDCHUNK);
break; break;
} }
case COBJ_CFUNCTION: { case COBJ_CFUNCTION: {
CObjCFunction *objCFunc = (CObjCFunction *)o; CObjCFunction *objCFunc = (CObjCFunction *)o;
printf("<c function> %p", (void *)objCFunc->cfunc); printf("%p", (void *)objCFunc->cfunc);
break; break;
} }
case COBJ_ERROR: { case COBJ_ERROR: {
CObjError *err = (CObjError *)o; CObjError *err = (CObjError *)o;
printf("<error> %p -> ", (void *)o); printf("%p -> ", (void *)o);
printValue(err->err); printValue(err->err);
break; break;
} }
case COBJ_METHOD: { case COBJ_METHOD: {
CObjMethod *method = (CObjMethod *)o; CObjMethod *method = (CObjMethod *)o;
printf("<method> %p -> ", (void *)method); printf("%p -> ", (void *)method);
printValue(method->func); printValue(method->func);
break; break;
} }
case COBJ_CLOSURE: { case COBJ_CLOSURE: {
CObjClosure *closure = (CObjClosure *)o; CObjClosure *closure = (CObjClosure *)o;
printf("<closure> %p -> ", (void *)closure); printf("%p -> ", (void *)closure);
printObject((CObj *)closure->function); // just print the function printObject((CObj *)closure->function); // just print the function
break; break;
} }
case COBJ_UPVALUE: { case COBJ_UPVALUE: {
CObjUpval *upval = (CObjUpval *)o; CObjUpval *upval = (CObjUpval *)o;
printf("<upvalue> %p -> ", (void *)upval->val); printf("%p -> ", (void *)upval->val);
printValue(*upval->val); printValue(*upval->val);
break; break;
} }
default: default:
printf("<unkn obj %p>", (void *)o); printf("%p", (void *)o);
} }
} }
@@ -771,6 +760,8 @@ const char *cosmoO_typeStr(CObj *obj)
return "<function>"; return "<function>";
case COBJ_CFUNCTION: case COBJ_CFUNCTION:
return "<c function>"; return "<c function>";
case COBJ_ERROR:
return "<error>";
case COBJ_METHOD: case COBJ_METHOD:
return "<method>"; return "<method>";
case COBJ_CLOSURE: case COBJ_CLOSURE:

View File

@@ -136,6 +136,7 @@ struct CObjUpval
#define cosmoV_readCFunction(x) (((CObjCFunction *)cosmoV_readRef(x))->cfunc) #define cosmoV_readCFunction(x) (((CObjCFunction *)cosmoV_readRef(x))->cfunc)
#define cosmoV_readMethod(x) ((CObjMethod *)cosmoV_readRef(x)) #define cosmoV_readMethod(x) ((CObjMethod *)cosmoV_readRef(x))
#define cosmoV_readClosure(x) ((CObjClosure *)cosmoV_readRef(x)) #define cosmoV_readClosure(x) ((CObjClosure *)cosmoV_readRef(x))
#define cosmoV_readError(x) ((CObjError *)cosmoV_readRef(x))
#define cosmoO_readCString(x) ((CObjString *)x)->str #define cosmoO_readCString(x) ((CObjString *)x)->str
#define cosmoO_readType(x) ((CObj *)x)->type #define cosmoO_readType(x) ((CObj *)x)->type
@@ -145,7 +146,6 @@ static inline bool isObjType(CValue val, CObjType type)
return IS_REF(val) && cosmoV_readRef(val)->type == type; return IS_REF(val) && cosmoV_readRef(val)->type == type;
} }
// just protects against macro expansion
static inline bool IS_CALLABLE(CValue val) static inline bool IS_CALLABLE(CValue val)
{ {
return IS_CLOSURE(val) || IS_CFUNCTION(val) || IS_METHOD(val); return IS_CLOSURE(val) || IS_CFUNCTION(val) || IS_METHOD(val);

View File

@@ -59,14 +59,15 @@ typedef struct CCompilerState
typedef struct typedef struct
{ {
CLexState lex;
CState *state; CState *state;
CLexState *lex;
CCompilerState *compiler; CCompilerState *compiler;
CObjString *module; // name of the module CObjString *module; // name of the module
CToken current; CToken current;
CToken previous; // token right after the current token CToken previous; // token right after the current token
bool hadError; int workingStackCount; // we push CValues of objects we need onto the stack so the garbage
bool panic; // collector can see them. this is the count of those values so we'll
// know how many to pop off when we're done
} CParseState; } CParseState;
typedef enum typedef enum
@@ -109,6 +110,12 @@ static CObjFunction *endCompiler(CParseState *pstate);
// ================================================================ [FRONT END/TALK TO LEXER] // ================================================================ [FRONT END/TALK TO LEXER]
static void keepTrackOf(CParseState *pstate, CValue val)
{
pstate->workingStackCount++;
cosmoV_pushValue(pstate->state, val);
}
static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, FunctionType type, static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, FunctionType type,
CCompilerState *enclosing) CCompilerState *enclosing)
{ {
@@ -124,6 +131,8 @@ static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, Func
ccstate->function = cosmoO_newFunction(pstate->state); ccstate->function = cosmoO_newFunction(pstate->state);
ccstate->function->module = pstate->module; ccstate->function->module = pstate->module;
keepTrackOf(pstate, cosmoV_newRef((CObj *)ccstate->function));
ccstate->loop.scope = -1; // there is no loop yet ccstate->loop.scope = -1; // there is no loop yet
if (type != FTYPE_SCRIPT) { if (type != FTYPE_SCRIPT) {
@@ -146,27 +155,27 @@ static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, Func
static void initParseState(CParseState *pstate, CCompilerState *ccstate, CState *s, static void initParseState(CParseState *pstate, CCompilerState *ccstate, CState *s,
const char *source, const char *module) const char *source, const char *module)
{ {
pstate->lex = cosmoL_newLexState(s, source); cosmoL_initLexState(s, &pstate->lex, source);
pstate->state = s; pstate->state = s;
pstate->hadError = false;
pstate->panic = false;
pstate->compiler = ccstate; pstate->compiler = ccstate;
pstate->module = cosmoO_copyString(s, module, strlen(module)); pstate->module = cosmoO_copyString(s, module, strlen(module));
pstate->workingStackCount = 0;
keepTrackOf(pstate, cosmoV_newRef((CObj *)pstate->module));
initCompilerState(pstate, ccstate, FTYPE_SCRIPT, NULL); // enclosing starts as NULL initCompilerState(pstate, ccstate, FTYPE_SCRIPT, NULL); // enclosing starts as NULL
} }
static void freeParseState(CParseState *pstate) static void freeParseState(CParseState *pstate)
{ {
cosmoL_freeLexState(pstate->state, pstate->lex); cosmoL_cleanupLexState(pstate->state, &pstate->lex);
// pop our working values off the stack
cosmoV_setTop(pstate->state, pstate->workingStackCount);
} }
static void errorAt(CParseState *pstate, CToken *token, const char *format, va_list args) static void errorAt(CParseState *pstate, CToken *token, const char *format, va_list args)
{ {
if (pstate->hadError)
return;
if (token->type == TOKEN_EOF) { if (token->type == TOKEN_EOF) {
cosmoV_pushString(pstate->state, "At end: "); cosmoV_pushString(pstate->state, "At end: ");
} else if (!(token->type == TOKEN_ERROR)) { } else if (!(token->type == TOKEN_ERROR)) {
@@ -177,14 +186,9 @@ static void errorAt(CParseState *pstate, CToken *token, const char *format, va_l
cosmoO_pushVFString(pstate->state, format, args); cosmoO_pushVFString(pstate->state, format, args);
cosmoV_concat(pstate->state, 2); // concats the two strings together // throw complete error string
cosmoV_concat(pstate->state, 2);
CObjError *err = cosmoV_throw(pstate->state); cosmoV_throw(pstate->state);
err->line = token->line;
err->parserError = true;
pstate->hadError = true;
pstate->panic = true;
} }
static void errorAtCurrent(CParseState *pstate, const char *format, ...) static void errorAtCurrent(CParseState *pstate, const char *format, ...)
@@ -206,7 +210,7 @@ static void error(CParseState *pstate, const char *format, ...)
static void advance(CParseState *pstate) static void advance(CParseState *pstate)
{ {
pstate->previous = pstate->current; pstate->previous = pstate->current;
pstate->current = cosmoL_scanToken(pstate->lex); pstate->current = cosmoL_scanToken(&pstate->lex);
if (pstate->current.type == TOKEN_ERROR) { if (pstate->current.type == TOKEN_ERROR) {
errorAtCurrent(pstate, pstate->current.start); errorAtCurrent(pstate, pstate->current.start);
@@ -280,7 +284,6 @@ uint16_t makeConstant(CParseState *pstate, CValue val)
int indx = addConstant(pstate->state, getChunk(pstate), val); int indx = addConstant(pstate->state, getChunk(pstate), val);
if (indx > UINT16_MAX) { if (indx > UINT16_MAX) {
error(pstate, "UInt overflow! Too many constants in one chunk!"); error(pstate, "UInt overflow! Too many constants in one chunk!");
return 0;
} }
return (uint16_t)indx; return (uint16_t)indx;
@@ -350,7 +353,6 @@ static void addLocal(CParseState *pstate, CToken name)
{ {
if (pstate->compiler->localCount > UINT8_MAX) { if (pstate->compiler->localCount > UINT8_MAX) {
error(pstate, "UInt overflow! Too many locals in scope!"); error(pstate, "UInt overflow! Too many locals in scope!");
return;
} }
Local *local = &pstate->compiler->locals[pstate->compiler->localCount++]; Local *local = &pstate->compiler->locals[pstate->compiler->localCount++];
@@ -365,7 +367,6 @@ static int addUpvalue(CParseState *pstate, CCompilerState *ccstate, uint8_t indx
if (upvals > UINT8_MAX) { if (upvals > UINT8_MAX) {
error(pstate, "UInt overflow! Too many upvalues in scope!"); error(pstate, "UInt overflow! Too many upvalues in scope!");
return -1;
} }
// check and make sure we haven't already captured it // check and make sure we haven't already captured it
@@ -481,6 +482,7 @@ static void string(CParseState *pstate, bool canAssign, Precedence prec)
{ {
CObjString *strObj = CObjString *strObj =
cosmoO_takeString(pstate->state, pstate->previous.start, pstate->previous.length); cosmoO_takeString(pstate->state, pstate->previous.start, pstate->previous.length);
keepTrackOf(pstate, cosmoV_newRef((CObj *)strObj));
writeConstant(pstate, cosmoV_newRef((CObj *)strObj)); writeConstant(pstate, cosmoV_newRef((CObj *)strObj));
} }
@@ -768,7 +770,6 @@ static void table(CParseState *pstate, bool canAssign, Precedence prec)
tblType = 1; // array-like tblType = 1; // array-like
} else { } else {
error(pstate, "Can't change table description type mid-definition!"); error(pstate, "Can't change table description type mid-definition!");
return;
} }
entries++; entries++;
@@ -818,7 +819,7 @@ static void object(CParseState *pstate, bool canAssign, Precedence prec)
// "pop" the 1 value // "pop" the 1 value
valuePopped(pstate, 1); valuePopped(pstate, 1);
entries++; entries++;
} while (match(pstate, TOKEN_COMMA) && !pstate->hadError); } while (match(pstate, TOKEN_COMMA));
consume(pstate, TOKEN_RIGHT_BRACE, "Expected '}' to end object definition."); consume(pstate, TOKEN_RIGHT_BRACE, "Expected '}' to end object definition.");
} }
@@ -1158,9 +1159,6 @@ static uint16_t parseVariable(CParseState *pstate, const char *errorMessage, boo
static void defineVariable(CParseState *pstate, uint16_t global, bool forceLocal) static void defineVariable(CParseState *pstate, uint16_t global, bool forceLocal)
{ {
if (pstate->hadError)
return;
if (pstate->compiler->scopeDepth > 0 || forceLocal) { if (pstate->compiler->scopeDepth > 0 || forceLocal) {
markInitialized(pstate, global); markInitialized(pstate, global);
valuePopped(pstate, 1); // the local stays on the stack! valuePopped(pstate, 1); // the local stays on the stack!
@@ -1177,7 +1175,7 @@ static void _proto(CParseState *pstate)
{ {
int entries = 0; 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)) { if (match(pstate, TOKEN_FUNC)) {
// define method // define method
consume(pstate, TOKEN_IDENTIFIER, "Expected identifier for method!"); consume(pstate, TOKEN_IDENTIFIER, "Expected identifier for method!");
@@ -1224,9 +1222,6 @@ static void localProto(CParseState *pstate)
static void popLocals(CParseState *pstate, int toScope) static void popLocals(CParseState *pstate, int toScope)
{ {
if (pstate->hadError)
return;
// count the locals in scope to pop // count the locals in scope to pop
int localsToPop = 0; int localsToPop = 0;
@@ -1376,7 +1371,7 @@ static void endLoop(CParseState *pstate)
patchJmp(pstate, pstate->compiler->loop.breaks[--pstate->compiler->loop.breakCount]); 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); pstate->compiler->loop.breakCapacity);
} }
@@ -1665,7 +1660,7 @@ static void breakStatement(CParseState *pstate)
pstate->compiler->localCount = savedLocals; pstate->compiler->localCount = savedLocals;
// add break to loop // 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.breakCount, pstate->compiler->loop.breakCapacity);
pstate->compiler->loop.breaks[pstate->compiler->loop.breakCount++] = writeJmp(pstate, OP_JMP); pstate->compiler->loop.breaks[pstate->compiler->loop.breakCount++] = writeJmp(pstate, OP_JMP);
} }
@@ -1686,18 +1681,6 @@ static void continueStatement(CParseState *pstate)
writeJmpBack(pstate, pstate->compiler->loop.startBytecode); 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) static int expressionPrecedence(CParseState *pstate, int needed, Precedence prec, bool forceNeeded)
{ {
int lastExpected = pstate->compiler->expectedValues; int lastExpected = pstate->compiler->expectedValues;
@@ -1787,10 +1770,6 @@ static void statement(CParseState *pstate)
static void declaration(CParseState *pstate) static void declaration(CParseState *pstate)
{ {
statement(pstate); statement(pstate);
// if we paniced, skip the whole statement!
if (pstate->panic)
synchronize(pstate);
} }
static CObjFunction *endCompiler(CParseState *pstate) static CObjFunction *endCompiler(CParseState *pstate)
@@ -1812,7 +1791,6 @@ CObjFunction *cosmoP_compileString(CState *state, const char *source, const char
{ {
CParseState parser; CParseState parser;
CCompilerState compiler; CCompilerState compiler;
cosmoM_freezeGC(state); // ignore all GC events while compiling
initParseState(&parser, &compiler, state, source, module); initParseState(&parser, &compiler, state, source, module);
advance(&parser); advance(&parser);
@@ -1825,24 +1803,10 @@ CObjFunction *cosmoP_compileString(CState *state, const char *source, const char
popLocals(&parser, 0); 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; CObjFunction *resFunc = compiler.function;
// finally free out parser states // finally free out parser states
endCompiler(&parser); endCompiler(&parser);
freeParseState(&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; return resFunc;
} }

View File

@@ -4,8 +4,7 @@
#include "clex.h" #include "clex.h"
#include "cosmo.h" #include "cosmo.h"
// compiles source into CChunk, if NULL is returned, a syntaxical error has occurred and pushed onto // compiles source into CChunk
// the stack
CObjFunction *cosmoP_compileString(CState *state, const char *source, const char *module); CObjFunction *cosmoP_compileString(CState *state, const char *source, const char *module);
#endif #endif

View File

@@ -7,6 +7,25 @@
#include <string.h> #include <string.h>
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;
return panic;
}
void cosmoV_freePanic(CState *state)
{
CPanic *panic = state->panic;
state->panic = panic->prev;
cosmoM_free(state, CPanic, panic);
}
CState *cosmoV_newState() CState *cosmoV_newState()
{ {
// we use C's malloc because we don't want to trigger a GC with an invalid state // we use C's malloc because we don't want to trigger a GC with an invalid state
@@ -17,8 +36,8 @@ CState *cosmoV_newState()
exit(1); exit(1);
} }
state->panic = false;
state->freezeGC = 1; // we start frozen state->freezeGC = 1; // we start frozen
state->panic = NULL;
// GC // GC
state->objects = NULL; state->objects = NULL;
@@ -26,7 +45,7 @@ CState *cosmoV_newState()
state->grayStack.count = 0; state->grayStack.count = 0;
state->grayStack.capacity = 2; state->grayStack.capacity = 2;
state->grayStack.array = NULL; state->grayStack.array = NULL;
state->allocatedBytes = sizeof(CState); state->allocatedBytes = 0;
state->nextGC = 1024 * 8; // threshhold starts at 8kb state->nextGC = 1024 * 8; // threshhold starts at 8kb
// init stack // init stack
@@ -34,8 +53,6 @@ CState *cosmoV_newState()
state->frameCount = 0; state->frameCount = 0;
state->openUpvalues = NULL; state->openUpvalues = NULL;
state->error = NULL;
// set default proto objects // set default proto objects
for (int i = 0; i < COBJ_MAX; i++) for (int i = 0; i < COBJ_MAX; i++)
state->protoObjects[i] = NULL; state->protoObjects[i] = NULL;
@@ -80,12 +97,17 @@ void cosmoV_freeState(CState *state)
#ifdef GC_DEBUG #ifdef GC_DEBUG
printf("state %p is being free'd!\n", state); printf("state %p is being free'd!\n", state);
#endif #endif
cosmoM_freezeGC(state);
// frees all the objects // frees all the objects
CObj *objs = state->objects; CObj *objs = state->objects;
while (objs != NULL) { while (objs != NULL) {
CObj *next = objs->next; CObj *next = objs->next;
#ifdef GC_DEBUG
printf("STATE FREEING %p\n", objs);
fflush(stdout);
#endif
cosmoO_free(state, objs); cosmoO_free(state, objs);
objs = next; objs = next;
} }
@@ -98,15 +120,14 @@ void cosmoV_freeState(CState *state)
cosmoT_clearTable(state, &state->strings); cosmoT_clearTable(state, &state->strings);
// free our gray stack & finally free the state structure // 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) {
printf("state->allocatedBytes doesn't match, got %lu\n", state->allocatedBytes);
}
#endif
// TODO: yeah idk, it looks like im missing 520 bytes somewhere? i'll look into it later
/*#ifdef GC_DEBUG
if (state->allocatedBytes != sizeof(CState)) {
printf("state->allocatedBytes doesn't match expected value (%lu), got %lu!",
sizeof(CState), state->allocatedBytes); exit(0);
}
#endif*/
free(state); free(state);
} }

View File

@@ -6,6 +6,8 @@
#include "ctable.h" #include "ctable.h"
#include "cvalue.h" #include "cvalue.h"
#include <setjmp.h>
struct CCallFrame struct CCallFrame
{ {
CObjClosure *closure; CObjClosure *closure;
@@ -38,36 +40,47 @@ typedef struct ArrayCObj
int capacity; int capacity;
} ArrayCObj; } ArrayCObj;
typedef struct CPanic
{
jmp_buf jmp;
StkPtr top;
struct CPanic *prev;
int frameCount;
} CPanic;
struct CState struct CState
{ {
bool panic;
int freezeGC; // when > 0, GC events will be ignored (for internal use)
int frameCount;
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
ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but
// *have been* found
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 CCallFrame callFrame[FRAME_MAX]; // call frames
CValue stack[STACK_MAX]; // stack 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
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
}; };
CPanic *cosmoV_newPanic(CState *state);
void cosmoV_freePanic(CState *state);
COSMO_API CState *cosmoV_newState(); 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 // 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_register(CState *state, int pairs);
COSMO_API void cosmoV_freeState(CState *state);
COSMO_API void cosmoV_printStack(CState *state); COSMO_API void cosmoV_printStack(CState *state);
#endif #endif

View File

@@ -34,6 +34,7 @@ void cosmoT_initTable(CState *state, CTable *tbl, int startCap)
tbl->capacityMask = startCap - 1; tbl->capacityMask = startCap - 1;
tbl->count = 0; tbl->count = 0;
tbl->tombstones = 0; tbl->tombstones = 0;
tbl->tombThreshold = 32;
tbl->table = NULL; // to let out GC know we're initalizing tbl->table = NULL; // to let out GC know we're initalizing
tbl->table = cosmoM_xmalloc(state, sizeof(CTableEntry) * startCap); tbl->table = cosmoM_xmalloc(state, sizeof(CTableEntry) * startCap);
@@ -47,7 +48,7 @@ void cosmoT_initTable(CState *state, CTable *tbl, int startCap)
void cosmoT_addTable(CState *state, CTable *from, CTable *to) void cosmoT_addTable(CState *state, CTable *from, CTable *to)
{ {
CTableEntry *entry; CTableEntry *entry;
int cap = from->capacityMask + 1; int cap = cosmoT_getCapacity(from);
for (int i = 0; i < cap; i++) { for (int i = 0; i < cap; i++) {
entry = &from->table[i]; entry = &from->table[i];
@@ -60,7 +61,7 @@ void cosmoT_addTable(CState *state, CTable *from, CTable *to)
void cosmoT_clearTable(CState *state, CTable *tbl) void cosmoT_clearTable(CState *state, CTable *tbl)
{ {
cosmoM_freearray(state, CTableEntry, tbl->table, (tbl->capacityMask + 1)); cosmoM_freeArray(state, CTableEntry, tbl->table, cosmoT_getCapacity(tbl));
} }
static uint32_t getObjectHash(CObj *obj) static uint32_t getObjectHash(CObj *obj)
@@ -113,10 +114,10 @@ static CTableEntry *findEntry(CState *state, CTableEntry *entries, int mask, CVa
if (IS_NIL(entry->val)) { if (IS_NIL(entry->val)) {
// it's empty! if we found a tombstone, return that so it'll be reused // it's empty! if we found a tombstone, return that so it'll be reused
return tomb != NULL ? tomb : entry; return tomb != NULL ? tomb : entry;
} else {
// its a tombstone!
tomb = entry;
} }
// its a tombstone!
tomb = entry;
} else if (cosmoV_equal(state, entry->key, key)) { } else if (cosmoV_equal(state, entry->key, key)) {
return entry; return entry;
} }
@@ -141,7 +142,7 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin
return; return;
CTableEntry *entries = cosmoM_xmalloc(state, size); CTableEntry *entries = cosmoM_xmalloc(state, size);
oldCap = tbl->capacityMask + 1; oldCap = cosmoT_getCapacity(tbl);
newCount = 0; newCount = 0;
// set all nodes as NIL : NIL // set all nodes as NIL : NIL
@@ -164,7 +165,7 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin
} }
// free the old table // free the old table
cosmoM_freearray(state, CTableEntry, tbl->table, oldCap); cosmoM_freeArray(state, CTableEntry, tbl->table, oldCap);
tbl->table = entries; tbl->table = entries;
tbl->capacityMask = newCapacity - 1; tbl->capacityMask = newCapacity - 1;
@@ -176,10 +177,10 @@ bool cosmoT_checkShrink(CState *state, CTable *tbl)
{ {
// if count > 8 and active entries < tombstones // if count > 8 and active entries < tombstones
if (tbl->count > MIN_TABLE_CAPACITY && if (tbl->count > MIN_TABLE_CAPACITY &&
(tbl->count - tbl->tombstones < tbl->tombstones || (tbl->count - tbl->tombstones < tbl->tombstones || tbl->tombstones > tbl->tombThreshold)) {
tbl->tombstones > 50)) { // TODO: 50 should be a threshhold // shrink based on active entries to the next pow of 2
resizeTbl(state, tbl, nextPow2(tbl->count - tbl->tombstones) * GROW_FACTOR, resizeTbl(state, tbl, nextPow2(tbl->count - tbl->tombstones) * GROW_FACTOR, false);
false); // shrink based on active entries to the next pow of 2 tbl->tombThreshold = tbl->count / 4;
return true; return true;
} }
@@ -190,7 +191,7 @@ bool cosmoT_checkShrink(CState *state, CTable *tbl)
COSMO_API CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key) COSMO_API CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key)
{ {
// make sure we have enough space allocated // make sure we have enough space allocated
int cap = tbl->capacityMask + 1; int cap = cosmoT_getCapacity(tbl);
if (tbl->count + 1 > (int)(cap * MAX_TABLE_FILL)) { if (tbl->count + 1 > (int)(cap * MAX_TABLE_FILL)) {
// grow table // grow table
int newCap = cap * GROW_FACTOR; int newCap = cap * GROW_FACTOR;
@@ -198,8 +199,7 @@ COSMO_API CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key)
} }
// insert into the table // insert into the table
CTableEntry *entry = CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key);
findEntry(state, tbl->table, tbl->capacityMask, key); // -1 for our capacity mask
if (IS_NIL(entry->key)) { if (IS_NIL(entry->key)) {
if (IS_NIL(entry->val)) // is it empty? if (IS_NIL(entry->val)) // is it empty?
@@ -255,9 +255,10 @@ CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32
{ {
if (tbl->count == 0) if (tbl->count == 0)
return 0; // sanity check return 0; // sanity check
uint32_t indx =
hash & tbl->capacityMask; // since we know the capacity will *always* be a power of 2, we // since we know the capacity will *always* be a power of 2, we
// can use bitwise & to perform a MUCH faster mod operation // can use bitwise & to perform a MUCH faster mod operation
uint32_t indx = hash & tbl->capacityMask;
// keep looking for an open slot in the entries array // keep looking for an open slot in the entries array
while (true) { while (true) {
@@ -280,7 +281,7 @@ CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32
void cosmoT_printTable(CTable *tbl, const char *name) void cosmoT_printTable(CTable *tbl, const char *name)
{ {
printf("==== [[%s]] ====\n", name); printf("==== [[%s]] ====\n", name);
int cap = tbl->capacityMask + 1; int cap = cosmoT_getCapacity(tbl);
for (int i = 0; i < cap; i++) { for (int i = 0; i < cap; i++) {
CTableEntry *entry = &tbl->table[i]; CTableEntry *entry = &tbl->table[i];
if (!(IS_NIL(entry->key))) { if (!(IS_NIL(entry->key))) {

View File

@@ -18,9 +18,12 @@ typedef struct CTable
int count; int count;
int capacityMask; // +1 to get the capacity int capacityMask; // +1 to get the capacity
int tombstones; int tombstones;
int tombThreshold;
CTableEntry *table; CTableEntry *table;
} CTable; } CTable;
#define cosmoT_getCapacity(tbl) ((tbl)->capacityMask + 1)
COSMO_API void cosmoT_initTable(CState *state, CTable *tbl, int startCap); COSMO_API void cosmoT_initTable(CState *state, CTable *tbl, int startCap);
COSMO_API void cosmoT_clearTable(CState *state, CTable *tbl); COSMO_API void cosmoT_clearTable(CState *state, CTable *tbl);
COSMO_API int cosmoT_count(CTable *tbl); COSMO_API int cosmoT_count(CTable *tbl);

View File

@@ -13,12 +13,12 @@ void initValArray(CState *state, CValueArray *val, size_t startCapacity)
void cleanValArray(CState *state, CValueArray *array) 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) 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; array->values[array->count++] = val;
} }

173
src/cvm.c
View File

@@ -10,6 +10,8 @@
#include <stdarg.h> #include <stdarg.h>
#include <string.h> #include <string.h>
#define cosmoV_protect(panic) setjmp(panic->jmp) == 0
COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...) COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...)
{ {
va_list args; va_list args;
@@ -36,9 +38,6 @@ COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud)
CObjFunction *func; CObjFunction *func;
if (cosmoD_undump(state, reader, ud, &func)) { if (cosmoD_undump(state, reader, ud, &func)) {
// fail recovery
state->panic = false;
cosmoV_pushRef(state, (CObj *)state->error);
return false; return false;
}; };
@@ -53,15 +52,20 @@ COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud)
return true; return true;
} }
// returns false if failed, error will be on the top of the stack. true if successful, closure will
// be on the top of the stack
COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name) COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name)
{ {
CObjFunction *func; CObjFunction *func;
CPanic *panic = cosmoV_newPanic(state);
if ((func = cosmoP_compileString(state, src, name)) != NULL) { if (cosmoV_protect(panic)) {
// success func = cosmoP_compileString(state, src, name);
#ifdef VM_DEBUG #ifdef VM_DEBUG
disasmChunk(&func->chunk, func->module->str, 0); disasmChunk(&func->chunk, func->module->str, 0);
#endif #endif
cosmoV_freePanic(state);
// push function onto the stack so it doesn't it cleaned up by the GC, at the same stack // push function onto the stack so it doesn't it cleaned up by the GC, at the same stack
// location put our closure // location put our closure
cosmoV_pushRef(state, (CObj *)func); cosmoV_pushRef(state, (CObj *)func);
@@ -69,9 +73,7 @@ COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *
return true; return true;
} }
// fail recovery cosmoV_freePanic(state);
state->panic = false;
cosmoV_pushRef(state, (CObj *)state->error);
return false; return false;
} }
@@ -109,30 +111,29 @@ 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 takes value on top of the stack and wraps an CObjError around it, then throws it
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..
*/ */
CObjError *cosmoV_throw(CState *state) void cosmoV_throw(CState *state)
{ {
StkPtr temp = cosmoV_getTop(state, 0); StkPtr temp = cosmoV_getTop(state, 0);
CObjError *error = cosmoO_newError(state, *temp); CObjError *error = cosmoO_newError(state, *temp);
state->error = error;
state->panic = true;
cosmoV_pop(state); // pops thrown value off the stack CValue val = cosmoV_newRef((CObj *)cosmoO_newError(state, *temp));
return error; 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);
}
} }
void cosmoV_error(CState *state, const char *format, ...) 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 // format the error string and push it onto the stack
va_list args; va_list args;
va_start(args, format); va_start(args, format);
@@ -237,18 +238,16 @@ void cosmoV_concat(CState *state, int vals)
} }
int cosmoV_execute(CState *state); int cosmoV_execute(CState *state);
bool invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults, int offset); void invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults, int offset);
/* /*
calls a native C Function with # args on the stack, nresults are pushed onto the stack upon calls a native C Function with # args on the stack, nresults are pushed onto the stack upon
return. return.
returns: state->top is moved to base + offset + nresults, with nresults pushed onto the stack
false: state paniced during C Function, error is at state->error from base + offset
true: state->top is moved to base + offset + nresults, with nresults pushed onto the stack
from base + offset
*/ */
static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nresults, int offset) static void callCFunction(CState *state, CosmoCFunction cfunc, int args, int nresults, int offset)
{ {
StkPtr savedBase = cosmoV_getTop(state, args); StkPtr savedBase = cosmoV_getTop(state, args);
@@ -260,13 +259,8 @@ static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nre
// remember where the return values are // remember where the return values are
StkPtr results = cosmoV_getTop(state, nres - 1); StkPtr results = cosmoV_getTop(state, nres - 1);
state->top = savedBase + offset; // set stack state->top = savedBase + offset; // set stack
// if the state paniced during the c function, return false
if (state->panic)
return false;
// push the return value back onto the stack // push the return value back onto the stack
memmove(state->top, results, memmove(state->top, results,
sizeof(CValue) * nres); // copies the return values to the top of the stack sizeof(CValue) * nres); // copies the return values to the top of the stack
@@ -275,20 +269,16 @@ static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nre
// now, if the caller function expected more return values, push nils onto the stack // now, if the caller function expected more return values, push nils onto the stack
for (int i = nres; i < nresults; i++) for (int i = nres; i < nresults; i++)
cosmoV_pushValue(state, cosmoV_newNil()); cosmoV_pushValue(state, cosmoV_newNil());
return true;
} }
/* /*
calls a raw closure object with # args on the stack, nresults are pushed onto the stack upon calls a raw closure object with # args on the stack, nresults are pushed onto the stack upon
return. return.
returns: stack->top is moved to base + offset + nresults, with nresults pushed onto the stack
false: state paniced, error is at state->error from base + offset
true: stack->top is moved to base + offset + nresults, with nresults pushed onto the stack
from base + offset
*/ */
static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults, int offset) static void rawCall(CState *state, CObjClosure *closure, int args, int nresults, int offset)
{ {
CObjFunction *func = closure->function; CObjFunction *func = closure->function;
@@ -312,7 +302,6 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults,
cosmoV_error(state, "Expected %d arguments for %s, got %d!", closure->function->args, cosmoV_error(state, "Expected %d arguments for %s, got %d!", closure->function->args,
closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str,
args); args);
return false;
} else { } else {
// load function into callframe // load function into callframe
pushCallFrame(state, closure, func->args); pushCallFrame(state, closure, func->args);
@@ -330,9 +319,6 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults,
// pop the callframe and return results :) // pop the callframe and return results :)
popCallFrame(state, offset); popCallFrame(state, offset);
if (state->panic) // panic state
return false;
// push the return values back onto the stack // push the return values back onto the stack
for (int i = 0; i < nres; i++) { for (int i = 0; i < nres; i++) {
state->top[i] = results[i]; state->top[i] = results[i];
@@ -342,11 +328,10 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults,
// now, if the caller function expected more return values, push nils onto the stack // now, if the caller function expected more return values, push nils onto the stack
for (int i = nres; i < nresults; i++) for (int i = nres; i < nresults; i++)
cosmoV_pushValue(state, cosmoV_newNil()); cosmoV_pushValue(state, cosmoV_newNil());
return true;
} }
bool callCValue(CState *state, CValue func, int args, int nresults, int offset) // returns true if successful, false if error
void callCValue(CState *state, CValue func, int args, int nresults, int offset)
{ {
#ifdef VM_DEBUG #ifdef VM_DEBUG
printf("\n"); printf("\n");
@@ -357,17 +342,19 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset)
if (!IS_REF(func)) { if (!IS_REF(func)) {
cosmoV_error(state, "Cannot call non-callable type %s!", cosmoV_typeStr(func)); cosmoV_error(state, "Cannot call non-callable type %s!", cosmoV_typeStr(func));
return false;
} }
switch (cosmoV_readRef(func)->type) { switch (cosmoV_readRef(func)->type) {
case COBJ_CLOSURE: case COBJ_CLOSURE:
return rawCall(state, cosmoV_readClosure(func), args, nresults, offset); rawCall(state, cosmoV_readClosure(func), args, nresults, offset);
break;
case COBJ_CFUNCTION: case COBJ_CFUNCTION:
return callCFunction(state, cosmoV_readCFunction(func), args, nresults, offset); callCFunction(state, cosmoV_readCFunction(func), args, nresults, offset);
break;
case COBJ_METHOD: { case COBJ_METHOD: {
CObjMethod *method = (CObjMethod *)cosmoV_readRef(func); CObjMethod *method = (CObjMethod *)cosmoV_readRef(func);
return invokeMethod(state, method->obj, method->func, args, nresults, offset + 1); invokeMethod(state, method->obj, method->func, args, nresults, offset + 1);
break;
} }
case COBJ_OBJECT: { // object is being instantiated, making another object case COBJ_OBJECT: { // object is being instantiated, making another object
CObjObject *protoObj = (CObjObject *)cosmoV_readRef(func); CObjObject *protoObj = (CObjObject *)cosmoV_readRef(func);
@@ -380,12 +367,10 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset)
// check if they defined an initializer (we accept 0 return values) // check if they defined an initializer (we accept 0 return values)
if (cosmoO_getIString(state, protoObj, ISTRING_INIT, &ret)) { if (cosmoO_getIString(state, protoObj, ISTRING_INIT, &ret)) {
if (!invokeMethod(state, (CObj *)newObj, ret, args, 0, offset + 1)) invokeMethod(state, (CObj *)newObj, ret, args, 0, offset + 1);
return false;
} else { } else {
// no default initializer // no default initializer
cosmoV_error(state, "Expected __init() in proto, object cannot be instantiated!"); cosmoV_error(state, "Expected __init() in proto, object cannot be instantiated!");
return false;
} }
if (nresults > 0) { if (nresults > 0) {
@@ -401,58 +386,51 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset)
} }
default: default:
cosmoV_error(state, "Cannot call non-callable type %s!", cosmoV_typeStr(func)); cosmoV_error(state, "Cannot call non-callable type %s!", cosmoV_typeStr(func));
return false;
} }
return true;
} }
bool invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults, int offset) void invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults, int offset)
{ {
// first, set the first argument to the object // first, set the first argument to the object
StkPtr temp = cosmoV_getTop(state, args); StkPtr temp = cosmoV_getTop(state, args);
*temp = cosmoV_newRef(obj); *temp = cosmoV_newRef(obj);
return callCValue(state, func, args + 1, nresults, offset); callCValue(state, func, args + 1, nresults, offset);
} }
// wraps cosmoV_call in a protected state, CObjError will be pushed onto the stack if function call // wraps cosmoV_call in a protected state, CObjError will be pushed onto the stack if function call
// failed, else return values are passed // failed, else return values are passed
COSMOVMRESULT cosmoV_pcall(CState *state, 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); CPanic *panic = cosmoV_newPanic(state);
if (!callCValue(state, *base, args, nresults, 0)) { if (cosmoV_protect(panic)) {
// restore panic state cosmoV_call(state, args, nresults);
state->panic = false; cosmoV_freePanic(state);
return true;
} else {
// if cosmoV_protect returns false, the error is already on the top of the stack
if (nresults > 0) { if (nresults > 0) {
cosmoV_pushRef(state, (CObj *)state->error);
// push other expected results onto the stack // push other expected results onto the stack
for (int i = 0; i < nresults - 1; i++) for (int i = 0; i < nresults - 1; i++)
cosmoV_pushValue(state, cosmoV_newNil()); cosmoV_pushValue(state, cosmoV_newNil());
} }
return COSMOVM_RUNTIME_ERR; cosmoV_freePanic(state);
return false;
} }
return COSMOVM_OK;
} }
/* // calls a callable object at stack->top - args - 1, passing the # of args to the callable, and
calls a callable object at stack->top - args - 1, passing the # of args to the callable, and // ensuring nresults are returned
ensuring nresults are returned // returns false if an error was thrown, else true if successful
void cosmoV_call(CState *state, int args, int nresults)
returns:
COSMOVM_OK: callable object exited normally
COSMOVM_RUNTIME_ERR: an error occurred, grab the error from state->error
*/
COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults)
{ {
StkPtr val = cosmoV_getTop(state, args); // function will always be right above the args StkPtr val = cosmoV_getTop(state, args); // function will always be right above the args
return callCValue(state, *val, args, nresults, 0) ? COSMOVM_OK : COSMOVM_RUNTIME_ERR; callCValue(state, *val, args, nresults, 0);
} }
static inline bool isFalsey(StkPtr val) static inline bool isFalsey(StkPtr val)
@@ -643,7 +621,7 @@ int _tbl__next(CState *state, int nargs, CValue *args)
CObjTable *table = (CObjTable *)cosmoV_readRef(val); CObjTable *table = (CObjTable *)cosmoV_readRef(val);
// while the entry is invalid, go to the next entry // while the entry is invalid, go to the next entry
int cap = table->tbl.capacityMask + 1; int cap = cosmoT_getCapacity(&table->tbl);
CTableEntry *entry; CTableEntry *entry;
do { do {
entry = &table->tbl.table[index++]; entry = &table->tbl.table[index++];
@@ -728,7 +706,7 @@ int cosmoV_execute(CState *state)
CCallFrame *frame = &state->callFrame[state->frameCount - 1]; // grabs the current frame CCallFrame *frame = &state->callFrame[state->frameCount - 1]; // grabs the current frame
CValue *constants = frame->closure->function->chunk.constants.values; // cache the pointer :) CValue *constants = frame->closure->function->chunk.constants.values; // cache the pointer :)
while (!state->panic) { for (;;) {
#ifdef VM_DEBUG #ifdef VM_DEBUG
cosmoV_printStack(state); cosmoV_printStack(state);
disasmInstr(&frame->closure->function->chunk, disasmInstr(&frame->closure->function->chunk,
@@ -767,7 +745,6 @@ int cosmoV_execute(CState *state)
{ {
uint8_t indx = READBYTE(frame); uint8_t indx = READBYTE(frame);
cosmoV_pushValue(state, frame->base[indx]); cosmoV_pushValue(state, frame->base[indx]);
continue;
} }
CASE(OP_GETUPVAL) : CASE(OP_GETUPVAL) :
{ {
@@ -813,9 +790,7 @@ int cosmoV_execute(CState *state)
{ {
uint8_t args = READBYTE(frame); uint8_t args = READBYTE(frame);
uint8_t nres = READBYTE(frame); uint8_t nres = READBYTE(frame);
if (cosmoV_call(state, args, nres) != COSMOVM_OK) { cosmoV_call(state, args, nres);
return -1;
}
} }
CASE(OP_CLOSURE) : CASE(OP_CLOSURE) :
{ {
@@ -1040,10 +1015,9 @@ int cosmoV_execute(CState *state)
cosmoV_pop(state); // pop the object from the stack cosmoV_pop(state); // pop the object from the stack
cosmoV_pushValue(state, val); cosmoV_pushValue(state, val);
cosmoV_pushRef(state, (CObj *)obj); cosmoV_pushRef(state, (CObj *)obj);
if (cosmoV_call(state, 1, 1) != cosmoV_call(
COSMOVM_OK) // we expect 1 return value on the stack, the iterable state, 1,
// object 1); // we expect 1 return value on the stack, the iterable object
return -1;
StkPtr iObj = cosmoV_getTop(state, 0); StkPtr iObj = cosmoV_getTop(state, 0);
@@ -1101,8 +1075,7 @@ int cosmoV_execute(CState *state)
} }
cosmoV_pushValue(state, *temp); cosmoV_pushValue(state, *temp);
if (cosmoV_call(state, 0, nresults) != COSMOVM_OK) cosmoV_call(state, 0, nresults);
return -1;
if (IS_NIL(*(cosmoV_getTop( if (IS_NIL(*(cosmoV_getTop(
state, 0)))) { // __next returned a nil, which means to exit the loop state, 0)))) { // __next returned a nil, which means to exit the loop
@@ -1332,20 +1305,23 @@ int cosmoV_execute(CState *state)
// compare & push // compare & push
cosmoV_pushBoolean(state, cosmoV_equal(state, *valA, *valB)); cosmoV_pushBoolean(state, cosmoV_equal(state, *valA, *valB));
} }
CASE(OP_LESS) :
{
NUMBEROP(cosmoV_newBoolean, <);
}
CASE(OP_GREATER) : CASE(OP_GREATER) :
{ {
NUMBEROP(cosmoV_newBoolean, >); NUMBEROP(cosmoV_newBoolean, >);
} }
CASE(OP_LESS) : CASE(OP_LESS_EQUAL) :
{ {
NUMBEROP(cosmoV_newBoolean, <); NUMBEROP(cosmoV_newBoolean, <=);
} }
CASE(OP_GREATER_EQUAL) : CASE(OP_GREATER_EQUAL) :
{ {
NUMBEROP(cosmoV_newBoolean, >=); NUMBEROP(cosmoV_newBoolean, >=);
} }
CASE(OP_LESS_EQUAL) CASE(OP_TRUE) : cosmoV_pushBoolean(state, true);
: {NUMBEROP(cosmoV_newBoolean, <=)} CASE(OP_TRUE) : cosmoV_pushBoolean(state, true);
CASE(OP_FALSE) : cosmoV_pushBoolean(state, false); CASE(OP_FALSE) : cosmoV_pushBoolean(state, false);
CASE(OP_NIL) : cosmoV_pushValue(state, cosmoV_newNil()); CASE(OP_NIL) : cosmoV_pushValue(state, cosmoV_newNil());
CASE(OP_RETURN) : CASE(OP_RETURN) :
@@ -1357,7 +1333,6 @@ int cosmoV_execute(CState *state)
} }
} }
// we'll only reach this if state->panic is true
return -1; return -1;
} }

View File

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