mirror of
https://github.com/janet-lang/janet
synced 2025-01-26 15:16:51 +00:00
Add test nanbox implementation. Works for 32 bit and 64 bit x86
This commit is contained in:
parent
412d40d09f
commit
68f5ea4361
7
Makefile
7
Makefile
@ -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
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
};
|
};
|
||||||
|
100
core/value.c
100
core/value.c
@ -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
596
core/vm.c
@ -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
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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
259
unittests/nanbox_test.c
Normal 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");
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user