diff --git a/.gitignore b/.gitignore index b650576f..4c8ee751 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,9 @@ dst # Generated files *.gen.h +# Generate test files +*.out + # Tools xxd diff --git a/Makefile b/Makefile index 94779499..0550f778 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,22 @@ -# DST +# Copyright (c) 2017 Calvin Rose +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. ################################ ##### Set global variables ##### @@ -12,22 +30,25 @@ CFLAGS=-std=c99 -Wall -Wextra -I./include -I./libs -g -DDST_VERSION=$(VERSION) PREFIX=/usr/local DST_TARGET=dst DST_XXD=xxd -# Use gdb. On mac use lldb +# Use gdb. On osx use lldb DEBUGGER=lldb -DST_INTERNAL_HEADERS=$(addprefix core/, internal.h bootstrap.h cache.h) -DST_HEADERS=$(addprefix include/dst/, dst.h) +DST_INTERNAL_HEADERS=$(addprefix core/,cache.h opcodes.h) +DST_HEADERS=$(addprefix include/dst/,dst.h) ############################# ##### Generated headers ##### ############################# + DST_LANG_SOURCES=$(addprefix libs/, bootstrap.dst) DST_LANG_HEADERS=$(patsubst %.dst,%.gen.h,$(DST_LANG_SOURCES)) +DST_ALL_HEADERS=$(DST_HEADERS) $(DST_INTERNAL_HEADERS) $(DST_LANG_HEADERS) all: $(DST_TARGET) ####################### ##### Build tools ##### ####################### + $(DST_XXD): libs/xxd.c $(CC) -o $(DST_XXD) libs/xxd.c @@ -37,19 +58,39 @@ $(DST_XXD): libs/xxd.c ################################### ##### The core vm and runtime ##### ################################### + DST_CORE_SOURCES=$(addprefix core/,\ - util.c wrap.c buffer.c array.c table.c userdata.c func.c\ - value.c vm.c ds.c gc.c thread.c serialize.c tuple.c\ - string.c bootstrap_parse.c client.c cache.c struct.c) + array.c asm.c buffer.c cache.c fiber.c func.c gc.c parse.c string.c\ + struct.c syscalls.c table.c tuple.c userdata.c util.c\ + value.c vm.c wrap.c) DST_CORE_OBJECTS=$(patsubst %.c,%.o,$(DST_CORE_SOURCES)) -$(DST_TARGET): $(DST_CORE_OBJECTS) $(DST_LANG_HEADERS) +$(DST_TARGET): $(DST_CORE_OBJECTS) $(CC) $(CFLAGS) -o $(DST_TARGET) $(DST_CORE_OBJECTS) # Compile all .c to .o -%.o: %.c $(DST_HEADERS) $(DST_INTERNAL_HEADERS) $(DST_LANG_HEADERS) +%.o: %.c $(DST_ALL_HEADERS) $(CC) $(CFLAGS) -o $@ -c $< +###################### +##### Unit Tests ##### +###################### + +CCU_FLAGS = $(CFLAGS) -DDST_UNIT_TEST + +DST_UNIT_BINARIES=$(addprefix unittests/,\ + array_test.out) + +%.out: %.c $(DST_CORE_OBJECTS) $(DST_ALL_HEADERS) unittests/unit.h + $(CC) $(CCU_FLAGS) $(DST_CORE_OBJECTS) $< -o $@ + +unit: $(DST_UNIT_BINARIES) + unittests/array_test.out + +################### +##### Testing ##### +################### + run: $(DST_TARGET) @ ./$(DST_TARGET) @@ -59,19 +100,24 @@ debug: $(DST_TARGET) valgrind: $(DST_TARGET) @ valgrind --leak-check=full -v ./$(DST_TARGET) -clean: - rm $(DST_TARGET) || true - rm $(DST_CORE_OBJECTS) || true - rm $(DST_LANG_HEADERS) || true - rm vgcore.* || true - rm $(DST_XXD) || true - test: $(DST_TARGET) @ ./$(DST_TARGET) dsttests/basic.dst valtest: $(DST_TARGET) valgrind --leak-check=full -v ./$(DST_TARGET) dsttests/basic.dst +################# +##### Other ##### +################# + +clean: + rm $(DST_TARGET) || true + rm $(DST_CORE_OBJECTS) || true + rm $(DST_LANG_HEADERS) || true + rm vgcore.* || true + rm unittests/*.out || true + rm $(DST_XXD) || true + install: $(DST_TARGET) cp $(DST_TARGET) $(BINDIR)/dst diff --git a/core/array.c b/core/array.c index c0b386df..8672013c 100644 --- a/core/array.c +++ b/core/array.c @@ -20,14 +20,12 @@ * IN THE SOFTWARE. */ -#include "internal.h" #include -/* Iniializes an array. Assumes the pointer to the array is already on the stack. */ -DstArray *dst_array(Dst *vm, uint32_t capacity) { - DstArray *array = dst_alloc(vm, DST_MEMORY_ARRAY, sizeof(DstArray)); +/* Iniializes an array */ +DstArray *dst_array_init(DstArray *array, uint32_t capacity) { DstValue *data = (DstValue *) malloc(sizeof(DstValue) * capacity); - if (NULL == array || NULL == data) { + if (NULL == data) { DST_OUT_OF_MEMORY; } array->count = 0; @@ -36,8 +34,18 @@ DstArray *dst_array(Dst *vm, uint32_t capacity) { return array; } +void dst_array_deinit(DstArray *array) { + free(array->data); +} + +/* Creates a new array */ +DstArray *dst_array(uint32_t capacity) { + DstArray *array = dst_alloc(DST_MEMORY_ARRAY, sizeof(DstArray)); + return dst_array_init(array, capacity); +} + /* Ensure the array has enough capacity for elements */ -void dst_array_ensure(Dst *vm, DstArray *array, uint32_t capacity) { +void dst_array_ensure(DstArray *array, uint32_t capacity) { DstValue *newData; DstValue *old = array->data; if (capacity <= array->capacity) return; @@ -50,10 +58,10 @@ void dst_array_ensure(Dst *vm, DstArray *array, uint32_t capacity) { } /* Set the count of an array. Extend with nil if needed. */ -void dst_array_setcount(Dst *vm, DstArray *array, uint32_t count) { +void dst_array_setcount(DstArray *array, uint32_t count) { if (count > array->count) { uint32_t i; - dst_array_ensure(vm, array, count + 1); + dst_array_ensure(array, count + 1); for (i = array->count; i < count; ++i) array->data[i].type = DST_NIL; } @@ -61,10 +69,10 @@ void dst_array_setcount(Dst *vm, DstArray *array, uint32_t count) { } /* Push a value to the top of the array */ -void dst_array_push(Dst *vm, DstArray *array, DstValue x) { +void dst_array_push(DstArray *array, DstValue x) { uint32_t newcount = array->count + 1; - if (newcount >= array->capacity) } - dst_array_ensure(vm, array, newcount * 2); + if (newcount >= array->capacity) { + dst_array_ensure(array, newcount * 2); } array->data[array->count] = x; array->count = newcount; diff --git a/core/asm.c b/core/asm.c index e69de29b..f80abc34 100644 --- a/core/asm.c +++ b/core/asm.c @@ -0,0 +1,617 @@ +/* +* Copyright (c) 2017 Calvin Rose +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to +* deal in the Software without restriction, including without limitation the +* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +* sell copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +* IN THE SOFTWARE. +*/ + +#include + +#include + +/* Bytecode op argument types */ + +/* s - a slot */ +/* c - a constant */ +/* i - a small integer */ +/* t - a type (have a simple type for non unions) */ +/* l - a label */ + +typedef enum DstOpArgType DstOpArgType; +enum DstOpArgType { + DST_OAT_SLOT, + DST_OAT_ENVIRONMENT, + DST_OAT_CONSTANT, + DST_OAT_INTEGER, + DST_OAT_TYPE, + DST_OAT_SIMPLETYPE, + DST_OAT_LABEL +}; + +/* Convert a slot to to an integer for bytecode */ + +/* Types of instructions */ +/* _0arg - op.---.--.-- (return-nil, noop, vararg arguments) + * _s - op.src.--.-- (push1) + * _l - op.XX.XX.XX (jump) + * _ss - op.dest.XX.XX (move, swap) + * _sl - op.check.XX.XX (jump-if) + * _st - op.check.TT.TT (typecheck) + * _si - op.dest.XX.XX (load-integer) + * _sss - op.dest.op1.op2 (add, subtract, arithmetic, comparison) + * _ses - op.dest.up.which (load-upvalue, save-upvalue) + * _sc - op.dest.CC.CC (load-constant, closure) + */ + +/* Various types of instructions */ +typedef enum DstInstructionType DstInstructionType; +enum DstInstructionType { + DIT_0, /* No args */ + DIT_S, /* One slot */ + DIT_L, /* One label */ + DIT_SS, /* Two slots */ + DIT_SL, + DIT_ST, + DIT_SI, + DIT_SU, /* Unsigned */ + DIT_SSS, + DIT_SES, + DIT_SC +}; + +/* Definition for an instruction in the assembler */ +typedef struct DstInstructionDef DstInstructionDef; +struct DstInstructionDef { + const char *name; + DstInstructionType type; + uint8_t opcode; +}; + +/* Hold all state needed during assembly */ +typedef struct DstAssembler DstAssembler; +struct DstAssembler { + DstAssembler *parent; + DstFuncDef *def; + jmp_buf on_error; + const char *errmessage; + + uint32_t environments_capacity; + uint32_t bytecode_count; /* Used for calculating labels */ + + DstTable labels; /* symbol -> bytecode index */ + DstTable constants; /* symbol -> constant index */ + DstTable slots; /* symbol -> slot index */ + DstTable envs; /* symbol -> environment index */ +}; + +/* Dst opcode descriptions in lexographic order. This + * allows a binary search over the elements to find the + * correct opcode given a name. This works in reasonable + * time and is easier to setup statically than a hash table or + * prefix tree. */ +static const DstInstructionDef dst_ops[] = { + {"add", DIT_SSS, 0x01}, + {"bitand", DIT_SSS, 0x02}, + {"bitor", DIT_SSS, 0x03}, + {"bitxor", DIT_SSS, 0x04}, + {"call", DIT_SS, 0x05}, + {"closure", DIT_SC, 0x06}, + {"divide", DIT_SSS, 0x07}, + {"jump", DIT_L, 0x08}, + {"jump-if", DIT_SL, 0x09}, + {"load-constant", DIT_SC, 0x0A}, + {"load-false", DIT_S, 0x0B}, + {"load-integer", DIT_SI, 0x0C}, + {"load-nil", DIT_S, 0x0D}, + {"load-true", DIT_S, 0x0E}, + {"load-upvalue", DIT_SES, 0x0F}, + {"move", DIT_SS, 0x10}, + {"modulo", DIT_SSS, 0x11}, + {"multiply", DIT_SSS, 0x12}, + {"noop", DIT_0, 0x00}, + {"push", DIT_S, 0x13}, + {"push2", DIT_SS, 0x14}, + {"push3", DIT_SSS, 0x15}, + {"push-array", DIT_S, 0x16}, + {"return", DIT_S, 0x19}, + {"return-nil", DIT_0, 0x1A}, + {"save-upvalue", DIT_SES, 0x1B}, + {"shift-left", DIT_SSS, 0x1C}, + {"shift-right", DIT_SSS, 0x1D}, + {"shift-right-signed", DIT_SSS, 0x1E}, + {"subtract", DIT_SSS, 0x1F}, + {"syscall", DIT_SU, 0x21}, + {"tailcall", DIT_S, 0x22}, + {"transfer", DIT_SSS, 0x23}, + {"typecheck", DIT_ST, 0x24}, +}; + +/* Compare a DST string to a native 0 terminated c string. Used in the + * binary search for the instruction definition. */ +static int dst_strcompare(const uint8_t *str, const char *other) { + uint32_t len = dst_string_length(str); + uint32_t index; + for (index = 0; index < len; index++) { + uint8_t c = str[index]; + uint8_t k = ((const uint8_t *)other)[index]; + if (c < k) return -1; + if (c > k) return 1; + if (k == '\0') break; + } + return (other[index] == '\0') ? 0 : -1; +} + +/* Find an instruction definition given its name */ +static const DstInstructionDef *dst_findi(const uint8_t *key) { + const DstInstructionDef *low = dst_ops; + const DstInstructionDef *hi = dst_ops + (sizeof(dst_ops) / sizeof(DstInstructionDef)); + while (low < hi) { + const DstInstructionDef *mid = low + ((hi - low) / 2); + int comp = dst_strcompare(key, mid->name); + if (comp < 0) { + hi = mid; + } else if (comp > 0) { + low = mid + 1; + } else { + return mid; + } + } + return NULL; +} + +/* Check a dst string against a bunch of test_strings. Return the + * index of the matching test_string, or -1 if not found. */ +static int strsearch(const uint8_t *str, const char **test_strings) { + uint32_t len = dst_string_length(str); + int index; + for (index = 0; ; index++) { + uint32_t i; + const char *testword = test_strings[index]; + if (NULL == testword) + break; + for (i = 0; i < len; i++) { + if (testword[i] != str[i]) + goto nextword; + } + return index; + nextword: + continue; + } + return -1; +} + +/* Deinitialize an Assembler. Does not deinitialize the parents. */ +static void dst_asm_deinit(DstAssembler *a) { + dst_table_deinit(&a->slots); + dst_table_deinit(&a->labels); + dst_table_deinit(&a->envs); + dst_table_deinit(&a->constants); +} + +/* Throw some kind of assembly error */ +static void dst_asm_error(DstAssembler *a, const char *message) { + a->errmessage = message; + longjmp(a->on_error, 1); +} +#define dst_asm_assert(a, c, m) do { if (!(c)) dst_asm_error((a), (m)); } while (0) + +/* Parse an argument to an assembly instruction, and return the result as an + * integer. This integer will need to be trimmed and bound checked. */ +static int64_t doarg_1(DstAssembler *a, DstOpArgType argtype, DstValue x) { + DstTable *c; + switch (argtype) { + case DST_OAT_SLOT: + c = &a->slots; + break; + case DST_OAT_ENVIRONMENT: + c = &a->envs; + break; + case DST_OAT_CONSTANT: + c = &a->constants; + break; + case DST_OAT_INTEGER: + c = NULL; + break; + case DST_OAT_TYPE: + case DST_OAT_SIMPLETYPE: + c = NULL; + break; + case DST_OAT_LABEL: + c = &a->labels; + break; + } + switch (x.type) { + default: + break; + case DST_INTEGER: + return x.as.integer; + case DST_TUPLE: + { + if (argtype == DST_OAT_TYPE) { + int64_t result = 0; + uint32_t i = 0; + for (i = 0; i < dst_tuple_length(x.as.tuple); i++) { + result |= doarg_1(a, DST_OAT_SIMPLETYPE, x.as.tuple[i]); + } + return result; + } + break; + } + case DST_SYMBOL: + { + if (NULL != c) { + DstValue result = dst_table_get(c, x); + if (result.type == DST_INTEGER) { + if (argtype == DST_OAT_LABEL) + return result.as.integer - a->bytecode_count; + return result.as.integer; + } else { + dst_asm_error(a, "unknown name"); + } + } else if (argtype == DST_OAT_TYPE || argtype == DST_OAT_SIMPLETYPE) { + int index = strsearch(x.as.string, dst_type_names); + if (index != -1) { + return (int64_t) index; + } else { + dst_asm_error(a, "unknown type"); + } + } + break; + } + } + dst_asm_error(a, "unexpected type parsing instruction argument"); + return 0; +} + +/* Trim a bytecode operand to 1, 2, or 3 bytes. Error out if + * the given argument doesn't fit in the required number of bytes. */ +static uint32_t doarg_2(DstAssembler *a, int nbytes, int hassign, int64_t arg) { + /* Calculate the min and max values that can be stored given + * nbytes, and whether or not the storage is signed */ + int64_t min = (-hassign) << ((nbytes << 3) - 1); + int64_t max = ~((-1) << ((nbytes << 3) - hassign)); + if (arg < min) + dst_asm_error(a, "instruction argument is too small"); + if (arg > max) + dst_asm_error(a, "instruction argument is too large"); + return (uint32_t) (arg & 0xFFFFFFFF); +} + +/* Parse a single argument to an instruction. Trims it as well as + * try to convert arguments to bit patterns */ +static uint32_t doarg(DstAssembler *a, DstOpArgType argtype, int nth, int nbytes, int hassign, DstValue x) { + int64_t arg1 = doarg_1(a, argtype, x); + return doarg_2(a, nbytes, hassign, arg1) << (nth << 3); +} + +/* Provide parsing methods for the different kinds of arguments */ +static uint32_t read_instruction(DstAssembler *a, const DstInstructionDef *idef, const DstValue *argt) { + uint32_t instr = idef->opcode; + switch (idef->type) { + case DIT_0: + { + if (dst_tuple_length(argt) != 1) + dst_asm_error(a, "expected 0 arguments: (op)"); + break; + } + case DIT_S: + { + if (dst_tuple_length(argt) != 2) + dst_asm_error(a, "expected 1 argument: (op, slot)"); + instr |= doarg(a, DST_OAT_SLOT, 3, 3, 0, argt[1]); + break; + } + case DIT_L: + { + if (dst_tuple_length(argt) != 2) + dst_asm_error(a, "expected 1 argument: (op, label)"); + instr |= doarg(a, DST_OAT_LABEL, 3, 3, 1, argt[1]); + break; + } + case DIT_SS: + { + if (dst_tuple_length(argt) != 3) + dst_asm_error(a, "expected 2 arguments: (op, slot, slot)"); + instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); + instr |= doarg(a, DST_OAT_SLOT, 3, 2, 0, argt[2]); + break; + } + case DIT_SL: + { + if (dst_tuple_length(argt) != 3) + dst_asm_error(a, "expected 2 arguments: (op, slot, label)"); + instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); + instr |= doarg(a, DST_OAT_LABEL, 3, 2, 1, argt[2]); + break; + } + case DIT_ST: + { + if (dst_tuple_length(argt) != 3) + dst_asm_error(a, "expected 2 arguments: (op, slot, type)"); + instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); + instr |= doarg(a, DST_OAT_TYPE, 3, 2, 0, argt[2]); + break; + } + case DIT_SI: + case DIT_SU: + { + if (dst_tuple_length(argt) != 3) + dst_asm_error(a, "expected 2 arguments: (op, slot, integer)"); + instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); + instr |= doarg(a, DST_OAT_INTEGER, 3, 2, idef->type == DIT_SI, argt[2]); + break; + } + case DIT_SSS: + { + if (dst_tuple_length(argt) != 4) + dst_asm_error(a, "expected 3 arguments: (op, slot, slot, slot)"); + instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); + instr |= doarg(a, DST_OAT_SLOT, 2, 1, 0, argt[2]); + instr |= doarg(a, DST_OAT_SLOT, 3, 1, 0, argt[3]); + break; + } + case DIT_SES: + { + DstAssembler *b = a; + uint32_t env; + if (dst_tuple_length(argt) != 4) + dst_asm_error(a, "expected 3 arguments: (op, slot, environment, envslot)"); + instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); + env = doarg(a, DST_OAT_ENVIRONMENT, 0, 1, 0, argt[2]); + instr |= env << 16; + for (env += 1; env > 0; env--) { + b = b->parent; + if (NULL == b) + dst_asm_error(a, "invalid environment index"); + } + instr |= doarg(b, DST_OAT_SLOT, 3, 1, 0, argt[3]); + break; + } + case DIT_SC: + { + if (dst_tuple_length(argt) != 3) + dst_asm_error(a, "expected 2 arguments: (op, slot, constant)"); + instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); + instr |= doarg(a, DST_OAT_CONSTANT, 3, 2, 0, argt[2]); + break; + } + } + return instr; +} + + +/* Add a closure environment to the assembler. Sub funcdefs may need + * to reference outer function environments, and may change the outer environment. + * Returns the index of the environment in the assembler's environments, or -1 + * if not found. */ +static int64_t dst_asm_addenv(DstAssembler *a, DstValue envname) { + DstValue check; + DstFuncDef *def = a->def; + uint32_t oldlen; + int64_t res; + /* Check for memoized value */ + check = dst_table_get(&a->envs, envname); + if (check.type != DST_NIL) { + return check.as.integer; + } + if (NULL == a->parent) { + return -1; + } + res = dst_asm_addenv(a->parent, envname); + if (res < 0) + return res; + oldlen = def->environments_length; + dst_table_put(&a->envs, envname, dst_wrap_integer(def->environments_length)); + if (oldlen >= a->environments_capacity) { + uint32_t newcap = 2 + 2 * oldlen; + def->environments = realloc(def->environments, newcap * sizeof(uint32_t)); + if (NULL == def->environments) { + DST_OUT_OF_MEMORY; + } + a->environments_capacity = newcap; + } + def->environments[def->environments_length++] = (uint32_t) res; + return (int64_t) oldlen; +} + +/* Helper to assembly. Return the assembled funcdef */ +static DstFuncDef *dst_asm1(DstAssembler *parent, DstValue src, const char **errout) { + DstAssembler a; + DstTable *t = src.as.table; + DstFuncDef *def; + uint32_t count, i; + const DstValue *arr; + DstValue x; + + /* Initialize funcdef */ + def = dst_alloc(DST_MEMORY_FUNCDEF, sizeof(DstFuncDef)); + if (NULL == def) { + DST_OUT_OF_MEMORY; + } + def->environments = NULL; + def->constants = NULL; + def->bytecode = NULL; + def->flags = 0; + def->slotcount = 0; + def->arity = 0; + def->constants_length = 0; + def->bytecode_length = 0; + def->environments_length = 0; + + /* Initialize Assembler */ + a.def = def; + a.parent = parent; + a.errmessage = NULL; + a.environments_capacity = 0; + a.bytecode_count = 0; + dst_table_init(&a.labels, 10); + dst_table_init(&a.constants, 10); + dst_table_init(&a.slots, 10); + dst_table_init(&a.envs, 10); + + /* Set error jump */ + if (setjmp(a.on_error)) { + dst_asm_deinit(&a); + if (NULL != a.parent) { + longjmp(a.parent->on_error, 1); + } + *errout = a.errmessage; + return NULL; + } + + dst_asm_assert(&a, src.type == DST_TABLE, "expected table for assembly"); + + /* Set function arity */ + x = dst_table_get(t, dst_wrap_symbol(dst_cstring("arity"))); + def->arity = x.type == DST_INTEGER ? x.as.integer : 0; + + /* Create slot aliases */ + x = dst_table_get(t, dst_wrap_symbol(dst_cstring("slots"))); + if (dst_seq_view(x, &arr, &count)) { + def->slotcount = count; + for (i = 0; i < count; i++) { + DstValue v = arr[i]; + if (v.type == DST_TUPLE) { + uint32_t j; + for (j = 0; j < dst_tuple_length(v.as.tuple); j++) { + if (v.as.tuple[j].type != DST_SYMBOL) + dst_asm_error(&a, "slot names must be symbols"); + dst_table_put(&a.slots, v.as.tuple[j], dst_wrap_integer(i)); + } + } else if (v.type == DST_SYMBOL) { + dst_table_put(&a.slots, v, dst_wrap_integer(i)); + } else { + dst_asm_error(&a, "slot names must be symbols or tuple of symbols"); + } + } + } else if (x.type == DST_INTEGER) { + def->slotcount = (uint32_t) x.as.integer; + } else { + dst_asm_error(&a, "slots must be specified"); + } + + + /* Create environment aliases */ + x = dst_table_get(t, dst_wrap_symbol(dst_cstring("environments"))); + if (dst_seq_view(x, &arr, &count)) { + for (i = 0; i < count; i++) { + dst_asm_assert(&a, arr[i].type == DST_SYMBOL, "environment must be a symbol"); + if (dst_asm_addenv(&a, arr[i]) < 0) { + dst_asm_error(&a, "environment not found"); + } + } + } + + /* Parse constants */ + x = dst_table_get(t, dst_wrap_symbol(dst_cstring("constants"))); + if (dst_seq_view(x, &arr, &count)) { + def->constants_length = count; + def->constants = malloc(sizeof(DstValue) * count); + if (NULL == def->constants) { + DST_OUT_OF_MEMORY; + } + for (i = 0; i < count; i++) { + DstValue ct = arr[i]; + if (ct.type == DST_TUPLE && + dst_tuple_length(ct.as.tuple) > 1 && + ct.as.tuple[0].type == DST_SYMBOL) { + uint32_t tcount = dst_tuple_length(ct.as.tuple); + const uint8_t *macro = ct.as.tuple[0].as.string; + if (0 == dst_strcompare(macro, "quote")) { + def->constants[i] = ct.as.tuple[1]; + } else if (tcount == 3 && + ct.as.tuple[1].type == DST_SYMBOL && + 0 == dst_strcompare(macro, "def")) { + def->constants[i] = ct.as.tuple[2]; + dst_table_put(&a.constants, ct.as.tuple[1], dst_wrap_integer(i)); + } else { + dst_asm_error(&a, "could not parse constant"); + } + /* Todo - parse nested funcdefs */ + } else { + def->constants[i] = ct; + } + } + } else { + def->constants = NULL; + def->constants_length = 0; + } + + /* Parse bytecode and labels */ + x = dst_table_get(t, dst_wrap_symbol(dst_cstring("bytecode"))); + if (dst_seq_view(x, &arr, &count)) { + /* Do labels and find length */ + uint32_t blength = 0; + for (i = 0; i < count; ++i) { + DstValue instr = arr[i]; + if (instr.type == DST_STRING) { + dst_table_put(&a.labels, instr, dst_wrap_integer(blength)); + } else if (instr.type == DST_TUPLE) { + blength++; + } else { + dst_asm_error(&a, "expected assembly instruction"); + } + } + /* Allocate bytecode array */ + def->bytecode_length = blength; + def->bytecode = malloc(sizeof(uint32_t) * blength); + if (NULL == def->bytecode) { + DST_OUT_OF_MEMORY; + } + /* Do bytecode */ + for (i = 0; i < count; ++i) { + DstValue instr = arr[i]; + if (instr.type == DST_STRING) { + continue; + } else { + uint32_t op; + const DstInstructionDef *idef; + dst_asm_assert(&a, instr.type == DST_TUPLE, "expected tuple"); + if (dst_tuple_length(instr.as.tuple) == 0) { + op = 0; + } else { + dst_asm_assert(&a, instr.as.tuple[0].type == DST_SYMBOL, + "expected symbol in assembly instruction"); + idef = dst_findi(instr.as.tuple[0].as.string); + dst_asm_assert(&a, NULL != idef, "unknown instruction"); + op = read_instruction(&a, idef, instr.as.tuple); + } + def->bytecode[a.bytecode_count++] = op; + } + } + } else { + dst_asm_error(&a, "bytecode expected"); + } + + /* Finish everything and return funcdef */ + dst_asm_deinit(&a); + def->environments = + realloc(def->environments, def->environments_length * sizeof(uint32_t)); + return def; +} + +/* Assembled a function definition. */ +int dst_asm(DstFuncDef **out, DstValue source) { + const char *err; + DstFuncDef *ret = dst_asm1(NULL, source, &err); + if (NULL == ret) { + return 1; + } + *out = ret; + return 0; +} diff --git a/core/buffer.c b/core/buffer.c index 7392bd29..208c4efe 100644 --- a/core/buffer.c +++ b/core/buffer.c @@ -20,12 +20,10 @@ * IN THE SOFTWARE. */ -#include "internal.h" #include /* Initialize a buffer */ -DstBuffer *dst_buffer(Dst *vm, uint32_t capacity) { - DstBuffer *buffer = dst_alloc(vm, DST_MEMORY_BUFFER, sizeof(DstBuffer)); +DstBuffer *dst_buffer_init(DstBuffer *buffer, uint32_t capacity) { uint8_t *data = malloc(sizeof(uint8_t) * capacity); if (NULL == data) { DST_OUT_OF_MEMORY; @@ -35,8 +33,19 @@ DstBuffer *dst_buffer(Dst *vm, uint32_t capacity) { return buffer; } +/* Deinitialize a buffer (free data memory) */ +void dst_buffer_deinit(DstBuffer *buffer) { + free(buffer->data); +} + +/* Initialize a buffer */ +DstBuffer *dst_buffer(uint32_t capacity) { + DstBuffer *buffer = dst_alloc(DST_MEMORY_BUFFER, sizeof(DstBuffer)); + return dst_buffer_init(buffer, capacity); +} + /* Ensure that the buffer has enough internal capacity */ -void dst_buffer_ensure(Dst *vm, DstBuffer *buffer, uint32_t capacity) { +void dst_buffer_ensure(DstBuffer *buffer, uint32_t capacity) { uint8_t *newData; uint8_t *old = buffer->data; if (capacity <= buffer->capacity) return; @@ -50,7 +59,7 @@ void dst_buffer_ensure(Dst *vm, DstBuffer *buffer, uint32_t capacity) { /* Adds capacity for enough extra bytes to the buffer. Ensures that the * next n bytes pushed to the buffer will not cause a reallocation */ -void dst_buffer_extra(Dst *vm, DstBuffer *buffer, uint32_t n) { +void dst_buffer_extra(DstBuffer *buffer, uint32_t n) { uint32_t newCount = buffer->count + n; if (newCount > buffer->capacity) { uint32_t newCapacity = newCount * 2; @@ -64,37 +73,37 @@ void dst_buffer_extra(Dst *vm, DstBuffer *buffer, uint32_t n) { } /* Push multiple bytes into the buffer */ -void dst_buffer_push_bytes(Dst *vm, DstBuffer *buffer, const uint8_t *string, uint32_t length) { +void dst_buffer_push_bytes(DstBuffer *buffer, const uint8_t *string, uint32_t length) { uint32_t newSize = buffer->count + length; if (newSize > buffer->capacity) { - dst_buffer_ensure(vm, buffer, 2 * newSize); + dst_buffer_ensure(buffer, 2 * newSize); } - dst_memcpy(buffer->data + buffer->count, string, length); + memcpy(buffer->data + buffer->count, string, length); buffer->count = newSize; } /* Push a cstring to buffer */ -void dst_buffer_push_cstring(Dst *vm, DstBuffer *buffer, const char *cstring) { +void dst_buffer_push_cstring(DstBuffer *buffer, const char *cstring) { uint32_t len = 0; while (cstring[len]) ++len; - dst_buffer_push_bytes(vm, buffer, (const uint8_t *) cstring, len); + dst_buffer_push_bytes(buffer, (const uint8_t *) cstring, len); } /* Push a single byte to the buffer */ -void dst_buffer_push_u8(Dst *vm, DstBuffer *buffer, uint8_t byte) { +void dst_buffer_push_u8(DstBuffer *buffer, uint8_t byte) { uint32_t newSize = buffer->count + 1; if (newSize > buffer->capacity) { - dst_buffer_ensure(vm, buffer, 2 * newSize); + dst_buffer_ensure(buffer, 2 * newSize); } buffer->data[buffer->count] = byte; buffer->count = newSize; } /* Push a 16 bit unsigned integer to the buffer */ -void dst_buffer_push_u16(Dst *vm, DstBuffer *buffer, uint16_t x) { +void dst_buffer_push_u16(DstBuffer *buffer, uint16_t x) { uint32_t newSize = buffer->count + 2; if (newSize > buffer->capacity) { - dst_buffer_ensure(vm, buffer, 2 * newSize); + dst_buffer_ensure(buffer, 2 * newSize); } buffer->data[buffer->count] = x & 0xFF; buffer->data[buffer->count + 1] = (x >> 8) & 0xFF; @@ -102,10 +111,10 @@ void dst_buffer_push_u16(Dst *vm, DstBuffer *buffer, uint16_t x) { } /* Push a 32 bit unsigned integer to the buffer */ -void dst_buffer_push_u32(Dst *vm, DstBuffer *buffer, uint32_t x) { +void dst_buffer_push_u32(DstBuffer *buffer, uint32_t x) { uint32_t newSize = buffer->count + 4; if (newSize > buffer->capacity) { - dst_buffer_ensure(vm, buffer, 2 * newSize); + dst_buffer_ensure(buffer, 2 * newSize); } buffer->data[buffer->count] = x & 0xFF; buffer->data[buffer->count + 1] = (x >> 8) & 0xFF; @@ -115,10 +124,10 @@ void dst_buffer_push_u32(Dst *vm, DstBuffer *buffer, uint32_t x) { } /* Push a 64 bit unsigned integer to the buffer */ -void dst_buffer_push_u64(Dst *vm, DstBuffer *buffer, uint64_t x) { +void dst_buffer_push_u64(DstBuffer *buffer, uint64_t x) { uint32_t newSize = buffer->count + 8; if (newSize > buffer->capacity) { - dst_buffer_ensure(vm, buffer, 2 * newSize); + dst_buffer_ensure(buffer, 2 * newSize); } buffer->data[buffer->count] = x & 0xFF; buffer->data[buffer->count + 1] = (x >> 8) & 0xFF; diff --git a/core/cache.c b/core/cache.c index b3dd0ad7..d2d21099 100644 --- a/core/cache.c +++ b/core/cache.c @@ -20,7 +20,7 @@ * IN THE SOFTWARE. */ -#include "internal.h" +#include #include "cache.h" /* All immutable values are cached in a global hash table. When an immutable @@ -32,6 +32,12 @@ * save memory. Values are removed from the cache when they are garbage collected. */ +/* Cache state */ +DstValue *dst_vm_cache; +uint32_t dst_vm_cache_capacity; +uint32_t dst_vm_cache_count; +uint32_t dst_vm_cache_deleted; + /* Check if two not necesarrily finalized immutable values * are equal. Does caching logic */ static int dst_cache_equal(DstValue x, DstValue y) { @@ -55,7 +61,7 @@ static int dst_cache_equal(DstValue x, DstValue y) { if (dst_struct_length(x.as.st) != dst_struct_length(y.as.st)) return 0; len = dst_struct_capacity(x.as.st); for (i = 0; i < len; ++i) - if (!dst_value_equals(x.as.st[i], y.as.st[i])) + if (!dst_equals(x.as.st[i], y.as.st[i])) return 0; return 1; case DST_TUPLE: @@ -63,7 +69,7 @@ static int dst_cache_equal(DstValue x, DstValue y) { if (dst_tuple_length(x.as.tuple) != dst_tuple_length(y.as.tuple)) return 0; len = dst_tuple_length(x.as.tuple); for (i = 0; i < len; ++i) - if (!dst_value_equals(x.as.tuple[i], y.as.tuple[i])) + if (!dst_equals(x.as.tuple[i], y.as.tuple[i])) return 0; return 1; } @@ -85,29 +91,29 @@ static int dst_cache_strequal(DstValue x, const uint8_t *str, uint32_t len, uint /* Find an item in the cache and return its location. * If the item is not found, return the location * where one would put it. */ -static DstValue *dst_cache_find(Dst *vm, DstValue key, int *success) { +static DstValue *dst_cache_find(DstValue key, int *success) { uint32_t bounds[4]; uint32_t i, j, index; - uint32_t hash = dst_value_hash(key); + uint32_t hash = dst_hash(key); DstValue *firstEmpty = NULL; - index = hash % vm->cache_capacity; + index = hash % dst_vm_cache_capacity; bounds[0] = index; - bounds[1] = vm->cache_capacity; + bounds[1] = dst_vm_cache_capacity; bounds[2] = 0; bounds[3] = index; for (j = 0; j < 4; j += 2) for (i = bounds[j]; i < bounds[j+1]; ++i) { - DstValue test = vm->cache[i]; + DstValue test = dst_vm_cache[i]; /* Check empty spots */ if (test.type == DST_NIL) { if (firstEmpty == NULL) - firstEmpty = vm->cache + i; + firstEmpty = dst_vm_cache + i; goto notfound; } /* Check for marked deleted - use booleans as deleted */ if (test.type == DST_BOOLEAN) { if (firstEmpty == NULL) - firstEmpty = vm->cache + i; + firstEmpty = dst_vm_cache + i; continue; } if (dst_cache_equal(test, key)) { @@ -115,10 +121,10 @@ static DstValue *dst_cache_find(Dst *vm, DstValue key, int *success) { *success = 1; if (firstEmpty != NULL) { *firstEmpty = test; - vm->cache[i].type = DST_BOOLEAN; + dst_vm_cache[i].type = DST_BOOLEAN; return firstEmpty; } - return vm->cache + i; + return dst_vm_cache + i; } } notfound: @@ -129,7 +135,7 @@ static DstValue *dst_cache_find(Dst *vm, DstValue key, int *success) { /* Find an item in the cache and return its location. * If the item is not found, return the location * where one would put it. Special case of dst_cache_find */ -DstValue *dst_cache_strfind(Dst *vm, +DstValue *dst_cache_strfind( const uint8_t *str, uint32_t len, uint32_t hash, @@ -137,24 +143,24 @@ DstValue *dst_cache_strfind(Dst *vm, uint32_t bounds[4]; uint32_t i, j, index; DstValue *firstEmpty = NULL; - index = hash % vm->cache_capacity; + index = hash % dst_vm_cache_capacity; bounds[0] = index; - bounds[1] = vm->cache_capacity; + bounds[1] = dst_vm_cache_capacity; bounds[2] = 0; bounds[3] = index; for (j = 0; j < 4; j += 2) for (i = bounds[j]; i < bounds[j+1]; ++i) { - DstValue test = vm->cache[i]; + DstValue test = dst_vm_cache[i]; /* Check empty spots */ if (test.type == DST_NIL) { if (firstEmpty == NULL) - firstEmpty = vm->cache + i; + firstEmpty = dst_vm_cache + i; goto notfound; } /* Check for marked deleted - use booleans as deleted */ if (test.type == DST_BOOLEAN) { if (firstEmpty == NULL) - firstEmpty = vm->cache + i; + firstEmpty = dst_vm_cache + i; continue; } if (dst_cache_strequal(test, str, len, hash)) { @@ -162,10 +168,10 @@ DstValue *dst_cache_strfind(Dst *vm, *success = 1; if (firstEmpty != NULL) { *firstEmpty = test; - vm->cache[i].type = DST_BOOLEAN; + dst_vm_cache[i].type = DST_BOOLEAN; return firstEmpty; } - return vm->cache + i; + return dst_vm_cache + i; } } notfound: @@ -174,24 +180,24 @@ DstValue *dst_cache_strfind(Dst *vm, } /* Resize the cache. */ -static void dst_cache_resize(Dst *vm, uint32_t newCapacity) { +static void dst_cache_resize(uint32_t newCapacity) { uint32_t i, oldCapacity; - DstValue *oldCache = vm->cache; + DstValue *oldCache = dst_vm_cache; DstValue *newCache = calloc(1, newCapacity * sizeof(DstValue)); if (newCache == NULL) { DST_OUT_OF_MEMORY; } - oldCapacity = vm->cache_capacity; - vm->cache = newCache; - vm->cache_capacity = newCapacity; - vm->cache_deleted = 0; + oldCapacity = dst_vm_cache_capacity; + dst_vm_cache = newCache; + dst_vm_cache_capacity = newCapacity; + dst_vm_cache_deleted = 0; /* Add all of the old cache entries back */ for (i = 0; i < oldCapacity; ++i) { int status; DstValue *bucket; DstValue x = oldCache[i]; if (x.type != DST_NIL && x.type != DST_BOOLEAN) { - bucket = dst_cache_find(vm, x, &status); + bucket = dst_cache_find(x, &status); if (status || bucket == NULL) { /* there was a problem with the algorithm. */ break; @@ -205,36 +211,36 @@ static void dst_cache_resize(Dst *vm, uint32_t newCapacity) { /* Add a value to the cache given we know it is not * already in the cache and we have a bucket. */ -DstValue dst_cache_add_bucket(Dst *vm, DstValue x, DstValue *bucket) { - if ((vm->cache_count + vm->cache_deleted) * 2 > vm->cache_capacity) { +DstValue dst_cache_add_bucket(DstValue x, DstValue *bucket) { + if ((dst_vm_cache_count + dst_vm_cache_deleted) * 2 > dst_vm_cache_capacity) { int status; - dst_cache_resize(vm, vm->cache_count * 4); - bucket = dst_cache_find(vm, x, &status); + dst_cache_resize(dst_vm_cache_count * 4); + bucket = dst_cache_find(x, &status); } /* Add x to the cache */ - vm->cache_count++; + dst_vm_cache_count++; *bucket = x; return x; } /* Add a value to the cache */ -DstValue dst_cache_add(Dst *vm, DstValue x) { +DstValue dst_cache_add(DstValue x) { int status = 0; - DstValue *bucket = dst_cache_find(vm, x, &status); + DstValue *bucket = dst_cache_find(x, &status); if (!status) { - return dst_cache_add_bucket(vm, x, bucket); + return dst_cache_add_bucket(x, bucket); } else { return *bucket; } } /* Remove a value from the cache */ -void dst_cache_remove(Dst *vm, DstValue x) { +void dst_cache_remove(DstValue x) { int status = 0; - DstValue *bucket = dst_cache_find(vm, x, &status); + DstValue *bucket = dst_cache_find(x, &status); if (status) { - vm->cache_count--; - vm->cache_deleted++; + dst_vm_cache_count--; + dst_vm_cache_deleted++; bucket->type = DST_BOOLEAN; } } diff --git a/core/cache.h b/core/cache.h index 6aeb3798..fa09f0d9 100644 --- a/core/cache.h +++ b/core/cache.h @@ -24,16 +24,15 @@ #define DST_CACHE_H_defined #include -#include "internal.h" -DstValue dst_cache_add(Dst *vm, DstValue x); -DstValue *dst_cache_strfind(Dst *vm, +DstValue dst_cache_add(DstValue x); +DstValue *dst_cache_strfind( const uint8_t *str, uint32_t len, uint32_t hash, int *success); -DstValue dst_cache_add_bucket(Dst *vm, DstValue x, DstValue *bucket); +DstValue dst_cache_add_bucket(DstValue x, DstValue *bucket); -void dst_cache_remove(Dst *vm, DstValue x); +void dst_cache_remove(DstValue x); #endif diff --git a/core/fiber.c b/core/fiber.c new file mode 100644 index 00000000..598da716 --- /dev/null +++ b/core/fiber.c @@ -0,0 +1,298 @@ +/* +* Copyright (c) 2017 Calvin Rose +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to +* deal in the Software without restriction, including without limitation the +* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +* sell copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +* IN THE SOFTWARE. +*/ + +#include +#include "gc.h" + +/* Initialize a new fiber */ +DstFiber *dst_fiber(DstFunction *func, uint32_t capacity) { + DstFiber *fiber = dst_alloc(DST_MEMORY_FIBER, sizeof(DstFiber)); + if (capacity < 2 * DST_FRAME_SIZE) + capacity = 2 * DST_FRAME_SIZE; + fiber->capacity = capacity; + DstValue *data = malloc(sizeof(DstValue) * capacity); + if (NULL == data) { + DST_OUT_OF_MEMORY; + } + fiber->data = data; + return dst_fiber_reset(fiber, func); +} + +/* Clear a fiber (reset it) */ +DstFiber *dst_fiber_reset(DstFiber *fiber, DstFunction *func) { + uint32_t i; + DstStackFrame *frame; + + if (NULL == func) { + /* Cfunction */ + fiber->frame = DST_FRAME_SIZE; + fiber->frametop = DST_FRAME_SIZE; + fiber->stacktop = fiber->frametop + DST_FRAME_SIZE; + fiber->status = DST_FIBER_PENDING; + frame = dst_fiber_frame(fiber); + + /* Initialize the frame */ + frame->prevframe = 0; + frame->pc = NULL; + frame->func = NULL; + } else { + /* Set the frame to the first available index */ + fiber->frame = DST_FRAME_SIZE; + fiber->frametop = DST_FRAME_SIZE + func->def->slotcount; + fiber->stacktop = fiber->frametop + DST_FRAME_SIZE; + fiber->status = DST_FIBER_PENDING; + frame = dst_fiber_frame(fiber); + + /* Initialize the frame */ + frame->prevframe = 0; + frame->pc = func->def->bytecode; + frame->func = func; + + /* Nil slots */ + for (i = DST_FRAME_SIZE; i < fiber->frametop; ++i) { + fiber->data[i].type = DST_NIL; + } + } + + /* Reset parent. Set it manually if needed. */ + fiber->parent = NULL; + return fiber; +} + +/* Ensure that the fiber has enough extra capacity */ +void dst_fiber_setcapacity(DstFiber *fiber, uint32_t n) { + DstValue *newData = realloc(fiber->data, sizeof(DstValue) * n); + if (NULL == newData) { + DST_OUT_OF_MEMORY; + } + fiber->data = newData; + fiber->capacity = n; +} + +/* Push a value on the next stack frame */ +void dst_fiber_push(DstFiber *fiber, DstValue x) { + if (fiber->stacktop >= fiber->capacity) { + dst_fiber_setcapacity(fiber, 2 * fiber->stacktop); + } + fiber->data[fiber->stacktop++] = x; +} + +/* Push 2 values on the next stack frame */ +void dst_fiber_push2(DstFiber *fiber, DstValue x, DstValue y) { + uint32_t newtop = fiber->stacktop + 2; + if (newtop > fiber->capacity) { + dst_fiber_setcapacity(fiber, 2 * newtop); + } + fiber->data[fiber->stacktop] = x; + fiber->data[fiber->stacktop + 1] = y; + fiber->stacktop = newtop; +} + +/* Push 3 values on the next stack frame */ +void dst_fiber_push3(DstFiber *fiber, DstValue x, DstValue y, DstValue z) { + uint32_t newtop = fiber->stacktop + 3; + if (newtop > fiber->capacity) { + dst_fiber_setcapacity(fiber, 2 * newtop); + } + fiber->data[fiber->stacktop] = x; + fiber->data[fiber->stacktop + 1] = y; + fiber->data[fiber->stacktop + 2] = z; + fiber->stacktop = newtop; +} + +/* Push an array on the next stack frame */ +void dst_fiber_pushn(DstFiber *fiber, const DstValue *arr, uint32_t n) { + uint32_t newtop = fiber->stacktop + n; + if (newtop > fiber->capacity) { + dst_fiber_setcapacity(fiber, 2 * newtop); + } + memcpy(fiber->data + fiber->stacktop, arr, n * sizeof(DstValue)); + fiber->stacktop = newtop; +} + +/* Pop a value off of the stack. Will not destroy a current stack frame. + * If there is nothing to pop of of the stack, return nil. */ +DstValue dst_fiber_popvalue(DstFiber *fiber) { + uint32_t newstacktop = fiber->stacktop - 1; + if (newstacktop <= fiber->frametop + DST_FRAME_SIZE) { + return dst_wrap_nil(); + } + fiber->stacktop = newstacktop; + return fiber->data[newstacktop]; +} + +/* Push a stack frame to a fiber */ +void dst_fiber_funcframe(DstFiber *fiber, DstFunction *func) { + DstStackFrame *newframe; + + uint32_t i; + uint32_t oldframe = fiber->frame; + uint32_t nextframe = fiber->frametop + DST_FRAME_SIZE; + uint32_t nextframetop = nextframe + func->def->slotcount; + uint32_t nextstacktop = nextframetop + DST_FRAME_SIZE; + + if (fiber->capacity < nextstacktop) { + dst_fiber_setcapacity(fiber, 2 * nextstacktop); + } + + /* Set the next frame */ + fiber->frame = nextframe; + fiber->frametop = nextframetop; + newframe = dst_fiber_frame(fiber); + + /* Set up the new frame */ + newframe->prevframe = oldframe; + newframe->pc = func->def->bytecode; + newframe->func = func; + + /* Nil unset locals (Needed for gc correctness) */ + for (i = fiber->stacktop; i < fiber->frametop; ++i) { + fiber->data[i].type = DST_NIL; + } + + /* Check varargs */ + if (func->def->flags & DST_FUNCDEF_FLAG_VARARG) { + uint32_t tuplehead = fiber->frame + func->def->arity; + if (tuplehead >= fiber->stacktop) { + fiber->data[tuplehead] = dst_wrap_tuple(dst_tuple_n(NULL, 0)); + } else { + fiber->data[tuplehead] = dst_wrap_tuple(dst_tuple_n( + fiber->data + tuplehead, + fiber->stacktop - tuplehead)); + } + } + + /* Set stack top */ + fiber->stacktop = nextstacktop; +} + +/* Create a tail frame for a function */ +void dst_fiber_funcframe_tail(DstFiber *fiber, DstFunction *func) { + uint32_t i; + uint32_t nextframetop = fiber->frame + func->def->slotcount; + uint32_t nextstacktop = nextframetop + DST_FRAME_SIZE; + uint32_t size = (fiber->stacktop - fiber->frametop) - DST_FRAME_SIZE; + uint32_t argtop = fiber->frame + size; + + if (fiber->capacity < nextstacktop) { + dst_fiber_setcapacity(fiber, 2 * nextstacktop); + } + + DstValue *stack = fiber->data + fiber->frame; + DstValue *args = fiber->data + fiber->frametop + DST_FRAME_SIZE; + + /* Detatch old function */ + if (NULL != dst_fiber_frame(fiber)->func) + dst_function_detach(dst_fiber_frame(fiber)->func); + + memmove(stack, args, size * sizeof(DstValue)); + + /* Set stack stuff */ + fiber->stacktop = nextstacktop; + fiber->frametop = nextframetop; + + /* Nil unset locals (Needed for gc correctness) */ + for (i = fiber->frame + size; i < fiber->frametop; ++i) { + fiber->data[i].type = DST_NIL; + } + + /* Check varargs */ + if (func->def->flags & DST_FUNCDEF_FLAG_VARARG) { + uint32_t tuplehead = fiber->frame + func->def->arity; + if (tuplehead >= argtop) { + fiber->data[tuplehead] = dst_wrap_tuple(dst_tuple_n(NULL, 0)); + } else { + fiber->data[tuplehead] = dst_wrap_tuple(dst_tuple_n( + fiber->data + tuplehead, + argtop - tuplehead)); + } + } + + /* Set frame stuff */ + dst_fiber_frame(fiber)->func = func; + dst_fiber_frame(fiber)->pc = func->def->bytecode; +} + +/* Push a stack frame to a fiber for a c function */ +void dst_fiber_cframe(DstFiber *fiber) { + DstStackFrame *newframe; + + uint32_t oldframe = fiber->frame; + uint32_t nextframe = fiber->frametop + DST_FRAME_SIZE; + uint32_t nextframetop = fiber->stacktop; + uint32_t nextstacktop = nextframetop + DST_FRAME_SIZE; + + if (fiber->capacity < nextstacktop) { + dst_fiber_setcapacity(fiber, 2 * nextstacktop); + } + + /* Set the next frame */ + fiber->frame = nextframe; + fiber->frametop = nextframetop; + fiber->stacktop = nextstacktop; + newframe = dst_fiber_frame(fiber); + + /* Set up the new frame */ + newframe->prevframe = oldframe; + newframe->pc = NULL; + newframe->func = NULL; +} + +/* Create a cframe for a tail call */ +void dst_fiber_cframe_tail(DstFiber *fiber) { + uint32_t size = (fiber->stacktop - fiber->frametop) - DST_FRAME_SIZE; + uint32_t nextframetop = fiber->frame + size;; + uint32_t nextstacktop = nextframetop + DST_FRAME_SIZE; + + DstValue *stack = fiber->data + fiber->frame; + DstValue *args = fiber->data + fiber->frametop + DST_FRAME_SIZE; + + /* Detach old function */ + if (NULL != dst_fiber_frame(fiber)->func) + dst_function_detach(dst_fiber_frame(fiber)->func); + + /* Copy pushed args to frame */ + memmove(stack, args, size * sizeof(DstValue)); + + /* Set the next frame */ + fiber->frametop = nextframetop; + fiber->stacktop = nextstacktop; + + /* Set up the new frame */ + dst_fiber_frame(fiber)->func = NULL; + dst_fiber_frame(fiber)->pc = NULL; +} + +/* Pop a stack frame from the fiber. Returns the new stack frame, or + * NULL if there are no more frames */ +void dst_fiber_popframe(DstFiber *fiber) { + DstStackFrame *frame = dst_fiber_frame(fiber); + + /* Clean up the frame (detach environments) */ + if (NULL != frame->func) + dst_function_detach(frame->func); + + /* Shrink stack */ + fiber->stacktop = fiber->frame; + fiber->frametop = fiber->frame - DST_FRAME_SIZE; + fiber->frame = frame->prevframe; +} diff --git a/core/func.c b/core/func.c index 689c7c91..c15a1e0b 100644 --- a/core/func.c +++ b/core/func.c @@ -20,525 +20,21 @@ * IN THE SOFTWARE. */ -#include +#include -#include "internal.h" -#include "wrap.h" -#include "gc.h" - -/* Bytecode op argument types */ - -/* s - a slot */ -/* c - a constant */ -/* i - a small integer */ -/* t - a type (have a simple type for non unions) */ -/* l - a label */ - -typedef enum DstOpArgType DstOpArgType; -enum DstOpArgType { - DST_OAT_SLOT, - DST_OAT_ENVIRONMENT, - DST_OAT_CONSTANT, - DST_OAT_INTEGER, - DST_OAT_TYPE, - DST_OAT_SIMPLETYPE, - DST_OAT_LABEL -} - -/* Convert a slot to to an integer for bytecode */ - -/* Types of instructions */ -/* _0arg - op.---.--.-- (return-nil, noop, vararg arguments) - * _s - op.src.--.-- (push1) - * _l - op.XX.XX.XX (jump) - * _ss - op.dest.XX.XX (move, swap) - * _sl - op.check.XX.XX (jump-if) - * _st - op.check.TT.TT (typecheck) - * _si - op.dest.XX.XX (load-integer) - * _sss - op.dest.op1.op2 (add, subtract, arithmetic, comparison) - * _ses - op.dest.up.which (load-upvalue, save-upvalue) - * _sc - op.dest.CC.CC (load-constant, closure) - */ - -/* Various types of instructions */ -typedef enum DstInstructionType DstInstructionType; -enum DstInstructionType { - DIT_0, /* No args */ - DIT_S, /* One slot */ - DIT_L, /* One label */ - DIT_SS, /* Two slots */ - DIT_SL, - DIT_ST, - DIT_SI, - DIT_SSS, - DIT_SES, - DIT_SC -}; - -/* Definition for an instruction in the assembler */ -typedef struct DstInstructionDef DstInstructionDef; -struct DstInstructionDef { - const char *name; - DstInstructionType type; - uint8_t opcode; -}; - -/* Hold all state needed during assembly */ -typedef struct DstAssembler DstAssembler; -struct DstAssembler { - DstAssembler *parent; - Dst *vm; - DstFuncDef *def; - DstValue name; - jmp_buf onError; - - DstTable *labels; /* symbol -> bytecode index */ - DstTable *constants; /* symbol -> constant index */ - DstTable *slots; /* symbol -> slot index */ - DstTable *envs; /* symbol -> environment index */ - uint32_t *bytecode; /* Where to put bytecode */ - uint32_t bytecode_capacity; /* Set once */ - uint32_t bytecode_count; -} - -/* The DST value types in order. These types can be used as - * mnemonics instead of a bit pattern for type checking */ -static const char *types[] = { - "nil", - "real", - "integer", - "boolean", - "string", - "symbol", - "array", - "tuple", - "table", - "struct", - "thread", - "buffer", - "function", - "cfunction", - "userdata" -}; - -/* Dst opcode descriptions in lexographic order. This - * allows a binary search over the elements to find the - * correct opcode given a name. This works in reasonable - * time is easier to setup statically than a hash table or - * prefix tree. */ -static const char *dst_ops[] = { - {"add", DIT_SSS, 0x01}, - {"bitand", DIT_SSS, 0x02}, - {"bitor", DIT_SSS, 0x03}, - {"bitxor", DIT_SSS, 0x04}, - {"call", DIT_SS, 0x05}, - {"closure", DIT_SC, 0x06}, - {"divide", DIT_SSS, 0x07}, - {"jump", DIT_L, 0x08}, - {"jump-if", DIT_SL, 0x09}, - {"load-constant", DIT_SC, 0x0A}, - {"load-false", DIT_S, 0x0B}, - {"load-integer", DIT_SI, 0x0C}, - {"load-nil", DIT_S, 0x0D}, - {"load-true", DIT_S, 0x0E}, - {"load-upvalue", DIT_SES, 0x0F}, - {"move", DIT_SS, 0x10}, - {"modulo", DIT_SSS, 0x11}, - {"multiply", DIT_SSS, 0x12}, - {"noop", DIT_0, 0x13}, - {"push", DIT_VARG, 0x14}, - {"push1", DIT_S, 0x15}, - {"push2", DIT_SS, 0x16}, - {"push3", DIT_SSS, 0x17}, - {"push-array", DIT_S, 0x18}, - {"return", DIT_S, 0x19}, - {"return-nil", DIT_0, 0x1A}, - {"save-upvalue", DIT_SES, 0x1B}, - {"shift-left", DIT_SSS, 0x1C}, - {"shift-right", DIT_SSS, 0x1D}, - {"shift-right-signed", DIT_SSS, 0x1E}, - {"subtract", DIT_SSS, 0x1F}, - {"swap", DIT_SS, 0x20}, - {"syscall", DIT_SI, 0x21}, - {"tail-call", DIT_S, 0x22}, - {"transfer", DIT_SSS, 0x23}, - {"typecheck", DIT_ST, 0x24}, -}; - -/* Compare a DST string to a native 0 terminated c string. Used in the - * binary search for the instruction definition. */ -static int dst_strcompare(const uint8_t *str, const char *other) { - uint32_t len = dst_string_length(str); - int index; - for (index = 0; index < len; index++) { - uint8_t c = str[index]; - uint8_t k = ((const uint8_t *)other)[index]; - if (c < k) return -1; - if (c > k) return 1; - if (k == '\0') break; - } - return (other[index] == '\0') ? 0 : -1; -} - -/* Find an instruction definition given its name */ -static DstInstructionDef *dst_findi(const uint8_t *key) { - DstInstructionDef *low = dst_ops; - DstInstructionDef *hi = dst_ops + (sizeof(dst_ops) / sizeof(DstInstructionDef)); - while (low < hi) { - DstInstructionDef *mid = low + ((hi - low) / 2); - int comp = dst_strcompare(key, mid->name); - if (comp < 0) { - hi = mid; - } else if (comp > 0) { - low = mid + 1; - } else { - return mid; - } - } - return NULL; -} - -/* Check a dst string against a bunch of test_strings. Return the - * index of the matching test_string, or -1 if not found. */ -static int strsearch(const uint8_t *str, const char **test_strings) { - uint32_t len = dst_string_length(str); - int index; - for (index = 0; ; index++) { - uint32_t i; - const char *testword = test_strings[index]; - if (NULL == testword) - break; - for (i = 0; i < len; i++) { - if (testword[i] != str[i]) - goto nextword; - } - return index; - :nextword - } - return -1; -} - -/* Takes some dst assembly and gets the required capacity - * for the output bytecode. Does not do any memory allocation, */ -static uint32_t estimate_capacity(const DstValue *assembly, uint32_t n) { - uint32_t i; - uint32_t cap = 0; - for (i = 0; i < n; i++) { - /* Ignore non tuple types, they are labels */ - if (assembly[i].type != DST_TUPLE) continue; - cap++; - } - return cap; -} - -/* Throw some kind of assembly error */ -static void dst_asm_error(DstAssembler *a, const char *message) { - printf("%s\n", message); - exit(1); -} - -/* Parse an argument to an assembly instruction, and return the result as an - * integer. This integer will need to be trimmed and bound checked. */ -static int64_t doarg_1(DstAssembler *a, DstOpArgType argtype, DstValue x) { - DstTable *names; - switch (argtype) { - case DST_OAT_SLOT: - c = a->slots; - break; - case DST_OAT_ENVIRONMENT: - c = e->envs; - break; - case DST_OAT_CONSTANT: - c = a->constants; - break; - case DST_OAT_INTEGER: - c = NULL; - break; - case DST_OAT_TYPE: - case DST_OAT_SIMPLETYPE: - c = NULL; - break; - case DST_OAT_LABEL: - c = a->labels; - break; - } - switch (x.type) { - default: - break; - case DST_INTEGER: - return x.as.integer; - case DST_TUPLE: - { - if (argtype == DST_OAT_TYPE) { - int64_t result = 0; - uint32_t i = 0; - for (i = 0; i < dst_tuple_length(x.as.tuple); i++) { - result |= dst_asm_argument(a, DST_OAT_SIMPLETYPE, x.as.tuple[i]); - } - return result; - } - break; - } - case DST_SYMBOL: - { - if (NULL != names) { - DstValue result = dst_table_get(names, x); - if (result.type == DST_INTEGER) { - if (argtype == DST_OAT_LABEL) - return result.as.integer - a->bytecode_count; - return result.as.integer; - } else { - dst_asm_error(a, "unknown name"); - } - } else if (argtype == DST_OAT_TYPE || argtype == DST_OAT_SIMPLETYPE) { - int index = strsearch(x.as.string, types); - if (index != -1) { - return (int64_t) index; - } else { - dst_asm_error(a, "unknown type"); - } - } - break; - } - } - dst_asm_error(a, "unexpected type parsing instruction argument"); -} - -/* Trim a bytecode operand to 1, 2, or 3 bytes. Error out if - * the given argument doesn't fit in the required number of bytes. */ -static uint32_t doarg_2(int nbytes, int hassign, int64_t arg) { - /* Calculate the min and max values that can be stored given - * nbytes, and whether or not the storage is signed */ - int64_t min = (-hassign) << ((nbytes << 3) - 1); - int64_t max = ~((-1) << ((nbytes << 3) - hassign)); - if (arg < min) - dst_asm_error(a, "instruction argument is too small"); - if (arg > max) - dst_asm_error(a, "instruction argument is too large"); - return (uint32_t) (arg & 0xFFFFFFFF); -} - -/* Parse a single argument to an instruction. Trims it as well as - * try to convert arguments to bit patterns */ -static uint32_t doarg(DstAssembler *a, DstOpArgType argtype, int nth, int nbytes, int hassign, DstValue x) { - int64_t arg1 = doarg_1(a, argtype, x); - return doarg_2(nbytes, hassign, arg1) << (nth << 3); -} - -/* Provide parsing methods for the different kinds of arguments */ -static uint32_t read_instruction(DstAssembler *a, const DstInstructionDef *idef, const DstValue *argt) { - uint32_t instr = idef->opcode; - switch (idef->type) { - case DIT_0: - { - if (dst_tuple_lenth(argt) != 1) - dst_asm_error(a, "expected 0 arguments: (op)"); - break; - } - case DIT_S: - { - if (dst_tuple_lenth(argt) != 2) - dst_asm_error(a, "expected 1 argument: (op, slot)"); - instr |= doarg(a, DST_OAT_SLOT, 3, 3, 0, argt[1]); - break; - } - case DIT_L: - { - if (dst_tuple_lenth(argt) != 2) - dst_asm_error(a, "expected 1 argument: (op, label)"); - instr |= doarg(a, DST_OAT_LABEL, 3, 3, 1, argt[1]); - break; - } - case DIT_SS: - { - if (dst_tuple_lenth(argt) != 3) - dst_asm_error(a, "expected 2 arguments: (op, slot, slot)"); - instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); - instr |= doarg(a, DST_OAT_SLOT, 3, 2, 0, argt[2]); - break; - } - case DIT_SL: - { - if (dst_tuple_lenth(argt) != 3) - dst_asm_error(a, "expected 2 arguments: (op, slot, label)"); - instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); - instr |= doarg(a, DST_OAT_LABEL, 3, 2, 1, argt[2]); - break; - } - case DIT_ST: - { - if (dst_tuple_lenth(argt) != 3) - dst_asm_error(a, "expected 2 arguments: (op, slot, type)"); - instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); - instr |= doarg(a, DST_OAT_TYPE, 3, 2, 0, argt[2]); - break; - } - case DIT_SI: - { - if (dst_tuple_lenth(argt) != 3) - dst_asm_error(a, "expected 2 arguments: (op, slot, integer)"); - instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); - instr |= doarg(a, DST_OAT_INTEGER, 3, 2, 1, argt[2]); - break; - } - case DIT_SSS: - { - if (dst_tuple_lenth(argt) != 4) - dst_asm_error(a, "expected 3 arguments: (op, slot, slot, slot)"); - instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); - instr |= doarg(a, DST_OAT_SLOT, 2, 1, 0, argt[2]); - instr |= doarg(a, DST_OAT_SLOT, 3, 1, 0, argt[3]); - break; - } - case DIT_SES: - { - DstAssembler *b = a; - uint32_t envn; - if (dst_tuple_lenth(argt) != 4) - dst_asm_error(a, "expected 3 arguments: (op, slot, environment, envslot)"); - instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); - envn = doarg(a, DST_OAT_ENVIRONMENT, 0, 1, 0, argt[2]); - instr |= envn << 16; - for (env += 1; env > 0; env--) { - b = b->parent; - if (NULL == b) - dst_asm_error(a, "invalid environment index"); - } - instr |= doarg(b, DST_OAT_SLOT, 3, 1, 0, argt[3]); - break; - } - case DIT_SC: - { - if (dst_tuple_lenth(argt) != 3) - dst_asm_error(a, "expected 2 arguments: (op, slot, constant)"); - instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); - instr |= doarg(a, DST_OAT_CONSTANT, 3, 2, 0, argt[2]); - break; - } - } - return instr; -} - -/* Do assembly. Return 0 if successful, else return an error code. */ -static void dst_asm1(DstAssembler *a, DstValue src) { - DstTable *t = src.as.table; - DstFuncDef *def = a->def; - uint32_t i; - DstValue x; - - if (src.type != DST_TABLE) - dst_asm_error(a, "expected table"); - x = dst_table_get(t, dst_wrap_symbol(dst_cstring(a->vm, "arity"))); - def->arity = x.type == DST_INTEGER ? x.as.integer : 0; - x = dst_table_get(t, dst_wrap_symbol(dst_cstring(a->vm, "stack"))); - def->locals = x.type == DST_INTEGER ? x.as.integer : 0; - - // Check name - x = dst_table_get(t, dst_wrap_symbol(dst_cstring(a->vm, "name"))); - if (x.type == SYMBOL) { - a->name = x; - } - - // Create slot aliases - x = dst_table_get(t, dst_wrap_symbol(dst_cstring(a->vm, "slots"))); - if (x.type == DST_ARRAY) { - for (i = 0; i < x.as.array->count; i++) { - DstValue v = x.as.array->data[i]; - if (v.type == DST_TUPLE) { - uint32_t j; - for (j = 0; j < dst_tuple_length(v.as.tuple); j++) { - if (v.as.tuple[j].type != SYMBOL) - dst_asm_error("slot names must be symbols"); - dst_table_put(a->vm, a->slots, v.as.tuple[j], dst_wrap_integer(i)); - } - } else if (v.type == DST_SYMBOL) { - dst_table_put(a->vm, a->slots, v, dst_wrap_integer(i)); - } else { - dst_asm_error(a, "slot names must be symbols or tuple of symbols"); - } - } - } - - // Create environment aliases - x = dst_table_get(t, dst_wrap_symbol(dst_cstring(a->vm, "environments"))); - if (x.type == DST_ARRAY) { - for (i = 0; i < x.as.array->count; i++) { - DstAssembler *b = a->parent; - DstValue v = x.as.array->data[i]; - if (v.type != DST_SYMBOL) { - dst_asm_error(a, "expected a symbol"); - while (NULL != b) { - if (dst_equals(b->name, v)) { - break; - } - b = b->parent; - } - // Check parent assemblers to find the given environment +/* If a frame has a closure environment, detach it from + * the stack and have it keep its own values */ +void dst_function_detach(DstFunction *func) { + /* Check for closure environment */ + if (NULL != func->envs && NULL != func->envs[0]) { + DstFuncEnv *env = func->envs[0]; + size_t s = sizeof(DstValue) * env->length; + DstValue *vmem = malloc(s); + if (NULL == vmem) { + DST_OUT_OF_MEMORY; } + memcpy(vmem, env->as.fiber->data + env->offset, s); + env->offset = 0; + env->as.values = vmem; } } - -/* Detach an environment that was on the stack from the stack, and - * ensure that its environment persists */ -void dst_funcenv_detach_(Dst *vm, DstFuncEnv *env) { - DstThread *thread = env->thread; - DstValue *stack = thread->data + thread->count; - uint32_t size = dst_frame_size(stack); - DstValue *values = malloc(sizeof(DstValue * size)); - if (NULL == values) { - DST_OUT_OF_MEMORY; - } - /* Copy stack into env values (the heap) */ - memcpy(values, stack, sizeof(DstValue) * size); - /* Update env */ - env->thread = NULL; - env->stackOffset = size; - env->values = values; -} - -/* Deinitialize an environment */ -void dst_funcenv_deinit(DstFuncEnv *env) { - if (NULL == env->thread && NULL != env->values) { - free(env->values); - } -} - -/* Create the FuncEnv for the current stack frame. */ -DstFuncEnv *dst_funcenv_init_(Dst *vm, DstFuncEnv *env, DstThread *thread, DstValue *stack) { - env->thread = thread; - env->stackOffset = thread->count; - env->values = NULL; - dst_frame_env(stack) = env; - return env; -} - - -/* Create a funcdef */ -DstFuncDef *dst_funcdef_init_(Dst *vm, DstFuncDef *def) { - uint8_t * byteCode = dst_alloc(c->vm, lastNBytes); - def->byteCode = (uint16_t *)byteCode; - def->byteCodeLen = lastNBytes / 2; - /* Copy the last chunk of bytes in the buffer into the new - * memory for the function's byteCOde */ - dst_memcpy(byteCode, buffer->data + buffer->count - lastNBytes, lastNBytes); - /* Remove the byteCode from the end of the buffer */ - buffer->count -= lastNBytes; - /* Create the literals used by this function */ - if (scope->literalsArray->count) { - def->literals = dst_alloc(c->vm, scope->literalsArray->count * sizeof(DstValue)); - dst_memcpy(def->literals, scope->literalsArray->data, - scope->literalsArray->count * sizeof(DstValue)); - } else { - def->literals = NULL; - } - def->literalsLen = scope->literalsArray->count; - /* Delete the sub scope */ - compiler_pop_scope(c); - /* Initialize the new FuncDef */ - def->locals = scope->frameSize; - def->arity = arity; - def->flags = (varargs ? DST_FUNCDEF_FLAG_VARARG : 0) | - (scope->touchParent ? DST_FUNCDEF_FLAG_NEEDSPARENT : 0) | - (scope->touchEnv ? DST_FUNCDEF_FLAG_NEEDSENV : 0); - return def; -} diff --git a/core/gc.c b/core/gc.c index 4fde9d38..168d3cd7 100644 --- a/core/gc.c +++ b/core/gc.c @@ -20,13 +20,17 @@ * IN THE SOFTWARE. */ -#include "internal.h" +#include #include "cache.h" -#include "wrap.h" + +/* GC State */ +void *dst_vm_blocks; +uint32_t dst_vm_memory_interval; +uint32_t dst_vm_next_collection; /* Helpers for marking the various gc types */ static void dst_mark_funcenv(DstFuncEnv *env); -static void dst_mark_funcdef(DstFuncEnv *def); +static void dst_mark_funcdef(DstFuncDef *def); static void dst_mark_function(DstFunction *func); static void dst_mark_array(DstArray *array); static void dst_mark_table(DstTable *table); @@ -34,7 +38,7 @@ static void dst_mark_struct(const DstValue *st); static void dst_mark_tuple(const DstValue *tuple); static void dst_mark_buffer(DstBuffer *buffer); static void dst_mark_string(const uint8_t *str); -static void dst_mark_thread(DstThread *thread); +static void dst_mark_fiber(DstFiber *fiber); static void dst_mark_udata(void *udata); /* Mark a value */ @@ -49,13 +53,13 @@ void dst_mark(DstValue x) { case DST_STRUCT: dst_mark_struct(x.as.st); break; case DST_TUPLE: dst_mark_tuple(x.as.tuple); break; case DST_BUFFER: dst_mark_buffer(x.as.buffer); break; - case DST_STRING: dst_mark_string(x.as.string); break; - case DST_THREAD: dst_mark_thread(x.as.thread); break; + case DST_FIBER: dst_mark_fiber(x.as.fiber); break; case DST_USERDATA: dst_mark_udata(x.as.pointer); break; } } -/* Unpin a value */ +/* Unpin a value. This enables the GC to collect the value's + * memory again. */ void dst_unpin(DstValue x) { switch (x.type) { default: break; @@ -67,13 +71,17 @@ void dst_unpin(DstValue x) { case DST_STRUCT: dst_unpin_struct(x.as.st); break; case DST_TUPLE: dst_unpin_tuple(x.as.tuple); break; case DST_BUFFER: dst_unpin_buffer(x.as.buffer); break; - case DST_STRING: dst_unpin_string(x.as.string); break; - case DST_THREAD: dst_unpin_thread(x.as.thread); break; - case DST_USERDATA: dst_unpin_udata(x.as.pointer); break; + case DST_FIBER: dst_unpin_fiber(x.as.fiber); break; + case DST_USERDATA: dst_unpin_userdata(x.as.pointer); break; } } -/* Pin a value */ +/* Pin a value. This prevents a value from being garbage collected. + * Needed if the valueis not accesible to the garbage collector, but + * still in use by the program. For example, a c function that + * creates a table, and then runs the garbage collector without + * ever saving the table anywhere (except on the c stack, which + * the gc cannot inspect). */ void dst_pin(DstValue x) { switch (x.type) { default: break; @@ -85,86 +93,84 @@ void dst_pin(DstValue x) { case DST_STRUCT: dst_pin_struct(x.as.st); break; case DST_TUPLE: dst_pin_tuple(x.as.tuple); break; case DST_BUFFER: dst_pin_buffer(x.as.buffer); break; - case DST_STRING: dst_pin_string(x.as.string); break; - case DST_THREAD: dst_pin_thread(x.as.thread); break; - case DST_USERDATA: dst_pin_udata(x.as.pointer); break; + case DST_FIBER: dst_pin_fiber(x.as.fiber); break; + case DST_USERDATA: dst_pin_userdata(x.as.pointer); break; } } static void dst_mark_string(const uint8_t *str) { - gc_mark(dst_string_raw(str)); + dst_gc_mark(dst_string_raw(str)); } static void dst_mark_buffer(DstBuffer *buffer) { - gc_mark(buffer); + dst_gc_mark(buffer); } static void dst_mark_udata(void *udata) { - gc_mark(dst_udata_header(udata)); + dst_gc_mark(dst_userdata_header(udata)); } /* Mark a bunch of items in memory */ static void dst_mark_many(const DstValue *values, uint32_t n) { const DstValue *end = values + n; while (values < end) { - dst_mark(*values) - ++values; + dst_mark(*values); + values += 1; } } static void dst_mark_array(DstArray *array) { - if (gc_reachable(array)) + if (dst_gc_reachable(array)) return; - gc_mark(array); + dst_gc_mark(array); dst_mark_many(array->data, array->count); } static void dst_mark_table(DstTable *table) { - if (gc_reachable(table)) + if (dst_gc_reachable(table)) return; - gc_mark(table); + dst_gc_mark(table); dst_mark_many(table->data, table->capacity); } static void dst_mark_struct(const DstValue *st) { - if (gc_reachable(dst_struct_raw(st))) + if (dst_gc_reachable(dst_struct_raw(st))) return; - gc_mark(dst_struct_raw(st)); + dst_gc_mark(dst_struct_raw(st)); dst_mark_many(st, dst_struct_capacity(st)); } static void dst_mark_tuple(const DstValue *tuple) { - if (gc_reachable(dst_tuple_raw(tuple))) + if (dst_gc_reachable(dst_tuple_raw(tuple))) return; - gc_mark(dst_tuple_raw(tuple)); - dst_mark_many(tuple, dst_tuple_count(tuple)); + dst_gc_mark(dst_tuple_raw(tuple)); + dst_mark_many(tuple, dst_tuple_length(tuple)); } /* Helper to mark function environments */ static void dst_mark_funcenv(DstFuncEnv *env) { - if (gc_reachable(env)) + if (dst_gc_reachable(env)) return; - gc_mark(env); - if (env->values) { - uint32_t count = env->stackOffset; - uint32_t i; - for (i = 0; i < count; ++i) - dst_mark_value(env->values[i]); + dst_gc_mark(env); + if (env->offset) { + /* On stack */ + dst_mark_fiber(env->as.fiber); + } else { + /* Not on stack */ + dst_mark_many(env->as.values, env->length); } - if (env->thread) - dst_mark_thread(env->thread); } /* GC helper to mark a FuncDef */ static void dst_mark_funcdef(DstFuncDef *def) { uint32_t count, i; - if (gc_reachable(def)) + if (dst_gc_reachable(def)) return; - gc_mark(def); - if (def->literals) { - count = def->literalsLen; + dst_gc_mark(def); + if (def->constants) { + count = def->constants_length; for (i = 0; i < count; ++i) { - DstValue v = def->literals[i]; + DstValue v = def->constants[i]; /* Funcdefs use boolean literals to store other funcdefs */ if (v.type == DST_BOOLEAN) { dst_mark_funcdef((DstFuncDef *) v.as.pointer); @@ -178,93 +184,91 @@ static void dst_mark_funcdef(DstFuncDef *def) { static void dst_mark_function(DstFunction *func) { uint32_t i; uint32_t numenvs; - if (gc_reachable(func)) + if (dst_gc_reachable(func)) return; - gc_mark(func) - numenvs = fun->def->envLen; + dst_gc_mark(func); + numenvs = func->def->environments_length; for (i = 0; i < numenvs; ++i) - dst_mark_funcenv(func->envs + i); + if (NULL != func->envs[i]) + dst_mark_funcenv(func->envs[i]); dst_mark_funcdef(func->def); } -/* Helper to mark a stack frame. Returns the next stackframe. */ -static DstValue *dst_mark_stackframe(Dst *vm, DstValue *stack) { - dst_mark(dst_frame_callee(stack)); - if (dst_frame_env(stack) != NULL) - dst_mark_funcenv(dst_frame_env(stack)); - /* Mark all values in the stack frame */ - dst_mark_many(stack, dst_frame_size(stack)); - /* Return the nexct frame */ - return stack + dst_frame_size(stack) + DST_FRAME_SIZE; -} - -static void dst_mark_thread(DstThread *thread) { - DstValue *frame = thread->data + DST_FRAME_SIZE; - DstValue *end = thread->data + thread->count; - if (gc_reachable(thread)) +static void dst_mark_fiber(DstFiber *fiber) { + uint32_t i, j; + DstStackFrame *frame; + if (dst_gc_reachable(fiber)) return; - gc_mark(thread); - while (frame <= end) - frame = dst_mark_stackframe(vm, frame); - if (thread->parent) - dst_mark_thread(thread->parent); + dst_gc_mark(fiber); + + i = fiber->frame; + j = fiber->frametop; + while (i != 0) { + frame = (DstStackFrame *)(fiber->data + i - DST_FRAME_SIZE); + if (NULL != frame->func) + dst_mark_function(frame->func); + /* Mark all values in the stack frame */ + dst_mark_many(fiber->data + i, j - i); + j = i - DST_FRAME_SIZE; + i = frame->prevframe; + } + + if (NULL != fiber->parent) + dst_mark_fiber(fiber->parent); + + dst_mark(fiber->ret); } /* Deinitialize a block of memory */ -static void dst_deinit_block(Dst *vm, GCMemoryHeader *block) { +static void dst_deinit_block(DstGCMemoryHeader *block) { void *mem = ((char *)(block + 1)); DstUserdataHeader *h = (DstUserdataHeader *)mem; void *smem = mem + 2 * sizeof(uint32_t); - switch (current->tags) { + switch (block->flags & DST_MEM_TYPEBITS) { default: break; /* Do nothing for non gc types */ case DST_MEMORY_STRING: - dst_cache_remove(vm, dst_wrap_string(smem)); + dst_cache_remove(dst_wrap_string(smem)); break; case DST_MEMORY_ARRAY: free(((DstArray*) mem)->data); break; case DST_MEMORY_TUPLE: - dst_cache_remove(vm, dst_wrap_tuple(smem)); + dst_cache_remove(dst_wrap_tuple(smem)); break; case DST_MEMORY_TABLE: free(((DstTable*) mem)->data); break; case DST_MEMORY_STRUCT: - dst_cache_remove(vm, dst_wrap_struct(smem)); + dst_cache_remove(dst_wrap_struct(smem)); break; - case DST_MEMORY_THREAD: - free(((DstThread *) mem)->data); + case DST_MEMORY_FIBER: + free(((DstFiber *) mem)->data); break; case DST_MEMORY_BUFFER: free(((DstBuffer *) mem)->data); break; case DST_MEMORY_FUNCTION: - { - DstFunction *f = (DstFunction *)mem; - if (NULL != f->envs) - free(f->envs); - } + free(((DstFunction *)mem)->envs); break; case DST_MEMORY_USERDATA: if (h->type->finalize) - h->type->finalize(vm, (void *)(h + 1), h->size); + h->type->finalize((void *)(h + 1), h->size); break; case DST_MEMORY_FUNCENV: { DstFuncEnv *env = (DstFuncEnv *)mem; - if (NULL == env->thread && NULL != env->values) - free(env->values); + if (0 == env->offset) + free(env->as.values); } break; case DST_MEMORY_FUNCDEF: { - DstFunDef *def = (DstFuncDef *)mem; + DstFuncDef *def = (DstFuncDef *)mem; /* TODO - get this all with one alloc and one free */ - free(def->envSizes); - free(def->envCaptures); - free(def->literals); - free(def->byteCode); + free(def->environments); + free(def->constants); + free(def->bytecode); } break; } @@ -272,20 +276,20 @@ static void dst_deinit_block(Dst *vm, GCMemoryHeader *block) { /* Iterate over all allocated memory, and free memory that is not * marked as reachable. Flip the gc color flag for next sweep. */ -void dst_sweep(Dst *vm) { - GCMemoryHeader *previous = NULL; - GCMemoryHeader *current = vm->blocks; - GCMemoryHeader *next; +void dst_sweep() { + DstGCMemoryHeader *previous = NULL; + DstGCMemoryHeader *current = dst_vm_blocks; + DstGCMemoryHeader *next; while (current) { next = current->next; if (current->flags & (DST_MEM_REACHABLE | DST_MEM_DISABLED)) { previous = current; } else { - dst_deinit_block(vm, current); + dst_deinit_block(current); if (previous) { previous->next = next; } else { - vm->blocks = next; + dst_vm_blocks = next; } free(current); } @@ -295,49 +299,45 @@ void dst_sweep(Dst *vm) { } /* Allocate some memory that is tracked for garbage collection */ -void *dst_alloc(Dst *vm, DstMemoryType type, size_t size) { - GCMemoryHeader *mdata; - size_t totalSize = size + sizeof(GCMemoryHeader); - void *mem = malloc(totalSize); +void *dst_alloc(DstMemoryType type, size_t size) { + DstGCMemoryHeader *mdata; + size_t total = size + sizeof(DstGCMemoryHeader); + void *mem = malloc(total); /* Check for bad malloc */ if (NULL == mem) { DST_OUT_OF_MEMORY; } - mdata = (GCMemoryHeader *)rawBlock; + mdata = (DstGCMemoryHeader *)mem; /* Configure block */ mdata->flags = type; /* Prepend block to heap list */ - vm->nextCollection += size; - mdata->next = vm->blocks; - vm->blocks = mdata; + dst_vm_next_collection += size; + mdata->next = dst_vm_blocks; + dst_vm_blocks = mdata; - return mem + sizeof(GCMemoryHeader); + return mem + sizeof(DstGCMemoryHeader); } /* Run garbage collection */ -void dst_collect(Dst *vm) { - if (vm->thread) - dst_mark_thread(vm->thread); - dst_mark_table(vm->modules); - dst_mark_table(vm->registry); - dst_mark_table(vm->env); - dst_mark(vm->ret); - dst_sweep(vm); - vm->nextCollection = 0; +void dst_collect() { + if (dst_vm_fiber) + dst_mark_fiber(dst_vm_fiber); + dst_sweep(); + dst_vm_next_collection = 0; } /* Free all allocated memory */ -void dst_clear_memory(Dst *vm) { - GCMemoryHeader *current = vm->blocks; +void dst_clear_memory() { + DstGCMemoryHeader *current = dst_vm_blocks; while (current) { - dst_deinit_block(vm, current); - GCMemoryHeader *next = current->next; + dst_deinit_block(current); + DstGCMemoryHeader *next = current->next; free(current); current = next; } - vm->blocks = NULL; + dst_vm_blocks = NULL; } diff --git a/core/gc.h b/core/gc.h deleted file mode 100644 index 4306a1c4..00000000 --- a/core/gc.h +++ /dev/null @@ -1,120 +0,0 @@ -/* -* Copyright (c) 2017 Calvin Rose -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to -* deal in the Software without restriction, including without limitation the -* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -* sell copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -* IN THE SOFTWARE. -*/ - -#ifndef DST_GC_H_defined -#define DST_GC_H_defined - -#include "internal.h" - -/* The metadata header associated with an allocated block of memory */ -#define gc_header(mem) ((GCMemoryHeader *)(mem) - 1) - -#define DST_MEM_REACHABLE 0x100 -#define DST_MEM_DISABLED 0x200 - -#define gc_settype(m, t) ((gc_header(m)->flags |= (0xFF & (t)))) -#define gc_type(m) (gc_header(m)->flags & 0xFF) - -#define gc_mark(m) (gc_header(m)->flags |= DST_MEM_REACHABLE) -#define gc_unmark(m) (gc_header(m)->flags &= ~DST_MEM_COLOR) -#define gc_reachable(m) (gc_header(m)->flags & DST_MEM_REACHABLE) - - -/* Memory header struct. Node of a linked list of memory blocks. */ -typedef struct GCMemoryHeader GCMemoryHeader; -struct GCMemoryHeader { - GCMemoryHeader *next; - uint32_t flags; -}; - -/* Memory types for the GC. Different from DstType to include funcenv and funcdef. */ -typedef enum DstMemoryType DstMemoryType; -enum DstMemoryType { - DST_MEMORY_NONE, - DST_MEMORY_STRING, - DST_MEMORY_ARRAY, - DST_MEMORY_TUPLE, - DST_MEMORY_TABLE, - DST_MEMORY_STRUCT, - DST_MEMORY_THREAD, - DST_MEMORY_BUFFER, - DST_MEMORY_FUNCTION, - DST_MEMORY_USERDATA, - DST_MEMORY_FUNCENV, - DST_MEMORY_FUNCDEF -} - -/* Prevent GC from freeing some memory. */ -#define dst_disablegc(m) (gc_header(m)->flags |= DST_MEM_DISABLED, (m)) - -/* To allocate collectable memory, one must calk dst_alloc, initialize the memory, - * and then call when dst_enablegc when it is initailize and reachable by the gc (on the DST stack) */ -void *dst_alloc(Dst *vm, DstMemoryType type, size_t size); -#define dst_enablegc(m) (gc_header(m)->flags &= ~DST_MEM_DISABLED, (m)) - -/* When doing C interop, it is often needed to disable GC on a value. - * This is needed when a garbage collection could occur in the middle - * of a c function. This could happen, for example, if one calls back - * into dst inside of a c function. The pin and unpin functions toggle - * garbage collection on a value when needed. Note that no dst functions - * will call gc when you don't want it to. GC only happens automatically - * in the interpreter loop. */ -void dst_pin(DstValue x); -void dst_unpin(DstValue x); - -/* Specific types can also be pinned and unpinned as well. */ -#define dst_pin_table dst_disablegc -#define dst_pin_array dst_disablegc -#define dst_pin_buffer dst_disablegc -#define dst_pin_function dst_disablegc -#define dst_pin_thread dst_disablegc -#define dst_pin_string(s) dst_disablegc(dst_string_raw(s)) -#define dst_pin_symbol(s) dst_disablegc(dst_string_raw(s)) -#define dst_pin_tuple(s) dst_disablegc(dst_tuple_raw(s)) -#define dst_pin_struct(s) dst_disablegc(dst_struct_raw(s)) -#define dst_pin_userdata(s) dst_disablegc(dst_userdata_header(s)) - -#define dst_unpin_table dst_enablegc -#define dst_unpin_array dst_enablegc -#define dst_unpin_buffer dst_enablegc -#define dst_unpin_function dst_enablegc -#define dst_unpin_thread dst_enablegc -#define dst_unpin_string(s) dst_enablegc(dst_string_raw(s)) -#define dst_unpin_symbol(s) dst_enablegc(dst_string_raw(s)) -#define dst_unpin_tuple(s) dst_enablegc(dst_tuple_raw(s)) -#define dst_unpin_struct(s) dst_enablegc(dst_struct_raw(s)) -#define dst_unpin_userdata(s) dst_enablegc(dst_userdata_header(s)) - -void dst_mark(DstValue x); -void dst_sweep(Dst *vm); - -/* Collect some memory */ -void dst_collect(Dst *vm); - -/* Clear all memory. */ -void dst_clear_memory(Dst *vm); - -/* Run garbage collection if needed */ -#define dst_maybe_collect(Dst *vm) do {\ - if (vm->nextCollection >= vm->memoryInterval) dst_collect(vm); } while (0) - -#endif diff --git a/core/internal.h b/core/internal.h deleted file mode 100644 index bf941e6a..00000000 --- a/core/internal.h +++ /dev/null @@ -1,405 +0,0 @@ -/* -* Copyright (c) 2017 Calvin Rose -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to -* deal in the Software without restriction, including without limitation the -* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -* sell copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -* IN THE SOFTWARE. -*/ - -#ifndef DST_INTERNAL_H_defined -#define DST_INTERNAL_H_defined - -#include -#include -#include - -/* String utils */ -#define dst_string_raw(s) ((uint32_t *)(s) - 2) -#define dst_string_length(s) (dst_string_raw(s)[0]) -#define dst_string_hash(s) (dst_string_raw(s)[1]) - -/* Tuple utils */ -#define dst_tuple_raw(t) ((uint32_t *)(t) - 2) -#define dst_tuple_length(t) (dst_tuple_raw(t)[0]) -#define dst_tuple_hash(t) (dst_tuple_raw(t)[1]) - -/* Struct utils */ -#define dst_struct_raw(t) ((uint32_t *)(t) - 2) -#define dst_struct_length(t) (dst_struct_raw(t)[0]) -#define dst_struct_capacity(t) (dst_struct_length(t) * 4) -#define dst_struct_hash(t) (dst_struct_raw(t)[1]) - -/* Userdata utils */ -#define dst_udata_header(u) ((DstUserdataHeader *)(u) - 1) -#define dst_udata_type(u) (dst_udata_header(u)->type) -#define dst_udata_size(u) (dst_udata_header(u)->size) - -/* Stack frame manipulation */ - -/* Size of stack frame in number of values */ -#define DST_FRAME_SIZE 5 - -/* Prevent some recursive functions from recursing too deeply - * ands crashing. */ -#define DST_RECURSION_GUARD 1000 - -/* Macros for referencing a stack frame given a stack */ -#define dst_frame_callee(s) (*(s - 1)) -#define dst_frame_size(s) ((s - 2)->data.dwords[0]) -#define dst_frame_prevsize(s) ((s - 2)->data.dwords[1]) -#define dst_frame_args(s) ((s - 3)->data.dwords[0]) -#define dst_frame_ret(s) ((s - 3)->data.dwords[1]) -#define dst_frame_pc(s) ((s - 4)->data.u16p) -#define dst_frame_env(s) ((s - 5)->data.env) - -/* C function helpers */ - -/* Return in a c function */ -#define dst_c_return(vm, x) do { (vm)->ret = (x); return DST_RETURN_OK; } while (0) - -/* Throw error from a c function */ -#define dst_c_throw(vm, e) do { (vm)->ret = (e); return DST_RETURN_ERROR; } while (0) - -/* Throw c string error from a c function */ -#define dst_c_throwc(vm, e) dst_c_throw((vm), dst_string_cv((vm), (e))) - -/* Assert from a c function */ -#define dst_c_assert(vm, cond, e) do { if (cond) dst_c_throw((vm), (e)); } while (0) - -/* What to do when out of memory */ -#ifndef DST_OUT_OF_MEMORY -#include -#define DST_OUT_OF_MEMORY do { printf("out of memory\n"); exit(1); } while (0) -#endif - -/* A general dst value type */ -typedef struct DstValue DstValue; - -/* All of the dst types */ -typedef int DstBoolean; -typedef struct DstFunction DstFunction; -typedef struct DstArray DstArray; -typedef struct DstBuffer DstBuffer; -typedef struct DstTable DstTable; -typedef struct DstThread DstThread; - -/* Other structs */ -typedef struct DstUserdataHeader DstUserdataHeader; -typedef struct DstFuncDef DstFuncDef; -typedef struct DstFuncEnv DstFuncEnv; -typedef union DstValueUnion DstValueUnion; -typedef struct DstUserType DstUserType; -typedef struct DstParser DstParser; -typedef struct DstParseState DstParseState; - -/* Union datatype */ -union DstValueUnion { - DstBoolean boolean; - double real; - int64_t integer; - DstArray *array; - DstBuffer *buffer; - DstTable *table; - DstThread *thread; - const DstValue *tuple; - DstCFunction cfunction; - DstFunction *function; - const DstValue *st; - const uint8_t *string; - /* Indirectly used union members */ - uint16_t *u16p; - uint32_t dwords[2]; - uint16_t words[4]; - uint8_t bytes[8]; - void *pointer; - const char *cstring; -}; - -/* The general dst value type. Contains a large union and - * the type information of the value */ -struct DstValue { - DstType type; - DstValueUnion as; -}; - -/* A lightweight green thread in dst. Does not correspond to - * operating system threads. */ -struct DstThread { - uint32_t count; - uint32_t capacity; - DstValue *data; - DstThread *parent; - enum { - DST_THREAD_PENDING = 0, - DST_THREAD_ALIVE, - DST_THREAD_DEAD, - DST_THREAD_ERROR - } status; -}; - -/* A dynamic array type. */ -struct DstArray { - uint32_t count; - uint32_t capacity; - DstValue *data; -}; - -/* A bytebuffer type. Used as a mutable string or string builder. */ -struct DstBuffer { - uint32_t count; - uint32_t capacity; - uint8_t *data; -}; - -/* A mutable associative data type. Backed by a hashtable. */ -struct DstTable { - uint32_t count; - uint32_t capacity; - uint32_t deleted; - DstValue *data; -}; - -/* Some function defintion flags */ -#define DST_FUNCDEF_FLAG_VARARG 1 -#define DST_FUNCDEF_FLAG_NEEDSENV 4 - -/* A function definition. Contains information needed to instantiate closures. */ -struct DstFuncDef { - uint32_t flags; - uint32_t locals; /* The amount of stack space required for the function */ - uint32_t arity; /* Not including varargs */ - uint32_t literalsLen; /* Number of literals */ - uint32_t byteCodeLen; /* Length of bytcode */ - uint32_t envLen; /* Number of environments */ - - uint32_t *envSizes; /* Minimum sizes of environments (for static analysis) */ - uint32_t *envCaptures; /* Which environments to capture from parent. Is a bitset with the parent's envLen bits. */ - DstValue *literals; /* Contains strings, FuncDefs, etc. */ - uint32_t *byteCode; -}; - -/* A fuction environment */ -struct DstFuncEnv { - DstThread *thread; /* When nil, index the local values */ - uint32_t stackOffset; /* Used as environment size when off stack */ - DstValue *values; -}; - -/* A function */ -struct DstFunction { - DstFuncDef *def; - DstFuncEnv *envs; -}; - -/* Defines a type for userdata */ -struct DstUserType { - const char *name; - int (*serialize)(Dst *vm, void *data, uint32_t len); - int (*deserialize)(Dst *vm); - void (*finalize)(Dst *vm, void *data, uint32_t len); -}; - -/* Contains information about userdata */ -struct DstUserdataHeader { - uint32_t size; - const DstUserType *type; -}; - -/* The VM state */ -struct Dst { - /* Garbage collection */ - void *blocks; - uint32_t memoryInterval; - uint32_t nextCollection; - uint32_t black : 1; - /* Immutable value cache */ - DstValue *cache; - uint32_t cache_capacity; - uint32_t cache_count; - uint32_t cache_deleted; - /* GC roots */ - DstThread *thread; - DstTable *modules; - DstTable *registry; - DstTable *env; - /* Return state */ - DstValue ret; - uint32_t flags; -}; - -/* Bytecode */ -enum DstOpCode { - DST_OP_FLS, /* Load false */ - DST_OP_TRU, /* Load true */ - DST_OP_NIL, /* Load nil */ - DST_OP_UPV, /* Load upvalue */ - DST_OP_JIF, /* Jump if */ - DST_OP_JMP, /* Jump */ - DST_OP_SUV, /* Set upvalue */ - DST_OP_CST, /* Load constant */ - DST_OP_I16, /* Load 16 bit signed integer */ - DST_OP_I32, /* Load 32 bit signed integer */ - DST_OP_I64, /* Load 64 bit signed integer */ - DST_OP_F64, /* Load 64 bit IEEE double */ - DST_OP_MOV, /* Move value */ - DST_OP_CLN, /* Create a closure */ - DST_OP_ARR, /* Create array */ - DST_OP_DIC, /* Create object */ - DST_OP_TUP, /* Create tuple */ - DST_OP_RET, /* Return from function */ - DST_OP_RTN, /* Return nil */ - DST_OP_PSK, /* Push stack */ - DST_OP_PAR, /* Push array or tuple */ - DST_OP_CAL, /* Call function */ - DST_OP_TCL, /* Tail call */ - DST_OP_TRN /* Transfer to new thread */ -}; - -/****/ -/* Value Stack Manipulation */ -/****/ -void dst_switchv(Dst *vm, DstValue x); -DstValue dst_popv(Dst *vm); -DstValue dst_peekv(Dst *vm); -void dst_pushv(Dst *vm, DstValue x); -DstValue dst_getv(Dst *vm, int64_t index); -void dst_setv(Dst *vm, int64_t index, DstValue x); - -/****/ -/* Buffer functions */ -/****/ -void dst_buffer_ensure_(Dst *vm, DstBuffer *buffer, uint32_t capacity); -void dst_buffer_push_u8_(Dst *vm, DstBuffer *buffer, uint8_t x); -void dst_buffer_push_u16_(Dst *vm, DstBuffer *buffer, uint16_t x); -void dst_buffer_push_u32_(Dst *vm, DstBuffer *buffer, uint32_t x); -void dst_buffer_push_u64_(Dst *vm, DstBuffer *buffer, uint64_t x); -void dst_buffer_push_bytes_(Dst *vm, DstBuffer *buffer, const uint8_t *bytes, uint32_t len); -void dst_buffer_push_cstring_(Dst *vm, DstBuffer *buffer, const char *string); - -/****/ -/* Array functions */ -/****/ -DstArray *dst_make_array(Dst *vm, uint32_t capacity); -void dst_array_ensure_(Dst *vm, DstArray *array, uint32_t capacity); - -/****/ -/* Tuple functions */ -/****/ - -DstValue *dst_tuple_begin(Dst *vm, uint32_t length); -const DstValue *dst_tuple_end(Dst *vm, DstValue *tuple); - -/****/ -/* String/Symbol functions */ -/****/ - -const uint8_t *dst_string_b(Dst *vm, const uint8_t *buf, uint32_t len); -const uint8_t *dst_string_c(Dst *vm, const char *cstring); -DstValue dst_string_cv(Dst *vm, const char *string); -DstValue dst_string_cvs(Dst *vm, const char *string); -int dst_string_compare(const uint8_t *lhs, const uint8_t *rhs); -const uint8_t *dst_string_bu(Dst *vm, const uint8_t *buf, uint32_t len); -const uint8_t *dst_string_cu(Dst *vm, const char *s); - -/****/ -/* Struct functions */ -/****/ - -DstValue *dst_struct_begin(Dst *vm, uint32_t count); -void dst_struct_put(DstValue *st, DstValue key, DstValue value); -const DstValue *dst_struct_end(Dst *vm, DstValue *st); -DstValue dst_struct_get(const DstValue *st, DstValue key); -DstValue dst_struct_next(const DstValue *st, DstValue key); - -/****/ -/* Table functions */ -/****/ - -DstTable *dst_make_table(Dst *vm, uint32_t capacity); -DstValue dst_table_get(DstTable *t, DstValue key); -DstValue dst_table_remove(DstTable *t, DstValue key); -void dst_table_put(Dst *vm, DstTable *t, DstValue key, DstValue value); -DstValue dst_table_next(DstTable *o, DstValue key); - -/****/ -/* Threads */ -/****/ - -#define dst_thread_stack(t) ((t)->data + (t)->count) -DstThread *dst_thread(Dst *vm, DstValue callee, uint32_t capacity); -DstThread *dst_thread_reset(Dst *vm, DstThread *thread, DstValue callee); -void dst_thread_ensure_extra(Dst *vm, DstThread *thread, uint32_t extra); -void dst_thread_push(Dst *vm, DstThread *thread, DstValue x); -void dst_thread_pushnil(Dst *vm, DstThread *thread, uint32_t n); -void dst_thread_tuplepack(Dst *vm, DstThread *thread, uint32_t n); -DstValue *dst_thread_beginframe(Dst *vm, DstThread *thread, DstValue callee, uint32_t arity); -void dst_thread_endframe(Dst *vm, DstThread *thread); -DstValue *dst_thread_popframe(Dst *vm, DstThread *thread); -uint32_t dst_thread_countframes(DstThread *thread); - -/****/ -/* Serialization */ -/****/ - -const char *dst_deserialize_internal( - Dst *vm, - const uint8_t *data, - uint32_t len, - DstValue *out, - const uint8_t **nextData); - -const char *dst_serialize_internal(Dst *vm, DstBuffer *buffer, DstValue x); - -/* Treat similar types through uniform interfaces for iteration */ -int dst_seq_view(DstValue seq, const DstValue **data, uint32_t *len); -int dst_chararray_view(DstValue str, const uint8_t **data, uint32_t *len); -int dst_hashtable_view(DstValue tab, const DstValue **data, uint32_t *cap); - -/****/ -/* Parse functions */ -/****/ - -#define PARSE_OK 0 -#define PARSE_ERROR 1 -#define PARSE_UNEXPECTED_EOS 2 - -int dst_parseb(Dst *vm, const uint8_t *src, const uint8_t **newsrc, uint32_t len); - -/* Parse a c string */ -int dst_parsec(Dst *vm, const char *src, const char **newsrc); - -/* Parse a DST char seq (Buffer, String, Symbol) */ -int dst_parse(Dst *vm); - -/****/ -/* Value functions */ -/****/ - -int dst_truthy(DstValue v); -int dst_equals(DstValue x, DstValue y); -uint32_t dst_hash(DstValue x); -int dst_compare(DstValue x, DstValue y); -uint32_t dst_calchash_array(const DstValue *array, uint32_t len); - -/****/ -/* Helper functions */ -/****/ -uint32_t dst_startrange(int64_t index, uint32_t modulo); -uint32_t dst_endrange(int64_t index, uint32_t modulo); -int64_t dst_normalize_index(Dst *vm, int64_t index); - -#endif /* DST_INTERNAL_H_defined */ diff --git a/core/opcodes.h b/core/opcodes.h new file mode 100644 index 00000000..e2187c99 --- /dev/null +++ b/core/opcodes.h @@ -0,0 +1,85 @@ +/* +* Copyright (c) 2017 Calvin Rose +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to +* deal in the Software without restriction, including without limitation the +* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +* sell copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +* IN THE SOFTWARE. +*/ + +#ifndef DST_OPCODES_H_defined +#define DST_OPCODES_H_defined + +typedef enum DstOpCode DstOpCode; +enum DstOpCode { + DOP_NOOP, + DOP_ERROR, + DOP_TYPECHECK, + DOP_RETURN, + DOP_RETURN_NIL, + DOP_COERCE_INTEGER, + DOP_COERCE_REAL, + DOP_COERCE_STRING, + DOP_ADD_INTEGER, + DOP_ADD_IMMEDIATE, + DOP_ADD_REAL, + DOP_ADD, + DOP_SUBTRACT_INTEGER, + DOP_SUBTRACT_REAL, + DOP_SUBTRACT, + DOP_MULTIPLY_INTEGER, + DOP_MULTIPLY_IMMEDIATE, + DOP_MULTIPLY_REAL, + DOP_MULTIPLY, + DOP_DIVIDE_INTEGER, + DOP_DIVIDE_IMMEDIATE, + DOP_DIVIDE_REAL, + DOP_DIVIDE, + DOP_BAND, + DOP_BOR, + DOP_BXOR, + DOP_BNOT, + DOP_SHIFT_LEFT, + DOP_SHIFT_LEFT_IMMEDIATE, + DOP_SHIFT_RIGHT, + DOP_SHIFT_RIGHT_IMMEDIATE, + DOP_SHIFT_RIGHT_UNSIGNED, + DOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE, + DOP_MOVE, + DOP_JUMP, + DOP_JUMP_IF, + DOP_GREATER_THAN, + DOP_EQUALS, + DOP_COMPARE, + DOP_LOAD_NIL, + DOP_LOAD_BOOLEAN, + DOP_LOAD_INTEGER, + DOP_LOAD_CONSTANT, + DOP_LOAD_UPVALUE, + DOP_SET_UPVALUE, + DOP_CLOSURE, + DOP_PUSH, + DOP_PUSH_2, + DOP_PUSH_3, + DOP_PUSH_ARRAY, + DOP_CALL, + DOP_TAILCALL, + DOP_SYSCALL, + DOP_LOAD_SYSCALL, + DOP_TRANSFER +}; + +#endif diff --git a/core/parse.c b/core/parse.c index 194c0dff..0642b1d4 100644 --- a/core/parse.c +++ b/core/parse.c @@ -20,7 +20,7 @@ * IN THE SOFTWARE. */ -#include "internal.h" +#include /* Get an integer power of 10 */ static double exp10(int power) { @@ -115,10 +115,12 @@ static int check_str_const(const char *ref, const uint8_t *start, const uint8_t } /* Quote a value */ -static void quote(Dst *vm) { - dst_cstring(vm, "quote"); - dst_hoist(vm, -2); - dst_tuple(vm, 2); +static DstValue quote(DstValue x) { + DstValue sym = dst_wrap_symbol(dst_cstring("quote")); + DstValue *t = dst_tuple_begin(2); + t[0] = sym; + t[1] = x; + return dst_wrap_tuple(dst_tuple_end(t)); } /* Check if a character is whitespace */ @@ -161,11 +163,14 @@ static int to_hex(uint8_t c) { } typedef struct { - Dst *vm; + DstFiber *fiber; const uint8_t *end; const char *errmsg; - int64_t sourcemap; - int status; + enum { + DST_PARSE_OK, + DST_PARSE_ERROR, + DST_PARSE_UNEPECTED_EOS + } status; } ParseArgs; /* Entry point of the recursive descent parser */ @@ -174,9 +179,9 @@ static const uint8_t *parse_recur( const uint8_t *src, uint32_t recur) { - Dst *vm = args->vm; const uint8_t *end = args->end; uint32_t qcount = 0; + DstValue ret; /* Prevent stack overflow */ if (recur == 0) goto too_much_recur; @@ -205,20 +210,20 @@ static const uint8_t *parse_recur( tokenend++; if (tokenend >= end) goto unexpected_eos; if (read_integer(src, tokenend, &integer)) { - dst_integer(vm, integer); + ret = dst_wrap_integer(integer); } else if (read_real(src, tokenend, &real, 0)) { - dst_real(vm, real); + ret = dst_wrap_real(real); } else if (check_str_const("nil", src, tokenend)) { - dst_nil(vm); + ret = dst_wrap_nil(); } else if (check_str_const("false", src, tokenend)) { - dst_false(vm); + ret = dst_wrap_boolean(0); } else if (check_str_const("true", src, tokenend)) { - dst_true(vm); + ret = dst_wrap_boolean(1); } else { if (*src >= '0' && *src <= '9') { goto sym_nodigits; } else { - dst_symbol(vm, src, tokenend - src); + ret = dst_wrap_symbol(dst_string(src, tokenend - src)); } } src = tokenend; @@ -230,7 +235,7 @@ static const uint8_t *parse_recur( while (tokenend < end && is_symbol_char(*tokenend)) tokenend++; if (tokenend >= end) goto unexpected_eos; - dst_string(vm, src, tokenend - src); + ret = dst_wrap_string(dst_string(src, tokenend - src)); src = tokenend; break; } @@ -256,7 +261,7 @@ static const uint8_t *parse_recur( } } if (containsEscape) { - uint8_t *buf = dst_string_begin(vm, len); + uint8_t *buf = dst_string_begin(len); uint8_t *write = buf; while (src < strend) { if (*src == '\\') { @@ -285,9 +290,9 @@ static const uint8_t *parse_recur( *write++ = *src++; } } - dst_string_end(vm, buf); + ret = dst_wrap_string(dst_string_end(buf)); } else { - dst_string(vm, strstart, strend - strstart); + ret = dst_wrap_string(dst_string(strstart, strend - strstart)); src = strend + 1; } break; @@ -297,7 +302,7 @@ static const uint8_t *parse_recur( case '(': case '[': case '{': { - uint32_t n = 0; + uint32_t n = 0, i = 0; uint8_t close; switch (*src++) { case '[': close = ']'; break; @@ -316,22 +321,36 @@ static const uint8_t *parse_recur( src++; switch (close) { case ')': - dst_tuple(vm, n); - break; case ']': - dst_arrayn(vm, n); + { + DstValue *tup = dst_tuple_begin(n); + for (i = n; i > 0; i--) + tup[i - 1] = dst_fiber_popvalue(args->fiber); + ret = dst_wrap_tuple(dst_tuple_end(tup)); break; + } case '}': + { if (n & 1) goto struct_oddargs; - dst_struct(vm, n/2); + DstValue *st = dst_struct_begin(n >> 1); + for (i = n; i > 0; i -= 2) { + DstValue val = dst_fiber_popvalue(args->fiber); + DstValue key = dst_fiber_popvalue(args->fiber); + dst_struct_put(st, key, val); + } + ret = dst_wrap_struct(dst_struct_end(st)); break; + } } break; } } /* Quote the returned value qcount times */ - while (qcount--) quote(vm); + while (qcount--) ret = quote(ret); + + /* Push the result to the stack */ + dst_fiber_push(args->fiber, ret); /* Return the new source position for further calls */ return src; @@ -340,88 +359,77 @@ static const uint8_t *parse_recur( unexpected_eos: args->errmsg = "unexpected end of source"; - args->status = PARSE_UNEXPECTED_EOS; + args->status = DST_PARSE_UNEXPECTED_EOS; return NULL; unexpected_character: args->errmsg = "unexpected character"; - args->status = PARSE_ERROR; + args->status = DST_PARSE_ERROR; return src; sym_nodigits: args->errmsg = "symbols cannot start with digits"; - args->status = PARSE_ERROR; + args->status = DST_PARSE_ERROR; return src; struct_oddargs: args->errmsg = "struct literal needs an even number of arguments"; - args->status = PARSE_ERROR; + args->status = DST_PARSE_ERROR; return src; unknown_strescape: args->errmsg = "unknown string escape sequence"; - args->status = PARSE_ERROR; + args->status = DST_PARSE_ERROR; return src; invalid_hex: args->errmsg = "invalid hex escape in string"; - args->status = PARSE_ERROR; + args->status = DST_PARSE_ERROR; return src; too_much_recur: args->errmsg = "recursed too deeply in parsing"; - args->status = PARSE_ERROR; + args->status = DST_PARSE_ERROR; return src; } -/* Parse an array of bytes */ -int dst_parseb(Dst *vm, const uint8_t *src, const uint8_t **newsrc, uint32_t len) { +/* Parse an array of bytes. Return value in the fiber return value. */ +int dst_parse(const uint8_t *src, uint32_t len, const uint8_t **newsrc) { ParseArgs args; - uint32_t nargs; + DstFiber *fiber = dst_vm_fiber; - /* Create a source map */ - dst_table(vm, 10); + /* Save original stack top */ + uint32_t oldstacktop = fiber->stacktop; - nargs = dst_stacksize(vm); - - args.vm = vm; - args.status = PARSE_OK; + args.fiber = fiber; + args.status = DST_PARSE_OK; args.end = src + len; args.errmsg = NULL; - args.sourcemap = dst_normalize_index(vm, -1); - src = parse_recur(&args, src, 2048); - if (newsrc) *newsrc = src; + src = parse_recur(&args, src, DST_RECURSION_GUARD); + if (NULL != newsrc) *newsrc = src; if (args.errmsg) { - /* Unwind the stack */ - dst_trimstack(vm, nargs); - dst_cstring(vm, args.errmsg); + fiber->ret = dst_wrap_string(dst_cstring(args.errmsg)); + } else { + fiber->ret = dst_fiber_popvalue(fiber); } + /* Reset stacktop */ + fiber->stacktop = oldstacktop; + return args.status; } /* Parse a c string */ -int dst_parsec(Dst *vm, const char *src, const char **newsrc) { +int dst_parsec(const char *src, const char **newsrc) { uint32_t len = 0; const uint8_t *ns = NULL; int status; while (src[len]) ++len; - status = dst_parseb(vm, (const uint8_t *)src, &ns, len); + status = dst_parse((const uint8_t *)src, len, &ns); if (newsrc) { *newsrc = (const char *)ns; } return status; } - -/* Parse a DST char seq (Buffer, String, Symbol) */ -int dst_parse(Dst *vm) { - int status; - uint32_t len; - const uint8_t *bytes = dst_bytes(vm, -1, &len); - status = dst_parseb(vm, bytes, NULL, len); - dst_swap(vm, -1, -2); - dst_pop(vm); - return status; -} diff --git a/core/string.c b/core/string.c index bf582ab7..a28e340d 100644 --- a/core/string.c +++ b/core/string.c @@ -20,28 +20,8 @@ * IN THE SOFTWARE. */ -#include "internal.h" +#include #include "cache.h" -#include "wrap.h" -#include "gc.h" - -static const char *types[] = { - "nil", - "real", - "integer", - "boolean", - "string", - "symbol", - "array", - "tuple", - "table", - "struct", - "thread", - "buffer", - "function", - "cfunction", - "userdata" -}; static const char base64[] = "0123456789" @@ -59,8 +39,8 @@ static uint32_t dst_string_calchash(const uint8_t *str, uint32_t len) { } /* Begin building a string */ -uint8_t *dst_string_begin(Dst *vm, uint32_t length) { - char *data = dst_alloc(vm, DST_MEMORY_NONE, 2 * sizeof(uint32_t) + length + 1); +uint8_t *dst_string_begin(uint32_t length) { + char *data = dst_alloc(DST_MEMORY_NONE, 2 * sizeof(uint32_t) + length + 1); uint8_t *str = (uint8_t *) (data + 2 * sizeof(uint32_t)); dst_string_length(str) = length; str[length] = 0; @@ -68,28 +48,32 @@ uint8_t *dst_string_begin(Dst *vm, uint32_t length) { } /* Finish building a string */ -const uint8_t *dst_string_end(Dst *vm, uint8_t *str) { - DstValue check = dst_string_calchash(str, dst_string_length(str)); - const uint8_t *ret = dst_cache_add(vm, dst_wrap_string(check)).as.string; - gc_settype(dst_string_raw(ret), DST_MEMORY_STRING); - return ret; +const uint8_t *dst_string_end(uint8_t *str) { + DstValue check; + dst_string_hash(str) = dst_string_calchash(str, dst_string_length(str)); + check = dst_cache_add(dst_wrap_string(str)); + /* Don't tag the memory of the string builder directly. If the string is + * already cached, we don't want the gc to remove it from cache when the original + * string builder is gced (check will contained the cached string) */ + dst_gc_settype(dst_string_raw(check.as.string), DST_MEMORY_STRING); + return check.as.string; } /* Load a buffer as a string */ -static const uint8_t *dst_string(Dst *vm, const uint8_t *buf, uint32_t len) { +const uint8_t *dst_string(const uint8_t *buf, uint32_t len) { uint32_t hash = dst_string_calchash(buf, len); int status = 0; - DstValue *bucket = dst_cache_strfind(vm, buf, len, hash, &status); + DstValue *bucket = dst_cache_strfind(buf, len, hash, &status); if (status) { - return bucket->data.string; + return bucket->as.string; } else { uint32_t newbufsize = len + 2 * sizeof(uint32_t) + 1; - uint8_t *str = (uint8_t *)(dst_alloc(vm, DST_MEMORY_STRING, newbufsize) + 2 * sizeof(uint32_t)); - dst_memcpy(str, buf, len); + uint8_t *str = (uint8_t *)(dst_alloc(DST_MEMORY_STRING, newbufsize) + 2 * sizeof(uint32_t)); + memcpy(str, buf, len); dst_string_length(str) = len; dst_string_hash(str) = hash; str[len] = 0; - return dst_cache_add_bucket(vm, dst_wrap_string(str), bucket).as.string; + return dst_cache_add_bucket(dst_wrap_string(str), bucket).as.string; } } @@ -111,7 +95,7 @@ static void inc_counter(uint8_t *digits, int base, int len) { /* Generate a unique symbol. This is used in the library function gensym. The * symbol string data does not have GC enabled on it yet. You must manuallyb enable * it later. */ -const uint8_t *dst_string_unique(Dst *vm, const uint8_t *buf, uint32_t len) { +const uint8_t *dst_string_unique(const uint8_t *buf, uint32_t len) { DstValue *bucket; uint32_t hash; uint8_t counter[6] = {63, 63, 63, 63, 63, 63}; @@ -119,7 +103,7 @@ const uint8_t *dst_string_unique(Dst *vm, const uint8_t *buf, uint32_t len) { * is enough for resolving collisions. */ uint32_t newlen = len + 8; uint32_t newbufsize = newlen + 2 * sizeof(uint32_t) + 1; - uint8_t *str = (uint8_t *)(dst_alloc(vm, DST_MEMORY_STRING, newbufsize) + 2 * sizeof(uint32_t)); + uint8_t *str = (uint8_t *)(dst_alloc(DST_MEMORY_STRING, newbufsize) + 2 * sizeof(uint32_t)); dst_string_length(str) = newlen; memcpy(str, buf, len); str[len] = '-'; @@ -133,24 +117,24 @@ const uint8_t *dst_string_unique(Dst *vm, const uint8_t *buf, uint32_t len) { for (i = 0; i < 6; ++i) saltbuf[i] = base64[counter[i]]; hash = dst_string_calchash(str, newlen); - bucket = dst_cache_strfind(vm, str, newlen, hash, &status); + bucket = dst_cache_strfind(str, newlen, hash, &status); } dst_string_hash(str) = hash; - return dst_cache_add_bucket(vm, dst_wrap_string(str), bucket).as.string; + return dst_cache_add_bucket(dst_wrap_string(str), bucket).as.string; } /* Generate a unique string from a cstring */ -const uint8_t *dst_cstring_unique(Dst *vm, const char *s) { +const uint8_t *dst_cstring_unique(const char *s) { uint32_t len = 0; while (s[len]) ++len; - return dst_string_unique(vm, (const uint8_t *)s, len); + return dst_string_unique((const uint8_t *)s, len); } /* Load a c string */ -const uint8_t *dst_cstring(Dst *vm, const char *str) { +const uint8_t *dst_cstring(const char *str) { uint32_t len = 0; while (str[len]) ++len; - return dst_string(vm, (const uint8_t *)str, len); + return dst_string((const uint8_t *)str, len); } /* Temporary buffer size */ @@ -161,14 +145,14 @@ static uint32_t real_to_string_impl(uint8_t *buf, double x) { return (uint32_t) count; } -static void real_to_string_b(Dst *vm, DstBuffer *buffer, double x) { - dst_buffer_ensure_(vm, buffer, buffer->count + DST_BUFSIZE); +static void real_to_string_b(DstBuffer *buffer, double x) { + dst_buffer_ensure(buffer, buffer->count + DST_BUFSIZE); buffer->count += real_to_string_impl(buffer->data + buffer->count, x); } -static void real_to_string(Dst *vm, double x) { +static const uint8_t *real_to_string(double x) { uint8_t buf[DST_BUFSIZE]; - dst_string(vm, buf, real_to_string_impl(buf, x)); + return dst_string(buf, real_to_string_impl(buf, x)); } static uint32_t integer_to_string_impl(uint8_t *buf, int64_t x) { @@ -201,14 +185,14 @@ static uint32_t integer_to_string_impl(uint8_t *buf, int64_t x) { return count; } -static void integer_to_string_b(Dst *vm, DstBuffer *buffer, int64_t x) { - dst_buffer_extra(vm, buffer, DST_BUFSIZE); +static void integer_to_string_b(DstBuffer *buffer, int64_t x) { + dst_buffer_extra(buffer, DST_BUFSIZE); buffer->count += integer_to_string_impl(buffer->data + buffer->count, x); } -static void integer_to_string(Dst *vm, int64_t x) { +static const uint8_t *integer_to_string(int64_t x) { uint8_t buf[DST_BUFSIZE]; - dst_string(vm, buf, integer_to_string_impl(buf, x)); + return dst_string(buf, integer_to_string_impl(buf, x)); } #define HEX(i) (((uint8_t *) base64)[(i)]) @@ -240,14 +224,16 @@ static uint32_t string_description_impl(uint8_t *buf, const char *title, void *p return (uint32_t) (c - buf); } -static void string_description_b(Dst *vm, DstBuffer *buffer, const char *title, void *pointer) { - dst_buffer_ensure_(vm, buffer, buffer->count + DST_BUFSIZE); +static void string_description_b(DstBuffer *buffer, const char *title, void *pointer) { + dst_buffer_ensure(buffer, buffer->count + DST_BUFSIZE); buffer->count += string_description_impl(buffer->data + buffer->count, title, pointer); } -static const uint8_t *string_description(Dst *vm, const char *title, void *pointer) { +/* Describes a pointer with a title (string_description("bork", myp) returns + * a string "") */ +static const uint8_t *string_description(const char *title, void *pointer) { uint8_t buf[DST_BUFSIZE]; - return dst_string(vm, buf, string_description_impl(buf, title, pointer)); + return dst_string(buf, string_description_impl(buf, title, pointer)); } #undef HEX @@ -302,82 +288,82 @@ static void dst_escape_string_impl(uint8_t *buf, const uint8_t *str) { buf[j++] = '"'; } -static void dst_escape_string_b(Dst *vm, DstBuffer *buffer, const uint8_t *str) { +void dst_escape_string_b(DstBuffer *buffer, const uint8_t *str) { uint32_t len = dst_escape_string_length(str); - dst_buffer_extra(vm, buffer, len); + dst_buffer_extra(buffer, len); dst_escape_string_impl(buffer->data + buffer->count, str); buffer->count += len; } -static const uint8_t *dst_escape_string(Dst *vm, const uint8_t *str) { +const uint8_t *dst_escape_string(const uint8_t *str) { uint32_t len = dst_escape_string_length(str); - uint8_t *buf = dst_string_begin(vm, len); + uint8_t *buf = dst_string_begin(len); dst_escape_string_impl(buf, str); - return dst_string_end(vm, buf); + return dst_string_end(buf); } /* Returns a string pointer with the description of the string */ -static const uint8_t *dst_short_description(Dst *vm) { +const uint8_t *dst_short_description(DstValue x) { switch (x.type) { case DST_NIL: - return dst_cstring(vm, "nil"); + return dst_cstring("nil"); case DST_BOOLEAN: if (x.as.boolean) - return dst_cstring(vm, "true"); + return dst_cstring("true"); else - return dst_cstring(vm, "false"); + return dst_cstring("false"); case DST_REAL: - return real_to_string(vm, x.as.real); + return real_to_string(x.as.real); case DST_INTEGER: - return integer_to_string(vm, x.as.integer); + return integer_to_string(x.as.integer); case DST_SYMBOL: return x.as.string; case DST_STRING: - return dst_escape_string(vm, x.as.string); + return dst_escape_string(x.as.string); case DST_USERDATA: - return string_description(vm, dst_udata_type(x.as.pointer)->name, x.as.pointer); + return string_description(dst_userdata_type(x.as.pointer)->name, x.as.pointer); default: - return string_description(vm, types[x.type], x.as.pointer); + return string_description(dst_type_names[x.type], x.as.pointer); } } -static void dst_short_description_b(Dst *vm, DstBuffer *buffer, DstValue x) { +void dst_short_description_b(DstBuffer *buffer, DstValue x) { switch (x.type) { case DST_NIL: - dst_buffer_push_cstring(vm, buffer, "nil"); + dst_buffer_push_cstring(buffer, "nil"); return; case DST_BOOLEAN: if (x.as.boolean) - dst_buffer_push_cstring(vm, buffer, "true"); + dst_buffer_push_cstring(buffer, "true"); else - dst_buffer_push_cstring(vm, buffer, "false"); + dst_buffer_push_cstring(buffer, "false"); return; case DST_REAL: - real_to_string_b(vm, buffer, x.as.real); + real_to_string_b(buffer, x.as.real); return; case DST_INTEGER: - integer_to_string_b(vm, buffer, x.as.integer); + integer_to_string_b(buffer, x.as.integer); return; case DST_SYMBOL: - dst_buffer_push_bytes(vm, buffer, x.as.string, dst_string_length(x.as.string)); + dst_buffer_push_bytes(buffer, x.as.string, dst_string_length(x.as.string)); return; case DST_STRING: - dst_escape_string_b(vm, buffer, x.as.string); + dst_escape_string_b(buffer, x.as.string); return; case DST_USERDATA: - string_description_b(vm, buffer, dst_udata_type(x.as.pointer)->name, x.as.pointer); + string_description_b(buffer, dst_userdata_type(x.as.pointer)->name, x.as.pointer); return; default: - string_description_b(vm, buffer, types[x.type], x.as.pointer); + string_description_b(buffer, dst_type_names[x.type], x.as.pointer); break; } } /* Static debug print helper */ -static int64_t dst_description_helper(Dst *vm, DstBuffer *b, DstTable *seen, DstValue x, int64_t next, int depth) { +static int64_t dst_description_helper(DstBuffer *b, DstTable *seen, DstValue x, int64_t next, int depth) { DstValue check = dst_table_get(seen, x); if (check.type == DST_INTEGER) { - dst_buffer_push_cstring(vm, b, ""); + dst_buffer_push_cstring(b, ""); } else { const char *open; const char *close; @@ -385,7 +371,7 @@ static int64_t dst_description_helper(Dst *vm, DstBuffer *b, DstTable *seen, Dst const DstValue *data; switch (x.type) { default: - dst_short_description_b(vm, b, x); + dst_short_description_b(b, x); return next; case DST_STRUCT: open = "{"; close = "}"; @@ -400,10 +386,10 @@ static int64_t dst_description_helper(Dst *vm, DstBuffer *b, DstTable *seen, Dst open = "["; close = "]"; break; } - dst_table_put(vm, seen, x, dst_wrap_integer(next++)); - dst_buffer_push_cstring(vm, b, open); + dst_table_put(seen, x, dst_wrap_integer(next++)); + dst_buffer_push_cstring(b, open); if (depth == 0) { - dst_buffer_push_cstring(vm, b, "..."); + dst_buffer_push_cstring(b, "..."); } else if (dst_hashtable_view(x, &data, &len)) { int isfirst = 1; for (i = 0; i < len; i += 2) { @@ -411,45 +397,45 @@ static int64_t dst_description_helper(Dst *vm, DstBuffer *b, DstTable *seen, Dst if (isfirst) isfirst = 0; else - dst_buffer_push_u8(vm, b, ' '); - next = dst_description_helper(vm, b, seen, data[i], next, depth - 1); - dst_buffer_push_u8(vm, b, ' '); - next = dst_description_helper(vm, b, seen, data[i + 1], next, depth - 1); + dst_buffer_push_u8(b, ' '); + next = dst_description_helper(b, seen, data[i], next, depth - 1); + dst_buffer_push_u8(b, ' '); + next = dst_description_helper(b, seen, data[i + 1], next, depth - 1); } } } else if (dst_seq_view(x, &data, &len)) { for (i = 0; i < len; ++i) { - next = dst_description_helper(vm, b, seen, data[i], next, depth - 1); + next = dst_description_helper(b, seen, data[i], next, depth - 1); if (i != len - 1) - dst_buffer_push_u8(vm, b, ' '); + dst_buffer_push_u8(b, ' '); } } - dst_buffer_push_cstring(vm, b, close); + dst_buffer_push_cstring(b, close); } return next; } /* Debug print. Returns a description of an object as a string. */ -const uint8_t *dst_description(Dst *vm, DstValue x) { - DstBuffer *buf = dst_buffer(vm, 10); - DstTable *seen = dst_table(vm, 10); +const uint8_t *dst_description(DstValue x) { + DstBuffer *buf = dst_buffer(10); + DstTable *seen = dst_table(10); /* Only print description up to a depth of 4 */ - dst_description_helper(vm, buf, seen, x, 0, 4); + dst_description_helper(buf, seen, x, 0, 4); - return dst_string(vm, buf->data, buf->count); + return dst_string(buf->data, buf->count); } -/* Convert any value to a dst string */ -const uint8_t *dst_to_string(Dst *vm, DstValue x) { - DstValue x = dst_getv(vm, -1); +/* Convert any value to a dst string. Similar to description, but + * strings, symbols, and buffers will return their content. */ +const uint8_t *dst_to_string(DstValue x) { switch (x.type) { default: - return dst_description(vm, x); + return dst_description(x); case DST_STRING: case DST_SYMBOL: return x.as.string; case DST_BUFFER: - return dst_string(vm, x.as.buffer->data, x.as.buffer->count); + return dst_string(x.as.buffer->data, x.as.buffer->count); } } diff --git a/core/struct.c b/core/struct.c index dc300412..c3b9be3c 100644 --- a/core/struct.c +++ b/core/struct.c @@ -20,11 +20,11 @@ * IN THE SOFTWARE. */ -#include "internal.h" +#include #include "cache.h" /* Begin creation of a struct */ -DstValue *dst_struct_begin(Dst *vm, uint32_t count) { +DstValue *dst_struct_begin(uint32_t count) { /* This expression determines size of structs. It must be a pure * function of count, and hold at least enough space for count * key value pairs. The minimum it could be is @@ -32,8 +32,8 @@ DstValue *dst_struct_begin(Dst *vm, uint32_t count) { * ensures that structs are less likely to have hash collisions. If more space * is added or s is changed, change the macro dst_struct_capacity in internal.h */ size_t s = sizeof(uint32_t) * 2 + 4 * count * sizeof(DstValue); - char *data = dst_alloc(vm, DST_MEMORY_STRUCT, s); - memset(data, 0, s) + char *data = dst_alloc(DST_MEMORY_STRUCT, s); + memset(data, 0, s); DstValue *st = (DstValue *) (data + 2 * sizeof(uint32_t)); dst_struct_length(st) = count; /* Use the hash storage space as a counter to see how many items @@ -47,7 +47,7 @@ DstValue *dst_struct_begin(Dst *vm, uint32_t count) { /* Find an item in a struct */ static const DstValue *dst_struct_find(const DstValue *st, DstValue key) { uint32_t cap = dst_struct_capacity(st); - uint32_t index = (dst_value_hash(key) % (cap / 2)) * 2; + uint32_t index = (dst_hash(key) % (cap / 2)) * 2; uint32_t i; for (i = index; i < cap; i += 2) if (st[i].type == DST_NIL || dst_equals(st[i], key)) @@ -122,11 +122,8 @@ void dst_struct_put(DstValue *st, DstValue key, DstValue value) { } /* Finish building a struct */ -static const DstValue *dst_struct_end(Dst *vm, DstValue *st) { - DstValue cached; +const DstValue *dst_struct_end(DstValue *st) { DstValue check; - /* For explicit tail recursion */ - recur: if (dst_struct_hash(st) != dst_struct_length(st)) { /* Error building struct, probably duplicate values. We need to rebuild * the struct using only the values that went in. The second creation should always @@ -137,19 +134,18 @@ static const DstValue *dst_struct_end(Dst *vm, DstValue *st) { for (i = 0; i < dst_struct_capacity(st); i += 2) { realCount += st[i].type != DST_NIL; } - newst = dst_struct_begin(vm, realCount); + newst = dst_struct_begin(realCount); for (i = 0; i < dst_struct_capacity(st); i += 2) { if (st[i].type != DST_NIL) { dst_struct_put(newst, st[i], st[i + 1]); } } st = newst; - goto recur; } dst_struct_hash(st) = dst_calchash_array(st, dst_struct_capacity(st)); - check.type = DST_STRUCT; - check.as.st = (const DstValue *) st; - return dst_cache_add(vm, check).as.st; + check = dst_cache_add(dst_wrap_struct(st)); + dst_gc_settype(dst_tuple_raw(check.as.st), DST_MEMORY_STRUCT); + return check.as.st; } /* Get an item from a struct */ diff --git a/core/syscalls.c b/core/syscalls.c new file mode 100644 index 00000000..e4df0494 --- /dev/null +++ b/core/syscalls.c @@ -0,0 +1,33 @@ +/* +* Copyright (c) 2017 Calvin Rose +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to +* deal in the Software without restriction, including without limitation the +* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +* sell copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +* IN THE SOFTWARE. +*/ + + +#include + +int dst_print(DstFiber *fiber, DstValue *argv, uint32_t argn) { + printf("Hello!\n"); + return 0; +} + +DstCFunction dst_vm_syscalls[256] = { + dst_print +}; diff --git a/core/table.c b/core/table.c index a5bd44df..7932eb08 100644 --- a/core/table.c +++ b/core/table.c @@ -20,15 +20,13 @@ * IN THE SOFTWARE. */ -#include "internal.h" -#include "wrap.h" -#include "gc.h" +#include /* Initialize a table */ -DstTable *dst_table(Dst *vm, uint32_t capacity) { - DstTable *table = dst_alloc(vm, DST_MEMORY_TABLE, sizeof(DstTable)); - DstValue *data = calloc(sizeof(DstValue), capacity); +DstTable *dst_table_init(DstTable *table, uint32_t capacity) { + DstValue *data; if (capacity < 2) capacity = 2; + data = calloc(sizeof(DstValue), capacity); if (NULL == data) { DST_OUT_OF_MEMORY; } @@ -39,6 +37,17 @@ DstTable *dst_table(Dst *vm, uint32_t capacity) { return table; } +/* Deinitialize a table */ +void dst_table_deinit(DstTable *table) { + free(table->data); +} + +/* Create a new table */ +DstTable *dst_table(uint32_t capacity) { + DstTable *table = dst_alloc(DST_MEMORY_TABLE, sizeof(DstTable)); + return dst_table_init(table, capacity); +} + /* Find the bucket that contains the given key. Will also return * bucket where key should go if not in the table. */ static DstValue *dst_table_find(DstTable *t, DstValue key) { @@ -63,7 +72,7 @@ static DstValue *dst_table_find(DstTable *t, DstValue key) { } /* Resize the dictionary table. */ -static void dst_table_rehash(Dst *vm, DstTable *t, uint32_t size) { +static void dst_table_rehash(DstTable *t, uint32_t size) { DstValue *olddata = t->data; DstValue *newdata = calloc(sizeof(DstValue), size); if (NULL == newdata) { @@ -110,7 +119,7 @@ DstValue dst_table_remove(DstTable *t, DstValue key) { } /* Put a value into the object */ -void dst_table_put(Dst *vm, DstTable *t, DstValue key, DstValue value) { +void dst_table_put(DstTable *t, DstValue key, DstValue value) { if (key.type == DST_NIL) return; if (value.type == DST_NIL) { dst_table_remove(t, key); @@ -120,7 +129,7 @@ void dst_table_put(Dst *vm, DstTable *t, DstValue key, DstValue value) { bucket[1] = value; } else { if (!bucket || 4 * (t->count + t->deleted) >= t->capacity) { - dst_table_rehash(vm, t, 4 * t->count + 6); + dst_table_rehash(t, 4 * t->count + 6); } bucket = dst_table_find(t, key); if (bucket[1].type == DST_BOOLEAN) diff --git a/core/thread.c b/core/thread.c deleted file mode 100644 index b736269c..00000000 --- a/core/thread.c +++ /dev/null @@ -1,200 +0,0 @@ -/* -* Copyright (c) 2017 Calvin Rose -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to -* deal in the Software without restriction, including without limitation the -* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -* sell copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -* IN THE SOFTWARE. -*/ - -#include "internal.h" -#include "wrap.h" -#include "gc.h" - -/* Initialize a new thread */ -DstThread *dst_thread(Dst *vm, DstValue callee, uint32_t capacity) { - DstThread *thread = dst_alloc(vm, DST_MEMORY_THREAD, sizeof(DstThread)); - if (capacity < DST_FRAME_SIZE) capacity = DST_FRAME_SIZE; - thread->capacity = capacity; - DstValue *data = malloc(vm, sizeof(DstValue) * capacity); - if (NULL == data) { - DST_OUT_OF_MEMORY; - } - thread->data = data; - return dst_thread_reset(vm, thread, callee); -} - -/* Clear a thread (reset it) */ -DstThread *dst_thread_reset(Dst *vm, DstThread *thread, DstValue callee) { - DstValue *stack; - thread->count = DST_FRAME_SIZE; - thread->status = DST_THREAD_PENDING; - stack = thread->data + DST_FRAME_SIZE; - dst_frame_size(stack) = 0; - dst_frame_prevsize(stack) = 0; - dst_frame_ret(stack) = 0; - dst_frame_args(stack) = 0; - dst_frame_pc(stack) = NULL; - dst_frame_env(stack) = NULL; - dst_frame_callee(stack) = callee; - dst_thread_endframe(vm, thread); - thread->parent = NULL; /* Need to set parent manually */ - return thread; -} - -/* Ensure that the thread has enough extra capacity */ -void dst_thread_ensure_extra(Dst *vm, DstThread *thread, uint32_t extra) { - DstValue *newData, *stack; - uint32_t usedCapacity, neededCapacity, newCapacity; - stack = thread->data + thread->count; - usedCapacity = thread->count + dst_frame_size(stack) + DST_FRAME_SIZE; - neededCapacity = usedCapacity + extra; - if (thread->capacity >= neededCapacity) return; - newCapacity = 2 * neededCapacity; - - newData = realloc(thread->data, sizeof(DstValue) * newCapacity); - if (NULL == newData) { - DST_OUT_OF_MEMORY; - } - - thread->data = newData; - thread->capacity = newCapacity; -} - -/* Push a value on the current stack frame*/ -void dst_thread_push(Dst *vm, DstThread *thread, DstValue x) { - DstValue *stack; - dst_thread_ensure_extra(vm, thread, 1); - stack = thread->data + thread->count; - stack[dst_frame_size(stack)++] = x; -} - -/* Push n nils onto the stack */ -void dst_thread_pushnil(Dst *vm, DstThread *thread, uint32_t n) { - DstValue *stack, *current, *end; - dst_thread_ensure_extra(vm, thread, n); - stack = thread->data + thread->count; - current = stack + dst_frame_size(stack); - end = current + n; - for (; current < end; ++current) { - current->type = DST_NIL; - } - dst_frame_size(stack) += n; -} - -/* Package up extra args after and including n into tuple at n. Used for - * packing up varargs to variadic functions. */ -void dst_thread_tuplepack(Dst *vm, DstThread *thread, uint32_t n) { - DstValue *stack = thread->data + thread->count; - uint32_t size = dst_frame_size(stack); - if (n >= size) { - /* Push one extra nil to ensure space for tuple */ - dst_thread_pushnil(vm, thread, n - size + 1); - stack = thread->data + thread->count; - stack[n].type = DST_TUPLE; - stack[n].as.tuple = dst_tuple_end(vm, dst_tuple_begin(vm, 0)); - dst_frame_size(stack) = n + 1; - } else { - uint32_t i; - DstValue *tuple = dst_tuple_begin(vm, size - n); - for (i = n; i < size; ++i) - tuple[i - n] = stack[i]; - stack[n].type = DST_TUPLE; - stack[n].as.tuple = dst_tuple_end(vm, tuple); - } -} - -/* Push a stack frame to a thread, with space for arity arguments. Returns the new - * stack. */ -DstValue *dst_thread_beginframe(Dst *vm, DstThread *thread, DstValue callee, uint32_t arity) { - uint32_t frameOffset; - DstValue *oldStack, *newStack; - - /* Push the frame */ - dst_thread_ensure_extra(vm, thread, DST_FRAME_SIZE + arity + 4); - oldStack = thread->data + thread->count; - frameOffset = dst_frame_size(oldStack) + DST_FRAME_SIZE; - newStack = oldStack + frameOffset; - dst_frame_prevsize(newStack) = dst_frame_size(oldStack); - dst_frame_env(newStack) = NULL; - dst_frame_size(newStack) = 0; - dst_frame_callee(newStack) = callee; - thread->count += frameOffset; - - /* Ensure the extra space and initialize to nil */ - dst_thread_pushnil(vm, thread, arity); - - /* Return ok */ - return thread->data + thread->count; -} - -/* After pushing arguments to a stack frame created with dst_thread_beginframe, call this - * to finalize the frame before starting a function call. */ -void dst_thread_endframe(Dst *vm, DstThread *thread) { - DstValue *stack = thread->data + thread->count; - DstValue callee = dst_frame_callee(stack); - if (callee.type == DST_FUNCTION) { - DstFunction *fn = callee.as.function; - uint32_t locals = fn->def->locals; - dst_frame_pc(stack) = fn->def->byteCode; - if (fn->def->flags & DST_FUNCDEF_FLAG_VARARG) { - uint32_t arity = fn->def->arity; - dst_thread_tuplepack(vm, thread, arity); - } - if (dst_frame_size(stack) < locals) { - dst_thread_pushnil(vm, thread, locals - dst_frame_size(stack)); - } - } -} - -/* Pop a stack frame from the thread. Returns the new stack frame, or - * NULL if there are no more frames */ -DstValue *dst_thread_popframe(Dst *vm, DstThread *thread) { - DstValue *stack = thread->data + thread->count; - uint32_t prevsize = dst_frame_prevsize(stack); - DstValue *nextstack = stack - DST_FRAME_SIZE - prevsize; - DstFuncEnv *env = dst_frame_env(stack); - - /* Check for closures */ - if (env != NULL) { - uint32_t size = dst_frame_size(stack); - env->thread = NULL; - env->stackOffset = size; - env->values = malloc(sizeof(DstValue) * size); - memcpy(env->values, stack, sizeof(DstValue) * size); - } - - /* Shrink stack */ - thread->count -= DST_FRAME_SIZE + prevsize; - - /* Check if the stack is empty, and if so, return null */ - if (thread->count) - return nextstack; - else - return NULL; -} - -/* Count the number of stack frames in a thread */ -uint32_t dst_thread_countframes(DstThread *thread) { - uint32_t count = 0; - const DstValue *stack = thread->data + DST_FRAME_SIZE; - const DstValue *laststack = thread->data + thread->count; - while (stack <= laststack) { - ++count; - stack += dst_frame_size(stack) + DST_FRAME_SIZE; - } - return count; -} diff --git a/core/tuple.c b/core/tuple.c index 3a4c0158..462475c4 100644 --- a/core/tuple.c +++ b/core/tuple.c @@ -20,28 +20,31 @@ * IN THE SOFTWARE. */ -#include "internal.h" +#include #include "cache.h" -#include "wrap.h" /* Create a new empty tuple of the given size. This will return memory * which should be filled with DstValues. The memory will not be collected until * dst_tuple_end is called. */ -DstValue *dst_tuple_begin(Dst *vm, uint32_t length) { - char *data = dst_alloc(vm, DST_MEMORY_TUPLE, 2 * sizeof(uint32_t) + length * sizeof(DstValue)); +DstValue *dst_tuple_begin(uint32_t length) { + char *data = dst_alloc(DST_MEMORY_NONE, 2 * sizeof(uint32_t) + length * sizeof(DstValue)); DstValue *tuple = (DstValue *)(data + (2 * sizeof(uint32_t))); dst_tuple_length(tuple) = length; return tuple; } /* Finish building a tuple */ -const DstValue *dst_tuple_end(Dst *vm, DstValue *tuple) { +const DstValue *dst_tuple_end(DstValue *tuple) { + DstValue check; dst_tuple_hash(tuple) = dst_calchash_array(tuple, dst_tuple_length(tuple)); - return dst_cache_add(vm, dst_wrap_tuple((const DstValue *) tuple)).as.tuple; + check = dst_cache_add(dst_wrap_tuple((const DstValue *) tuple)); + dst_gc_settype(dst_tuple_raw(check.as.tuple), DST_MEMORY_TUPLE); + return check.as.tuple; } -const DstValue *dst_tuple_n(Dst *vm, DstValue *values, uint32_t n) { - DstValue *t = dst_tuple_begin(vm, n); +/* Build a tuple with n values */ +const DstValue *dst_tuple_n(DstValue *values, uint32_t n) { + DstValue *t = dst_tuple_begin(n); memcpy(t, values, sizeof(DstValue) * n); - return dst_tuple_end(vm, t); + return dst_tuple_end(t); } diff --git a/core/userdata.c b/core/userdata.c index 5253a388..ca7e5cda 100644 --- a/core/userdata.c +++ b/core/userdata.c @@ -20,16 +20,14 @@ * IN THE SOFTWARE. */ -#include "internal.h" -#include "wrap.h" +#include /* Create new userdata */ -void *dst_userdata(Dst *vm, uint32_t size, const DstUserType *utype) { - DstValue ud; - char *data = dst_alloc(vm, DST_USERDATA, sizeof(DstUserdataHeader) + size); +void *dst_userdata(uint32_t size, const DstUserType *utype) { + char *data = dst_alloc(DST_MEMORY_USERDATA, sizeof(DstUserdataHeader) + size); DstUserdataHeader *header = (DstUserdataHeader *)data; void *user = data + sizeof(DstUserdataHeader); header->size = size; header->type = utype; - return dst_wrap_userdata(user); + return user; } diff --git a/core/util.c b/core/util.c index 700309e8..83723dc6 100644 --- a/core/util.c +++ b/core/util.c @@ -20,23 +20,28 @@ * IN THE SOFTWARE. */ -#include "internal.h" +#include -int dst_checkerr(Dst *vm) { return !!vm->flags; } -void dst_return(Dst *vm, DstValue x) { - vm->ret = x; -} - -void dst_throw(Dst *vm) { - vm->flags = 1; - vm->ret = dst_popv(vm); -} - -void dst_cerr(Dst *vm, const char *message) { - vm->flags = 1; - vm->ret = dst_string_cv(vm, message); -} +/* The DST value types in order. These types can be used as + * mnemonics instead of a bit pattern for type checking */ +const char *dst_type_names[15] = { + "nil", + "real", + "integer", + "boolean", + "string", + "symbol", + "array", + "tuple", + "table", + "struct", + "fiber", + "buffer", + "function", + "cfunction", + "userdata" +}; /* Read both tuples and arrays as c pointers + uint32_t length. Return 1 if the * view can be constructed, 0 if an invalid type. */ @@ -84,6 +89,18 @@ int dst_hashtable_view(DstValue tab, const DstValue **data, uint32_t *cap) { return 0; } +/* Convert a real to int */ +int64_t dst_real_to_integer(double real) { + /* TODO - consider c undefined behavior */ + return (int64_t) real; +} + +/* Convert an integer to a real */ +double dst_integer_to_real(int64_t integer) { + /* TODO - consider c undefined behavior */ + return (double) integer; +} + /* Convert an index used by the capi to an absolute index */ uint32_t dst_startrange(int64_t index, uint32_t modulo) { if (index < 0) index += modulo; @@ -94,11 +111,3 @@ uint32_t dst_startrange(int64_t index, uint32_t modulo) { uint32_t dst_endrange(int64_t index, uint32_t modulo) { return dst_startrange(index, modulo + 1); } - -/* Convert a possibly negative index to a positive index on the current stack. */ -int64_t dst_normalize_index(Dst *vm, int64_t index) { - if (index < 0) { - index += dst_frame_size(dst_thread_stack(vm->thread)); - } - return index; -} diff --git a/core/value.c b/core/value.c index 1e993a13..c213f1eb 100644 --- a/core/value.c +++ b/core/value.c @@ -20,7 +20,7 @@ * IN THE SOFTWARE. */ -#include "internal.h" +#include /* * Define a number of functions that can be used internally on ANY DstValue. @@ -82,7 +82,8 @@ uint32_t dst_hash(DstValue x) { default: if (sizeof(double) == sizeof(void *)) { /* Assuming 8 byte pointer */ - hash = x.as.dwords[0] ^ x.as.dwords[1]; + uint64_t i = x.as.integer; + hash = (i >> 32) ^ (i & 0xFFFFFFFF); } else { /* Assuming 4 byte pointer (or smaller) */ hash = (uint32_t) x.as.pointer; @@ -97,7 +98,7 @@ uint32_t dst_calchash_array(const DstValue *array, uint32_t len) { const DstValue *end = array + len; uint32_t hash = 5381; while (array < end) - hash = (hash << 5) + hash + dst_value_hash(*array++); + hash = (hash << 5) + hash + dst_hash(*array++); return hash; } @@ -138,6 +139,15 @@ int dst_compare(DstValue x, DstValue y) { return x.as.boolean ? 1 : -1; } case DST_REAL: + + /* Check for nans to ensure total order */ + if (x.as.real != x.as.real) + return y.as.real != y.as.real + ? 0 + : -1; + if (y.as.real != y.as.real) + return 1; + if (x.as.real == y.as.real) { return 0; } else { @@ -159,7 +169,7 @@ int dst_compare(DstValue x, DstValue y) { uint32_t ylen = dst_tuple_length(y.as.tuple); uint32_t count = xlen < ylen ? xlen : ylen; for (i = 0; i < count; ++i) { - int comp = dst_value_compare(x.as.tuple[i], y.as.tuple[i]); + int comp = dst_compare(x.as.tuple[i], y.as.tuple[i]); if (comp != 0) return comp; } if (xlen < ylen) @@ -169,6 +179,7 @@ int dst_compare(DstValue x, DstValue y) { return 0; } break; + /* TODO - how should structs compare by default? For now, just use pointers. */ default: if (x.as.string == y.as.string) { return 0; diff --git a/core/vm.c b/core/vm.c index 9a62846e..b38d63ff 100644 --- a/core/vm.c +++ b/core/vm.c @@ -20,508 +20,661 @@ * IN THE SOFTWARE. */ -#include "internal.h" -#include "wrap.h" +#include +#include "opcodes.h" -static const char DST_NO_UPVALUE[] = "no upvalue"; -static const char DST_EXPECTED_FUNCTION[] = "expected function"; +/* VM State */ +DstFiber *dst_vm_fiber; /* Start running the VM from where it left off. */ -int dst_continue(Dst *vm) { +int dst_continue(DstFiber *fiber) { + /* VM state */ DstValue *stack; - uint16_t *pc; + uint32_t *pc; + DstFunction *func; - /* Some temporary values */ - DstValue temp, v1, v2; + /* Used to extract bits from the opcode that correspond to arguments. + * Pulls out unsigned integers */ +#define oparg(shift, mask) ((*pc >> ((shift) << 3)) & (mask)) -#define dst_exit(vm, r) return ((vm)->ret = (r), DST_RETURN_OK) -#define dst_error(vm, e) do { (vm)->ret = dst_string_cv((vm), (e)); goto vm_error; } while (0) -#define dst_assert(vm, cond, e) do {if (!(cond)){dst_error((vm), (e));}} while (0) +#define vm_throw(e) do { fiber->ret = dst_wrap_string(dst_cstring((e))); goto vm_error; } while (0) +#define vm_assert(cond, e) do {if (!(cond)) vm_throw((e)); } while (0) - /* Intialize local state */ - vm->thread->status = DST_THREAD_ALIVE; - stack = dst_thread_stack(vm->thread); - pc = dst_frame_pc(stack); +#define vm_binop_integer(op) \ + stack[oparg(1, 0xFF)] = dst_wrap_integer(\ + stack[oparg(2, 0xFF)].as.integer op stack[oparg(3, 0xFF)].as.integer\ + );\ + pc++;\ + continue; - /* Main interpreter loop */ +#define vm_binop_real(op)\ + stack[oparg(1, 0xFF)] = dst_wrap_real(\ + stack[oparg(2, 0xFF)].as.real op stack[oparg(3, 0xFF)].as.real\ + );\ + pc++;\ + continue; + +#define vm_binop_immediate(op)\ + stack[oparg(1, 0xFF)] = dst_wrap_integer(\ + stack[oparg(2, 0xFF)].as.integer op (*((int32_t *)pc) >> 24)\ + );\ + pc++;\ + continue; + +#define vm_binop(op)\ + {\ + DstValue op1 = stack[oparg(2, 0xFF)];\ + DstValue op2 = stack[oparg(3, 0xFF)];\ + vm_assert(op1.type == DST_INTEGER || op1.type == DST_REAL, "expected number");\ + vm_assert(op2.type == DST_INTEGER || op2.type == DST_REAL, "expected number");\ + stack[oparg(1, 0xFF)] = op1.type == DST_INTEGER\ + ? op2.type == DST_INTEGER\ + ? dst_wrap_integer(op1.as.integer op op2.as.integer)\ + : dst_wrap_real(dst_integer_to_real(op1.as.integer) op op2.as.real)\ + : op2.type == DST_INTEGER\ + ? dst_wrap_real(op1.as.real op dst_integer_to_real(op2.as.integer))\ + : dst_wrap_real(op1.as.real op op2.as.real);\ + pc++;\ + continue;\ + } + +#define vm_init_fiber_state() \ + fiber->status = DST_FIBER_ALIVE;\ + stack = fiber->data + fiber->frame;\ + pc = dst_stack_frame(stack)->pc;\ + func = dst_stack_frame(stack)->func; + + vm_init_fiber_state(); + + /* Main interpreter loop. It is large, but it is + * is maintainable. Adding new opcodes is mostly just adding newcases + * to this loop, adding the opcode to opcodes.h, and adding it to the assembler. + * Some opcodes, especially ones that do arithmetic, are almost entirely + * templated by the above macros. */ for (;;) { - switch (*pc) { + switch (*pc & 0xFF) { default: - dst_error(vm, "unknown opcode"); + vm_throw("unknown opcode"); break; - case DST_OP_FLS: /* Load False */ - temp.type = DST_BOOLEAN; - temp.as.boolean = 0; - stack[pc[1]] = temp; - pc += 2; + case DOP_NOOP: + pc++; continue; - case DST_OP_TRU: /* Load True */ - temp.type = DST_BOOLEAN; - temp.as.boolean = 1; - stack[pc[1]] = temp; - pc += 2; + case DOP_ERROR: + fiber->ret = stack[oparg(1, 0xFF)]; + goto vm_error; + + case DOP_TYPECHECK: + vm_assert((1 << stack[oparg(1, 0xFF)].type) & oparg(2, 0xFFFF), + "typecheck failed"); + pc++; continue; - case DST_OP_NIL: /* Load Nil */ - temp.type = DST_NIL; - stack[pc[1]] = temp; - pc += 2; - continue; + case DOP_RETURN: + fiber->ret = stack[oparg(1, 0xFFFFFF)]; + goto vm_return; - case DST_OP_I16: /* Load Small Integer */ - temp.type = DST_INTEGER; - temp.as.integer = ((int16_t *)(pc))[2]; - stack[pc[1]] = temp; - pc += 3; - continue; + case DOP_RETURN_NIL: + fiber->ret.type = DST_NIL; + goto vm_return; - case DST_OP_UPV: /* Load Up Value */ - case DST_OP_SUV: /* Set Up Value */ - { - DstValue *upv; - DstFunction *fn; - DstFuncEnv *env; - uint16_t level = pc[2]; - temp = dst_frame_callee(stack); - dst_assert(vm, temp.type == DST_FUNCTION, DST_EXPECTED_FUNCTION); - fn = temp.as.function; - if (level == 0) - upv = stack + pc[3]; - else { - while (fn && --level) - fn = fn->parent; - dst_assert(vm, fn, DST_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] == DST_OP_UPV) { - stack[pc[1]] = *upv; - } else { - *upv = stack[pc[1]]; - } - pc += 4; - } - continue; - - case DST_OP_JIF: /* Jump If */ - if (dst_value_truthy(stack[pc[1]])) { - pc += 4; + case DOP_COERCE_INTEGER: + { + DstValue input = stack[oparg(2, 0xFFFF)]; + if (input.type == DST_INTEGER) { + stack[oparg(1, 0xFF)] = input; + } else if (input.type == DST_REAL) { + stack[oparg(1, 0xFF)] = dst_wrap_integer(dst_real_to_integer(input.as.real)); } else { - pc += *((int32_t *)(pc + 2)); + vm_throw("expected number"); } continue; + } - case DST_OP_JMP: /* Jump */ - pc += *((int32_t *)(pc + 1)); - continue; - - case DST_OP_CST: /* Load constant value */ - v1 = dst_frame_callee(stack); - dst_assert(vm, v1.type == DST_FUNCTION, DST_EXPECTED_FUNCTION); - if (pc[2] > v1.as.function->def->literalsLen) - dst_error(vm, DST_NO_UPVALUE); - stack[pc[1]] = v1.as.function->def->literals[pc[2]]; - pc += 3; - continue; - - case DST_OP_I32: /* Load 32 bit integer */ - temp.type = DST_INTEGER; - temp.as.integer = *((int32_t *)(pc + 2)); - stack[pc[1]] = temp; - pc += 4; - continue; - - case DST_OP_I64: /* Load 64 bit integer */ - temp.type = DST_INTEGER; - temp.as.integer = *((int64_t *)(pc + 2)); - stack[pc[1]] = temp; - pc += 6; - continue; - - case DST_OP_F64: /* Load 64 bit float */ - temp.type = DST_REAL; - temp.as.real = *((double *)(pc + 2)); - stack[pc[1]] = temp; - pc += 6; - continue; - - case DST_OP_MOV: /* Move Values */ - stack[pc[1]] = stack[pc[2]]; - pc += 3; - continue; - - case DST_OP_CLN: /* Create closure from constant FuncDef */ - { - DstFunction *fn; - v1 = dst_frame_callee(stack); - temp = v1.as.function->def->literals[pc[2]]; - if (temp.type != DST_FUNCDEF) - dst_error(vm, "cannot create closure from non-funcdef"); - fn = dst_mem_resumegc(dst_alloc(vm, sizeof(DstFunction))); - fn->def = temp.as.def; - /* Don't always set the parent. We might want to let the gc get it */ - if (temp.as.def->flags & DST_FUNCDEF_FLAG_NEEDSPARENT) - fn->parent = v1.as.function; - else - fn->parent = NULL; - if (v1.type != DST_FUNCTION) - dst_error(vm, DST_EXPECTED_FUNCTION); - if (dst_frame_env(stack) == NULL && (fn->def->flags & DST_FUNCDEF_FLAG_NEEDSENV)) { - dst_frame_env(stack) = dst_mem_resumegc(dst_alloc(vm, sizeof(DstFuncEnv))); - dst_frame_env(stack)->thread = vm->thread; - dst_frame_env(stack)->stackOffset = vm->thread->count; - dst_frame_env(stack)->values = NULL; - } - if (pc[2] > v1.as.function->def->literalsLen) - dst_error(vm, DST_NO_UPVALUE); - if (fn->def->flags & DST_FUNCDEF_FLAG_NEEDSENV) - fn->env = dst_frame_env(stack); - else - fn->env = NULL; - temp.type = DST_FUNCTION; - temp.as.function = fn; - stack[pc[1]] = temp; - pc += 3; + case DOP_COERCE_REAL: + { + DstValue input = stack[oparg(2, 0xFFFF)]; + if (input.type == DST_INTEGER) { + stack[oparg(1, 0xFF)] = dst_wrap_real(dst_integer_to_real(input.as.integer)); + } else if (input.type == DST_REAL) { + stack[oparg(1, 0xFF)] = input; + } else { + vm_throw("expected number"); } + continue; + } + + case DOP_COERCE_STRING: + stack[oparg(1, 0xFF)] = dst_wrap_string(dst_to_string(stack[oparg(2, 0xFFFF)])); + pc++; break; - case DST_OP_RTN: /* Return nil */ - temp.type = DST_NIL; - goto vm_return; + case DOP_ADD_INTEGER: + vm_binop_integer(+); - case DST_OP_RET: /* Return */ - temp = stack[pc[1]]; - goto vm_return; + case DOP_ADD_IMMEDIATE: + vm_binop_immediate(+); - case DST_OP_PSK: /* Push stack */ + case DOP_ADD_REAL: + vm_binop_real(+); + + case DOP_ADD: + vm_binop(+); + + case DOP_SUBTRACT_INTEGER: + vm_binop_integer(-); + + case DOP_SUBTRACT_REAL: + vm_binop_real(-); + + case DOP_SUBTRACT: + vm_binop(-); + + case DOP_MULTIPLY_INTEGER: + vm_binop_integer(*); + + case DOP_MULTIPLY_IMMEDIATE: + vm_binop_immediate(*); + + case DOP_MULTIPLY_REAL: + vm_binop_real(*); + + case DOP_MULTIPLY: + vm_binop(*); + + case DOP_DIVIDE_INTEGER: + vm_assert(stack[oparg(3, 0xFF)].as.integer != 0, "integer divide by zero"); + vm_assert(!(stack[oparg(3, 0xFF)].as.integer == -1 && + stack[oparg(2, 0xFF)].as.integer == DST_INTEGER_MIN), + "integer divide overflow"); + vm_binop_integer(/); + + case DOP_DIVIDE_IMMEDIATE: { - uint16_t arity = pc[1]; - uint16_t i; - uint16_t newBase = dst_frame_size(stack) + DST_FRAME_SIZE; - dst_frame_args(stack) = newBase; - dst_thread_ensure_extra(vm, vm->thread, DST_FRAME_SIZE + arity); - stack = dst_thread_stack(vm->thread); - dst_frame_size(stack) += DST_FRAME_SIZE + arity; - /* Nil stuff */ - for (i = 0; i < DST_FRAME_SIZE; ++i) - stack[newBase + i - DST_FRAME_SIZE].type = DST_NIL; - /* Write arguments */ - for (i = 0; i < arity; ++i) - stack[newBase + i] = stack[pc[2 + i]]; - pc += 2 + arity; + int64_t op1 = stack[oparg(2, 0xFF)].as.integer; + int64_t op2 = *((int32_t *)pc) >> 24; + /* Check for degenerate integer division (divide by zero, and dividing + * min value by -1). These checks could be omitted if the arg is not + * 0 or -1. */ + if (op2 == 0) + vm_throw("integer divide by zero"); + if (op2 == -1) + vm_throw("integer divide overflow"); + else + stack[oparg(1, 0xFF)] = dst_wrap_integer(op1 / op2); + pc++; + continue; } - break; - case DST_OP_PAR: /* Push array or tuple */ + case DOP_DIVIDE_REAL: + vm_binop_real(/); + + case DOP_DIVIDE: { - uint32_t count, i, oldsize; - const DstValue *data; - temp = stack[pc[1]]; - if (temp.type == DST_TUPLE) { - count = dst_tuple_length(temp.as.tuple); - data = temp.as.tuple; - } else if (temp.type == DST_ARRAY){ - count = temp.as.array->count; - data = temp.as.array->data; + DstValue op1 = stack[oparg(2, 0xFF)]; + DstValue op2 = stack[oparg(3, 0xFF)]; + vm_assert(op1.type == DST_INTEGER || op1.type == DST_REAL, "expected number"); + vm_assert(op2.type == DST_INTEGER || op2.type == DST_REAL, "expected number"); + if (op2.type == DST_INTEGER && op2.as.integer == 0) + op2 = dst_wrap_real(0.0); + if (op2.type == DST_INTEGER && op2.as.integer == -1 && + op1.type == DST_INTEGER && op1.as.integer == DST_INTEGER_MIN) + op2 = dst_wrap_real(-1); + stack[oparg(1, 0xFF)] = op1.type == DST_INTEGER + ? op2.type == DST_INTEGER + ? dst_wrap_integer(op1.as.integer / op2.as.integer) + : dst_wrap_real(dst_integer_to_real(op1.as.integer) / op2.as.real) + : op2.type == DST_INTEGER + ? dst_wrap_real(op1.as.real / dst_integer_to_real(op2.as.integer)) + : dst_wrap_real(op1.as.real / op2.as.real); + pc++; + continue; + } + + case DOP_BAND: + vm_binop_integer(&); + + case DOP_BOR: + vm_binop_integer(|); + + case DOP_BXOR: + vm_binop_integer(^); + + case DOP_BNOT: + stack[oparg(1, 0xFF)] = dst_wrap_integer(~stack[oparg(2, 0xFFFF)].as.integer); + continue; + + case DOP_SHIFT_RIGHT_UNSIGNED: + stack[oparg(1, 0xFF)] = dst_wrap_integer( + stack[oparg(2, 0xFF)].as.uinteger + >> + stack[oparg(3, 0xFF)].as.uinteger + ); + pc++; + continue; + + case DOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE: + stack[oparg(1, 0xFF)] = dst_wrap_integer( + stack[oparg(2, 0xFF)].as.uinteger >> oparg(3, 0xFF) + ); + pc++; + continue; + + case DOP_SHIFT_RIGHT: + vm_binop_integer(>>); + + case DOP_SHIFT_RIGHT_IMMEDIATE: + stack[oparg(1, 0xFF)] = dst_wrap_integer( + stack[oparg(2, 0xFF)].as.uinteger >> oparg(3, 0xFF) + ); + pc++; + continue; + + case DOP_SHIFT_LEFT: + vm_binop_integer(<<); + + case DOP_MOVE: + stack[oparg(1, 0xFF)] = stack[oparg(2, 0xFFFF)]; + pc++; + continue; + + case DOP_JUMP: + pc += (*(int32_t *)pc) >> 8; + continue; + + case DOP_JUMP_IF: + if (dst_truthy(stack[oparg(1, 0xFF)])) { + pc += (*(int32_t *)pc) >> 16; + } else { + pc++; + } + continue; + + case DOP_GREATER_THAN: + stack[oparg(1, 0xFF)].type = DST_BOOLEAN; + stack[oparg(1, 0xFF)].as.boolean = dst_compare( + stack[oparg(2, 0xFF)], + stack[oparg(3 ,0xFF)] + ) > 0; + pc++; + continue; + + case DOP_EQUALS: + stack[oparg(1, 0xFF)].type = DST_BOOLEAN; + stack[oparg(1, 0xFF)].as.boolean = dst_equals( + stack[oparg(2, 0xFF)], + stack[oparg(3 ,0xFF)] + ); + pc++; + continue; + + case DOP_COMPARE: + stack[oparg(1, 0xFF)].type = DST_INTEGER; + stack[oparg(1, 0xFF)].as.integer = dst_compare( + stack[oparg(2, 0xFF)], + stack[oparg(3 ,0xFF)] + ); + pc++; + continue; + + case DOP_LOAD_NIL: + stack[oparg(1, 0xFFFFFF)].type = DST_NIL; + pc++; + continue; + + case DOP_LOAD_BOOLEAN: + stack[oparg(1, 0xFF)] = dst_wrap_boolean(oparg(2, 0xFFFF)); + pc++; + continue; + + case DOP_LOAD_INTEGER: + stack[oparg(1, 0xFF)] = dst_wrap_integer(*((int32_t *)pc) >> 16); + pc++; + continue; + + case DOP_LOAD_CONSTANT: + vm_assert(oparg(2, 0xFFFF) < func->def->constants_length, "invalid constant"); + stack[oparg(1, 0xFF)] = func->def->constants[oparg(2, 0xFFFF)]; + pc++; + continue; + + case DOP_LOAD_UPVALUE: + { + uint32_t eindex = oparg(2, 0xFF); + uint32_t vindex = oparg(3, 0xFF); + DstFuncEnv *env; + vm_assert(func->def->environments_length > eindex, "invalid upvalue"); + env = func->envs[eindex]; + vm_assert(env->length > vindex, "invalid upvalue"); + if (env->offset) { + /* On stack */ + stack[oparg(1, 0xFF)] = env->as.fiber->data[env->offset + vindex]; } else { - dst_error(vm, "expected array or tuple"); + /* Off stack */ + stack[oparg(1, 0xFF)] = env->as.values[vindex]; } - oldsize = dst_frame_size(stack); - dst_thread_pushnil(vm, vm->thread, count); - stack = dst_thread_stack(vm->thread); - for (i = 0; i < count; ++i) - stack[oldsize + i] = data[i]; - /*dst_frame_size(stack) += count;*/ - pc += 2; + pc++; + continue; } + + case DOP_SET_UPVALUE: + { + uint32_t eindex = oparg(2, 0xFF); + uint32_t vindex = oparg(3, 0xFF); + DstFuncEnv *env; + vm_assert(func->def->environments_length > eindex, "invalid upvalue"); + env = func->envs[eindex]; + vm_assert(env->length > vindex, "invalid upvalue"); + if (env->offset) { + env->as.fiber->data[env->offset + vindex] = stack[oparg(1, 0xFF)]; + } else { + env->as.values[vindex] = stack[oparg(1, 0xFF)]; + } + pc++; + continue; + } + + case DOP_CLOSURE: + { + uint32_t i; + DstFunction *fn; + DstFuncDef *fd; + vm_assert(oparg(2, 0xFFFF) < func->def->constants_length, "invalid constant"); + vm_assert(func->def->constants[oparg(2, 0xFFFF)].type == DST_NIL, "constant must be funcdef"); + fd = (DstFuncDef *)(func->def->constants[oparg(2, 0xFFFF)].as.pointer); + fn = dst_alloc(DST_MEMORY_FUNCTION, sizeof(DstFunction)); + fn->envs = malloc(sizeof(DstFuncEnv *) * fd->environments_length); + if (NULL == fn->envs) { + DST_OUT_OF_MEMORY; + } + if (fd->flags & DST_FUNCDEF_FLAG_NEEDSENV) { + /* Delayed capture of current stack frame */ + DstFuncEnv *env = dst_alloc(DST_MEMORY_FUNCENV, sizeof(DstFuncEnv)); + env->offset = fiber->frame; + env->as.fiber = fiber; + env->length = func->def->slotcount; + fn->envs[0] = env; + } else { + fn->envs[0] = NULL; + } + for (i = 1; i < fd->environments_length; ++i) { + uint32_t inherit = fd->environments[i]; + fn->envs[i] = func->envs[inherit]; + } + stack[oparg(1, 0xFF)] = dst_wrap_function(fn); + pc++; + break; + } + + case DOP_PUSH: + dst_fiber_push(fiber, stack[oparg(1, 0xFFFFFF)]); + pc++; break; - case DST_OP_CAL: /* Call */ - { - uint16_t newStackIndex = dst_frame_args(stack); - uint16_t size = dst_frame_size(stack); - temp = stack[pc[1]]; - dst_frame_size(stack) = newStackIndex - DST_FRAME_SIZE; - dst_frame_ret(stack) = pc[2]; - dst_frame_pc(stack) = pc + 3; - if (newStackIndex < DST_FRAME_SIZE) - dst_error(vm, "invalid call instruction"); - vm->thread->count += newStackIndex; - stack = dst_thread_stack(vm->thread); - dst_frame_size(stack) = size - newStackIndex; - dst_frame_prevsize(stack) = newStackIndex - DST_FRAME_SIZE; - dst_frame_callee(stack) = temp; - } - goto common_function_call; + case DOP_PUSH_2: + dst_fiber_push2(fiber, + stack[oparg(1, 0xFF)], + stack[oparg(2, 0xFFFF)]); + pc++; + break;; - case DST_OP_TCL: /* Tail call */ + case DOP_PUSH_3: + dst_fiber_push3(fiber, + stack[oparg(1, 0xFF)], + stack[oparg(2, 0xFF)], + stack[oparg(3, 0xFF)]); + pc++; + break; + + case DOP_PUSH_ARRAY: { - uint16_t newStackIndex = dst_frame_args(stack); - uint16_t size = dst_frame_size(stack); - uint16_t i; - temp = stack[pc[1]]; - /* Check for closures */ - if (dst_frame_env(stack)) { - DstFuncEnv *env = dst_frame_env(stack); - env->thread = NULL; - env->stackOffset = size; - env->values = dst_mem_resumegc(dst_alloc(vm, sizeof(DstValue) * size)); - dst_memcpy(env->values, stack, sizeof(DstValue) * size); + uint32_t count; + const DstValue *array; + if (dst_seq_view(stack[oparg(1, 0xFFFFFF)], &array, &count)) { + dst_fiber_pushn(fiber, array, count); + } else { + vm_throw("expected array or tuple"); } - if (newStackIndex) - for (i = 0; i < size - newStackIndex; ++i) - stack[i] = stack[newStackIndex + i]; - dst_frame_size(stack) = size - newStackIndex; - dst_frame_callee(stack) = temp; + pc++; + break; } - goto common_function_call; - /* Code common to all function calls */ - common_function_call: - dst_frame_args(stack) = 0; - dst_frame_env(stack) = NULL; - dst_thread_endframe(vm, vm->thread); - stack = vm->thread->data + vm->thread->count; - temp = dst_frame_callee(stack); - if (temp.type == DST_FUNCTION) { - pc = temp.as.function->def->byteCode; - } else if (temp.type == DST_CFUNCTION) { - int status; - vm->ret.type = DST_NIL; - status = temp.as.cfunction(vm); - if (status) { + case DOP_CALL: + { + DstValue callee = stack[oparg(2, 0xFFFF)]; + if (callee.type == DST_FUNCTION) { + func = callee.as.function; + dst_fiber_funcframe(fiber, func); + stack = fiber->data + fiber->frame; + pc = func->def->bytecode; + break; + } else if (callee.type == DST_CFUNCTION) { + dst_fiber_cframe(fiber); + stack = fiber->data + fiber->frame; + fiber->ret.type = DST_NIL; + if (callee.as.cfunction(fiber, stack, fiber->frametop - fiber->frame)) { + dst_fiber_popframe(fiber); goto vm_error; } else { - temp = vm->ret; + dst_fiber_popframe(fiber); goto vm_return; } } else { - dst_error(vm, DST_EXPECTED_FUNCTION); + vm_throw("cannot call non-function type"); } break; + } - case DST_OP_ARR: /* Array literal */ - { - uint32_t i; - uint32_t arrayLen = pc[2]; - DstArray *array = dst_make_array(vm, arrayLen); - array->count = arrayLen; - for (i = 0; i < arrayLen; ++i) - array->data[i] = stack[pc[3 + i]]; - temp.type = DST_ARRAY; - temp.as.array = array; - stack[pc[1]] = temp; - pc += 3 + arrayLen; - } - break; - - case DST_OP_DIC: /* Table literal */ - { - uint32_t i = 3; - uint32_t kvs = pc[2]; - DstTable *t = dst_make_table(vm, 2 * kvs); - dst_mem_suspendgc(t); - dst_mem_suspendgc(t->data); - kvs = kvs + 3; - while (i < kvs) { - v1 = stack[pc[i++]]; - v2 = stack[pc[i++]]; - dst_table_put(vm, t, v1, v2); + case DOP_TAILCALL: + { + DstValue callee = stack[oparg(2, 0xFFFF)]; + if (callee.type == DST_FUNCTION) { + func = callee.as.function; + dst_fiber_funcframe_tail(fiber, func); + stack = fiber->data + fiber->frame; + pc = func->def->bytecode; + break; + } else if (callee.type == DST_CFUNCTION) { + dst_fiber_cframe_tail(fiber); + stack = fiber->data + fiber->frame; + fiber->ret.type = DST_NIL; + if (callee.as.cfunction(fiber, stack, fiber->frametop - fiber->frame)) { + dst_fiber_popframe(fiber); + goto vm_error; + } else { + dst_fiber_popframe(fiber); + goto vm_return; } - temp.type = DST_TABLE; - temp.as.table = t; - stack[pc[1]] = temp; - dst_mem_resumegc(t); - dst_mem_resumegc(t->data); - pc += kvs; + } else { + vm_throw("expected function"); } break; + } - case DST_OP_TUP: /* Tuple literal */ + case DOP_SYSCALL: { - uint32_t i; - uint32_t len = pc[2]; - DstValue *tuple = dst_tuple_begin(vm, len); - for (i = 0; i < len; ++i) - tuple[i] = stack[pc[3 + i]]; - temp.type = DST_TUPLE; - temp.as.tuple = dst_tuple_end(vm, tuple); - stack[pc[1]] = temp; - pc += 3 + len; + DstCFunction f = dst_vm_syscalls[oparg(2, 0xFF)]; + vm_assert(NULL != f, "invalid syscall"); + dst_fiber_cframe(fiber); + stack = fiber->data + fiber->frame; + fiber->ret.type = DST_NIL; + if (f(fiber, stack, fiber->frametop - fiber->frame)) { + dst_fiber_popframe(fiber); + goto vm_error; + } else { + dst_fiber_popframe(fiber); + goto vm_return; + } + continue; } - break; - case DST_OP_TRN: /* Transfer */ - temp = stack[pc[2]]; /* The thread */ - v1 = stack[pc[3]]; /* The value to pass in */ - if (temp.type != DST_THREAD && temp.type != DST_NIL) - dst_error(vm, "expected thread"); - if (temp.type == DST_NIL && vm->thread->parent) { - temp.type = DST_THREAD; - temp.as.thread = vm->thread->parent; + case DOP_LOAD_SYSCALL: + { + DstCFunction f = dst_vm_syscalls[oparg(2, 0xFF)]; + vm_assert(NULL != f, "invalid syscall"); + stack[oparg(1, 0xFF)] = dst_wrap_cfunction(f); + pc++; + continue; } - if (temp.type == DST_THREAD) { - if (temp.as.thread->status != DST_THREAD_PENDING) - dst_error(vm, "can only enter pending thread"); - } - dst_frame_ret(stack) = pc[1]; - vm->thread->status = DST_THREAD_PENDING; - dst_frame_pc(stack) = pc + 4; - if (temp.type == DST_NIL) { - vm->ret = v1; + + case DOP_TRANSFER: + { + DstFiber *nextfiber; + DstStackFrame *frame = dst_stack_frame(stack); + DstValue temp = stack[oparg(2, 0xFF)]; + DstValue retvalue = stack[oparg(3, 0xFF)]; + vm_assert(temp.type == DST_FIBER || + temp.type == DST_NIL, "expected fiber"); + nextfiber = temp.type == DST_FIBER + ? temp.as.fiber + : fiber->parent; + /* Check for root fiber */ + if (NULL == nextfiber) { + frame->pc = pc; + fiber->ret = retvalue; return 0; } - temp.as.thread->status = DST_THREAD_ALIVE; - vm->thread = temp.as.thread; - stack = dst_thread_stack(temp.as.thread); - if (dst_frame_callee(stack).type != DST_FUNCTION) - goto vm_return; - stack[dst_frame_ret(stack)] = v1; - pc = dst_frame_pc(stack); + vm_assert(nextfiber->status == DST_FIBER_PENDING, "can only transfer to pending fiber"); + frame->pc = pc; + fiber->status = DST_FIBER_PENDING; + fiber = nextfiber; + vm_init_fiber_state(); + stack[oparg(1, 0xFF)] = retvalue; + pc++; continue; + } - /* Handle returning from stack frame. Expect return value in temp. */ + /* Handle returning from stack frame. Expect return value in fiber->ret */ vm_return: - stack = dst_thread_popframe(vm, vm->thread); - while (vm->thread->count < DST_FRAME_SIZE || - vm->thread->status == DST_THREAD_DEAD || - vm->thread->status == DST_THREAD_ERROR) { - vm->thread->status = DST_THREAD_DEAD; - if (vm->thread->parent) { - vm->thread = vm->thread->parent; - if (vm->thread->status == DST_THREAD_ALIVE) { + { + DstValue ret = fiber->ret; + dst_fiber_popframe(fiber); + while (fiber->frame || + fiber->status == DST_FIBER_DEAD || + fiber->status == DST_FIBER_ERROR) { + fiber->status = DST_FIBER_DEAD; + if (fiber->parent) { + fiber = fiber->parent; + if (fiber->status == DST_FIBER_ALIVE) { /* If the parent thread is still alive, we are inside a cfunction */ - vm->ret = temp; return 0; } - stack = vm->thread->data + vm->thread->count; + stack = fiber->data + fiber->frame; } else { - vm->ret = temp; return 0; } } - vm->thread->status = DST_THREAD_ALIVE; - pc = dst_frame_pc(stack); - stack[dst_frame_ret(stack)] = temp; + fiber->status = DST_FIBER_ALIVE; + stack = fiber->data + fiber->frame; + pc = dst_stack_frame(stack)->pc; + stack[oparg(1, 0xFF)] = ret; + pc++; continue; + } /* Handle errors from c functions and vm opcodes */ vm_error: - vm->thread->status = DST_THREAD_ERROR; - while (vm->thread->count < DST_FRAME_SIZE || - vm->thread->status == DST_THREAD_DEAD || - vm->thread->status == DST_THREAD_ERROR) { - if (vm->thread->parent == NULL) + { + DstValue ret = fiber->ret; + fiber->status = DST_FIBER_ERROR; + while (fiber->frame || + fiber->status == DST_FIBER_DEAD || + fiber->status == DST_FIBER_ERROR) { + if (fiber->parent == NULL) return 1; - vm->thread = vm->thread->parent; - if (vm->thread->status == DST_THREAD_ALIVE) { + fiber = fiber->parent; + if (fiber->status == DST_FIBER_ALIVE) { /* If the parent thread is still alive, we are inside a cfunction */ return 1; } } - vm->thread->status = DST_THREAD_ALIVE; - stack = vm->thread->data + vm->thread->count; - stack[dst_frame_ret(stack)] = vm->ret; - pc = dst_frame_pc(stack); + fiber->status = DST_FIBER_ALIVE; + stack = fiber->data + fiber->frame; + pc = dst_stack_frame(stack)->pc; + stack[oparg(1, 0xFF)] = ret; + pc++; continue; - + } + } /* end switch */ /* Check for collection every cycle. If the instruction definitely does * not allocate memory, it can use continue instead of break to * skip this check */ - dst_maybe_collect(vm); + dst_maybe_collect(); } /* end for */ +#undef oparg + +#undef vm_error +#undef vm_assert +#undef vm_binop +#undef vm_binop_real +#undef vm_binop_integer +#undef vm_binop_immediate +#undef vm_init_fiber_state + } /* Run the vm with a given function. This function is * called to start the vm. */ -int dst_run(Dst *vm, DstValue callee) { +int dst_run(DstValue callee) { int result; - if (vm->thread && - (vm->thread->status == DST_THREAD_DEAD || - vm->thread->status == DST_THREAD_ALIVE)) { - /* Reuse old thread */ - dst_thread_reset(vm, vm->thread, callee); - } else { - /* Create new thread */ - vm->thread = dst_thread(vm, callee, 64); - } if (callee.type == DST_CFUNCTION) { - vm->ret.type = DST_NIL; - result = callee.as.cfunction(vm); + dst_vm_fiber = dst_fiber(NULL, 0); + dst_vm_fiber->ret.type = DST_NIL; + dst_fiber_cframe(dst_vm_fiber); + result = callee.as.cfunction(dst_vm_fiber, dst_vm_fiber->data + dst_vm_fiber->frame, 0); } else if (callee.type == DST_FUNCTION) { - result = dst_continue(vm); + dst_vm_fiber = dst_fiber(callee.as.function, 64); + result = dst_continue(dst_vm_fiber); } else { - vm->ret = dst_string_cv(vm, "expected function"); - return 1; - } - /* Handle yields */ - while (!result && vm->thread->status == DST_THREAD_PENDING) { - /* Send back in the value yielded - TODO - do something useful with this */ - DstValue *stack = dst_thread_stack(vm->thread); - stack[dst_frame_ret(stack)] = vm->ret; - /* Resume */ - result = dst_continue(vm); + dst_vm_fiber->ret = dst_wrap_string(dst_cstring("expected function")); + result = 1; } return result; } /* Setup functions */ -Dst *dst_init() { - Dst *vm = malloc(sizeof(Dst)); - if (NULL == vm) { - DST_OUT_OF_MEMORY; - } - vm->ret.type = DST_NIL; +int dst_init() { /* Garbage collection */ - vm->blocks = NULL; - vm->nextCollection = 0; + dst_vm_blocks = NULL; + dst_vm_next_collection = 0; /* Setting memoryInterval to zero forces * a collection pretty much every cycle, which is * horrible for performance, but helps ensure * there are no memory bugs during dev */ - vm->memoryInterval = 0; + dst_vm_memory_interval = 0; + + uint32_t initialCacheCapacity = 1024; /* Set up the cache */ - vm->cache = calloc(1, 128 * sizeof(DstValue)); - vm->cache_capacity = vm->cache == NULL ? 0 : 128; - vm->cache_count = 0; - vm->cache_deleted = 0; - /* Set up global env */ - vm->modules = dst_make_table(vm, 10); - vm->registry = dst_make_table(vm, 10); - vm->env = dst_make_table(vm, 10); + dst_vm_cache = calloc(1, initialCacheCapacity * sizeof(DstValue)); + if (NULL == dst_vm_cache) { + return 1; + } + dst_vm_cache_capacity = dst_vm_cache == NULL ? 0 : initialCacheCapacity; + dst_vm_cache_count = 0; + dst_vm_cache_deleted = 0; /* Set thread */ - vm->thread = dst_thread(vm, vm->ret, 100); - dst_thread_pushnil(vm, vm->thread, 10); - return vm; + dst_vm_fiber = NULL; + return 0; } /* Clear all memory associated with the VM */ -void dst_deinit(Dst *vm) { - dst_clear_memory(vm); - vm->thread = NULL; - vm->modules = NULL; - vm->registry = NULL; - vm->ret.type = DST_NIL; +void dst_deinit() { + dst_clear_memory(); + dst_vm_fiber = NULL; /* Deinit the cache */ - free(vm->cache); - vm->cache = NULL; - vm->cache_count = 0; - vm->cache_capacity = 0; - vm->cache_deleted = 0; - /* Free the vm */ - free(vm); + free(dst_vm_cache); + dst_vm_cache = NULL; + dst_vm_cache_count = 0; + dst_vm_cache_capacity = 0; + dst_vm_cache_deleted = 0; } diff --git a/core/wrap.c b/core/wrap.c index fad19541..b3facbc0 100644 --- a/core/wrap.c +++ b/core/wrap.c @@ -20,7 +20,7 @@ * IN THE SOFTWARE. */ -#include "wrap.h" +#include /* Wrapper functions wrap a data type that is used from C into a * dst value, which can then be used in dst internal functions. Use @@ -40,11 +40,7 @@ DstValue dst_wrap_##NAME(TYPE x) {\ y.type = DTYPE;\ y.as.UM = x;\ return y;\ -}\ -\ -TYPE dst_unwrap_##NAME(Dst *vm, int64_t i) {\ - return dst_getv(vm, i).as.UM;\ -}\ +} DST_WRAP_DEFINE(real, double, DST_REAL, real) DST_WRAP_DEFINE(integer, int64_t, DST_INTEGER, integer) @@ -54,7 +50,7 @@ DST_WRAP_DEFINE(symbol, const uint8_t *, DST_SYMBOL, string) DST_WRAP_DEFINE(array, DstArray *, DST_ARRAY, array) DST_WRAP_DEFINE(tuple, const DstValue *, DST_TUPLE, tuple) DST_WRAP_DEFINE(struct, const DstValue *, DST_STRUCT, st) -DST_WRAP_DEFINE(thread, DstThread *, DST_THREAD, thread) +DST_WRAP_DEFINE(thread, DstFiber *, DST_FIBER, fiber) DST_WRAP_DEFINE(buffer, DstBuffer *, DST_BUFFER, buffer) DST_WRAP_DEFINE(function, DstFunction *, DST_FUNCTION, function) DST_WRAP_DEFINE(cfunction, DstCFunction, DST_CFUNCTION, cfunction) diff --git a/core/wrap.h b/core/wrap.h deleted file mode 100644 index 75a535ff..00000000 --- a/core/wrap.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -* Copyright (c) 2017 Calvin Rose -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to -* deal in the Software without restriction, including without limitation the -* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -* sell copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -* IN THE SOFTWARE. -*/ - -#ifndef DST_WRAP_H_defined -#define DST_WRAP_H_defined - -#include - -/* Wrap data in DstValue */ -DstValue dst_wrap_nil(); -DstValue dst_wrap_real(double x); -DstValue dst_wrap_integer(int64_t x); -DstValue dst_wrap_boolean(int x); -DstValue dst_wrap_string(const uint8_t *x); -DstValue dst_wrap_symbol(const uint8_t *x); -DstValue dst_wrap_array(DstArray *x); -DstValue dst_wrap_tuple(const DstValue *x); -DstValue dst_wrap_struct(const DstValue *x); -DstValue dst_wrap_thread(DstThread *x); -DstValue dst_wrap_buffer(DstBuffer *x); -DstValue dst_wrap_function(DstFunction *x); -DstValue dst_wrap_cfunction(DstCFunction x); -DstValue dst_wrap_table(DstTable *x); -DstValue dst_wrap_userdata(void *x); - -/* Unwrap values */ -double dst_unwrap_real(Dst *vm, int64_t i); -int64_t dst_unwrap_integer(Dst *vm, int64_t i); -int dst_unwrap_boolean(Dst *vm, int64_t i); -const uint8_t *dst_unwrap_string(Dst *vm, int64_t i); -const uint8_t *dst_unwrap_symbol(Dst *vm, int64_t i); -DstArray *dst_unwrap_array(Dst *vm, int64_t i); -const DstValue *dst_unwrap_tuple(Dst *vm, int64_t i); -const DstValue *dst_unwrap_struct(Dst *vm, int64_t i); -DstThread *dst_unwrap_thread(Dst *vm, int64_t i); -DstBuffer *dst_unwrap_buffer(Dst *vm, int64_t i); -DstFunction *dst_unwrap_function(Dst *vm, int64_t i); -DstCFunction dst_unwrap_cfunction(Dst *vm, int64_t i); -DstTable *dst_unwrap_table(Dst *vm, int64_t i); -void *dst_unwrap_userdata(Dst *vm, int64_t i); - -#endif diff --git a/core/client.c b/junkyard/client.c similarity index 100% rename from core/client.c rename to junkyard/client.c diff --git a/core/compile.c b/junkyard/compile.c similarity index 100% rename from core/compile.c rename to junkyard/compile.c diff --git a/core/ds.c b/junkyard/ds.c similarity index 99% rename from core/ds.c rename to junkyard/ds.c index 14dbe080..79fc64d7 100644 --- a/core/ds.c +++ b/junkyard/ds.c @@ -20,7 +20,6 @@ * IN THE SOFTWARE. */ -#include "internal.h" #include "cache.h" #include @@ -71,7 +70,7 @@ const char *dst_value_get(DstValue ds, DstValue key, DstValue *out) { return NULL; } -void dst_get(Dst *vm) { +int dst_get(Dst *vm) { DstValue ds = dst_popv(vm); DstValue key = dst_popv(vm); DstValue ret; diff --git a/core/serialize.c b/junkyard/serialize.c similarity index 100% rename from core/serialize.c rename to junkyard/serialize.c diff --git a/core/stl.c b/junkyard/stl.c similarity index 100% rename from core/stl.c rename to junkyard/stl.c diff --git a/libs/sample.dsts b/libs/sample.dsts index ce13ac2e..354f9ce4 100644 --- a/libs/sample.dsts +++ b/libs/sample.dsts @@ -3,7 +3,6 @@ # simply construct a FuncDef from this file. This file is also parsed # in the same markup as dst itself (extended S expressions) { - stack 10 arity 3 signature (integer integer integer integer) source "source file path" @@ -38,7 +37,8 @@ 0x0000123400000135 ... ] -# Slots can be named as well for convenience. +# The number of slots available for the function. +# Slots can be named as well for convenience. slots [ x y diff --git a/unittests/array_test.c b/unittests/array_test.c new file mode 100644 index 00000000..7cfb9bec --- /dev/null +++ b/unittests/array_test.c @@ -0,0 +1,17 @@ +#include "unit.h" +#include + +int main() { + int64_t i; + DstArray *array = dst_array(10); + assert(array->capacity == 10); + assert(array->count == 0); + for (i = 0; i < 500; ++i) + dst_array_push(array, dst_wrap_integer(i)); + for (i = 0; i < 500; ++i) + assert(array->data[i].type == DST_INTEGER && array->data[i].as.integer == i); + for (i = 0; i < 200; ++i) + dst_array_pop(array); + assert(array->count == 300); + return 0; +} diff --git a/unittests/unit.h b/unittests/unit.h new file mode 100644 index 00000000..ba402027 --- /dev/null +++ b/unittests/unit.h @@ -0,0 +1,31 @@ +/* +* Copyright (c) 2017 Calvin Rose +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to +* deal in the Software without restriction, including without limitation the +* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +* sell copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +* IN THE SOFTWARE. +*/ + +/* Header for unit testing of dst components */ + +#ifndef UNIT_H_U09UBW3V +#define UNIT_H_U09UBW3V + +#include +#include + +#endif /* end of include guard: UNIT_H_U09UBW3V */