2020-10-28 05:16:30 +00:00
|
|
|
#include "cstate.h"
|
|
|
|
#include "ctable.h"
|
|
|
|
#include "cobj.h"
|
|
|
|
#include "cmem.h"
|
2020-11-17 01:58:16 +00:00
|
|
|
#include "cvm.h"
|
2020-10-28 05:16:30 +00:00
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
// we don't actually hash the whole string :eyes:
|
|
|
|
uint32_t hashString(const char *str, size_t sz) {
|
|
|
|
uint32_t hash = sz;
|
|
|
|
size_t step = (sz>>5)+1;
|
|
|
|
|
|
|
|
for (int i = sz; i >= step; i-=step)
|
|
|
|
hash = ((hash << 5) + (hash>>2)) + str[i-1];
|
|
|
|
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
|
2020-11-06 01:53:55 +00:00
|
|
|
CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type) {
|
2020-10-28 05:16:30 +00:00
|
|
|
CObj* obj = (CObj*)cosmoM_xmalloc(state, sz);
|
|
|
|
obj->type = type;
|
|
|
|
obj->isMarked = false;
|
|
|
|
|
|
|
|
obj->next = state->objects;
|
|
|
|
state->objects = obj;
|
2020-12-07 21:53:23 +00:00
|
|
|
|
|
|
|
obj->nextRoot = NULL;
|
2020-10-28 23:29:50 +00:00
|
|
|
#ifdef GC_DEBUG
|
|
|
|
printf("allocated %p with OBJ_TYPE %d\n", obj, type);
|
|
|
|
#endif
|
2020-10-28 05:16:30 +00:00
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
2020-11-06 01:53:55 +00:00
|
|
|
void cosmoO_free(CState *state, CObj* obj) {
|
2020-10-28 05:16:30 +00:00
|
|
|
#ifdef GC_DEBUG
|
|
|
|
printf("freeing %p [", obj);
|
|
|
|
printObject(obj);
|
|
|
|
printf("]\n");
|
|
|
|
#endif
|
|
|
|
switch(obj->type) {
|
|
|
|
case COBJ_STRING: {
|
|
|
|
CObjString *objStr = (CObjString*)obj;
|
2020-11-17 09:10:55 +00:00
|
|
|
cosmoM_freearray(state, char, objStr->str, objStr->length + 1);
|
2020-10-28 05:16:30 +00:00
|
|
|
cosmoM_free(state, CObjString, objStr);
|
|
|
|
break;
|
|
|
|
}
|
2020-11-04 04:10:51 +00:00
|
|
|
case COBJ_OBJECT: {
|
|
|
|
CObjObject *objTbl = (CObjObject*)obj;
|
2020-11-03 04:32:39 +00:00
|
|
|
cosmoT_clearTable(state, &objTbl->tbl);
|
2020-11-04 04:10:51 +00:00
|
|
|
cosmoM_free(state, CObjObject, objTbl);
|
2020-11-03 04:32:39 +00:00
|
|
|
break;
|
|
|
|
}
|
2020-10-28 05:16:30 +00:00
|
|
|
case COBJ_UPVALUE: {
|
|
|
|
cosmoM_free(state, CObjUpval, obj);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case COBJ_FUNCTION: {
|
|
|
|
CObjFunction *objFunc = (CObjFunction*)obj;
|
|
|
|
cleanChunk(state, &objFunc->chunk);
|
|
|
|
cosmoM_free(state, CObjFunction, objFunc);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case COBJ_CFUNCTION: {
|
|
|
|
cosmoM_free(state, CObjCFunction, obj);
|
|
|
|
break;
|
|
|
|
}
|
2020-11-10 01:44:12 +00:00
|
|
|
case COBJ_METHOD: {
|
|
|
|
cosmoM_free(state, CObjMethod, obj); // we don't own the closure or the object so /shrug
|
|
|
|
break;
|
|
|
|
}
|
2020-10-28 05:16:30 +00:00
|
|
|
case COBJ_CLOSURE: {
|
|
|
|
CObjClosure* closure = (CObjClosure*)obj;
|
|
|
|
cosmoM_freearray(state, CObjUpval*, closure->upvalues, closure->upvalueCount);
|
|
|
|
cosmoM_free(state, CObjClosure, closure);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-06 01:53:55 +00:00
|
|
|
bool cosmoO_equal(CObj* obj1, CObj* obj2) {
|
2020-10-28 05:16:30 +00:00
|
|
|
if (obj1->type != obj2->type)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
switch (obj1->type) {
|
|
|
|
case COBJ_STRING:
|
|
|
|
return obj1 == obj2; // compare pointers because we already intern all strings :)
|
2020-11-03 04:32:39 +00:00
|
|
|
case COBJ_CFUNCTION: {
|
|
|
|
CObjCFunction *cfunc1 = (CObjCFunction*)obj1;
|
|
|
|
CObjCFunction *cfunc2 = (CObjCFunction*)obj2;
|
|
|
|
return cfunc1->cfunc == cfunc2->cfunc;
|
|
|
|
}
|
2020-10-28 05:16:30 +00:00
|
|
|
default:
|
2020-11-03 04:32:39 +00:00
|
|
|
return false;
|
2020-10-28 05:16:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-10 01:44:12 +00:00
|
|
|
CObjObject *cosmoO_newObject(CState *state) {
|
|
|
|
CObjObject *obj = (CObjObject*)cosmoO_allocateBase(state, sizeof(CObjObject), COBJ_OBJECT);
|
2020-11-15 18:22:11 +00:00
|
|
|
obj->proto = state->protoObj;
|
2020-11-17 01:58:16 +00:00
|
|
|
obj->istringFlags = 0;
|
2020-11-13 23:39:47 +00:00
|
|
|
obj->user = NULL; // reserved for C API
|
|
|
|
cosmoV_pushValue(state, cosmoV_newObj(obj)); // so our GC can keep track of it
|
2020-11-10 01:44:12 +00:00
|
|
|
cosmoT_initTable(state, &obj->tbl, ARRAY_START);
|
|
|
|
cosmoV_pop(state);
|
2020-11-03 04:32:39 +00:00
|
|
|
|
2020-11-10 01:44:12 +00:00
|
|
|
return obj;
|
2020-11-03 04:32:39 +00:00
|
|
|
}
|
|
|
|
|
2020-10-28 05:16:30 +00:00
|
|
|
CObjFunction *cosmoO_newFunction(CState *state) {
|
2020-11-06 01:53:55 +00:00
|
|
|
CObjFunction *func = (CObjFunction*)cosmoO_allocateBase(state, sizeof(CObjFunction), COBJ_FUNCTION);
|
2020-10-28 05:16:30 +00:00
|
|
|
func->args = 0;
|
|
|
|
func->upvals = 0;
|
|
|
|
func->name = NULL;
|
|
|
|
|
|
|
|
initChunk(state, &func->chunk, ARRAY_START);
|
|
|
|
return func;
|
|
|
|
}
|
|
|
|
|
|
|
|
CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func) {
|
2020-11-06 01:53:55 +00:00
|
|
|
CObjCFunction *cfunc = (CObjCFunction*)cosmoO_allocateBase(state, sizeof(CObjCFunction), COBJ_CFUNCTION);
|
2020-10-28 05:16:30 +00:00
|
|
|
cfunc->cfunc = func;
|
|
|
|
return cfunc;
|
|
|
|
}
|
|
|
|
|
2020-11-13 05:04:09 +00:00
|
|
|
CObjMethod *cosmoO_newCMethod(CState *state, CObjCFunction *func, CObjObject *obj) {
|
|
|
|
CObjMethod *method = (CObjMethod*)cosmoO_allocateBase(state, sizeof(CObjMethod), COBJ_METHOD);
|
2020-11-17 21:07:56 +00:00
|
|
|
method->func = cosmoV_newObj(func);
|
2020-11-13 05:04:09 +00:00
|
|
|
method->obj = obj;
|
|
|
|
return method;
|
|
|
|
}
|
|
|
|
|
2020-11-10 01:44:12 +00:00
|
|
|
CObjMethod *cosmoO_newMethod(CState *state, CObjClosure *func, CObjObject *obj) {
|
|
|
|
CObjMethod *method = (CObjMethod*)cosmoO_allocateBase(state, sizeof(CObjMethod), COBJ_METHOD);
|
2020-11-17 21:07:56 +00:00
|
|
|
method->func = cosmoV_newObj(func);
|
2020-11-10 01:44:12 +00:00
|
|
|
method->obj = obj;
|
|
|
|
return method;
|
|
|
|
}
|
|
|
|
|
2020-10-28 05:16:30 +00:00
|
|
|
CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func) {
|
|
|
|
// intialize array of pointers
|
|
|
|
CObjUpval **upvalues = cosmoM_xmalloc(state, sizeof(CObjUpval*) * func->upvals);
|
|
|
|
|
|
|
|
for (int i = 0; i < func->upvals; i++) {
|
|
|
|
upvalues[i] = NULL;
|
|
|
|
}
|
|
|
|
|
2020-11-06 01:53:55 +00:00
|
|
|
CObjClosure *closure = (CObjClosure*)cosmoO_allocateBase(state, sizeof(CObjClosure), COBJ_CLOSURE);
|
2020-10-28 05:16:30 +00:00
|
|
|
closure->function = func;
|
|
|
|
closure->upvalues = upvalues;
|
|
|
|
closure->upvalueCount = func->upvals;
|
|
|
|
|
|
|
|
return closure;
|
|
|
|
}
|
|
|
|
|
|
|
|
CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val) {
|
2020-11-06 01:53:55 +00:00
|
|
|
CObjUpval *upval = (CObjUpval*)cosmoO_allocateBase(state, sizeof(CObjUpval), COBJ_UPVALUE);
|
2020-10-28 05:16:30 +00:00
|
|
|
upval->val = val;
|
|
|
|
upval->closed = cosmoV_newNil();
|
|
|
|
upval->next = NULL;
|
|
|
|
|
|
|
|
return upval;
|
|
|
|
}
|
|
|
|
|
|
|
|
CObjString *cosmoO_copyString(CState *state, const char *str, size_t sz) {
|
|
|
|
uint32_t hash = hashString(str, sz);
|
|
|
|
CObjString *lookup = cosmoT_lookupString(&state->strings, str, sz, hash);
|
|
|
|
|
|
|
|
// have we already interned this string?
|
|
|
|
if (lookup != NULL)
|
|
|
|
return lookup;
|
|
|
|
|
|
|
|
char *buf = cosmoM_xmalloc(state, sizeof(char) * (sz + 1)); // +1 for null terminator
|
|
|
|
memcpy(buf, str, sz); // copy string to heap
|
|
|
|
buf[sz] = '\0'; // don't forget our null terminator
|
|
|
|
|
|
|
|
return cosmoO_allocateString(state, buf, sz, hash);
|
|
|
|
}
|
|
|
|
|
|
|
|
CObjString *cosmoO_takeString(CState *state, char *str, size_t sz) {
|
|
|
|
uint32_t hash = hashString(str, sz);
|
|
|
|
|
|
|
|
CObjString *lookup = cosmoT_lookupString(&state->strings, str, sz, hash);
|
|
|
|
|
|
|
|
// have we already interned this string?
|
|
|
|
if (lookup != NULL) {
|
|
|
|
cosmoM_freearray(state, char, str, sz); // free our passed character array, it's unneeded!
|
|
|
|
return lookup;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cosmoO_allocateString(state, str, sz, hash);
|
|
|
|
}
|
|
|
|
|
|
|
|
CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uint32_t hash) {
|
2020-11-06 01:53:55 +00:00
|
|
|
CObjString *strObj = (CObjString*)cosmoO_allocateBase(state, sizeof(CObjString), COBJ_STRING);
|
2020-10-28 05:16:30 +00:00
|
|
|
strObj->str = (char*)str;
|
|
|
|
strObj->length = sz;
|
|
|
|
strObj->hash = hash;
|
|
|
|
|
|
|
|
// we push & pop the string so our GC can find it (we don't use freezeGC/unfreezeGC because we *want* a GC event to happen)
|
|
|
|
cosmoV_pushValue(state, cosmoV_newObj(strObj));
|
|
|
|
cosmoT_insert(state, &state->strings, cosmoV_newObj((CObj*)strObj));
|
|
|
|
cosmoV_pop(state);
|
|
|
|
|
|
|
|
return strObj;
|
|
|
|
}
|
|
|
|
|
2020-11-06 01:53:55 +00:00
|
|
|
bool cosmoO_getObject(CState *state, CObjObject *object, CValue key, CValue *val) {
|
2020-11-17 01:58:16 +00:00
|
|
|
if (!cosmoT_get(&object->tbl, key, val)) { // if the field doesn't exist in the object, check the proto
|
|
|
|
if (object->proto != NULL) { // sanity check
|
2020-12-06 20:11:33 +00:00
|
|
|
// first though, check for a member of the proto object, if it fails then lookup __getters
|
2020-11-20 21:10:49 +00:00
|
|
|
if (cosmoO_getObject(state, object->proto, key, val))
|
|
|
|
return true;
|
|
|
|
|
2020-12-06 20:11:33 +00:00
|
|
|
// if this fails or the key isn't in that table then we default to __index
|
|
|
|
if (cosmoO_getIString(state, object->proto, ISTRING_GETTER, val) && IS_OBJECT(*val) && cosmoO_getObject(state, cosmoV_readObject(*val), key, val)) {
|
|
|
|
cosmoV_pushValue(state, *val); // push function
|
|
|
|
cosmoV_pushValue(state, cosmoV_newObj(object)); // push object
|
|
|
|
cosmoV_call(state, 1); // call the function with the 1 argument
|
|
|
|
*val = *cosmoV_pop(state); // set value to the return value of __index
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-11-20 21:10:49 +00:00
|
|
|
// then check for __index, if that exists, call it
|
2020-11-17 01:58:16 +00:00
|
|
|
if (cosmoO_getIString(state, object->proto, ISTRING_INDEX, val)) {
|
|
|
|
cosmoV_pushValue(state, *val); // push function
|
|
|
|
cosmoV_pushValue(state, cosmoV_newObj(object)); // push object
|
|
|
|
cosmoV_pushValue(state, key); // push key
|
|
|
|
cosmoV_call(state, 2); // call the function with the 2 arguments
|
|
|
|
*val = *cosmoV_pop(state); // set value to the return value of __index
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false; // no protoobject to check against
|
2020-11-10 01:44:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2020-11-06 01:53:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void cosmoO_setObject(CState *state, CObjObject *object, CValue key, CValue val) {
|
2020-11-24 21:16:37 +00:00
|
|
|
CValue ret;
|
2020-12-06 20:11:33 +00:00
|
|
|
|
|
|
|
// if there's a prototype, check for the tag methods!
|
|
|
|
if (object->proto != NULL) {
|
|
|
|
// first check for __setters
|
|
|
|
if (cosmoO_getIString(state, object->proto, ISTRING_SETTER, &ret) && IS_OBJECT(ret) && cosmoO_getObject(state, cosmoV_readObject(ret), key, &ret)) {
|
|
|
|
cosmoV_pushValue(state, ret); // push function
|
|
|
|
cosmoV_pushValue(state, cosmoV_newObj(object)); // push object
|
|
|
|
cosmoV_pushValue(state, val); // push new value
|
|
|
|
cosmoV_call(state, 2);
|
|
|
|
cosmoV_pop(state); // pop return value
|
|
|
|
}
|
|
|
|
|
|
|
|
// then check for __newindex in the prototype
|
|
|
|
if (cosmoO_getIString(state, object->proto, ISTRING_NEWINDEX, &ret)) {
|
|
|
|
cosmoV_pushValue(state, ret); // push function
|
|
|
|
cosmoV_pushValue(state, cosmoV_newObj(object)); // push object
|
|
|
|
cosmoV_pushValue(state, key); // push key & value pair
|
|
|
|
cosmoV_pushValue(state, val);
|
|
|
|
cosmoV_call(state, 3);
|
|
|
|
cosmoV_pop(state); // pop return value
|
|
|
|
return;
|
|
|
|
}
|
2020-11-24 21:16:37 +00:00
|
|
|
}
|
|
|
|
|
2020-11-17 01:58:16 +00:00
|
|
|
object->istringFlags = 0; // reset cache
|
2020-11-24 21:16:37 +00:00
|
|
|
if (IS_NIL(val)) { // if we're setting an index to nil, we can safely mark that as a tombstone
|
|
|
|
cosmoT_remove(state, &object->tbl, key);
|
|
|
|
} else {
|
|
|
|
CValue *newVal = cosmoT_insert(state, &object->tbl, key);
|
|
|
|
*newVal = val;
|
|
|
|
}
|
2020-11-06 01:53:55 +00:00
|
|
|
}
|
|
|
|
|
2020-12-06 19:38:05 +00:00
|
|
|
void cosmoO_setUserData(CState *state, CObjObject *object, void *p) {
|
|
|
|
object->user = p;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *cosmoO_getUserData(CState *state, CObjObject *object) {
|
|
|
|
return object->user;
|
|
|
|
}
|
|
|
|
|
2020-11-17 01:58:16 +00:00
|
|
|
bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val) {
|
|
|
|
if (readFlag(object->istringFlags, flag))
|
|
|
|
return false; // it's been cached as bad
|
|
|
|
|
|
|
|
if (!cosmoO_getObject(state, object, cosmoV_newObj(state->iStrings[flag]), val)) {
|
|
|
|
// mark it bad!
|
|
|
|
setFlagOn(object->istringFlags, flag);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true; // :)
|
|
|
|
}
|
|
|
|
|
2020-11-10 01:44:12 +00:00
|
|
|
CObjString *cosmoO_toString(CState *state, CObj *obj) {
|
|
|
|
switch (obj->type) {
|
2020-10-28 05:16:30 +00:00
|
|
|
case COBJ_STRING: {
|
2020-11-10 01:44:12 +00:00
|
|
|
return (CObjString*)obj;
|
2020-10-28 05:16:30 +00:00
|
|
|
}
|
|
|
|
case COBJ_FUNCTION: {
|
2020-11-10 01:44:12 +00:00
|
|
|
CObjFunction *func = (CObjFunction*)obj;
|
2020-10-28 05:16:30 +00:00
|
|
|
return func->name != NULL ? func->name : cosmoO_copyString(state, UNNAMEDCHUNK, strlen(UNNAMEDCHUNK));
|
|
|
|
}
|
2020-11-04 04:10:51 +00:00
|
|
|
case COBJ_OBJECT: { // TODO: maybe not safe??
|
2020-11-03 04:32:39 +00:00
|
|
|
char buf[64];
|
2020-11-10 01:44:12 +00:00
|
|
|
int sz = sprintf(buf, "<obj> %p", obj) + 1; // +1 for the null character
|
2020-11-03 04:32:39 +00:00
|
|
|
return cosmoO_copyString(state, buf, sz);
|
|
|
|
}
|
2020-10-28 05:16:30 +00:00
|
|
|
default:
|
2020-11-10 01:44:12 +00:00
|
|
|
return cosmoO_copyString(state, "<unkn obj>", 10);
|
2020-10-28 05:16:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void printObject(CObj *o) {
|
|
|
|
switch (o->type) {
|
|
|
|
case COBJ_STRING: {
|
|
|
|
CObjString *objStr = (CObjString*)o;
|
|
|
|
printf("\"%.*s\"", objStr->length, objStr->str);
|
|
|
|
break;
|
|
|
|
}
|
2020-11-04 04:10:51 +00:00
|
|
|
case COBJ_OBJECT: {
|
|
|
|
printf("<obj> %p", o);
|
2020-11-03 04:32:39 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-10-28 05:16:30 +00:00
|
|
|
case COBJ_UPVALUE: {
|
|
|
|
CObjUpval *upval = (CObjUpval*)o;
|
|
|
|
printf("<upvalue %p> -> ", upval->val);
|
|
|
|
printValue(*upval->val);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case COBJ_FUNCTION: {
|
|
|
|
CObjFunction *objFunc = (CObjFunction*)o;
|
|
|
|
if (objFunc->name != NULL)
|
|
|
|
printf("<function> %.*s", objFunc->name->length, objFunc->name->str);
|
|
|
|
else
|
2020-11-10 01:44:12 +00:00
|
|
|
printf("<function> %s", UNNAMEDCHUNK);
|
2020-10-28 05:16:30 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case COBJ_CFUNCTION: {
|
|
|
|
CObjCFunction *objCFunc = (CObjCFunction*)o;
|
|
|
|
printf("<c function> %p", objCFunc->cfunc);
|
|
|
|
break;
|
|
|
|
}
|
2020-11-10 01:44:12 +00:00
|
|
|
case COBJ_METHOD: {
|
|
|
|
CObjMethod *method = (CObjMethod*)o;
|
2020-11-17 21:07:56 +00:00
|
|
|
printf("<method> %p : ", method->obj);
|
|
|
|
printValue(method->func);
|
2020-11-10 01:44:12 +00:00
|
|
|
break;
|
|
|
|
}
|
2020-10-28 05:16:30 +00:00
|
|
|
case COBJ_CLOSURE: {
|
|
|
|
CObjClosure *closure = (CObjClosure*)o;
|
|
|
|
printObject((CObj*)closure->function); // just print the function
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
2020-11-10 01:44:12 +00:00
|
|
|
printf("<unkn obj>");
|
2020-10-28 05:16:30 +00:00
|
|
|
}
|
2020-11-30 18:32:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *cosmoO_typeStr(CObj* obj) {
|
|
|
|
switch (obj->type) {
|
|
|
|
case COBJ_STRING: return "<string>";
|
|
|
|
case COBJ_OBJECT: return "<object>";
|
|
|
|
case COBJ_FUNCTION: return "<function>";
|
|
|
|
case COBJ_CFUNCTION: return "<c function>";
|
|
|
|
case COBJ_METHOD: return "<method>";
|
|
|
|
case COBJ_CLOSURE: return "<closure>";
|
|
|
|
case COBJ_UPVALUE: return "<upvalue>";
|
|
|
|
|
|
|
|
default:
|
|
|
|
return "<unkn obj>"; // TODO: maybe panic? could be a malformed object :eyes:
|
|
|
|
}
|
2020-10-28 05:16:30 +00:00
|
|
|
}
|