mirror of
https://github.com/janet-lang/janet
synced 2024-11-05 00:06:16 +00:00
dafc121f4d
capitalizing them.
958 lines
27 KiB
C
958 lines
27 KiB
C
/*
|
|
* 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 <dst/dst.h>
|
|
#include <dst/dstopcodes.h>
|
|
#include "state.h"
|
|
#include "fiber.h"
|
|
#include "gc.h"
|
|
#include "symcache.h"
|
|
|
|
/* VM state */
|
|
DST_THREAD_LOCAL int dst_vm_stackn = 0;
|
|
DST_THREAD_LOCAL DstFiber *dst_vm_fiber = NULL;
|
|
|
|
/* Maybe collect garbage */
|
|
#define dst_maybe_collect() do {\
|
|
if (dst_vm_next_collection >= dst_vm_gc_interval) dst_collect(); } while (0)
|
|
|
|
/* Start running the VM from where it left off. */
|
|
Dst dst_run(DstFiber *fiber) {
|
|
|
|
/* Save old fiber to reset */
|
|
DstFiber *old_vm_fiber = dst_vm_fiber;
|
|
|
|
/* VM state */
|
|
register Dst *stack;
|
|
register uint32_t *pc;
|
|
register DstFunction *func;
|
|
|
|
/* Keep in mind the garbage collector cannot see this value.
|
|
* Values stored here should be used immediately */
|
|
Dst retreg;
|
|
|
|
/* Increment the stackn */
|
|
if (dst_vm_stackn >= DST_RECURSION_GUARD) {
|
|
fiber->status = DST_FIBER_ERROR;
|
|
return dst_cstringv("C stack recursed too deeply");
|
|
}
|
|
dst_vm_stackn++;
|
|
|
|
/* Setup fiber state */
|
|
dst_vm_fiber = fiber;
|
|
dst_gcroot(dst_wrap_fiber(fiber));
|
|
if (fiber->flags & DST_FIBER_FLAG_NEW) {
|
|
dst_fiber_funcframe(fiber, fiber->root);
|
|
fiber->flags &= ~DST_FIBER_FLAG_NEW;
|
|
}
|
|
fiber->status = DST_FIBER_ALIVE;
|
|
stack = fiber->data + fiber->frame;
|
|
pc = dst_stack_frame(stack)->pc;
|
|
func = dst_stack_frame(stack)->func;
|
|
|
|
/* Use computed gotos for GCC and clang, otherwise use switch */
|
|
#ifdef __GNUC__
|
|
#define VM_START() {vm_next();
|
|
#define VM_END() }
|
|
#define VM_OP(op) label_##op :
|
|
#define VM_DEFAULT() label_unknown_op:
|
|
#define vm_next() goto *op_lookup[*pc & 0xFF];
|
|
static void *op_lookup[255] = {
|
|
&&label_DOP_NOOP,
|
|
&&label_DOP_ERROR,
|
|
&&label_DOP_TYPECHECK,
|
|
&&label_DOP_RETURN,
|
|
&&label_DOP_RETURN_NIL,
|
|
&&label_DOP_ADD_INTEGER,
|
|
&&label_DOP_ADD_IMMEDIATE,
|
|
&&label_DOP_ADD_REAL,
|
|
&&label_DOP_ADD,
|
|
&&label_DOP_SUBTRACT_INTEGER,
|
|
&&label_DOP_SUBTRACT_REAL,
|
|
&&label_DOP_SUBTRACT,
|
|
&&label_DOP_MULTIPLY_INTEGER,
|
|
&&label_DOP_MULTIPLY_IMMEDIATE,
|
|
&&label_DOP_MULTIPLY_REAL,
|
|
&&label_DOP_MULTIPLY,
|
|
&&label_DOP_DIVIDE_INTEGER,
|
|
&&label_DOP_DIVIDE_IMMEDIATE,
|
|
&&label_DOP_DIVIDE_REAL,
|
|
&&label_DOP_DIVIDE,
|
|
&&label_DOP_BAND,
|
|
&&label_DOP_BOR,
|
|
&&label_DOP_BXOR,
|
|
&&label_DOP_BNOT,
|
|
&&label_DOP_SHIFT_LEFT,
|
|
&&label_DOP_SHIFT_LEFT_IMMEDIATE,
|
|
&&label_DOP_SHIFT_RIGHT,
|
|
&&label_DOP_SHIFT_RIGHT_IMMEDIATE,
|
|
&&label_DOP_SHIFT_RIGHT_UNSIGNED,
|
|
&&label_DOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE,
|
|
&&label_DOP_MOVE_FAR,
|
|
&&label_DOP_MOVE_NEAR,
|
|
&&label_DOP_JUMP,
|
|
&&label_DOP_JUMP_IF,
|
|
&&label_DOP_JUMP_IF_NOT,
|
|
&&label_DOP_GREATER_THAN,
|
|
&&label_DOP_GREATER_THAN_INTEGER,
|
|
&&label_DOP_GREATER_THAN_IMMEDIATE,
|
|
&&label_DOP_GREATER_THAN_REAL,
|
|
&&label_DOP_GREATER_THAN_EQUAL_REAL,
|
|
&&label_DOP_LESS_THAN,
|
|
&&label_DOP_LESS_THAN_INTEGER,
|
|
&&label_DOP_LESS_THAN_IMMEDIATE,
|
|
&&label_DOP_LESS_THAN_REAL,
|
|
&&label_DOP_LESS_THAN_EQUAL_REAL,
|
|
&&label_DOP_EQUALS,
|
|
&&label_DOP_EQUALS_INTEGER,
|
|
&&label_DOP_EQUALS_IMMEDIATE,
|
|
&&label_DOP_EQUALS_REAL,
|
|
&&label_DOP_COMPARE,
|
|
&&label_DOP_LOAD_NIL,
|
|
&&label_DOP_LOAD_TRUE,
|
|
&&label_DOP_LOAD_FALSE,
|
|
&&label_DOP_LOAD_INTEGER,
|
|
&&label_DOP_LOAD_CONSTANT,
|
|
&&label_DOP_LOAD_UPVALUE,
|
|
&&label_DOP_LOAD_SELF,
|
|
&&label_DOP_SET_UPVALUE,
|
|
&&label_DOP_CLOSURE,
|
|
&&label_DOP_PUSH,
|
|
&&label_DOP_PUSH_2,
|
|
&&label_DOP_PUSH_3,
|
|
&&label_DOP_PUSH_ARRAY,
|
|
&&label_DOP_CALL,
|
|
&&label_DOP_TAILCALL,
|
|
&&label_DOP_RESUME,
|
|
&&label_DOP_YIELD,
|
|
&&label_DOP_GET,
|
|
&&label_DOP_PUT,
|
|
&&label_DOP_GET_INDEX,
|
|
&&label_DOP_PUT_INDEX,
|
|
&&label_DOP_LENGTH,
|
|
&&label_DOP_DEBUG,
|
|
&&label_unknown_op
|
|
};
|
|
#else
|
|
#define VM_START() for(;;){switch(*pc & 0xFF){
|
|
#define VM_END() }}
|
|
#define VM_OP(op) case op :
|
|
#define VM_DEFAULT() default:
|
|
#define vm_next() continue
|
|
#endif
|
|
|
|
#define vm_checkgc_next() dst_maybe_collect(); vm_next()
|
|
|
|
/* Used to extract bits from the opcode that correspond to arguments.
|
|
* Pulls out unsigned integers */
|
|
#define oparg(shift, mask) (((*pc) >> ((shift) << 3)) & (mask))
|
|
|
|
#define vm_throw(e) do { retreg = dst_cstringv(e); goto vm_error; } while (0)
|
|
#define vm_assert(cond, e) do {if (!(cond)) vm_throw((e)); } while (0)
|
|
|
|
#define vm_binop_integer(op) \
|
|
stack[oparg(1, 0xFF)] = dst_wrap_integer(\
|
|
dst_unwrap_integer(stack[oparg(2, 0xFF)]) op dst_unwrap_integer(stack[oparg(3, 0xFF)])\
|
|
);\
|
|
pc++;\
|
|
vm_next();
|
|
|
|
#define vm_binop_real(op)\
|
|
stack[oparg(1, 0xFF)] = dst_wrap_real(\
|
|
dst_unwrap_real(stack[oparg(2, 0xFF)]) op dst_unwrap_real(stack[oparg(3, 0xFF)])\
|
|
);\
|
|
pc++;\
|
|
vm_next();
|
|
|
|
#define vm_binop_immediate(op)\
|
|
stack[oparg(1, 0xFF)] = dst_wrap_integer(\
|
|
dst_unwrap_integer(stack[oparg(2, 0xFF)]) op (*((int32_t *)pc) >> 24)\
|
|
);\
|
|
pc++;\
|
|
vm_next();
|
|
|
|
#define vm_binop(op)\
|
|
{\
|
|
Dst op1 = stack[oparg(2, 0xFF)];\
|
|
Dst op2 = stack[oparg(3, 0xFF)];\
|
|
vm_assert(dst_checktype(op1, DST_INTEGER) || dst_checktype(op1, DST_REAL), "expected number");\
|
|
vm_assert(dst_checktype(op2, DST_INTEGER) || dst_checktype(op2, DST_REAL), "expected number");\
|
|
stack[oparg(1, 0xFF)] = dst_checktype(op1, DST_INTEGER)\
|
|
? (dst_checktype(op2, DST_INTEGER)\
|
|
? dst_wrap_integer(dst_unwrap_integer(op1) op dst_unwrap_integer(op2))\
|
|
: dst_wrap_real((double)dst_unwrap_integer(op1) op dst_unwrap_real(op2)))\
|
|
: (dst_checktype(op2, DST_INTEGER)\
|
|
? dst_wrap_real(dst_unwrap_real(op1) op (double)dst_unwrap_integer(op2))\
|
|
: dst_wrap_real(dst_unwrap_real(op1) op dst_unwrap_real(op2)));\
|
|
pc++;\
|
|
vm_next();\
|
|
}
|
|
|
|
/* Main interpreter loop. Semantically is a switch on
|
|
* (*pc & 0xFF) inside of an infinte loop. */
|
|
VM_START();
|
|
|
|
VM_DEFAULT();
|
|
VM_OP(DOP_DEBUG)
|
|
goto vm_debug;
|
|
|
|
VM_OP(DOP_NOOP)
|
|
pc++;
|
|
vm_next();
|
|
|
|
VM_OP(DOP_ERROR)
|
|
retreg = stack[oparg(1, 0xFF)];
|
|
goto vm_error;
|
|
|
|
VM_OP(DOP_TYPECHECK)
|
|
if (!((1 << dst_type(stack[oparg(1, 0xFF)])) & oparg(2, 0xFFFF))) {
|
|
DstArgs tempargs;
|
|
tempargs.n = oparg(1, 0xFF) + 1;
|
|
tempargs.v = stack;
|
|
dst_typemany_err(tempargs, oparg(1, 0xFF), oparg(2, 0xFFFF));
|
|
goto vm_error;
|
|
}
|
|
pc++;
|
|
vm_next();
|
|
|
|
VM_OP(DOP_RETURN)
|
|
retreg = stack[oparg(1, 0xFFFFFF)];
|
|
goto vm_return;
|
|
|
|
VM_OP(DOP_RETURN_NIL)
|
|
retreg = dst_wrap_nil();
|
|
goto vm_return;
|
|
|
|
VM_OP(DOP_ADD_INTEGER)
|
|
vm_binop_integer(+);
|
|
|
|
VM_OP(DOP_ADD_IMMEDIATE)
|
|
vm_binop_immediate(+);
|
|
|
|
VM_OP(DOP_ADD_REAL)
|
|
vm_binop_real(+);
|
|
|
|
VM_OP(DOP_ADD)
|
|
vm_binop(+);
|
|
|
|
VM_OP(DOP_SUBTRACT_INTEGER)
|
|
vm_binop_integer(-);
|
|
|
|
VM_OP(DOP_SUBTRACT_REAL)
|
|
vm_binop_real(-);
|
|
|
|
VM_OP(DOP_SUBTRACT)
|
|
vm_binop(-);
|
|
|
|
VM_OP(DOP_MULTIPLY_INTEGER)
|
|
vm_binop_integer(*);
|
|
|
|
VM_OP(DOP_MULTIPLY_IMMEDIATE)
|
|
vm_binop_immediate(*);
|
|
|
|
VM_OP(DOP_MULTIPLY_REAL)
|
|
vm_binop_real(*);
|
|
|
|
VM_OP(DOP_MULTIPLY)
|
|
vm_binop(*);
|
|
|
|
VM_OP(DOP_DIVIDE_INTEGER)
|
|
vm_assert(dst_unwrap_integer(stack[oparg(3, 0xFF)]) != 0, "integer divide error");
|
|
vm_assert(!(dst_unwrap_integer(stack[oparg(3, 0xFF)]) == -1 &&
|
|
dst_unwrap_integer(stack[oparg(2, 0xFF)]) == INT32_MIN),
|
|
"integer divide error");
|
|
vm_binop_integer(/);
|
|
|
|
VM_OP(DOP_DIVIDE_IMMEDIATE)
|
|
{
|
|
int32_t op1 = dst_unwrap_integer(stack[oparg(2, 0xFF)]);
|
|
int32_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 error");
|
|
if (op2 == -1 && op1 == INT32_MIN)
|
|
vm_throw("integer divide error");
|
|
else
|
|
stack[oparg(1, 0xFF)] = dst_wrap_integer(op1 / op2);
|
|
pc++;
|
|
vm_next();
|
|
}
|
|
|
|
VM_OP(DOP_DIVIDE_REAL)
|
|
vm_binop_real(/);
|
|
|
|
VM_OP(DOP_DIVIDE)
|
|
{
|
|
Dst op1 = stack[oparg(2, 0xFF)];
|
|
Dst op2 = stack[oparg(3, 0xFF)];
|
|
vm_assert(dst_checktype(op1, DST_INTEGER) || dst_checktype(op1, DST_REAL), "expected number");
|
|
vm_assert(dst_checktype(op2, DST_INTEGER) || dst_checktype(op2, DST_REAL), "expected number");
|
|
if (dst_checktype(op2, DST_INTEGER) && dst_unwrap_integer(op2) == 0)
|
|
vm_throw("integer divide error");
|
|
if (dst_checktype(op2, DST_INTEGER) && dst_unwrap_integer(op2) == -1 &&
|
|
dst_checktype(op1, DST_INTEGER) && dst_unwrap_integer(op1) == INT32_MIN)
|
|
vm_throw("integer divide error");
|
|
stack[oparg(1, 0xFF)] = dst_checktype(op1, DST_INTEGER)
|
|
? (dst_checktype(op2, DST_INTEGER)
|
|
? dst_wrap_integer(dst_unwrap_integer(op1) / dst_unwrap_integer(op2))
|
|
: dst_wrap_real((double)dst_unwrap_integer(op1) / dst_unwrap_real(op2)))
|
|
: (dst_checktype(op2, DST_INTEGER)
|
|
? dst_wrap_real(dst_unwrap_real(op1) / (double)dst_unwrap_integer(op2))
|
|
: dst_wrap_real(dst_unwrap_real(op1) / dst_unwrap_real(op2)));
|
|
pc++;
|
|
vm_next();
|
|
}
|
|
|
|
VM_OP(DOP_BAND)
|
|
vm_binop_integer(&);
|
|
|
|
VM_OP(DOP_BOR)
|
|
vm_binop_integer(|);
|
|
|
|
VM_OP(DOP_BXOR)
|
|
vm_binop_integer(^);
|
|
|
|
VM_OP(DOP_BNOT)
|
|
stack[oparg(1, 0xFF)] = dst_wrap_integer(~dst_unwrap_integer(stack[oparg(2, 0xFFFF)]));
|
|
vm_next();
|
|
|
|
VM_OP(DOP_SHIFT_RIGHT_UNSIGNED)
|
|
stack[oparg(1, 0xFF)] = dst_wrap_integer(
|
|
(int32_t)(((uint32_t)dst_unwrap_integer(stack[oparg(2, 0xFF)]))
|
|
>>
|
|
dst_unwrap_integer(stack[oparg(3, 0xFF)]))
|
|
);
|
|
pc++;
|
|
vm_next();
|
|
|
|
VM_OP(DOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE)
|
|
stack[oparg(1, 0xFF)] = dst_wrap_integer(
|
|
(int32_t) (((uint32_t)dst_unwrap_integer(stack[oparg(2, 0xFF)])) >> oparg(3, 0xFF))
|
|
);
|
|
pc++;
|
|
vm_next();
|
|
|
|
VM_OP(DOP_SHIFT_RIGHT)
|
|
vm_binop_integer(>>);
|
|
|
|
VM_OP(DOP_SHIFT_RIGHT_IMMEDIATE)
|
|
stack[oparg(1, 0xFF)] = dst_wrap_integer(
|
|
(int32_t)(dst_unwrap_integer(stack[oparg(2, 0xFF)]) >> oparg(3, 0xFF))
|
|
);
|
|
pc++;
|
|
vm_next();
|
|
|
|
VM_OP(DOP_SHIFT_LEFT)
|
|
vm_binop_integer(<<);
|
|
|
|
VM_OP(DOP_SHIFT_LEFT_IMMEDIATE)
|
|
stack[oparg(1, 0xFF)] = dst_wrap_integer(
|
|
dst_unwrap_integer(stack[oparg(2, 0xFF)]) << oparg(3, 0xFF)
|
|
);
|
|
pc++;
|
|
vm_next();
|
|
|
|
VM_OP(DOP_MOVE_NEAR)
|
|
stack[oparg(1, 0xFF)] = stack[oparg(2, 0xFFFF)];
|
|
pc++;
|
|
vm_next();
|
|
|
|
VM_OP(DOP_MOVE_FAR)
|
|
stack[oparg(2, 0xFFFF)] = stack[oparg(1, 0xFF)];
|
|
pc++;
|
|
vm_next();
|
|
|
|
VM_OP(DOP_JUMP)
|
|
pc += (*(int32_t *)pc) >> 8;
|
|
vm_next();
|
|
|
|
VM_OP(DOP_JUMP_IF)
|
|
if (dst_truthy(stack[oparg(1, 0xFF)])) {
|
|
pc += (*(int32_t *)pc) >> 16;
|
|
} else {
|
|
pc++;
|
|
}
|
|
vm_next();
|
|
|
|
VM_OP(DOP_JUMP_IF_NOT)
|
|
if (dst_truthy(stack[oparg(1, 0xFF)])) {
|
|
pc++;
|
|
} else {
|
|
pc += (*(int32_t *)pc) >> 16;
|
|
}
|
|
vm_next();
|
|
|
|
VM_OP(DOP_LESS_THAN)
|
|
stack[oparg(1, 0xFF)] = dst_wrap_boolean(dst_compare(
|
|
stack[oparg(2, 0xFF)],
|
|
stack[oparg(3, 0xFF)]
|
|
) < 0);
|
|
pc++;
|
|
vm_next();
|
|
|
|
/* Candidate */
|
|
VM_OP(DOP_LESS_THAN_INTEGER)
|
|
stack[oparg(1, 0xFF)] = dst_wrap_boolean(
|
|
dst_unwrap_integer(stack[oparg(2, 0xFF)]) <
|
|
dst_unwrap_integer(stack[oparg(3, 0xFF)]));
|
|
pc++;
|
|
vm_next();
|
|
|
|
/* Candidate */
|
|
VM_OP(DOP_LESS_THAN_IMMEDIATE)
|
|
stack[oparg(1, 0xFF)] = dst_wrap_boolean(
|
|
dst_unwrap_integer(stack[oparg(2, 0xFF)]) < ((*(int32_t *)pc) >> 24)
|
|
);
|
|
pc++;
|
|
vm_next();
|
|
|
|
/* Candidate */
|
|
VM_OP(DOP_LESS_THAN_REAL)
|
|
stack[oparg(1, 0xFF)] = dst_wrap_boolean(
|
|
dst_unwrap_real(stack[oparg(2, 0xFF)]) <
|
|
dst_unwrap_real(stack[oparg(3, 0xFF)]));
|
|
pc++;
|
|
vm_next();
|
|
|
|
/* Candidate */
|
|
VM_OP(DOP_LESS_THAN_EQUAL_REAL)
|
|
stack[oparg(1, 0xFF)] = dst_wrap_boolean(
|
|
dst_unwrap_real(stack[oparg(2, 0xFF)]) <=
|
|
dst_unwrap_real(stack[oparg(3, 0xFF)]));
|
|
pc++;
|
|
vm_next();
|
|
|
|
|
|
VM_OP(DOP_GREATER_THAN)
|
|
stack[oparg(1, 0xFF)] = dst_wrap_boolean(dst_compare(
|
|
stack[oparg(2, 0xFF)],
|
|
stack[oparg(3, 0xFF)]
|
|
) > 0);
|
|
pc++;
|
|
vm_next();
|
|
|
|
/* Candidate */
|
|
VM_OP(DOP_GREATER_THAN_INTEGER)
|
|
stack[oparg(1, 0xFF)] = dst_wrap_boolean(
|
|
dst_unwrap_integer(stack[oparg(2, 0xFF)]) >
|
|
dst_unwrap_integer(stack[oparg(3, 0xFF)]));
|
|
pc++;
|
|
vm_next();
|
|
|
|
/* Candidate */
|
|
VM_OP(DOP_GREATER_THAN_IMMEDIATE)
|
|
stack[oparg(1, 0xFF)] = dst_wrap_boolean(
|
|
dst_unwrap_integer(stack[oparg(2, 0xFF)]) > ((*(int32_t *)pc) >> 24)
|
|
);
|
|
pc++;
|
|
vm_next();
|
|
|
|
/* Candidate */
|
|
VM_OP(DOP_GREATER_THAN_REAL)
|
|
stack[oparg(1, 0xFF)] = dst_wrap_boolean(
|
|
dst_unwrap_real(stack[oparg(2, 0xFF)]) >
|
|
dst_unwrap_real(stack[oparg(3, 0xFF)]));
|
|
pc++;
|
|
vm_next();
|
|
|
|
/* Candidate */
|
|
VM_OP(DOP_GREATER_THAN_EQUAL_REAL)
|
|
stack[oparg(1, 0xFF)] = dst_wrap_boolean(
|
|
dst_unwrap_real(stack[oparg(2, 0xFF)]) >=
|
|
dst_unwrap_real(stack[oparg(3, 0xFF)]));
|
|
pc++;
|
|
vm_next();
|
|
|
|
VM_OP(DOP_EQUALS)
|
|
stack[oparg(1, 0xFF)] = dst_wrap_boolean(dst_equals(
|
|
stack[oparg(2, 0xFF)],
|
|
stack[oparg(3, 0xFF)]
|
|
));
|
|
pc++;
|
|
vm_next();
|
|
|
|
/* Candidate */
|
|
VM_OP(DOP_EQUALS_INTEGER)
|
|
stack[oparg(1, 0xFF)] = dst_wrap_boolean(
|
|
dst_unwrap_integer(stack[oparg(2, 0xFF)]) ==
|
|
dst_unwrap_integer(stack[oparg(3, 0xFF)])
|
|
);
|
|
pc++;
|
|
vm_next();
|
|
|
|
/* Candidate */
|
|
VM_OP(DOP_EQUALS_REAL)
|
|
stack[oparg(1, 0xFF)] = dst_wrap_boolean(
|
|
dst_unwrap_real(stack[oparg(2, 0xFF)]) ==
|
|
dst_unwrap_real(stack[oparg(3, 0xFF)])
|
|
);
|
|
pc++;
|
|
vm_next();
|
|
|
|
/* Candidate */
|
|
VM_OP(DOP_EQUALS_IMMEDIATE)
|
|
stack[oparg(1, 0xFF)] = dst_wrap_boolean(
|
|
dst_unwrap_integer(stack[oparg(2, 0xFF)]) == ((*(int32_t *)pc) >> 24)
|
|
);
|
|
pc++;
|
|
vm_next();
|
|
|
|
VM_OP(DOP_COMPARE)
|
|
stack[oparg(1, 0xFF)] = dst_wrap_integer(dst_compare(
|
|
stack[oparg(2, 0xFF)],
|
|
stack[oparg(3, 0xFF)]
|
|
));
|
|
pc++;
|
|
vm_next();
|
|
|
|
VM_OP(DOP_LOAD_NIL)
|
|
stack[oparg(1, 0xFFFFFF)] = dst_wrap_nil();
|
|
pc++;
|
|
vm_next();
|
|
|
|
VM_OP(DOP_LOAD_TRUE)
|
|
stack[oparg(1, 0xFFFFFF)] = dst_wrap_boolean(1);
|
|
pc++;
|
|
vm_next();
|
|
|
|
VM_OP(DOP_LOAD_FALSE)
|
|
stack[oparg(1, 0xFFFFFF)] = dst_wrap_boolean(0);
|
|
pc++;
|
|
vm_next();
|
|
|
|
VM_OP(DOP_LOAD_INTEGER)
|
|
stack[oparg(1, 0xFF)] = dst_wrap_integer(*((int32_t *)pc) >> 16);
|
|
pc++;
|
|
vm_next();
|
|
|
|
VM_OP(DOP_LOAD_CONSTANT)
|
|
{
|
|
int32_t index = oparg(2, 0xFFFF);
|
|
vm_assert(index < func->def->constants_length, "invalid constant");
|
|
stack[oparg(1, 0xFF)] = func->def->constants[index];
|
|
pc++;
|
|
vm_next();
|
|
}
|
|
|
|
VM_OP(DOP_LOAD_SELF)
|
|
stack[oparg(1, 0xFFFFFF)] = dst_wrap_function(func);
|
|
pc++;
|
|
vm_next();
|
|
|
|
VM_OP(DOP_LOAD_UPVALUE)
|
|
{
|
|
int32_t eindex = oparg(2, 0xFF);
|
|
int32_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 {
|
|
/* Off stack */
|
|
stack[oparg(1, 0xFF)] = env->as.values[vindex];
|
|
}
|
|
pc++;
|
|
vm_next();
|
|
}
|
|
|
|
VM_OP(DOP_SET_UPVALUE)
|
|
{
|
|
int32_t eindex = oparg(2, 0xFF);
|
|
int32_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++;
|
|
vm_next();
|
|
}
|
|
|
|
VM_OP(DOP_CLOSURE)
|
|
{
|
|
DstFuncDef *fd;
|
|
DstFunction *fn;
|
|
int32_t elen;
|
|
vm_assert((int32_t)oparg(2, 0xFFFF) < func->def->defs_length, "invalid funcdef");
|
|
fd = func->def->defs[(int32_t)oparg(2, 0xFFFF)];
|
|
elen = fd->environments_length;
|
|
fn = dst_gcalloc(DST_MEMORY_FUNCTION, sizeof(DstFunction) + (elen * sizeof(DstFuncEnv *)));
|
|
fn->def = fd;
|
|
{
|
|
int32_t i;
|
|
for (i = 0; i < elen; ++i) {
|
|
int32_t inherit = fd->environments[i];
|
|
if (inherit == -1) {
|
|
DstStackFrame *frame = dst_stack_frame(stack);
|
|
if (!frame->env) {
|
|
/* Lazy capture of current stack frame */
|
|
DstFuncEnv *env = dst_gcalloc(DST_MEMORY_FUNCENV, sizeof(DstFuncEnv));
|
|
env->offset = fiber->frame;
|
|
env->as.fiber = fiber;
|
|
env->length = func->def->slotcount;
|
|
frame->env = env;
|
|
}
|
|
fn->envs[i] = frame->env;
|
|
} else {
|
|
fn->envs[i] = func->envs[inherit];
|
|
}
|
|
}
|
|
}
|
|
stack[oparg(1, 0xFF)] = dst_wrap_function(fn);
|
|
pc++;
|
|
vm_checkgc_next();
|
|
}
|
|
|
|
VM_OP(DOP_PUSH)
|
|
dst_fiber_push(fiber, stack[oparg(1, 0xFFFFFF)]);
|
|
pc++;
|
|
stack = fiber->data + fiber->frame;
|
|
vm_checkgc_next();
|
|
|
|
VM_OP(DOP_PUSH_2)
|
|
dst_fiber_push2(fiber,
|
|
stack[oparg(1, 0xFF)],
|
|
stack[oparg(2, 0xFFFF)]);
|
|
pc++;
|
|
stack = fiber->data + fiber->frame;
|
|
vm_checkgc_next();
|
|
|
|
VM_OP(DOP_PUSH_3)
|
|
dst_fiber_push3(fiber,
|
|
stack[oparg(1, 0xFF)],
|
|
stack[oparg(2, 0xFF)],
|
|
stack[oparg(3, 0xFF)]);
|
|
pc++;
|
|
stack = fiber->data + fiber->frame;
|
|
vm_checkgc_next();
|
|
|
|
VM_OP(DOP_PUSH_ARRAY)
|
|
{
|
|
const Dst *vals;
|
|
int32_t len;
|
|
if (dst_seq_view(stack[oparg(1, 0xFFFFFF)], &vals, &len)) {
|
|
dst_fiber_pushn(fiber, vals, len);
|
|
} else {
|
|
vm_throw("expected array/tuple");
|
|
}
|
|
}
|
|
pc++;
|
|
stack = fiber->data + fiber->frame;
|
|
vm_checkgc_next();
|
|
|
|
VM_OP(DOP_CALL)
|
|
{
|
|
Dst callee = stack[oparg(2, 0xFFFF)];
|
|
if (fiber->maxstack &&
|
|
fiber->stacktop > fiber->maxstack) {
|
|
vm_throw("stack overflow");
|
|
}
|
|
if (dst_checktype(callee, DST_FUNCTION)) {
|
|
func = dst_unwrap_function(callee);
|
|
dst_stack_frame(stack)->pc = pc;
|
|
dst_fiber_funcframe(fiber, func);
|
|
stack = fiber->data + fiber->frame;
|
|
pc = func->def->bytecode;
|
|
vm_checkgc_next();
|
|
} else if (dst_checktype(callee, DST_CFUNCTION)) {
|
|
DstArgs args;
|
|
args.n = fiber->stacktop - fiber->stackstart;
|
|
dst_fiber_cframe(fiber);
|
|
retreg = dst_wrap_nil();
|
|
args.v = fiber->data + fiber->frame;
|
|
args.ret = &retreg;
|
|
if (dst_unwrap_cfunction(callee)(args)) {
|
|
goto vm_error;
|
|
}
|
|
goto vm_return_cfunc;
|
|
}
|
|
vm_throw("expected function");
|
|
}
|
|
|
|
VM_OP(DOP_TAILCALL)
|
|
{
|
|
Dst callee = stack[oparg(1, 0xFFFFFF)];
|
|
if (dst_checktype(callee, DST_FUNCTION)) {
|
|
func = dst_unwrap_function(callee);
|
|
dst_fiber_funcframe_tail(fiber, func);
|
|
stack = fiber->data + fiber->frame;
|
|
pc = func->def->bytecode;
|
|
vm_checkgc_next();
|
|
} else if (dst_checktype(callee, DST_CFUNCTION)) {
|
|
DstArgs args;
|
|
args.n = fiber->stacktop - fiber->stackstart;
|
|
dst_fiber_cframe(fiber);
|
|
retreg = dst_wrap_nil();
|
|
args.v = fiber->data + fiber->frame;
|
|
args.ret = &retreg;
|
|
if (dst_unwrap_cfunction(callee)(args)) {
|
|
goto vm_error;
|
|
}
|
|
goto vm_return_cfunc_tail;
|
|
}
|
|
vm_throw("expected function");
|
|
}
|
|
|
|
VM_OP(DOP_RESUME)
|
|
{
|
|
DstFiber *nextfiber;
|
|
Dst fiberval = stack[oparg(2, 0xFF)];
|
|
Dst val = stack[oparg(3, 0xFF)];
|
|
vm_assert(dst_checktype(fiberval, DST_FIBER), "expected fiber");
|
|
nextfiber = dst_unwrap_fiber(fiberval);
|
|
switch (nextfiber->status) {
|
|
default:
|
|
vm_throw("expected pending, new, or debug fiber");
|
|
case DST_FIBER_DEBUG:
|
|
break;
|
|
case DST_FIBER_NEW:
|
|
{
|
|
dst_fiber_push(nextfiber, val);
|
|
dst_fiber_funcframe(nextfiber, nextfiber->root);
|
|
nextfiber->flags &= ~DST_FIBER_FLAG_NEW;
|
|
break;
|
|
}
|
|
case DST_FIBER_PENDING:
|
|
{
|
|
DstStackFrame *nextframe = dst_fiber_frame(nextfiber);
|
|
nextfiber->data[nextfiber->frame + ((*nextframe->pc >> 8) & 0xFF)] = val;
|
|
nextframe->pc++;
|
|
break;
|
|
}
|
|
}
|
|
fiber->child = nextfiber;
|
|
retreg = dst_run(nextfiber);
|
|
dst_vm_fiber = fiber;
|
|
switch (nextfiber->status) {
|
|
case DST_FIBER_DEBUG:
|
|
if (nextfiber->flags & DST_FIBER_MASK_DEBUG) goto vm_debug;
|
|
fiber->child = NULL;
|
|
break;
|
|
case DST_FIBER_ERROR:
|
|
if (nextfiber->flags & DST_FIBER_MASK_ERROR) goto vm_error;
|
|
fiber->child = NULL;
|
|
break;
|
|
case DST_FIBER_PENDING:
|
|
if (nextfiber->flags & DST_FIBER_MASK_YIELD) {
|
|
fiber->status = DST_FIBER_PENDING;
|
|
goto vm_exit;
|
|
}
|
|
fiber->child = NULL;
|
|
break;
|
|
default:
|
|
fiber->child = NULL;
|
|
break;
|
|
}
|
|
stack[oparg(1, 0xFF)] = retreg;
|
|
pc++;
|
|
vm_checkgc_next();
|
|
}
|
|
|
|
VM_OP(DOP_YIELD)
|
|
{
|
|
retreg = stack[oparg(2, 0xFFFF)];
|
|
fiber->status = DST_FIBER_PENDING;
|
|
goto vm_exit;
|
|
}
|
|
|
|
VM_OP(DOP_PUT)
|
|
dst_put(stack[oparg(1, 0xFF)],
|
|
stack[oparg(2, 0xFF)],
|
|
stack[oparg(3, 0xFF)]);
|
|
++pc;
|
|
vm_checkgc_next();
|
|
|
|
VM_OP(DOP_PUT_INDEX)
|
|
dst_setindex(stack[oparg(1, 0xFF)],
|
|
stack[oparg(2, 0xFF)],
|
|
oparg(3, 0xFF));
|
|
++pc;
|
|
vm_checkgc_next();
|
|
|
|
VM_OP(DOP_GET)
|
|
stack[oparg(1, 0xFF)] = dst_get(
|
|
stack[oparg(2, 0xFF)],
|
|
stack[oparg(3, 0xFF)]);
|
|
++pc;
|
|
vm_next();
|
|
|
|
VM_OP(DOP_GET_INDEX)
|
|
stack[oparg(1, 0xFF)] = dst_getindex(
|
|
stack[oparg(2, 0xFF)],
|
|
oparg(3, 0xFF));
|
|
++pc;
|
|
vm_next();
|
|
|
|
VM_OP(DOP_LENGTH)
|
|
stack[oparg(1, 0xFF)] = dst_wrap_integer(dst_length(stack[oparg(2, 0xFFFF)]));
|
|
++pc;
|
|
vm_next();
|
|
|
|
/* Return from c function. Simpler than returning from dst function */
|
|
vm_return_cfunc:
|
|
{
|
|
dst_fiber_popframe(fiber);
|
|
if (fiber->frame == 0) goto vm_return_root;
|
|
stack = fiber->data + fiber->frame;
|
|
stack[oparg(1, 0xFF)] = retreg;
|
|
pc++;
|
|
vm_checkgc_next();
|
|
}
|
|
|
|
/* Return from a cfunction that is in tail position (pop 2 stack frames) */
|
|
vm_return_cfunc_tail:
|
|
{
|
|
dst_fiber_popframe(fiber);
|
|
dst_fiber_popframe(fiber);
|
|
if (fiber->frame == 0) goto vm_return_root;
|
|
goto vm_reset;
|
|
}
|
|
|
|
/* Handle returning from stack frame. Expect return value in retreg */
|
|
vm_return:
|
|
{
|
|
dst_fiber_popframe(fiber);
|
|
if (fiber->frame == 0) goto vm_return_root;
|
|
goto vm_reset;
|
|
}
|
|
|
|
/* Exit loop with return value */
|
|
vm_return_root:
|
|
{
|
|
fiber->status = DST_FIBER_DEAD;
|
|
goto vm_exit;
|
|
}
|
|
|
|
/* Handle errors from c functions and vm opcodes */
|
|
vm_error:
|
|
{
|
|
fiber->status = DST_FIBER_ERROR;
|
|
goto vm_exit;
|
|
}
|
|
|
|
/* Handle debugger interrupts */
|
|
vm_debug:
|
|
{
|
|
fiber->status = DST_FIBER_DEBUG;
|
|
retreg = dst_wrap_nil();
|
|
goto vm_exit;
|
|
}
|
|
|
|
/* Exit from vm loop */
|
|
vm_exit:
|
|
{
|
|
dst_stack_frame(stack)->pc = pc;
|
|
dst_vm_stackn--;
|
|
dst_gcunroot(dst_wrap_fiber(fiber));
|
|
dst_vm_fiber = old_vm_fiber;
|
|
return retreg;
|
|
}
|
|
|
|
/* Reset state of machine */
|
|
vm_reset:
|
|
{
|
|
stack = fiber->data + fiber->frame;
|
|
func = dst_stack_frame(stack)->func;
|
|
pc = dst_stack_frame(stack)->pc;
|
|
stack[oparg(1, 0xFF)] = retreg;
|
|
pc++;
|
|
vm_checkgc_next();
|
|
}
|
|
|
|
VM_END()
|
|
|
|
#undef oparg
|
|
|
|
#undef vm_error
|
|
#undef vm_assert
|
|
#undef vm_binop
|
|
#undef vm_binop_real
|
|
#undef vm_binop_integer
|
|
#undef vm_binop_immediate
|
|
|
|
}
|
|
|
|
Dst dst_resume(DstFiber *fiber, int32_t argn, const Dst *argv) {
|
|
switch (fiber->status) {
|
|
default:
|
|
dst_exit("expected new, pending or debug fiber");
|
|
case DST_FIBER_DEBUG:
|
|
break;
|
|
case DST_FIBER_NEW:
|
|
{
|
|
int32_t i;
|
|
for (i = 0; i < argn; i++)
|
|
dst_fiber_push(fiber, argv[i]);
|
|
dst_fiber_funcframe(fiber, fiber->root);
|
|
fiber->flags &= ~DST_FIBER_FLAG_NEW;
|
|
break;
|
|
}
|
|
case DST_FIBER_PENDING:
|
|
{
|
|
DstStackFrame *frame = dst_fiber_frame(fiber);
|
|
fiber->data[fiber->frame + ((*frame->pc >> 8) & 0xFF)] = argn > 0
|
|
? argv[0]
|
|
: dst_wrap_nil();
|
|
frame->pc++;
|
|
break;
|
|
}
|
|
}
|
|
return dst_run(fiber);
|
|
}
|
|
|
|
/* Setup VM */
|
|
int dst_init() {
|
|
/* Garbage collection */
|
|
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 */
|
|
dst_vm_gc_interval = 0x10000;
|
|
dst_symcache_init();
|
|
/* Initialize gc roots */
|
|
dst_vm_roots = NULL;
|
|
dst_vm_root_count = 0;
|
|
dst_vm_root_capacity = 0;
|
|
return 0;
|
|
}
|
|
|
|
/* Clear all memory associated with the VM */
|
|
void dst_deinit() {
|
|
dst_clear_memory();
|
|
dst_symcache_deinit();
|
|
free(dst_vm_roots);
|
|
dst_vm_roots = NULL;
|
|
dst_vm_root_count = 0;
|
|
dst_vm_root_capacity = 0;
|
|
}
|