2017-03-16 00:56:37 +00:00
|
|
|
#include <gst/vm.h>
|
|
|
|
#include <gst/util.h>
|
|
|
|
#include <gst/value.h>
|
|
|
|
#include <gst/ds.h>
|
|
|
|
#include <gst/gc.h>
|
|
|
|
#include <gst/thread.h>
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-03-08 20:08:46 +00:00
|
|
|
/* Macros for errors in the vm */
|
|
|
|
|
|
|
|
/* Exit from the VM normally */
|
|
|
|
#define gst_exit(vm, r) return ((vm)->ret = (r), GST_RETURN_OK)
|
|
|
|
|
|
|
|
/* Bail from the VM with an error string. */
|
2017-03-10 05:09:42 +00:00
|
|
|
#define gst_error(vm, e) do { (vm)->ret = gst_load_cstring((vm), (e)); goto vm_error; } while (0)
|
2017-03-08 20:08:46 +00:00
|
|
|
|
|
|
|
/* Crash. Not catchable, unlike error. */
|
|
|
|
#define gst_crash(vm, e) return ((vm)->crash = (e), GST_RETURN_CRASH)
|
|
|
|
|
|
|
|
/* Error if the condition is false */
|
|
|
|
#define gst_assert(vm, cond, e) do {if (!(cond)){gst_error((vm), (e));}} while (0)
|
|
|
|
|
2017-02-23 22:21:13 +00:00
|
|
|
static const char GST_NO_UPVALUE[] = "no upvalue";
|
|
|
|
static const char GST_EXPECTED_FUNCTION[] = "expected function";
|
|
|
|
static const char GST_EXPECTED_NUMBER_ROP[] = "expected right operand to be number";
|
|
|
|
static const char GST_EXPECTED_NUMBER_LOP[] = "expected left operand to be number";
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-03-10 05:09:42 +00:00
|
|
|
/* Contextual macro to state in function with VM */
|
|
|
|
#define GST_STATE_SYNC() do { \
|
|
|
|
thread = *vm->thread; \
|
|
|
|
stack = thread.data + thread.count; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
/* Write local state back to VM */
|
|
|
|
#define GST_STATE_WRITE() do { \
|
2017-03-10 05:17:34 +00:00
|
|
|
*vm->thread = thread; \
|
2017-03-10 05:09:42 +00:00
|
|
|
} while (0)
|
2017-03-11 22:04:59 +00:00
|
|
|
|
2017-03-10 05:09:42 +00:00
|
|
|
/* Start running the VM from where it left off. Continue running
|
|
|
|
* until the stack size is smaller than minStackSize. */
|
|
|
|
static int gst_continue_size(Gst *vm, uint32_t stackBase) {
|
2017-02-26 16:47:50 +00:00
|
|
|
/* VM state */
|
2017-03-08 15:54:50 +00:00
|
|
|
GstThread thread;
|
2017-02-26 16:47:50 +00:00
|
|
|
GstValue *stack;
|
|
|
|
GstValue temp, v1, v2;
|
|
|
|
uint16_t *pc;
|
2017-03-08 20:08:46 +00:00
|
|
|
|
2017-03-10 05:17:34 +00:00
|
|
|
/* Intialize local state */
|
|
|
|
GST_STATE_SYNC();
|
2017-03-10 05:09:42 +00:00
|
|
|
pc = gst_frame_pc(stack);
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-26 16:47:50 +00:00
|
|
|
/* Main interpreter loop */
|
2017-02-09 20:02:59 +00:00
|
|
|
for (;;) {
|
2017-02-26 16:47:50 +00:00
|
|
|
|
|
|
|
switch (*pc) {
|
2017-02-12 15:27:18 +00:00
|
|
|
|
2017-03-10 05:09:42 +00:00
|
|
|
default:
|
2017-03-10 05:17:34 +00:00
|
|
|
gst_error(vm, "unknown opcode");
|
2017-03-10 05:09:42 +00:00
|
|
|
break;
|
|
|
|
|
2017-03-07 20:29:40 +00:00
|
|
|
#define OP_BINARY_MATH(op) \
|
2017-02-26 16:47:50 +00:00
|
|
|
v1 = stack[pc[2]]; \
|
|
|
|
v2 = stack[pc[3]]; \
|
2017-02-23 22:21:13 +00:00
|
|
|
gst_assert(vm, v1.type == GST_NUMBER, GST_EXPECTED_NUMBER_LOP); \
|
|
|
|
gst_assert(vm, v2.type == GST_NUMBER, GST_EXPECTED_NUMBER_ROP); \
|
2017-02-16 02:02:00 +00:00
|
|
|
temp.type = GST_NUMBER; \
|
2017-02-13 05:11:30 +00:00
|
|
|
temp.data.number = v1.data.number op v2.data.number; \
|
2017-02-26 16:47:50 +00:00
|
|
|
stack[pc[1]] = temp; \
|
|
|
|
pc += 4; \
|
2017-03-14 19:55:50 +00:00
|
|
|
continue;
|
2017-02-12 15:27:18 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_ADD: /* Addition */
|
2017-03-07 20:29:40 +00:00
|
|
|
OP_BINARY_MATH(+)
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_SUB: /* Subtraction */
|
2017-03-07 20:29:40 +00:00
|
|
|
OP_BINARY_MATH(-)
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_MUL: /* Multiplication */
|
2017-03-07 20:29:40 +00:00
|
|
|
OP_BINARY_MATH(*)
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_DIV: /* Division */
|
2017-03-07 20:29:40 +00:00
|
|
|
OP_BINARY_MATH(/)
|
2017-02-12 15:27:18 +00:00
|
|
|
|
2017-03-07 20:29:40 +00:00
|
|
|
#undef OP_BINARY_MATH
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_NOT: /* Boolean unary (Boolean not) */
|
|
|
|
temp.type = GST_BOOLEAN;
|
2017-02-26 16:47:50 +00:00
|
|
|
temp.data.boolean = !gst_truthy(stack[pc[2]]);
|
|
|
|
stack[pc[1]] = temp;
|
|
|
|
pc += 3;
|
2017-03-14 19:55:50 +00:00
|
|
|
continue;
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-03-10 05:17:34 +00:00
|
|
|
case GST_OP_NEG: /* Unary negation */
|
|
|
|
v1 = stack[pc[2]];
|
|
|
|
gst_assert(vm, v1.type == GST_NUMBER, GST_EXPECTED_NUMBER_LOP);
|
|
|
|
temp.type = GST_NUMBER;
|
|
|
|
temp.data.number = -v1.data.number;
|
|
|
|
stack[pc[1]] = temp;
|
|
|
|
pc += 3;
|
2017-03-14 19:55:50 +00:00
|
|
|
continue;
|
2017-03-10 05:17:34 +00:00
|
|
|
|
|
|
|
case GST_OP_INV: /* Unary multiplicative inverse */
|
|
|
|
v1 = stack[pc[2]];
|
|
|
|
gst_assert(vm, v1.type == GST_NUMBER, GST_EXPECTED_NUMBER_LOP);
|
|
|
|
temp.type = GST_NUMBER;
|
|
|
|
temp.data.number = 1 / v1.data.number;
|
|
|
|
stack[pc[1]] = temp;
|
|
|
|
pc += 3;
|
2017-03-14 19:55:50 +00:00
|
|
|
continue;
|
2017-03-08 15:21:09 +00:00
|
|
|
|
2017-03-21 03:06:38 +00:00
|
|
|
case GST_OP_LEN: /* Length */
|
|
|
|
{
|
|
|
|
int status = gst_length(vm, stack[pc[2]], &v1);
|
|
|
|
if (status == GST_RETURN_OK)
|
|
|
|
stack[pc[1]] = v1;
|
|
|
|
else
|
|
|
|
goto vm_error;
|
|
|
|
pc += 3;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_FLS: /* Load False */
|
|
|
|
temp.type = GST_BOOLEAN;
|
2017-02-13 05:11:30 +00:00
|
|
|
temp.data.boolean = 0;
|
2017-02-26 16:47:50 +00:00
|
|
|
stack[pc[1]] = temp;
|
|
|
|
pc += 2;
|
2017-03-14 19:55:50 +00:00
|
|
|
continue;
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_TRU: /* Load True */
|
|
|
|
temp.type = GST_BOOLEAN;
|
2017-02-13 05:11:30 +00:00
|
|
|
temp.data.boolean = 1;
|
2017-02-26 16:47:50 +00:00
|
|
|
stack[pc[1]] = temp;
|
|
|
|
pc += 2;
|
2017-03-14 19:55:50 +00:00
|
|
|
continue;
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_NIL: /* Load Nil */
|
|
|
|
temp.type = GST_NIL;
|
2017-02-26 16:47:50 +00:00
|
|
|
stack[pc[1]] = temp;
|
|
|
|
pc += 2;
|
2017-03-14 19:55:50 +00:00
|
|
|
continue;
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_I16: /* Load Small Integer */
|
|
|
|
temp.type = GST_NUMBER;
|
2017-02-26 16:47:50 +00:00
|
|
|
temp.data.number = ((int16_t *)(pc))[2];
|
|
|
|
stack[pc[1]] = temp;
|
|
|
|
pc += 3;
|
2017-03-14 19:55:50 +00:00
|
|
|
continue;
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_UPV: /* Load Up Value */
|
2017-02-26 16:47:50 +00:00
|
|
|
case GST_OP_SUV: /* Set Up Value */
|
|
|
|
{
|
|
|
|
GstValue *upv;
|
2017-03-10 05:09:42 +00:00
|
|
|
GstFunction *fn;
|
2017-02-26 16:47:50 +00:00
|
|
|
GstFuncEnv *env;
|
|
|
|
uint16_t level = pc[2];
|
2017-03-10 05:09:42 +00:00
|
|
|
temp = gst_frame_callee(stack);
|
|
|
|
gst_assert(vm, temp.type == GST_FUNCTION, GST_EXPECTED_FUNCTION);
|
|
|
|
fn = temp.data.function;
|
2017-02-26 16:47:50 +00:00
|
|
|
if (level == 0)
|
|
|
|
upv = stack + pc[3];
|
|
|
|
else {
|
|
|
|
while (fn && --level)
|
|
|
|
fn = fn->parent;
|
|
|
|
gst_assert(vm, fn, GST_NO_UPVALUE);
|
|
|
|
env = fn->env;
|
|
|
|
if (env->thread)
|
|
|
|
upv = env->thread->data + env->stackOffset + pc[3];
|
|
|
|
else
|
|
|
|
upv = env->values + pc[3];
|
|
|
|
}
|
|
|
|
if (pc[0] == GST_OP_UPV) {
|
|
|
|
stack[pc[1]] = *upv;
|
|
|
|
} else {
|
|
|
|
*upv = stack[pc[1]];
|
|
|
|
}
|
|
|
|
pc += 4;
|
|
|
|
}
|
2017-03-14 19:55:50 +00:00
|
|
|
continue;
|
2017-02-13 05:11:30 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_JIF: /* Jump If */
|
2017-02-26 16:47:50 +00:00
|
|
|
if (gst_truthy(stack[pc[1]])) {
|
|
|
|
pc += 4;
|
2017-02-13 05:11:30 +00:00
|
|
|
} else {
|
2017-02-26 16:47:50 +00:00
|
|
|
pc += *((int32_t *)(pc + 2));
|
2017-02-13 05:11:30 +00:00
|
|
|
}
|
2017-03-14 19:55:50 +00:00
|
|
|
continue;
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_JMP: /* Jump */
|
2017-02-26 16:47:50 +00:00
|
|
|
pc += *((int32_t *)(pc + 1));
|
2017-03-14 19:55:50 +00:00
|
|
|
continue;
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_CST: /* Load constant value */
|
2017-03-10 05:17:34 +00:00
|
|
|
v1 = gst_frame_callee(stack);
|
2017-03-10 05:09:42 +00:00
|
|
|
gst_assert(vm, v1.type == GST_FUNCTION, GST_EXPECTED_FUNCTION);
|
|
|
|
if (pc[2] > v1.data.function->def->literalsLen)
|
2017-03-08 20:08:46 +00:00
|
|
|
gst_error(vm, GST_NO_UPVALUE);
|
2017-03-10 05:09:42 +00:00
|
|
|
stack[pc[1]] = v1.data.function->def->literals[pc[2]];
|
2017-02-26 16:47:50 +00:00
|
|
|
pc += 3;
|
2017-03-14 19:55:50 +00:00
|
|
|
continue;
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_I32: /* Load 32 bit integer */
|
|
|
|
temp.type = GST_NUMBER;
|
2017-02-26 16:47:50 +00:00
|
|
|
temp.data.number = *((int32_t *)(pc + 2));
|
|
|
|
stack[pc[1]] = temp;
|
|
|
|
pc += 4;
|
2017-03-14 19:55:50 +00:00
|
|
|
continue;
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_F64: /* Load 64 bit float */
|
|
|
|
temp.type = GST_NUMBER;
|
2017-02-26 16:47:50 +00:00
|
|
|
temp.data.number = (GstNumber) *((double *)(pc + 2));
|
|
|
|
stack[pc[1]] = temp;
|
|
|
|
pc += 6;
|
2017-03-14 19:55:50 +00:00
|
|
|
continue;
|
2017-02-13 05:11:30 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_MOV: /* Move Values */
|
2017-02-26 16:47:50 +00:00
|
|
|
stack[pc[1]] = stack[pc[2]];
|
|
|
|
pc += 3;
|
2017-03-14 19:55:50 +00:00
|
|
|
continue;
|
2017-02-13 05:11:30 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_CLN: /* Create closure from constant FuncDef */
|
2017-02-26 16:47:50 +00:00
|
|
|
{
|
2017-03-07 20:29:40 +00:00
|
|
|
GstFunction *fn;
|
2017-03-10 05:09:42 +00:00
|
|
|
v1 = gst_frame_callee(stack);
|
|
|
|
if (v1.type != GST_FUNCTION)
|
2017-02-26 16:47:50 +00:00
|
|
|
gst_error(vm, GST_EXPECTED_FUNCTION);
|
2017-03-10 05:09:42 +00:00
|
|
|
if (gst_frame_env(stack) == NULL) {
|
|
|
|
gst_frame_env(stack) = gst_alloc(vm, sizeof(GstFuncEnv));
|
2017-02-26 16:47:50 +00:00
|
|
|
*vm->thread = thread;
|
2017-03-10 05:09:42 +00:00
|
|
|
gst_frame_env(stack)->thread = vm->thread;
|
|
|
|
gst_frame_env(stack)->stackOffset = thread.count;
|
|
|
|
gst_frame_env(stack)->values = NULL;
|
2017-02-26 16:47:50 +00:00
|
|
|
}
|
2017-03-10 05:09:42 +00:00
|
|
|
if (pc[2] > v1.data.function->def->literalsLen)
|
2017-03-08 20:08:46 +00:00
|
|
|
gst_error(vm, GST_NO_UPVALUE);
|
2017-03-10 05:09:42 +00:00
|
|
|
temp = v1.data.function->def->literals[pc[2]];
|
2017-03-19 16:16:40 +00:00
|
|
|
if (temp.type != GST_FUNCDEF)
|
2017-02-26 16:47:50 +00:00
|
|
|
gst_error(vm, "cannot create closure");
|
|
|
|
fn = gst_alloc(vm, sizeof(GstFunction));
|
2017-03-19 16:16:40 +00:00
|
|
|
fn->def = temp.data.def;
|
2017-03-10 05:09:42 +00:00
|
|
|
fn->parent = v1.data.function;
|
|
|
|
fn->env = gst_frame_env(stack);
|
2017-02-26 16:47:50 +00:00
|
|
|
temp.type = GST_FUNCTION;
|
|
|
|
temp.data.function = fn;
|
|
|
|
stack[pc[1]] = temp;
|
|
|
|
pc += 3;
|
|
|
|
}
|
2017-02-13 05:11:30 +00:00
|
|
|
break;
|
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_EQL: /* Equality */
|
|
|
|
temp.type = GST_BOOLEAN;
|
2017-02-26 16:47:50 +00:00
|
|
|
temp.data.boolean = gst_equals(stack[pc[2]], stack[pc[3]]);
|
|
|
|
stack[pc[1]] = temp;
|
|
|
|
pc += 4;
|
2017-03-14 19:55:50 +00:00
|
|
|
continue;
|
2017-02-13 05:11:30 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_LTN: /* Less Than */
|
|
|
|
temp.type = GST_BOOLEAN;
|
2017-02-26 16:47:50 +00:00
|
|
|
temp.data.boolean = (gst_compare(stack[pc[2]], stack[pc[3]]) == -1);
|
|
|
|
stack[pc[1]] = temp;
|
|
|
|
pc += 4;
|
2017-03-14 19:55:50 +00:00
|
|
|
continue;
|
2017-02-13 05:11:30 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_LTE: /* Less Than or Equal to */
|
|
|
|
temp.type = GST_BOOLEAN;
|
2017-02-26 16:47:50 +00:00
|
|
|
temp.data.boolean = (gst_compare(stack[pc[2]], stack[pc[3]]) != 1);
|
|
|
|
stack[pc[1]] = temp;
|
|
|
|
pc += 4;
|
2017-03-14 19:55:50 +00:00
|
|
|
continue;
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_ARR: /* Array literal */
|
2017-02-13 05:11:30 +00:00
|
|
|
{
|
|
|
|
uint32_t i;
|
2017-02-26 16:47:50 +00:00
|
|
|
uint32_t arrayLen = pc[2];
|
2017-02-16 02:02:00 +00:00
|
|
|
GstArray *array = gst_array(vm, arrayLen);
|
2017-02-13 05:11:30 +00:00
|
|
|
array->count = arrayLen;
|
|
|
|
for (i = 0; i < arrayLen; ++i)
|
2017-02-26 16:47:50 +00:00
|
|
|
array->data[i] = stack[pc[3 + i]];
|
2017-02-16 02:02:00 +00:00
|
|
|
temp.type = GST_ARRAY;
|
2017-02-13 05:11:30 +00:00
|
|
|
temp.data.array = array;
|
2017-02-26 16:47:50 +00:00
|
|
|
stack[pc[1]] = temp;
|
|
|
|
pc += 3 + arrayLen;
|
2017-02-13 05:11:30 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_DIC: /* Object literal */
|
2017-02-13 05:11:30 +00:00
|
|
|
{
|
|
|
|
uint32_t i = 3;
|
2017-02-26 16:47:50 +00:00
|
|
|
uint32_t kvs = pc[2];
|
2017-02-16 02:02:00 +00:00
|
|
|
GstObject *o = gst_object(vm, kvs + 2);
|
2017-02-13 05:11:30 +00:00
|
|
|
kvs = kvs + 3;
|
|
|
|
while (i < kvs) {
|
2017-02-26 16:47:50 +00:00
|
|
|
v1 = stack[pc[i++]];
|
|
|
|
v2 = stack[pc[i++]];
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_object_put(vm, o, v1, v2);
|
2017-02-12 20:16:55 +00:00
|
|
|
}
|
2017-02-16 02:02:00 +00:00
|
|
|
temp.type = GST_OBJECT;
|
|
|
|
temp.data.object = o;
|
2017-02-26 16:47:50 +00:00
|
|
|
stack[pc[1]] = temp;
|
|
|
|
pc += kvs;
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|
2017-02-13 05:11:30 +00:00
|
|
|
break;
|
2017-03-07 20:29:40 +00:00
|
|
|
|
|
|
|
case GST_OP_TUP: /* Tuple literal */
|
|
|
|
{
|
|
|
|
uint32_t i;
|
|
|
|
uint32_t len = pc[2];
|
|
|
|
GstValue *tuple = gst_tuple(vm, len);
|
|
|
|
for (i = 0; i < len; ++i)
|
|
|
|
tuple[i] = stack[pc[3 + i]];
|
|
|
|
temp.type = GST_TUPLE;
|
|
|
|
temp.data.tuple = tuple;
|
|
|
|
stack[pc[1]] = temp;
|
|
|
|
pc += 3 + len;
|
|
|
|
}
|
|
|
|
break;
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-03-08 20:08:46 +00:00
|
|
|
case GST_OP_GET: /* Associative get */
|
2017-03-10 05:17:34 +00:00
|
|
|
{
|
|
|
|
const char *err;
|
|
|
|
err = gst_get(stack[pc[2]], stack[pc[3]], stack + pc[1]);
|
|
|
|
if (err != NULL)
|
|
|
|
gst_error(vm, err);
|
|
|
|
pc += 4;
|
|
|
|
}
|
2017-03-14 19:55:50 +00:00
|
|
|
continue;
|
2017-03-08 20:08:46 +00:00
|
|
|
|
|
|
|
case GST_OP_SET: /* Associative set */
|
2017-03-10 05:17:34 +00:00
|
|
|
{
|
|
|
|
const char *err;
|
|
|
|
err = gst_set(vm, stack[pc[1]], stack[pc[2]], stack[pc[3]]);
|
|
|
|
if (err != NULL)
|
|
|
|
gst_error(vm, err);
|
|
|
|
pc += 4;
|
|
|
|
}
|
2017-03-14 19:55:50 +00:00
|
|
|
break;
|
2017-02-13 05:11:30 +00:00
|
|
|
|
2017-03-10 05:17:34 +00:00
|
|
|
case GST_OP_ERR: /* Throw error */
|
|
|
|
vm->ret = stack[pc[1]];
|
|
|
|
goto vm_error;
|
2017-02-22 23:19:46 +00:00
|
|
|
|
2017-03-10 05:17:34 +00:00
|
|
|
case GST_OP_TRY: /* Begin try block */
|
|
|
|
gst_frame_errloc(stack) = pc[1];
|
|
|
|
gst_frame_errjmp(stack) = pc + *(uint32_t *)(pc + 2);
|
|
|
|
pc += 4;
|
2017-03-14 19:55:50 +00:00
|
|
|
continue;
|
2017-02-22 23:19:46 +00:00
|
|
|
|
2017-03-10 05:17:34 +00:00
|
|
|
case GST_OP_UTY: /* End try block */
|
|
|
|
gst_frame_errjmp(stack) = NULL;
|
|
|
|
pc++;
|
2017-03-14 19:55:50 +00:00
|
|
|
continue;
|
2017-02-22 23:19:46 +00:00
|
|
|
|
2017-03-10 05:09:42 +00:00
|
|
|
case GST_OP_RTN: /* Return nil */
|
2017-03-12 22:23:27 +00:00
|
|
|
stack = gst_thread_popframe(vm, &thread);
|
|
|
|
if (thread.count < stackBase) {
|
|
|
|
vm->ret.type = GST_NIL;
|
2017-03-14 19:55:50 +00:00
|
|
|
GST_STATE_WRITE();
|
2017-03-12 22:23:27 +00:00
|
|
|
return GST_RETURN_OK;
|
|
|
|
}
|
2017-03-14 19:55:50 +00:00
|
|
|
pc = gst_frame_pc(stack);
|
2017-03-12 22:23:27 +00:00
|
|
|
stack[gst_frame_ret(stack)].type = GST_NIL;
|
2017-03-14 19:55:50 +00:00
|
|
|
continue;
|
2017-03-10 05:09:42 +00:00
|
|
|
|
|
|
|
case GST_OP_RET: /* Return */
|
2017-03-15 05:26:45 +00:00
|
|
|
temp = stack[pc[1]];
|
2017-03-12 22:23:27 +00:00
|
|
|
stack = gst_thread_popframe(vm, &thread);
|
|
|
|
if (thread.count < stackBase) {
|
|
|
|
vm->ret = temp;
|
2017-03-14 19:55:50 +00:00
|
|
|
GST_STATE_WRITE();
|
2017-03-12 22:23:27 +00:00
|
|
|
return GST_RETURN_OK;
|
2017-03-10 05:17:34 +00:00
|
|
|
}
|
2017-03-14 19:55:50 +00:00
|
|
|
pc = gst_frame_pc(stack);
|
2017-03-12 22:23:27 +00:00
|
|
|
stack[gst_frame_ret(stack)] = temp;
|
2017-03-14 19:55:50 +00:00
|
|
|
continue;
|
2017-03-10 05:09:42 +00:00
|
|
|
|
|
|
|
case GST_OP_CAL: /* Call */
|
|
|
|
case GST_OP_TCL: /* Tail call */
|
2017-03-15 05:26:45 +00:00
|
|
|
{
|
|
|
|
GstValue *oldStack;
|
|
|
|
temp = stack[pc[1]];
|
2017-03-14 19:55:50 +00:00
|
|
|
int isTCall = *pc == GST_OP_TCL;
|
2017-03-15 05:26:45 +00:00
|
|
|
uint32_t i, arity, offset, size;
|
|
|
|
uint16_t ret = pc[2];
|
|
|
|
offset = isTCall ? 3 : 4;
|
|
|
|
arity = pc[offset - 1];
|
|
|
|
/* Push new frame */
|
|
|
|
stack = gst_thread_beginframe(vm, &thread, temp, arity);
|
2017-03-19 21:29:25 +00:00
|
|
|
if (stack == NULL) gst_error(vm, "expected function");
|
2017-03-15 05:26:45 +00:00
|
|
|
oldStack = stack - GST_FRAME_SIZE - gst_frame_prevsize(stack);
|
|
|
|
/* Write arguments */
|
|
|
|
size = gst_frame_size(stack);
|
|
|
|
for (i = 0; i < arity; ++i)
|
|
|
|
stack[i + size - arity] = oldStack[pc[offset + i]];
|
|
|
|
/* Finish new frame */
|
|
|
|
gst_thread_endframe(vm, &thread);
|
|
|
|
/* Check tail call - if so, replace frame. */
|
|
|
|
if (isTCall) {
|
|
|
|
stack = gst_thread_tail(vm, &thread);
|
|
|
|
} else {
|
|
|
|
gst_frame_ret(oldStack) = ret;
|
|
|
|
}
|
|
|
|
/* Call function */
|
|
|
|
temp = gst_frame_callee(stack);
|
|
|
|
if (temp.type == GST_FUNCTION) {
|
|
|
|
/* Save pc and set new pc */
|
2017-03-14 19:55:50 +00:00
|
|
|
if (!isTCall)
|
|
|
|
gst_frame_pc(oldStack) = pc + offset + arity;
|
2017-03-15 05:26:45 +00:00
|
|
|
pc = temp.data.function->def->byteCode;
|
|
|
|
} else {
|
|
|
|
int status;
|
2017-03-19 16:16:40 +00:00
|
|
|
gst_frame_pc(stack) = pc;
|
2017-03-15 05:26:45 +00:00
|
|
|
GST_STATE_WRITE();
|
|
|
|
vm->ret.type = GST_NIL;
|
|
|
|
status = temp.data.cfunction(vm);
|
|
|
|
GST_STATE_SYNC();
|
|
|
|
stack = gst_thread_popframe(vm, &thread);
|
|
|
|
if (status == GST_RETURN_OK)
|
|
|
|
if (thread.count < stackBase) {
|
2017-03-14 19:55:50 +00:00
|
|
|
GST_STATE_WRITE();
|
2017-03-15 05:26:45 +00:00
|
|
|
return status;
|
2017-03-14 19:55:50 +00:00
|
|
|
} else {
|
2017-03-15 05:26:45 +00:00
|
|
|
stack[gst_frame_ret(stack)] = vm->ret;
|
2017-03-14 19:55:50 +00:00
|
|
|
if (isTCall)
|
|
|
|
pc = gst_frame_pc(stack);
|
|
|
|
else
|
|
|
|
pc += offset + arity;
|
|
|
|
}
|
2017-03-15 05:26:45 +00:00
|
|
|
else
|
|
|
|
goto vm_error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2017-03-10 05:09:42 +00:00
|
|
|
|
|
|
|
/* Handle errors from c functions and vm opcodes */
|
|
|
|
vm_error:
|
2017-03-21 03:06:38 +00:00
|
|
|
if (stack == NULL)
|
|
|
|
return GST_RETURN_ERROR;
|
2017-03-12 22:23:27 +00:00
|
|
|
while (gst_frame_errjmp(stack) == NULL) {
|
|
|
|
stack = gst_thread_popframe(vm, &thread);
|
|
|
|
if (thread.count < stackBase)
|
|
|
|
return GST_RETURN_ERROR;
|
|
|
|
}
|
2017-03-10 05:09:42 +00:00
|
|
|
pc = gst_frame_errjmp(stack);
|
|
|
|
stack[gst_frame_errloc(stack)] = vm->ret;
|
|
|
|
break;
|
|
|
|
|
2017-03-14 19:55:50 +00:00
|
|
|
} /* end switch */
|
2017-03-10 05:09:42 +00:00
|
|
|
|
2017-02-26 16:47:50 +00:00
|
|
|
/* TODO: Move collection only to places that allocate memory */
|
2017-02-23 22:21:13 +00:00
|
|
|
/* This, however, is good for testing to ensure no memory leaks */
|
2017-02-26 16:47:50 +00:00
|
|
|
*vm->thread = thread;
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_maybe_collect(vm);
|
2017-03-10 05:09:42 +00:00
|
|
|
|
|
|
|
} /* end for */
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Continue running the VM after it has stopped */
|
|
|
|
int gst_continue(Gst *vm) {
|
2017-03-10 05:17:34 +00:00
|
|
|
return gst_continue_size(vm, vm->thread->count);
|
2017-03-10 05:09:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Run the vm with a given function */
|
2017-03-12 22:23:27 +00:00
|
|
|
int gst_run(Gst *vm, GstValue callee) {
|
2017-03-14 19:55:50 +00:00
|
|
|
GstValue *stack;
|
2017-03-12 22:23:27 +00:00
|
|
|
vm->thread = gst_thread(vm, callee, 64);
|
2017-03-14 19:55:50 +00:00
|
|
|
if (vm->thread == NULL)
|
|
|
|
return GST_RETURN_CRASH;
|
|
|
|
stack = gst_thread_stack(vm->thread);
|
|
|
|
/* If callee was not actually a function, get the delegate function */
|
|
|
|
callee = gst_frame_callee(stack);
|
|
|
|
if (callee.type == GST_CFUNCTION) {
|
2017-03-15 05:26:45 +00:00
|
|
|
int status;
|
|
|
|
vm->ret.type = GST_NIL;
|
|
|
|
status = callee.data.cfunction(vm);
|
|
|
|
gst_thread_popframe(vm, vm->thread);
|
|
|
|
return status;
|
2017-03-14 19:55:50 +00:00
|
|
|
} else {
|
|
|
|
return gst_continue(vm);
|
|
|
|
}
|
2017-03-10 05:09:42 +00:00
|
|
|
}
|
|
|
|
|
2017-03-12 22:23:27 +00:00
|
|
|
/* Call a gst function */
|
2017-03-10 05:09:42 +00:00
|
|
|
int gst_call(Gst *vm, GstValue callee, uint32_t arity, GstValue *args) {
|
|
|
|
GstValue *stack;
|
2017-03-14 19:55:50 +00:00
|
|
|
uint32_t i, size;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
/* Set the return position */
|
|
|
|
stack = gst_thread_stack(vm->thread);
|
|
|
|
gst_frame_ret(stack) = gst_frame_size(stack);
|
|
|
|
|
|
|
|
/* Add extra space for returning value */
|
|
|
|
gst_thread_pushnil(vm, vm->thread, 1);
|
2017-03-12 22:23:27 +00:00
|
|
|
stack = gst_thread_beginframe(vm, vm->thread, callee, arity);
|
2017-03-14 19:55:50 +00:00
|
|
|
|
|
|
|
/* Write args to stack */
|
|
|
|
size = gst_frame_size(stack) - arity;
|
2017-03-12 22:23:27 +00:00
|
|
|
for (i = 0; i < arity; ++i)
|
2017-03-14 19:55:50 +00:00
|
|
|
stack[i + size] = args[i];
|
2017-03-12 22:23:27 +00:00
|
|
|
gst_thread_endframe(vm, vm->thread);
|
2017-03-14 19:55:50 +00:00
|
|
|
|
|
|
|
/* Call function */
|
2017-03-15 05:26:45 +00:00
|
|
|
callee = gst_frame_callee(stack);
|
|
|
|
if (callee.type == GST_FUNCTION) {
|
2017-03-14 19:55:50 +00:00
|
|
|
gst_frame_pc(stack) = callee.data.function->def->byteCode;
|
2017-03-15 05:26:45 +00:00
|
|
|
status = gst_continue(vm);
|
|
|
|
} else {
|
|
|
|
vm->ret.type = GST_NIL;
|
|
|
|
status = callee.data.cfunction(vm);
|
|
|
|
gst_thread_popframe(vm, vm->thread);
|
|
|
|
}
|
2017-03-14 19:55:50 +00:00
|
|
|
|
|
|
|
/* Pop the extra nil */
|
|
|
|
--gst_frame_size(gst_thread_stack(vm->thread));
|
|
|
|
|
|
|
|
return status;
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|
|
|
|
|
2017-02-10 04:28:11 +00:00
|
|
|
/* Get an argument from the stack */
|
2017-02-16 02:02:00 +00:00
|
|
|
GstValue gst_arg(Gst *vm, uint16_t index) {
|
2017-03-12 22:23:27 +00:00
|
|
|
GstValue *stack = gst_thread_stack(vm->thread);
|
2017-03-10 05:09:42 +00:00
|
|
|
uint16_t frameSize = gst_frame_size(stack);
|
2017-03-08 20:08:46 +00:00
|
|
|
if (frameSize <= index) {
|
2017-03-10 05:17:34 +00:00
|
|
|
GstValue ret;
|
|
|
|
ret.type = GST_NIL;
|
|
|
|
return ret;
|
2017-03-08 20:08:46 +00:00
|
|
|
}
|
2017-02-26 16:47:50 +00:00
|
|
|
return stack[index];
|
2017-02-10 04:28:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Put a value on the stack */
|
2017-02-16 02:02:00 +00:00
|
|
|
void gst_set_arg(Gst* vm, uint16_t index, GstValue x) {
|
2017-03-12 22:23:27 +00:00
|
|
|
GstValue *stack = gst_thread_stack(vm->thread);
|
2017-03-10 05:09:42 +00:00
|
|
|
uint16_t frameSize = gst_frame_size(stack);
|
2017-03-10 05:17:34 +00:00
|
|
|
if (frameSize <= index) return;
|
2017-02-26 16:47:50 +00:00
|
|
|
stack[index] = x;
|
2017-02-10 04:28:11 +00:00
|
|
|
}
|
|
|
|
|
2017-02-11 19:01:06 +00:00
|
|
|
/* Get the size of the VMStack */
|
2017-02-16 02:02:00 +00:00
|
|
|
uint16_t gst_count_args(Gst *vm) {
|
2017-03-12 22:23:27 +00:00
|
|
|
GstValue *stack = gst_thread_stack(vm->thread);
|
2017-03-10 05:09:42 +00:00
|
|
|
return gst_frame_size(stack);
|
2017-02-11 19:01:06 +00:00
|
|
|
}
|
|
|
|
|
2017-02-09 20:02:59 +00:00
|
|
|
/* Initialize the VM */
|
2017-02-16 02:02:00 +00:00
|
|
|
void gst_init(Gst *vm) {
|
|
|
|
vm->ret.type = GST_NIL;
|
2017-02-22 23:19:46 +00:00
|
|
|
vm->crash = NULL;
|
2017-02-12 20:16:55 +00:00
|
|
|
/* Garbage collection */
|
2017-02-09 23:50:47 +00:00
|
|
|
vm->blocks = NULL;
|
|
|
|
vm->nextCollection = 0;
|
2017-02-13 05:11:30 +00:00
|
|
|
/* Setting memoryInterval to zero currently forces
|
|
|
|
* a collection pretty much every cycle, which is
|
|
|
|
* obviously horrible for performance. It helps ensure
|
|
|
|
* there are no memory bugs during dev */
|
2017-02-23 22:21:13 +00:00
|
|
|
vm->memoryInterval = 2000;
|
2017-02-09 23:50:47 +00:00
|
|
|
vm->black = 0;
|
2017-02-13 02:54:18 +00:00
|
|
|
/* Add thread */
|
2017-02-13 03:25:17 +00:00
|
|
|
vm->thread = NULL;
|
2017-03-14 23:13:17 +00:00
|
|
|
vm->rootenv.type = GST_NIL;
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear all memory associated with the VM */
|
2017-02-16 02:02:00 +00:00
|
|
|
void gst_deinit(Gst *vm) {
|
2017-02-23 22:21:13 +00:00
|
|
|
gst_clear_memory(vm);
|
2017-03-14 23:13:17 +00:00
|
|
|
vm->thread = NULL;
|
|
|
|
vm->rootenv.type = GST_NIL;
|
|
|
|
vm->ret.type = GST_NIL;
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|