CObjMethod refactor

This commit is contained in:
CPunch 2020-11-12 23:04:09 -06:00
parent 40ae495aaf
commit 08b6dcbf4c
12 changed files with 135 additions and 65 deletions

View File

@ -1,8 +1,15 @@
-- makes a hundred thousand entries in the hashtable
var test = {}
for (var i = 0; i < 100000; i=i+1) do
test[i] = "" .. i*i
class test
function __init(self, x)
self.x = x
end
print(test[4]) -- should print '16'
function print(self)
print(self.x)
return self.x
end
end
var t = test("hello")
print(t.print())

View File

@ -4,7 +4,7 @@
#include "cobj.h"
#include "cmem.h"
void cosmoB_loadlibrary(CState *state) {
void cosmoB_loadLibrary(CState *state) {
cosmoM_freezeGC(state);
cosmoV_register(state, "print", cosmoV_newObj(cosmoO_newCFunction(state, cosmoB_print)));
cosmoM_unfreezeGC(state);
@ -19,3 +19,27 @@ CValue cosmoB_print(CState *state, int nargs, CValue *args) {
return cosmoV_newNil(); // print doesn't return any args
}
CValue cosmoB_dsetMeta(CState *state, int nargs, CValue *args) {
if (nargs == 2) {
CObjObject *obj = cosmoV_readObject(args[0]); // object to set meta too
CObjObject *meta = cosmoV_readObject(args[1]);
obj->meta = meta; // boom done
}
return cosmoV_newNil(); // nothing
}
CValue cosmoB_dgetMeta(CState *state, int nargs, CValue *args) {
return cosmoV_newObj(cosmoV_readObject(args[0])->meta); // just return the meta
}
void cosmoB_loadDebug(CState *state) {
cosmoV_pushString(state, "getMeta");
cosmoV_pushCFunction(state, cosmoB_dgetMeta);
cosmoV_pushString(state, "setMeta");
cosmoV_pushCFunction(state, cosmoB_dsetMeta);
cosmoV_pushObject(state, 2);
state->metaObj = cosmoV_readObject(*cosmoV_pop(state));
}

View File

@ -3,7 +3,9 @@
#include "cstate.h"
COSMO_API void cosmoB_loadlibrary(CState *state);
COSMO_API void cosmoB_loadLibrary(CState *state);
COSMO_API void cosmoB_loadDebug(CState *state);
COSMO_API CValue cosmoB_print(CState *state, int nargs, CValue *args);
#endif

View File

@ -4,6 +4,7 @@
#include "ctable.h"
#include "cparse.h"
#include "cobj.h"
#include "cbaselib.h"
/*
copy buffer to new larger buffer, and free the old buffer
@ -213,6 +214,8 @@ void markRoots(CState *state) {
for (int i = 0; i < INTERNALSTRING_MAX; i++)
markObject(state, (CObj*)state->internalStrings[i]);
// mark our meta object
markObject(state, (CObj*)state->metaObj);
traceGrays(state);
}

View File

@ -94,7 +94,7 @@ bool cosmoO_equal(CObj* obj1, CObj* obj2) {
CObjObject *cosmoO_newObject(CState *state) {
CObjObject *obj = (CObjObject*)cosmoO_allocateBase(state, sizeof(CObjObject), COBJ_OBJECT);
obj->meta = NULL;
obj->meta = state->metaObj;
cosmoV_pushValue(state, cosmoV_newObj(obj)); // so out GC can keep track of it
cosmoT_initTable(state, &obj->tbl, ARRAY_START);
cosmoV_pop(state);
@ -118,10 +118,19 @@ CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func) {
return cfunc;
}
CObjMethod *cosmoO_newCMethod(CState *state, CObjCFunction *func, CObjObject *obj) {
CObjMethod *method = (CObjMethod*)cosmoO_allocateBase(state, sizeof(CObjMethod), COBJ_METHOD);
method->cfunc = func;
method->obj = obj;
method->isCFunc = true;
return method;
}
CObjMethod *cosmoO_newMethod(CState *state, CObjClosure *func, CObjObject *obj) {
CObjMethod *method = (CObjMethod*)cosmoO_allocateBase(state, sizeof(CObjMethod), COBJ_METHOD);
method->closure = func;
method->obj = obj;
method->isCFunc = false;
return method;
}

View File

@ -65,7 +65,11 @@ typedef struct CObjClosure {
typedef struct CObjMethod {
CommonHeader; // "is a " CObj
CObjObject *obj; // obj this method is bound too
CObjClosure *closure; // TODO: change this to a union to a pointer to a closure object or a c function object
union {
CObjClosure *closure;
CObjCFunction *cfunc;
};
bool isCFunc;
} CObjMethod;
typedef struct CObjUpval {
@ -82,12 +86,12 @@ typedef struct CObjUpval {
#define IS_METHOD(x) isObjType(x, COBJ_METHOD)
#define IS_CLOSURE(x) isObjType(x, COBJ_CLOSURE)
#define cosmoV_readString(x) ((CObjString*)cosmoV_readObj(x))
#define cosmoV_readObject(x) ((CObjObject*)cosmoV_readObj(x))
#define cosmoV_readFunction(x) ((CObjFunction*)cosmoV_readObj(x))
#define cosmoV_readCFunction(x) (((CObjCFunction*)cosmoV_readObj(x))->cfunc)
#define cosmoV_readMethod(x) ((CObjMethod*)cosmoV_readObj(x))
#define cosmoV_readClosure(x) ((CObjClosure*)cosmoV_readObj(x))
#define cosmoV_readString(x) ((CObjString*)cosmoV_readObj((x)))
#define cosmoV_readObject(x) ((CObjObject*)cosmoV_readObj((x)))
#define cosmoV_readFunction(x) ((CObjFunction*)cosmoV_readObj((x)))
#define cosmoV_readCFunction(x) (((CObjCFunction*)cosmoV_readObj((x)))->cfunc)
#define cosmoV_readMethod(x) ((CObjMethod*)cosmoV_readObj((x)))
#define cosmoV_readClosure(x) ((CObjClosure*)cosmoV_readObj((x)))
static inline bool isObjType(CValue val, CObjType type) {
return IS_OBJ(val) && cosmoV_readObj(val)->type == type;
@ -102,6 +106,7 @@ CObjObject *cosmoO_newObject(CState *state);
CObjFunction *cosmoO_newFunction(CState *state);
CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func);
CObjMethod *cosmoO_newMethod(CState *state, CObjClosure *func, CObjObject *obj);
CObjMethod *cosmoO_newCMethod(CState *state, CObjCFunction *func, CObjObject *obj);
CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func);
CObjString *cosmoO_toString(CState *state, CObj *val);
CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val);

View File

@ -955,7 +955,7 @@ static void functionDeclaration(CParseState *pstate) {
}
static void returnStatement(CParseState *pstate) {
if (pstate->compiler->type != FTYPE_FUNCTION) {
if (pstate->compiler->type != FTYPE_FUNCTION && pstate->compiler->type != FTYPE_METHOD) {
error(pstate, "Expected 'return' in function!");
return;
}

View File

@ -30,6 +30,7 @@ CState *cosmoV_newState() {
state->top = state->stack;
state->frameCount = 0;
state->openUpvalues = NULL;
state->metaObj = NULL;
cosmoT_initTable(state, &state->strings, 8); // init string table
cosmoT_initTable(state, &state->globals, 8); // init global table

View File

@ -38,6 +38,7 @@ typedef struct CState {
int frameCount;
CObjString *internalStrings[INTERNALSTRING_MAX]; // strings used internally by the VM, eg. __init
CObjObject *metaObj; // start met obj for all objects (NULL by default)
} CState;
COSMO_API CState *cosmoV_newState();

110
src/cvm.c
View File

@ -137,6 +137,43 @@ bool call(CState *state, CObjClosure *closure, int args) {
return true;
}
bool callMethod(CState* state, CObjMethod *method, int args) {
if (method->isCFunc) {
StkPtr savedBase = state->top - args - 1;
cosmoM_freezeGC(state);
CValue res = method->cfunc->cfunc(state, args+1, savedBase);
cosmoM_unfreezeGC(state);
state->top = savedBase;
cosmoV_pushValue(state, res);
} else {
CObjClosure *closure = method->closure;
StkPtr val = state->top - args - 1;
if (args+1 != closure->function->args) {
runtimeError(state, "Expected %d parameters for %s, got %d!", closure->function->args, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, args+1);
return false;
}
// load function into callframe
pushCallFrame(state, closure, closure->function->args);
// execute
if (!cosmoV_execute(state))
return false;
CValue* result = state->top - 1;
// pop the callframe and return result
popCallFrame(state);
state->top++;
cosmoV_pushValue(state, *result); // and push return value
}
return true;
}
// args = # of pass parameters
COSMOVMRESULT cosmoV_call(CState *state, int args) {
StkPtr val = cosmoV_getTop(state, args); // function will always be right above the args
@ -157,26 +194,7 @@ COSMOVMRESULT cosmoV_call(CState *state, int args) {
case COBJ_METHOD: {
CObjMethod *method = (CObjMethod*)val->val.obj;
*val = cosmoV_newObj(method->obj); // sets the object on the stack so the function can reference it in it's first argument
CObjClosure *closure = method->closure;
if (args+1 != closure->function->args) {
runtimeError(state, "Expected %d parameters for %s, got %d!", closure->function->args, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, args+1);
return COSMOVM_RUNTIME_ERR;
}
// load function into callframe
pushCallFrame(state, closure, closure->function->args);
// execute
if (!cosmoV_execute(state))
return COSMOVM_RUNTIME_ERR;
CValue* result = state->top - 1;
// pop the callframe and return result
popCallFrame(state);
state->top++; // adjust stack back into place
cosmoV_pushValue(state, *result); // and push return value
callMethod(state, method, args);
break;
}
case COBJ_OBJECT: {
@ -187,25 +205,9 @@ COSMOVMRESULT cosmoV_call(CState *state, int args) {
CValue ret;
// check if they defined an initalizer
if (cosmoO_getObject(state, metaObj, cosmoV_newObj(state->internalStrings[INTERNALSTRING_INIT]), &ret) && IS_CLOSURE(ret)) {
CObjClosure *closure = cosmoV_readClosure(ret);
if (args+1 != closure->function->args) {
runtimeError(state, "Expected %d parameters for %s, got %d!", closure->function->args, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, args+1);
return COSMOVM_RUNTIME_ERR;
}
// load function into callframe
pushCallFrame(state, closure, closure->function->args);
// execute
if (!cosmoV_execute(state))
return COSMOVM_RUNTIME_ERR;
// we throw away the return result, it's ignored
// pop the callframe and return the new object :)
popCallFrame(state);
state->top++; // adjust stack back into place
if (cosmoV_getObject(state, metaObj, cosmoV_newObj(state->internalStrings[INTERNALSTRING_INIT]), &ret) && IS_METHOD(ret)) {
callMethod(state, cosmoV_readMethod(ret), args);
cosmoV_pop(state);
} else {
// no default initalizer
if (args != 0) {
@ -221,13 +223,13 @@ COSMOVMRESULT cosmoV_call(CState *state, int args) {
case COBJ_CFUNCTION: {
// it's a C function, so call it
CosmoCFunction cfunc = ((CObjCFunction*)(val->val.obj))->cfunc;
CValue *savedBase = state->top - args - 1;
CValue *savedBase = state->top - args;
cosmoM_freezeGC(state); // we don't want a GC event during c api because we don't actually trust the user to know how to evade the GC
CValue res = cfunc(state, args, state->top - args);
CValue res = cfunc(state, args, savedBase);
cosmoM_unfreezeGC(state);
state->top = savedBase;
state->top = savedBase - 1;
cosmoV_pushValue(state, res);
break;
}
@ -262,6 +264,24 @@ COSMO_API void cosmoV_pushObject(CState *state, int pairs) {
cosmoV_pushValue(state, cosmoV_newObj(newObj));
}
COSMO_API bool cosmoV_getObject(CState *state, CObjObject *object, CValue key, CValue *val) {
if (cosmoO_getObject(state, object, key, val)) {
if (val->type == COSMO_TOBJ ) {
if (val->val.obj->type == COBJ_CLOSURE) { // is it a function? if so, make it a method to the current object
CObjMethod *method = cosmoO_newMethod(state, (CObjClosure*)val->val.obj, object);
*val = cosmoV_newObj(method);
} else if (val->val.obj->type == COBJ_CFUNCTION) {
CObjMethod *method = cosmoO_newCMethod(state, (CObjCFunction*)val->val.obj, object);
*val = cosmoV_newObj(method);
}
}
return true;
}
return false;
}
#define BINARYOP(typeConst, op) \
StkPtr valA = cosmoV_getTop(state, 1); \
StkPtr valB = cosmoV_getTop(state, 0); \
@ -403,11 +423,7 @@ bool cosmoV_execute(CState *state) {
CObjObject *object = (CObjObject*)temp->val.obj;
CValue val; // to hold our value
cosmoO_getObject(state, object, *key, &val);
if (IS_CLOSURE(val)) { // is it a function? if so, make it a method to the current object
CObjMethod *method = cosmoO_newMethod(state, cosmoV_readClosure(val), object);
val = cosmoV_newObj(method);
}
cosmoV_getObject(state, object, *key, &val);
cosmoV_setTop(state, 2); // pops the object & the key
cosmoV_pushValue(state, val); // pushes the field result
break;

View File

@ -15,6 +15,7 @@ typedef enum {
// args = # of pass parameters, nresults = # of expected results
COSMO_API COSMOVMRESULT cosmoV_call(CState *state, int args);
COSMO_API void cosmoV_pushObject(CState *state, int pairs);
COSMO_API bool cosmoV_getObject(CState *state, CObjObject *object, CValue key, CValue *val);
// nice to have wrappers

View File

@ -48,7 +48,8 @@ static void repl() {
_ACTIVE = true;
CState *state = cosmoV_newState();
cosmoB_loadlibrary(state);
cosmoB_loadLibrary(state);
cosmoB_loadDebug(state);
// TODO: there's gotta be a better way to do this
cosmoV_register(state, "quit", cosmoV_newObj(cosmoO_newCFunction(state, cosmoB_quitRepl)));
@ -103,7 +104,7 @@ static char *readFile(const char* path) {
static void runFile(const char* fileName) {
char* script = readFile(fileName);
CState *state = cosmoV_newState();
cosmoB_loadlibrary(state);
cosmoB_loadLibrary(state);
cosmoV_register(state, "input", cosmoV_newObj(cosmoO_newCFunction(state, cosmoB_input)));