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>, functions: HashMap, Function>, platform_functions: HashMap, PlatformFunction>, constants: HashMap, 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> { &self.classes } pub fn classes_mut(&mut self) -> &mut HashMap, Rc> { &mut self.classes } pub fn functions(&self) -> &HashMap, Function> { &self.functions } pub fn functions_mut(&mut self) -> &mut HashMap, Function> { &mut self.functions } pub fn platform_functions(&self) -> &HashMap, PlatformFunction> { &self.platform_functions } pub fn platform_functions_mut(&mut self) -> &mut HashMap, PlatformFunction> { &mut self.platform_functions } pub fn constants(&self) -> &HashMap, Constant> { &self.constants } pub fn constants_mut(&mut self) -> &mut HashMap, Constant> { &mut self.constants } } pub struct CallFrame<'a> { function: &'a Function, instructions: &'a Vec, ip: usize, stack: Vec, fp: usize, return_value: Option, } 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 { &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 { self.return_value.take() } } pub struct CallStack<'a> { call_frames: Vec>, } 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, call_stack: &mut CallStack<'a>, function_name: &str, arguments: &[Value], ) -> Option { // 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 = 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::>(); // 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::>(); 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 }