From 66d77bc54b8deabf39a8a89c1947708f611a8248 Mon Sep 17 00:00:00 2001 From: cpunch Date: Fri, 19 Feb 2021 17:04:23 -0600 Subject: [PATCH] Refactored cosmoO_equals This sets up room for the '__equal' metamethod to be added - cosmoO_equals now requires the state to be passed - cosmoV_equals now requires the state to be passed - cosmoT_get now requires the state to be passed --- src/cbaselib.c | 13 +++---------- src/cchunk.c | 2 +- src/cmem.c | 1 + src/cobj.c | 45 ++++++++++++++++++++++++++++++++++----------- src/cobj.h | 2 +- src/ctable.c | 14 +++++++------- src/ctable.h | 2 +- src/cvalue.c | 4 ++-- src/cvalue.h | 2 +- src/cvm.c | 6 +++--- src/cvm.h | 4 ++++ 11 files changed, 58 insertions(+), 37 deletions(-) diff --git a/src/cbaselib.c b/src/cbaselib.c index 6d7e52d..bcca5e4 100644 --- a/src/cbaselib.c +++ b/src/cbaselib.c @@ -32,21 +32,14 @@ int cosmoB_assert(CState *state, int nargs, CValue *args) { if (!IS_BOOLEAN(args[0]) || (nargs == 2 && !IS_STRING(args[1]))) { if (nargs == 2) { cosmoV_typeError(state, "assert()", ", ", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); - } - else { + } else { cosmoV_typeError(state, "assert()", "", "%s", cosmoV_typeStr(args[0])); } return 0; } - if (!cosmoV_readBoolean(args[0])) { // expression passed was false, error! - if (nargs == 2) { - cosmoV_error(state, "%s", cosmoV_readCString(args[1])); - } - else { // optional custom error message - cosmoV_error(state, "%s", "assert() failed!"); - } - } // else do nothing :) + if (!cosmoV_readBoolean(args[0])) // expression passed was false, error! + cosmoV_error(state, "%s", nargs == 2 ? cosmoV_readCString(args[1]) : "assert() failed!"); return 0; } diff --git a/src/cchunk.c b/src/cchunk.c index 7d58b0e..107e560 100644 --- a/src/cchunk.c +++ b/src/cchunk.c @@ -39,7 +39,7 @@ void freeChunk(CState* state, CChunk *chunk) { int addConstant(CState* state, CChunk *chunk, CValue value) { // before adding the constant, check if we already have it for (size_t i = 0; i < chunk->constants.count; i++) { - if (cosmoV_equal(value, chunk->constants.values[i])) + if (cosmoV_equal(state, value, chunk->constants.values[i])) return i; // we already have a matching constant! } diff --git a/src/cmem.c b/src/cmem.c index 9770022..a07868c 100644 --- a/src/cmem.c +++ b/src/cmem.c @@ -86,6 +86,7 @@ void markArray(CState *state, CValueArray *array) { } // mark all references associated with the object +// black = keep, white = discard void blackenObject(CState *state, CObj *obj) { markObject(state, (CObj*)obj->proto); switch (obj->type) { diff --git a/src/cobj.c b/src/cobj.c index 3da6e87..95550c5 100644 --- a/src/cobj.c +++ b/src/cobj.c @@ -90,25 +90,48 @@ void cosmoO_free(CState *state, CObj* obj) { cosmoM_free(state, CObjClosure, closure); break; } - case COBJ_MAX: { /* stubbed, should never happen */ } + case COBJ_MAX: + default: { /* stubbed, should never happen */ } } } -bool cosmoO_equal(CObj* obj1, CObj* obj2) { +bool cosmoO_equal(CState *state, CObj* obj1, CObj* obj2) { + if (obj1 == obj2) // its the same object, this compares strings for us since they're interned anyways :) + return true; + if (obj1->type != obj2->type) - return false; + goto _eqFail; switch (obj1->type) { - case COBJ_STRING: - return obj1 == obj2; // compare pointers because we already intern all strings :) case COBJ_CFUNCTION: { CObjCFunction *cfunc1 = (CObjCFunction*)obj1; CObjCFunction *cfunc2 = (CObjCFunction*)obj2; - return cfunc1->cfunc == cfunc2->cfunc; + if (cfunc1->cfunc == cfunc2->cfunc) + return true; + goto _eqFail; + } + case COBJ_METHOD: { + CObjMethod *method1 = (CObjMethod*)obj1; + CObjMethod *method2 = (CObjMethod*)obj2; + if (cosmoV_equal(state, method1->func, method2->func)) + return true; + goto _eqFail; + } + case COBJ_CLOSURE: { + CObjClosure *closure1 = (CObjClosure*)obj1; + CObjClosure *closure2 = (CObjClosure*)obj2; + // we just compare the function pointer + if (closure1->function == closure2->function) + return true; + goto _eqFail; } default: - return false; + goto _eqFail; } + +_eqFail: + // TODO: add support for an '__equal' metamethod + return false; } CObjObject *cosmoO_newObject(CState *state) { @@ -312,8 +335,8 @@ bool cosmoO_isDescendant(CObj *obj, CObjObject *proto) { // returns false if error thrown bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj) { - if (!cosmoT_get(&proto->tbl, key, val)) { // if the field doesn't exist in the object, check the proto - if (cosmoO_getIString(state, proto, ISTRING_GETTER, val) && IS_TABLE(*val) && cosmoT_get(&cosmoV_readTable(*val)->tbl, key, val)) { + if (!cosmoT_get(state, &proto->tbl, key, val)) { // if the field doesn't exist in the object, check the proto + if (cosmoO_getIString(state, proto, ISTRING_GETTER, val) && IS_TABLE(*val) && cosmoT_get(state, &cosmoV_readTable(*val)->tbl, key, val)) { cosmoV_pushValue(state, *val); // push function cosmoV_pushRef(state, (CObj*)obj); // push object if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // call the function with the 1 argument @@ -342,7 +365,7 @@ void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue va } // check for __setters - if (cosmoO_getIString(state, proto, ISTRING_SETTER, &ret) && IS_TABLE(ret) && cosmoT_get(&cosmoV_readTable(ret)->tbl, key, &ret)) { + if (cosmoO_getIString(state, proto, ISTRING_SETTER, &ret) && IS_TABLE(ret) && cosmoT_get(state, &cosmoV_readTable(ret)->tbl, key, &ret)) { cosmoV_pushValue(state, ret); // push function cosmoV_pushRef(state, (CObj*)obj); // push object cosmoV_pushValue(state, val); // push new value @@ -398,7 +421,7 @@ bool rawgetIString(CState *state, CObjObject *object, int flag, CValue *val) { if (readFlag(object->istringFlags, flag)) return false; // it's been cached as bad - if (!cosmoT_get(&object->tbl, cosmoV_newRef(state->iStrings[flag]), val)) { + if (!cosmoT_get(state, &object->tbl, cosmoV_newRef(state->iStrings[flag]), val)) { // mark it bad! setFlagOn(object->istringFlags, flag); return false; diff --git a/src/cobj.h b/src/cobj.h index 47bffec..32f7bf6 100644 --- a/src/cobj.h +++ b/src/cobj.h @@ -136,7 +136,7 @@ static inline bool IS_CALLABLE(CValue val) { } void cosmoO_free(CState *state, CObj* obj); -bool cosmoO_equal(CObj* obj1, CObj* obj2); +bool cosmoO_equal(CState *state, CObj* obj1, CObj* obj2); // walks the protos of obj and checks for proto bool cosmoO_isDescendant(CObj *obj, CObjObject *proto); diff --git a/src/ctable.c b/src/ctable.c index 333deed..8cbf3a7 100644 --- a/src/ctable.c +++ b/src/ctable.c @@ -86,7 +86,7 @@ uint32_t getValueHash(CValue *val) { } // mask should always be (capacity - 1) -static CTableEntry *findEntry(CTableEntry *entries, int mask, CValue key) { +static CTableEntry *findEntry(CState *state, CTableEntry *entries, int mask, CValue key) { uint32_t hash = getValueHash(&key); uint32_t indx = hash & mask; // since we know the capacity will *always* be a power of 2, we can use bitwise & to perform a MUCH faster mod operation CTableEntry *tomb = NULL; @@ -104,7 +104,7 @@ static CTableEntry *findEntry(CTableEntry *entries, int mask, CValue key) { // its a tombstone! tomb = entry; } - } else if (cosmoV_equal(entry->key, key)) { + } else if (cosmoV_equal(state, entry->key, key)) { return entry; } @@ -142,7 +142,7 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin continue; // skip empty keys // get new entry location & update the node - CTableEntry *newEntry = findEntry(entries, newCapacity - 1, oldEntry->key); + CTableEntry *newEntry = findEntry(state, entries, newCapacity - 1, oldEntry->key); newEntry->key = oldEntry->key; newEntry->val = oldEntry->val; newCount++; // inc count @@ -178,7 +178,7 @@ COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) { } // insert into the table - CTableEntry *entry = findEntry(tbl->table, tbl->capacityMask, key); // -1 for our capacity mask + CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key); // -1 for our capacity mask if (IS_NIL(entry->key)) { if (IS_NIL(entry->val)) // is it empty? @@ -191,14 +191,14 @@ COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) { return &entry->val; } -bool cosmoT_get(CTable *tbl, CValue key, CValue *val) { +bool cosmoT_get(CState *state, CTable *tbl, CValue key, CValue *val) { // sanity check if (tbl->count == 0) { *val = cosmoV_newNil(); return false; } - CTableEntry *entry = findEntry(tbl->table, tbl->capacityMask, key); + CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key); *val = entry->val; // return if get was successful @@ -208,7 +208,7 @@ bool cosmoT_get(CTable *tbl, CValue key, CValue *val) { bool cosmoT_remove(CState* state, CTable *tbl, CValue key) { if (tbl->count == 0) return 0; // sanity check - CTableEntry *entry = findEntry(tbl->table, tbl->capacityMask, key); + CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key); if (IS_NIL(entry->key)) // sanity check return false; diff --git a/src/ctable.h b/src/ctable.h index 31dd125..3287c93 100644 --- a/src/ctable.h +++ b/src/ctable.h @@ -26,7 +26,7 @@ bool cosmoT_checkShrink(CState *state, CTable *tbl); CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32_t hash); CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key); -bool cosmoT_get(CTable *tbl, CValue key, CValue *val); +bool cosmoT_get(CState *state, CTable *tbl, CValue key, CValue *val); bool cosmoT_remove(CState *state, CTable *tbl, CValue key); void cosmoT_printTable(CTable *tbl, const char *name); diff --git a/src/cvalue.c b/src/cvalue.c index ee58775..1ded646 100644 --- a/src/cvalue.c +++ b/src/cvalue.c @@ -19,7 +19,7 @@ void appendValArray(CState *state, CValueArray *array, CValue val) { array->values[array->count++] = val; } -bool cosmoV_equal(CValue valA, CValue valB) { +bool cosmoV_equal(CState *state, CValue valA, CValue valB) { if (GET_TYPE(valA) != GET_TYPE(valB)) // are they the same type? return false; @@ -27,7 +27,7 @@ bool cosmoV_equal(CValue valA, CValue valB) { switch (GET_TYPE(valA)) { case COSMO_TBOOLEAN: return cosmoV_readBoolean(valA) == cosmoV_readBoolean(valB); case COSMO_TNUMBER: return cosmoV_readNumber(valA) == cosmoV_readNumber(valB); - case COSMO_TREF: return cosmoO_equal(cosmoV_readRef(valA), cosmoV_readRef(valB)); + case COSMO_TREF: return cosmoO_equal(state, cosmoV_readRef(valA), cosmoV_readRef(valB)); case COSMO_TNIL: return true; default: return false; diff --git a/src/cvalue.h b/src/cvalue.h index 0ee8895..469c285 100644 --- a/src/cvalue.h +++ b/src/cvalue.h @@ -114,7 +114,7 @@ void cleanValArray(CState *state, CValueArray *array); // cleans array void appendValArray(CState *state, CValueArray *array, CValue val); void printValue(CValue val); -COSMO_API bool cosmoV_equal(CValue valA, CValue valB); +COSMO_API bool cosmoV_equal(CState *state, CValue valA, CValue valB); COSMO_API CObjString *cosmoV_toString(CState *state, CValue val); COSMO_API cosmo_Number cosmoV_toNumber(CState *state, CValue val); COSMO_API const char *cosmoV_typeStr(CValue val); // return constant char array for corresponding type diff --git a/src/cvm.c b/src/cvm.c index 0fc21bf..3c6ab35 100644 --- a/src/cvm.c +++ b/src/cvm.c @@ -633,7 +633,7 @@ int cosmoV_execute(CState *state) { uint16_t indx = READUINT(); CValue ident = constants[indx]; // grabs identifier CValue val; // to hold our value - cosmoT_get(&state->globals->tbl, ident, &val); + cosmoT_get(state, &state->globals->tbl, ident, &val); cosmoV_pushValue(state, val); // pushes the value to the stack continue; } @@ -766,7 +766,7 @@ int cosmoV_execute(CState *state) { } else if (obj->type == COBJ_TABLE) { CObjTable *tbl = (CObjTable*)obj; - cosmoT_get(&tbl->tbl, *key, &val); + cosmoT_get(state, &tbl->tbl, *key, &val); } else { cosmoV_error(state, "No proto defined! Couldn't __index from type %s", cosmoV_typeStr(*temp)); return -1; @@ -1182,7 +1182,7 @@ int cosmoV_execute(CState *state) { StkPtr valA = cosmoV_pop(state); // compare & push - cosmoV_pushBoolean(state, cosmoV_equal(*valA, *valB)); + cosmoV_pushBoolean(state, cosmoV_equal(state, *valA, *valB)); continue; } case OP_GREATER: { diff --git a/src/cvm.h b/src/cvm.h index 0519527..a3b0595 100644 --- a/src/cvm.h +++ b/src/cvm.h @@ -62,6 +62,10 @@ COSMO_API bool cosmoV_set(CState *state); // wraps the closure into a CObjMethod, so the function is called as an invoked method COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val); +// clears the stack, callstack and restores the state into a usable state after a calloverflow or another hard to recover error +// (keeps the global table intact) +COSMO_API bool cosmoV_restore(CState *state); + // nice to have wrappers // pushes a raw CValue to the stack, might throw an error if the stack is overflowed (with the SAFE_STACK macro on)