Minor VM refactor + GC bug fix

The VM now respects metamethods from proto objects connected to tables. Also the CState is frozen while being instantiated.
This commit is contained in:
CPunch 2021-01-23 17:16:45 -06:00
parent 0ad0df5fba
commit fd1481fa43
3 changed files with 158 additions and 187 deletions

View File

@ -7,13 +7,19 @@
//#define GC_STRESS //#define GC_STRESS
//#define GC_DEBUG //#define GC_DEBUG
// arrays will grow by a factor of 2 // arrays *must* grow by a factor of 2
#define GROW_FACTOR 2 #define GROW_FACTOR 2
#define HEAP_GROW_FACTOR 2 #define HEAP_GROW_FACTOR 2
#define ARRAY_START 8 #define ARRAY_START 8
#ifdef GC_DEBUG
#define cosmoM_freearray(state, type, buf, capacity) \ #define cosmoM_freearray(state, type, buf, capacity) \
cosmoM_reallocate(state, buf, sizeof(type) *capacity, 0) printf("freeing array %p [size %lu] at %s:%d\n", buf, sizeof(type) * capacity, __FILE__, __LINE__); \
cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0)
#else
#define cosmoM_freearray(state, type, buf, capacity) \
cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0)
#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) { \
@ -22,8 +28,14 @@
buf = (type*)cosmoM_reallocate(state, buf, sizeof(type) *old, sizeof(type) *capacity); \ buf = (type*)cosmoM_reallocate(state, buf, sizeof(type) *old, sizeof(type) *capacity); \
} }
#ifdef GC_DEBUG
#define cosmoM_free(state, type, x) \
printf("freeing %p [size %lu] at %s:%d\n", x, sizeof(type), __FILE__, __LINE__); \
cosmoM_reallocate(state, x, sizeof(type), 0)
#else
#define cosmoM_free(state, type, x) \ #define cosmoM_free(state, type, x) \
cosmoM_reallocate(state, x, sizeof(type), 0) cosmoM_reallocate(state, x, sizeof(type), 0)
#endif
#define cosmoM_isFrozen(state) \ #define cosmoM_isFrozen(state) \
(state->freezeGC > 0) (state->freezeGC > 0)

View File

