2017-02-09 20:02:59 +00:00
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "vm.h"
|
|
|
|
#include "value.h"
|
2017-02-09 23:50:47 +00:00
|
|
|
#include "ds.h"
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-19 16:19:39 +00:00
|
|
|
static const char OOM[] = "out of memory";
|
|
|
|
static const char NO_UPVALUE[] = "no upvalue";
|
|
|
|
static const char EXPECTED_FUNCTION[] = "expected function";
|
|
|
|
static const char VMS_EXPECTED_NUMBER_ROP[] = "expected right operand to be number";
|
|
|
|
static const char VMS_EXPECTED_NUMBER_LOP[] = "expected left operand to be number";
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-12 15:27:18 +00:00
|
|
|
/* The size of a StackFrame in units of Values. */
|
2017-02-16 02:02:00 +00:00
|
|
|
#define FRAME_SIZE ((sizeof(GstStackFrame) + sizeof(GstValue) - 1) / sizeof(GstValue))
|
2017-02-12 15:27:18 +00:00
|
|
|
|
|
|
|
/* Get the stack frame pointer for a thread */
|
2017-02-16 02:02:00 +00:00
|
|
|
static GstStackFrame *thread_frame(GstThread * thread) {
|
|
|
|
return (GstStackFrame *)(thread->data + thread->count - FRAME_SIZE);
|
2017-02-12 15:27:18 +00:00
|
|
|
}
|
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
/* Ensure that a thread has enough space in it */
|
|
|
|
static void thread_ensure(Gst *vm, GstThread *thread, uint32_t size) {
|
|
|
|
if (size > thread->capacity) {
|
|
|
|
uint32_t newCap = size * 2;
|
|
|
|
GstValue *newData = gst_alloc(vm, sizeof(GstValue) * newCap);
|
|
|
|
memcpy(newData, thread->data, thread->capacity * sizeof(GstValue));
|
|
|
|
thread->data = newData;
|
|
|
|
thread->capacity = newCap;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Push a stack frame onto a thread */
|
|
|
|
static void thread_push(Gst *vm, GstThread *thread, GstValue callee, uint32_t size) {
|
|
|
|
uint16_t oldSize;
|
|
|
|
uint32_t nextCount, i;
|
|
|
|
GstStackFrame *frame;
|
|
|
|
if (thread->count) {
|
|
|
|
frame = thread_frame(thread);
|
|
|
|
oldSize = frame->size;
|
|
|
|
} else {
|
|
|
|
oldSize = 0;
|
|
|
|
}
|
|
|
|
nextCount = thread->count + oldSize + FRAME_SIZE;
|
|
|
|
thread_ensure(vm, thread, nextCount + size);
|
|
|
|
thread->count = nextCount;
|
|
|
|
/* Ensure values start out as nil so as to not confuse
|
|
|
|
* the garabage collector */
|
|
|
|
for (i = nextCount; i < nextCount + size; ++i)
|
|
|
|
thread->data[i].type = GST_NIL;
|
|
|
|
vm->base = thread->data + thread->count;
|
|
|
|
vm->frame = frame = (GstStackFrame *)(vm->base - FRAME_SIZE);
|
|
|
|
/* Set up the new stack frame */
|
|
|
|
frame->prevSize = oldSize;
|
|
|
|
frame->size = size;
|
|
|
|
frame->env = NULL;
|
|
|
|
frame->callee = callee;
|
2017-02-19 16:19:39 +00:00
|
|
|
frame->errorJump = NULL;
|
2017-02-16 02:02:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy the current function stack to the current closure
|
|
|
|
environment. Call when exiting function with closures. */
|
|
|
|
static void thread_split_env(Gst *vm) {
|
|
|
|
GstStackFrame *frame = vm->frame;
|
|
|
|
GstFuncEnv *env = frame->env;
|
|
|
|
/* Check for closures */
|
|
|
|
if (env) {
|
|
|
|
GstThread *thread = vm->thread;
|
|
|
|
uint32_t size = frame->size;
|
|
|
|
env->thread = NULL;
|
|
|
|
env->stackOffset = size;
|
|
|
|
env->values = gst_alloc(vm, sizeof(GstValue) * size);
|
|
|
|
memcpy(env->values, thread->data + thread->count, size * sizeof(GstValue));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Pop the top-most stack frame from stack */
|
|
|
|
static void thread_pop(Gst *vm) {
|
|
|
|
GstThread *thread = vm->thread;
|
|
|
|
GstStackFrame *frame = vm->frame;
|
|
|
|
uint32_t delta = FRAME_SIZE + frame->prevSize;
|
|
|
|
if (thread->count) {
|
|
|
|
thread_split_env(vm);
|
|
|
|
} else {
|
2017-02-19 16:19:39 +00:00
|
|
|
gst_crash(vm, "stack underflow");
|
2017-02-16 02:02:00 +00:00
|
|
|
}
|
|
|
|
thread->count -= delta;
|
|
|
|
vm->base -= delta;
|
|
|
|
vm->frame = (GstStackFrame *)(vm->base - FRAME_SIZE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-09 23:50:47 +00:00
|
|
|
/* The metadata header associated with an allocated block of memory */
|
2017-02-16 02:02:00 +00:00
|
|
|
#define gc_header(mem) ((GCMemoryHeader *)(mem) - 1)
|
2017-02-09 23:50:47 +00:00
|
|
|
|
|
|
|
/* Memory header struct. Node of a linked list of memory blocks. */
|
|
|
|
typedef struct GCMemoryHeader GCMemoryHeader;
|
|
|
|
struct GCMemoryHeader {
|
|
|
|
GCMemoryHeader * next;
|
2017-02-13 02:54:18 +00:00
|
|
|
uint32_t color : 1;
|
2017-02-09 23:50:47 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Forward declaration */
|
2017-02-16 02:02:00 +00:00
|
|
|
static void gst_mark(Gst *vm, GstValue *x);
|
2017-02-09 23:50:47 +00:00
|
|
|
|
|
|
|
/* Helper to mark function environments */
|
2017-02-16 02:02:00 +00:00
|
|
|
static void gst_mark_funcenv(Gst *vm, GstFuncEnv *env) {
|
|
|
|
if (gc_header(env)->color != vm->black) {
|
|
|
|
GstValue temp;
|
|
|
|
gc_header(env)->color = vm->black;
|
2017-02-09 23:50:47 +00:00
|
|
|
if (env->thread) {
|
2017-02-16 02:02:00 +00:00
|
|
|
temp.type = GST_THREAD;
|
2017-02-15 01:45:34 +00:00
|
|
|
temp.data.thread = env->thread;
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_mark(vm, &temp);
|
2017-02-13 04:45:52 +00:00
|
|
|
}
|
|
|
|
if (env->values) {
|
2017-02-09 23:50:47 +00:00
|
|
|
uint32_t count = env->stackOffset;
|
|
|
|
uint32_t i;
|
2017-02-16 02:02:00 +00:00
|
|
|
gc_header(env->values)->color = vm->black;
|
2017-02-13 02:54:18 +00:00
|
|
|
for (i = 0; i < count; ++i)
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_mark(vm, env->values + i);
|
2017-02-09 23:50:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-13 02:54:18 +00:00
|
|
|
/* GC helper to mark a FuncDef */
|
2017-02-16 02:02:00 +00:00
|
|
|
static void gst_mark_funcdef(Gst *vm, GstFuncDef *def) {
|
|
|
|
if (gc_header(def)->color != vm->black) {
|
|
|
|
gc_header(def)->color = vm->black;
|
|
|
|
gc_header(def->byteCode)->color = vm->black;
|
2017-02-13 02:54:18 +00:00
|
|
|
uint32_t count, i;
|
|
|
|
if (def->literals) {
|
|
|
|
count = def->literalsLen;
|
2017-02-16 02:02:00 +00:00
|
|
|
gc_header(def->literals)->color = vm->black;
|
2017-02-13 21:48:11 +00:00
|
|
|
for (i = 0; i < count; ++i) {
|
|
|
|
/* If the literal is a NIL type, it actually
|
|
|
|
* contains a FuncDef */
|
2017-02-16 02:02:00 +00:00
|
|
|
if (def->literals[i].type == GST_NIL) {
|
|
|
|
gst_mark_funcdef(vm, (GstFuncDef *) def->literals[i].data.pointer);
|
2017-02-13 21:48:11 +00:00
|
|
|
} else {
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_mark(vm, def->literals + i);
|
2017-02-13 21:48:11 +00:00
|
|
|
}
|
|
|
|
}
|
2017-02-13 02:54:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Helper to mark a stack frame. Returns the next frame. */
|
2017-02-16 02:02:00 +00:00
|
|
|
static GstStackFrame *gst_mark_stackframe(Gst *vm, GstStackFrame *frame) {
|
2017-02-13 02:54:18 +00:00
|
|
|
uint32_t i;
|
2017-02-16 02:02:00 +00:00
|
|
|
GstValue *stack = (GstValue *)frame + FRAME_SIZE;
|
|
|
|
gst_mark(vm, &frame->callee);
|
2017-02-13 02:54:18 +00:00
|
|
|
if (frame->env)
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_mark_funcenv(vm, frame->env);
|
2017-02-13 02:54:18 +00:00
|
|
|
for (i = 0; i < frame->size; ++i)
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_mark(vm, stack + i);
|
|
|
|
return (GstStackFrame *)(stack + frame->size);
|
2017-02-13 02:54:18 +00:00
|
|
|
}
|
|
|
|
|
2017-02-09 23:50:47 +00:00
|
|
|
/* Mark allocated memory associated with a value. This is
|
2017-02-13 03:25:17 +00:00
|
|
|
* the main function for doing the garbage collection mark phase. */
|
2017-02-16 02:02:00 +00:00
|
|
|
static void gst_mark(Gst *vm, GstValue *x) {
|
2017-02-09 23:50:47 +00:00
|
|
|
switch (x->type) {
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_NIL:
|
|
|
|
case GST_BOOLEAN:
|
|
|
|
case GST_NUMBER:
|
|
|
|
case GST_CFUNCTION:
|
2017-02-09 23:50:47 +00:00
|
|
|
break;
|
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_STRING:
|
|
|
|
gc_header(gst_string_raw(x->data.string))->color = vm->black;
|
2017-02-09 23:50:47 +00:00
|
|
|
break;
|
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_BYTEBUFFER:
|
|
|
|
gc_header(x->data.buffer)->color = vm->black;
|
|
|
|
gc_header(x->data.buffer->data)->color = vm->black;
|
2017-02-09 23:50:47 +00:00
|
|
|
break;
|
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_ARRAY:
|
|
|
|
if (gc_header(x->data.array)->color != vm->black) {
|
2017-02-09 23:50:47 +00:00
|
|
|
uint32_t i, count;
|
|
|
|
count = x->data.array->count;
|
2017-02-16 02:02:00 +00:00
|
|
|
gc_header(x->data.array)->color = vm->black;
|
|
|
|
gc_header(x->data.array->data)->color = vm->black;
|
2017-02-09 23:50:47 +00:00
|
|
|
for (i = 0; i < count; ++i)
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_mark(vm, x->data.array->data + i);
|
2017-02-09 23:50:47 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_THREAD:
|
|
|
|
if (gc_header(x->data.thread)->color != vm->black) {
|
|
|
|
GstThread *thread = x->data.thread;
|
|
|
|
GstStackFrame *frame = (GstStackFrame *)thread->data;
|
|
|
|
GstStackFrame *end = thread_frame(thread);
|
|
|
|
gc_header(thread)->color = vm->black;
|
|
|
|
gc_header(thread->data)->color = vm->black;
|
2017-02-13 02:54:18 +00:00
|
|
|
while (frame <= end)
|
2017-02-16 02:02:00 +00:00
|
|
|
frame = gst_mark_stackframe(vm, frame);
|
2017-02-09 23:50:47 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_FUNCTION:
|
|
|
|
if (gc_header(x->data.function)->color != vm->black) {
|
|
|
|
GstFunction *f = x->data.function;
|
|
|
|
gc_header(f)->color = vm->black;
|
|
|
|
gst_mark_funcdef(vm, f->def);
|
2017-02-13 04:45:52 +00:00
|
|
|
if (f->env)
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_mark_funcenv(vm, f->env);
|
2017-02-13 02:54:18 +00:00
|
|
|
if (f->parent) {
|
2017-02-16 02:02:00 +00:00
|
|
|
GstValue temp;
|
|
|
|
temp.type = GST_FUNCTION;
|
|
|
|
temp.data.function = f->parent;
|
|
|
|
gst_mark(vm, &temp);
|
2017-02-09 23:50:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OBJECT:
|
|
|
|
if (gc_header(x->data.object)->color != vm->black) {
|
|
|
|
uint32_t i;
|
|
|
|
GstBucket *bucket;
|
|
|
|
gc_header(x->data.object)->color = vm->black;
|
|
|
|
gc_header(x->data.object->buckets)->color = vm->black;
|
|
|
|
for (i = 0; i < x->data.object->capacity; ++i) {
|
|
|
|
bucket = x->data.object->buckets[i];
|
|
|
|
while (bucket) {
|
2017-02-16 20:10:59 +00:00
|
|
|
gc_header(bucket)->color = vm->black;
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_mark(vm, &bucket->key);
|
|
|
|
gst_mark(vm, &bucket->value);
|
|
|
|
bucket = bucket->next;
|
|
|
|
}
|
2017-02-09 23:50:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Iterate over all allocated memory, and free memory that is not
|
|
|
|
* marked as reachable. Flip the gc color flag for next sweep. */
|
2017-02-16 02:02:00 +00:00
|
|
|
static void gst_sweep(Gst *vm) {
|
|
|
|
GCMemoryHeader *previous = NULL;
|
|
|
|
GCMemoryHeader *current = vm->blocks;
|
|
|
|
GCMemoryHeader *next;
|
2017-02-09 23:50:47 +00:00
|
|
|
while (current) {
|
2017-02-13 04:45:52 +00:00
|
|
|
next = current->next;
|
2017-02-09 23:50:47 +00:00
|
|
|
if (current->color != vm->black) {
|
|
|
|
if (previous) {
|
2017-02-13 04:45:52 +00:00
|
|
|
previous->next = next;
|
2017-02-09 23:50:47 +00:00
|
|
|
} else {
|
2017-02-13 04:45:52 +00:00
|
|
|
vm->blocks = next;
|
2017-02-09 23:50:47 +00:00
|
|
|
}
|
|
|
|
free(current);
|
|
|
|
} else {
|
|
|
|
previous = current;
|
|
|
|
}
|
2017-02-13 04:45:52 +00:00
|
|
|
current = next;
|
2017-02-09 23:50:47 +00:00
|
|
|
}
|
|
|
|
/* Rotate flag */
|
|
|
|
vm->black = !vm->black;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Prepare a memory block */
|
2017-02-16 02:02:00 +00:00
|
|
|
static void *gst_alloc_prepare(Gst *vm, char *rawBlock, uint32_t size) {
|
|
|
|
GCMemoryHeader *mdata;
|
2017-02-09 23:50:47 +00:00
|
|
|
if (rawBlock == NULL) {
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_crash(vm, OOM);
|
2017-02-09 23:50:47 +00:00
|
|
|
}
|
|
|
|
vm->nextCollection += size;
|
2017-02-16 02:02:00 +00:00
|
|
|
mdata = (GCMemoryHeader *)rawBlock;
|
2017-02-09 23:50:47 +00:00
|
|
|
mdata->next = vm->blocks;
|
|
|
|
vm->blocks = mdata;
|
|
|
|
mdata->color = !vm->black;
|
|
|
|
return rawBlock + sizeof(GCMemoryHeader);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate some memory that is tracked for garbage collection */
|
2017-02-16 02:02:00 +00:00
|
|
|
void *gst_alloc(Gst *vm, uint32_t size) {
|
2017-02-09 23:50:47 +00:00
|
|
|
uint32_t totalSize = size + sizeof(GCMemoryHeader);
|
2017-02-16 02:02:00 +00:00
|
|
|
return gst_alloc_prepare(vm, malloc(totalSize), totalSize);
|
2017-02-09 23:50:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate some zeroed memory that is tracked for garbage collection */
|
2017-02-16 02:02:00 +00:00
|
|
|
void *gst_zalloc(Gst *vm, uint32_t size) {
|
2017-02-12 15:27:18 +00:00
|
|
|
uint32_t totalSize = size + sizeof(GCMemoryHeader);
|
2017-02-16 02:02:00 +00:00
|
|
|
return gst_alloc_prepare(vm, calloc(1, totalSize), totalSize);
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Run garbage collection */
|
2017-02-16 02:02:00 +00:00
|
|
|
void gst_collect(Gst *vm) {
|
2017-02-10 04:28:11 +00:00
|
|
|
if (vm->lock > 0) return;
|
2017-02-12 20:16:55 +00:00
|
|
|
/* Thread can be null */
|
2017-02-13 04:45:52 +00:00
|
|
|
if (vm->thread) {
|
2017-02-16 02:02:00 +00:00
|
|
|
GstValue thread;
|
|
|
|
thread.type = GST_THREAD;
|
2017-02-15 01:45:34 +00:00
|
|
|
thread.data.thread = vm->thread;
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_mark(vm, &thread);
|
2017-02-13 04:45:52 +00:00
|
|
|
}
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_mark(vm, &vm->ret);
|
2017-02-22 23:19:46 +00:00
|
|
|
gst_mark(vm, &vm->error);
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_sweep(vm);
|
2017-02-09 23:50:47 +00:00
|
|
|
vm->nextCollection = 0;
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Run garbage collection if needed */
|
2017-02-16 02:02:00 +00:00
|
|
|
void gst_maybe_collect(Gst *vm) {
|
2017-02-12 20:16:55 +00:00
|
|
|
if (vm->nextCollection >= vm->memoryInterval)
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_collect(vm);
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Get an upvalue */
|
2017-02-16 02:02:00 +00:00
|
|
|
static GstValue *gst_vm_upvalue_location(Gst *vm, GstFunction *fn, uint16_t level, uint16_t index) {
|
|
|
|
GstFuncEnv *env;
|
|
|
|
GstValue *stack;
|
|
|
|
if (!level)
|
2017-02-12 20:16:55 +00:00
|
|
|
return vm->base + index;
|
2017-02-11 19:01:06 +00:00
|
|
|
while (fn && --level)
|
2017-02-09 20:02:59 +00:00
|
|
|
fn = fn->parent;
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_assert(vm, fn, NO_UPVALUE);
|
2017-02-09 20:02:59 +00:00
|
|
|
env = fn->env;
|
|
|
|
if (env->thread)
|
|
|
|
stack = env->thread->data + env->stackOffset;
|
|
|
|
else
|
|
|
|
stack = env->values;
|
|
|
|
return stack + index;
|
|
|
|
}
|
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
/* Get a literal */
|
|
|
|
static GstValue gst_vm_literal(Gst *vm, GstFunction *fn, uint16_t index) {
|
2017-02-09 20:02:59 +00:00
|
|
|
if (index > fn->def->literalsLen) {
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_error(vm, NO_UPVALUE);
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|
2017-02-12 15:27:18 +00:00
|
|
|
return fn->def->literals[index];
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|
|
|
|
|
2017-02-12 15:27:18 +00:00
|
|
|
/* Boolean truth definition */
|
2017-02-16 02:02:00 +00:00
|
|
|
static int truthy(GstValue v) {
|
|
|
|
return v.type != GST_NIL && !(v.type == GST_BOOLEAN && !v.data.boolean);
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Return from the vm */
|
2017-02-16 02:02:00 +00:00
|
|
|
static void gst_vm_return(Gst *vm, GstValue ret) {
|
|
|
|
thread_pop(vm);
|
2017-02-13 02:54:18 +00:00
|
|
|
if (vm->thread->count == 0) {
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_exit(vm, ret);
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|
2017-02-13 02:54:18 +00:00
|
|
|
vm->pc = vm->frame->pc;
|
|
|
|
vm->base[vm->frame->ret] = ret;
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Implementation of the opcode for function calls */
|
2017-02-16 02:02:00 +00:00
|
|
|
static void gst_vm_call(Gst *vm) {
|
|
|
|
GstThread *thread = vm->thread;
|
|
|
|
GstValue callee = vm->base[vm->pc[1]];
|
2017-02-12 15:27:18 +00:00
|
|
|
uint32_t arity = vm->pc[3];
|
|
|
|
uint32_t oldCount = thread->count;
|
2017-02-09 20:02:59 +00:00
|
|
|
uint32_t i;
|
2017-02-16 02:02:00 +00:00
|
|
|
GstValue *oldBase;
|
2017-02-13 02:54:18 +00:00
|
|
|
vm->frame->pc = vm->pc + 4 + arity;
|
|
|
|
vm->frame->ret = vm->pc[2];
|
2017-02-16 02:02:00 +00:00
|
|
|
if (callee.type == GST_FUNCTION) {
|
|
|
|
GstFunction *fn = callee.data.function;
|
|
|
|
thread_push(vm, thread, callee, fn->def->locals);
|
|
|
|
} else if (callee.type == GST_CFUNCTION) {
|
|
|
|
thread_push(vm, thread, callee, arity);
|
2017-02-12 15:27:18 +00:00
|
|
|
} else {
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_error(vm, EXPECTED_FUNCTION);
|
2017-02-12 15:27:18 +00:00
|
|
|
}
|
|
|
|
oldBase = thread->data + oldCount;
|
2017-02-16 02:02:00 +00:00
|
|
|
if (callee.type == GST_CFUNCTION) {
|
2017-02-09 20:02:59 +00:00
|
|
|
for (i = 0; i < arity; ++i)
|
2017-02-12 15:27:18 +00:00
|
|
|
vm->base[i] = oldBase[vm->pc[4 + i]];
|
2017-02-10 04:28:11 +00:00
|
|
|
++vm->lock;
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_vm_return(vm, callee.data.cfunction(vm));
|
2017-02-10 04:28:11 +00:00
|
|
|
--vm->lock;
|
2017-02-12 20:16:55 +00:00
|
|
|
} else {
|
2017-02-16 02:02:00 +00:00
|
|
|
GstFunction *f = callee.data.function;
|
2017-02-12 20:16:55 +00:00
|
|
|
uint32_t locals = f->def->locals;
|
|
|
|
for (i = 0; i < arity; ++i)
|
|
|
|
vm->base[i] = oldBase[vm->pc[4 + i]];
|
|
|
|
for (; i < locals; ++i)
|
2017-02-16 02:02:00 +00:00
|
|
|
vm->base[i].type = GST_NIL;
|
2017-02-09 20:02:59 +00:00
|
|
|
vm->pc = f->def->byteCode;
|
2017-02-12 20:16:55 +00:00
|
|
|
}
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Implementation of the opcode for tail calls */
|
2017-02-16 02:02:00 +00:00
|
|
|
static void gst_vm_tailcall(Gst *vm) {
|
|
|
|
GstThread *thread = vm->thread;
|
|
|
|
GstValue callee = vm->base[vm->pc[1]];
|
2017-02-12 15:27:18 +00:00
|
|
|
uint32_t arity = vm->pc[2];
|
|
|
|
uint16_t newFrameSize, currentFrameSize;
|
2017-02-09 20:02:59 +00:00
|
|
|
uint32_t i;
|
|
|
|
/* Check for closures */
|
2017-02-16 02:02:00 +00:00
|
|
|
thread_split_env(vm);
|
|
|
|
if (callee.type == GST_CFUNCTION) {
|
2017-02-12 20:16:55 +00:00
|
|
|
newFrameSize = arity;
|
2017-02-16 02:02:00 +00:00
|
|
|
} else if (callee.type == GST_FUNCTION) {
|
|
|
|
GstFunction * f = callee.data.function;
|
2017-02-12 20:16:55 +00:00
|
|
|
newFrameSize = f->def->locals;
|
|
|
|
} else {
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_error(vm, EXPECTED_FUNCTION);
|
2017-02-12 20:16:55 +00:00
|
|
|
}
|
|
|
|
/* Ensure stack has enough space for copies of arguments */
|
2017-02-13 02:54:18 +00:00
|
|
|
currentFrameSize = vm->frame->size;
|
2017-02-16 02:02:00 +00:00
|
|
|
thread_ensure(vm, thread, thread->count + currentFrameSize + arity);
|
2017-02-12 15:27:18 +00:00
|
|
|
vm->base = thread->data + thread->count;
|
|
|
|
/* Copy the arguments into the extra space */
|
2017-02-16 02:02:00 +00:00
|
|
|
for (i = 0; i < arity; ++i)
|
2017-02-12 20:16:55 +00:00
|
|
|
vm->base[currentFrameSize + i] = vm->base[vm->pc[3 + i]];
|
2017-02-09 20:02:59 +00:00
|
|
|
/* Copy the end of the stack to the parameter position */
|
2017-02-16 02:02:00 +00:00
|
|
|
memcpy(vm->base, vm->base + currentFrameSize, arity * sizeof(GstValue));
|
2017-02-12 20:16:55 +00:00
|
|
|
/* nil the non argument part of the stack for gc */
|
2017-02-16 02:02:00 +00:00
|
|
|
for (i = arity; i < newFrameSize; ++i)
|
|
|
|
vm->base[i].type = GST_NIL;
|
2017-02-12 15:27:18 +00:00
|
|
|
/* Update the stack frame */
|
2017-02-13 02:54:18 +00:00
|
|
|
vm->frame->size = newFrameSize;
|
|
|
|
vm->frame->callee = callee;
|
|
|
|
vm->frame->env = NULL;
|
2017-02-16 02:02:00 +00:00
|
|
|
if (callee.type == GST_CFUNCTION) {
|
2017-02-10 04:28:11 +00:00
|
|
|
++vm->lock;
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_vm_return(vm, callee.data.cfunction(vm));
|
2017-02-10 04:28:11 +00:00
|
|
|
--vm->lock;
|
2017-02-12 20:16:55 +00:00
|
|
|
} else {
|
2017-02-16 02:02:00 +00:00
|
|
|
GstFunction *f = callee.data.function;
|
2017-02-09 20:02:59 +00:00
|
|
|
vm->pc = f->def->byteCode;
|
2017-02-12 20:16:55 +00:00
|
|
|
}
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Instantiate a closure */
|
2017-02-16 02:02:00 +00:00
|
|
|
static GstValue gst_vm_closure(Gst *vm, uint16_t literal) {
|
|
|
|
GstThread *thread = vm->thread;
|
|
|
|
if (vm->frame->callee.type != GST_FUNCTION) {
|
|
|
|
gst_error(vm, EXPECTED_FUNCTION);
|
2017-02-09 20:02:59 +00:00
|
|
|
} else {
|
2017-02-16 02:02:00 +00:00
|
|
|
GstValue constant, ret;
|
|
|
|
GstFunction *fn, *current;
|
|
|
|
GstFuncEnv *env = vm->frame->env;
|
2017-02-09 20:02:59 +00:00
|
|
|
if (!env) {
|
2017-02-16 02:02:00 +00:00
|
|
|
env = gst_alloc(vm, sizeof(GstFuncEnv));
|
2017-02-09 20:02:59 +00:00
|
|
|
env->thread = thread;
|
|
|
|
env->stackOffset = thread->count;
|
|
|
|
env->values = NULL;
|
2017-02-13 02:54:18 +00:00
|
|
|
vm->frame->env = env;
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|
2017-02-16 02:02:00 +00:00
|
|
|
current = vm->frame->callee.data.function;
|
|
|
|
constant = gst_vm_literal(vm, current, literal);
|
|
|
|
if (constant.type != GST_NIL) {
|
2017-02-19 16:19:39 +00:00
|
|
|
gst_error(vm, "cannot create closure");
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|
2017-02-16 02:02:00 +00:00
|
|
|
fn = gst_alloc(vm, sizeof(GstFunction));
|
|
|
|
fn->def = (GstFuncDef *) constant.data.pointer;
|
2017-02-09 20:02:59 +00:00
|
|
|
fn->parent = current;
|
|
|
|
fn->env = env;
|
2017-02-16 02:02:00 +00:00
|
|
|
ret.type = GST_FUNCTION;
|
|
|
|
ret.data.function = fn;
|
2017-02-12 15:27:18 +00:00
|
|
|
return ret;
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Start running the VM */
|
2017-02-16 02:02:00 +00:00
|
|
|
int gst_start(Gst *vm) {
|
2017-02-09 20:02:59 +00:00
|
|
|
|
|
|
|
/* Set jmp_buf to jump back to for return. */
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
if ((n = setjmp(vm->jump))) {
|
|
|
|
/* Good return */
|
|
|
|
if (n == 1) {
|
2017-02-19 16:19:39 +00:00
|
|
|
vm->lock = 0;
|
2017-02-09 20:02:59 +00:00
|
|
|
return 0;
|
2017-02-19 16:19:39 +00:00
|
|
|
} else if (n == 2) {
|
|
|
|
/* Error. Handling TODO. */
|
2017-02-22 23:19:46 +00:00
|
|
|
while (vm->thread->count && !vm->frame->errorJump) {
|
2017-02-19 16:19:39 +00:00
|
|
|
thread_pop(vm);
|
2017-02-22 23:19:46 +00:00
|
|
|
}
|
|
|
|
if (vm->thread->count == 0)
|
|
|
|
return n;
|
2017-02-19 16:19:39 +00:00
|
|
|
/* Jump to the error location */
|
|
|
|
vm->pc = vm->frame->errorJump;
|
2017-02-22 23:19:46 +00:00
|
|
|
/* Set error */
|
|
|
|
vm->base[vm->frame->errorSlot] = vm->error;
|
2017-02-19 16:19:39 +00:00
|
|
|
vm->lock = 0;
|
2017-02-09 20:02:59 +00:00
|
|
|
} else {
|
2017-02-19 16:19:39 +00:00
|
|
|
/* Crash. just return */
|
|
|
|
vm->lock = 0;
|
2017-02-09 20:02:59 +00:00
|
|
|
return n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (;;) {
|
2017-02-16 02:02:00 +00:00
|
|
|
GstValue temp, v1, v2;
|
2017-02-09 20:02:59 +00:00
|
|
|
uint16_t opcode = *vm->pc;
|
|
|
|
|
|
|
|
switch (opcode) {
|
2017-02-12 15:27:18 +00:00
|
|
|
|
2017-02-13 05:11:30 +00:00
|
|
|
#define DO_BINARY_MATH(op) \
|
|
|
|
v1 = vm->base[vm->pc[2]]; \
|
|
|
|
v2 = vm->base[vm->pc[3]]; \
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_assert(vm, v1.type == GST_NUMBER, VMS_EXPECTED_NUMBER_LOP); \
|
|
|
|
gst_assert(vm, v2.type == GST_NUMBER, VMS_EXPECTED_NUMBER_ROP); \
|
|
|
|
temp.type = GST_NUMBER; \
|
2017-02-13 05:11:30 +00:00
|
|
|
temp.data.number = v1.data.number op v2.data.number; \
|
|
|
|
vm->base[vm->pc[1]] = temp; \
|
|
|
|
vm->pc += 4; \
|
|
|
|
break;
|
2017-02-12 15:27:18 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_ADD: /* Addition */
|
2017-02-13 05:11:30 +00:00
|
|
|
DO_BINARY_MATH(+)
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_SUB: /* Subtraction */
|
2017-02-13 05:11:30 +00:00
|
|
|
DO_BINARY_MATH(-)
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_MUL: /* Multiplication */
|
2017-02-13 05:11:30 +00:00
|
|
|
DO_BINARY_MATH(*)
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_DIV: /* Division */
|
2017-02-13 05:11:30 +00:00
|
|
|
DO_BINARY_MATH(/)
|
2017-02-12 15:27:18 +00:00
|
|
|
|
2017-02-13 05:11:30 +00:00
|
|
|
#undef DO_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-13 05:11:30 +00:00
|
|
|
temp.data.boolean = !truthy(vm->base[vm->pc[2]]);
|
|
|
|
vm->base[vm->pc[1]] = temp;
|
|
|
|
vm->pc += 3;
|
|
|
|
break;
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_LD0: /* Load 0 */
|
|
|
|
temp.type = GST_NUMBER;
|
2017-02-13 05:11:30 +00:00
|
|
|
temp.data.number = 0;
|
|
|
|
vm->base[vm->pc[1]] = temp;
|
|
|
|
vm->pc += 2;
|
|
|
|
break;
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_LD1: /* Load 1 */
|
|
|
|
temp.type = GST_NUMBER;
|
2017-02-13 05:11:30 +00:00
|
|
|
temp.data.number = 1;
|
|
|
|
vm->base[vm->pc[1]] = temp;
|
|
|
|
vm->pc += 2;
|
|
|
|
break;
|
2017-02-09 20:02:59 +00:00
|
|
|
|
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;
|
|
|
|
vm->base[vm->pc[1]] = temp;
|
|
|
|
vm->pc += 2;
|
|
|
|
break;
|
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;
|
|
|
|
vm->base[vm->pc[1]] = temp;
|
|
|
|
vm->pc += 2;
|
|
|
|
break;
|
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-13 05:11:30 +00:00
|
|
|
vm->base[vm->pc[1]] = temp;
|
|
|
|
vm->pc += 2;
|
|
|
|
break;
|
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-13 05:11:30 +00:00
|
|
|
temp.data.number = ((int16_t *)(vm->pc))[2];
|
|
|
|
vm->base[vm->pc[1]] = temp;
|
|
|
|
vm->pc += 3;
|
|
|
|
break;
|
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-13 05:11:30 +00:00
|
|
|
temp = vm->frame->callee;
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_assert(vm, temp.type == GST_FUNCTION, EXPECTED_FUNCTION);
|
|
|
|
vm->base[vm->pc[1]] = *gst_vm_upvalue_location(vm, temp.data.function, vm->pc[2], vm->pc[3]);
|
2017-02-13 05:11:30 +00:00
|
|
|
vm->pc += 4;
|
|
|
|
break;
|
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_JIF: /* Jump If */
|
2017-02-13 05:11:30 +00:00
|
|
|
if (truthy(vm->base[vm->pc[1]])) {
|
2017-02-12 15:27:18 +00:00
|
|
|
vm->pc += 4;
|
2017-02-13 05:11:30 +00:00
|
|
|
} else {
|
|
|
|
vm->pc += *((int32_t *)(vm->pc + 2));
|
|
|
|
}
|
|
|
|
break;
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_JMP: /* Jump */
|
2017-02-13 05:11:30 +00:00
|
|
|
vm->pc += *((int32_t *)(vm->pc + 1));
|
|
|
|
break;
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_CAL: /* Call */
|
|
|
|
gst_vm_call(vm);
|
2017-02-13 05:11:30 +00:00
|
|
|
break;
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_RET: /* Return */
|
|
|
|
gst_vm_return(vm, vm->base[vm->pc[1]]);
|
2017-02-13 05:11:30 +00:00
|
|
|
break;
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_SUV: /* Set Up Value */
|
2017-02-13 05:11:30 +00:00
|
|
|
temp = vm->frame->callee;
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_assert(vm, temp.type == GST_FUNCTION, EXPECTED_FUNCTION);
|
|
|
|
*gst_vm_upvalue_location(vm, temp.data.function, vm->pc[2], vm->pc[3]) = vm->base[vm->pc[1]];
|
2017-02-13 05:11:30 +00:00
|
|
|
vm->pc += 4;
|
|
|
|
break;
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_CST: /* Load constant value */
|
2017-02-13 05:11:30 +00:00
|
|
|
temp = vm->frame->callee;
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_assert(vm, temp.type == GST_FUNCTION, EXPECTED_FUNCTION);
|
|
|
|
vm->base[vm->pc[1]] = gst_vm_literal(vm, temp.data.function, vm->pc[2]);
|
2017-02-13 05:11:30 +00:00
|
|
|
vm->pc += 3;
|
|
|
|
break;
|
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-13 05:11:30 +00:00
|
|
|
temp.data.number = *((int32_t *)(vm->pc + 2));
|
|
|
|
vm->base[vm->pc[1]] = temp;
|
|
|
|
vm->pc += 4;
|
|
|
|
break;
|
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;
|
|
|
|
temp.data.number = (GstNumber) *((double *)(vm->pc + 2));
|
2017-02-13 05:11:30 +00:00
|
|
|
vm->base[vm->pc[1]] = temp;
|
|
|
|
vm->pc += 6;
|
|
|
|
break;
|
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_MOV: /* Move Values */
|
2017-02-13 05:11:30 +00:00
|
|
|
vm->base[vm->pc[1]] = vm->base[vm->pc[2]];
|
|
|
|
vm->pc += 3;
|
|
|
|
break;
|
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_CLN: /* Create closure from constant FuncDef */
|
|
|
|
vm->base[vm->pc[1]] = gst_vm_closure(vm, vm->pc[2]);
|
2017-02-13 05:11:30 +00:00
|
|
|
vm->pc += 3;
|
|
|
|
break;
|
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_EQL: /* Equality */
|
|
|
|
temp.type = GST_BOOLEAN;
|
|
|
|
temp.data.boolean = gst_equals(vm->base[vm->pc[2]], vm->base[vm->pc[3]]);
|
2017-02-13 05:11:30 +00:00
|
|
|
vm->base[vm->pc[1]] = temp;
|
|
|
|
vm->pc += 4;
|
|
|
|
break;
|
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_LTN: /* Less Than */
|
|
|
|
temp.type = GST_BOOLEAN;
|
|
|
|
temp.data.boolean = (gst_compare(vm->base[vm->pc[2]], vm->base[vm->pc[3]]) == -1);
|
2017-02-13 05:11:30 +00:00
|
|
|
vm->base[vm->pc[1]] = temp;
|
|
|
|
vm->pc += 4;
|
|
|
|
break;
|
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_LTE: /* Less Than or Equal to */
|
|
|
|
temp.type = GST_BOOLEAN;
|
|
|
|
temp.data.boolean = (gst_compare(vm->base[vm->pc[2]], vm->base[vm->pc[3]]) != 1);
|
2017-02-13 05:11:30 +00:00
|
|
|
vm->base[vm->pc[1]] = temp;
|
|
|
|
vm->pc += 4;
|
|
|
|
break;
|
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;
|
|
|
|
uint32_t arrayLen = vm->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)
|
|
|
|
array->data[i] = vm->base[vm->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-12 15:27:18 +00:00
|
|
|
vm->base[vm->pc[1]] = temp;
|
2017-02-13 05:11:30 +00:00
|
|
|
vm->pc += 3 + arrayLen;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
uint32_t kvs = vm->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) {
|
|
|
|
v1 = vm->base[vm->pc[i++]];
|
|
|
|
v2 = vm->base[vm->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-13 05:11:30 +00:00
|
|
|
vm->base[vm->pc[1]] = temp;
|
|
|
|
vm->pc += kvs;
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|
2017-02-13 05:11:30 +00:00
|
|
|
break;
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_TCL: /* Tail call */
|
|
|
|
gst_vm_tailcall(vm);
|
2017-02-13 05:11:30 +00:00
|
|
|
break;
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-13 05:11:30 +00:00
|
|
|
/* Macro for generating some math operators */
|
|
|
|
#define DO_MULTI_MATH(op, start) { \
|
|
|
|
uint16_t count = vm->pc[2]; \
|
|
|
|
uint16_t i; \
|
2017-02-16 02:02:00 +00:00
|
|
|
GstNumber accum = start; \
|
2017-02-13 05:11:30 +00:00
|
|
|
for (i = 0; i < count; ++i) { \
|
|
|
|
v1 = vm->base[vm->pc[3 + i]]; \
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_assert(vm, v1.type == GST_NUMBER, "Expected number"); \
|
2017-02-13 05:11:30 +00:00
|
|
|
accum = accum op v1.data.number; \
|
|
|
|
} \
|
2017-02-16 02:02:00 +00:00
|
|
|
temp.type = GST_NUMBER; \
|
2017-02-13 05:11:30 +00:00
|
|
|
temp.data.number = accum; \
|
|
|
|
vm->base[vm->pc[1]] = temp; \
|
|
|
|
vm->pc += 3 + count; \
|
|
|
|
break; \
|
|
|
|
}
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-13 05:11:30 +00:00
|
|
|
/* Vectorized math */
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_ADM:
|
2017-02-13 05:11:30 +00:00
|
|
|
DO_MULTI_MATH(+, 0)
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_SBM:
|
2017-02-13 05:11:30 +00:00
|
|
|
DO_MULTI_MATH(-, 0)
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_MUM:
|
2017-02-13 05:11:30 +00:00
|
|
|
DO_MULTI_MATH(*, 1)
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_DVM:
|
2017-02-13 05:11:30 +00:00
|
|
|
DO_MULTI_MATH(/, 1)
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-13 05:11:30 +00:00
|
|
|
#undef DO_MULTI_MATH
|
2017-02-12 20:53:52 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_RTN: /* Return nil */
|
|
|
|
temp.type = GST_NIL;
|
|
|
|
gst_vm_return(vm, temp);
|
2017-02-13 05:11:30 +00:00
|
|
|
break;
|
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_GET:
|
|
|
|
temp = gst_get(vm, vm->base[vm->pc[2]], vm->base[vm->pc[3]]);
|
2017-02-13 05:11:30 +00:00
|
|
|
vm->base[vm->pc[1]] = temp;
|
|
|
|
vm->pc += 4;
|
|
|
|
break;
|
2017-02-12 20:53:52 +00:00
|
|
|
|
2017-02-16 02:02:00 +00:00
|
|
|
case GST_OP_SET:
|
|
|
|
gst_set(vm, vm->base[vm->pc[1]], vm->base[vm->pc[2]], vm->base[vm->pc[3]]);
|
2017-02-13 05:11:30 +00:00
|
|
|
vm->pc += 4;
|
|
|
|
break;
|
|
|
|
|
2017-02-22 23:19:46 +00:00
|
|
|
case GST_OP_ERR:
|
|
|
|
vm->error = vm->base[vm->pc[1]];
|
|
|
|
longjmp(vm->jump, 2);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GST_OP_TRY:
|
|
|
|
vm->frame->errorSlot = vm->pc[1];
|
|
|
|
vm->frame->errorJump = vm->pc + *(uint32_t *)(vm->pc + 2);
|
|
|
|
vm->pc += 4;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GST_OP_UTY:
|
|
|
|
vm->frame->errorJump = NULL;
|
|
|
|
vm->pc++;
|
|
|
|
break;
|
|
|
|
|
2017-02-13 05:11:30 +00:00
|
|
|
default:
|
2017-02-19 16:19:39 +00:00
|
|
|
gst_error(vm, "unknown opcode");
|
2017-02-13 05:11:30 +00:00
|
|
|
break;
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|
2017-02-13 05:11:30 +00:00
|
|
|
|
|
|
|
/* Move collection only to places that allocate memory */
|
|
|
|
/* This, however, is good for testing */
|
2017-02-16 02:02:00 +00:00
|
|
|
gst_maybe_collect(vm);
|
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-02-13 02:54:18 +00:00
|
|
|
uint16_t frameSize = vm->frame->size;
|
2017-02-19 16:19:39 +00:00
|
|
|
gst_assert(vm, frameSize > index, "cannot get arg out of stack bounds");
|
2017-02-12 20:16:55 +00:00
|
|
|
return vm->base[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-02-13 02:54:18 +00:00
|
|
|
uint16_t frameSize = vm->frame->size;
|
2017-02-19 16:19:39 +00:00
|
|
|
gst_assert(vm, frameSize > index, "cannot set arg out of stack bounds");
|
2017-02-12 20:16:55 +00:00
|
|
|
vm->base[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-02-13 02:54:18 +00:00
|
|
|
return vm->frame->size;
|
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->error.type = GST_NIL;
|
2017-02-09 20:02:59 +00:00
|
|
|
vm->base = NULL;
|
2017-02-13 02:54:18 +00:00
|
|
|
vm->frame = NULL;
|
2017-02-09 20:02:59 +00:00
|
|
|
vm->pc = NULL;
|
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-12 20:16:55 +00:00
|
|
|
vm->memoryInterval = 0;
|
2017-02-09 23:50:47 +00:00
|
|
|
vm->black = 0;
|
2017-02-10 04:28:11 +00:00
|
|
|
vm->lock = 0;
|
2017-02-13 02:54:18 +00:00
|
|
|
/* Add thread */
|
2017-02-13 03:25:17 +00:00
|
|
|
vm->thread = NULL;
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Load a function into the VM. The function will be called with
|
|
|
|
* no arguments when run */
|
2017-02-16 02:02:00 +00:00
|
|
|
void gst_load(Gst *vm, GstValue callee) {
|
2017-02-15 01:45:34 +00:00
|
|
|
uint32_t startCapacity = 100;
|
2017-02-16 02:02:00 +00:00
|
|
|
GstThread *thread = gst_alloc(vm, sizeof(GstThread));
|
|
|
|
thread->data = gst_alloc(vm, sizeof(GstValue) * startCapacity);
|
2017-02-15 01:45:34 +00:00
|
|
|
thread->capacity = startCapacity;
|
|
|
|
thread->count = 0;
|
2017-02-12 20:16:55 +00:00
|
|
|
vm->thread = thread;
|
2017-02-16 02:02:00 +00:00
|
|
|
if (callee.type == GST_FUNCTION) {
|
|
|
|
GstFunction *fn = callee.data.function;
|
|
|
|
thread_push(vm, thread, callee, fn->def->locals);
|
2017-02-12 20:16:55 +00:00
|
|
|
vm->pc = fn->def->byteCode;
|
2017-02-16 02:02:00 +00:00
|
|
|
} else if (callee.type == GST_CFUNCTION) {
|
|
|
|
thread_push(vm, thread, callee, 0);
|
2017-02-12 20:16:55 +00:00
|
|
|
vm->pc = NULL;
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
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) {
|
|
|
|
GCMemoryHeader *current = vm->blocks;
|
2017-02-09 23:50:47 +00:00
|
|
|
while (current) {
|
2017-02-16 02:02:00 +00:00
|
|
|
GCMemoryHeader *next = current->next;
|
2017-02-09 23:50:47 +00:00
|
|
|
free(current);
|
|
|
|
current = next;
|
|
|
|
}
|
|
|
|
vm->blocks = NULL;
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|