mod array; pub mod constant; mod field; pub mod function; pub mod implementation; pub mod instruction; mod interface; pub mod lib; mod method; pub mod object; pub mod op_codes; pub mod platform; pub mod source_code_location; pub mod r#type; pub mod util; pub mod value; mod virtual_method; use crate::vm::array::DvmArray; use crate::vm::constant::{DvmConstant, DvmConstantArray}; use crate::vm::implementation::DvmImplementation; use crate::vm::instruction::{Immediate, Instruction, Location}; use crate::vm::object::DvmObject; use crate::vm::value::DvmValue; use function::DvmFunction; use source_code_location::SourceCodeLocation; use std::cell::RefCell; use std::collections::HashMap; use std::fmt::Display; use std::rc::Rc; pub fn dump_state(state: &DvmState, message: &str) { println!("----"); println!("{}", message); println!("----"); println!( "Current Instruction: {:?}", state.current_instruction.clone().unwrap() ); println!("Registers:"); for (i, register) in state.registers.iter().enumerate() { println!("\tr{:x}: {:?}", i, register); } println!("Call stack:"); for call in state.call_stack.iter() { println!("\t{}", call); } println!("Stack:"); for (i, value) in state.stack.iter().enumerate() { println!("\t{}: {}", i, value); } println!("----"); } macro_rules! dvm_panic { ($context: expr, $message: expr) => { dump_state($context, $message); panic!() }; } pub type PlatformFunction = fn(state: &mut DvmState, context: &DvmContext); pub struct DvmState { call_stack: Vec, current_instruction: Option, registers: Vec, stack: Vec, } impl DvmState { pub fn new() -> Self { DvmState { call_stack: vec![], current_instruction: None, registers: vec![DvmValue::Empty; 16], stack: vec![], } } pub fn pop_stack(&mut self) -> DvmValue { self.stack.pop().unwrap_or_else(|| { dvm_panic!(self, "Stack underflow!"); }) } pub fn get_stack_argument(&self, index: usize) -> DvmValue { self.stack .get(self.stack.len() - 2 - index) .unwrap_or_else(|| { dvm_panic!(self, "Stack underflow!"); }) .clone() } } pub struct DvmContext { main_function: Rc, implementations: HashMap>, static_functions: HashMap>, platform_functions: HashMap, } impl DvmContext { pub fn new(main_function: Rc) -> Self { DvmContext { main_function, implementations: HashMap::new(), static_functions: HashMap::new(), platform_functions: HashMap::new(), } } pub fn add_platform_functions( &mut self, platform_functions: HashMap, ) { for (fqn, platform_function) in platform_functions { self.platform_functions.insert(fqn, platform_function); } } } pub struct DvmCall { function_name: Rc, source_code_location: SourceCodeLocation, } impl Display for DvmCall { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_fmt(format_args!( "{} in {} @ {}:{}", self.function_name, self.source_code_location.source_file_name, self.source_code_location.line, self.source_code_location.char )) } } fn update_index(index: &mut usize, offset: isize) { if offset.is_negative() { *index -= offset.abs() as usize; } else { *index += offset as usize; } } fn push_call_frame( function_name: Rc, source_code_location: SourceCodeLocation, state: &mut DvmState, ) { state.call_stack.push(DvmCall { function_name, source_code_location, }); } fn pop_call_frame(state: &mut DvmState) { state.call_stack.pop(); } fn compute_index_with_offset( registers: &Vec, index_register: usize, offset: &Option, ) -> usize { let index = registers[index_register].as_usize().expect(&format!( "Could not convert the value of index_register {} to usize; found {:?}", index_register, registers[index_register] )); if let Some(o) = offset { if o.is_negative() { index - o.unsigned_abs() } else { index + o.unsigned_abs() } } else { index } } fn copy_from_source(state: &DvmState, source: &Location) -> DvmValue { match source { Location::Register(register) => state.registers[*register].clone(), Location::Stack { offset } => state.stack[*offset].clone(), Location::Field { object_register, field_name, } => state.registers[*object_register].map_object( |o| o.borrow().read_field(field_name).clone(), |v| { dvm_panic!(state, &format!("Expected an object, but found: {}", v)); }, ), Location::Array { array_register, index_register, offset, } => { let index = compute_index_with_offset(&state.registers, *index_register, offset); state.registers[*array_register].map_array( |a| a.borrow().read_element(index), |v| { dvm_panic!(state, &format!("Expected an array, but found: {}", v)); }, ) } } } fn write_to_destination(state: &mut DvmState, destination: &Location, value: DvmValue) { match destination { Location::Register(register) => { state.registers[*register] = value; } Location::Stack { offset } => { state.stack[*offset] = value; } Location::Field { object_register, field_name, } => { state.registers[*object_register] .expect_object() .borrow_mut() .write_field(field_name, value); } Location::Array { array_register, index_register, offset, } => { let index = compute_index_with_offset(&state.registers, *index_register, offset); state.registers[*array_register] .expect_array() .borrow_mut() .write_element(index, value); } } } fn constant_array_to_array(constant_array: &DvmConstantArray) -> DvmArray { match constant_array { DvmConstantArray::Bytes(v) => DvmArray::Bytes(v.clone()), DvmConstantArray::Ints(v) => DvmArray::Ints(v.clone()), DvmConstantArray::Longs(v) => DvmArray::Longs(v.clone()), DvmConstantArray::Doubles(v) => DvmArray::Doubles(v.clone()), DvmConstantArray::USizes(v) => DvmArray::USizes(v.clone()), DvmConstantArray::Booleans(v) => DvmArray::Booleans(v.clone()), DvmConstantArray::Strings(vs) => { DvmArray::Strings(vs.iter().map(|s| Rc::new(s.clone())).collect()) } DvmConstantArray::Arrays(va) => DvmArray::Arrays( va.iter() .map(|a| Rc::new(RefCell::new(constant_array_to_array(a)))) .collect(), ), } } fn constant_to_value(constant: &DvmConstant) -> DvmValue { match constant { DvmConstant::String(s) => DvmValue::String(Rc::new(s.clone())), DvmConstant::Array(a) => DvmValue::Array(Rc::new(RefCell::new(constant_array_to_array(a)))), } } fn immediate_to_value(immediate: &Immediate) -> DvmValue { match immediate { Immediate::Byte(b) => DvmValue::Byte(*b), Immediate::Int(i) => DvmValue::Int(*i), Immediate::Long(l) => DvmValue::Long(*l), Immediate::Double(d) => DvmValue::Double(*d), Immediate::Usize(s) => DvmValue::USize(*s), Immediate::Boolean(b) => DvmValue::Boolean(*b), Immediate::Constant(c) => constant_to_value(c), Immediate::Empty => DvmValue::Empty, } } pub fn run_main_function(state: &mut DvmState, context: &DvmContext) -> i32 { let main_function = context.main_function.clone(); push_call_frame( Rc::new(main_function.fqn().to_string()), main_function.source_code_location().clone(), state, ); run(main_function.instructions(), state, context); pop_call_frame(state); state.pop_stack().expect_int_or_else(|v| { dvm_panic!(state, &format!("Expected DvmValue::Int, but found {}", v)); }) } // TODO: find all possible unwraps/expects and wrap with call to dvm_panic pub fn run(instructions: &[Instruction], state: &mut DvmState, context: &DvmContext) { let mut index = 0; while index < instructions.len() { state.current_instruction = Some(instructions[index].clone()); use Instruction::*; match &instructions[index] { MoveImmediate { destination, immediate, } => { write_to_destination(state, destination, immediate_to_value(immediate)); } Copy { source, destination, } => { let value = copy_from_source(state, source); write_to_destination(state, destination, value); } Push { source_register } => { state.stack.push(state.registers[*source_register].clone()); } Pop { destination_register, } => { let value = state.stack.pop().unwrap_or_else(|| { dvm_panic!(state, "Stack underflow"); }); if let Some(register_index) = destination_register { state.registers[*register_index] = value; } } AllocateObject { implementation_name, destination_register, } => { let implementation = context .implementations .get(implementation_name.as_str()) .unwrap_or_else(|| { dvm_panic!( state, &format!("Cannot find implementation {}", implementation_name) ); }) .clone(); let object = DvmObject::new(implementation); state.registers[*destination_register] = DvmValue::Object(Rc::new(RefCell::new(object))); } InvokeStatic { function_name, source_code_location, } => { // Find function let static_function = context .static_functions .get(function_name.as_str()) .unwrap_or_else(|| { dvm_panic!( state, &format!("Cannot find static function {}", function_name) ); }) .clone(); // Do call state.stack.push(DvmValue::Empty); // space for return value push_call_frame(function_name.clone(), source_code_location.clone(), state); run(static_function.instructions(), state, context); pop_call_frame(state); } InvokeObject { object_register, function_name, source_code_location, } => { // Find method and get instructions let object_value = state.registers[*object_register].clone(); let object_rc = object_value.expect_object_or_else(|v| { dvm_panic!( state, &format!("Expected DvmValue::Object, but found {}", object_value) ); }); let object_ref = object_rc.borrow(); let method = object_ref .get_method(function_name.as_str()) .unwrap_or_else(|| { dvm_panic!( state, &format!( "Cannot find method {} for object {:?}", function_name, object_ref ) ); }); let function = method.dm_fn(); let instructions = function.instructions(); // Do call state.stack.push(DvmValue::Empty); // space for return value state.stack.push(object_value); // self object push_call_frame(function_name.clone(), source_code_location.clone(), state); run(instructions, state, context); pop_call_frame(state); state.stack.pop(); // self object } InvokeStaticPlatform { function_name, source_code_location, } => { // Find function let platform_function = context .platform_functions .get(function_name.as_str()) .unwrap_or_else(|| { dvm_panic!( state, &format!("Cannot find static platform function {}", function_name) ); }); // Do call state.stack.push(DvmValue::Empty); // space for return value push_call_frame(function_name.clone(), source_code_location.clone(), state); platform_function(state, context); pop_call_frame(state); } InvokeObjectPlatform { object_register, function_name, source_code_location, } => { // Find function let object_platform_function = context .platform_functions .get(function_name.as_str()) .unwrap_or_else(|| { dvm_panic!( state, &format!("Cannot find object platform function {}", function_name) ); }); // Get self object let object_value = state.registers[*object_register].clone(); if !object_value.is_object() { dvm_panic!( state, &format!( "Expected DvmValue::Object, but found DvmValue::{:?}", object_value ) ); } // Do call state.stack.push(DvmValue::Empty); // space for return value state.stack.push(object_value); // self object push_call_frame(function_name.clone(), source_code_location.clone(), state); object_platform_function(state, context); pop_call_frame(state); state.stack.pop(); // self object } Add { .. } => {} Subtract { .. } => {} Multiply { .. } => {} Divide { .. } => {} Modulo { .. } => {} Power { .. } => {} Jump { offset } => { update_index(&mut index, *offset); } JumpEqual { left_register, right_register, offset, } => { let left_value = &state.registers[*left_register]; let right_value = &state.registers[*right_register]; if left_value == right_value { update_index(&mut index, *offset); } } JumpNotEqual { .. } => {} JumpLessThan { .. } => {} JumpGreaterThan { .. } => {} JumpLessThanEqual { .. } => {} JumpGreaterThanEqual { .. } => {} Return => { break; } } index += 1; } }