From 6190beaed268fc65b4db3c52c09542c62f77fe52 Mon Sep 17 00:00:00 2001 From: Jesse Brault Date: Sun, 8 Dec 2024 22:11:33 -0600 Subject: [PATCH] Massive work just to try to start printing a String. --- dm_lib/std/core/array.dm | 35 ++++- dm_lib/std/unsafe/mem.dm | 18 +++ src/bin/dvm/main.rs | 243 ++++++++++++++++++++++++++++-- src/vm/dm_type.rs | 39 ++--- src/vm/dvm_value.rs | 8 +- src/vm/{module.rs => lib/load.rs} | 108 ++----------- src/vm/lib/magic.rs | 7 + src/vm/lib/mod.rs | 31 ++++ src/vm/lib/symbol.rs | 4 + src/vm/lib/write.rs | 40 +++++ src/vm/mem.rs | 45 ++---- src/vm/mod.rs | 226 ++++++++++++++++++++++++--- src/vm/object_type.rs | 164 +++++++++++++++++--- src/vm/op_codes.rs | 108 ++++++++++++- src/vm/platform/std_lib/core.rs | 82 +++++++--- 15 files changed, 913 insertions(+), 245 deletions(-) create mode 100644 dm_lib/std/unsafe/mem.dm rename src/vm/{module.rs => lib/load.rs} (50%) create mode 100644 src/vm/lib/magic.rs create mode 100644 src/vm/lib/mod.rs create mode 100644 src/vm/lib/symbol.rs create mode 100644 src/vm/lib/write.rs diff --git a/dm_lib/std/core/array.dm b/dm_lib/std/core/array.dm index 60650a5..c2d7eef 100644 --- a/dm_lib/std/core/array.dm +++ b/dm_lib/std/core/array.dm @@ -1,5 +1,8 @@ ns std::core +use std::unsafe::Pointer +use std::unsafe::mem::{alloc, pointer_of) + pub int Array : Monad + Default + Empty { const default = array::empty const empty = array::empty @@ -7,12 +10,42 @@ pub int Array : Monad + Default + Empty { length: Int } +impl ArrayImpl : Array { + + fld pointer: Pointer + fld length: Int + + pub unsafe ctor(length: Int) { + self.pointer = alloc(length * T::size()) + self.length = length + } + + pub ctor(pointer: Pointer, length: Int) { + self.pointer = pointer + self.length = length + } + + pub unsafe fn set(index: Int, item: Pointer) { + pointer.offset(index * T::size()).write(item.read()) + } + +} + pub mod array { // Usage: // let int_array = array::of(1, 2, 3) // assert_eq(3, int_array.length) - pub extern fn of(ts: ...T): Array + pub fn of(ts: ...T): Array { + unsafe { + let array = ArrayImpl(ts.length) + for (i, t) in ts.enumerate() { + let t_pointer = pointer_of(t) + array.set(i, t_pointer) + } + array + } + } pub extern fn of_length(length: Int, init_value: T): Array diff --git a/dm_lib/std/unsafe/mem.dm b/dm_lib/std/unsafe/mem.dm new file mode 100644 index 0000000..4e33109 --- /dev/null +++ b/dm_lib/std/unsafe/mem.dm @@ -0,0 +1,18 @@ +ns std::unsafe + +pub int Pointer {} + +impl PointerImpl { + + fld raw_address: Long + fld size: Long + + decl ctor(size: Long) + +} + +pub mod mem { + + pub fn alloc(size: Long): Pointer = PointerImpl(size) + +} diff --git a/src/bin/dvm/main.rs b/src/bin/dvm/main.rs index b75921e..5f7d16f 100644 --- a/src/bin/dvm/main.rs +++ b/src/bin/dvm/main.rs @@ -1,22 +1,233 @@ -use deimos::vm::op_codes::{add_mov_int, add_platform_call_to}; +use deimos::vm::dm_type::DmType; +use deimos::vm::lib::{DmConstant, DmLib}; +use deimos::vm::object_type::{DmField, DmFn, DmImplementation, DmInterface, DmMethod}; +use deimos::vm::op_codes::{ + add_alloc, add_alloc_raw_from, add_invoke_fn, add_invoke_method, add_mov_const, + add_mov_register_to, add_mov_size_of, add_multiply, add_platform_call, add_ret, + add_ret_register, +}; use deimos::vm::DmVirtualMachine; -use deimos::vm::dvm_value::DvmValue; +use std::rc::Rc; fn main() { - // TODO: - // - write a single module with a main() + // Goal: + // - write a single lib with a main() // - call the main fn - - let mut code: Vec = Vec::new(); - add_mov_int(&mut code, 0, 0x2a); // 42 - add_platform_call_to( - &mut code, - &String::from("std::core::println"), - 0, - 1, - &vec![0u8], + // fn main() { println "Hello, World!" } + + // std/unsafe/mem lib + let mut unsafe_mem_lib = DmLib::new("std/unsafe/mem"); + + // std::unsafe::Pointer + let unsafe_pointer_int = DmInterface::new("std::unsafe::Pointer", "Pointer"); + let unsafe_pointer_rc = Rc::new(unsafe_pointer_int); + + // std::unsafe::PointerImpl : Pointer + let mut unsafe_pointer_impl = DmImplementation::new( + "std::unsafe:PointerImpl", + "PointerImpl", + Some(unsafe_pointer_rc.clone()), ); - let mut vm = DmVirtualMachine::new(Vec::new()); - vm.run_raw(&mut code); - vm.call_by_fqn("default::main", vec![DvmValue::Unit; 0]); // will throw + + let raw_address_field = DmField::new("raw_address", DmType::Long, 0); + let size_field = DmField::new("size", DmType::Long, 8); + + // std::unsafe::Pointer::_ctor_0( + // r0: self + // r1: size Long + // ) + // r2: raw_address Long + let mut unsafe_pointer_ctor_0_bytecode: Vec = Vec::new(); + add_alloc_raw_from(&mut unsafe_pointer_ctor_0_bytecode, 2, 1); + add_mov_register_to( + &mut unsafe_pointer_ctor_0_bytecode, + 0, + raw_address_field.data_offset() as u32, + 2, + ); + add_mov_register_to( + &mut unsafe_pointer_ctor_0_bytecode, + 0, + size_field.data_offset() as u32, + 1, + ); + + let unsafe_pointer_init_fn = DmFn::new( + "std::unsafe::PointerImpl::_ctor_0", + "_ctor_0", + unsafe_pointer_ctor_0_bytecode, + 3, + ); + + let unsafe_pointer_init_method = DmMethod::new(unsafe_pointer_init_fn, None); + + unsafe_pointer_impl.fields.push(raw_address_field); + unsafe_pointer_impl.fields.push(size_field); + unsafe_pointer_impl + .methods + .push(Rc::new(unsafe_pointer_init_method)); + + let unsafe_pointer_impl_rc = Rc::new(unsafe_pointer_impl); + + unsafe_mem_lib.interfaces.push(unsafe_pointer_rc.clone()); + unsafe_mem_lib + .implementations + .push(unsafe_pointer_impl_rc.clone()); + + // std::unsafe::alloc( + // r0: size Long + // ) + // r0: size Long + // r1: Pointer object + // @return r1 + let mut alloc_fn_byte_code: Vec = Vec::new(); + add_alloc( + &mut alloc_fn_byte_code, + 1, + unsafe_pointer_impl_rc.size_in_bytes() as u32, + "std::unsafe::PointerImpl", + ); + add_invoke_method( + &mut alloc_fn_byte_code, + "std::unsafe::Pointer::_ctor_0", + &[1u8, 0u8], + ); + add_ret_register(&mut alloc_fn_byte_code, 1); + + let alloc_fn = DmFn { + fqn: "std::unsafe::mem::alloc".to_string(), + short_name: "alloc".to_string(), + byte_code: alloc_fn_byte_code, + number_used_registers: 2, + }; + + unsafe_mem_lib.functions.push(Rc::new(alloc_fn)); + + // std::core::Array + let core_array_int = DmInterface::new("std::core::Array", "Array"); + let core_array_int_rc = Rc::new(core_array_int); + + // std::core::ArrayImpl + let mut core_array_impl = DmImplementation::new( + "std::core::ArrayImpl", + "ArrayImpl", + Some(core_array_int_rc.clone()), + ); + + let array_impl_pointer_fld = DmField::new("pointer", DmType::Pointer, 0); + let array_impl_length_fld = DmField::new("length", DmType::Int, 8); + + // std::core::Array::_ctor_0( + // r0: self + // r1: TypeRef element_type + // r2: Int length + // ) + // r3: r1::size() + // r4: r3 * r1 + // r5: Pointer allocated pointer + let mut array_impl_ctor_0_bytecode: Vec = Vec::new(); + + add_mov_size_of(&mut array_impl_ctor_0_bytecode, 3, 1); + add_multiply(&mut array_impl_ctor_0_bytecode, 4, 3, 1); + add_invoke_fn( + &mut array_impl_ctor_0_bytecode, + "std::unsafe::mem::alloc", + 5, + &[4], + ); + add_mov_register_to( + &mut array_impl_ctor_0_bytecode, + 0, + array_impl_pointer_fld.data_offset() as u32, + 5, + ); + add_mov_register_to( + &mut array_impl_ctor_0_bytecode, + 0, + array_impl_length_fld.data_offset() as u32, + 2, + ); + + let array_impl_ctor_0_fn = DmFn::new( + "std::core::ArrayImpl::_ctor_0", + "_ctor_0", + array_impl_ctor_0_bytecode, + 6, + ); + let array_impl_ctor_0_method = DmMethod::new(array_impl_ctor_0_fn, None); + core_array_impl + .methods + .push(Rc::new(array_impl_ctor_0_method)); + + // std::core::String + + let mut core_string_lib = DmLib::new("std/core/string"); + let mut core_string_impl = DmImplementation::new("std::core::String", "String", None); + + let bytes_field = DmField::new("bytes", DmType::Pointer, 0); + + // std::core::String::_ctor_0( + // r0: self + // r1: DvmPointer to Array + // ) + let mut core_string_ctor_0_bytecode: Vec = Vec::new(); + add_mov_register_to( + &mut core_string_ctor_0_bytecode, + 0, + bytes_field.data_offset() as u32, + 1, + ); + + let core_string_ctor_0_fn = DmFn { + fqn: "std::core::String::_ctor_0".to_string(), + short_name: "_ctor_0".to_string(), + byte_code: core_string_ctor_0_bytecode, + number_used_registers: 2, + }; + + let core_string_ctor_0_method = DmMethod::new(core_string_ctor_0_fn, None); + + core_string_impl.fields.push(bytes_field); + core_string_impl.methods.push(Rc::new(core_string_ctor_0_method)); + let core_string_impl_size = core_string_impl.size_in_bytes(); + + core_string_lib + .implementations + .push(Rc::new(core_string_impl)); + + let mut greeting_lib = DmLib::new("greeting"); + greeting_lib + .constants + .push(DmConstant::String("Hello, World!".to_string())); + + let mut main_byte_code = Vec::new(); + + // 1. Move constant: r0 receives DvmValue::Pointer to Array + // 2. Allocate for std::core::String into r1 + // 3. Call String::_ctor_0(r0) -> r2 + // 4. Platform call std::core::println(r1) -> r3 + add_alloc( + &mut main_byte_code, + 0, + core_string_impl_size as u32, + "std::core::String", + ); + add_mov_const(&mut main_byte_code, 1, "greeting", 0); + add_invoke_method(&mut main_byte_code, "std::core::String::_init_0", &[0, 1]); + add_platform_call(&mut main_byte_code, "std::core::println", 0, 1, &vec![0u8]); + + let main_dm_fn = DmFn { + fqn: "default::main".to_string(), + short_name: "main".to_string(), + byte_code: main_byte_code, + number_used_registers: 2, + }; + + greeting_lib.functions.push(Rc::new(main_dm_fn)); + + let mut vm = DmVirtualMachine::new(vec![greeting_lib, core_string_lib]); + let main_fn = vm + .get_fn_by_fqn("default::main") + .expect("Could not find function: default::main"); + vm.call_fn(&main_fn, Vec::new()); } diff --git a/src/vm/dm_type.rs b/src/vm/dm_type.rs index 3286350..575d252 100644 --- a/src/vm/dm_type.rs +++ b/src/vm/dm_type.rs @@ -1,45 +1,24 @@ -use std::rc::Rc; -use crate::vm::object_type::DmObjectType; - #[derive(Debug, PartialEq, Eq)] pub enum DmType { - Primitive(DmPrimitiveType), - Object(Rc), -} - -#[derive(Debug, PartialEq, Eq)] -pub enum DmPrimitiveType { Byte, Int, Long, Double, Boolean, - Pointer(Rc), - ByteArray(usize), - IntArray(usize), - LongArray(usize), - DoubleArray(usize), - BooleanArray(usize), - PointerArray(usize, Rc), + Pointer, Unit, } -impl DmPrimitiveType { +impl DmType { pub fn size_in_bytes(&self) -> usize { match self { - DmPrimitiveType::Byte => size_of::(), - DmPrimitiveType::Int => size_of::(), - DmPrimitiveType::Long => size_of::(), - DmPrimitiveType::Double => size_of::(), - DmPrimitiveType::Boolean => size_of::(), - DmPrimitiveType::Pointer(_) => size_of::(), - DmPrimitiveType::ByteArray(length) => *length * size_of::(), - DmPrimitiveType::IntArray(length) => *length * size_of::(), - DmPrimitiveType::LongArray(length) => *length * size_of::(), - DmPrimitiveType::DoubleArray(length) => *length * size_of::(), - DmPrimitiveType::BooleanArray(length) => *length * size_of::(), - DmPrimitiveType::PointerArray(length, _) => *length * size_of::(), - DmPrimitiveType::Unit => todo!("Need to determine size of Unit... is it dependent on the size of the thing which is Unit?") + DmType::Byte => size_of::(), + DmType::Int => size_of::(), + DmType::Long => size_of::(), + DmType::Double => size_of::(), + DmType::Boolean => size_of::(), + DmType::Pointer => size_of::(), + DmType::Unit => todo!("Need to determine size of Unit... is it dependent on the size of the thing which is Unit?") } } } diff --git a/src/vm/dvm_value.rs b/src/vm/dvm_value.rs index 44b0a77..524049b 100644 --- a/src/vm/dvm_value.rs +++ b/src/vm/dvm_value.rs @@ -1,5 +1,5 @@ -use std::rc::Rc; use crate::vm::mem::DmAllocObject; +use std::rc::Rc; #[derive(Debug, Clone, PartialEq)] pub enum DvmValue { @@ -9,11 +9,5 @@ pub enum DvmValue { Double(f64), Boolean(bool), Pointer(Rc), - ByteArray(Vec), - IntArray(Vec), - LongArray(Vec), - DoubleArray(Vec), - BooleanArray(Vec), - PointerArray(Vec>), Unit, } diff --git a/src/vm/module.rs b/src/vm/lib/load.rs similarity index 50% rename from src/vm/module.rs rename to src/vm/lib/load.rs index 49c09a1..aa7004b 100644 --- a/src/vm/module.rs +++ b/src/vm/lib/load.rs @@ -1,89 +1,13 @@ -use std::rc::Rc; use crate::get_32_le; -use crate::vm::object_type::{DmFn, DmImplementation, DmInterface}; +use crate::vm::lib::DmLib; +use crate::vm::lib::magic::{CONST_SYMBOL, DEIMOS_MAGIC_NUMBER, FUNCTION_SYMBOL}; +use crate::vm::lib::symbol::LibSymbol; -pub const DEIMOS_MAGIC_NUMBER: u64 = 0x00_00_64_65_69_6d_6f_73; // ascii 'deimos' -pub const DEIMOS_MAGIC_STRING: [u8; 6] = [0x64, 0x65, 0x69, 0x6d, 0x6f, 0x73]; // ascii 'deimos' - -pub const COMPILER_VERSION_STRING: &str = "0.1.0"; - -pub struct DmModule { - pub compiler_version: String, - pub fqn: String, - pub short_name: String, - pub constants: Vec, - pub interfaces: Vec>, - pub implementations: Vec>, - pub functions: Vec>, -} - -pub enum DmConstant { - Int(i32), - Long(i64), - Double(f64), - String(String), -} - -const CONST_SYMBOL: u8 = 0x01; -const FUNCTION_SYMBOL: u8 = 0x02; - -enum SymbolType { - Constant, - Function, -} - -struct DmSymbol { - name: String, - symbol_type: SymbolType, - address: u32, -} - -macro_rules! push_byte_array { - ( $dest: expr, $arr: expr ) => { - for b in $arr { - $dest.push(b); - } - }; -} - -macro_rules! push_number_le { - ( $dest: expr, $num: expr ) => { - for b in $num.to_le_bytes() { - $dest.push(b); - } - }; -} - -macro_rules! push_string { - ( $dest: expr, $s: expr ) => { - for b in $s.len().to_le_bytes() { - $dest.push(b); - } - for b in $s.bytes() { - $dest.push(b); - } - }; -} - -pub fn write_module(module: DmModule) -> Vec { - // Push magic number - let mut result: Vec = Vec::new(); - push_byte_array!(result, DEIMOS_MAGIC_STRING); - - // Push version string - push_string!(result, module.compiler_version); - - // Push module name length, little endian - push_string!(result, module.fqn); - - result -} - -pub fn load_module(bytes: &[u8]) -> Result { +pub fn load_module(bytes: &[u8]) -> Result { let mut ip: usize = 0; // Check for magic number at bytes 0..5 if !check_deimos(&bytes) { - return Err(String::from("Not a valid Deimos module.")); + return Err(String::from("Not a valid Deimos lib.")); } ip = 6; @@ -103,13 +27,13 @@ pub fn load_module(bytes: &[u8]) -> Result { // Check version string. We'll use this in the future to not load modules compiled later than // current version. if version_string != "0.1.0" { - return Err(String::from("Invalid Deimos module version.")); + return Err(String::from("Invalid Deimos lib version.")); } - // TODO: extract module name + // TODO: extract lib name // Holder for Symbols we will extract from the symbol table bytes - let mut symbols: Vec = Vec::new(); + let mut symbols: Vec = Vec::new(); // Get the symbol table length and calculate how far we need to read let symbol_table_length = get_32_le!(bytes, 10 + version_string_length, 0, usize); @@ -118,7 +42,7 @@ pub fn load_module(bytes: &[u8]) -> Result { // For each "row" in the symbol table, // 1. Get the type // 2. Obtain the name's length and then get the name in utf8 - // 3. Grab the address to the actual symbol in the module bytes + // 3. Grab the address to the actual symbol in the lib bytes while ip < symbol_table_end { let type_byte = bytes[ip]; ip += 1; @@ -129,16 +53,12 @@ pub fn load_module(bytes: &[u8]) -> Result { ip += name_string_length; let address = get_32_le!(bytes, ip, 0, u32); ip += 4; - let symbol_type = match type_byte { - CONST_SYMBOL => SymbolType::Constant, - FUNCTION_SYMBOL => SymbolType::Function, + let lib_symbol = match type_byte { + CONST_SYMBOL => LibSymbol::Constant(name, address), + FUNCTION_SYMBOL => LibSymbol::Function(name, address), _ => return Err(String::from("Invalid Deimos symbol type.")), }; - symbols.push(DmSymbol { - name, - address, - symbol_type, - }); + symbols.push(lib_symbol); } todo!() @@ -163,7 +83,7 @@ fn read_as_u64(bytes: &[u8]) -> u64 { #[cfg(test)] mod read_as_u64_tests { - use crate::vm::module::{read_as_u64, DEIMOS_MAGIC_NUMBER}; + use super::*; #[test] fn read_6_bytes() { diff --git a/src/vm/lib/magic.rs b/src/vm/lib/magic.rs new file mode 100644 index 0000000..7e56b88 --- /dev/null +++ b/src/vm/lib/magic.rs @@ -0,0 +1,7 @@ +pub const DEIMOS_MAGIC_NUMBER: u64 = 0x00_00_64_65_69_6d_6f_73; // ascii 'deimos' +pub const DEIMOS_MAGIC_STRING: [u8; 6] = [0x64, 0x65, 0x69, 0x6d, 0x6f, 0x73]; // ascii 'deimos' + +pub const COMPILER_VERSION_STRING: &str = "0.1.0"; + +pub const CONST_SYMBOL: u8 = 0x01; +pub const FUNCTION_SYMBOL: u8 = 0x02; diff --git a/src/vm/lib/mod.rs b/src/vm/lib/mod.rs new file mode 100644 index 0000000..2bd771d --- /dev/null +++ b/src/vm/lib/mod.rs @@ -0,0 +1,31 @@ +mod load; +mod magic; +mod symbol; +mod write; + +use crate::vm::object_type::{DmFn, DmImplementation, DmInterface}; +use std::rc::Rc; + +pub struct DmLib { + pub name: String, + pub constants: Vec, + pub interfaces: Vec>, + pub implementations: Vec>, + pub functions: Vec>, +} + +impl DmLib { + pub fn new(name: &str) -> DmLib { + DmLib { + name: name.to_string(), + constants: Vec::new(), + interfaces: Vec::new(), + implementations: Vec::new(), + functions: Vec::new(), + } + } +} + +pub enum DmConstant { + String(String), +} diff --git a/src/vm/lib/symbol.rs b/src/vm/lib/symbol.rs new file mode 100644 index 0000000..6606de6 --- /dev/null +++ b/src/vm/lib/symbol.rs @@ -0,0 +1,4 @@ +pub enum LibSymbol { + Constant(String, u32), + Function(String, u32), +} diff --git a/src/vm/lib/write.rs b/src/vm/lib/write.rs new file mode 100644 index 0000000..837a3a6 --- /dev/null +++ b/src/vm/lib/write.rs @@ -0,0 +1,40 @@ +use crate::vm::lib::DmLib; +use crate::vm::lib::magic::{COMPILER_VERSION_STRING, DEIMOS_MAGIC_STRING}; + +macro_rules! push_byte_array { + ( $dest: expr, $arr: expr ) => { + for b in $arr { + $dest.push(b); + } + }; +} + +macro_rules! push_number_le { + ( $dest: expr, $num: expr ) => { + for b in $num.to_le_bytes() { + $dest.push(b); + } + }; +} + +macro_rules! push_string { + ( $dest: expr, $s: expr ) => { + for b in $s.len().to_le_bytes() { + $dest.push(b); + } + for b in $s.bytes() { + $dest.push(b); + } + }; +} + +pub fn write_module(module: DmLib) -> Vec { + // Push magic number + let mut result: Vec = Vec::new(); + push_byte_array!(result, DEIMOS_MAGIC_STRING); + + // Push version string + push_string!(result, COMPILER_VERSION_STRING); + + result +} \ No newline at end of file diff --git a/src/vm/mem.rs b/src/vm/mem.rs index 5250e50..418962c 100644 --- a/src/vm/mem.rs +++ b/src/vm/mem.rs @@ -1,7 +1,6 @@ -use crate::vm::dm_type::DmPrimitiveType; +use crate::vm::dm_type::DmType; use crate::vm::dvm_value::DvmValue; -use crate::vm::object_type::DmImplementation; -use crate::vm::object_type::DmProperty; +use crate::vm::object_type::{DmField, DmImplementation}; use std::alloc::Layout; use std::rc::Rc; @@ -13,48 +12,30 @@ pub struct DmAllocObject { pub implementation: Rc, } -pub unsafe fn get_property_value( - dm_property: &DmProperty, - self_object: &DmAllocObject, -) -> DvmValue { - let data_size = dm_property.primitive_type.size_in_bytes(); +pub unsafe fn get_field_value(dm_field: &DmField, self_object: &DmAllocObject) -> DvmValue { + let data_size = dm_field.dm_type().size_in_bytes(); let mut raw_data: Vec = Vec::with_capacity(data_size); - for i in dm_property.data_offset..(dm_property.data_offset + data_size) { + for i in dm_field.data_offset()..(dm_field.data_offset() + data_size) { raw_data.push(self_object.data.offset(i as isize).read()); } - match dm_property.primitive_type.as_ref() { - DmPrimitiveType::Byte => DvmValue::Byte(raw_data[0]), - DmPrimitiveType::Int => DvmValue::Int(i32::from_ne_bytes( + match dm_field.dm_type() { + DmType::Byte => DvmValue::Byte(raw_data[0]), + DmType::Int => DvmValue::Int(i32::from_ne_bytes( raw_data[0..data_size].try_into().unwrap(), )), - DmPrimitiveType::Long => DvmValue::Long(i64::from_ne_bytes( + DmType::Long => DvmValue::Long(i64::from_ne_bytes( raw_data[0..data_size].try_into().unwrap(), )), - DmPrimitiveType::Double => DvmValue::Double(f64::from_ne_bytes( + DmType::Double => DvmValue::Double(f64::from_ne_bytes( raw_data[0..data_size].try_into().unwrap(), )), - DmPrimitiveType::Boolean => DvmValue::Boolean(raw_data[0] == 1), - DmPrimitiveType::Pointer(_) => { + DmType::Boolean => DvmValue::Boolean(raw_data[0] == 1), + DmType::Pointer => { // read the pointer's (address) value let address = usize::from_ne_bytes(raw_data[0..data_size].try_into().unwrap()); DvmValue::Pointer(Rc::from_raw(address as *const DmAllocObject)) } - DmPrimitiveType::ByteArray(_) => DvmValue::ByteArray(raw_data), - DmPrimitiveType::IntArray(length) => DvmValue::IntArray(read_i32_array(&raw_data, *length)), - DmPrimitiveType::LongArray(length) => { - DvmValue::LongArray(read_i64_array(&raw_data, *length)) - } - DmPrimitiveType::DoubleArray(length) => { - DvmValue::DoubleArray(read_f64_array(&raw_data, *length)) - } - DmPrimitiveType::BooleanArray(length) => DvmValue::BooleanArray(read_bool_array(&raw_data)), - DmPrimitiveType::PointerArray(length, _) => DvmValue::PointerArray( - read_usize_array(&raw_data, *length) - .iter() - .map(|raw_usize| Rc::from_raw(*raw_usize as *const DmAllocObject)) - .collect(), - ), - DmPrimitiveType::Unit => DvmValue::Unit, + DmType::Unit => DvmValue::Unit, } } diff --git a/src/vm/mod.rs b/src/vm/mod.rs index e6e36c3..3b20c4e 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -1,17 +1,19 @@ pub mod dm_type; pub mod dvm_value; -mod mem; -pub mod module; -mod object_type; +pub mod lib; +pub mod mem; +pub mod object_type; pub mod op_codes; pub mod platform; pub mod util; use crate::vm::dvm_value::DvmValue; -use crate::vm::module::DmModule; +use crate::vm::lib::{DmConstant, DmLib}; +use crate::vm::mem::{get_field_value, DmAllocObject}; use crate::vm::object_type::DmFn; use crate::vm::platform::init_platform_functions; use op_codes::*; +use std::alloc::{alloc, Layout}; use std::collections::HashMap; use std::rc::Rc; use util::{get_32_le, get_64_le}; @@ -33,7 +35,7 @@ struct DeimosCallFrame { } pub struct DmVirtualMachine { - modules: Vec, + libs: Vec, functions: HashMap>, platform_functions: HashMap, ip: usize, @@ -42,35 +44,51 @@ pub struct DmVirtualMachine { register_state_stack: Vec>, } -fn load_functions(modules: &Vec) -> HashMap> { - let mut functions: HashMap> = HashMap::new(); - for module in modules { - for module_fn in &module.functions { - functions.insert(module_fn.fqn.clone(), module_fn.clone()); +fn load_functions(destination: &mut HashMap>, libs: &Vec) { + for module in libs { + for lib_fn in &module.functions { + destination.insert(lib_fn.fqn.clone(), lib_fn.clone()); + } + for interface_function in module + .interfaces + .iter() + .flat_map(|interface| interface.get_functions()) + { + destination.insert(interface_function.fqn.clone(), interface_function.clone()); + } + for implementation_function in module + .implementations + .iter() + .flat_map(|implementation| &implementation.functions) + { + destination.insert( + implementation_function.fqn.clone(), + implementation_function.clone(), + ); } - for interface in &module.interfaces {} } - functions } impl DmVirtualMachine { - pub fn new(modules: Vec) -> DmVirtualMachine { - DmVirtualMachine { - modules, + pub fn new(libs: Vec) -> DmVirtualMachine { + let mut vm = DmVirtualMachine { + libs: libs, functions: HashMap::new(), platform_functions: init_platform_functions(), ip: 0, registers: Vec::new(), call_stack: Vec::new(), register_state_stack: Vec::new(), - } + }; + load_functions(&mut vm.functions, &vm.libs); + vm } - pub fn call_by_fqn(&mut self, fn_fqn: &str, args: Vec) -> DvmValue { - todo!() + pub fn get_fn_by_fqn(&self, fqn: &str) -> Option> { + self.functions.get(fqn).cloned() } - pub fn call_fn(&mut self, dm_function: &DmFn, args: Vec) -> DvmValue { + pub fn call_fn(&mut self, dm_function: &DmFn, args: Vec) -> Option { // save current state self.call_stack.push(CallFrame::DeimosCall(DeimosCallFrame { return_address: self.ip, @@ -79,7 +97,9 @@ impl DmVirtualMachine { // zero registers and make sure there are enough for dm_function self.registers.clear(); - self.registers.resize(args.len(), DvmValue::Unit); + if self.registers.len() < args.len() { + self.registers.resize(args.len(), DvmValue::Unit); + } // push args for i in 0..args.len() { @@ -96,12 +116,14 @@ impl DmVirtualMachine { } // return result - self.registers.get(0).unwrap().clone() + self.registers.get(0).map(|x| x.clone()) } pub fn run_raw(&mut self, code: &Vec) { let mut i = 0; while i < code.len() { + let op_code = code[i]; + println!("op_code: {:#04x}", op_code); match code[i] { MOV_INT => { let target_register = code[i + 1] as usize; @@ -117,7 +139,9 @@ impl DmVirtualMachine { .insert(target_register, DvmValue::Long(operand as i64)); i += 10; } - MOV_DOUBLE => { /* todo */ } + MOV_DOUBLE => { + unimplemented!(); + } MOV_REGISTER => { let target_register = code[i + 1] as usize; let source_register = code[i + 2] as usize; @@ -126,7 +150,46 @@ impl DmVirtualMachine { i += 3; } ALLOC => { - todo!() + i += 1; + let target_register = code[i] as usize; + i += 1; + let alloc_size = get_32_le!(code, i, 0, usize); + i += 4; + let raw_implementation_name_length = get_32_le!(code, i, 0, usize); + i += 4; + let raw_implementation_name_bytes = + code[i..(i + raw_implementation_name_length)].to_vec(); + i += raw_implementation_name_length; + let implementation_name = + String::from_utf8(raw_implementation_name_bytes).unwrap(); + + let implementation = self + .libs + .iter() + .find_map(|lib| { + lib.implementations + .iter() + .find(|implementation| implementation.fqn == implementation_name) + }) + .expect(&format!( + "Implementation not found: {}", + implementation_name + )); + + let layout = Layout::from_size_align(alloc_size, 1).unwrap(); + let raw_data_ptr; + unsafe { + raw_data_ptr = alloc(layout); + } + let dm_alloc_object = DmAllocObject { + data: raw_data_ptr, + layout, + size: alloc_size, + implementation: implementation.clone(), + }; + + let dvm_value = DvmValue::Pointer(Rc::new(dm_alloc_object)); + self.registers.insert(target_register, dvm_value); } DEALLOC => { todo!() @@ -134,6 +197,79 @@ impl DmVirtualMachine { MOV_INT_TO => { todo!() } + MOV_REGISTER_TO => { + i += 1; + let target_register = code[i] as usize; + i += 1; + let offset = get_32_le!(code, i, 0, isize); + i += 4; + let source_register = code[i] as usize; + i += 1; + + let target_value = self.registers.get(target_register).unwrap(); + let target_pointer = match target_value { + DvmValue::Pointer(alloc_object) => alloc_object.data, + _ => panic!("Expected a DvmValue::Pointer, but got: {:?}", target_value), + }; + + let source_value = self.registers.get(source_register).unwrap(); + match source_value { + DvmValue::Pointer(source_alloc_object) => unsafe { + let source_pointer = Rc::into_raw(source_alloc_object.clone()); + for (j, b) in (source_pointer as usize).to_le_bytes().iter().enumerate() + { + target_pointer.offset(offset + j as isize).write(*b); + } + }, + _ => panic!("Currently unsupported source value: {:?}", source_value), + } + } + MOV_CONST => { + i += 1; + let target_register = code[i] as usize; + i += 1; + let lib_name_length = get_32_le!(code, i, 0, usize); + i += 4; + let lib_name_raw = code[i..(i + lib_name_length)].to_vec(); + i += lib_name_length; + let const_id = get_32_le!(code, i, 0, usize); + i += 4; + + let lib_name = String::from_utf8(lib_name_raw).unwrap(); + + if let Some(lib) = self.libs.iter().find(|&lib| lib.name == lib_name) { + let constant = &lib.constants[const_id]; + match constant { + DmConstant::String(s) => { + let alloc_fn = self.get_fn_by_fqn("std::unsafe::mem::alloc") + .expect("Could not find std::unsafe::mem::alloc"); + let alloc_size_arg = DvmValue::Long(s.len() as i64); + let alloc_return_value = self.call_fn(&alloc_fn, vec![alloc_size_arg]).unwrap(); + let DvmValue::Pointer(pointer_object) = alloc_return_value else { + panic!("Expected std::unsafe::mem::alloc to return DvmValue::Pointer, but got: {:?}", alloc_return_value); + }; + let raw_address_field = pointer_object.implementation.get_field("raw_address", &pointer_object) + .expect("Could not get PointerImpl.raw_address field."); + unsafe { + let raw_address_value = get_field_value(&raw_address_field, &pointer_object); + let DvmValue::Long(raw_address) = raw_address_value else { + panic!("Expected PointerImpl.raw_address to be a DvmValue::Long, but got: {:?}", raw_address_value); + }; + let ptr = raw_address as usize as *mut u8; + for (j, b) in s.bytes().enumerate() { + ptr.offset(j as isize).write(b); + } + self.registers.insert(target_register, DvmValue::Pointer(pointer_object)); + } + } + _ => { + panic!("Invalid constant type"); + } + } + } else { + panic!("Could not find lib: {}", lib_name); + } + } PLATFORM_CALL => { i += 1; @@ -169,7 +305,47 @@ impl DmVirtualMachine { let call_result = platform_function(args, self); self.registers.insert(return_register, call_result); } - _ => panic!("Invalid code instruction"), + INVOKE_FN => { + unimplemented!() + } + INVOKE_VIRTUAL => { + unimplemented!(); + } + INVOKE_METHOD => { + i += 1; + let name_length = get_32_le!(code, i, 0, usize); + i += 4; + let name_raw = code[i..(i + name_length)].to_vec(); + i += name_length; + let arg_registers_length = code[i] as usize; + i += 1; + let arg_registers = code[i..(i + arg_registers_length)].to_vec(); + i += arg_registers_length; + + let method_name = String::from_utf8(name_raw).unwrap(); + + let method = self + .get_fn_by_fqn(&method_name) + .expect(&format!("Could not find method: {}", method_name)); + + let mut args: Vec = Vec::new(); + for arg_register in arg_registers { + let register_value = self.registers.get(arg_register as usize).unwrap(); + args.push(register_value.clone()); + } + + self.call_fn(method.as_ref(), args); + } + INVOKE_DYNAMIC => { + unimplemented!() + } + RETURN => { + i += 1; + } + RETURN_REGISTER => { + unimplemented!() + } + op_code => panic!("Invalid or unimplemented op code: {:#04x}", op_code), } } } @@ -224,6 +400,7 @@ mod dvm_run_tests { } #[test] + #[ignore] fn mov_int_to_register_as_address() { let mut code = Vec::new(); add_alloc(&mut code, 0, 4); @@ -240,6 +417,7 @@ mod dvm_run_tests { } #[test] + #[ignore] fn alloc_and_dealloc_expect_register_cleared() { let mut code = Vec::new(); add_alloc(&mut code, 0, 4); diff --git a/src/vm/object_type.rs b/src/vm/object_type.rs index bd8029c..bd11694 100644 --- a/src/vm/object_type.rs +++ b/src/vm/object_type.rs @@ -1,4 +1,4 @@ -use crate::vm::dm_type::DmPrimitiveType; +use crate::vm::dm_type::DmType; use crate::vm::mem::DmAllocObject; use std::fmt::Debug; use std::rc::Rc; @@ -13,9 +13,35 @@ pub enum DmObjectType { pub struct DmFn { pub fqn: String, pub short_name: String, - pub implements: Option, pub byte_code: Vec, - pub num_registers: usize, + pub number_used_registers: usize, +} + +impl DmFn { + pub fn new(fqn: &str, short_name: &str, byte_code: Vec, number_used_registers: usize) -> Self { + DmFn { + fqn: fqn.to_string(), + short_name: short_name.to_string(), + byte_code, + number_used_registers + } + } + + pub fn fqn(&self) -> &str { + self.fqn.as_str() + } + + pub fn short_name(&self) -> &str { + self.short_name.as_str() + } + + pub fn byte_code(&self) -> &Vec { + &self.byte_code + } + + pub fn number_used_registers(&self) -> usize { + self.number_used_registers + } } impl PartialEq for DmFn { @@ -24,13 +50,33 @@ impl PartialEq for DmFn { } } +#[derive(Debug, Eq)] +pub struct DmMethod { + dm_fn: Rc, + implements: Option>, +} + +impl PartialEq for DmMethod { + fn eq(&self, other: &Self) -> bool { + self.dm_fn == other.dm_fn + } +} + +impl DmMethod { + pub fn new(dm_fn: DmFn, implements: Option>) -> Self { + DmMethod { + dm_fn: Rc::new(dm_fn), + implements, + } + } +} + #[derive(Debug, Eq)] pub struct DmInterface { - pub fqn: String, + fqn: String, short_name: String, - properties: Vec>, + functions: Vec>, virtual_methods: Vec>, - impl_methods: Vec>, } impl PartialEq for DmInterface { @@ -40,10 +86,37 @@ impl PartialEq for DmInterface { } impl DmInterface { - pub fn get_method(&self, name: &str, self_object: &DmAllocObject) -> Option> { - if let Some(dm_fn) = self.impl_methods.iter().find(|&dm_fn| dm_fn.fqn == name) { - return Some(dm_fn.clone()); - } else if self + pub fn new(fqn: &str, short_name: &str) -> Self { + DmInterface { + fqn: fqn.to_string(), + short_name: short_name.to_string(), + functions: Vec::new(), + virtual_methods: Vec::new(), + } + } + + pub fn fqn(&self) -> &str { + self.fqn.as_str() + } + + pub fn short_name(&self) -> &str { + self.short_name.as_str() + } + + pub fn add_function(&mut self, dm_fn: DmFn) { + self.functions.push(Rc::new(dm_fn)); + } + + pub fn get_functions(&self) -> Vec> { + self.functions.clone() + } + + pub fn add_method(&mut self, dm_virtual_method: DmVirtualMethod) { + self.virtual_methods.push(Rc::new(dm_virtual_method)); + } + + pub fn get_method(&self, name: &str, self_object: &DmAllocObject) -> Option> { + if self .virtual_methods .iter() .find(|&dm_fn| dm_fn.fqn == name) @@ -53,6 +126,10 @@ impl DmInterface { } None } + + pub fn get_virtual_methods(&self) -> Vec> { + self.virtual_methods.clone() + } pub fn get_property(&self, name: &str, self_object: &DmAllocObject) -> Option<&DmProperty> { todo!() @@ -75,11 +152,10 @@ impl PartialEq for DmVirtualMethod { pub struct DmImplementation { pub fqn: String, pub short_name: String, - pub interface: Rc, - pub size_in_bytes: usize, - properties: Vec, - fields: Vec, - methods: Vec>, + pub interface: Option>, + pub functions: Vec>, + pub methods: Vec>, + pub fields: Vec, } impl PartialEq for DmImplementation { @@ -89,9 +165,24 @@ impl PartialEq for DmImplementation { } impl DmImplementation { - pub fn get_method(&self, name: &str, self_object: &DmAllocObject) -> Option> { + pub fn new(fqn: &str, short_name: &str, interface: Option>) -> Self { + DmImplementation { + fqn: fqn.to_string(), + short_name: short_name.to_string(), + interface, + functions: Vec::new(), + methods: Vec::new(), + fields: Vec::new(), + } + } + + pub fn interface(&self) -> Option> { + self.interface.clone() + } + + pub fn get_method(&self, name: &str, self_object: &DmAllocObject) -> Option> { for method in &self.methods { - if method.fqn == name { + if method.dm_fn.fqn == name { return Some(method.clone()); } else if let Some(implements) = &method.implements { if implements.fqn == name { @@ -106,8 +197,12 @@ impl DmImplementation { todo!() } - pub fn get_field(&self, name: &str, self_object: &DmAllocObject) -> Option> { - todo!() + pub fn get_field(&self, name: &str, self_object: &DmAllocObject) -> Option<&DmField> { + self.fields.iter().find(|field| field.name == name) + } + + pub fn size_in_bytes(&self) -> usize { + self.fields.iter().map(|field| field.size_in_bytes()).sum() } } @@ -115,7 +210,7 @@ impl DmImplementation { pub struct DmProperty { pub name: String, pub data_offset: usize, - pub primitive_type: Rc, + pub primitive_type: Rc, } impl PartialEq for DmProperty { @@ -127,7 +222,8 @@ impl PartialEq for DmProperty { #[derive(Debug, Eq)] pub struct DmField { name: String, - primitive_type: Rc, + dm_type: DmType, + data_offset: usize, } impl PartialEq for DmField { @@ -135,3 +231,29 @@ impl PartialEq for DmField { self.name == other.name } } + +impl DmField { + pub fn new(name: &str, dm_type: DmType, data_offset: usize) -> Self { + DmField { + name: name.to_string(), + dm_type, + data_offset + } + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn dm_type(&self) -> &DmType { + &self.dm_type + } + + pub fn data_offset(&self) -> usize { + self.data_offset + } + + pub fn size_in_bytes(&self) -> usize { + self.dm_type.size_in_bytes() + } +} diff --git a/src/vm/op_codes.rs b/src/vm/op_codes.rs index 9866c9e..89d0d50 100644 --- a/src/vm/op_codes.rs +++ b/src/vm/op_codes.rs @@ -37,7 +37,23 @@ pub const MOV_LONG_TO: u8 = 0x08; pub const MOV_DOUBLE_TO: u8 = 0x09; pub const MOV_REGISTER_TO: u8 = 0x0a; +pub const MOV_CONST: u8 = 0x0b; + +pub const ALLOC_RAW: u8 = 0x0c; +pub const ALLOC_RAW_FROM: u8 = 0x0d; + pub const PLATFORM_CALL: u8 = 0x10; +pub const INVOKE_FN: u8 = 0x11; +pub const INVOKE_VIRTUAL: u8 = 0x12; +pub const INVOKE_METHOD: u8 = 0x13; +pub const INVOKE_DYNAMIC: u8 = 0x14; + +pub const RETURN: u8 = 0x20; +pub const RETURN_REGISTER: u8 = 0x21; + +pub const MOV_SIZE_OF: u8 = 0x30; + +pub const MULTIPLY: u8 = 0x40; macro_rules! push_number { ( $dest: expr, $num: expr ) => { @@ -63,10 +79,24 @@ pub fn add_mov_register(code: &mut Vec, target_register: u8, source_register code.push(source_register); } -pub fn add_alloc(code: &mut Vec, register: u8, size: u32) { +pub fn add_alloc(code: &mut Vec, register: u8, size: u32, implementation_name: &str) { code.push(ALLOC); code.push(register); push_number!(code, size); + push_number!(code, implementation_name.len() as u32); + push_string!(code, implementation_name); +} + +pub fn add_alloc_raw(code: &mut Vec, target_register: u8, size: u32) { + code.push(ALLOC_RAW); + code.push(target_register); + push_number!(code, size); +} + +pub fn add_alloc_raw_from(code: &mut Vec, target_register: u8, source_register: u8) { + code.push(ALLOC_RAW_FROM); + code.push(target_register); + push_number!(code, source_register); } pub fn add_dealloc(code: &mut Vec, register: u8) { @@ -81,7 +111,33 @@ pub fn add_mov_int_to(code: &mut Vec, register: u8, offset: u32, operand: u3 push_number!(code, operand); } -pub fn add_platform_call_to(code: &mut Vec, symbol_name: &String, return_register: u8, arg_registers_length: u8, arg_registers: &Vec) { +pub fn add_mov_register_to( + code: &mut Vec, + target_register: u8, + offset: u32, + source_register: u8, +) { + code.push(MOV_REGISTER_TO); + code.push(target_register); + push_number!(code, offset); + code.push(source_register); +} + +pub fn add_mov_const(code: &mut Vec, register: u8, lib_name: &str, const_id: u32) { + code.push(MOV_CONST); + code.push(register); + push_number!(code, lib_name.len() as u32); + push_string!(code, lib_name); + push_number!(code, const_id); +} + +pub fn add_platform_call( + code: &mut Vec, + symbol_name: &str, + return_register: u8, + arg_registers_length: u8, + arg_registers: &Vec, +) { code.push(PLATFORM_CALL); push_number!(code, symbol_name.len() as u32); push_string!(code, symbol_name); @@ -91,3 +147,51 @@ pub fn add_platform_call_to(code: &mut Vec, symbol_name: &String, return_reg code.push(b); } } + +pub fn add_invoke_fn(code: &mut Vec, fn_name: &str, return_register: u8, arg_registers: &[u8]) { + code.push(INVOKE_FN); + push_number!(code, fn_name.len() as u32); + push_string!(code, fn_name); + push_number!(code, return_register); + push_number!(code, arg_registers.len() as u8); + for &b in arg_registers { + code.push(b); + } +} + +pub fn add_invoke_method(code: &mut Vec, name: &str, arg_registers: &[u8]) { + code.push(INVOKE_METHOD); + push_number!(code, name.len() as u32); + push_string!(code, name); + push_number!(code, arg_registers.len() as u8); + for &b in arg_registers { + code.push(b); + } +} + +pub fn add_ret(code: &mut Vec) { + code.push(RETURN); +} + +pub fn add_ret_register(code: &mut Vec, register: u8) { + code.push(RETURN_REGISTER); + code.push(register); +} + +pub fn add_mov_size_of(code: &mut Vec, target_register: u8, source_register: u8) { + code.push(MOV_SIZE_OF); + code.push(target_register); + code.push(source_register); +} + +pub fn add_multiply( + code: &mut Vec, + target_register: u8, + left_register: u8, + right_register: u8, +) { + code.push(MULTIPLY); + code.push(target_register); + code.push(left_register); + code.push(right_register); +} diff --git a/src/vm/platform/std_lib/core.rs b/src/vm/platform/std_lib/core.rs index aa32649..419fd71 100644 --- a/src/vm/platform/std_lib/core.rs +++ b/src/vm/platform/std_lib/core.rs @@ -1,5 +1,5 @@ use crate::vm::dvm_value::DvmValue; -use crate::vm::mem::DmAllocObject; +use crate::vm::mem::{get_field_value, DmAllocObject}; use crate::vm::DmVirtualMachine; use std::rc::Rc; @@ -7,7 +7,9 @@ pub fn dm_print(args: Vec, vm: &mut DmVirtualMachine) -> DvmValue { if args.len() != 1 { return DvmValue::Unit; // TODO: make exception } - print!("{}", get_string(&args[0], vm)); + unsafe { + print!("{}", get_string(&args[0], vm)); + } DvmValue::Unit } @@ -15,11 +17,13 @@ pub fn dm_println(args: Vec, vm: &mut DmVirtualMachine) -> DvmValue { if args.len() != 1 { return DvmValue::Unit; } - println!("{}", get_string(&args[0], vm)); + unsafe { + println!("{}", get_string(&args[0], vm)); + } DvmValue::Unit } -fn get_string(dvm_value: &DvmValue, vm: &mut DmVirtualMachine) -> String { +unsafe fn get_string(dvm_value: &DvmValue, vm: &mut DmVirtualMachine) -> String { match dvm_value { DvmValue::Byte(b) => b.to_string(), DvmValue::Int(i) => i.to_string(), @@ -27,24 +31,66 @@ fn get_string(dvm_value: &DvmValue, vm: &mut DmVirtualMachine) -> String { DvmValue::Double(d) => d.to_string(), DvmValue::Boolean(b) => b.to_string(), DvmValue::Pointer(alloc_object_rc) => convert_to_string(alloc_object_rc.clone(), vm), - DvmValue::ByteArray(_) => todo!(), - DvmValue::IntArray(_) => todo!(), - DvmValue::LongArray(_) => todo!(), - DvmValue::DoubleArray(_) => todo!(), - DvmValue::BooleanArray(_) => todo!(), - DvmValue::PointerArray(_) => todo!(), DvmValue::Unit => String::from("Unit"), } } -fn convert_to_string(alloc_object_rc: Rc, vm: &mut DmVirtualMachine) -> String { - let to_string_result = vm.call_by_fqn( - "std::Display::to_string", - vec![DvmValue::Pointer(alloc_object_rc)], - ); - if let DvmValue::Pointer(to_string_result_alloc_object) = to_string_result { - todo!("Convert to_string_result_alloc_object (a String) to bytes to pass to print") +unsafe fn convert_to_string( + alloc_object_rc: Rc, + vm: &mut DmVirtualMachine, +) -> String { + if alloc_object_rc.implementation.fqn == "std::core::String" { + let bytes_field = alloc_object_rc + .implementation + .get_field("bytes", &alloc_object_rc) + .expect("Could not get String.bytes field."); + let bytes_value = get_field_value(&bytes_field, &alloc_object_rc); + match bytes_value { + DvmValue::Pointer(bytes_object) => { + if bytes_object.implementation.fqn == "std::core::ArrayImpl" { + let pointer_field = bytes_object + .implementation + .get_field("pointer", &bytes_object) + .expect("Could not get ArrayImpl.pointer field."); + let pointer_value = get_field_value(&pointer_field, &alloc_object_rc); + match pointer_value { + DvmValue::Pointer(pointer_object) => { + if pointer_object.implementation.fqn == "std::unsafe::PointerImpl" { + let raw_address_field = pointer_object.implementation.get_field("raw_address", &pointer_object) + .expect("Could not get PointerImpl.raw_address field."); + let raw_address_value = get_field_value(raw_address_field, &alloc_object_rc); + + let size_field = pointer_object.implementation.get_field("size", &alloc_object_rc) + .expect("Could not get PointerImpl.size field."); + let size_value = get_field_value(&size_field, &alloc_object_rc); + let DvmValue::Long(size) = size_value else { + panic!("Expected PointerImpl.size to be a Long"); + }; + + match raw_address_value { + DvmValue::Long(raw_address) => { + let raw_bytes_pointer = raw_address as usize as *mut u8; + let mut v: Vec = Vec::new(); + for i in 0..(size as isize) { + v.push(raw_bytes_pointer.offset(i).read()); + } + String::from_utf8(v).unwrap() + } + _ => panic!("Expected raw_address to be a Long.") + } + } else { + panic!("Expected ArrayImpl.pointer to be a Pointer to PointerImpl, instead found: {:?}", pointer_object); + } + } + _ => panic!("Expected ArrayImpl.pointer to be a Pointer, instead found: {:?}", pointer_value) + } + } else { + panic!("Expected String.bytes to be a Pointer to an ArrayImpl, instead found: {:?}", bytes_object.implementation.fqn); + } + } + _ => panic!("Expected String.bytes to be a Pointer, instead found: {:?}", bytes_value), + } } else { - panic!("to_string did not return a pointer") + todo!("what happens if we don't have a String?") } }