2024-11-04 13:25:31 -05:00
|
|
|
use std::{
|
|
|
|
cmp::Ordering,
|
|
|
|
collections::HashMap,
|
|
|
|
rc::Rc,
|
|
|
|
sync::{atomic::AtomicBool, Arc},
|
|
|
|
};
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
chunk::Instruction,
|
|
|
|
exception::{throw, Exception, Result},
|
2024-11-12 15:40:51 -05:00
|
|
|
lstring::{LStr, LString},
|
2024-11-04 13:25:31 -05:00
|
|
|
parser::ast::{BinaryOp, UnaryOp},
|
2024-11-04 13:31:53 -05:00
|
|
|
symbol::{
|
|
|
|
Symbol, SYM_CALL_STACK_OVERFLOW, SYM_INTERRUPTED, SYM_NAME_ERROR, SYM_TYPE_ERROR,
|
|
|
|
},
|
2024-11-04 13:25:31 -05:00
|
|
|
value::{
|
|
|
|
function::{FuncAttrs, Function, NativeFunc},
|
|
|
|
Value,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
struct TryFrame {
|
|
|
|
idx: usize,
|
|
|
|
stack_len: usize,
|
|
|
|
}
|
2024-02-21 11:04:18 -05:00
|
|
|
|
|
|
|
struct CallFrame {
|
|
|
|
func: Rc<Function>,
|
2024-02-22 14:27:35 -05:00
|
|
|
locals: Vec<Value>,
|
|
|
|
try_frames: Vec<TryFrame>,
|
2024-02-21 11:04:18 -05:00
|
|
|
ip: usize,
|
2024-02-22 14:27:35 -05:00
|
|
|
root: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CallFrame {
|
|
|
|
fn new(func: Rc<Function>, locals: Vec<Value>) -> Self {
|
|
|
|
Self {
|
|
|
|
func,
|
|
|
|
locals,
|
2024-02-28 11:47:15 -05:00
|
|
|
try_frames: Vec::new(),
|
|
|
|
ip: 0,
|
|
|
|
root: false,
|
2024-02-22 14:27:35 -05:00
|
|
|
}
|
|
|
|
}
|
2024-02-21 11:04:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Vm {
|
|
|
|
stack: Vec<Value>,
|
|
|
|
call_stack: Vec<CallFrame>,
|
|
|
|
stack_max: usize,
|
|
|
|
globals: HashMap<Symbol, Value>,
|
2024-02-27 00:18:43 -05:00
|
|
|
interrupt: Arc<AtomicBool>,
|
2024-11-12 15:40:51 -05:00
|
|
|
args: Vec<LString>,
|
2024-02-21 11:04:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn binary_op(o: BinaryOp, a: Value, b: Value) -> Result<Value> {
|
|
|
|
match o {
|
|
|
|
BinaryOp::Add => a + b,
|
|
|
|
BinaryOp::Sub => a - b,
|
|
|
|
BinaryOp::Mul => a * b,
|
|
|
|
BinaryOp::Div => a / b,
|
|
|
|
BinaryOp::Mod => a.modulo(b),
|
|
|
|
BinaryOp::IntDiv => a.int_div(b),
|
|
|
|
BinaryOp::Pow => a.pow(b),
|
2024-03-30 12:21:09 -04:00
|
|
|
BinaryOp::Shl => a << b,
|
|
|
|
BinaryOp::Shr => a >> b,
|
|
|
|
BinaryOp::BitAnd => a & b,
|
|
|
|
BinaryOp::BitXor => a ^ b,
|
|
|
|
BinaryOp::BitOr => a | b,
|
2024-02-21 11:04:18 -05:00
|
|
|
BinaryOp::Eq => Ok(Value::Bool(a == b)),
|
|
|
|
BinaryOp::Ne => Ok(Value::Bool(a != b)),
|
|
|
|
BinaryOp::Gt => a.val_cmp(&b).map(|o| Value::Bool(o == Ordering::Greater)),
|
|
|
|
BinaryOp::Ge => a.val_cmp(&b).map(|o| Value::Bool(o != Ordering::Less)),
|
|
|
|
BinaryOp::Lt => a.val_cmp(&b).map(|o| Value::Bool(o == Ordering::Less)),
|
|
|
|
BinaryOp::Le => a.val_cmp(&b).map(|o| Value::Bool(o != Ordering::Greater)),
|
|
|
|
BinaryOp::Range => a.range(&b, false),
|
|
|
|
BinaryOp::RangeIncl => a.range(&b, true),
|
|
|
|
BinaryOp::Concat => a.concat(&b),
|
2024-02-23 14:54:34 -05:00
|
|
|
BinaryOp::Append => a.append(b),
|
2024-02-21 11:04:18 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn unary_op(o: UnaryOp, a: Value) -> Result<Value> {
|
|
|
|
match o {
|
|
|
|
UnaryOp::Neg => -a,
|
|
|
|
UnaryOp::Not => Ok(Value::Bool(!a.truthy())),
|
|
|
|
UnaryOp::RangeEndless => a.range_endless(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-27 00:18:43 -05:00
|
|
|
enum CallOutcome {
|
|
|
|
Call(Vec<Value>),
|
|
|
|
Partial(Value),
|
|
|
|
}
|
|
|
|
|
2024-10-31 12:36:53 -04:00
|
|
|
fn get_call_outcome(args: Vec<Value>) -> Result<CallOutcome> {
|
2024-02-27 00:18:43 -05:00
|
|
|
let f = &args[0];
|
|
|
|
let Some(attrs) = f.func_attrs() else {
|
|
|
|
throw!(*SYM_TYPE_ERROR, "cannot call non-function {f:#}")
|
|
|
|
};
|
|
|
|
let argc = args.len() - 1;
|
2024-11-04 13:25:31 -05:00
|
|
|
match argc.cmp(&attrs.arity) {
|
|
|
|
Ordering::Equal => Ok(CallOutcome::Call(args)),
|
2024-11-12 15:40:51 -05:00
|
|
|
Ordering::Greater => {
|
|
|
|
throw!(*SYM_TYPE_ERROR, "too many arguments for function {}", f)
|
|
|
|
}
|
2024-11-04 13:25:31 -05:00
|
|
|
Ordering::Less => {
|
|
|
|
let remaining = attrs.arity - argc;
|
|
|
|
let f = f.clone();
|
|
|
|
let nf = move |vm: &mut Vm, inner_args: Vec<Value>| {
|
|
|
|
let mut ia = inner_args.into_iter();
|
|
|
|
ia.next();
|
|
|
|
let args: Vec<Value> = args.clone().into_iter().chain(ia).collect();
|
|
|
|
vm.call_value(f.clone(), args)
|
|
|
|
};
|
|
|
|
let nf = NativeFunc {
|
|
|
|
attrs: FuncAttrs {
|
|
|
|
arity: remaining,
|
|
|
|
name: None,
|
|
|
|
},
|
|
|
|
func: Box::new(nf),
|
|
|
|
};
|
|
|
|
Ok(CallOutcome::Partial(nf.into()))
|
|
|
|
}
|
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
}
|
|
|
|
|
2024-02-21 11:04:18 -05:00
|
|
|
impl Vm {
|
2024-11-12 15:40:51 -05:00
|
|
|
pub fn new(stack_max: usize, args: Vec<LString>) -> Self {
|
2024-02-21 11:04:18 -05:00
|
|
|
Self {
|
|
|
|
stack: Vec::with_capacity(16),
|
|
|
|
call_stack: Vec::with_capacity(16),
|
|
|
|
globals: HashMap::with_capacity(16),
|
|
|
|
stack_max,
|
2024-11-12 15:40:51 -05:00
|
|
|
args,
|
2024-02-27 00:18:43 -05:00
|
|
|
interrupt: Arc::new(AtomicBool::new(false)),
|
2024-02-21 11:04:18 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-27 00:18:43 -05:00
|
|
|
pub fn get_interrupt(&self) -> Arc<AtomicBool> {
|
|
|
|
self.interrupt.clone()
|
|
|
|
}
|
2024-02-23 14:54:34 -05:00
|
|
|
|
2024-02-21 11:04:18 -05:00
|
|
|
pub fn set_global(&mut self, name: Symbol, val: Value) {
|
|
|
|
self.globals.insert(name, val);
|
|
|
|
}
|
|
|
|
|
2024-03-30 12:21:09 -04:00
|
|
|
pub fn set_global_name<'a, S>(&mut self, name: S, val: Value)
|
2024-11-04 13:25:31 -05:00
|
|
|
where
|
|
|
|
S: Into<&'a LStr>,
|
|
|
|
{
|
2024-03-30 12:21:09 -04:00
|
|
|
self.globals.insert(Symbol::get(name.into()), val);
|
2024-02-23 14:54:34 -05:00
|
|
|
}
|
|
|
|
|
2024-02-21 11:04:18 -05:00
|
|
|
pub fn get_global(&self, name: Symbol) -> Option<&Value> {
|
|
|
|
self.globals.get(&name)
|
|
|
|
}
|
|
|
|
|
2024-02-27 00:18:43 -05:00
|
|
|
pub fn globals(&self) -> &HashMap<Symbol, Value> {
|
|
|
|
&self.globals
|
|
|
|
}
|
|
|
|
|
2024-11-12 15:40:51 -05:00
|
|
|
pub fn args(&self) -> &Vec<LString> {
|
|
|
|
&self.args
|
|
|
|
}
|
|
|
|
|
2024-02-27 00:18:43 -05:00
|
|
|
pub fn call_value(&mut self, value: Value, args: Vec<Value>) -> Result<Value> {
|
|
|
|
self.check_interrupt()?;
|
|
|
|
match get_call_outcome(args)? {
|
|
|
|
CallOutcome::Partial(v) => Ok(v),
|
|
|
|
CallOutcome::Call(args) => match value {
|
|
|
|
Value::Function(f) => self.run_function(f, args),
|
|
|
|
Value::NativeFunc(f) => (f.func)(self, args),
|
2024-11-04 13:25:31 -05:00
|
|
|
_ => unreachable!("already verified by calling get_call_type"),
|
|
|
|
},
|
2024-02-27 00:18:43 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn run_function(&mut self, func: Rc<Function>, args: Vec<Value>) -> Result<Value> {
|
|
|
|
if func.attrs.arity + 1 != args.len() {
|
|
|
|
throw!(*SYM_TYPE_ERROR, "function call with wrong argument count");
|
|
|
|
}
|
|
|
|
let init_stack_len = self.stack.len();
|
|
|
|
let mut frame = CallFrame::new(func, args);
|
|
|
|
frame.root = true;
|
|
|
|
|
|
|
|
loop {
|
|
|
|
let instr = frame.func.chunk.instrs[frame.ip];
|
|
|
|
frame.ip += 1;
|
|
|
|
match self.run_instr(&mut frame, instr) {
|
|
|
|
Ok(None) => (),
|
|
|
|
Ok(Some(v)) => {
|
|
|
|
self.stack.truncate(init_stack_len);
|
|
|
|
return Ok(v)
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
if let Err(e) = self.handle_exception(&mut frame, e) {
|
|
|
|
self.stack.truncate(init_stack_len);
|
|
|
|
return Err(e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn handle_exception(&mut self, frame: &mut CallFrame, exc: Exception) -> Result<()> {
|
|
|
|
loop {
|
|
|
|
while let Some(try_frame) = frame.try_frames.pop() {
|
|
|
|
let table = &frame.func.chunk.try_tables[try_frame.idx];
|
|
|
|
for catch in &table.catches {
|
2024-11-04 13:31:53 -05:00
|
|
|
if catch.types.is_none()
|
|
|
|
|| catch.types.as_ref().unwrap().contains(&exc.ty)
|
|
|
|
{
|
2024-02-27 00:18:43 -05:00
|
|
|
frame.ip = catch.addr;
|
|
|
|
frame.locals.truncate(table.local_count);
|
|
|
|
self.stack.truncate(try_frame.stack_len);
|
|
|
|
self.stack.push(Value::Table(exc.to_table()));
|
|
|
|
return Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if frame.root {
|
|
|
|
return Err(exc)
|
|
|
|
}
|
|
|
|
*frame = self.call_stack.pop().expect("no root frame");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-21 11:04:18 -05:00
|
|
|
#[inline]
|
|
|
|
fn push(&mut self, v: Value) {
|
|
|
|
self.stack.push(v);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn pop(&mut self) -> Value {
|
|
|
|
self.stack.pop().expect("temporary stack underflow")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn pop_n(&mut self, n: usize) -> Vec<Value> {
|
|
|
|
let res = self.stack.split_off(self.stack.len() - n);
|
|
|
|
assert!(res.len() == n, "temporary stack underflow");
|
|
|
|
res
|
|
|
|
}
|
|
|
|
|
2024-02-27 00:18:43 -05:00
|
|
|
fn check_interrupt(&mut self) -> Result<()> {
|
2024-11-04 13:25:31 -05:00
|
|
|
if self
|
|
|
|
.interrupt
|
|
|
|
.fetch_and(false, std::sync::atomic::Ordering::Relaxed)
|
|
|
|
{
|
2024-02-27 00:18:43 -05:00
|
|
|
throw!(*SYM_INTERRUPTED)
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2024-02-21 11:04:18 -05:00
|
|
|
|
2024-11-04 13:31:53 -05:00
|
|
|
fn run_instr(
|
|
|
|
&mut self,
|
|
|
|
frame: &mut CallFrame,
|
|
|
|
instr: Instruction,
|
|
|
|
) -> Result<Option<Value>> {
|
2024-02-21 11:04:18 -05:00
|
|
|
use Instruction as I;
|
2024-02-23 14:54:34 -05:00
|
|
|
|
2024-02-22 14:27:35 -05:00
|
|
|
match instr {
|
2024-02-27 00:18:43 -05:00
|
|
|
// do nothing
|
2024-02-22 14:27:35 -05:00
|
|
|
I::Nop => (),
|
2024-02-27 00:18:43 -05:00
|
|
|
// [] -> [locals[n]]
|
2024-11-04 13:25:31 -05:00
|
|
|
I::LoadLocal(n) => self.push(frame.locals[usize::from(n)].clone()),
|
2024-02-27 00:18:43 -05:00
|
|
|
// [x] -> [], locals[n] = x
|
2024-11-04 13:25:31 -05:00
|
|
|
I::StoreLocal(n) => frame.locals[usize::from(n)] = self.pop(),
|
2024-02-27 00:18:43 -05:00
|
|
|
// [x] -> [], locals.push(x)
|
2024-11-04 13:25:31 -05:00
|
|
|
I::NewLocal => frame.locals.push(self.pop()),
|
2024-02-27 00:18:43 -05:00
|
|
|
// locals.pop_n(n)
|
2024-11-04 13:25:31 -05:00
|
|
|
I::DropLocal(n) => frame.locals.truncate(frame.locals.len() - usize::from(n)),
|
2024-02-27 00:18:43 -05:00
|
|
|
// [] -> [globals[s]]
|
2024-02-22 14:27:35 -05:00
|
|
|
I::LoadGlobal(s) => {
|
|
|
|
let sym = unsafe { s.to_symbol_unchecked() };
|
|
|
|
let v = match self.globals.get(&sym) {
|
|
|
|
Some(v) => v.clone(),
|
2024-02-23 14:54:34 -05:00
|
|
|
None => throw!(*SYM_NAME_ERROR, "undefined global {}", sym.name()),
|
2024-02-22 14:27:35 -05:00
|
|
|
};
|
|
|
|
self.push(v);
|
2024-11-04 13:25:31 -05:00
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
// [x] -> [], globals[s] = x
|
2024-02-22 14:27:35 -05:00
|
|
|
I::StoreGlobal(s) => {
|
|
|
|
let sym = unsafe { s.to_symbol_unchecked() };
|
|
|
|
let v = self.pop();
|
|
|
|
self.globals.insert(sym, v);
|
2024-11-04 13:25:31 -05:00
|
|
|
}
|
|
|
|
I::CloseOver(n) => {
|
|
|
|
let n = usize::from(n);
|
|
|
|
let v = std::mem::replace(&mut frame.locals[n], Value::Nil);
|
|
|
|
let v = v.to_cell();
|
|
|
|
frame.locals[n] = v.clone();
|
|
|
|
self.push(v);
|
|
|
|
}
|
|
|
|
I::Closure(n) => {
|
|
|
|
let f = frame.func.chunk.consts[usize::from(n)].clone();
|
|
|
|
let Value::Function(f) = f else {
|
|
|
|
panic!("attempt to build closure from non-closure constant")
|
|
|
|
};
|
|
|
|
let mut f = f.as_ref().clone();
|
|
|
|
|
|
|
|
let captured: Vec<_> = self
|
|
|
|
.pop_n(f.state.len())
|
|
|
|
.into_iter()
|
|
|
|
.map(|v| {
|
|
|
|
let Value::Cell(v) = v else {
|
|
|
|
panic!("attempt to build closure from non-cell local");
|
|
|
|
};
|
|
|
|
v
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
f.state = captured.into_boxed_slice();
|
|
|
|
self.push(f.into());
|
|
|
|
}
|
|
|
|
I::LoadUpvalue(n) => {
|
|
|
|
let v = frame.func.state[usize::from(n)].clone();
|
|
|
|
self.push(v.borrow().clone());
|
|
|
|
}
|
|
|
|
I::StoreUpvalue(n) => {
|
|
|
|
let v = frame.func.state[usize::from(n)].clone();
|
|
|
|
*v.borrow_mut() = self.pop();
|
|
|
|
}
|
|
|
|
I::ContinueUpvalue(n) => {
|
|
|
|
let v = frame.func.state[usize::from(n)].clone();
|
|
|
|
self.push(Value::Cell(v));
|
|
|
|
}
|
|
|
|
I::LoadClosedLocal(n) => {
|
|
|
|
let Value::Cell(c) = &frame.locals[usize::from(n)] else {
|
|
|
|
panic!("attempt to load from closed non-cell local");
|
|
|
|
};
|
2024-10-31 12:36:53 -04:00
|
|
|
self.push(c.borrow().clone());
|
2024-11-04 13:25:31 -05:00
|
|
|
}
|
|
|
|
I::StoreClosedLocal(n) => {
|
|
|
|
let Value::Cell(c) = &frame.locals[usize::from(n)] else {
|
|
|
|
panic!("attempt to store to closed non-cell local");
|
|
|
|
};
|
|
|
|
*c.borrow_mut() = self.pop();
|
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
// [] -> [consts[n]]
|
2024-11-04 13:25:31 -05:00
|
|
|
I::Const(n) => self.push(frame.func.chunk.consts[usize::from(n)].clone()),
|
2024-02-27 00:18:43 -05:00
|
|
|
// [] -> [nil]
|
2024-02-22 14:27:35 -05:00
|
|
|
I::Nil => self.push(Value::Nil),
|
2024-02-27 00:18:43 -05:00
|
|
|
// [] -> [b]
|
2024-02-22 14:27:35 -05:00
|
|
|
I::Bool(b) => self.push(Value::Bool(b)),
|
2024-02-27 00:18:43 -05:00
|
|
|
// [] -> [s]
|
2024-02-23 14:54:34 -05:00
|
|
|
I::Symbol(s) => {
|
|
|
|
let sym = unsafe { Symbol::from_id_unchecked(u32::from(s)) };
|
2024-02-22 14:27:35 -05:00
|
|
|
self.push(Value::Symbol(sym));
|
2024-11-04 13:25:31 -05:00
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
// [] -> [n]
|
2024-02-22 14:27:35 -05:00
|
|
|
I::Int(n) => self.push(Value::Int(i64::from(n))),
|
2024-02-27 00:18:43 -05:00
|
|
|
// [x] -> [x,x]
|
2024-02-22 14:27:35 -05:00
|
|
|
I::Dup => self.push(self.stack[self.stack.len() - 1].clone()),
|
2024-02-27 00:18:43 -05:00
|
|
|
// [x,y] -> [x,y,x,y]
|
2024-02-22 14:27:35 -05:00
|
|
|
I::DupTwo => {
|
|
|
|
self.push(self.stack[self.stack.len() - 2].clone());
|
|
|
|
self.push(self.stack[self.stack.len() - 2].clone());
|
2024-11-04 13:25:31 -05:00
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
// [a0,a1...an] -> []
|
2024-11-12 15:40:51 -05:00
|
|
|
I::Drop => {
|
|
|
|
self.pop();
|
2024-11-04 13:25:31 -05:00
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
// [x,y] -> [y,x]
|
2024-02-22 14:27:35 -05:00
|
|
|
I::Swap => {
|
|
|
|
let len = self.stack.len();
|
|
|
|
self.stack.swap(len - 1, len - 2);
|
2024-11-04 13:25:31 -05:00
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
// [x,y] -> [y op x]
|
2024-02-22 14:27:35 -05:00
|
|
|
I::BinaryOp(op) => {
|
|
|
|
let b = self.pop();
|
|
|
|
let a = self.pop();
|
|
|
|
self.push(binary_op(op, a, b)?);
|
2024-11-04 13:25:31 -05:00
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
// [x] -> [op x]
|
2024-02-22 14:27:35 -05:00
|
|
|
I::UnaryOp(op) => {
|
|
|
|
let a = self.pop();
|
|
|
|
self.push(unary_op(op, a)?);
|
2024-11-04 13:25:31 -05:00
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
// [a0,a1...an] -.> [[a0,a1...an]]
|
2024-02-22 14:27:35 -05:00
|
|
|
I::NewList(n) => {
|
|
|
|
let list = self.pop_n(n as usize);
|
2024-03-07 19:38:57 -05:00
|
|
|
self.push(list.into());
|
2024-11-04 13:25:31 -05:00
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
// [l,a0,a1...an] -.> [l ++ [a0,a1...an]]
|
2024-02-22 14:27:35 -05:00
|
|
|
I::GrowList(n) => {
|
|
|
|
let ext = self.pop_n(n as usize);
|
|
|
|
let list = self.pop();
|
2024-11-04 13:25:31 -05:00
|
|
|
let Value::List(list) = list else {
|
|
|
|
panic!("not a list")
|
|
|
|
};
|
2024-02-22 14:27:35 -05:00
|
|
|
list.borrow_mut().extend(ext);
|
|
|
|
self.push(Value::List(list));
|
2024-11-04 13:25:31 -05:00
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
// [k0,v0...kn,vn] -.> [{k0=v0...kn=vn}]
|
2024-02-22 14:27:35 -05:00
|
|
|
I::NewTable(n) => {
|
|
|
|
let mut table = HashMap::new();
|
|
|
|
for _ in 0..n {
|
|
|
|
let v = self.pop();
|
|
|
|
let k = self.pop();
|
|
|
|
table.insert(k.try_into()?, v);
|
|
|
|
}
|
2024-03-07 19:38:57 -05:00
|
|
|
self.push(table.into());
|
2024-11-04 13:25:31 -05:00
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
// [t,k0,v0...kn,vn] -> [t ++ {k0=v0...kn=vn}]
|
2024-02-22 14:27:35 -05:00
|
|
|
I::GrowTable(n) => {
|
|
|
|
let mut ext = self.pop_n(2 * n as usize);
|
|
|
|
let table = self.pop();
|
2024-11-04 13:25:31 -05:00
|
|
|
let Value::Table(table) = table else {
|
|
|
|
panic!("not a table")
|
|
|
|
};
|
2024-02-22 14:27:35 -05:00
|
|
|
let mut table_ref = table.borrow_mut();
|
|
|
|
for _ in 0..n {
|
|
|
|
// can't panic: pop_n checked that ext would have len 2*n
|
|
|
|
let v = ext.pop().unwrap();
|
|
|
|
let k = ext.pop().unwrap();
|
|
|
|
table_ref.insert(k.try_into()?, v);
|
|
|
|
}
|
|
|
|
drop(table_ref);
|
|
|
|
self.push(Value::Table(table));
|
2024-11-04 13:25:31 -05:00
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
// [ct, idx] -> [ct!idx]
|
2024-02-22 14:27:35 -05:00
|
|
|
I::Index => {
|
|
|
|
let idx = self.pop();
|
|
|
|
let ct = self.pop();
|
|
|
|
self.push(ct.index(idx)?);
|
2024-11-04 13:25:31 -05:00
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
// [ct, idx, v] -> [v], ct!idx = v
|
2024-02-22 14:27:35 -05:00
|
|
|
I::StoreIndex => {
|
|
|
|
let v = self.pop();
|
|
|
|
let idx = self.pop();
|
|
|
|
let ct = self.pop();
|
2024-02-27 00:18:43 -05:00
|
|
|
ct.store_index(self, idx, v.clone())?;
|
2024-02-22 14:27:35 -05:00
|
|
|
self.push(v);
|
2024-11-04 13:25:31 -05:00
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
// ip = n
|
2024-02-23 14:54:34 -05:00
|
|
|
I::Jump(n) => {
|
2024-02-27 00:18:43 -05:00
|
|
|
self.check_interrupt()?;
|
2024-03-07 19:38:57 -05:00
|
|
|
frame.ip = usize::from(n);
|
2024-11-04 13:25:31 -05:00
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
// [v] ->, [], if v then ip = n
|
2024-11-04 13:25:31 -05:00
|
|
|
I::JumpTrue(n) => {
|
|
|
|
if self.pop().truthy() {
|
|
|
|
self.check_interrupt()?;
|
|
|
|
frame.ip = usize::from(n);
|
|
|
|
}
|
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
// [v] ->, [], if not v then ip = n
|
2024-11-04 13:25:31 -05:00
|
|
|
I::JumpFalse(n) => {
|
|
|
|
if !self.pop().truthy() {
|
|
|
|
self.check_interrupt()?;
|
|
|
|
frame.ip = usize::from(n);
|
|
|
|
}
|
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
// [v] -> [iter(v)]
|
2024-02-22 14:27:35 -05:00
|
|
|
I::IterBegin => {
|
|
|
|
let iter = self.pop().to_iter_function()?;
|
|
|
|
self.push(iter);
|
2024-11-04 13:25:31 -05:00
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
// [i,cell(v)] -> [i,v]
|
|
|
|
// [i,nil] -> [], ip = n
|
|
|
|
I::IterTest(n) => {
|
2024-03-07 19:38:57 -05:00
|
|
|
if let Some(v) = self.pop().iter_unpack() {
|
|
|
|
self.push(v);
|
|
|
|
} else {
|
|
|
|
self.pop();
|
|
|
|
frame.ip = usize::from(n);
|
2024-02-27 00:18:43 -05:00
|
|
|
}
|
2024-11-04 13:25:31 -05:00
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
// try_frames.push(t, stack.len())
|
2024-02-22 14:27:35 -05:00
|
|
|
I::BeginTry(t) => {
|
|
|
|
let tryframe = TryFrame {
|
|
|
|
idx: usize::from(t),
|
2024-11-04 13:25:31 -05:00
|
|
|
stack_len: self.stack.len(),
|
2024-02-22 14:27:35 -05:00
|
|
|
};
|
|
|
|
frame.try_frames.push(tryframe);
|
2024-11-04 13:25:31 -05:00
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
// try_frames.pop()
|
2024-02-22 14:27:35 -05:00
|
|
|
I::EndTry => {
|
|
|
|
frame.try_frames.pop().expect("no try to pop");
|
2024-11-04 13:25:31 -05:00
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
// [f,a0,a1...an] -> [f(a0,a1...an)]
|
2024-02-22 14:27:35 -05:00
|
|
|
I::Call(n) => {
|
2024-11-12 15:40:51 -05:00
|
|
|
self.check_interrupt()?;
|
2024-02-22 14:27:35 -05:00
|
|
|
let n = usize::from(n);
|
2024-02-27 00:18:43 -05:00
|
|
|
|
|
|
|
let args = self.pop_n(n + 1);
|
|
|
|
|
|
|
|
let args = match get_call_outcome(args)? {
|
|
|
|
CallOutcome::Call(args) => args,
|
|
|
|
CallOutcome::Partial(v) => {
|
|
|
|
self.push(v);
|
|
|
|
return Ok(None)
|
2024-02-22 14:27:35 -05:00
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
if let Value::NativeFunc(nf) = &args[0] {
|
|
|
|
let nf = nf.clone();
|
2024-02-21 11:04:18 -05:00
|
|
|
|
2024-02-28 10:46:42 -05:00
|
|
|
// safety: frame is restored immediately
|
|
|
|
// after function call ends
|
2024-02-28 11:47:15 -05:00
|
|
|
// ~25% performance improvement in
|
|
|
|
// code heavy on native function calls
|
2024-02-27 10:56:32 -05:00
|
|
|
unsafe {
|
|
|
|
let f = std::ptr::read(frame);
|
|
|
|
self.call_stack.push(f);
|
|
|
|
}
|
|
|
|
|
2024-02-28 10:46:42 -05:00
|
|
|
let res = (nf.func)(self, args);
|
2024-02-27 10:56:32 -05:00
|
|
|
|
2024-02-28 11:47:15 -05:00
|
|
|
// safety: frame was referencing invalid memory due to
|
|
|
|
// previous unsafe block, write will fix that
|
|
|
|
unsafe {
|
|
|
|
let f = self.call_stack.pop().expect("no frame to pop");
|
|
|
|
std::ptr::write(frame, f);
|
|
|
|
}
|
2024-02-28 10:46:42 -05:00
|
|
|
|
2024-02-28 11:47:15 -05:00
|
|
|
// make sure we restored the value of frame
|
|
|
|
// before propagating exceptions
|
|
|
|
let res = res?;
|
2024-02-22 14:27:35 -05:00
|
|
|
|
2024-10-31 12:36:53 -04:00
|
|
|
self.push(res);
|
2024-02-27 00:18:43 -05:00
|
|
|
} else if let Value::Function(func) = &args[0] {
|
2024-02-22 14:27:35 -05:00
|
|
|
if self.call_stack.len() + 1 >= self.stack_max {
|
2024-02-23 14:54:34 -05:00
|
|
|
throw!(*SYM_CALL_STACK_OVERFLOW, "call stack overflow")
|
2024-02-22 14:27:35 -05:00
|
|
|
}
|
|
|
|
|
2024-02-27 10:56:32 -05:00
|
|
|
let new_frame = CallFrame::new(func.clone(), args);
|
|
|
|
let old_frame = std::mem::replace(frame, new_frame);
|
|
|
|
self.call_stack.push(old_frame);
|
2024-02-22 14:27:35 -05:00
|
|
|
} else {
|
2024-02-27 00:18:43 -05:00
|
|
|
unreachable!("already verified by calling get_call_type");
|
2024-02-22 14:27:35 -05:00
|
|
|
}
|
2024-11-04 13:25:31 -05:00
|
|
|
}
|
2024-11-12 15:40:51 -05:00
|
|
|
// [f,a0,a1...an] -> [], return f(a0,a1...an)
|
|
|
|
I::Tail(n) => {
|
|
|
|
self.check_interrupt()?;
|
|
|
|
let n = usize::from(n);
|
|
|
|
|
|
|
|
let args = self.pop_n(n + 1);
|
|
|
|
|
|
|
|
let args = match get_call_outcome(args)? {
|
|
|
|
CallOutcome::Call(args) => args,
|
|
|
|
CallOutcome::Partial(v) => {
|
|
|
|
if frame.root {
|
|
|
|
return Ok(Some(v))
|
|
|
|
}
|
|
|
|
self.check_interrupt()?;
|
|
|
|
*frame = self.call_stack.pop().expect("no root frame");
|
|
|
|
return Ok(None)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Value::NativeFunc(nf) = &args[0] {
|
|
|
|
let nf = nf.clone();
|
|
|
|
|
|
|
|
let res = (nf.func)(self, args)?;
|
|
|
|
|
|
|
|
if frame.root {
|
|
|
|
return Ok(Some(res))
|
|
|
|
}
|
|
|
|
|
|
|
|
self.push(res);
|
|
|
|
*frame = self.call_stack.pop().expect("no root frame");
|
|
|
|
} else if let Value::Function(func) = &args[0] {
|
|
|
|
let mut new_frame = CallFrame::new(func.clone(), args);
|
|
|
|
new_frame.root = frame.root;
|
|
|
|
*frame = new_frame;
|
|
|
|
} else {
|
|
|
|
unreachable!("already verified by calling get_call_type");
|
|
|
|
}
|
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
// [v] -> [], return v
|
2024-11-04 13:25:31 -05:00
|
|
|
I::Return if frame.root => return Ok(Some(self.pop())),
|
2024-02-27 00:18:43 -05:00
|
|
|
// [v] -> [], return v
|
2024-02-22 14:27:35 -05:00
|
|
|
I::Return => {
|
2024-02-27 00:18:43 -05:00
|
|
|
self.check_interrupt()?;
|
2024-02-22 14:27:35 -05:00
|
|
|
*frame = self.call_stack.pop().expect("no root frame");
|
2024-11-04 13:25:31 -05:00
|
|
|
}
|
2024-02-22 14:27:35 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(None)
|
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
}
|
2024-02-21 11:04:18 -05:00
|
|
|
|
2024-02-27 00:18:43 -05:00
|
|
|
#[macro_export]
|
|
|
|
macro_rules! vmcall {
|
|
|
|
($vm:expr; $func:expr, $($arg:expr),*) => {{
|
|
|
|
let f = $func;
|
|
|
|
$vm.call_value(f.clone(), vec![f, $($arg),*])
|
|
|
|
}};
|
|
|
|
($vm:expr; $func:expr) => {{
|
|
|
|
let f = $func;
|
|
|
|
$vm.call_value(f.clone(), vec![f])
|
|
|
|
}};
|
|
|
|
}
|
2024-02-21 11:04:18 -05:00
|
|
|
|
2024-02-27 00:18:43 -05:00
|
|
|
#[macro_export]
|
|
|
|
macro_rules! vmcalliter {
|
|
|
|
($($input:tt)*) => {
|
2024-02-28 10:46:42 -05:00
|
|
|
$crate::vmcall!($($input)*).map(|v| v.iter_unpack())
|
2024-02-27 00:18:43 -05:00
|
|
|
}
|
2024-02-21 11:04:18 -05:00
|
|
|
}
|