Attempt to implement runtime types.

This commit is contained in:
Jesse Brault 2024-12-01 11:59:22 -06:00
parent 9bc27b841b
commit 7903c1cfb3
7 changed files with 312 additions and 104 deletions

View File

@ -5,7 +5,7 @@ fn main() {
let mut code: Vec<u8> = Vec::new(); let mut code: Vec<u8> = Vec::new();
add_mov_int(&mut code, 0, 42); add_mov_int(&mut code, 0, 42);
add_platform_call_to(&mut code, &String::from("std::core::print"), 0, 1, &vec![0u8]); add_platform_call_to(&mut code, &String::from("std::core::print"), 0, 1, &vec![0u8]);
let mut vm = DmVirtualMachine::new(); let mut vm = DmVirtualMachine::new(Vec::new());
vm.run(&mut code); vm.run(&mut code);
println!() println!()
} }

78
src/vm/mem.rs Normal file
View File

@ -0,0 +1,78 @@
use crate::vm::types::{DmImplementation, DmProperty, DmType};
use crate::vm::values::DmValue;
use std::alloc::Layout;
pub struct DmAllocObject<'a> {
pub data: *mut u8,
pub size: usize,
pub layout: Layout,
pub object_type: &'a DmImplementation<'a>,
}
pub unsafe fn get_property_value(dm_property: &DmProperty, dm_property_object: &DmAllocObject) -> DmValue {
let mut data: Vec<u8> = Vec::with_capacity(dm_property.dm_type.size_in_bytes());
for i in dm_property.data_offset..(dm_property.data_offset + dm_property.dm_type.size_in_bytes()) {
data.push(dm_property_object.data.offset(i as isize).read());
}
match dm_property.dm_type {
DmType::Byte => {
DmValue::DmByte(data[0])
}
DmType::Int => {
DmValue::DmInt(i32::from_ne_bytes(data[0..4].try_into().unwrap()))
}
DmType::Long => {
DmValue::DmLong(i64::from_ne_bytes(data[0..8].try_into().unwrap()))
}
DmType::Double => {
DmValue::DmDouble(f64::from_ne_bytes(data[0..8].try_into().unwrap()))
}
DmType::Boolean => {
DmValue::DmBoolean(i32::from_ne_bytes(data[0..4].try_into().unwrap()) != 0)
}
DmType::ObjectPointer(_) => {
DmValue::DmPointer(usize::from_ne_bytes(data[0..8].try_into().unwrap()) as *mut DmAllocObject)
}
DmType::ByteArray(_) => {
DmValue::DmByteArray(data)
}
DmType::IntArray(length) => {
let mut result = Vec::with_capacity(length);
for i in 0..length {
result.push(i32::from_ne_bytes(data[(i * 4)..(i * 4 + 4)].try_into().unwrap()));
}
DmValue::DmIntArray(result)
}
DmType::LongArray(length) => {
let mut result = Vec::with_capacity(length);
for i in 0..length {
result.push(i64::from_ne_bytes(data[i * 8..(i * 8 + 8)].try_into().unwrap()));
}
DmValue::DmLongArray(result)
}
DmType::DoubleArray(length) => {
let mut result = Vec::with_capacity(length);
for i in 0..length {
result.push(f64::from_ne_bytes(data[i * 8..(i * 8 + 8)].try_into().unwrap()));
}
DmValue::DmDoubleArray(result)
}
DmType::BooleanArray(length) => {
let mut result = Vec::with_capacity(length);
for i in 0..length {
result.push(i32::from_ne_bytes(data[(i * 4)..(i * 4 + 4)].try_into().unwrap()) != 0);
}
DmValue::DmBooleanArray(result)
}
DmType::ObjectPointerArray(length, _) => {
let mut result = Vec::with_capacity(length);
for i in 0..length {
result.push(usize::from_ne_bytes(data[(i * 8)..(i * 8 + 8)].try_into().unwrap()) as *mut DmAllocObject);
}
DmValue::DmPointerArray(result)
}
DmType::Unit => {
DmValue::DmUnit
}
}
}

