mirror of
https://github.com/CPunch/Cosmo.git
synced 2024-12-04 22:46:31 +00:00
Compare commits
2 Commits
f26376e6f5
...
37e42eb60b
Author | SHA1 | Date | |
---|---|---|---|
37e42eb60b | |||
cd3047c271 |
2
Makefile
2
Makefile
@ -1,7 +1,7 @@
|
|||||||
# make clean && make && ./bin/cosmo
|
# make clean && make && ./bin/cosmo
|
||||||
|
|
||||||
CC=clang
|
CC=clang
|
||||||
CFLAGS=-fPIE -Wall -Isrc -O3 -std=c99
|
CFLAGS=-fPIE -Wall -Isrc -O3 -std=c99 #-g -fsanitize=address
|
||||||
LDFLAGS=-lm #-fsanitize=address
|
LDFLAGS=-lm #-fsanitize=address
|
||||||
OUT=bin/cosmo
|
OUT=bin/cosmo
|
||||||
|
|
||||||
|
25
main.c
25
main.c
@ -45,21 +45,20 @@ int cosmoB_input(CState *state, int nargs, CValue *args)
|
|||||||
|
|
||||||
static bool interpret(CState *state, const char *script, const char *mod)
|
static bool interpret(CState *state, const char *script, const char *mod)
|
||||||
{
|
{
|
||||||
bool ret;
|
|
||||||
|
|
||||||
// cosmoV_compileString pushes the result onto the stack (COBJ_ERROR or COBJ_CLOSURE)
|
// cosmoV_compileString pushes the result onto the stack (COBJ_ERROR or COBJ_CLOSURE)
|
||||||
if (cosmoV_compileString(state, script, mod)) {
|
if (cosmoV_compileString(state, script, mod)) {
|
||||||
// 0 args being passed, 0 results expected
|
// 0 args being passed, 0 results expected
|
||||||
if (!cosmoV_call(state, 0, 0))
|
if (!cosmoV_pcall(state, 0, 0)) {
|
||||||
cosmoV_printError(state, state->error);
|
cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
cosmoV_pop(state); // pop the error off the stack
|
cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
|
||||||
cosmoV_printError(state, state->error);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = state->panic;
|
return true;
|
||||||
state->panic = false; // so our repl isn't broken
|
|
||||||
return !ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void repl(CState *state)
|
static void repl(CState *state)
|
||||||
@ -158,8 +157,7 @@ void compileScript(CState *state, const char *in, const char *out)
|
|||||||
CObjFunction *func = cosmoV_readClosure(*cosmoV_getTop(state, 0))->function;
|
CObjFunction *func = cosmoV_readClosure(*cosmoV_getTop(state, 0))->function;
|
||||||
cosmoD_dump(state, func, fileWriter, (void *)fout);
|
cosmoD_dump(state, func, fileWriter, (void *)fout);
|
||||||
} else {
|
} else {
|
||||||
cosmoV_pop(state); // pop the error off the stack
|
cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
|
||||||
cosmoV_printError(state, state->error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free(script);
|
free(script);
|
||||||
@ -172,14 +170,13 @@ void loadScript(CState *state, const char *in)
|
|||||||
{
|
{
|
||||||
FILE *file = fopen(in, "rb");
|
FILE *file = fopen(in, "rb");
|
||||||
if (!cosmoV_undump(state, fileReader, file)) {
|
if (!cosmoV_undump(state, fileReader, file)) {
|
||||||
cosmoV_pop(state); // pop the error off the stack
|
cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
|
||||||
cosmoV_printError(state, state->error);
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
printf("[!] loaded %s!\n", in);
|
printf("[!] loaded %s!\n", in);
|
||||||
if (!cosmoV_call(state, 0, 0))
|
if (!cosmoV_pcall(state, 0, 0))
|
||||||
cosmoV_printError(state, state->error);
|
cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
|
||||||
|
|
||||||
fclose(file);
|
fclose(file);
|
||||||
}
|
}
|
||||||
|
@ -288,9 +288,6 @@ static void markRoots(CState *state)
|
|||||||
// mark the user defined roots
|
// mark the user defined roots
|
||||||
markUserRoots(state);
|
markUserRoots(state);
|
||||||
|
|
||||||
// mark other misc. internally reserved objects
|
|
||||||
markObject(state, (CObj *)state->error);
|
|
||||||
|
|
||||||
for (int i = 0; i < COBJ_MAX; i++)
|
for (int i = 0; i < COBJ_MAX; i++)
|
||||||
markObject(state, (CObj *)state->protoObjects[i]);
|
markObject(state, (CObj *)state->protoObjects[i]);
|
||||||
|
|
||||||
|
27
src/cobj.c
27
src/cobj.c
@ -164,14 +164,12 @@ _eqFail:
|
|||||||
cosmoV_pushValue(state, eq1);
|
cosmoV_pushValue(state, eq1);
|
||||||
cosmoV_pushRef(state, obj1);
|
cosmoV_pushRef(state, obj1);
|
||||||
cosmoV_pushRef(state, obj2);
|
cosmoV_pushRef(state, obj2);
|
||||||
if (!cosmoV_call(state, 2, 1))
|
cosmoV_call(state, 2, 1);
|
||||||
return false;
|
|
||||||
|
|
||||||
// check return value and make sure it's a boolean
|
// check return value and make sure it's a boolean
|
||||||
if (!IS_BOOLEAN(*cosmoV_getTop(state, 0))) {
|
if (!IS_BOOLEAN(*cosmoV_getTop(state, 0))) {
|
||||||
cosmoV_error(state, "__equal expected to return <boolean>, got %s!",
|
cosmoV_error(state, "__equal expected to return <boolean>, got %s!",
|
||||||
cosmoV_typeStr(*cosmoV_pop(state)));
|
cosmoV_typeStr(*cosmoV_pop(state)));
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// return the result
|
// return the result
|
||||||
@ -409,9 +407,8 @@ bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *v
|
|||||||
cosmoT_get(state, &cosmoV_readTable(*val)->tbl, key, val)) {
|
cosmoT_get(state, &cosmoV_readTable(*val)->tbl, key, val)) {
|
||||||
cosmoV_pushValue(state, *val); // push function
|
cosmoV_pushValue(state, *val); // push function
|
||||||
cosmoV_pushRef(state, (CObj *)obj); // push object
|
cosmoV_pushRef(state, (CObj *)obj); // push object
|
||||||
if (!cosmoV_call(state, 1, 1)) // call the function with the 1 argument
|
cosmoV_call(state, 1, 1); // call the function with the 1 argument
|
||||||
return false;
|
*val = *cosmoV_pop(state); // set value to the return value of __index
|
||||||
*val = *cosmoV_pop(state); // set value to the return value of __index
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -530,9 +527,8 @@ bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *v
|
|||||||
cosmoV_pushValue(state, *val); // push function
|
cosmoV_pushValue(state, *val); // push function
|
||||||
cosmoV_pushRef(state, (CObj *)object); // push object
|
cosmoV_pushRef(state, (CObj *)object); // push object
|
||||||
cosmoV_pushValue(state, key); // push key
|
cosmoV_pushValue(state, key); // push key
|
||||||
if (!cosmoV_call(state, 2, 1)) // call the function with the 2 arguments
|
cosmoV_call(state, 2, 1); // call the function with the 2 arguments
|
||||||
return false;
|
*val = *cosmoV_pop(state); // set value to the return value of __index
|
||||||
*val = *cosmoV_pop(state); // set value to the return value of __index
|
|
||||||
return true;
|
return true;
|
||||||
} else { // there's no __index function defined!
|
} else { // there's no __index function defined!
|
||||||
cosmoV_error(state, "Couldn't index object without __index function!");
|
cosmoV_error(state, "Couldn't index object without __index function!");
|
||||||
@ -550,7 +546,8 @@ bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue
|
|||||||
cosmoV_pushRef(state, (CObj *)object); // push object
|
cosmoV_pushRef(state, (CObj *)object); // push object
|
||||||
cosmoV_pushValue(state, key); // push key & value pair
|
cosmoV_pushValue(state, key); // push key & value pair
|
||||||
cosmoV_pushValue(state, val);
|
cosmoV_pushValue(state, val);
|
||||||
return cosmoV_call(state, 3, 0);
|
cosmoV_call(state, 3, 0);
|
||||||
|
return true;
|
||||||
} else { // there's no __newindex function defined
|
} else { // there's no __newindex function defined
|
||||||
cosmoV_error(state, "Couldn't set index on object without __newindex function!");
|
cosmoV_error(state, "Couldn't set index on object without __newindex function!");
|
||||||
}
|
}
|
||||||
@ -567,8 +564,7 @@ CObjString *cosmoO_toString(CState *state, CObj *obj)
|
|||||||
if (protoObject != NULL && cosmoO_getIString(state, protoObject, ISTRING_TOSTRING, &res)) {
|
if (protoObject != NULL && cosmoO_getIString(state, protoObject, ISTRING_TOSTRING, &res)) {
|
||||||
cosmoV_pushValue(state, res);
|
cosmoV_pushValue(state, res);
|
||||||
cosmoV_pushRef(state, (CObj *)obj);
|
cosmoV_pushRef(state, (CObj *)obj);
|
||||||
if (!cosmoV_call(state, 1, 1))
|
cosmoV_call(state, 1, 1);
|
||||||
return cosmoO_copyString(state, "<err>", 5);
|
|
||||||
|
|
||||||
// make sure the __tostring function returned a string
|
// make sure the __tostring function returned a string
|
||||||
StkPtr ret = cosmoV_getTop(state, 0);
|
StkPtr ret = cosmoV_getTop(state, 0);
|
||||||
@ -633,14 +629,12 @@ cosmo_Number cosmoO_toNumber(CState *state, CObj *obj)
|
|||||||
if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_TONUMBER, &res)) {
|
if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_TONUMBER, &res)) {
|
||||||
cosmoV_pushValue(state, res);
|
cosmoV_pushValue(state, res);
|
||||||
cosmoV_pushRef(state, (CObj *)obj);
|
cosmoV_pushRef(state, (CObj *)obj);
|
||||||
if (!cosmoV_call(state, 1, 1)) // call res, expect 1 return val of <number>
|
cosmoV_call(state, 1, 1); // call res, expect 1 return val of <number>
|
||||||
return 0;
|
|
||||||
|
|
||||||
StkPtr temp = cosmoV_getTop(state, 0);
|
StkPtr temp = cosmoV_getTop(state, 0);
|
||||||
if (!IS_NUMBER(*temp)) {
|
if (!IS_NUMBER(*temp)) {
|
||||||
cosmoV_error(state, "__tonumber expected to return <number>, got %s!",
|
cosmoV_error(state, "__tonumber expected to return <number>, got %s!",
|
||||||
cosmoV_typeStr(*temp));
|
cosmoV_typeStr(*temp));
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// return number
|
// return number
|
||||||
@ -666,8 +660,7 @@ int cosmoO_count(CState *state, CObj *obj)
|
|||||||
if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_COUNT, &res)) {
|
if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_COUNT, &res)) {
|
||||||
cosmoV_pushValue(state, res);
|
cosmoV_pushValue(state, res);
|
||||||
cosmoV_pushRef(state, (CObj *)obj);
|
cosmoV_pushRef(state, (CObj *)obj);
|
||||||
if (!cosmoV_call(state, 1, 1)) // call res, we expect 1 return value of type <number>
|
cosmoV_call(state, 1, 1); // call res, we expect 1 return value of type <number>
|
||||||
return 0;
|
|
||||||
|
|
||||||
StkPtr ret = cosmoV_getTop(state, 0);
|
StkPtr ret = cosmoV_getTop(state, 0);
|
||||||
if (!IS_NUMBER(*ret)) {
|
if (!IS_NUMBER(*ret)) {
|
||||||
|
@ -136,6 +136,7 @@ struct CObjUpval
|
|||||||
#define cosmoV_readCFunction(x) (((CObjCFunction *)cosmoV_readRef(x))->cfunc)
|
#define cosmoV_readCFunction(x) (((CObjCFunction *)cosmoV_readRef(x))->cfunc)
|
||||||
#define cosmoV_readMethod(x) ((CObjMethod *)cosmoV_readRef(x))
|
#define cosmoV_readMethod(x) ((CObjMethod *)cosmoV_readRef(x))
|
||||||
#define cosmoV_readClosure(x) ((CObjClosure *)cosmoV_readRef(x))
|
#define cosmoV_readClosure(x) ((CObjClosure *)cosmoV_readRef(x))
|
||||||
|
#define cosmoV_readError(x) ((CObjError *)cosmoV_readRef(x))
|
||||||
|
|
||||||
#define cosmoO_readCString(x) ((CObjString *)x)->str
|
#define cosmoO_readCString(x) ((CObjString *)x)->str
|
||||||
#define cosmoO_readType(x) ((CObj *)x)->type
|
#define cosmoO_readType(x) ((CObj *)x)->type
|
||||||
|
15
src/cparse.c
15
src/cparse.c
@ -65,6 +65,8 @@ typedef struct
|
|||||||
CObjString *module; // name of the module
|
CObjString *module; // name of the module
|
||||||
CToken current;
|
CToken current;
|
||||||
CToken previous; // token right after the current token
|
CToken previous; // token right after the current token
|
||||||
|
int workingStackCount; // we push CValues of objects we need onto the stack so the garbage collector can see them.
|
||||||
|
// this is the count of those values so we'll know how many to pop off when we're done
|
||||||
} CParseState;
|
} CParseState;
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
@ -107,6 +109,12 @@ static CObjFunction *endCompiler(CParseState *pstate);
|
|||||||
|
|
||||||
// ================================================================ [FRONT END/TALK TO LEXER]
|
// ================================================================ [FRONT END/TALK TO LEXER]
|
||||||
|
|
||||||
|
static void keepTrackOf(CParseState *pstate, CValue val)
|
||||||
|
{
|
||||||
|
pstate->workingStackCount++;
|
||||||
|
cosmoV_pushValue(pstate->state, val);
|
||||||
|
}
|
||||||
|
|
||||||
static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, FunctionType type,
|
static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, FunctionType type,
|
||||||
CCompilerState *enclosing)
|
CCompilerState *enclosing)
|
||||||
{
|
{
|
||||||
@ -122,7 +130,7 @@ static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, Func
|
|||||||
ccstate->function = cosmoO_newFunction(pstate->state);
|
ccstate->function = cosmoO_newFunction(pstate->state);
|
||||||
ccstate->function->module = pstate->module;
|
ccstate->function->module = pstate->module;
|
||||||
|
|
||||||
cosmoV_pushRef(pstate->state, (CObj *)ccstate->function);
|
keepTrackOf(pstate, cosmoV_newRef((CObj *)ccstate->function));
|
||||||
|
|
||||||
ccstate->loop.scope = -1; // there is no loop yet
|
ccstate->loop.scope = -1; // there is no loop yet
|
||||||
|
|
||||||
@ -151,13 +159,18 @@ static void initParseState(CParseState *pstate, CCompilerState *ccstate, CState
|
|||||||
pstate->state = s;
|
pstate->state = s;
|
||||||
pstate->compiler = ccstate;
|
pstate->compiler = ccstate;
|
||||||
pstate->module = cosmoO_copyString(s, module, strlen(module));
|
pstate->module = cosmoO_copyString(s, module, strlen(module));
|
||||||
|
pstate->workingStackCount = 0;
|
||||||
|
|
||||||
|
keepTrackOf(pstate, cosmoV_newRef((CObj *)pstate->module));
|
||||||
initCompilerState(pstate, ccstate, FTYPE_SCRIPT, NULL); // enclosing starts as NULL
|
initCompilerState(pstate, ccstate, FTYPE_SCRIPT, NULL); // enclosing starts as NULL
|
||||||
}
|
}
|
||||||
|
|
||||||
static void freeParseState(CParseState *pstate)
|
static void freeParseState(CParseState *pstate)
|
||||||
{
|
{
|
||||||
cosmoL_cleanupLexState(pstate->state, &pstate->lex);
|
cosmoL_cleanupLexState(pstate->state, &pstate->lex);
|
||||||
|
|
||||||
|
// pop our working values off the stack
|
||||||
|
cosmoV_setTop(pstate->state, pstate->workingStackCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void errorAt(CParseState *pstate, CToken *token, const char *format, va_list args)
|
static void errorAt(CParseState *pstate, CToken *token, const char *format, va_list args)
|
||||||
|
@ -12,6 +12,7 @@ CPanic *cosmoV_newPanic(CState *state)
|
|||||||
CPanic *panic = cosmoM_xmalloc(state, sizeof(CPanic));
|
CPanic *panic = cosmoM_xmalloc(state, sizeof(CPanic));
|
||||||
panic->prev = state->panic;
|
panic->prev = state->panic;
|
||||||
state->panic = panic;
|
state->panic = panic;
|
||||||
|
|
||||||
return panic;
|
return panic;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,6 +20,7 @@ void cosmoV_freePanic(CState *state)
|
|||||||
{
|
{
|
||||||
CPanic *panic = state->panic;
|
CPanic *panic = state->panic;
|
||||||
state->panic = panic->prev;
|
state->panic = panic->prev;
|
||||||
|
|
||||||
cosmoM_free(state, CPanic, panic);
|
cosmoM_free(state, CPanic, panic);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +34,6 @@ CState *cosmoV_newState()
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
state->panic = false;
|
|
||||||
state->freezeGC = 1; // we start frozen
|
state->freezeGC = 1; // we start frozen
|
||||||
state->panic = NULL;
|
state->panic = NULL;
|
||||||
|
|
||||||
@ -50,8 +51,6 @@ CState *cosmoV_newState()
|
|||||||
state->frameCount = 0;
|
state->frameCount = 0;
|
||||||
state->openUpvalues = NULL;
|
state->openUpvalues = NULL;
|
||||||
|
|
||||||
state->error = NULL;
|
|
||||||
|
|
||||||
// set default proto objects
|
// set default proto objects
|
||||||
for (int i = 0; i < COBJ_MAX; i++)
|
for (int i = 0; i < COBJ_MAX; i++)
|
||||||
state->protoObjects[i] = NULL;
|
state->protoObjects[i] = NULL;
|
||||||
|
@ -52,7 +52,6 @@ struct CState
|
|||||||
int frameCount;
|
int frameCount;
|
||||||
CPanic *panic;
|
CPanic *panic;
|
||||||
|
|
||||||
CObjError *error; // NULL, unless panic is true
|
|
||||||
CObj *objects; // tracks all of our allocated objects
|
CObj *objects; // tracks all of our allocated objects
|
||||||
CObj *userRoots; // user definable roots, this holds CObjs that should be considered "roots",
|
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
|
// lets the VM know you are holding a reference to a CObj in your code
|
||||||
|
130
src/cvm.c
130
src/cvm.c
@ -10,17 +10,7 @@
|
|||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
bool cosmoV_protect(CState *state)
|
#define cosmoV_protect(panic) setjmp(panic->jmp) == 0
|
||||||
{
|
|
||||||
CPanic *panic = cosmoV_newPanic(state);
|
|
||||||
|
|
||||||
if (setjmp(panic->jmp) == 0) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
cosmoV_freePanic(state);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...)
|
COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...)
|
||||||
{
|
{
|
||||||
@ -48,9 +38,6 @@ COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud)
|
|||||||
CObjFunction *func;
|
CObjFunction *func;
|
||||||
|
|
||||||
if (cosmoD_undump(state, reader, ud, &func)) {
|
if (cosmoD_undump(state, reader, ud, &func)) {
|
||||||
// fail recovery
|
|
||||||
state->panic = false;
|
|
||||||
cosmoV_pushRef(state, (CObj *)state->error);
|
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -65,24 +52,28 @@ COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name)
|
||||||
{
|
{
|
||||||
CObjFunction *func;
|
CObjFunction *func;
|
||||||
|
CPanic *panic = cosmoV_newPanic(state);
|
||||||
|
|
||||||
if (cosmoV_protect(state)) {
|
if (cosmoV_protect(panic)) {
|
||||||
if ((func = cosmoP_compileString(state, src, name)) != NULL) {
|
func = cosmoP_compileString(state, src, name);
|
||||||
// success
|
|
||||||
#ifdef VM_DEBUG
|
#ifdef VM_DEBUG
|
||||||
disasmChunk(&func->chunk, func->module->str, 0);
|
disasmChunk(&func->chunk, func->module->str, 0);
|
||||||
#endif
|
#endif
|
||||||
// push function onto the stack so it doesn't it cleaned up by the GC, at the same stack
|
cosmoV_freePanic(state);
|
||||||
// location put our closure
|
|
||||||
cosmoV_pushRef(state, (CObj *)func);
|
// push function onto the stack so it doesn't it cleaned up by the GC, at the same stack
|
||||||
*(cosmoV_getTop(state, 0)) = cosmoV_newRef(cosmoO_newClosure(state, func));
|
// location put our closure
|
||||||
return true;
|
cosmoV_pushRef(state, (CObj *)func);
|
||||||
}
|
*(cosmoV_getTop(state, 0)) = cosmoV_newRef(cosmoO_newClosure(state, func));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cosmoV_freePanic(state);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,18 +235,16 @@ void cosmoV_concat(CState *state, int vals)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int cosmoV_execute(CState *state);
|
int cosmoV_execute(CState *state);
|
||||||
bool invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults, int offset);
|
void invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults, int offset);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
calls a native C Function with # args on the stack, nresults are pushed onto the stack upon
|
calls a native C Function with # args on the stack, nresults are pushed onto the stack upon
|
||||||
return.
|
return.
|
||||||
|
|
||||||
returns:
|
state->top is moved to base + offset + nresults, with nresults pushed onto the stack
|
||||||
false: state paniced during C Function, error is at state->error
|
from base + offset
|
||||||
true: state->top is moved to base + offset + nresults, with nresults pushed onto the stack
|
|
||||||
from base + offset
|
|
||||||
*/
|
*/
|
||||||
static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nresults, int offset)
|
static void callCFunction(CState *state, CosmoCFunction cfunc, int args, int nresults, int offset)
|
||||||
{
|
{
|
||||||
StkPtr savedBase = cosmoV_getTop(state, args);
|
StkPtr savedBase = cosmoV_getTop(state, args);
|
||||||
|
|
||||||
@ -267,13 +256,8 @@ static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nre
|
|||||||
|
|
||||||
// remember where the return values are
|
// remember where the return values are
|
||||||
StkPtr results = cosmoV_getTop(state, nres - 1);
|
StkPtr results = cosmoV_getTop(state, nres - 1);
|
||||||
|
|
||||||
state->top = savedBase + offset; // set stack
|
state->top = savedBase + offset; // set stack
|
||||||
|
|
||||||
// if the state paniced during the c function, return false
|
|
||||||
if (state->panic)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// push the return value back onto the stack
|
// push the return value back onto the stack
|
||||||
memmove(state->top, results,
|
memmove(state->top, results,
|
||||||
sizeof(CValue) * nres); // copies the return values to the top of the stack
|
sizeof(CValue) * nres); // copies the return values to the top of the stack
|
||||||
@ -282,20 +266,16 @@ static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nre
|
|||||||
// now, if the caller function expected more return values, push nils onto the stack
|
// now, if the caller function expected more return values, push nils onto the stack
|
||||||
for (int i = nres; i < nresults; i++)
|
for (int i = nres; i < nresults; i++)
|
||||||
cosmoV_pushValue(state, cosmoV_newNil());
|
cosmoV_pushValue(state, cosmoV_newNil());
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
calls a raw closure object with # args on the stack, nresults are pushed onto the stack upon
|
calls a raw closure object with # args on the stack, nresults are pushed onto the stack upon
|
||||||
return.
|
return.
|
||||||
|
|
||||||
returns:
|
stack->top is moved to base + offset + nresults, with nresults pushed onto the stack
|
||||||
false: state paniced, error is at state->error
|
from base + offset
|
||||||
true: stack->top is moved to base + offset + nresults, with nresults pushed onto the stack
|
|
||||||
from base + offset
|
|
||||||
*/
|
*/
|
||||||
static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults, int offset)
|
static void rawCall(CState *state, CObjClosure *closure, int args, int nresults, int offset)
|
||||||
{
|
{
|
||||||
CObjFunction *func = closure->function;
|
CObjFunction *func = closure->function;
|
||||||
|
|
||||||
@ -319,7 +299,6 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults,
|
|||||||
cosmoV_error(state, "Expected %d arguments for %s, got %d!", closure->function->args,
|
cosmoV_error(state, "Expected %d arguments for %s, got %d!", closure->function->args,
|
||||||
closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str,
|
closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str,
|
||||||
args);
|
args);
|
||||||
return false;
|
|
||||||
} else {
|
} else {
|
||||||
// load function into callframe
|
// load function into callframe
|
||||||
pushCallFrame(state, closure, func->args);
|
pushCallFrame(state, closure, func->args);
|
||||||
@ -337,9 +316,6 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults,
|
|||||||
// pop the callframe and return results :)
|
// pop the callframe and return results :)
|
||||||
popCallFrame(state, offset);
|
popCallFrame(state, offset);
|
||||||
|
|
||||||
if (state->panic) // panic state
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// push the return values back onto the stack
|
// push the return values back onto the stack
|
||||||
for (int i = 0; i < nres; i++) {
|
for (int i = 0; i < nres; i++) {
|
||||||
state->top[i] = results[i];
|
state->top[i] = results[i];
|
||||||
@ -349,12 +325,10 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults,
|
|||||||
// now, if the caller function expected more return values, push nils onto the stack
|
// now, if the caller function expected more return values, push nils onto the stack
|
||||||
for (int i = nres; i < nresults; i++)
|
for (int i = nres; i < nresults; i++)
|
||||||
cosmoV_pushValue(state, cosmoV_newNil());
|
cosmoV_pushValue(state, cosmoV_newNil());
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns true if successful, false if error
|
// returns true if successful, false if error
|
||||||
bool callCValue(CState *state, CValue func, int args, int nresults, int offset)
|
void callCValue(CState *state, CValue func, int args, int nresults, int offset)
|
||||||
{
|
{
|
||||||
#ifdef VM_DEBUG
|
#ifdef VM_DEBUG
|
||||||
printf("\n");
|
printf("\n");
|
||||||
@ -365,17 +339,19 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset)
|
|||||||
|
|
||||||
if (!IS_REF(func)) {
|
if (!IS_REF(func)) {
|
||||||
cosmoV_error(state, "Cannot call non-callable type %s!", cosmoV_typeStr(func));
|
cosmoV_error(state, "Cannot call non-callable type %s!", cosmoV_typeStr(func));
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (cosmoV_readRef(func)->type) {
|
switch (cosmoV_readRef(func)->type) {
|
||||||
case COBJ_CLOSURE:
|
case COBJ_CLOSURE:
|
||||||
return rawCall(state, cosmoV_readClosure(func), args, nresults, offset);
|
rawCall(state, cosmoV_readClosure(func), args, nresults, offset);
|
||||||
|
break;
|
||||||
case COBJ_CFUNCTION:
|
case COBJ_CFUNCTION:
|
||||||
return callCFunction(state, cosmoV_readCFunction(func), args, nresults, offset);
|
callCFunction(state, cosmoV_readCFunction(func), args, nresults, offset);
|
||||||
|
break;
|
||||||
case COBJ_METHOD: {
|
case COBJ_METHOD: {
|
||||||
CObjMethod *method = (CObjMethod *)cosmoV_readRef(func);
|
CObjMethod *method = (CObjMethod *)cosmoV_readRef(func);
|
||||||
return invokeMethod(state, method->obj, method->func, args, nresults, offset + 1);
|
invokeMethod(state, method->obj, method->func, args, nresults, offset + 1);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case COBJ_OBJECT: { // object is being instantiated, making another object
|
case COBJ_OBJECT: { // object is being instantiated, making another object
|
||||||
CObjObject *protoObj = (CObjObject *)cosmoV_readRef(func);
|
CObjObject *protoObj = (CObjObject *)cosmoV_readRef(func);
|
||||||
@ -388,12 +364,10 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset)
|
|||||||
|
|
||||||
// check if they defined an initializer (we accept 0 return values)
|
// check if they defined an initializer (we accept 0 return values)
|
||||||
if (cosmoO_getIString(state, protoObj, ISTRING_INIT, &ret)) {
|
if (cosmoO_getIString(state, protoObj, ISTRING_INIT, &ret)) {
|
||||||
if (!invokeMethod(state, (CObj *)newObj, ret, args, 0, offset + 1))
|
invokeMethod(state, (CObj *)newObj, ret, args, 0, offset + 1);
|
||||||
return false;
|
|
||||||
} else {
|
} else {
|
||||||
// no default initializer
|
// no default initializer
|
||||||
cosmoV_error(state, "Expected __init() in proto, object cannot be instantiated!");
|
cosmoV_error(state, "Expected __init() in proto, object cannot be instantiated!");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nresults > 0) {
|
if (nresults > 0) {
|
||||||
@ -409,19 +383,16 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset)
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
cosmoV_error(state, "Cannot call non-callable type %s!", cosmoV_typeStr(func));
|
cosmoV_error(state, "Cannot call non-callable type %s!", cosmoV_typeStr(func));
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults, int offset)
|
void invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults, int offset)
|
||||||
{
|
{
|
||||||
// first, set the first argument to the object
|
// first, set the first argument to the object
|
||||||
StkPtr temp = cosmoV_getTop(state, args);
|
StkPtr temp = cosmoV_getTop(state, args);
|
||||||
*temp = cosmoV_newRef(obj);
|
*temp = cosmoV_newRef(obj);
|
||||||
|
|
||||||
return callCValue(state, func, args + 1, nresults, offset);
|
callCValue(state, func, args + 1, nresults, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
// wraps cosmoV_call in a protected state, CObjError will be pushed onto the stack if function call
|
// wraps cosmoV_call in a protected state, CObjError will be pushed onto the stack if function call
|
||||||
@ -429,22 +400,22 @@ bool invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults,
|
|||||||
// returns false if function call failed, true if function call succeeded
|
// returns false if function call failed, true if function call succeeded
|
||||||
bool cosmoV_pcall(CState *state, int args, int nresults)
|
bool cosmoV_pcall(CState *state, int args, int nresults)
|
||||||
{
|
{
|
||||||
if (cosmoV_protect(state)) {
|
CPanic *panic = cosmoV_newPanic(state);
|
||||||
|
|
||||||
|
if (cosmoV_protect(panic)) {
|
||||||
cosmoV_call(state, args, nresults);
|
cosmoV_call(state, args, nresults);
|
||||||
|
cosmoV_freePanic(state);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
printf("caught panic!\n");
|
// if cosmoV_protect returns false, the error is already on the top of the stack
|
||||||
// restore panic state
|
|
||||||
state->panic = false;
|
|
||||||
|
|
||||||
if (nresults > 0) {
|
if (nresults > 0) {
|
||||||
cosmoV_pushRef(state, (CObj *)state->error);
|
|
||||||
|
|
||||||
// push other expected results onto the stack
|
// push other expected results onto the stack
|
||||||
for (int i = 0; i < nresults - 1; i++)
|
for (int i = 0; i < nresults - 1; i++)
|
||||||
cosmoV_pushValue(state, cosmoV_newNil());
|
cosmoV_pushValue(state, cosmoV_newNil());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cosmoV_freePanic(state);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -452,11 +423,11 @@ bool cosmoV_pcall(CState *state, int args, int nresults)
|
|||||||
// calls a callable object at stack->top - args - 1, passing the # of args to the callable, and
|
// calls a callable object at stack->top - args - 1, passing the # of args to the callable, and
|
||||||
// ensuring nresults are returned
|
// ensuring nresults are returned
|
||||||
// returns false if an error was thrown, else true if successful
|
// returns false if an error was thrown, else true if successful
|
||||||
bool cosmoV_call(CState *state, int args, int nresults)
|
void cosmoV_call(CState *state, int args, int nresults)
|
||||||
{
|
{
|
||||||
StkPtr val = cosmoV_getTop(state, args); // function will always be right above the args
|
StkPtr val = cosmoV_getTop(state, args); // function will always be right above the args
|
||||||
|
|
||||||
return callCValue(state, *val, args, nresults, 0);
|
callCValue(state, *val, args, nresults, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool isFalsey(StkPtr val)
|
static inline bool isFalsey(StkPtr val)
|
||||||
@ -732,7 +703,7 @@ int cosmoV_execute(CState *state)
|
|||||||
CCallFrame *frame = &state->callFrame[state->frameCount - 1]; // grabs the current frame
|
CCallFrame *frame = &state->callFrame[state->frameCount - 1]; // grabs the current frame
|
||||||
CValue *constants = frame->closure->function->chunk.constants.values; // cache the pointer :)
|
CValue *constants = frame->closure->function->chunk.constants.values; // cache the pointer :)
|
||||||
|
|
||||||
while (!state->panic) {
|
for (;;) {
|
||||||
#ifdef VM_DEBUG
|
#ifdef VM_DEBUG
|
||||||
cosmoV_printStack(state);
|
cosmoV_printStack(state);
|
||||||
disasmInstr(&frame->closure->function->chunk,
|
disasmInstr(&frame->closure->function->chunk,
|
||||||
@ -816,9 +787,7 @@ int cosmoV_execute(CState *state)
|
|||||||
{
|
{
|
||||||
uint8_t args = READBYTE(frame);
|
uint8_t args = READBYTE(frame);
|
||||||
uint8_t nres = READBYTE(frame);
|
uint8_t nres = READBYTE(frame);
|
||||||
if (!cosmoV_call(state, args, nres)) {
|
cosmoV_call(state, args, nres);
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
CASE(OP_CLOSURE) :
|
CASE(OP_CLOSURE) :
|
||||||
{
|
{
|
||||||
@ -1043,10 +1012,9 @@ int cosmoV_execute(CState *state)
|
|||||||
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_pushRef(state, (CObj *)obj);
|
cosmoV_pushRef(state, (CObj *)obj);
|
||||||
if (!cosmoV_call(
|
cosmoV_call(
|
||||||
state, 1,
|
state, 1,
|
||||||
1)) // we expect 1 return value on the stack, the iterable object
|
1); // we expect 1 return value on the stack, the iterable object
|
||||||
return -1;
|
|
||||||
|
|
||||||
StkPtr iObj = cosmoV_getTop(state, 0);
|
StkPtr iObj = cosmoV_getTop(state, 0);
|
||||||
|
|
||||||
@ -1104,8 +1072,7 @@ int cosmoV_execute(CState *state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cosmoV_pushValue(state, *temp);
|
cosmoV_pushValue(state, *temp);
|
||||||
if (!cosmoV_call(state, 0, nresults))
|
cosmoV_call(state, 0, nresults);
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (IS_NIL(*(cosmoV_getTop(
|
if (IS_NIL(*(cosmoV_getTop(
|
||||||
state, 0)))) { // __next returned a nil, which means to exit the loop
|
state, 0)))) { // __next returned a nil, which means to exit the loop
|
||||||
@ -1363,7 +1330,6 @@ int cosmoV_execute(CState *state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// we'll only reach this if state->panic is true
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
14
src/cvm.h
14
src/cvm.h
@ -13,16 +13,19 @@
|
|||||||
cosmoV_execute by about 20% from benchmarking. of course, if you know
|
cosmoV_execute by about 20% from benchmarking. of course, if you know
|
||||||
your compiler supports computed gotos, you can define VM_JUMPTABLE
|
your compiler supports computed gotos, you can define VM_JUMPTABLE
|
||||||
|
|
||||||
|
although, this is disabled when VM_DEBUG is defined, since it can cause
|
||||||
|
issues with debugging
|
||||||
|
|
||||||
BTW: be weary of maliciously crafted cosmo dumps!! it's very easy to crash
|
BTW: be weary of maliciously crafted cosmo dumps!! it's very easy to crash
|
||||||
cosmo with this enabled and reading invalid opcodes due to us just using the
|
cosmo with this enabled and reading invalid opcodes due to us just using the
|
||||||
opcode as an index into the jump table
|
opcode as an index into the jump table
|
||||||
*/
|
*/
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
#if (defined(__GNUC__) || defined(__clang__)) && !defined(VM_DEBUG)
|
||||||
# define VM_JUMPTABLE
|
# define VM_JUMPTABLE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// args = # of pass parameters, nresults = # of expected results
|
// args = # of pass parameters, nresults = # of expected results
|
||||||
COSMO_API bool cosmoV_call(CState *state, int args, int nresults);
|
COSMO_API void cosmoV_call(CState *state, int args, int nresults);
|
||||||
COSMO_API bool cosmoV_pcall(CState *state, int args, int nresults);
|
COSMO_API bool cosmoV_pcall(CState *state, int args, int nresults);
|
||||||
|
|
||||||
// pushes new object onto the stack & returns a pointer to the new object
|
// pushes new object onto the stack & returns a pointer to the new object
|
||||||
@ -97,13 +100,6 @@ static inline void cosmoV_pushValue(CState *state, CValue val)
|
|||||||
|
|
||||||
// we reserve 8 slots for the error string and whatever c api we might be in
|
// we reserve 8 slots for the error string and whatever c api we might be in
|
||||||
if (stackSize >= STACK_MAX - 8) {
|
if (stackSize >= STACK_MAX - 8) {
|
||||||
if (state->panic) { // we're in a panic state, let the 8 reserved slots be filled
|
|
||||||
if (stackSize < STACK_MAX)
|
|
||||||
*(state->top++) = val;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cosmoV_error(state, "Stack overflow!");
|
cosmoV_error(state, "Stack overflow!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user