1
0
mirror of https://github.com/janet-lang/janet synced 2024-09-28 07:08:14 +00:00

Add test nanbox implementation. Works for 32 bit and 64 bit x86

This commit is contained in:
bakpakin 2017-11-26 19:31:40 -05:00
parent 412d40d09f
commit 68f5ea4361
8 changed files with 619 additions and 382 deletions

View File

@ -26,7 +26,7 @@ PREFIX?=/usr/local
BINDIR=$(PREFIX)/bin BINDIR=$(PREFIX)/bin
VERSION=\"0.0.0-beta\" VERSION=\"0.0.0-beta\"
CFLAGS=-std=c99 -Wall -Wextra -I./include -I./libs -g -DDST_VERSION=$(VERSION) CFLAGS=-std=c99 -Wall -m32 -Wextra -I./include -I./libs -g -DDST_VERSION=$(VERSION)
PREFIX=/usr/local PREFIX=/usr/local
DST_TARGET=dst DST_TARGET=dst
DST_XXD=xxd DST_XXD=xxd
@ -78,8 +78,8 @@ $(DST_TARGET): $(DST_CORE_OBJECTS)
CCU_FLAGS = $(CFLAGS) -DDST_UNIT_TEST CCU_FLAGS = $(CFLAGS) -DDST_UNIT_TEST
DST_UNIT_BINARIES=$(addprefix unittests/,\ DST_UNIT_BINARIES=$(addprefix unittests/,\
asm_test.out array_test.out buffer_test.out fiber_test.out parse_test.out \ asm_test.out array_test.out buffer_test.out fiber_test.out \
table_test.out) nanbox_test.out parse_test.out table_test.out)
%.out: %.c $(DST_CORE_OBJECTS) $(DST_ALL_HEADERS) unittests/unit.h %.out: %.c $(DST_CORE_OBJECTS) $(DST_ALL_HEADERS) unittests/unit.h
$(CC) $(CCU_FLAGS) $(DST_CORE_OBJECTS) $< -o $@ $(CC) $(CCU_FLAGS) $(DST_CORE_OBJECTS) $< -o $@
@ -89,6 +89,7 @@ unit: $(DST_UNIT_BINARIES)
unittests/asm_test.out unittests/asm_test.out
unittests/buffer_test.out unittests/buffer_test.out
unittests/fiber_test.out unittests/fiber_test.out
unittests/nanbox_test.out
unittests/parse_test.out unittests/parse_test.out
unittests/table_test.out unittests/table_test.out

View File