View File

@ -4,13 +4,15 @@ pub mod platform;
pub mod types; pub mod types;
pub mod util; pub mod util;
pub mod values; pub mod values;
mod mem;
use crate::vm::module::DmFunction; use crate::vm::mem::DmAllocObject;
use crate::vm::module::DmModule;
use crate::vm::platform::init_platform_functions; use crate::vm::platform::init_platform_functions;
use crate::vm::types::{DmFn, DmImplementation};
use op_codes::*; use op_codes::*;
use std::alloc::{alloc_zeroed, dealloc, Layout}; use std::alloc::{alloc_zeroed, dealloc, Layout};
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::Index;
use types::DmType; use types::DmType;
use util::{get_32_le, get_64_le}; use util::{get_32_le, get_64_le};
use values::DmValue; use values::DmValue;
@ -25,15 +27,15 @@ enum CallFrame {
pub struct PlatformCallFrame { pub struct PlatformCallFrame {
pub name: String, pub name: String,
pub args: Vec<u64>, pub args: Vec<DmValue>,
pub arg_types: Vec<DmType>,
} }
struct DeimosCallFrame { struct DeimosCallFrame {
return_address: usize, return_address: usize,
} }
pub struct DmVirtualMachine { pub struct DmVirtualMachine<'a> {
modules: Vec<DmModule<'a>>,
platform_functions: HashMap<String, PlatformFunction>, platform_functions: HashMap<String, PlatformFunction>,
ip: usize, ip: usize,
call_stack: Vec<CallFrame>, call_stack: Vec<CallFrame>,
@ -41,9 +43,10 @@ pub struct DmVirtualMachine {
register_state_stack: Vec<Vec<DmValue>>, register_state_stack: Vec<Vec<DmValue>>,
} }
impl DmVirtualMachine { impl DmVirtualMachine<'_> {
pub fn new() -> DmVirtualMachine { pub fn new(modules: Vec<DmModule>) -> DmVirtualMachine {
DmVirtualMachine { DmVirtualMachine {
modules,
ip: 0, ip: 0,
registers: Vec::new(), registers: Vec::new(),
platform_functions: init_platform_functions(), platform_functions: init_platform_functions(),
@ -54,7 +57,7 @@ impl DmVirtualMachine {
pub fn call( pub fn call(
&mut self, &mut self,
dm_function: &DmFunction, dm_function: &DmFn,
args: Vec<DmValue>, args: Vec<DmValue>,
) -> DmValue { ) -> DmValue {
// save current state // save current state
@ -110,15 +113,36 @@ impl DmVirtualMachine {
i += 3; i += 3;
} }
ALLOC => { ALLOC => {
let target_register = code[i + 1] as usize; i += 1;
let size = get_32_le!(code, i, 2, usize); let target_register = code[i] as usize;
i += 1;
let symbol_name_length = get_32_le!(code, i, 0, usize);
i += 4;
let raw_symbol_name = code[i..(i + symbol_name_length)].to_vec();
i += symbol_name_length;
let symbol_name = String::from_utf8(raw_symbol_name).unwrap();
// find implementation
let mut implementation_result: Option<&DmImplementation> = None;
'module_find: for module in self.modules.iter() {
if let Some(dm_implementation) = module.implementations.get(&symbol_name) {
implementation_result = Some(dm_implementation);
break 'module_find;
}
}
if implementation_result == None {
panic!("Implementation {} not found", symbol_name);
}
let dm_implementation = implementation_result.unwrap();
let size = dm_implementation.size_in_bytes;
let layout = Layout::from_size_align(size, 4).unwrap(); let layout = Layout::from_size_align(size, 4).unwrap();
let pointer = unsafe { alloc_zeroed(layout) }; let pointer = unsafe { alloc_zeroed(layout) };
let dm_object = Box::new(DmObject { let dm_object = Box::new(DmAllocObject {
object_type: dm_implementation,
data: pointer, data: pointer,
size, size,
layout, layout,
object_type: todo!(),
}); });
let dm_object_pointer = Box::into_raw(dm_object); let dm_object_pointer = Box::into_raw(dm_object);
self.registers.insert(target_register, DmPointer(dm_object_pointer)); self.registers.insert(target_register, DmPointer(dm_object_pointer));
@ -201,30 +225,6 @@ impl DmVirtualMachine {
} }
pub struct DmObjectType {
name: String,
properties: HashMap<String, DmObjectProperty>,
fields: HashMap<String, DmObjectField>,
methods: HashMap<String, DmFunction>,
}
pub struct DmObject {
object_type: Box<DmObjectType>,
data: *mut u8,
size: usize,
layout: Layout,
}
pub struct DmObjectProperty {
name: String,
property_type: DmType,
}
pub struct DmObjectField {
name: String,
field_type: DmType,
}
#[cfg(test)] #[cfg(test)]
mod dvm_run_tests { mod dvm_run_tests {
use super::*; use super::*;
@ -240,7 +240,7 @@ mod dvm_run_tests {
fn mov_1_as_int() { fn mov_1_as_int() {
let mut code = Vec::new(); let mut code = Vec::new();
add_mov_int(&mut code, 0, 1); add_mov_int(&mut code, 0, 1);
let mut vm = DmVirtualMachine::new(); let mut vm = DmVirtualMachine::new(Vec::new());
vm.run(&code); vm.run(&code);
assert_register!(DmInt(1), vm.registers.get(0)); assert_register!(DmInt(1), vm.registers.get(0));
} }
@ -249,7 +249,7 @@ mod dvm_run_tests {
fn move_65535_as_int() { fn move_65535_as_int() {
let mut code = Vec::new(); let mut code = Vec::new();
add_mov_int(&mut code, 0, 0xffff); add_mov_int(&mut code, 0, 0xffff);
let mut vm = DmVirtualMachine::new(); let mut vm = DmVirtualMachine::new(Vec::new());
vm.run(&code); vm.run(&code);
assert_register!(DmInt(0xffff), vm.registers.get(0)); assert_register!(DmInt(0xffff), vm.registers.get(0));
} }
@ -258,7 +258,7 @@ mod dvm_run_tests {
fn move_int_max_as_int() { fn move_int_max_as_int() {
let mut code = Vec::new(); let mut code = Vec::new();
add_mov_int(&mut code, 0, 0x0fff_ffff); add_mov_int(&mut code, 0, 0x0fff_ffff);
let mut vm = DmVirtualMachine::new(); let mut vm = DmVirtualMachine::new(Vec::new());
vm.run(&code); vm.run(&code);
assert_register!(DmInt(0x0fff_ffff), vm.registers.get(0)); assert_register!(DmInt(0x0fff_ffff), vm.registers.get(0));
} }
@ -268,7 +268,7 @@ mod dvm_run_tests {
let mut code = Vec::new(); let mut code = Vec::new();
add_mov_int(&mut code, 1, 1); add_mov_int(&mut code, 1, 1);
add_mov_register(&mut code, 0, 1); add_mov_register(&mut code, 0, 1);
let mut vm = DmVirtualMachine::new(); let mut vm = DmVirtualMachine::new(Vec::new());
vm.registers.resize(2, DmUnit); vm.registers.resize(2, DmUnit);
vm.run(&code); vm.run(&code);
assert_register!(DmInt(1), vm.registers.get(0)); assert_register!(DmInt(1), vm.registers.get(0));
@ -280,7 +280,7 @@ mod dvm_run_tests {
let mut code = Vec::new(); let mut code = Vec::new();
add_alloc(&mut code, 0, 4); add_alloc(&mut code, 0, 4);
add_mov_int_to(&mut code, 0, 0, 0xff); add_mov_int_to(&mut code, 0, 0, 0xff);
let mut vm = DmVirtualMachine::new(); let mut vm = DmVirtualMachine::new(Vec::new());
vm.run(&code); vm.run(&code);
let box_address = vm.registers.get(0).unwrap().clone(); let box_address = vm.registers.get(0).unwrap().clone();
match box_address { match box_address {
@ -299,7 +299,7 @@ mod dvm_run_tests {
let mut code = Vec::new(); let mut code = Vec::new();
add_alloc(&mut code, 0, 4); add_alloc(&mut code, 0, 4);
add_dealloc(&mut code, 0); add_dealloc(&mut code, 0);
let mut vm = DmVirtualMachine::new(); let mut vm = DmVirtualMachine::new(Vec::new());
vm.run(&code); vm.run(&code);
assert_register!(DmUnit, vm.registers.get(0)); assert_register!(DmUnit, vm.registers.get(0));
} }

View File

@ -1,17 +1,20 @@
use crate::get_32_le; use crate::get_32_le;
use crate::vm::types::{DmFn, DmImplementation, DmInterface};
use std::collections::HashMap; use std::collections::HashMap;
use std::io::Read;
pub const DEIMOS_MAGIC_NUMBER: u64 = 0x00_00_64_65_69_6d_6f_73; // ascii 'deimos' pub const DEIMOS_MAGIC_NUMBER: u64 = 0x00_00_64_65_69_6d_6f_73; // ascii 'deimos'
const DEIMOS_MAGIC_STRING: [u8; 6] = [0x64, 0x65, 0x69, 0x6d, 0x6f, 0x73]; // ascii 'deimos' pub const DEIMOS_MAGIC_STRING: [u8; 6] = [0x64, 0x65, 0x69, 0x6d, 0x6f, 0x73]; // ascii 'deimos'
pub const VERSION_STRING: &str = "0.1.0"; pub const COMPILER_VERSION_STRING: &str = "0.1.0";
pub struct DmModule { pub struct DmModule<'a> {
name: String, pub compiler_version: String,
version: String, pub fqn: String,
constants: HashMap<String, DmConstant>, pub short_name: String,
functions: HashMap<String, DmFunction>, pub constants: HashMap<String, DmConstant>,
pub interfaces: HashMap<String, DmInterface<'a>>,
pub implementations: HashMap<String, DmImplementation<'a>>,
pub functions: HashMap<String, DmFn>,
} }
pub enum DmConstant { pub enum DmConstant {
@ -21,12 +24,6 @@ pub enum DmConstant {
String(String), String(String),
} }
pub struct DmFunction {
pub name: String,
pub byte_code: Vec<u8>,
pub num_registers: usize,
}
const CONST_SYMBOL: u8 = 0x01; const CONST_SYMBOL: u8 = 0x01;
const FUNCTION_SYMBOL: u8 = 0x02; const FUNCTION_SYMBOL: u8 = 0x02;
@ -74,10 +71,10 @@ pub fn write_module(module: DmModule) -> Vec<u8> {
push_byte_array!(result, DEIMOS_MAGIC_STRING); push_byte_array!(result, DEIMOS_MAGIC_STRING);
// Push version string // Push version string
push_string!(result, module.version); push_string!(result, module.compiler_version);
// Push module name length, little endian // Push module name length, little endian
push_string!(result, module.name); push_string!(result, module.fqn);
result result
} }

View File

@ -1,26 +1,5 @@
use crate::vm::module::DmFunction;
use crate::vm::DmValue::*; use crate::vm::DmValue::*;
use crate::vm::{DmObject, DmValue, DmVirtualMachine}; use crate::vm::{mem, DmValue, DmVirtualMachine};
fn get_method<'a>(ptr: *mut DmObject, name: String) -> Option<&'a DmFunction> {
unsafe {
(*ptr).object_type.methods.get(&name)
}
}
fn get_rust_string_from_dm_object(ptr: &*mut DmObject) -> String {
unsafe {
let dm_object = Box::from_raw(*ptr);
format!("{}@{:?}", dm_object.object_type.name, dm_object.data)
}
}
fn get_rust_string_from_dm_string(ptr: *mut DmObject) -> String {
unsafe {
let dm_string = Box::from_raw(ptr);
todo!()
}
}
pub fn dm_print(args: Vec<DmValue>, vm: &mut DmVirtualMachine) -> DmValue { pub fn dm_print(args: Vec<DmValue>, vm: &mut DmVirtualMachine) -> DmValue {
if args.len() != 1 { if args.len() != 1 {
@ -42,20 +21,26 @@ pub fn dm_print(args: Vec<DmValue>, vm: &mut DmVirtualMachine) -> DmValue {
DmBoolean(b) => { DmBoolean(b) => {
print!("{}", *b); print!("{}", *b);
} }
DmPointer(ptr) => { DmPointer(ptr) => unsafe {
if let Some(to_string) = get_method(*ptr, String::from("to_string")) { let dm_alloc_object = Box::from_raw(*ptr);
let call_result = vm.call(&to_string, vec![args[0].clone()]); let dm_object_type = &dm_alloc_object.object_type;
match call_result { if let Some(to_string_method) = dm_object_type.get_method(String::from("std::core::Object::to_string"), &dm_alloc_object) {
DmPointer(dm_string_ptr) => { let call_result = vm.call(to_string_method, vec![args[0].clone()]);
let string = get_rust_string_from_dm_string(dm_string_ptr); if let DmPointer(dm_string_pointer) = call_result {
print!("{}", string); let dm_string_object = Box::from_raw(dm_string_pointer);
} let bytes_property = dm_string_object.object_type.get_property(String::from("bytes"), &dm_string_object).unwrap();
_ => { let string_bytes_value = mem::get_property_value(bytes_property, &dm_string_object);
// TODO: vm throw or return exception? if let DmByteArray(raw_string_bytes) = string_bytes_value {
// TODO: other encodings
print!("{}", String::from_utf8(raw_string_bytes).unwrap());
} else {
panic!("Expected std::core::String.bytes to be a ByteArray.")
} }
} else {
panic!("Expected {} to return a string.", to_string_method.fqn)
} }
} else { } else {
print!("{}", get_rust_string_from_dm_object(ptr)); println!("{}@{:?}", dm_object_type.get_fqn(), dm_alloc_object.data)
} }
}, },
DmByteArray(bs) => { DmByteArray(bs) => {

View File

@ -1,14 +1,162 @@
#[derive(PartialEq, Eq, Clone, Debug, Copy)] use crate::vm::mem::DmAllocObject;
pub enum DmType { use std::collections::HashMap;
#[derive(Debug, PartialEq, Eq)]
pub enum DmType<'a> {
Byte,
Int, Int,
Long, Long,
Double, Double,
Boolean, Boolean,
Pointer, ObjectPointer(&'a dyn DmObjectType),
IntArray, ByteArray(usize),
LongArray, IntArray(usize),
DoubleArray, LongArray(usize),
BooleanArray, DoubleArray(usize),
PointerArray, BooleanArray(usize),
ObjectPointerArray(usize, &'a dyn DmObjectType),
Unit, Unit,
} }
impl DmType<'_> {
pub fn size_in_bytes(&self) -> usize {
match self {
DmType::Byte => 1,
DmType::Int => 4,
DmType::Long => 8,
DmType::Double => 8,
DmType::Boolean => 4,
DmType::ObjectPointer(_) => 8,
DmType::ByteArray(length) => *length,
DmType::IntArray(length) => *length * 4,
DmType::LongArray(length) => *length * 8,
DmType::DoubleArray(length) => *length * 8,
DmType::BooleanArray(length) => *length * 4,
DmType::ObjectPointerArray(length, _) => length * 8,
DmType::Unit => 0
}
}
}
pub trait DmObjectType {
fn get_fqn(&self) -> &String;
fn get_method(&self, name: String, self_object: &DmAllocObject) -> Option<&DmFn>;
fn get_property(&self, name: String, self_object: &DmAllocObject) -> Option<&DmProperty>;
}
#[derive(Debug, Eq)]
pub struct DmFn {
pub fqn: String,
pub short_name: String,
pub byte_code: Vec<u8>,
pub num_registers: usize,
}
impl PartialEq for DmFn {
fn eq(&self, other: &Self) -> bool {
self.fqn == other.fqn
}
}
#[derive(Debug, Eq)]
pub struct DmInterface<'a> {
fqn: String,
short_name: String,
properties: HashMap<String, DmProperty<'a>>,
virtual_methods: HashMap<String, DmVirtualMethod>,
impl_methods: HashMap<String, DmFn>,
}
impl PartialEq for DmInterface<'_> {
fn eq(&self, other: &Self) -> bool {
self.fqn == other.fqn
}
}
impl DmObjectType for DmInterface<'_> {
fn get_fqn(&self) -> &String {
&self.fqn
}
fn get_method(&self, name: String, self_object: &DmAllocObject) -> Option<&DmFn> {
if self.impl_methods.contains_key(&name) {
self.impl_methods.get(&name)
} else if self.virtual_methods.contains_key(&name) {
self_object.object_type.get_method(name, &self_object)
} else {
None
}
}
fn get_property(&self, name: String, self_object: &DmAllocObject) -> Option<&DmProperty> {
self.properties.get(&name)
}
}
#[derive(Debug, Eq)]
pub struct DmVirtualMethod {
pub fqn: String,
pub short_name: String,
}
impl PartialEq for DmVirtualMethod {
fn eq(&self, other: &Self) -> bool {
self.fqn == other.fqn
}
}
#[derive(Debug, Eq)]
pub struct DmImplementation<'a> {
pub fqn: String,
pub short_name: String,
pub interface: &'a DmInterface<'a>,
pub properties: HashMap<String, DmProperty<'a>>,
pub fields: HashMap<String, DmField<'a>>,
pub size_in_bytes: usize,
pub methods: HashMap<String, DmFn>
}
impl PartialEq for DmImplementation<'_> {
fn eq(&self, other: &Self) -> bool {
self.fqn == other.fqn
}
}
impl DmObjectType for DmImplementation<'_> {
fn get_fqn(&self) -> &String {
&self.fqn
}
fn get_method(&self, name: String, self_object: &DmAllocObject) -> Option<&DmFn> {
self.methods.get(&name)
}
fn get_property(&self, name: String, self_object: &DmAllocObject) -> Option<&DmProperty> {
self.properties.get(&name)
}
}
#[derive(Debug, Eq)]
pub struct DmProperty<'a> {
pub name: String,
pub data_offset: usize,
pub dm_type: DmType<'a>
}
impl PartialEq for DmProperty<'_> {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
}
}
#[derive(Debug, Eq)]
pub struct DmField<'a> {
name: String,
dm_type: DmType<'a>,
}
impl PartialEq for DmField<'_> {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
}
}

View File

@ -1,4 +1,4 @@
use crate::vm::DmObject; use crate::vm::mem::DmAllocObject;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum DmValue { pub enum DmValue {
@ -7,12 +7,12 @@ pub enum DmValue {
DmLong(i64), DmLong(i64),
DmDouble(f64), DmDouble(f64),
DmBoolean(bool), DmBoolean(bool),
DmPointer(*mut DmObject), DmPointer(*mut DmAllocObject),
DmByteArray(Vec<u8>), DmByteArray(Vec<u8>),
DmIntArray(Vec<i32>), DmIntArray(Vec<i32>),
DmLongArray(Vec<i64>), DmLongArray(Vec<i64>),
DmDoubleArray(Vec<f64>), DmDoubleArray(Vec<f64>),
DmBooleanArray(Vec<bool>), DmBooleanArray(Vec<bool>),
DmPointerArray(Vec<*mut DmObject>), DmPointerArray(Vec<*mut DmAllocObject>),
DmUnit, DmUnit,
} }