diff --git a/main.c b/main.c index 944bd83..b8824ab 100644 --- a/main.c +++ b/main.c @@ -75,7 +75,7 @@ static void repl(CState *state) cosmoV_pushString(state, "input"); cosmoV_pushCFunction(state, cosmoB_input); - cosmoV_register(state, 2); + cosmoV_addGlobals(state, 2); while (_ACTIVE) { if (!(line = linenoise("> "))) { // better than gets() @@ -130,7 +130,7 @@ static bool runFile(CState *state, const char *fileName) cosmoV_pushString(state, "input"); cosmoV_pushCFunction(state, cosmoB_input); - cosmoV_register(state, 1); + cosmoV_addGlobals(state, 1); ret = interpret(state, script, fileName); diff --git a/src/cmem.c b/src/cmem.c index a931ba9..02ff584 100644 --- a/src/cmem.c +++ b/src/cmem.c @@ -6,6 +6,7 @@ #include "cstate.h" #include "ctable.h" #include "cvalue.h" +#include "cvm.h" // realloc wrapper void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize) @@ -246,22 +247,23 @@ static void sweep(CState *state) prev->next = object; } + // call __gc on the object + CObjObject *protoObject = cosmoO_grabProto(oldObj); + CValue res; + + // use user-defined __gc + if (protoObject != NULL && cosmoO_getIString(state, protoObject, ISTRING_GC, &res)) { + cosmoV_pushValue(state, res); + cosmoV_pushRef(state, (CObj *)oldObj); + cosmoV_call(state, 1, 0); + } + + cosmoO_free(state, oldObj); } } } -static void markUserRoots(CState *state) -{ - CObj *root = state->userRoots; - - // traverse userRoots and mark all the object - while (root != NULL) { - markObject(state, root); - root = root->nextRoot; - } -} - static void markRoots(CState *state) { // mark all values on the stack @@ -285,8 +287,7 @@ static void markRoots(CState *state) for (int i = 0; i < ISTRING_MAX; i++) markObject(state, (CObj *)state->iStrings[i]); - // mark the user defined roots - markUserRoots(state); + markTable(state, &state->registry); for (int i = 0; i < COBJ_MAX; i++) markObject(state, (CObj *)state->protoObjects[i]); @@ -323,44 +324,3 @@ COSMO_API void cosmoM_updateThreshhold(CState *state) { state->nextGC = state->allocatedBytes * HEAP_GROW_FACTOR; } - -COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot) -{ - // first, check and make sure this root doesn't already exist in the list - CObj *root = state->userRoots; - while (root != NULL) { - if (root == newRoot) // found in the list, abort - return; - - root = root->nextRoot; - } - - // adds root to userRoot linked list - newRoot->nextRoot = state->userRoots; - state->userRoots = newRoot; -} - -COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot) -{ - CObj *prev = NULL; - CObj *root = state->userRoots; - - // traverse the userRoot linked list - while (root != NULL) { - if (root == oldRoot) { // found root in list - - // remove from the linked list - if (prev == NULL) { - state->userRoots = root->nextRoot; - } else { - prev->nextRoot = root->nextRoot; - } - - root->nextRoot = NULL; - break; - } - - prev = root; - root = root->nextRoot; - } -} diff --git a/src/cmem.h b/src/cmem.h index 0063892..b617d76 100644 --- a/src/cmem.h +++ b/src/cmem.h @@ -67,13 +67,6 @@ COSMO_API bool cosmoM_checkGarbage(CState *state, COSMO_API void cosmoM_collectGarbage(CState *state); COSMO_API void cosmoM_updateThreshhold(CState *state); -// lets the VM know you are holding a reference to a CObj and to not free it -// NOTE: prefer to use the stack when possible -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 -COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot); - // wrapper for cosmoM_reallocate so we can track our memory usage static inline void *cosmoM_xmalloc(CState *state, size_t sz) { diff --git a/src/cobj.c b/src/cobj.c index 4f90673..ba47eda 100644 --- a/src/cobj.c +++ b/src/cobj.c @@ -31,7 +31,6 @@ CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type) obj->next = state->objects; state->objects = obj; - obj->nextRoot = NULL; #ifdef GC_DEBUG printf("allocated %s %p\n", cosmoO_typeStr(obj), obj); #endif @@ -51,9 +50,9 @@ void cosmoO_free(CState *state, CObj *obj) break; } case COBJ_OBJECT: { - CObjObject *objTbl = (CObjObject *)obj; - cosmoT_clearTable(state, &objTbl->tbl); - cosmoM_free(state, CObjObject, objTbl); + CObjObject *objObj = (CObjObject *)obj; + cosmoT_clearTable(state, &objObj->tbl); + cosmoM_free(state, CObjObject, objObj); break; } case COBJ_TABLE: { @@ -492,6 +491,7 @@ void cosmoO_unlock(CObjObject *object) object->isLocked = false; } + bool rawgetIString(CState *state, CObjObject *object, int flag, CValue *val) { if (readFlag(object->istringFlags, flag)) @@ -511,6 +511,8 @@ bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val) CObjObject *obj = object; do { + printf("getting flag %d from obj: %p\n", flag, obj); + fflush(stdout); if (rawgetIString(state, obj, flag, val)) return true; } while ((obj = obj->_obj.proto) != NULL); // sets obj to it's proto and compares it to NULL diff --git a/src/cobj.h b/src/cobj.h index 7f651b3..055d8fa 100644 --- a/src/cobj.h +++ b/src/cobj.h @@ -34,7 +34,6 @@ typedef int (*CosmoCFunction)(CState *state, int argCount, CValue *args); struct CObj { struct CObj *next; - struct CObj *nextRoot; // for the root linked list struct CObjObject *proto; // protoobject, describes the behavior of the object CObjType type; bool isMarked; // for the GC diff --git a/src/cstate.c b/src/cstate.c index 81a084b..eb67366 100644 --- a/src/cstate.c +++ b/src/cstate.c @@ -42,7 +42,6 @@ CState *cosmoV_newState() // GC state->objects = NULL; - state->userRoots = NULL; state->grayStack.count = 0; state->grayStack.capacity = 2; state->grayStack.array = NULL; @@ -62,11 +61,13 @@ CState *cosmoV_newState() state->iStrings[i] = NULL; cosmoT_initTable(state, &state->strings, 16); // init string table + cosmoT_initTable(state, &state->registry, 16); state->globals = cosmoO_newTable(state); // init global table // setup all strings used by the VM state->iStrings[ISTRING_INIT] = cosmoO_copyString(state, "__init", 6); + state->iStrings[ISTRING_GC] = cosmoO_copyString(state, "__gc", 4); state->iStrings[ISTRING_TOSTRING] = cosmoO_copyString(state, "__tostring", 10); state->iStrings[ISTRING_TONUMBER] = cosmoO_copyString(state, "__tonumber", 10); state->iStrings[ISTRING_INDEX] = cosmoO_copyString(state, "__index", 7); @@ -120,6 +121,8 @@ void cosmoV_freeState(CState *state) // free our string table (the string table includes the internal VM strings) cosmoT_clearTable(state, &state->strings); + cosmoT_clearTable(state, &state->registry); + // free our gray stack & finally free the state structure cosmoM_freeArray(state, CObj *, state->grayStack.array, state->grayStack.capacity); @@ -133,7 +136,7 @@ void cosmoV_freeState(CState *state) } // expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value -void cosmoV_register(CState *state, int pairs) +void cosmoV_addGlobals(CState *state, int pairs) { for (int i = 0; i < pairs; i++) { StkPtr key = cosmoV_getTop(state, 1); @@ -146,6 +149,51 @@ void cosmoV_register(CState *state, int pairs) } } +// expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value +void cosmoV_addRegistry(CState *state, int pairs) +{ + for (int i = 0; i < pairs; i++) { + StkPtr key = cosmoV_getTop(state, 1); + StkPtr val = cosmoV_getTop(state, 0); + + CValue *oldVal = cosmoT_insert(state, &state->registry, *key); + *oldVal = *val; + + cosmoV_setTop(state, 2); // pops the 2 values off the stack + } +} + +// expects 1 key on the stack, pushes result +void cosmoV_getRegistry(CState *state) { + CValue key = *cosmoV_pop(state); + CValue val; + + if (!cosmoT_get(state, &state->registry, key, &val)) { + cosmoV_error(state, "failed to grab %s from registry", cosmoV_typeStr(key)); + } + + printf("got %s from registry - ", cosmoV_typeStr(val)); + printValue(val); + printf("\n"); + cosmoV_pushValue(state, val); +} + +void cosmoV_setProto(CState *state) { + StkPtr objVal = cosmoV_getTop(state, 1); + StkPtr protoVal = cosmoV_getTop(state, 0); + + if (!IS_REF(*objVal) || !IS_OBJECT(*protoVal)) { + cosmoV_error(state, "cannot set %s to proto of type %s", cosmoV_typeStr(*objVal), cosmoV_typeStr(*protoVal)); + } + + // actually set the protos + CObj *obj = cosmoV_readRef(*objVal); + CObjObject *proto = cosmoV_readObject(*protoVal); + obj->proto = proto; + + cosmoV_setTop(state, 2); +} + void cosmoV_printStack(CState *state) { printf("==== [[ stack dump ]] ====\n"); diff --git a/src/cstate.h b/src/cstate.h index a527ec8..f6d53eb 100644 --- a/src/cstate.h +++ b/src/cstate.h @@ -18,6 +18,7 @@ struct CCallFrame typedef enum IStringEnum { ISTRING_INIT, // __init + ISTRING_GC, // __gc ISTRING_TOSTRING, // __tostring ISTRING_TONUMBER, // __tonumber ISTRING_EQUAL, // __equals @@ -56,6 +57,7 @@ struct CState 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; + CTable registry; ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but // *have been* found @@ -63,14 +65,12 @@ struct CState 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 + int freezeGC; // when > 0, GC events will be ignored (for internal use) + int frameCount; }; CPanic *cosmoV_newPanic(CState *state); @@ -80,7 +80,16 @@ COSMO_API CState *cosmoV_newState(); COSMO_API void cosmoV_freeState(CState *state); // expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value -COSMO_API void cosmoV_register(CState *state, int pairs); +COSMO_API void cosmoV_addGlobals(CState *state, int pairs); + +// expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value +COSMO_API void cosmoV_addRegistry(CState *state, int pairs); + +// expects 1 key on the stack, pushes result +COSMO_API void cosmoV_getRegistry(CState *state); + +// expects ->proto = (2 total) to be on the stack +COSMO_API void cosmoV_setProto(CState *state); COSMO_API void cosmoV_printStack(CState *state); diff --git a/src/cvm.c b/src/cvm.c index 7e91b04..875b52d 100644 --- a/src/cvm.c +++ b/src/cvm.c @@ -12,7 +12,7 @@ #define cosmoV_protect(panic) setjmp(panic->jmp) == 0 -COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...) +void cosmoV_pushFString(CState *state, const char *format, ...) { va_list args; va_start(args, format); @@ -21,7 +21,7 @@ COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...) } // inserts val at state->top - indx - 1, moving everything else up -COSMO_API void cosmo_insert(CState *state, int indx, CValue val) +void cosmoV_insert(CState *state, int indx, CValue val) { StkPtr tmp = cosmoV_getTop(state, indx); @@ -33,7 +33,7 @@ COSMO_API void cosmo_insert(CState *state, int indx, CValue val) state->top++; } -COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud) +bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud) { CObjFunction *func; @@ -54,7 +54,7 @@ COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud) // 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) +bool cosmoV_compileString(CState *state, const char *src, const char *name) { CObjFunction *func; CPanic *panic = cosmoV_newPanic(state); @@ -77,7 +77,7 @@ COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char * return false; } -COSMO_API void cosmoV_printError(CState *state, CObjError *err) +void cosmoV_printError(CState *state, CObjError *err) { // print stack trace for (int i = 0; i < err->frameCount; i++) { @@ -439,7 +439,7 @@ static inline bool isFalsey(StkPtr val) return IS_NIL(*val) || (IS_BOOLEAN(*val) && !cosmoV_readBoolean(*val)); } -COSMO_API CObjObject *cosmoV_makeObject(CState *state, int pairs) +CObjObject *cosmoV_makeObject(CState *state, int pairs) { StkPtr key, val; CObjObject *newObj = cosmoO_newObject(state); @@ -460,7 +460,7 @@ COSMO_API CObjObject *cosmoV_makeObject(CState *state, int pairs) return newObj; } -COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj) +bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj) { bool replaced = state->protoObjects[objType] != NULL; state->protoObjects[objType] = obj; @@ -469,7 +469,7 @@ COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjO CObj *curr = state->objects; while (curr != NULL) { // update the proto - if (curr->type == objType && curr->proto != NULL) { + if (curr != (CObj *)obj && curr->type == objType && curr->proto != NULL) { curr->proto = obj; } curr = curr->next; @@ -478,7 +478,7 @@ COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjO return replaced; } -COSMO_API void cosmoV_makeTable(CState *state, int pairs) +void cosmoV_makeTable(CState *state, int pairs) { StkPtr key, val; CObjTable *newObj = cosmoO_newTable(state); @@ -530,7 +530,7 @@ void cosmoV_rawset(CState *state, CObj *_obj, CValue key, CValue val) cosmoO_setRawObject(state, object, key, val, _obj); } -COSMO_API void cosmoV_get(CState *state) +void cosmoV_get(CState *state) { CValue val; StkPtr obj = cosmoV_getTop(state, 1); // object was pushed first @@ -548,7 +548,7 @@ COSMO_API void cosmoV_get(CState *state) } // yes, this would technically make it possible to set fields of types other than . go crazy -COSMO_API void cosmoV_set(CState *state) +void cosmoV_set(CState *state) { StkPtr obj = cosmoV_getTop(state, 2); // object was pushed first StkPtr key = cosmoV_getTop(state, 1); // then the key @@ -564,7 +564,7 @@ COSMO_API void cosmoV_set(CState *state) cosmoV_setTop(state, 3); } -COSMO_API void cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val) +void cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val) { cosmoV_rawget(state, obj, key, val); @@ -578,6 +578,19 @@ COSMO_API void cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *va } } +bool cosmoV_isValueUserType(CState *state, CValue val, int userType) { + if (!IS_OBJECT(val)) { + return false; + } + + CObjObject *obj = cosmoV_readObject(val); + if (obj->userT != userType) { + return false; + } + + return true; +} + int _tbl__next(CState *state, int nargs, CValue *args) { if (nargs != 1) { @@ -836,7 +849,8 @@ int cosmoV_execute(CState *state) CObj *obj = cosmoV_readRef(*temp); CObjObject *proto = cosmoO_grabProto(obj); - CValue val; // to hold our value + CValue val = cosmoV_newNil(); // to hold our value + if (proto != NULL) { // check for __index metamethod @@ -907,7 +921,7 @@ int cosmoV_execute(CState *state) } CASE(OP_GETOBJECT) : { - CValue val; // to hold our value + CValue val = cosmoV_newNil(); // to hold our value StkPtr temp = cosmoV_getTop(state, 0); // that should be the object uint16_t ident = READUINT(frame); // use for the key @@ -925,7 +939,7 @@ int cosmoV_execute(CState *state) } CASE(OP_GETMETHOD) : { - CValue val; // to hold our value + CValue val = cosmoV_newNil(); // to hold our value StkPtr temp = cosmoV_getTop(state, 0); // that should be the object uint16_t ident = READUINT(frame); // use for the key diff --git a/src/cvm.h b/src/cvm.h index d4bbada..4eac843 100644 --- a/src/cvm.h +++ b/src/cvm.h @@ -36,11 +36,11 @@ COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...); COSMO_API void cosmoV_printError(CState *state, CObjError *err); COSMO_API void cosmoV_throw(CState *state); COSMO_API void cosmoV_error(CState *state, const char *format, ...); -COSMO_API void cosmo_insert(CState *state, int indx, CValue val); +COSMO_API void cosmoV_insert(CState *state, int indx, CValue val); /* Sets the default proto objects for the passed objType. Also walks through the object heap and - updates protos for the passed objType if that CObj* has no proto. + updates protos for the passed objType if that CObj* has no proto. returns true if replacing a previously registered proto object for this type */ @@ -72,13 +72,16 @@ COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud) COSMO_API void cosmoV_get(CState *state); /* - expects object to be pushed, then the key, and finally the new value. pops the key & object + expects object to be pushed, then the key, and finally the new value. pops the object, key & value */ COSMO_API void cosmoV_set(CState *state); // wraps the closure into a CObjMethod, so the function is called as an invoked method COSMO_API void cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val); +// check if the value at the top of the stack is a user type +COSMO_API bool cosmoV_isValueUserType(CState *state, CValue val, int userType); + // nice to have wrappers // pushes a raw CValue to the stack, might throw an error if the stack is overflowed (with the