@ -16,7 +16,7 @@ CState *cosmoV_newState() {
} }
state->panic = false; state->panic = false;
state->freezeGC = false; state->freezeGC = 1; // we start frozen
// GC // GC
state->objects = NULL; state->objects = NULL;
@ -38,7 +38,6 @@ CState *cosmoV_newState() {
for (int i = 0; i < COBJ_MAX; i++) for (int i = 0; i < COBJ_MAX; i++)
state->protoObjects[i] = NULL; state->protoObjects[i] = NULL;
// first, set all strings to NULL so our GC doesn't read garbage data
for (int i = 0; i < ISTRING_MAX; i++) for (int i = 0; i < ISTRING_MAX; i++)
state->iStrings[i] = NULL; state->iStrings[i] = NULL;
@ -68,13 +67,16 @@ CState *cosmoV_newState() {
for (int i = 0; i < ISTRING_MAX; i++) for (int i = 0; i < ISTRING_MAX; i++)
state->iStrings[i]->isIString = true; state->iStrings[i]->isIString = true;
state->freezeGC = 0; // unfreeze the state
return state; return state;
} }
void cosmoV_freeState(CState *state) { void cosmoV_freeState(CState *state) {
#ifdef GC_DEBUG #ifdef GC_DEBUG
printf("state %p is being freed!\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) {
@ -92,7 +94,15 @@ void cosmoV_freeState(CState *state) {
cosmoT_clearTable(state, &state->globals); cosmoT_clearTable(state, &state->globals);
// free our gray stack & finally free the state structure // free our gray stack & finally free the state structure
free(state->grayStack.array); cosmoM_freearray(state, CObj*, state->grayStack.array, state->grayStack.capacity);
// 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);
} }

227
src/cvm.c
View File

@ -691,37 +691,30 @@ int cosmoV_execute(CState *state) {
StkPtr temp = cosmoV_getTop(state, 1); // after that should be the table StkPtr temp = cosmoV_getTop(state, 1); // after that should be the table
// sanity check // sanity check
if (IS_OBJ(*temp)) { if (!IS_OBJ(*temp)) {
cosmoV_error(state, "Couldn't index type %s!", cosmoV_typeStr(*temp));
return -1;
}
CObj *obj = cosmoV_readObj(*temp);
CObjObject *proto = cosmoO_grabProto(obj);
CValue val; // to hold our value CValue val; // to hold our value
switch (cosmoV_readObj(*temp)->type) { if (proto != NULL) {
case COBJ_TABLE: { // check for __index metamethod
CObjTable *tbl = (CObjTable*)cosmoV_readObj(*temp); if (!cosmoO_indexObject(state, proto, *key, &val)) // if returns false, cosmoV_error was called
return -1;
} else if (obj->type == COBJ_TABLE) {
CObjTable *tbl = (CObjTable*)obj;
cosmoT_get(&tbl->tbl, *key, &val); cosmoT_get(&tbl->tbl, *key, &val);
break; } else {
}
default: { // check for __index metamethod
CObjObject *object = cosmoO_grabProto(cosmoV_readObj(*temp));
if (object == NULL) {
cosmoV_error(state, "No proto defined! Couldn't __index from type %s", cosmoV_typeStr(*temp)); cosmoV_error(state, "No proto defined! Couldn't __index from type %s", cosmoV_typeStr(*temp));
return -1; return -1;
} }
if (!cosmoO_indexObject(state, object, *key, &val)) // if returns false, cosmoV_error was called
return -1;
break;
}
}
cosmoV_setTop(state, 2); // pops the table & the key cosmoV_setTop(state, 2); // pops the table & the key
cosmoV_pushValue(state, val); // pushes the field result cosmoV_pushValue(state, val); // pushes the field result
} else {
cosmoV_error(state, "Couldn't index type %s!", cosmoV_typeStr(*temp));
}
continue; continue;
} }
case OP_NEWINDEX: { case OP_NEWINDEX: {
@ -730,37 +723,29 @@ int cosmoV_execute(CState *state) {
StkPtr temp = cosmoV_getTop(state, 2); // table is after the key StkPtr temp = cosmoV_getTop(state, 2); // table is after the key
// sanity check // sanity check
if (IS_OBJ(*temp)) { if (!IS_OBJ(*temp)) {
switch (cosmoV_readObj(*temp)->type) {
case COBJ_TABLE: { // index and set table
CObjTable *tbl = (CObjTable*)cosmoV_readObj(*temp);
CValue *newVal = cosmoT_insert(state, &tbl->tbl, *key);
*newVal = *value; // set the index
break;
}
default: { // check for __newindex
CObjObject *object = cosmoO_grabProto(cosmoV_readObj(*temp));
if (object == NULL) {
cosmoV_error(state, "No proto defined! Couldn't __newindex from type %s", cosmoV_typeStr(*temp));
return -1;
}
if (!cosmoO_newIndexObject(state, object, *key, *value)) // if it returns false, cosmoV_error was called
return -1;
break;
}
}
// pop everything off the stack
cosmoV_setTop(state, 3);
} else {
cosmoV_error(state, "Couldn't set index with type %s!", cosmoV_typeStr(*temp)); cosmoV_error(state, "Couldn't set index with type %s!", cosmoV_typeStr(*temp));
return -1; return -1;
} }
CObj *obj = cosmoV_readObj(*temp);
CObjObject *proto = cosmoO_grabProto(obj);
if (proto != NULL) {
if (!cosmoO_newIndexObject(state, proto, *key, *value)) // if it returns false, cosmoV_error was called
return -1;
} else if (obj->type == COBJ_TABLE) {
CObjTable *tbl = (CObjTable*)obj;
CValue *newVal = cosmoT_insert(state, &tbl->tbl, *key);
*newVal = *value; // set the index
} else {
cosmoV_error(state, "No proto defined! Couldn't __newindex from type %s", cosmoV_typeStr(*temp));
return -1;
}
// pop everything off the stack
cosmoV_setTop(state, 3);
continue; continue;
} }
case OP_NEWOBJECT: { case OP_NEWOBJECT: {
@ -856,40 +841,12 @@ int cosmoV_execute(CState *state) {
} }
CObj *obj = cosmoV_readObj(*temp); CObj *obj = cosmoV_readObj(*temp);
switch (obj->type) { CObjObject *proto = cosmoO_grabProto(obj);
case COBJ_TABLE: {
CObjTable *tbl = (CObjTable*)obj;
cosmoV_pushValue(state, cosmoV_newObj(state->iStrings[ISTRING_RESERVED])); // key
cosmoV_pushValue(state, cosmoV_newObj(tbl)); // value
cosmoV_pushString(state, "__next"); // key
CObjCFunction *tbl_next = cosmoO_newCFunction(state, _tbl__next);
cosmoV_pushValue(state, cosmoV_newObj(tbl_next)); // value
cosmoV_makeObject(state, 2); // pushes the new object to the stack
CObjObject *obj = cosmoV_readObject(*(cosmoV_getTop(state, 0)));
cosmoO_setUserI(state, obj, 0); // increment for iterator
// make our CObjMethod for OP_NEXT to call
CObjMethod *method = cosmoO_newMethod(state, cosmoV_newObj(tbl_next), (CObj*)obj);
cosmoV_setTop(state, 2); // pops the object & the tbl
cosmoV_pushValue(state, cosmoV_newObj(method)); // pushes the method for OP_NEXT
break;
}
default: {
CObjObject *object = cosmoO_grabProto(obj);
CValue val; CValue val;
if (object == NULL) { if (proto != NULL) {
cosmoV_error(state, "No proto defined! Couldn't get from type %s", cosmoO_typeStr(obj));
return -1;
}
// grab __iter & call it // grab __iter & call it
if (cosmoO_getIString(state, object, ISTRING_ITER, &val)) { if (cosmoO_getIString(state, proto, ISTRING_ITER, &val)) {
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_pushValue(state, cosmoV_newObj(obj)); cosmoV_pushValue(state, cosmoV_newObj(obj));
@ -909,10 +866,31 @@ int cosmoV_execute(CState *state) {
cosmoV_error(state, "Expected iterable object! '__iter' not defined!"); cosmoV_error(state, "Expected iterable object! '__iter' not defined!");
return -1; return -1;
} }
} else if (obj->type == COBJ_TABLE) {
CObjTable *tbl = (CObjTable*)obj;
break; cosmoV_pushValue(state, cosmoV_newObj(state->iStrings[ISTRING_RESERVED])); // key
} cosmoV_pushValue(state, cosmoV_newObj(tbl)); // value
cosmoV_pushString(state, "__next"); // key
CObjCFunction *tbl_next = cosmoO_newCFunction(state, _tbl__next);
cosmoV_pushValue(state, cosmoV_newObj(tbl_next)); // value
cosmoV_makeObject(state, 2); // pushes the new object to the stack
CObjObject *obj = cosmoV_readObject(*(cosmoV_getTop(state, 0)));
cosmoO_setUserI(state, obj, 0); // increment for iterator
// make our CObjMethod for OP_NEXT to call
CObjMethod *method = cosmoO_newMethod(state, cosmoV_newObj(tbl_next), (CObj*)obj);
cosmoV_setTop(state, 2); // pops the object & the tbl
cosmoV_pushValue(state, cosmoV_newObj(method)); // pushes the method for OP_NEXT
} else {
cosmoV_error(state, "No proto defined! Couldn't get from type %s", cosmoO_typeStr(obj));
return -1;
} }
continue; continue;
} }
case OP_NEXT: { case OP_NEXT: {
@ -1052,53 +1030,45 @@ int cosmoV_execute(CState *state) {
StkPtr temp = cosmoV_getTop(state, 1); // object should be above the key StkPtr temp = cosmoV_getTop(state, 1); // object should be above the key
StkPtr key = cosmoV_getTop(state, 0); // grabs key StkPtr key = cosmoV_getTop(state, 0); // grabs key
if (IS_OBJ(*temp)) { if (!IS_OBJ(*temp)) {
switch (cosmoV_readObj(*temp)->type) { cosmoV_error(state, "Couldn't index non-indexable type %s!", cosmoV_typeStr(*temp));
case COBJ_TABLE: {
CObjTable *tbl = (CObjTable*)cosmoV_readObj(*temp);
CValue *val = cosmoT_insert(state, &tbl->tbl, *key);
// pops tbl & key from stack
cosmoV_setTop(state, 2);
if (IS_NUMBER(*val)) {
cosmoV_pushValue(state, *val); // pushes old value onto the stack :)
*val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc);
} else {
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val));
return -1; return -1;
} }
break;
} CObj *obj = cosmoV_readObj(*temp);
default: { CObjObject *proto = cosmoO_grabProto(obj);
CObjObject *object = cosmoO_grabProto(cosmoV_readObj(*temp));
CValue val; CValue val;
if (object == NULL) { // call __index if the proto was found
cosmoV_error(state, "No proto defined! Couldn't __index from type %s", cosmoV_typeStr(*temp)); if (proto != NULL) {
return -1; if (cosmoO_indexObject(state, proto, *key, &val)) {
} if (!IS_NUMBER(val)) {
// call __index
if (cosmoO_indexObject(state, object, *key, &val)) {
if (IS_NUMBER(val)) {
cosmoV_pushValue(state, val); // pushes old value onto the stack :)
// call __newindex
if (!cosmoO_newIndexObject(state, object, *key, cosmoV_newNumber(cosmoV_readNumber(val) + inc)))
return -1;
} else {
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(val)); cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(val));
return -1; return -1;
} }
} else {
cosmoV_pushValue(state, val); // pushes old value onto the stack :)
// call __newindex
if (!cosmoO_newIndexObject(state, proto, *key, cosmoV_newNumber(cosmoV_readNumber(val) + inc)))
return -1;
} else
return -1; // cosmoO_indexObject failed and threw an error
} else if (obj->type == COBJ_TABLE) {
CObjTable *tbl = (CObjTable*)obj;
CValue *val = cosmoT_insert(state, &tbl->tbl, *key);
if (!IS_NUMBER(*val)) {
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val));
return -1; return -1;
} }
break;
} // pops tbl & key from stack
} cosmoV_setTop(state, 2);
cosmoV_pushValue(state, *val); // pushes old value onto the stack :)
*val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc); // sets table index
} else { } else {
cosmoV_error(state, "Couldn't set index with type %s!", cosmoV_typeStr(*temp)); cosmoV_error(state, "No proto defined! Couldn't __index from type %s", cosmoV_typeStr(*temp));
return -1; return -1;
} }
@ -1112,24 +1082,6 @@ int cosmoV_execute(CState *state) {
// sanity check // sanity check
if (IS_OBJ(*temp)) { if (IS_OBJ(*temp)) {
switch (cosmoV_readObj(*temp)->type) {
case COBJ_TABLE: {
CObjTable *tbl = (CObjTable*)cosmoV_readObj(*temp);
CValue *val = cosmoT_insert(state, &tbl->tbl, ident);
// pops tbl from stack
cosmoV_pop(state);
if (IS_NUMBER(*val)) {
cosmoV_pushValue(state, *val); // pushes old value onto the stack :)
*val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc);
} else {
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val));
return -1;
}
break;
}
default: {
CObj *obj = cosmoV_readObj(*temp); CObj *obj = cosmoV_readObj(*temp);
CValue val; CValue val;
@ -1148,9 +1100,6 @@ int cosmoV_execute(CState *state) {
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(val)); cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(val));
return -1; return -1;
} }
break;
}
}
} else { } else {
cosmoV_error(state, "Couldn't set a field on type %s!", cosmoV_typeStr(*temp)); cosmoV_error(state, "Couldn't set a field on type %s!", cosmoV_typeStr(*temp));
return -1; return -1;