removed 'roots', replaced with a registry table

- removed cosmoM_addRoot
- removed cosmoM_removeRoot
- renamed cosmoV_register to cosmoV_addGlobals
- added cosmoV_addRegistry
- added cosmoV_getRegistry
- added cosmoV_setProto
- added cosmoV_isValueUserType
This commit is contained in:
CPunch 2023-09-05 14:35:29 -05:00 committed by cpunch
parent 6701a63a63
commit bf36412699
9 changed files with 121 additions and 93 deletions

4
main.c
View File

@ -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);

View File

@ -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;
}
}

View File

@ -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)
{

View File

@ -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

View File

@ -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

View File

@ -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");

View File

@ -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 <object>->proto = <object> (2 total) to be on the stack
COSMO_API void cosmoV_setProto(CState *state);
COSMO_API void cosmoV_printStack(CState *state);

View File

@ -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 <string>. 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

View File

@ -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 <obj> 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