deimos-lang/dvm-lib/src/vm/mod.rs

707 lines
27 KiB
Rust

use crate::instruction::Instruction;
use crate::platform_function::PlatformFunction;
use crate::vm::class::Class;
use crate::vm::constant::Constant;
use crate::vm::function::Function;
use crate::vm::object::get_object;
use crate::vm::operand::Operand;
use crate::vm::operand_helpers::{
add_operand_to_value, location_or_integer_to_value, location_or_number_to_value,
move_operand_to_value, multiply_operand_to_value, push_operand_to_value,
return_operand_to_value, set_field_operand_to_value, subtract_operand_to_value,
};
use crate::vm::util::*;
use crate::vm::value::Value;
use std::cell::RefCell;
use std::collections::HashMap;
use std::ptr;
use std::rc::Rc;
pub mod class;
pub mod constant;
pub mod function;
pub mod object;
pub mod operand;
mod operand_helpers;
pub mod type_info;
mod util;
pub mod value;
pub struct DvmContext {
classes: HashMap<Rc<str>, Rc<Class>>,
functions: HashMap<Rc<str>, Function>,
platform_functions: HashMap<Rc<str>, PlatformFunction>,
constants: HashMap<Rc<str>, Constant>,
}
impl DvmContext {
pub fn new() -> Self {
Self {
classes: HashMap::new(),
functions: HashMap::new(),
platform_functions: HashMap::new(),
constants: HashMap::new(),
}
}
pub fn classes(&self) -> &HashMap<Rc<str>, Rc<Class>> {
&self.classes
}
pub fn classes_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<Class>> {
&mut self.classes
}
pub fn functions(&self) -> &HashMap<Rc<str>, Function> {
&self.functions
}
pub fn functions_mut(&mut self) -> &mut HashMap<Rc<str>, Function> {
&mut self.functions
}
pub fn platform_functions(&self) -> &HashMap<Rc<str>, PlatformFunction> {
&self.platform_functions
}
pub fn platform_functions_mut(&mut self) -> &mut HashMap<Rc<str>, PlatformFunction> {
&mut self.platform_functions
}
pub fn constants(&self) -> &HashMap<Rc<str>, Constant> {
&self.constants
}
pub fn constants_mut(&mut self) -> &mut HashMap<Rc<str>, Constant> {
&mut self.constants
}
}
pub struct CallFrame<'a> {
function: &'a Function,
instructions: &'a Vec<Instruction>,
ip: usize,
stack: Vec<Operand>,
fp: usize,
return_value: Option<Value>,
}
impl<'a> CallFrame<'a> {
fn new(function: &'a Function) -> Self {
Self {
function,
instructions: function.instructions(),
ip: 0,
stack: vec![],
fp: 0,
return_value: None,
}
}
fn function(&self) -> &'a Function {
self.function
}
fn instructions(&self) -> &'a [Instruction] {
self.instructions
}
fn ip(&self) -> usize {
self.ip
}
fn increment_ip(&mut self) {
self.ip += 1;
}
fn stack(&self) -> &[Operand] {
&self.stack
}
fn stack_mut(&mut self) -> &mut Vec<Operand> {
&mut self.stack
}
fn fp(&self) -> usize {
self.fp
}
fn set_fp(&mut self, fp: usize) {
self.fp = fp;
}
fn return_value(&self) -> Option<&Value> {
self.return_value.as_ref()
}
fn set_return_value(&mut self, return_value: Value) {
self.return_value = Some(return_value);
}
fn take_return_value(&mut self) -> Option<Value> {
self.return_value.take()
}
}
pub struct CallStack<'a> {
call_frames: Vec<CallFrame<'a>>,
}
impl<'a> CallStack<'a> {
pub fn new() -> Self {
Self {
call_frames: vec![],
}
}
fn push(&mut self, call_frame: CallFrame<'a>) {
self.call_frames.push(call_frame);
}
fn pop(&mut self) -> CallFrame<'a> {
self.call_frames.pop().unwrap()
}
fn top(&self) -> &CallFrame<'a> {
&self.call_frames[self.call_frames.len() - 1]
}
fn top_mut(&mut self) -> &mut CallFrame<'a> {
let call_frames_len = self.call_frames.len();
&mut self.call_frames[call_frames_len - 1] // dang borrow checker
}
fn maybe_top(&self) -> Option<&CallFrame<'a>> {
self.call_frames.last()
}
fn maybe_top_mut(&mut self) -> Option<&mut CallFrame<'a>> {
self.call_frames.last_mut()
}
fn penultimate(&self) -> Option<&CallFrame<'a>> {
self.call_frames.get(self.call_frames.len() - 2)
}
fn penultimate_mut(&mut self) -> Option<&mut CallFrame<'a>> {
let call_frames_len = self.call_frames.len();
self.call_frames.get_mut(call_frames_len - 2) // dang borrow checker
}
}
pub fn call<'a>(
context: &'a DvmContext,
registers: &mut Vec<Operand>,
call_stack: &mut CallStack<'a>,
function_name: &str,
arguments: &[Value],
) -> Option<Value> {
// check if DVM_DEBUG is enabled
let debug = std::env::var("DVM_DEBUG")
.map(|s| s.eq("1"))
.unwrap_or(false);
let function = context
.functions
.get(function_name)
.expect(&format!("Function {} not found", function_name));
// push call frame
call_stack.push(CallFrame::new(function));
// put each arg on the stack
for argument in arguments {
call_stack
.top_mut()
.stack_mut()
.push(Operand::Value(argument.clone()));
}
// set fp for this function
call_stack.top_mut().set_fp(arguments.len());
// ensure enough stack space
call_stack.top_mut().stack_mut().resize(
arguments.len() + (function.stack_size() as usize),
Operand::Null,
);
// container for final return value
let mut return_value: Option<Value> = None;
while call_stack.maybe_top().is_some()
&& call_stack.top().ip() < call_stack.top().instructions().len()
{
let instruction = &call_stack.top().instructions()[call_stack.top().ip()];
if debug {
println!("{}", instruction);
}
match instruction {
/* Move instructions */
Instruction::Move(source, destination) => {
// move a value to a destination
// could be a copy or an immediate
let value =
move_operand_to_value(source, registers, call_stack.top(), context.constants());
put_value(registers, call_stack.top_mut(), destination, value);
call_stack.top_mut().increment_ip();
}
/* Push instructions */
Instruction::Push(operand) => {
let value = push_operand_to_value(
operand,
registers,
call_stack.top(),
context.constants(),
);
call_stack.top_mut().stack_mut().push(Operand::Value(value));
call_stack.top_mut().increment_ip();
}
/* Invoke instructions */
Instruction::InvokeStatic(function_name, arg_count) => {
let function = context
.functions
.get(function_name)
.expect(&format!("Function {} not found", function_name));
// get args
// this isn't perfect (yet), but for now it works and satisfies the borrow checkers
let mut args = call_stack.top().stack()
[call_stack.top().stack().len() - arg_count..]
.iter()
.map(|operand| operand.unwrap_value().clone())
.map(|value| Operand::Value(value))
.collect::<Vec<_>>();
// save registers
for register in registers.iter_mut() {
let value = std::mem::take(register);
call_stack.top_mut().stack_mut().push(value);
}
// increment caller frame ip
call_stack.top_mut().increment_ip();
// push a new call frame
call_stack.push(CallFrame::new(function));
// push args
call_stack.top_mut().stack_mut().append(&mut args);
// ensure enough stack space
call_stack
.top_mut()
.stack_mut()
.resize(arg_count + (function.stack_size() as usize), Operand::Null);
// set fp for callee frame
let callee_frame = call_stack.top_mut();
callee_frame.set_fp(*arg_count);
}
Instruction::InvokePlatformStatic(function_name, arg_count) => {
let platform_function = context
.platform_functions()
.get(function_name)
.expect(&format!("Platform function {} not found", function_name));
let arg_operands =
&call_stack.top().stack()[call_stack.top().stack().len() - arg_count..];
let args = arg_operands
.iter()
.map(|operand| operand.unwrap_value().clone())
.collect::<Vec<_>>();
let result = platform_function(&args);
match result {
Ok(return_value) => {
// push return value to top of caller stack if it's not null
if !matches!(return_value, Value::Null) {
call_stack
.top_mut()
.stack_mut()
.push(Operand::Value(return_value));
}
}
Err(error) => {
// Eventually we will have some kind of exception handling
panic!("{}", error);
}
}
call_stack.top_mut().increment_ip();
}
/* Object instructions */
Instruction::Allocate(class_fqn, destination) => {
let class = context
.classes()
.get(class_fqn)
.expect(&format!("No such class loaded: {}", class_fqn));
let object = get_object(class);
let as_rc = Rc::new(RefCell::new(object));
let value = Value::Object(as_rc);
put_value(registers, call_stack.top_mut(), destination, value);
call_stack.top_mut().increment_ip();
}
Instruction::GetFieldPointer(self_location, field_index, destination) => {
let object = load_value(
registers,
call_stack.top().stack(),
call_stack.top().fp(),
self_location,
)
.unwrap_object()
.clone();
let field_ref = ptr::from_ref(&object.borrow_mut().fields_mut()[*field_index]);
put_operand(
registers,
call_stack.top_mut(),
destination,
Operand::FieldPointer(field_ref),
);
call_stack.top_mut().increment_ip();
}
Instruction::GetFieldPointerMut(self_location, field_index, destination) => {
let object = load_value(
registers,
call_stack.top().stack(),
call_stack.top().fp(),
self_location,
)
.unwrap_object()
.clone();
let field_ref = ptr::from_mut(&mut object.borrow_mut().fields_mut()[*field_index]);
put_operand(
registers,
call_stack.top_mut(),
destination,
Operand::FieldPointerMut(field_ref),
);
call_stack.top_mut().increment_ip();
}
Instruction::ReadField(field_pointer_location, destination) => {
let field_pointer = load_operand(
registers,
call_stack.top().stack(),
call_stack.top().fp(),
field_pointer_location,
)
.unwrap_field_pointer();
// clone is very important, otherwise we mess up the self object!
let value = unsafe { field_pointer.read() }.clone();
put_value(registers, call_stack.top_mut(), destination, value);
call_stack.top_mut().increment_ip();
}
Instruction::SetField(field_point_mut_location, operand) => {
let fp = call_stack.top().fp(); // borrow checker :)
let field_pointer_mut = load_operand_mut(
registers,
call_stack.top_mut().stack_mut(),
fp,
field_point_mut_location,
)
.unwrap_field_pointer_mut();
let value = set_field_operand_to_value(
operand,
registers,
call_stack.top(),
context.constants(),
);
unsafe {
field_pointer_mut.write(value);
}
call_stack.top_mut().increment_ip();
}
/* Multiplicative instructions */
Instruction::Multiply(left_operand, right_operand, destination) => {
let left_value =
multiply_operand_to_value(left_operand, registers, call_stack.top());
let right_value =
multiply_operand_to_value(right_operand, registers, call_stack.top());
let result = match left_value {
Value::Int(li) => match right_value {
Value::Int(ri) => Value::Int(li * ri),
Value::Double(rd) => Value::Double(li as f64 * rd),
_ => panic!("Attempt to multiply {:?} and {:?}", left_value, right_value),
},
Value::Double(ld) => match right_value {
Value::Int(ri) => Value::Double(ld * ri as f64),
Value::Double(rd) => Value::Double(ld * rd),
_ => panic!("Attempt to multiply {:?} and {:?}", left_value, right_value),
},
_ => panic!("Attempt to multiply {:?} and {:?}", left_value, right_value),
};
put_value(registers, call_stack.top_mut(), destination, result);
call_stack.top_mut().increment_ip();
}
Instruction::Divide(left, right, destination) => {
let left_value = location_or_number_to_value(left, registers, call_stack.top());
let right_value = location_or_number_to_value(right, registers, call_stack.top());
let result = match left_value {
Value::Int(li) => match right_value {
Value::Int(ri) => Value::Double(li as f64 / ri as f64),
Value::Double(rd) => Value::Double(li as f64 / rd),
_ => panic!("Attempt to divide {:?} and {:?}", left_value, right_value),
},
Value::Double(ld) => match right_value {
Value::Int(ri) => Value::Double(ld / ri as f64),
Value::Double(rd) => Value::Double(ld / rd),
_ => panic!("Attempt to divide {:?} and {:?}", left_value, right_value),
},
_ => panic!("Attempt to divide {:?} and {:?}", left_value, right_value),
};
put_value(registers, call_stack.top_mut(), destination, result);
call_stack.top_mut().increment_ip();
}
Instruction::Modulo(left, right, destination) => {
let left_value = location_or_number_to_value(left, registers, call_stack.top());
let right_value = location_or_number_to_value(right, registers, call_stack.top());
let result = match left_value {
Value::Int(li) => match right_value {
Value::Int(ri) => Value::Int(li % ri),
Value::Double(rd) => Value::Double(li as f64 % rd),
_ => panic!("Attempt to modulo {:?} and {:?}", left_value, right_value),
},
Value::Double(ld) => match right_value {
Value::Int(ri) => Value::Double(ld % ri as f64),
Value::Double(rd) => Value::Double(ld % rd),
_ => panic!("Attempt to modulo {:?} and {:?}", left_value, right_value),
},
_ => panic!("Attempt to modulo {:?} and {:?}", left_value, right_value),
};
put_value(registers, call_stack.top_mut(), destination, result);
call_stack.top_mut().increment_ip();
}
/* Add instructions */
Instruction::Add(left_operand, right_operand, destination) => {
let left_value = add_operand_to_value(
&left_operand,
registers,
call_stack.top(),
context.constants(),
);
let right_value = add_operand_to_value(
&right_operand,
registers,
call_stack.top(),
context.constants(),
);
let result = match left_value {
Value::Int(li) => match right_value {
Value::Int(ri) => Value::Int(li + ri),
Value::Double(rd) => Value::Double(li as f64 + rd),
Value::String(rs) => Value::String(format!("{}{}", li, rs).into()),
_ => panic!(
"Attempt to add non-addable {:?} type to {:?}",
right_value, left_value
),
},
Value::Double(ld) => match right_value {
Value::Int(ri) => Value::Double(ld + ri as f64),
Value::Double(rd) => Value::Double(ld + rd),
Value::String(rs) => Value::String(format!("{}{}", ld, rs).into()),
_ => panic!(
"Attempt to add non-addable {:?} type to {:?}",
right_value, left_value
),
},
Value::String(ref ls) => match right_value {
Value::Int(ri) => Value::String(format!("{}{}", ls, ri).into()),
Value::Double(rd) => Value::String(format!("{}{}", ls, rd).into()),
Value::String(rs) => Value::String(format!("{}{}", ls, rs).into()),
_ => panic!(
"Attempt to add non-addable {:?} type to {:?}",
right_value, left_value
),
},
_ => panic!("Attempt to add with non-addable type: {:?}", left_value),
};
put_value(registers, call_stack.top_mut(), destination, result);
call_stack.top_mut().increment_ip();
}
Instruction::Subtract(left_operand, right_operand, destination) => {
let left = subtract_operand_to_value(left_operand, registers, call_stack.top());
let right = subtract_operand_to_value(right_operand, registers, call_stack.top());
let result = match left {
Value::Int(li) => match right {
Value::Int(ri) => Value::Int(li - ri),
Value::Double(rd) => Value::Double(li as f64 - rd),
_ => panic!("Attempt to subtract {:?} from {:?}", right, left),
},
Value::Double(ld) => match right {
Value::Int(ri) => Value::Double(ld - ri as f64),
Value::Double(rd) => Value::Double(ld - rd),
_ => panic!("Attempt to subtract {:?} from {:?}", right, left),
},
_ => panic!("Attempt to subtract {:?} from {:?}", right, left),
};
put_value(registers, call_stack.top_mut(), destination, result);
call_stack.top_mut().increment_ip();
}
/* Shift instructions */
Instruction::LeftShift(left, right, destination) => {
let left_value = location_or_integer_to_value(left, registers, call_stack.top());
let right_value = location_or_integer_to_value(right, registers, call_stack.top());
let result = match left_value {
Value::Int(li) => match right_value {
Value::Int(ri) => Value::Int(li << ri),
_ => panic!("Attempt to left shift {} by {}", left, right),
},
_ => panic!("Attempt to left shift {} by {}", left_value, right_value),
};
put_value(registers, call_stack.top_mut(), destination, result);
call_stack.top_mut().increment_ip();
}
Instruction::RightShift(left, right, destination) => {
let left_value = location_or_integer_to_value(left, registers, call_stack.top());
let right_value = location_or_integer_to_value(right, registers, call_stack.top());
let result = match left_value {
Value::Int(li) => match right_value {
Value::Int(ri) => Value::Int(li >> ri),
_ => panic!("Attempt to right shift {} by {}", left_value, right_value),
},
_ => panic!("Attempt to right shift {} by {}", left_value, right_value),
};
put_value(registers, call_stack.top_mut(), destination, result);
call_stack.top_mut().increment_ip();
}
/* Bitwise instructions */
Instruction::BitwiseAnd(left, right, destination) => {
let left_value = location_or_integer_to_value(left, registers, call_stack.top());
let right_value = location_or_integer_to_value(right, registers, call_stack.top());
let result = match left_value {
Value::Int(li) => match right_value {
Value::Int(ri) => Value::Int(li & ri),
_ => panic!("Attempt to bitwise and {} by {}", left_value, right_value),
},
_ => panic!("Attempt to bitwise and {} by {}", left_value, right_value),
};
put_value(registers, call_stack.top_mut(), destination, result);
call_stack.top_mut().increment_ip();
}
Instruction::BitwiseXor(left, right, destination) => {
let left_value = location_or_integer_to_value(left, registers, call_stack.top());
let right_value = location_or_integer_to_value(right, registers, call_stack.top());
let result = match left_value {
Value::Int(li) => match right_value {
Value::Int(ri) => Value::Int(li ^ ri),
_ => panic!("Attempt to bitwise xor {} by {}", left_value, right_value),
},
_ => panic!(
"Attempt to left bitwise xor {} by {}",
left_value, right_value
),
};
put_value(registers, call_stack.top_mut(), destination, result);
call_stack.top_mut().increment_ip();
}
Instruction::BitwiseOr(left, right, destination) => {
let left_value = location_or_integer_to_value(left, registers, call_stack.top());
let right_value = location_or_integer_to_value(right, registers, call_stack.top());
let result = match left_value {
Value::Int(li) => match right_value {
Value::Int(ri) => Value::Int(li | ri),
_ => panic!("Attempt to bitwise or {} by {}", left_value, right_value),
},
_ => panic!(
"Attempt to left bitwise or {} by {}",
left_value, right_value
),
};
put_value(registers, call_stack.top_mut(), destination, result);
call_stack.top_mut().increment_ip();
}
/* Pop instructions */
Instruction::Pop(maybe_location) => {
let value = call_stack.top_mut().stack_mut().pop().unwrap();
if let Some(location) = maybe_location {
put_operand(registers, call_stack.top_mut(), location, value);
}
call_stack.top_mut().increment_ip();
}
/* Return instructions */
Instruction::SetReturnValue(return_operand) => {
let value = return_operand_to_value(
return_operand,
registers,
call_stack.top(),
context.constants(),
);
call_stack.top_mut().set_return_value(value);
call_stack.top_mut().increment_ip();
}
Instruction::Return => {
let mut callee = call_stack.pop(); // pop this frame
let maybe_caller = call_stack.maybe_top_mut();
if let Some(caller) = maybe_caller {
// restore registers
for i in (0..registers.len()).rev() {
registers[i] = caller.stack_mut().pop().unwrap();
}
// pop args
for _ in 0..callee.function().parameter_count() {
caller.stack_mut().pop();
}
// push return value if some
if let Some(return_value) = callee.take_return_value() {
// n.b. callee
caller.stack_mut().push(Operand::Value(return_value));
}
} else {
// callee is the bottommost frame ("main" or whatever was called)
// set the return value
return_value = callee.take_return_value();
}
// do not increment ip of the caller; this was done above BEFORE the call
}
}
if debug {
if let Some(top) = call_stack.maybe_top() {
println!(" stack: {:?}", top.stack());
println!(" registers: {:?}", registers);
println!(" rv: {:?}", top.return_value());
}
}
}
return_value
}