@ -141,7 +141,7 @@ const uint8_t *dst_cstring(const char *str) {
#define DST_BUFSIZE 36 #define DST_BUFSIZE 36
static uint32_t real_to_string_impl(uint8_t *buf, double x) { static uint32_t real_to_string_impl(uint8_t *buf, double x) {
int count = snprintf((char *) buf, DST_BUFSIZE, "%.21gF", x); int count = snprintf((char *) buf, DST_BUFSIZE, "%.21g", x);
return (uint32_t) count; return (uint32_t) count;
} }

View File

@ -101,22 +101,36 @@ int dst_sys_struct(DstValue *argv, uint32_t argn) {
int dst_sys_get(DstValue *argv, uint32_t argn) { int dst_sys_get(DstValue *argv, uint32_t argn) {
uint32_t i; uint32_t i;
if (argn < 2) { DstValue ds;
if (argn < 1) {
dst_vm_fiber->ret = dst_cstringv("expected at least 1 argument"); dst_vm_fiber->ret = dst_cstringv("expected at least 1 argument");
return 1; return 1;
} }
DstValue ds = argv[0]; ds = argv[0];
for (i = 1; i < argn; i++) { for (i = 1; i < argn; i++) {
const char *err = dst_try_get(ds, argv[i], &ds); ds = dst_get(ds, argv[i]);
if (NULL != err) { if (ds.type == DST_NIL)
dst_vm_fiber->ret = dst_cstringv(err); break;
return 1;
}
} }
dst_vm_fiber->ret = ds; dst_vm_fiber->ret = ds;
return 0; return 0;
} }
int dst_sys_put(DstValue *argv, uint32_t argn) {
DstValue ds, key, value;
if (argn < 3) {
dst_vm_fiber->ret = dst_cstringv("expected at least 3 arguments");
return 1;
}
if(dst_sys_get(argv, argn - 2))
return 1;
ds = dst_vm_fiber->ret;
key = argv[argn - 2];
value = argv[argn - 1];
dst_put(ds, key, value);
return 0;
}
DstCFunction dst_vm_syscalls[256] = { DstCFunction dst_vm_syscalls[256] = {
dst_sys_print, dst_sys_print,
dst_sys_asm, dst_sys_asm,
@ -124,5 +138,7 @@ DstCFunction dst_vm_syscalls[256] = {
dst_sys_array, dst_sys_array,
dst_sys_struct, dst_sys_struct,
dst_sys_table, dst_sys_table,
dst_sys_get,
dst_sys_put,
NULL NULL
}; };

View File

@ -193,98 +193,80 @@ int dst_compare(DstValue x, DstValue y) {
return 1; return 1;
} }
/* Get a value out af an associated data structure. /* Get a value out af an associated data structure. For invalid
* Returns possible c error message, and NULL for no error. The * data structure or invalid key, returns nil. */
* useful return value is written to out on success */ DstValue dst_get(DstValue ds, DstValue key) {
const char *dst_try_get(DstValue ds, DstValue key, DstValue *out) {
int64_t index;
DstValue ret;
switch (ds.type) { switch (ds.type) {
case DST_ARRAY: case DST_ARRAY:
if (key.type != DST_INTEGER) return "expected integer key"; if (key.type == DST_INTEGER &&
index = key.as.integer; key.as.integer >= 0 &&
if (index < 0 || index >= ds.as.array->count) key.as.integer < ds.as.array->count)
return "invalid array access"; return ds.as.array->data[key.as.integer];
ret = ds.as.array->data[index];
break; break;
case DST_TUPLE: case DST_TUPLE:
if (key.type != DST_INTEGER) return "expected integer key"; if (key.type == DST_INTEGER &&
index = key.as.integer; key.as.integer >= 0 &&
if (index < 0 || index >= dst_tuple_length(ds.as.tuple)) key.as.integer < dst_tuple_length(ds.as.tuple))
return "invalid tuple access"; return ds.as.tuple[key.as.integer];
ret = ds.as.tuple[index];
break; break;
case DST_BUFFER: case DST_BUFFER:
if (key.type != DST_INTEGER) return "expected integer key"; if (key.type == DST_INTEGER &&
index = key.as.integer; key.as.integer >= 0 &&
if (index < 0 || index >= ds.as.buffer->count) key.as.integer < ds.as.buffer->count)
return "invalid buffer access"; return dst_wrap_integer(ds.as.buffer->data[key.as.integer]);
ret.type = DST_INTEGER;
ret.as.integer = ds.as.buffer->data[index];
break; break;
case DST_STRING: case DST_STRING:
case DST_SYMBOL: case DST_SYMBOL:
if (key.type != DST_INTEGER) return "expected integer key"; if (key.type == DST_INTEGER &&
index = key.as.integer; key.as.integer >= 0 &&
if (index < 0 || index >= dst_string_length(ds.as.string)) key.as.integer < dst_string_length(ds.as.string))
return "invalid string access"; return dst_wrap_integer(ds.as.string[key.as.integer]);
ret.type = DST_INTEGER;
ret.as.integer = ds.as.string[index];
break; break;
case DST_STRUCT: case DST_STRUCT:
ret = dst_struct_get(ds.as.st, key); return dst_struct_get(ds.as.st, key);
break;
case DST_TABLE: case DST_TABLE:
ret = dst_table_get(ds.as.table, key); return dst_table_get(ds.as.table, key);
break;
default: default:
return "cannot get"; break;
} }
*out = ret; return dst_wrap_nil();
return NULL;
} }
/* Set a value in an associative data structure. Returns possible /* Set a value in an associative data structure. Returns possible
* error message, and NULL if no error. */ * error message, and NULL if no error. */
const char *dst_try_put(DstValue ds, DstValue key, DstValue value) { void dst_put(DstValue ds, DstValue key, DstValue value) {
int64_t index;
switch (ds.type) { switch (ds.type) {
case DST_ARRAY: case DST_ARRAY:
if (key.type != DST_INTEGER) return "expected integer key"; if (key.type == DST_INTEGER &&
index = key.as.integer; key.as.integer >= 0 &&
if (index < 0 || index >= ds.as.array->count) key.as.integer < ds.as.array->count)
return "invalid array access"; ds.as.array->data[key.as.integer] = value;
ds.as.array->data[index] = value; return;
break;
case DST_BUFFER: case DST_BUFFER:
if (key.type != DST_INTEGER) return "expected integer key"; if (key.type == DST_INTEGER &&
index = key.as.integer; value.type == DST_INTEGER &&
if (value.type != DST_INTEGER) return "expected integer value"; key.as.integer >= 0 &&
if (index < 0 || index >= ds.as.buffer->count) key.as.integer < ds.as.buffer->count)
return "invalid buffer access"; ds.as.buffer->data[key.as.integer] = value.as.integer;
ds.as.buffer->data[index] = (uint8_t) value.as.integer; return;
break;
case DST_TABLE: case DST_TABLE:
dst_table_put(ds.as.table, key, value); dst_table_put(ds.as.table, key, value);
break; return;
default: default:
return "cannot set"; return;
} }
return NULL;
} }
/* Get the next key in an associative data structure. Used for iterating through an /* Get the next key in an associative data structure. Used for iterating through an
* associative data structure. */ * associative data structure. */
const char *dst_try_next(DstValue ds, DstValue key, DstValue *out) { DstValue dst_next(DstValue ds, DstValue key) {
switch(ds.type) { switch(ds.type) {
default: default:
return "expected table or struct"; return dst_wrap_nil();
case DST_TABLE: case DST_TABLE:
*out = dst_table_next(ds.as.table, key); return dst_table_next(ds.as.table, key);
return NULL;
case DST_STRUCT: case DST_STRUCT:
*out = dst_struct_next(ds.as.st, key); return dst_struct_next(ds.as.st, key);
return NULL;
} }
} }

596
core/vm.c
View File

@ -49,6 +49,11 @@ static int dst_update_fiber() {
return 0; return 0;
} }
/* Eventually use computed gotos for more effient vm loop. */
#define vm_next() continue
#define vm_checkgc_next() dst_maybe_collect(); continue
/* Start running the VM from where it left off. */ /* Start running the VM from where it left off. */
int dst_continue() { int dst_continue() {
@ -69,21 +74,21 @@ int dst_continue() {
stack[oparg(2, 0xFF)].as.integer op stack[oparg(3, 0xFF)].as.integer\ stack[oparg(2, 0xFF)].as.integer op stack[oparg(3, 0xFF)].as.integer\
);\ );\
pc++;\ pc++;\
continue; vm_next();
#define vm_binop_real(op)\ #define vm_binop_real(op)\
stack[oparg(1, 0xFF)] = dst_wrap_real(\ stack[oparg(1, 0xFF)] = dst_wrap_real(\
stack[oparg(2, 0xFF)].as.real op stack[oparg(3, 0xFF)].as.real\ stack[oparg(2, 0xFF)].as.real op stack[oparg(3, 0xFF)].as.real\
);\ );\
pc++;\ pc++;\
continue; vm_next();
#define vm_binop_immediate(op)\ #define vm_binop_immediate(op)\
stack[oparg(1, 0xFF)] = dst_wrap_integer(\ stack[oparg(1, 0xFF)] = dst_wrap_integer(\
stack[oparg(2, 0xFF)].as.integer op (*((int32_t *)pc) >> 24)\ stack[oparg(2, 0xFF)].as.integer op (*((int32_t *)pc) >> 24)\
);\ );\
pc++;\ pc++;\
continue; vm_next();
#define vm_binop(op)\ #define vm_binop(op)\
{\ {\
@ -99,7 +104,7 @@ int dst_continue() {
? dst_wrap_real(op1.as.real op dst_integer_to_real(op2.as.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));\ : dst_wrap_real(op1.as.real op op2.as.real));\
pc++;\ pc++;\
continue;\ vm_next();\
} }
#define vm_init_fiber_state() \ #define vm_init_fiber_state() \
@ -120,344 +125,343 @@ int dst_continue() {
switch (*pc & 0xFF) { switch (*pc & 0xFF) {
default: default:
vm_throw("unknown opcode"); vm_throw("unknown opcode");
break;
case DOP_NOOP: case DOP_NOOP:
pc++; pc++;
continue; vm_next();
case DOP_ERROR: case DOP_ERROR:
dst_vm_fiber->ret = stack[oparg(1, 0xFF)]; dst_vm_fiber->ret = stack[oparg(1, 0xFF)];
goto vm_error; goto vm_error;
case DOP_TYPECHECK: case DOP_TYPECHECK:
vm_assert((1 << stack[oparg(1, 0xFF)].type) & oparg(2, 0xFFFF), vm_assert((1 << stack[oparg(1, 0xFF)].type) & oparg(2, 0xFFFF),
"typecheck failed"); "typecheck failed");
pc++; pc++;
continue; vm_next();
case DOP_RETURN: case DOP_RETURN:
dst_vm_fiber->ret = stack[oparg(1, 0xFFFFFF)]; dst_vm_fiber->ret = stack[oparg(1, 0xFFFFFF)];
goto vm_return; goto vm_return;
case DOP_RETURN_NIL: case DOP_RETURN_NIL:
dst_vm_fiber->ret.type = DST_NIL; dst_vm_fiber->ret.type = DST_NIL;
goto vm_return; goto vm_return;
case DOP_ADD_INTEGER: case DOP_ADD_INTEGER:
vm_binop_integer(+); vm_binop_integer(+);
case DOP_ADD_IMMEDIATE: case DOP_ADD_IMMEDIATE:
vm_binop_immediate(+); vm_binop_immediate(+);
case DOP_ADD_REAL: case DOP_ADD_REAL:
vm_binop_real(+); vm_binop_real(+);
case DOP_ADD: case DOP_ADD:
vm_binop(+); vm_binop(+);
case DOP_SUBTRACT_INTEGER: case DOP_SUBTRACT_INTEGER:
vm_binop_integer(-); vm_binop_integer(-);
case DOP_SUBTRACT_REAL: case DOP_SUBTRACT_REAL:
vm_binop_real(-); vm_binop_real(-);
case DOP_SUBTRACT: case DOP_SUBTRACT:
vm_binop(-); vm_binop(-);
case DOP_MULTIPLY_INTEGER: case DOP_MULTIPLY_INTEGER:
vm_binop_integer(*); vm_binop_integer(*);
case DOP_MULTIPLY_IMMEDIATE: case DOP_MULTIPLY_IMMEDIATE:
vm_binop_immediate(*); vm_binop_immediate(*);
case DOP_MULTIPLY_REAL: case DOP_MULTIPLY_REAL:
vm_binop_real(*); vm_binop_real(*);
case DOP_MULTIPLY: case DOP_MULTIPLY:
vm_binop(*); vm_binop(*);
case DOP_DIVIDE_INTEGER: 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 != 0, "integer divide by zero");
vm_assert(!(stack[oparg(3, 0xFF)].as.integer == -1 && vm_assert(!(stack[oparg(3, 0xFF)].as.integer == -1 &&
stack[oparg(2, 0xFF)].as.integer == DST_INTEGER_MIN), stack[oparg(2, 0xFF)].as.integer == DST_INTEGER_MIN),
"integer divide overflow"); "integer divide overflow");
vm_binop_integer(/); vm_binop_integer(/);
case DOP_DIVIDE_IMMEDIATE: case DOP_DIVIDE_IMMEDIATE:
{ {
int64_t op1 = stack[oparg(2, 0xFF)].as.integer; int64_t op1 = stack[oparg(2, 0xFF)].as.integer;
int64_t op2 = *((int32_t *)pc) >> 24; int64_t op2 = *((int32_t *)pc) >> 24;
/* Check for degenerate integer division (divide by zero, and dividing /* Check for degenerate integer division (divide by zero, and dividing
* min value by -1). These checks could be omitted if the arg is not * min value by -1). These checks could be omitted if the arg is not
* 0 or -1. */ * 0 or -1. */
if (op2 == 0) if (op2 == 0)
vm_throw("integer divide by zero"); vm_throw("integer divide by zero");
if (op2 == -1) if (op2 == -1)
vm_throw("integer divide overflow"); vm_throw("integer divide overflow");
else else
stack[oparg(1, 0xFF)] = dst_wrap_integer(op1 / op2); stack[oparg(1, 0xFF)] = dst_wrap_integer(op1 / op2);
pc++; pc++;
continue; vm_next();
} }
case DOP_DIVIDE_REAL: case DOP_DIVIDE_REAL:
vm_binop_real(/); vm_binop_real(/);
case DOP_DIVIDE: case DOP_DIVIDE:
{ {
DstValue op1 = stack[oparg(2, 0xFF)]; DstValue op1 = stack[oparg(2, 0xFF)];
DstValue op2 = stack[oparg(3, 0xFF)]; DstValue op2 = stack[oparg(3, 0xFF)];
vm_assert(op1.type == DST_INTEGER || op1.type == DST_REAL, "expected number"); vm_assert(op1.type == DST_INTEGER || op1.type == DST_REAL, "expected number");
vm_assert(op2.type == DST_INTEGER || op2.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) if (op2.type == DST_INTEGER && op2.as.integer == 0)
op2 = dst_wrap_real(0.0); op2 = dst_wrap_real(0.0);
if (op2.type == DST_INTEGER && op2.as.integer == -1 && if (op2.type == DST_INTEGER && op2.as.integer == -1 &&
op1.type == DST_INTEGER && op1.as.integer == DST_INTEGER_MIN) op1.type == DST_INTEGER && op1.as.integer == DST_INTEGER_MIN)
op2 = dst_wrap_real(-1); op2 = dst_wrap_real(-1);
stack[oparg(1, 0xFF)] = op1.type == DST_INTEGER stack[oparg(1, 0xFF)] = op1.type == DST_INTEGER
? op2.type == DST_INTEGER ? op2.type == DST_INTEGER
? dst_wrap_integer(op1.as.integer / op2.as.integer) ? dst_wrap_integer(op1.as.integer / op2.as.integer)
: dst_wrap_real(dst_integer_to_real(op1.as.integer) / op2.as.real) : dst_wrap_real(dst_integer_to_real(op1.as.integer) / op2.as.real)
: op2.type == DST_INTEGER : op2.type == DST_INTEGER
? dst_wrap_real(op1.as.real / dst_integer_to_real(op2.as.integer)) ? dst_wrap_real(op1.as.real / dst_integer_to_real(op2.as.integer))
: dst_wrap_real(op1.as.real / op2.as.real); : dst_wrap_real(op1.as.real / op2.as.real);
pc++; pc++;
continue; vm_next();
} }
case DOP_BAND: case DOP_BAND:
vm_binop_integer(&); vm_binop_integer(&);
case DOP_BOR: case DOP_BOR:
vm_binop_integer(|); vm_binop_integer(|);
case DOP_BXOR: case DOP_BXOR:
vm_binop_integer(^); vm_binop_integer(^);
case DOP_BNOT: case DOP_BNOT:
stack[oparg(1, 0xFF)] = dst_wrap_integer(~stack[oparg(2, 0xFFFF)].as.integer); stack[oparg(1, 0xFF)] = dst_wrap_integer(~stack[oparg(2, 0xFFFF)].as.integer);
continue; vm_next();
case DOP_SHIFT_RIGHT_UNSIGNED: case DOP_SHIFT_RIGHT_UNSIGNED:
stack[oparg(1, 0xFF)] = dst_wrap_integer( stack[oparg(1, 0xFF)] = dst_wrap_integer(
stack[oparg(2, 0xFF)].as.uinteger stack[oparg(2, 0xFF)].as.uinteger
>> >>
stack[oparg(3, 0xFF)].as.uinteger stack[oparg(3, 0xFF)].as.uinteger
); );
pc++; pc++;
continue; vm_next();
case DOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE: case DOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE:
stack[oparg(1, 0xFF)] = dst_wrap_integer( stack[oparg(1, 0xFF)] = dst_wrap_integer(
stack[oparg(2, 0xFF)].as.uinteger >> oparg(3, 0xFF) stack[oparg(2, 0xFF)].as.uinteger >> oparg(3, 0xFF)
); );
pc++; pc++;
continue; vm_next();
case DOP_SHIFT_RIGHT: case DOP_SHIFT_RIGHT:
vm_binop_integer(>>); vm_binop_integer(>>);
case DOP_SHIFT_RIGHT_IMMEDIATE: case DOP_SHIFT_RIGHT_IMMEDIATE:
stack[oparg(1, 0xFF)] = dst_wrap_integer( stack[oparg(1, 0xFF)] = dst_wrap_integer(
(int64_t)(stack[oparg(2, 0xFF)].as.uinteger >> oparg(3, 0xFF)) (int64_t)(stack[oparg(2, 0xFF)].as.uinteger >> oparg(3, 0xFF))
); );
pc++; pc++;
continue; vm_next();
case DOP_SHIFT_LEFT: case DOP_SHIFT_LEFT:
vm_binop_integer(<<); vm_binop_integer(<<);
case DOP_SHIFT_LEFT_IMMEDIATE: case DOP_SHIFT_LEFT_IMMEDIATE:
stack[oparg(1, 0xFF)] = dst_wrap_integer( stack[oparg(1, 0xFF)] = dst_wrap_integer(
stack[oparg(2, 0xFF)].as.integer << oparg(3, 0xFF) stack[oparg(2, 0xFF)].as.integer << oparg(3, 0xFF)
); );
pc++; pc++;
continue; vm_next();
case DOP_MOVE: case DOP_MOVE:
stack[oparg(1, 0xFF)] = stack[oparg(2, 0xFFFF)]; stack[oparg(1, 0xFF)] = stack[oparg(2, 0xFFFF)];
pc++; pc++;
continue; vm_next();
case DOP_JUMP: case DOP_JUMP:
pc += (*(int32_t *)pc) >> 8; pc += (*(int32_t *)pc) >> 8;
continue; vm_next();
case DOP_JUMP_IF: case DOP_JUMP_IF:
if (dst_truthy(stack[oparg(1, 0xFF)])) { if (dst_truthy(stack[oparg(1, 0xFF)])) {
pc += (*(int32_t *)pc) >> 16; pc += (*(int32_t *)pc) >> 16;
} else { } else {
pc++; pc++;
} }
continue; vm_next();
case DOP_JUMP_IF_NOT: case DOP_JUMP_IF_NOT:
if (dst_truthy(stack[oparg(1, 0xFF)])) { if (dst_truthy(stack[oparg(1, 0xFF)])) {
pc++; pc++;
} else { } else {
pc += (*(int32_t *)pc) >> 16; pc += (*(int32_t *)pc) >> 16;
} }
continue; vm_next();
case DOP_LESS_THAN: case DOP_LESS_THAN:
stack[oparg(1, 0xFF)].type = DST_BOOLEAN; stack[oparg(1, 0xFF)].type = DST_BOOLEAN;
stack[oparg(1, 0xFF)].as.boolean = dst_compare( stack[oparg(1, 0xFF)].as.boolean = dst_compare(
stack[oparg(2, 0xFF)], stack[oparg(2, 0xFF)],
stack[oparg(3, 0xFF)] stack[oparg(3, 0xFF)]
) < 0; ) < 0;
pc++; pc++;
continue; vm_next();
case DOP_GREATER_THAN: case DOP_GREATER_THAN:
stack[oparg(1, 0xFF)].type = DST_BOOLEAN; stack[oparg(1, 0xFF)].type = DST_BOOLEAN;
stack[oparg(1, 0xFF)].as.boolean = dst_compare( stack[oparg(1, 0xFF)].as.boolean = dst_compare(
stack[oparg(2, 0xFF)], stack[oparg(2, 0xFF)],
stack[oparg(3, 0xFF)] stack[oparg(3, 0xFF)]
) > 0; ) > 0;
pc++; pc++;
continue; vm_next();
case DOP_EQUALS: case DOP_EQUALS:
stack[oparg(1, 0xFF)].type = DST_BOOLEAN; stack[oparg(1, 0xFF)].type = DST_BOOLEAN;
stack[oparg(1, 0xFF)].as.boolean = dst_equals( stack[oparg(1, 0xFF)].as.boolean = dst_equals(
stack[oparg(2, 0xFF)], stack[oparg(2, 0xFF)],
stack[oparg(3, 0xFF)] stack[oparg(3, 0xFF)]
); );
pc++; pc++;
continue; vm_next();
case DOP_COMPARE: case DOP_COMPARE:
stack[oparg(1, 0xFF)].type = DST_INTEGER; stack[oparg(1, 0xFF)].type = DST_INTEGER;
stack[oparg(1, 0xFF)].as.integer = dst_compare( stack[oparg(1, 0xFF)].as.integer = dst_compare(
stack[oparg(2, 0xFF)], stack[oparg(2, 0xFF)],
stack[oparg(3, 0xFF)] stack[oparg(3, 0xFF)]
); );
pc++; pc++;
continue; vm_next();
case DOP_LOAD_NIL: case DOP_LOAD_NIL:
stack[oparg(1, 0xFFFFFF)].type = DST_NIL; stack[oparg(1, 0xFFFFFF)].type = DST_NIL;
pc++; pc++;
continue; vm_next();
case DOP_LOAD_BOOLEAN: case DOP_LOAD_BOOLEAN:
stack[oparg(1, 0xFF)] = dst_wrap_boolean(oparg(2, 0xFFFF)); stack[oparg(1, 0xFF)] = dst_wrap_boolean(oparg(2, 0xFFFF));
pc++; pc++;
continue; vm_next();
case DOP_LOAD_INTEGER: case DOP_LOAD_INTEGER:
stack[oparg(1, 0xFF)] = dst_wrap_integer(*((int32_t *)pc) >> 16); stack[oparg(1, 0xFF)] = dst_wrap_integer(*((int32_t *)pc) >> 16);
pc++; pc++;
continue; vm_next();
case DOP_LOAD_CONSTANT: case DOP_LOAD_CONSTANT:
vm_assert(oparg(2, 0xFFFF) < func->def->constants_length, "invalid constant"); vm_assert(oparg(2, 0xFFFF) < func->def->constants_length, "invalid constant");
stack[oparg(1, 0xFF)] = func->def->constants[oparg(2, 0xFFFF)]; stack[oparg(1, 0xFF)] = func->def->constants[oparg(2, 0xFFFF)];
pc++; pc++;
continue; vm_next();
case DOP_LOAD_UPVALUE: case DOP_LOAD_UPVALUE:
{ {
uint32_t eindex = oparg(2, 0xFF); uint32_t eindex = oparg(2, 0xFF);
uint32_t vindex = oparg(3, 0xFF); uint32_t vindex = oparg(3, 0xFF);
DstFuncEnv *env; DstFuncEnv *env;
vm_assert(func->def->environments_length > eindex, "invalid upvalue"); vm_assert(func->def->environments_length > eindex, "invalid upvalue");
env = func->envs[eindex]; env = func->envs[eindex];
vm_assert(env->length > vindex, "invalid upvalue"); vm_assert(env->length > vindex, "invalid upvalue");
if (env->offset) { if (env->offset) {
/* On stack */ /* On stack */
stack[oparg(1, 0xFF)] = env->as.fiber->data[env->offset + vindex]; stack[oparg(1, 0xFF)] = env->as.fiber->data[env->offset + vindex];
} else { } else {
/* Off stack */ /* Off stack */
stack[oparg(1, 0xFF)] = env->as.values[vindex]; stack[oparg(1, 0xFF)] = env->as.values[vindex];
}
pc++;
continue;
} }
pc++;
vm_next();
}
case DOP_SET_UPVALUE: case DOP_SET_UPVALUE:
{ {
uint32_t eindex = oparg(2, 0xFF); uint32_t eindex = oparg(2, 0xFF);
uint32_t vindex = oparg(3, 0xFF); uint32_t vindex = oparg(3, 0xFF);
DstFuncEnv *env; DstFuncEnv *env;
vm_assert(func->def->environments_length > eindex, "invalid upvalue"); vm_assert(func->def->environments_length > eindex, "invalid upvalue");
env = func->envs[eindex]; env = func->envs[eindex];
vm_assert(env->length > vindex, "invalid upvalue"); vm_assert(env->length > vindex, "invalid upvalue");
if (env->offset) { if (env->offset) {
env->as.fiber->data[env->offset + vindex] = stack[oparg(1, 0xFF)]; env->as.fiber->data[env->offset + vindex] = stack[oparg(1, 0xFF)];
} else { } else {
env->as.values[vindex] = stack[oparg(1, 0xFF)]; env->as.values[vindex] = stack[oparg(1, 0xFF)];
}
pc++;
continue;
} }
pc++;
vm_next();
}
case DOP_CLOSURE: case DOP_CLOSURE:
{ {
uint32_t i; uint32_t i;
DstFunction *fn; DstFunction *fn;
DstFuncDef *fd; DstFuncDef *fd;
vm_assert(oparg(2, 0xFFFF) < func->def->constants_length, "invalid constant"); 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"); 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); fd = (DstFuncDef *)(func->def->constants[oparg(2, 0xFFFF)].as.pointer);
fn = dst_alloc(DST_MEMORY_FUNCTION, sizeof(DstFunction)); fn = dst_alloc(DST_MEMORY_FUNCTION, sizeof(DstFunction));
fn->envs = malloc(sizeof(DstFuncEnv *) * fd->environments_length); fn->envs = malloc(sizeof(DstFuncEnv *) * fd->environments_length);
if (NULL == fn->envs) { if (NULL == fn->envs) {
DST_OUT_OF_MEMORY; 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 = dst_vm_fiber->frame;
env->as.fiber = dst_vm_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;
} }
if (fd->flags & DST_FUNCDEF_FLAG_NEEDSENV) {
/* Delayed capture of current stack frame */
DstFuncEnv *env = dst_alloc(DST_MEMORY_FUNCENV, sizeof(DstFuncEnv));
env->offset = dst_vm_fiber->frame;
env->as.fiber = dst_vm_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++;
vm_checkgc_next();
}
case DOP_PUSH: case DOP_PUSH:
dst_fiber_push(dst_vm_fiber, stack[oparg(1, 0xFFFFFF)]); dst_fiber_push(dst_vm_fiber, stack[oparg(1, 0xFFFFFF)]);
pc++; pc++;
break; vm_checkgc_next();
case DOP_PUSH_2: case DOP_PUSH_2:
dst_fiber_push2(dst_vm_fiber, dst_fiber_push2(dst_vm_fiber,
stack[oparg(1, 0xFF)], stack[oparg(1, 0xFF)],
stack[oparg(2, 0xFFFF)]); stack[oparg(2, 0xFFFF)]);
pc++; pc++;
break;; vm_checkgc_next();
case DOP_PUSH_3: case DOP_PUSH_3:
dst_fiber_push3(dst_vm_fiber, dst_fiber_push3(dst_vm_fiber,
stack[oparg(1, 0xFF)], stack[oparg(1, 0xFF)],
stack[oparg(2, 0xFF)], stack[oparg(2, 0xFF)],
stack[oparg(3, 0xFF)]); stack[oparg(3, 0xFF)]);
pc++; pc++;
break; vm_checkgc_next();
case DOP_PUSH_ARRAY: case DOP_PUSH_ARRAY:
{ {
uint32_t count; uint32_t count;
const DstValue *array; const DstValue *array;
if (dst_seq_view(stack[oparg(1, 0xFFFFFF)], &array, &count)) { if (dst_seq_view(stack[oparg(1, 0xFFFFFF)], &array, &count)) {
dst_fiber_pushn(dst_vm_fiber, array, count); dst_fiber_pushn(dst_vm_fiber, array, count);
} else { } else {
vm_throw("expected array or tuple"); vm_throw("expected array or tuple");
}
pc++;
break;
} }
pc++;
vm_checkgc_next();
}
case DOP_CALL: case DOP_CALL:
{ {
@ -467,7 +471,7 @@ int dst_continue() {
dst_fiber_funcframe(dst_vm_fiber, func); dst_fiber_funcframe(dst_vm_fiber, func);
stack = dst_vm_fiber->data + dst_vm_fiber->frame; stack = dst_vm_fiber->data + dst_vm_fiber->frame;
pc = func->def->bytecode; pc = func->def->bytecode;
break; vm_checkgc_next();
} else if (callee.type == DST_CFUNCTION) { } else if (callee.type == DST_CFUNCTION) {
dst_fiber_cframe(dst_vm_fiber); dst_fiber_cframe(dst_vm_fiber);
dst_vm_fiber->ret.type = DST_NIL; dst_vm_fiber->ret.type = DST_NIL;
@ -475,13 +479,10 @@ int dst_continue() {
dst_vm_fiber->data + dst_vm_fiber->frame, dst_vm_fiber->data + dst_vm_fiber->frame,
dst_vm_fiber->frametop - dst_vm_fiber->frame)) { dst_vm_fiber->frametop - dst_vm_fiber->frame)) {
goto vm_error; goto vm_error;
} else {
goto vm_return_cfunc;
} }
} else { goto vm_return_cfunc;
vm_throw("cannot call non-function type");
} }
break; vm_throw("cannot call non-function type");
} }
case DOP_TAILCALL: case DOP_TAILCALL:
@ -492,7 +493,7 @@ int dst_continue() {
dst_fiber_funcframe_tail(dst_vm_fiber, func); dst_fiber_funcframe_tail(dst_vm_fiber, func);
stack = dst_vm_fiber->data + dst_vm_fiber->frame; stack = dst_vm_fiber->data + dst_vm_fiber->frame;
pc = func->def->bytecode; pc = func->def->bytecode;
break; vm_checkgc_next();
} else if (callee.type == DST_CFUNCTION) { } else if (callee.type == DST_CFUNCTION) {
dst_fiber_cframe_tail(dst_vm_fiber); dst_fiber_cframe_tail(dst_vm_fiber);
dst_vm_fiber->ret.type = DST_NIL; dst_vm_fiber->ret.type = DST_NIL;
@ -500,38 +501,33 @@ int dst_continue() {
dst_vm_fiber->data + dst_vm_fiber->frame, dst_vm_fiber->data + dst_vm_fiber->frame,
dst_vm_fiber->frametop - dst_vm_fiber->frame)) { dst_vm_fiber->frametop - dst_vm_fiber->frame)) {
goto vm_error; goto vm_error;
} else {
goto vm_return_cfunc;
} }
} else { goto vm_return_cfunc;
vm_throw("expected function");
} }
break; vm_throw("expected function");
} }
case DOP_SYSCALL: case DOP_SYSCALL:
{ {
DstCFunction f = dst_vm_syscalls[oparg(2, 0xFF)]; DstCFunction f = dst_vm_syscalls[oparg(2, 0xFF)];
vm_assert(NULL != f, "invalid syscall"); vm_assert(NULL != f, "invalid syscall");
dst_fiber_cframe(dst_vm_fiber); dst_fiber_cframe(dst_vm_fiber);
dst_vm_fiber->ret.type = DST_NIL; dst_vm_fiber->ret.type = DST_NIL;
if (f(dst_vm_fiber->data + dst_vm_fiber->frame, if (f(dst_vm_fiber->data + dst_vm_fiber->frame,
dst_vm_fiber->frametop - dst_vm_fiber->frame)) { dst_vm_fiber->frametop - dst_vm_fiber->frame)) {
goto vm_error; goto vm_error;
} else {
goto vm_return_cfunc;
}
continue;
} }
goto vm_return_cfunc;
}
case DOP_LOAD_SYSCALL: case DOP_LOAD_SYSCALL:
{ {
DstCFunction f = dst_vm_syscalls[oparg(2, 0xFF)]; DstCFunction f = dst_vm_syscalls[oparg(2, 0xFF)];
vm_assert(NULL != f, "invalid syscall"); vm_assert(NULL != f, "invalid syscall");
stack[oparg(1, 0xFF)] = dst_wrap_cfunction(f); stack[oparg(1, 0xFF)] = dst_wrap_cfunction(f);
pc++; pc++;
continue; vm_next();
} }
case DOP_TRANSFER: case DOP_TRANSFER:
{ {
@ -557,49 +553,36 @@ int dst_continue() {
vm_init_fiber_state(); vm_init_fiber_state();
stack[oparg(1, 0xFF)] = retvalue; stack[oparg(1, 0xFF)] = retvalue;
pc++; pc++;
continue; vm_next();
} }
case DOP_PUT: case DOP_PUT:
{ dst_put(stack[oparg(1, 0xFF)],
const char *err = dst_try_put( stack[oparg(2, 0xFF)],
stack[oparg(1, 0xFF)], stack[oparg(3, 0xFF)]);
stack[oparg(2, 0xFF)], ++pc;
stack[oparg(3, 0xFF)]); vm_checkgc_next();
if (NULL != err) {
vm_throw(err);
}
++pc;
}
continue;
case DOP_PUT_INDEX: case DOP_PUT_INDEX:
dst_setindex( dst_setindex(stack[oparg(1, 0xFF)],
stack[oparg(1, 0xFF)],
stack[oparg(3, 0xFF)], stack[oparg(3, 0xFF)],
oparg(3, 0xFF)); oparg(3, 0xFF));
++pc; ++pc;
continue; vm_next();
case DOP_GET: case DOP_GET:
{ stack[oparg(1, 0xFF)] = dst_get(
const char *err = dst_try_get( stack[oparg(2, 0xFF)],
stack[oparg(2, 0xFF)], stack[oparg(3, 0xFF)]);
stack[oparg(3, 0xFF)], ++pc;
stack + oparg(1, 0xFF)); vm_next();
if (NULL != err) {
vm_throw(err);
}
++pc;
}
continue;
case DOP_GET_INDEX: case DOP_GET_INDEX:
stack[oparg(1, 0xFF)] = dst_getindex( stack[oparg(1, 0xFF)] = dst_getindex(
stack[oparg(2, 0xFF)], stack[oparg(2, 0xFF)],
oparg(3, 0xFF)); oparg(3, 0xFF));
++pc; ++pc;
continue; vm_next();
/* Return from c function. Simpler than retuning from dst function */ /* Return from c function. Simpler than retuning from dst function */
vm_return_cfunc: vm_return_cfunc:
@ -610,7 +593,7 @@ int dst_continue() {
return 0; return 0;
stack[oparg(1, 0xFF)] = ret; stack[oparg(1, 0xFF)] = ret;
pc++; pc++;
continue; vm_checkgc_next();
} }
/* Handle returning from stack frame. Expect return value in fiber->ret */ /* Handle returning from stack frame. Expect return value in fiber->ret */
@ -624,7 +607,7 @@ int dst_continue() {
pc = dst_stack_frame(stack)->pc; pc = dst_stack_frame(stack)->pc;
stack[oparg(1, 0xFF)] = ret; stack[oparg(1, 0xFF)] = ret;
pc++; pc++;
continue; vm_checkgc_next();
} }
/* Handle errors from c functions and vm opcodes */ /* Handle errors from c functions and vm opcodes */
@ -638,16 +621,11 @@ int dst_continue() {
pc = dst_stack_frame(stack)->pc; pc = dst_stack_frame(stack)->pc;
stack[oparg(1, 0xFF)] = ret; stack[oparg(1, 0xFF)] = ret;
pc++; pc++;
continue; vm_checkgc_next();
} }
} /* end switch */ } /* 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();
} /* end for */ } /* end for */
#undef oparg #undef oparg

