2020-09-14 14:09:29 -07:00
|
|
|
pub mod consts;
|
2020-10-07 17:21:01 -07:00
|
|
|
pub mod error;
|
2020-09-14 16:32:00 -07:00
|
|
|
pub mod frame;
|
|
|
|
|
pub mod inst;
|
2020-10-07 15:48:24 -07:00
|
|
|
pub mod signal;
|
2020-09-01 17:32:48 -07:00
|
|
|
|
2020-09-14 16:32:00 -07:00
|
|
|
use crate::{
|
2020-10-07 17:21:01 -07:00
|
|
|
obj::{builtin::BUILTIN_OBJS, reserved::*, prelude::*},
|
2020-09-28 16:48:30 -07:00
|
|
|
vm::{consts::ConstPool, frame::*, inst::*, signal::*},
|
2020-09-14 16:32:00 -07:00
|
|
|
};
|
2020-09-01 17:32:48 -07:00
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
2020-09-28 16:48:30 -07:00
|
|
|
pub struct Vm<'c> {
|
2020-09-01 17:32:48 -07:00
|
|
|
stack: Vec<ObjRef>,
|
|
|
|
|
frames: Vec<Frame>,
|
|
|
|
|
pc: usize,
|
|
|
|
|
condition: bool,
|
2020-09-28 16:48:30 -07:00
|
|
|
const_pool: &'c ConstPool,
|
2020-09-14 16:32:00 -07:00
|
|
|
}
|
|
|
|
|
|
2020-09-28 16:48:30 -07:00
|
|
|
impl<'c> Vm<'c> {
|
|
|
|
|
pub fn new(const_pool: &'c ConstPool,) -> Self {
|
2020-09-01 17:32:48 -07:00
|
|
|
Self {
|
|
|
|
|
stack: Default::default(),
|
2020-09-14 16:32:00 -07:00
|
|
|
frames: vec![],
|
2020-09-01 17:32:48 -07:00
|
|
|
pc: 0,
|
|
|
|
|
condition: false,
|
2020-09-28 16:48:30 -07:00
|
|
|
const_pool,
|
2020-09-01 17:32:48 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-14 16:32:00 -07:00
|
|
|
/// Gets the current stack frame, if any.
|
|
|
|
|
pub fn frame(&self) -> Option<&Frame> {
|
|
|
|
|
self.frames.last()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Gets the current stack frame mutably, if any.
|
|
|
|
|
pub fn frame_mut(&mut self) -> Option<&mut Frame> {
|
|
|
|
|
self.frames.last_mut()
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-14 16:37:16 -07:00
|
|
|
/// Gets the list of stack frames.
|
|
|
|
|
pub fn frames(&self) -> &Vec<Frame> {
|
|
|
|
|
&self.frames
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-07 15:48:24 -07:00
|
|
|
/// Mutably gets the list of stack frames.
|
|
|
|
|
pub(crate) fn frames_mut(&mut self) -> &mut Vec<Frame> {
|
|
|
|
|
&mut self.frames
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-14 16:32:00 -07:00
|
|
|
/// Gets the stack.
|
2020-09-01 17:32:48 -07:00
|
|
|
pub fn stack(&self) -> &Vec<ObjRef> {
|
|
|
|
|
&self.stack
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-14 16:32:00 -07:00
|
|
|
/// Gets the stack, mutably.
|
2020-09-01 17:32:48 -07:00
|
|
|
pub fn stack_mut(&mut self) -> &mut Vec<ObjRef> {
|
|
|
|
|
&mut self.stack
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-14 16:32:00 -07:00
|
|
|
/// Pushes a value to the stack.
|
2020-09-01 17:32:48 -07:00
|
|
|
pub fn push(&mut self, value: ObjRef) {
|
|
|
|
|
self.stack_mut().push(value);
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-14 16:32:00 -07:00
|
|
|
/// Pops a value from the stack.
|
2020-09-01 17:32:48 -07:00
|
|
|
pub fn pop(&mut self) -> Option<ObjRef> {
|
|
|
|
|
self.stack_mut().pop()
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-14 16:32:00 -07:00
|
|
|
/// Gets the current program counter address.
|
2020-09-01 17:32:48 -07:00
|
|
|
pub fn pc(&self) -> usize {
|
|
|
|
|
self.pc
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-14 16:32:00 -07:00
|
|
|
/// Set the next program counter value.
|
|
|
|
|
///
|
|
|
|
|
/// This may cause the running program to crash. Handle with care.
|
|
|
|
|
pub fn set_pc(&mut self, pc: usize) {
|
|
|
|
|
self.pc = pc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Gets whether the condition flag has been set or not.
|
2020-09-01 17:32:48 -07:00
|
|
|
pub fn condition(&self) -> bool {
|
|
|
|
|
self.condition
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-14 16:32:00 -07:00
|
|
|
/// Sets the condition flag to the specified value.
|
|
|
|
|
pub fn set_condition(&mut self, condition: bool) {
|
|
|
|
|
self.condition = condition;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-07 17:21:01 -07:00
|
|
|
/// Calls a function.
|
|
|
|
|
pub fn call(&mut self, fun: ObjRef, args: Vec<ObjRef>) -> ObjRef {
|
|
|
|
|
self.handle_signal(Signal::Call(fun, args));
|
|
|
|
|
self.resume_until_return();
|
|
|
|
|
self.pop().expect("return value")
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-24 15:29:44 -07:00
|
|
|
/// Resumes execution of the current program.
|
|
|
|
|
pub fn resume(&mut self) {
|
2020-10-07 15:48:24 -07:00
|
|
|
while !self.frames().is_empty() {
|
|
|
|
|
self.resume_until_return();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Resume execution until the currently executing function returns.
|
|
|
|
|
pub fn resume_until_return(&mut self) {
|
|
|
|
|
// resume execution until control is returned to the originally calling function
|
|
|
|
|
let start_frames = self.frames().len();
|
|
|
|
|
if start_frames == 0 {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while self.frames().len() >= start_frames {
|
|
|
|
|
let frame = self.frame().unwrap();
|
|
|
|
|
let signal = match frame {
|
|
|
|
|
Frame::Native(fun) => {
|
|
|
|
|
let callee = fun.callee().clone();
|
2020-10-07 17:21:01 -07:00
|
|
|
let args = fun.args().clone();
|
|
|
|
|
(*fun.fun_ptr())(callee, self, args)
|
2020-09-24 15:29:44 -07:00
|
|
|
}
|
2020-10-07 15:48:24 -07:00
|
|
|
Frame::User(_) => {
|
|
|
|
|
self.resume_user_fun()
|
2020-09-24 15:29:44 -07:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
self.handle_signal(signal);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-07 15:48:24 -07:00
|
|
|
fn resume_user_fun(&mut self) -> Signal {
|
|
|
|
|
while let Some(Frame::User(_)) = self.frame() {
|
|
|
|
|
if let Some(signal) = self.tick() {
|
|
|
|
|
return signal;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Signal::Return
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-24 15:29:44 -07:00
|
|
|
/// Handles a signal targeting the VM.
|
|
|
|
|
///
|
|
|
|
|
/// The signal may originate from the VM itself, or from an external location. Signals
|
|
|
|
|
/// generally interrupt control flow of a program.
|
|
|
|
|
pub fn handle_signal(&mut self, signal: Signal) {
|
|
|
|
|
match signal {
|
2020-10-07 15:48:24 -07:00
|
|
|
Signal::Call(callee, args) => {
|
|
|
|
|
read_obj!(let callee_obj = callee);
|
|
|
|
|
let frame = callee_obj.as_fun()
|
|
|
|
|
.expect("callable function")
|
|
|
|
|
.create_frame(callee.clone(), self, args);
|
2020-10-08 16:10:10 -07:00
|
|
|
// Jump to the first address of the new function call if it's a user function
|
|
|
|
|
if let Frame::User(_) = &frame {
|
|
|
|
|
self.set_pc(0);
|
|
|
|
|
}
|
2020-10-07 15:48:24 -07:00
|
|
|
self.frames_mut().push(frame);
|
2020-09-24 15:29:44 -07:00
|
|
|
}
|
|
|
|
|
Signal::Return => {
|
2020-10-07 15:48:24 -07:00
|
|
|
// 1. pop return value
|
|
|
|
|
let ret_val = self.pop().expect("return value");
|
2020-09-24 15:29:44 -07:00
|
|
|
|
2020-10-07 15:48:24 -07:00
|
|
|
// 2. pop stack frame
|
|
|
|
|
let frame = self.frames_mut().pop().expect("stack frame");
|
2020-09-24 15:29:44 -07:00
|
|
|
|
2020-10-07 15:48:24 -07:00
|
|
|
// 3. reset PC
|
|
|
|
|
if let Frame::User(frame) = frame {
|
|
|
|
|
self.set_pc(frame.last_pc());
|
|
|
|
|
}
|
2020-09-24 15:29:44 -07:00
|
|
|
|
2020-10-07 15:48:24 -07:00
|
|
|
// 4. push return value
|
|
|
|
|
self.push(ret_val);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-14 16:32:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Gets the current instruction.
|
|
|
|
|
#[inline(always)]
|
|
|
|
|
fn load_inst(&self) -> Inst {
|
2020-10-07 15:48:24 -07:00
|
|
|
let frame = if let Frame::User(frame) = self.frame().expect("frame") {
|
|
|
|
|
frame
|
2020-09-14 16:32:00 -07:00
|
|
|
} else {
|
2020-09-24 15:29:44 -07:00
|
|
|
panic!("expected user function frame")
|
|
|
|
|
};
|
2020-10-07 15:48:24 -07:00
|
|
|
frame.code()[self.pc()]
|
2020-09-14 16:32:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Run a single instruction - inlined version.
|
|
|
|
|
///
|
|
|
|
|
/// Since this is inlined, it is probably a bad idea to allow users to use it everywhere. The
|
|
|
|
|
/// exposed API function `tick()` calls this function, but does not inline itself (unless the
|
|
|
|
|
/// optimizer thinks it's a good idea to do so).
|
|
|
|
|
#[inline]
|
2020-09-24 15:29:44 -07:00
|
|
|
fn tick(&mut self) -> Option<Signal> {
|
2020-09-14 16:32:00 -07:00
|
|
|
let inst = self.load_inst();
|
|
|
|
|
let mut next_pc = self.pc() + 1;
|
2020-09-24 15:29:44 -07:00
|
|
|
let mut signal = None;
|
2020-09-14 16:32:00 -07:00
|
|
|
match inst {
|
|
|
|
|
Inst::PushSym(sym) => {
|
|
|
|
|
let sym_ref = global_sym_ref(sym);
|
|
|
|
|
self.push(sym_ref);
|
|
|
|
|
}
|
|
|
|
|
Inst::PushConst(hdl) => {
|
2020-09-28 16:48:30 -07:00
|
|
|
let obj_ref = self.const_pool.get(hdl).clone();
|
2020-09-14 16:32:00 -07:00
|
|
|
self.push(obj_ref);
|
|
|
|
|
}
|
2020-09-27 19:33:18 -07:00
|
|
|
Inst::LoadLocal(local) => {
|
2020-10-07 15:48:24 -07:00
|
|
|
let value = self.get_local(local)
|
|
|
|
|
.expect("TODO: throw error for missing local");
|
|
|
|
|
self.push(value);
|
2020-09-24 15:29:44 -07:00
|
|
|
}
|
2020-09-27 19:33:18 -07:00
|
|
|
Inst::LoadGlobal(global) => {
|
2020-10-07 15:48:24 -07:00
|
|
|
let value = self.get_global(global)
|
2020-10-07 17:21:01 -07:00
|
|
|
.or_else(|| self.get_builtin(global))
|
2020-10-07 15:48:24 -07:00
|
|
|
.expect("TODO: throw error for missing global");
|
|
|
|
|
self.push(value);
|
2020-09-27 19:33:18 -07:00
|
|
|
}
|
|
|
|
|
Inst::PopLocal(name) => {
|
2020-09-24 15:29:44 -07:00
|
|
|
let tos = self.pop()
|
|
|
|
|
.expect("stack underflow");
|
|
|
|
|
// pop into name
|
|
|
|
|
if let Some(name) = name {
|
|
|
|
|
self.set_local(name, tos);
|
|
|
|
|
}
|
|
|
|
|
// else discard
|
|
|
|
|
}
|
2020-09-27 19:33:18 -07:00
|
|
|
Inst::PopGlobal(name) => {
|
|
|
|
|
let tos = self.pop()
|
|
|
|
|
.expect("stack underflow");
|
|
|
|
|
// pop into name
|
|
|
|
|
if let Some(name) = name {
|
|
|
|
|
self.set_global(name, tos);
|
|
|
|
|
}
|
|
|
|
|
// else discard
|
|
|
|
|
}
|
2020-09-14 16:32:00 -07:00
|
|
|
Inst::GetAttr(sym) => {
|
|
|
|
|
let obj_ref = self.pop().expect("getattr object");
|
|
|
|
|
read_obj!(let obj = obj_ref);
|
|
|
|
|
let attr = obj
|
|
|
|
|
.get_attr(sym)
|
|
|
|
|
.unwrap_or_else(|| global_sym_ref(NIL_NAME.sym));
|
|
|
|
|
self.push(attr);
|
|
|
|
|
}
|
2020-09-24 15:29:44 -07:00
|
|
|
Inst::SetAttr(sym) => {
|
|
|
|
|
let target = self.pop()
|
|
|
|
|
.expect("no target available for SetAttr");
|
|
|
|
|
let source = self.pop()
|
|
|
|
|
.expect("no source available for SetAttr");
|
|
|
|
|
write_obj!(let target = target);
|
|
|
|
|
if let Some(attrs) = target.attrs_mut() {
|
|
|
|
|
attrs.insert(sym, source);
|
|
|
|
|
} else {
|
|
|
|
|
todo!("TODO: throw an error for attributes that can't be set");
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-14 16:32:00 -07:00
|
|
|
Inst::Jump(addr) => {
|
|
|
|
|
next_pc = addr;
|
|
|
|
|
}
|
|
|
|
|
Inst::JumpTrue(addr) => {
|
|
|
|
|
if self.condition {
|
|
|
|
|
next_pc = addr;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-24 15:29:44 -07:00
|
|
|
Inst::Call(argc) => {
|
|
|
|
|
let stack_top = self.stack.len() - argc;
|
|
|
|
|
let args = self.stack.split_off(stack_top);
|
2020-09-28 16:48:30 -07:00
|
|
|
let tos = self.pop()
|
|
|
|
|
.expect("stack underflow");
|
|
|
|
|
read_obj!(let tos = tos);
|
|
|
|
|
let callee = tos.get_call()
|
|
|
|
|
.expect("TODO: throw an error for missing __call__ attr");
|
|
|
|
|
signal = Some(Signal::Call(callee, args));
|
2020-09-24 15:29:44 -07:00
|
|
|
}
|
2020-09-14 16:32:00 -07:00
|
|
|
Inst::Index => todo!(),
|
2020-09-24 15:29:44 -07:00
|
|
|
Inst::Return => { signal = Some(Signal::Return); }
|
2020-09-14 16:32:00 -07:00
|
|
|
Inst::UnNeg => todo!(),
|
|
|
|
|
Inst::UnPos => todo!(),
|
2020-09-28 16:48:30 -07:00
|
|
|
Inst::BinPlus => {
|
|
|
|
|
let rhs = self.pop().unwrap();
|
|
|
|
|
let lhs = self.pop().unwrap();
|
|
|
|
|
let fun = {
|
|
|
|
|
read_obj!(let lhs = lhs);
|
|
|
|
|
lhs.get_plus().expect("TODO: throw an error for missing __plus__ attr")
|
|
|
|
|
};
|
|
|
|
|
signal = Some(Signal::Call(fun, vec![lhs, rhs]));
|
|
|
|
|
}
|
|
|
|
|
Inst::BinMinus => {
|
|
|
|
|
let rhs = self.pop().unwrap();
|
|
|
|
|
let lhs = self.pop().unwrap();
|
|
|
|
|
let fun = {
|
|
|
|
|
read_obj!(let lhs = lhs);
|
|
|
|
|
lhs.get_minus().expect("TODO: throw an error for missing __minus__ attr")
|
|
|
|
|
};
|
|
|
|
|
signal = Some(Signal::Call(fun, vec![lhs, rhs]));
|
|
|
|
|
}
|
|
|
|
|
Inst::BinMul => {
|
|
|
|
|
let rhs = self.pop().unwrap();
|
|
|
|
|
let lhs = self.pop().unwrap();
|
|
|
|
|
let fun = {
|
|
|
|
|
read_obj!(let lhs = lhs);
|
|
|
|
|
lhs.get_mul().expect("TODO: throw an error for missing __mul__ attr")
|
|
|
|
|
};
|
|
|
|
|
signal = Some(Signal::Call(fun, vec![lhs, rhs]));
|
|
|
|
|
}
|
2020-09-14 16:32:00 -07:00
|
|
|
Inst::BinDiv => todo!(),
|
2020-09-28 16:48:30 -07:00
|
|
|
Inst::BinEq => {
|
|
|
|
|
let rhs = self.pop().unwrap();
|
|
|
|
|
let lhs = self.pop().unwrap();
|
|
|
|
|
let fun = {
|
|
|
|
|
read_obj!(let lhs = lhs);
|
|
|
|
|
lhs.get_eq().expect("TODO: throw an error for missing __eq__ attr")
|
|
|
|
|
};
|
|
|
|
|
signal = Some(Signal::Call(fun, vec![lhs, rhs]));
|
|
|
|
|
}
|
2020-09-14 16:32:00 -07:00
|
|
|
Inst::BinNeq => todo!(),
|
|
|
|
|
Inst::BinLt => todo!(),
|
|
|
|
|
Inst::BinLe => todo!(),
|
|
|
|
|
Inst::BinGt => todo!(),
|
|
|
|
|
Inst::BinGe => todo!(),
|
|
|
|
|
Inst::BinAnd => todo!(),
|
|
|
|
|
Inst::BinOr => todo!(),
|
|
|
|
|
}
|
|
|
|
|
self.set_pc(next_pc);
|
2020-09-24 15:29:44 -07:00
|
|
|
|
|
|
|
|
signal
|
|
|
|
|
}
|
2020-10-07 15:48:24 -07:00
|
|
|
|
|
|
|
|
fn get_local(&self, name: Name) -> Option<ObjRef> {
|
|
|
|
|
self.frame()?
|
|
|
|
|
.user_frame()?
|
|
|
|
|
.bindings()
|
|
|
|
|
.get(&name.index())
|
|
|
|
|
.cloned()
|
|
|
|
|
}
|
2020-09-24 15:29:44 -07:00
|
|
|
|
|
|
|
|
fn set_local(&mut self, name: Name, value: ObjRef) {
|
2020-10-07 15:48:24 -07:00
|
|
|
let frame = self
|
|
|
|
|
.frame_mut()
|
|
|
|
|
.expect("user stack frame")
|
|
|
|
|
.user_frame_mut()
|
|
|
|
|
.unwrap();
|
|
|
|
|
let bindings = frame.bindings_mut();
|
|
|
|
|
bindings.insert(name.index(), value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_global(&self, name: Name) -> Option<ObjRef> {
|
2020-10-07 17:21:01 -07:00
|
|
|
self.frames()
|
2020-10-07 15:48:24 -07:00
|
|
|
.first()?
|
|
|
|
|
.user_frame()?
|
|
|
|
|
.bindings()
|
|
|
|
|
.get(&name.index())
|
|
|
|
|
.cloned()
|
2020-09-27 19:33:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_global(&mut self, name: Name, value: ObjRef) {
|
2020-10-07 15:48:24 -07:00
|
|
|
let frame = self.frames_mut()
|
|
|
|
|
.first_mut()
|
|
|
|
|
.expect("global stack frame")
|
|
|
|
|
.user_frame_mut()
|
|
|
|
|
.expect("user-defined function stack frame");
|
|
|
|
|
let bindings = frame.bindings_mut();
|
|
|
|
|
bindings.insert(name.index(), value);
|
2020-09-24 15:29:44 -07:00
|
|
|
}
|
2020-10-07 17:21:01 -07:00
|
|
|
|
|
|
|
|
fn get_builtin(&self, name: Name) -> Option<ObjRef> {
|
|
|
|
|
read_obj!(let fun = self.frames()
|
|
|
|
|
.first()?
|
|
|
|
|
.user_frame()?
|
|
|
|
|
.callee()
|
|
|
|
|
);
|
|
|
|
|
let fun: &UserFun = std::any::Any::downcast_ref(fun.as_any()).unwrap();
|
|
|
|
|
let sym = fun.locals()[name.index()];
|
|
|
|
|
BUILTIN_OBJS.get(&sym).cloned()
|
|
|
|
|
}
|
2020-09-01 17:32:48 -07:00
|
|
|
}
|