View File

@ -1,6 +1,6 @@
{ {
bytecode [ bytecode [
(load-integer 0 10000) (load-integer 0 15)
(load-integer 1 0) (load-integer 1 0)
(load-constant 3 lookup) (load-constant 3 lookup)
@ -8,10 +8,10 @@
(equals 2 1 0) (equals 2 1 0)
(jump-if 2 :done) (jump-if 2 :done)
(push 0) (push 0)
(shift-right-immediate 0 0 1) (add-immediate 0 0 -1)
(syscall 2 0) (syscall 2 0)
(get 2 3 0) (get 2 3 0)
(push3 2 3 0) (push 2)
(syscall 2 0) (syscall 2 0)
(jump :label) (jump :label)

View File

@ -1,6 +1,7 @@
#include "unit.h" #include "unit.h"
#include <dst/dst.h> #include <dst/dst.h>
int main() { int main() {
int64_t i; int64_t i;
dst_init(); dst_init();

259
unittests/nanbox_test.c Normal file
View File

@ -0,0 +1,259 @@
#include <dst/dst.h>
#include <stdbool.h>
#include <math.h>
#include "unit.h"
/* Required interface for DstValue */
/* wrap and unwrap for all types */
/* Get type quickly */
/* Check against type quickly */
/* Small footprint */
/* 64 bit integer support undecided */
/* dst_type(x)
* dst_checktype(x, t)
* dst_wrap_##TYPE(x)
* dst_unwrap_##TYPE(x)
* dst_truthy(x)
* dst_memclear(p, n) - clear memory for hash tables to nils
* dst_isnan(x) - check if value is nan
*/
typedef union dst_t dst_t;
union dst_t {
uint64_t u64;
int64_t i64;
void *pointer;
double real;
};
/* dst_t */
/* This representation uses 48 bit pointers. The trade off vs. the LuaJIT style
* 47 bit payload representaion is that the type bits are no long contiguous. Type
* checking can still be fast, but typewise polymorphism takes a bit longer. However,
* we can avoid some annoying problems with a full 48 bit address space. */
enum dst_t_tag {
DST_T_NIL,
DST_T_TRUE,
DST_T_FALSE,
DST_T_INTEGER,
DST_T_FIBER,
DST_T_STRUCT,
DST_T_TUPLE,
DST_T_ARRAY,
DST_T_BUFFER,
DST_T_TABLE,
DST_T_USERDATA,
DST_T_FUNCTION,
DST_T_CFUNCTION,
DST_T_STRING,
DST_T_SYMBOL,
DST_T_REAL
};
/* |.......Tag.......|.......................Payload..................| */
/* Non-double: t|11111111111|1ttt|xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */
/* Types of NIL, TRUE, and FALSE must have payload set to all 1s. */
/* Double (no NaNs): x xxxxxxxxxxx xxxx xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */
/* A simple scheme for nan boxed values */
/* normal doubles, denormalized doubles, and infinities are doubles */
/* Quiet nan is nil. Sign bit should be 0. */
#define DST_NANBOX_TYPEBITS 0x0007000000000000lu
#ifdef DST_64
#define DST_NANBOX_POINTERBITS 0x0000FFFFFFFFFFFFlu
#else
#define DST_NANBOX_POINTERBITS 0x00000000FFFFFFFFlu
#endif
#define DST_NANBOX_QUIET_BIT 0x0008000000000000lu
#define dst_nanbox_isreal(x) (!isnan((x).real))
#define dst_nanbox_tag(type) \
((((uint64_t)(type) & 0x8) << 12) | 0x7FF8 | (type))
#define dst_nanbox_tagbits(x) \
((x).u64 & 0xFFFF000000000000lu)
#define dst_nanbox_payloadbits(x) \
((x).u64 & 0x0000FFFFFFFFFFFFlu)
#define dst_nanbox_type(x) \
(dst_nanbox_isreal(x) \
? DST_T_REAL \
: (((x).u64 & DST_NANBOX_TYPEBITS) >> 48) | (((x).u64 >> 60) & 0x8))
#define dst_nanbox_checktype(x, t) \
(((t) == DST_T_REAL) \
? dst_nanbox_isreal(x) \
: (!dst_nanbox_isreal(x) && (((x).u64 >> 48) == dst_nanbox_tag(t))))
static inline void *dst_nanbox_to_pointer(dst_t x) {
x.i64 = (x.i64 << 16) >> 16;
return x.pointer;
}
static inline dst_t dst_nanbox_from_pointer(void *p, uint64_t tagmask) {
dst_t ret;
ret.pointer = p;
ret.u64 &= DST_NANBOX_POINTERBITS;
ret.u64 |= tagmask;
return ret;
}
static inline dst_t dst_nanbox_from_double(double d) {
dst_t ret;
ret.real = d;
/* Normalize NaNs to nil */
if (d != d)
ret.u64 = dst_nanbox_tag(DST_T_NIL) << 48;
return ret;
}
static inline dst_t dst_nanbox_from_bits(uint64_t bits) {
dst_t ret;
ret.u64 = bits;
return ret;
}
#define dst_nanbox_truthy(x) \
(~(dst_nanbox_checktype((x), DST_NIL) || dst_nanbox_checktype((x), DST_FALSE)))
#define dst_nanbox_from_payload(t, p) \
dst_nanbox_from_bits((dst_nanbox_tag(t) << 48) | (p))
#define dst_nanbox_wrap_(p, t) \
dst_nanbox_from_pointer((p), (dst_nanbox_tag(t) << 48) | 0x7FF8000000000000lu)
/* Wrap the simple types */
#define dst_nanbox_wrap_nil() dst_nanbox_from_payload(DST_T_NIL, 0xFFFFFFFFFFFFlu)
#define dst_nanbox_wrap_true() dst_nanbox_from_payload(DST_T_TRUE, 0xFFFFFFFFFFFFlu)
#define dst_nanbox_wrap_false() dst_nanbox_from_payload(DST_T_FALSE, 0xFFFFFFFFFFFFlu)
#define dst_nanbox_wrap_boolean(b) dst_nanbox_from_payload((b) ? DST_T_TRUE : DST_T_FALSE, 0xFFFFFFFFFFFFlu)
#define dst_nanbox_wrap_integer(i) dst_nanbox_from_payload(DST_T_INTEGER, (uint32_t)(i))
#define dst_nanbox_wrap_real(r) dst_nanbox_from_double(r)
#define dst_nanbox_unwrap_boolean(x) \
(((x).u64 >> 48) == dst_nanbox_tag(DST_T_TRUE))
#define dst_nanbox_unwrap_integer(x) \
((int32_t)((x).u64 & 0xFFFFFFFFlu))
#define dst_nanbox_unwrap_real(x) ((x).real)
/* Wrap the pointer types */
#define dst_nanbox_wrap_struct(s) dst_nanbox_wrap_((s), DST_T_STRUCT)
#define dst_nanbox_wrap_tuple(s) dst_nanbox_wrap_((s), DST_T_TUPLE)
#define dst_nanbox_wrap_fiber(s) dst_nanbox_wrap_((s), DST_T_FIBER)
#define dst_nanbox_wrap_array(s) dst_nanbox_wrap_((s), DST_T_ARRAY)
#define dst_nanbox_wrap_table(s) dst_nanbox_wrap_((s), DST_T_TABLE)
#define dst_nanbox_wrap_buffer(s) dst_nanbox_wrap_((s), DST_T_BUFFER)
#define dst_nanbox_wrap_string(s) dst_nanbox_wrap_((s), DST_T_STRING)
#define dst_nanbox_wrap_symbol(s) dst_nanbox_wrap_((s), DST_T_SYMBOL)
#define dst_nanbox_wrap_userdata(s) dst_nanbox_wrap_((s), DST_T_USERDATA)
#define dst_nanbox_wrap_function(s) dst_nanbox_wrap_((s), DST_T_FUNCTION)
#define dst_nanbox_wrap_cfunction(s) dst_nanbox_wrap_((s), DST_T_CFUNCTION)
/* Unwrap the pointer types */
#define dst_nanbox_unwrap_struct(x) ((const DstValue *)dst_nanbox_to_pointer(x))
#define dst_nanbox_unwrap_tuple(x) ((const DstValue *)dst_nanbox_to_pointer(x))
#define dst_nanbox_unwrap_fiber(x) ((DstFiber *)dst_nanbox_to_pointer(x))
#define dst_nanbox_unwrap_array(x) ((DstArray *)dst_nanbox_to_pointer(x))
#define dst_nanbox_unwrap_table(x) ((DstTable *)dst_nanbox_to_pointer(x))
#define dst_nanbox_unwrap_buffer(x) ((DstBuffer *)dst_nanbox_to_pointer(x))
#define dst_nanbox_unwrap_string(x) ((const uint8_t *)dst_nanbox_to_pointer(x))
#define dst_nanbox_unwrap_symbol(x) ((const uint8_t *)dst_nanbox_to_pointer(x))
#define dst_nanbox_unwrap_userdata(x) (dst_nanbox_to_pointer(x))
#define dst_nanbox_unwrap_function(x) ((DstFunction *)dst_nanbox_to_pointer(x))
#define dst_nanbox_unwrap_cfunction(x) ((DstCFunction)dst_nanbox_to_pointer(x))
void dst_nanbox_print(dst_t x) {
printf("hex: 0x%llx, "
"description: ", x.u64);
switch (dst_nanbox_type(x)) {
case DST_T_NIL:
printf("nil\n");
break;
case DST_T_TRUE:
printf("true\n");
break;
case DST_T_FALSE:
printf("false\n");
break;
case DST_T_INTEGER:
printf("%dI\n", dst_nanbox_unwrap_integer(x));
break;
case DST_T_STRUCT:
printf("<struct %p>\n", dst_nanbox_unwrap_struct(x));
break;
case DST_T_TUPLE:
printf("<tuple %p>\n", dst_nanbox_unwrap_tuple(x));
break;
case DST_T_FIBER:
printf("<fiber %p>\n", dst_nanbox_unwrap_fiber(x));
break;
case DST_T_ARRAY:
printf("<array %p>\n", dst_nanbox_unwrap_array(x));
break;
case DST_T_TABLE:
printf("<table %p>\n", dst_nanbox_unwrap_table(x));
break;
case DST_T_STRING:
printf("<string %p>\n", dst_nanbox_unwrap_string(x));
break;
case DST_T_SYMBOL:
printf("<symbol %p>\n", dst_nanbox_unwrap_symbol(x));
break;
case DST_T_USERDATA:
printf("<userdata %p>\n", dst_nanbox_unwrap_userdata(x));
break;
case DST_T_FUNCTION:
printf("<function %p>\n", dst_nanbox_unwrap_function(x));
break;
case DST_T_CFUNCTION:
printf("<cfunction %p>\n", dst_nanbox_unwrap_cfunction(x));
break;
case DST_T_BUFFER:
printf("<buffer %p>\n", dst_nanbox_unwrap_buffer(x));
break;
default:
printf("unknown type 0x%llu\n", dst_nanbox_type(x));
case DST_T_REAL:
printf("%.21g\n", dst_nanbox_unwrap_real(x));
break;
}
}
int main() {
printf("--- nan box test ---\n");
printf("sizeof(dst_t) = %d\n", sizeof(dst_t));
DstArray array;
dst_nanbox_print(dst_nanbox_wrap_real(0.125));
dst_nanbox_print(dst_nanbox_wrap_real(19236910.125));
dst_nanbox_print(dst_nanbox_wrap_real(123120.125));
dst_nanbox_print(dst_nanbox_wrap_real(0.0));
dst_nanbox_print(dst_nanbox_wrap_real(1.0/0.0));
dst_nanbox_print(dst_nanbox_wrap_real(1.0/-0.0));
dst_nanbox_print(dst_nanbox_wrap_real(0.0/-0.0));
dst_nanbox_print(dst_nanbox_wrap_real(0.0/0.0));
dst_nanbox_print(dst_nanbox_wrap_true());
dst_nanbox_print(dst_nanbox_wrap_false());
dst_nanbox_print(dst_nanbox_wrap_integer(123));
dst_nanbox_print(dst_nanbox_wrap_integer(-123));
dst_nanbox_print(dst_nanbox_wrap_integer(0));
dst_nanbox_print(dst_nanbox_wrap_array(&array));
dst_nanbox_print(dst_nanbox_wrap_table(&array));
dst_nanbox_print(dst_nanbox_wrap_string(&array));
dst_nanbox_print(dst_nanbox_wrap_buffer(&array));
printf("--- nan box test end ---\n");
}