Major refactoring of virtual machine.

This commit is contained in:
Jesse Brault 2025-04-12 21:20:08 -05:00
parent a1ceab9d8f
commit 22e8605cc6
19 changed files with 1078 additions and 990 deletions

View File

@ -1,10 +1,14 @@
use deimos::vm::dm_type::DmType; use deimos::vm::field::DvmField;
use deimos::vm::function::DvmFunction;
use deimos::vm::interface::DmInterface;
use deimos::vm::lib::{DmConstant, DmLib}; use deimos::vm::lib::{DmConstant, DmLib};
use deimos::vm::object_type::{DmField, DmFn, DmImplementation, DmInterface, DmMethod}; use deimos::vm::method::DvmMethod;
use deimos::vm::object_type::DvmImplementation;
use deimos::vm::op_codes::{ use deimos::vm::op_codes::{
add_alloc, add_dealloc, add_invoke_fn, add_mov_const, add_platform_call, add_store, add_alloc, add_dealloc, add_invoke_fn, add_mov_const, add_platform_call, add_store,
}; };
use deimos::vm::platform::init_platform_functions; use deimos::vm::platform::init_platform_functions;
use deimos::vm::r#type::DvmType;
use deimos::vm::{call_fn, DvmContext, DvmState}; use deimos::vm::{call_fn, DvmContext, DvmState};
use std::rc::Rc; use std::rc::Rc;
@ -13,12 +17,12 @@ fn main() {
// - write a single lib with a main() // - write a single lib with a main()
// - call the main fn // - call the main fn
// fn main() { println "Hello, World!" } // fn main() { println "Hello, World!" }
// std/core/array lib // std/core/array lib
let mut array_lib = DmLib::new("std/core/array"); let mut array_lib = DmLib::new("std/core/array");
// std::core::Array // std::core::Array
let array_int = DmInterface::new("std::core::Array", "Array"); let array_int = DmInterface::new("std::core::Array");
array_lib.add_interface(array_int); array_lib.add_interface(array_int);
let array_int_rc = array_lib let array_int_rc = array_lib
.interfaces() .interfaces()
@ -27,15 +31,11 @@ fn main() {
.unwrap(); .unwrap();
// std::core::ArrayImpl // std::core::ArrayImpl
let mut array_impl = DmImplementation::new( let mut array_impl = DvmImplementation::new("std::core::ArrayImpl", Some(array_int_rc.clone()));
"std::core::ArrayImpl",
"ArrayImpl",
Some(array_int_rc.clone()),
);
let array_impl_ptr_address_fld = DmField::new("ptr_address", DmType::USize); let array_impl_ptr_address_fld = DvmField::new("ptr_address", DvmType::USize);
let array_impl_ptr_size_fld = DmField::new("ptr_size", DmType::USize); let array_impl_ptr_size_fld = DvmField::new("ptr_size", DvmType::USize);
let array_impl_length_fld = DmField::new("length", DmType::Long); let array_impl_length_fld = DvmField::new("length", DvmType::Long);
array_impl.add_field(array_impl_ptr_address_fld); array_impl.add_field(array_impl_ptr_address_fld);
array_impl.add_field(array_impl_ptr_size_fld); array_impl.add_field(array_impl_ptr_size_fld);
@ -53,14 +53,14 @@ fn main() {
add_store(&mut array_impl_ctor_0_bytecode, 0, 1, 2); add_store(&mut array_impl_ctor_0_bytecode, 0, 1, 2);
add_store(&mut array_impl_ctor_0_bytecode, 0, 2, 3); add_store(&mut array_impl_ctor_0_bytecode, 0, 2, 3);
let array_impl_ctor_0_fn = DmFn::new( let array_impl_ctor_0_fn = DvmFunction::new(
"std::core::ArrayImpl::_ctor_0", "std::core::ArrayImpl::_ctor_0",
"_ctor_0", "_ctor_0",
array_impl_ctor_0_bytecode, array_impl_ctor_0_bytecode,
4, 4,
None, None,
); );
let array_impl_ctor_0_method = DmMethod::new(array_impl_ctor_0_fn, None); let array_impl_ctor_0_method = DvmMethod::new(array_impl_ctor_0_fn, None);
array_impl.add_method(array_impl_ctor_0_method); array_impl.add_method(array_impl_ctor_0_method);
array_lib.add_implementation(array_impl); array_lib.add_implementation(array_impl);
@ -68,7 +68,7 @@ fn main() {
// std::core::String // std::core::String
let mut string_lib = DmLib::new("std/core/string"); let mut string_lib = DmLib::new("std/core/string");
let string_int = DmInterface::new("std::core::String", "String"); let string_int = DmInterface::new("std::core::String");
string_lib.add_interface(string_int); string_lib.add_interface(string_int);
let string_int_rc = string_lib let string_int_rc = string_lib
.interfaces() .interfaces()
@ -76,13 +76,10 @@ fn main() {
.find(|interface| interface.fqn() == "std::core::String") .find(|interface| interface.fqn() == "std::core::String")
.unwrap(); .unwrap();
let mut string_impl = DmImplementation::new( let mut string_impl =
"std::core::StringImpl", DvmImplementation::new("std::core::StringImpl", Some(string_int_rc.clone()));
"StringImpl",
Some(string_int_rc.clone()),
);
let bytes_field = DmField::new("bytes", DmType::Object); let bytes_field = DvmField::new("bytes", DvmType::Object);
string_impl.add_field(bytes_field); string_impl.add_field(bytes_field);
// std::core::String::_ctor_0( // std::core::String::_ctor_0(
@ -92,7 +89,7 @@ fn main() {
let mut string_ctor_0_bytecode: Vec<u8> = Vec::new(); let mut string_ctor_0_bytecode: Vec<u8> = Vec::new();
add_store(&mut string_ctor_0_bytecode, 0, 0, 1); add_store(&mut string_ctor_0_bytecode, 0, 0, 1);
let string_ctor_0_fn = DmFn::new( let string_ctor_0_fn = DvmFunction::new(
"std::core::StringImpl::_ctor_0", "std::core::StringImpl::_ctor_0",
"_ctor_0", "_ctor_0",
string_ctor_0_bytecode, string_ctor_0_bytecode,
@ -100,7 +97,7 @@ fn main() {
None, None,
); );
let string_ctor_0_method = DmMethod::new(string_ctor_0_fn, None); let string_ctor_0_method = DvmMethod::new(string_ctor_0_fn, None);
string_impl.add_method(string_ctor_0_method); string_impl.add_method(string_ctor_0_method);
@ -148,7 +145,7 @@ fn main() {
// Dealloc ArrayImpl // Dealloc ArrayImpl
add_dealloc(&mut main_byte_code, 2); add_dealloc(&mut main_byte_code, 2);
let main_dm_fn = DmFn::new("main", "main", main_byte_code, 7, None); let main_dm_fn = DvmFunction::new("main", "main", main_byte_code, 7, None);
greeting_lib.add_function(main_dm_fn); greeting_lib.add_function(main_dm_fn);

131
src/vm/array.rs Normal file
View File

@ -0,0 +1,131 @@
use crate::vm::object::DvmObject;
use crate::vm::pointer::DvmPointer;
use crate::vm::value::DvmValue;
use std::cell::RefCell;
use std::fmt::{Display, Write};
use std::rc::Rc;
use std::slice::Iter;
#[derive(Debug, PartialEq)]
pub enum DvmArray {
Bytes(Vec<u8>),
Ints(Vec<i32>),
Longs(Vec<i64>),
Doubles(Vec<f64>),
USizes(Vec<usize>),
Booleans(Vec<bool>),
Objects(Vec<Rc<RefCell<DvmObject>>>),
Arrays(Vec<Rc<RefCell<DvmArray>>>),
ConstantPointers(Vec<DvmPointer>),
}
impl DvmArray {
pub fn read_element(&self, index: usize) -> DvmValue {
match self {
DvmArray::Bytes(vec) => DvmValue::Byte(vec[index]),
DvmArray::Ints(vec) => DvmValue::Int(vec[index]),
DvmArray::Longs(vec) => DvmValue::Long(vec[index]),
DvmArray::Doubles(vec) => DvmValue::Double(vec[index]),
DvmArray::USizes(vec) => DvmValue::USize(vec[index]),
DvmArray::Booleans(vec) => DvmValue::Boolean(vec[index]),
DvmArray::Objects(vec) => DvmValue::Object(vec[index].clone()),
DvmArray::Arrays(vec) => DvmValue::Array(vec[index].clone()),
DvmArray::ConstantPointers(vec) => DvmValue::ConstantPointer(vec[index].clone()),
}
}
pub fn write_element(&mut self, index: usize, value: DvmValue) {
match self {
DvmArray::Bytes(vec) => {
vec[index] = value.expect_byte();
}
DvmArray::Ints(vec) => {
vec[index] = value.expect_int();
}
DvmArray::Longs(vec) => {
vec[index] = value.expect_long();
}
DvmArray::Doubles(vec) => {
vec[index] = value.expect_double();
}
DvmArray::USizes(vec) => {
vec[index] = value.expect_usize();
}
DvmArray::Booleans(vec) => {
vec[index] = value.expect_boolean();
}
DvmArray::Objects(vec) => {
vec[index] = value.expect_object();
}
DvmArray::Arrays(vec) => {
vec[index] = value.expect_array();
}
DvmArray::ConstantPointers(vec) => {
vec[index] = value.expect_constant_pointer();
}
}
}
}
impl Display for DvmArray {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"[{}]",
self.into_iter()
.map(|v| v.to_string())
.collect::<Vec<_>>()
.join(", ")
)
}
}
pub enum DvmArrayIter<'a> {
Bytes(Iter<'a, u8>),
Ints(Iter<'a, i32>),
Longs(Iter<'a, i64>),
Doubles(Iter<'a, f64>),
USizes(Iter<'a, usize>),
Booleans(Iter<'a, bool>),
Objects(Iter<'a, Rc<RefCell<DvmObject>>>),
Arrays(Iter<'a, Rc<RefCell<DvmArray>>>),
ConstantPointers(Iter<'a, DvmPointer>),
}
impl<'a> Iterator for DvmArrayIter<'a> {
type Item = DvmValue;
fn next(&mut self) -> Option<Self::Item> {
match self {
DvmArrayIter::Bytes(iter) => iter.next().map(|b| DvmValue::Byte(*b)),
DvmArrayIter::Ints(iter) => iter.next().map(|i| DvmValue::Int(*i)),
DvmArrayIter::Longs(iter) => iter.next().map(|l| DvmValue::Long(*l)),
DvmArrayIter::Doubles(iter) => iter.next().map(|d| DvmValue::Double(*d)),
DvmArrayIter::USizes(iter) => iter.next().map(|u| DvmValue::USize(*u)),
DvmArrayIter::Booleans(iter) => iter.next().map(|b| DvmValue::Boolean(*b)),
DvmArrayIter::Objects(iter) => iter.next().map(|o| DvmValue::Object(o.clone())),
DvmArrayIter::Arrays(iter) => iter.next().map(|a| DvmValue::Array(a.clone())),
DvmArrayIter::ConstantPointers(iter) => {
iter.next().map(|p| DvmValue::ConstantPointer(p.clone()))
}
}
}
}
impl<'a> IntoIterator for &'a DvmArray {
type Item = DvmValue;
type IntoIter = DvmArrayIter<'a>;
fn into_iter(self) -> Self::IntoIter {
match self {
DvmArray::Bytes(v) => DvmArrayIter::Bytes(v.iter()),
DvmArray::Ints(v) => DvmArrayIter::Ints(v.iter()),
DvmArray::Longs(v) => DvmArrayIter::Longs(v.iter()),
DvmArray::Doubles(v) => DvmArrayIter::Doubles(v.iter()),
DvmArray::USizes(v) => DvmArrayIter::USizes(v.iter()),
DvmArray::Booleans(v) => DvmArrayIter::Booleans(v.iter()),
DvmArray::Objects(v) => DvmArrayIter::Objects(v.iter()),
DvmArray::Arrays(v) => DvmArrayIter::Arrays(v.iter()),
DvmArray::ConstantPointers(v) => DvmArrayIter::ConstantPointers(v.iter()),
}
}
}

View File

@ -1,10 +0,0 @@
#[derive(Debug, PartialEq, Eq)]
pub enum DmType {
Byte,
Int,
Long,
Double,
Boolean,
Object,
USize,
}

View File

@ -1,66 +0,0 @@
use crate::vm::object::DvmObject;
use std::rc::Rc;
#[derive(Debug, PartialEq, Clone)]
pub enum DvmValue {
Byte(u8),
Int(i32),
Long(i64),
Double(f64),
Boolean(bool),
Object(Rc<DvmObject>),
USize(usize),
Uninit,
Void,
}
impl DvmValue {
pub fn from_object(object: DvmObject) -> Self {
DvmValue::Object(Rc::new(object))
}
pub fn expect_long(&self) -> i64 {
if let DvmValue::Long(l) = self {
*l
} else {
panic!("Expected DvmValue::Long, but found DvmValue::{:?}", self)
}
}
pub fn is_long(&self) -> bool {
match self {
DvmValue::Long(_) => true,
_ => false,
}
}
pub fn expect_object(&self) -> Rc<DvmObject> {
if let DvmValue::Object(o) = self {
o.clone()
} else {
panic!("Expected DvmValue::Object, but found DvmValue::{:?}", self);
}
}
pub fn is_object(&self) -> bool {
match self {
DvmValue::Object(_) => true,
_ => false,
}
}
pub fn expect_usize(&self) -> usize {
if let DvmValue::USize(u) = self {
*u
} else {
panic!("Expected DvmValue::USize, but found DvmValue::{:?}", self);
}
}
pub fn is_usize(&self) -> bool {
match self {
DvmValue::USize(_) => true,
_ => false,
}
}
}

36
src/vm/field.rs Normal file
View File

@ -0,0 +1,36 @@
use crate::vm::r#type::DvmType;
#[derive(Debug, Eq)]
pub struct DvmField {
name: String,
dvm_type: DvmType,
index: usize,
}
impl PartialEq for DvmField {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
}
}
impl DvmField {
pub fn new(name: &str, dvm_type: DvmType, index: usize) -> Self {
DvmField {
name: name.to_string(),
dvm_type,
index,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn dvm_type(&self) -> &DvmType {
&self.dvm_type
}
pub fn index(&self) -> usize {
self.index
}
}

32
src/vm/function.rs Normal file
View File

@ -0,0 +1,32 @@
use crate::vm::instruction::Instruction;
#[derive(Debug)]
pub struct DvmFunction {
fqn: String,
instructions: Vec<Instruction>,
}
impl DvmFunction {
pub fn new(fqn: &str, instructions: &[Instruction]) -> Self {
DvmFunction {
fqn: fqn.to_string(),
instructions: Vec::from(instructions),
}
}
pub fn fqn(&self) -> &str {
self.fqn.as_str()
}
pub fn instructions(&self) -> &Vec<Instruction> {
&self.instructions
}
}
impl PartialEq for DvmFunction {
fn eq(&self, other: &Self) -> bool {
self.fqn == other.fqn
}
}
impl Eq for DvmFunction {}

139
src/vm/instruction.rs Normal file
View File

@ -0,0 +1,139 @@
use std::rc::Rc;
#[derive(Debug, Clone)]
pub enum Instruction {
MoveImmediate {
destination: Location,
immediate: Immediate,
},
Copy {
source: Location,
destination: Location,
},
Push {
source_register: usize,
},
Pop {
destination_register: usize,
},
AllocateObject {
implementation_name: Rc<String>,
destination_register: usize,
},
InvokeStatic {
function_name: Rc<String>,
source_code_location: SourceCodeLocation,
},
InvokeObject {
object_register: usize,
function_name: Rc<String>,
source_code_location: SourceCodeLocation,
},
PlatformCall {
platform_function_name: Rc<String>,
source_code_location: SourceCodeLocation,
},
Add {
left: u8,
right: u8,
destination: u8,
},
Subtract {
left: u8,
right: u8,
destination: u8,
},
Multiply {
left: u8,
right: u8,
destination: u8,
},
Divide {
left: u8,
right: u8,
destination: u8,
},
Modulo {
left: u8,
right: u8,
destination: u8,
},
Power {
left: u8,
right: u8,
destination: u8,
},
Jump {
offset: isize,
},
JumpEqual {
left_register: usize,
right_register: usize,
offset: isize,
},
JumpNotEqual {
left_register: usize,
right_register: usize,
offset: isize,
},
JumpLessThan {
left_register: usize,
right_register: usize,
offset: isize,
},
JumpGreaterThan {
left_register: usize,
right_register: usize,
offset: isize,
},
JumpLessThanEqual {
left_register: usize,
right_register: usize,
offset: isize,
},
JumpGreaterThanEqual {
left_register: usize,
right_register: usize,
offset: isize,
},
Return,
}
#[derive(Debug, Clone)]
pub enum Immediate {
Byte(u8),
Int(i32),
Long(i64),
Double(f64),
Usize(usize),
Boolean(bool),
ConstantPointer {
raw_pointer: *const u8,
length: usize
},
Empty,
}
#[derive(Debug, Clone)]
pub enum Location {
Register(usize),
Stack {
offset: usize,
},
Field {
object_register: usize,
field_name: Rc<String>,
},
Array {
array_register: usize,
index_register: usize,
offset: Option<isize>,
},
}
#[derive(Debug, Clone)]
pub struct SourceCodeLocation {
pub source_file_name: String,
pub line: i64,
pub char: i64,
}

46
src/vm/interface.rs Normal file
View File

@ -0,0 +1,46 @@
use crate::vm::function::DvmFunction;
use crate::vm::virtual_method::DmVirtualMethod;
use std::rc::Rc;
#[derive(Debug, Eq)]
pub struct DmInterface {
fqn: String,
static_functions: Vec<Rc<DvmFunction>>,
virtual_methods: Vec<DmVirtualMethod>,
}
impl PartialEq for DmInterface {
fn eq(&self, other: &Self) -> bool {
self.fqn == other.fqn
}
}
impl DmInterface {
pub fn new(fqn: &str) -> Self {
DmInterface {
fqn: fqn.to_string(),
static_functions: Vec::new(),
virtual_methods: Vec::new(),
}
}
pub fn fqn(&self) -> &str {
self.fqn.as_str()
}
pub fn add_function(&mut self, dm_fn: DvmFunction) {
self.static_functions.push(Rc::new(dm_fn));
}
pub fn functions(&self) -> &Vec<Rc<DvmFunction>> {
&self.static_functions
}
pub fn add_method(&mut self, dm_virtual_method: DmVirtualMethod) {
self.virtual_methods.push(dm_virtual_method);
}
pub fn virtual_methods(&self) -> &Vec<DmVirtualMethod> {
&self.virtual_methods
}
}

View File

@ -3,15 +3,17 @@ mod magic;
mod symbol; mod symbol;
mod write; mod write;
use crate::vm::object_type::{DmFn, DmImplementation, DmInterface}; use crate::vm::function::DvmFunction;
use crate::vm::interface::DmInterface;
use crate::vm::object_type::DvmImplementation;
use std::rc::Rc; use std::rc::Rc;
pub struct DmLib { pub struct DmLib {
name: String, name: String,
constants: Vec<DmConstant>, constants: Vec<DmConstant>,
interfaces: Vec<Rc<DmInterface>>, interfaces: Vec<Rc<DmInterface>>,
implementations: Vec<Rc<DmImplementation>>, implementations: Vec<Rc<DvmImplementation>>,
functions: Vec<Rc<DmFn>>, functions: Vec<Rc<DvmFunction>>,
} }
impl DmLib { impl DmLib {
@ -45,19 +47,19 @@ impl DmLib {
self.interfaces.push(Rc::new(interface)); self.interfaces.push(Rc::new(interface));
} }
pub fn implementations(&self) -> &Vec<Rc<DmImplementation>> { pub fn implementations(&self) -> &Vec<Rc<DvmImplementation>> {
&self.implementations &self.implementations
} }
pub fn add_implementation(&mut self, implementation: DmImplementation) { pub fn add_implementation(&mut self, implementation: DvmImplementation) {
self.implementations.push(Rc::new(implementation)); self.implementations.push(Rc::new(implementation));
} }
pub fn functions(&self) -> &Vec<Rc<DmFn>> { pub fn functions(&self) -> &Vec<Rc<DvmFunction>> {
&self.functions &self.functions
} }
pub fn add_function(&mut self, function: DmFn) { pub fn add_function(&mut self, function: DvmFunction) {
self.functions.push(Rc::new(function)); self.functions.push(Rc::new(function));
} }
} }

View File

@ -1,86 +0,0 @@
use crate::vm::dm_type::DmType;
use crate::vm::dvm_value::DvmValue;
use crate::vm::object::DvmObject;
use std::rc::Rc;
unsafe fn read_field(data_pointer: *const u8, dm_type: &DmType) -> DvmValue {
match dm_type {
DmType::Byte => DvmValue::Byte(data_pointer.read()),
DmType::Int => DvmValue::Int(data_pointer.cast::<i32>().read()),
DmType::Long => DvmValue::Long(data_pointer.cast::<i64>().read()),
DmType::USize => DvmValue::USize(data_pointer.cast::<usize>().read()),
DmType::Double => DvmValue::Double(data_pointer.cast::<f64>().read()),
DmType::Boolean => DvmValue::Boolean(data_pointer.cast::<bool>().read()),
DmType::Object => DvmValue::Object(data_pointer.cast::<Rc<DvmObject>>().read()),
}
}
pub fn read_field_by_name(field_name: &str, self_object: &DvmObject) -> DvmValue {
let (field_index, field) = self_object
.implementation()
.fields()
.iter()
.enumerate()
.find(|(_index, field)| field.name() == field_name)
.expect(&format!(
"Cannot find field {} in {}",
field_name,
self_object.implementation().fqn()
));
let data_pointer = self_object.data()[field_index];
unsafe { read_field(data_pointer, field.dm_type()) }
}
pub fn read_field_by_index(index: usize, self_object: &DvmObject) -> DvmValue {
let field = &self_object.implementation().fields()[index];
let data_pointer = self_object.data()[index];
unsafe { read_field(data_pointer, field.dm_type()) }
}
unsafe fn write_field(data_pointer: *mut u8, value: DvmValue) {
match value {
DvmValue::Byte(b) => {
data_pointer.write(b);
}
DvmValue::Int(i) => {
data_pointer.cast::<i32>().write(i);
}
DvmValue::Long(l) => {
data_pointer.cast::<i64>().write(l);
}
DvmValue::USize(u) => {
data_pointer.cast::<usize>().write(u);
}
DvmValue::Double(d) => {
data_pointer.cast::<f64>().write(d);
}
DvmValue::Boolean(b) => {
data_pointer.cast::<bool>().write(b);
}
DvmValue::Object(o) => {
data_pointer.cast::<Rc<DvmObject>>().write(o);
}
DvmValue::Uninit => panic!("Cannot write DvmValue::Uninit to field."),
DvmValue::Void => panic!("Cannot write DvmValue::Void to field."),
}
}
pub fn write_field_by_name(field_name: &str, self_object: &DvmObject, value: DvmValue) {
let field_index = self_object
.implementation()
.fields()
.iter()
.position(|field| field.name() == field_name)
.expect(&format!(
"Cannot find field {} in {}",
field_name,
self_object.implementation().fqn()
));
let data_pointer = self_object.data()[field_index];
unsafe { write_field(data_pointer, value) };
}
pub fn write_field_by_index(index: usize, self_object: &DvmObject, value: DvmValue) {
let data_pointer = self_object.data()[index];
unsafe { write_field(data_pointer, value) };
}

32
src/vm/method.rs Normal file
View File

@ -0,0 +1,32 @@
use crate::vm::function::DvmFunction;
use crate::vm::virtual_method::DmVirtualMethod;
use std::rc::Rc;
#[derive(Debug, Eq)]
pub struct DvmMethod {
function: Rc<DvmFunction>,
implements: Option<Rc<DmVirtualMethod>>,
}
impl PartialEq for DvmMethod {
fn eq(&self, other: &Self) -> bool {
self.function == other.function
}
}
impl DvmMethod {
pub fn new(dm_fn: DvmFunction, implements: Option<Rc<DmVirtualMethod>>) -> Self {
DvmMethod {
function: Rc::new(dm_fn),
implements,
}
}
pub fn fqn(&self) -> &str {
self.function.fqn()
}
pub fn dm_fn(&self) -> Rc<DvmFunction> {
self.function.clone()
}
}

View File

@ -1,556 +1,350 @@
pub mod dm_type; mod array;
pub mod dvm_value; mod field;
mod function;
mod instruction;
mod interface;
pub mod lib; pub mod lib;
pub mod mem; mod method;
pub mod object; pub mod object;
pub mod object_type; pub mod object_type;
pub mod op_codes; pub mod op_codes;
pub mod platform; pub mod platform;
mod pointer;
pub mod r#type;
pub mod util; pub mod util;
pub mod value;
mod virtual_method;
use crate::vm::dvm_value::DvmValue; use crate::vm::instruction::{Immediate, Instruction, Location, SourceCodeLocation};
use crate::vm::lib::{DmConstant, DmLib};
use crate::vm::mem::{read_field_by_index, write_field_by_index};
use crate::vm::object::DvmObject; use crate::vm::object::DvmObject;
use crate::vm::object_type::DmFn; use crate::vm::object_type::DvmImplementation;
use op_codes::*; use crate::vm::pointer::DvmPointer;
use std::alloc::{alloc, dealloc, Layout}; use crate::vm::value::DvmValue;
use function::DvmFunction;
use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Display;
use std::rc::Rc; use std::rc::Rc;
pub type PlatformFunction = fn dvm_panic(context: &DvmContext, message: &str) {
fn(args: Vec<DvmValue>, state: &mut DvmState, context: &DvmContext) -> DvmValue; println!("----");
println!("Deimos Virtual Machine: Panic!");
#[derive(Debug)] println!("{}", message);
enum CallFrame { println!("----");
PlatformCall(CallFrameInfo), println!("Current Instruction: {:?}", context.current_instruction);
DeimosCall(CallFrameInfo), println!("Registers:");
for (i, register) in context.registers.iter().enumerate() {
println!("\tr{}: {:?}", i, register);
}
println!("Call stack:");
for call in context.call_stack.iter() {
println!("\t{}", call);
}
println!("Stack:");
for (i, value) in context.stack.iter().enumerate() {
println!("\t{}: {}", i, value);
}
println!("----");
} }
#[derive(Debug)] macro_rules! dvm_panic {
struct CallFrameInfo { ($context: expr, $message: expr) => {
pub fqn: String, dvm_panic($context, $message);
panic!()
};
} }
pub type PlatformFunction = fn(context: &mut DvmContext);
pub struct DvmContext { pub struct DvmContext {
libs: Vec<Rc<DmLib>>, call_stack: Vec<DvmCall>,
function_cache: HashMap<String, Rc<DmFn>>, current_instruction: Option<Instruction>,
registers: Vec<DvmValue>,
stack: Vec<DvmValue>,
implementations: HashMap<String, Rc<DvmImplementation>>,
static_functions: HashMap<String, Rc<DvmFunction>>,
platform_functions: HashMap<String, PlatformFunction>, platform_functions: HashMap<String, PlatformFunction>,
} }
impl DvmContext { impl DvmContext {
pub fn new() -> Self { pub fn new() -> Self {
DvmContext { DvmContext {
libs: Vec::new(), call_stack: vec![],
function_cache: HashMap::new(), current_instruction: None,
registers: vec![],
stack: vec![],
implementations: HashMap::new(),
static_functions: HashMap::new(),
platform_functions: HashMap::new(), platform_functions: HashMap::new(),
} }
} }
pub fn load_libs(&mut self, libs: Vec<Rc<DmLib>>) { pub fn pop_stack(&mut self) -> DvmValue {
for lib in libs { self.stack.pop().unwrap_or_else(|| {
for lib_fn in lib.functions() { dvm_panic!(self, "Stack underflow!");
self.function_cache })
.insert(lib_fn.fqn().to_string(), lib_fn.clone());
}
for interface_function in lib
.interfaces()
.iter()
.flat_map(|interface| interface.functions())
{
self.function_cache.insert(
interface_function.fqn().to_string(),
interface_function.clone(),
);
}
for implementation_function in lib
.implementations()
.iter()
.flat_map(|implementation| implementation.functions())
{
self.function_cache.insert(
implementation_function.fqn().to_string(),
implementation_function.clone(),
);
}
for method in lib
.implementations()
.iter()
.flat_map(|implementation| implementation.methods())
{
self.function_cache
.insert(method.dm_fn().fqn().to_string(), method.dm_fn());
}
self.libs.push(lib);
}
}
pub fn load_platform_fns(&mut self, platform_fns: HashMap<String, PlatformFunction>) {
platform_fns.iter().for_each(|(fqn, platform_function)| {
self.platform_functions
.insert(fqn.to_string(), *platform_function);
});
}
pub fn fn_by_fqn(&self, fqn: &str) -> Option<Rc<DmFn>> {
self.function_cache.get(fqn).cloned()
} }
} }
pub struct DvmState { pub struct DvmCall {
call_stack: Vec<CallFrame>, function_name: Rc<String>,
registers: Vec<DvmValue>, source_code_location: SourceCodeLocation,
register_state_stack: Vec<Vec<DvmValue>>,
} }
impl DvmState { impl Display for DvmCall {
pub fn new() -> Self { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
DvmState { f.write_fmt(format_args!(
call_stack: Vec::new(), "{} in {} @ {}:{}",
registers: Vec::new(), self.function_name,
register_state_stack: Vec::new(), self.source_code_location.source_file_name,
} self.source_code_location.line,
self.source_code_location.char
))
} }
} }
macro_rules! dump_state { fn update_index(index: &mut usize, offset: isize) {
( $state: expr ) => { if offset.is_negative() {
println!("Registers: "); *index -= offset.abs() as usize;
for (i, register) in $state.registers.iter().enumerate() {
println!(" r{}: {:?}", i, register);
}
println!("Call stack:");
for call_frame in &$state.call_stack {
match call_frame {
CallFrame::PlatformCall(frame_info) => {
println!(" {}", frame_info.fqn);
}
CallFrame::DeimosCall(frame_info) => {
println!(" {}", frame_info.fqn);
}
}
}
};
}
macro_rules! dvm_panic {
( $message: expr, $state: expr ) => {{
println!("----");
println!("Dvm panic: {}", $message);
println!("----");
dump_state!($state);
panic!();
}};
}
pub fn call_fn(
state: &mut DvmState,
context: &DvmContext,
dm_fn: &DmFn,
args: Vec<DvmValue>,
) -> DvmValue {
// save current state
state.call_stack.push(CallFrame::DeimosCall(CallFrameInfo {
fqn: dm_fn.fqn().to_string(),
}));
state.register_state_stack.push(state.registers.clone());
// zero registers and make sure there are enough for dm_function
state.registers.clear();
if state.registers.len() < dm_fn.number_used_registers() {
state
.registers
.resize(dm_fn.number_used_registers(), DvmValue::Uninit);
}
// push args
for (i, arg) in args.iter().enumerate() {
state.registers[i] = arg.clone();
}
// run the byte code
run_byte_code(state, context, &dm_fn.byte_code());
// restore state
state.registers = state.register_state_stack.pop().unwrap();
state.call_stack.pop();
// get return value
if let Some(return_register) = dm_fn.return_register() {
state
.registers
.get(return_register as usize)
.map(|m| m.clone())
.unwrap()
} else { } else {
DvmValue::Void *index += offset as usize;
} }
} }
macro_rules! next_8 { fn push_call_frame(
( $byte_code: expr, $T: ident ) => { function_name: Rc<String>,
$byte_code.next().unwrap() as $T source_code_location: SourceCodeLocation,
}; context: &mut DvmContext,
) {
context.call_stack.push(DvmCall {
function_name,
source_code_location,
});
} }
macro_rules! next_32_le { fn pop_call_frame(context: &mut DvmContext) {
( $byte_code: expr, $T: ident ) => { context.call_stack.pop();
next_8!($byte_code, $T)
+ (next_8!($byte_code, $T) << 8)
+ (next_8!($byte_code, $T) << 16)
+ (next_8!($byte_code, $T) << 24)
};
} }
macro_rules! next_64_le { fn compute_index_with_offset(
( $byte_code: expr, $T: ident ) => { registers: &Vec<DvmValue>,
next_8!($byte_code, $T) index_register: usize,
+ (next_8!($byte_code, $T) << 8) offset: &Option<isize>,
+ (next_8!($byte_code, $T) << 16) ) -> usize {
+ (next_8!($byte_code, $T) << 24) let index = registers[index_register].as_usize().expect(&format!(
+ (next_8!($byte_code, $T) << 32) "Could not convert the value of index_register {} to usize; found {:?}",
+ (next_8!($byte_code, $T) << 40) index_register, registers[index_register]
+ (next_8!($byte_code, $T) << 48) ));
+ (next_8!($byte_code, $T) << 56) if let Some(o) = offset {
}; if o.is_negative() {
} index - o.unsigned_abs()
} else {
macro_rules! next_usize_le { index + o.unsigned_abs()
( $byte_code: expr ) => {{
let mut u: usize = 0;
for i in 0..std::mem::size_of::<usize>() {
u += next_8!($byte_code, usize) << (8 * i);
} }
u } else {
}}; index
}
} }
macro_rules! read_array_u8 { fn copy_from_source(context: &DvmContext, source: &Location) -> DvmValue {
( $iter: expr ) => {{ match source {
let arr_length = next_8!($iter, u8); Location::Register(register) => context.registers[*register].clone(),
let mut arr = Vec::with_capacity(arr_length as usize); Location::Stack { offset } => context.stack[*offset].clone(),
let mut i = 0; Location::Field {
while i < arr_length { object_register,
arr.push(next_8!($iter, u8)); field_name,
i += 1; } => context.registers[*object_register].map_object(
|o| o.borrow().read_field(field_name).clone(),
|v| {
dvm_panic!(context, &format!("Expected an object, but found: {}", v));
},
),
Location::Array {
array_register,
index_register,
offset,
} => {
let index = compute_index_with_offset(&context.registers, *index_register, offset);
context.registers[*array_register].map_array(
|a| a.borrow().read_element(index),
|v| {
dvm_panic!(context, &format!("Expected an array, but found: {}", v));
},
)
} }
arr }
}};
} }
macro_rules! read_string { fn write_to_destination(context: &mut DvmContext, destination: &Location, value: DvmValue) {
( $iter: expr ) => {{ match destination {
let str_length = next_32_le!($iter, usize); Location::Register(register) => {
let mut str_raw: Vec<u8> = Vec::new(); context.registers[*register] = value;
let mut i = 0;
while i < str_length {
str_raw.push($iter.next().unwrap());
i += 1;
} }
String::from_utf8(str_raw).unwrap() Location::Stack { offset } => {
}}; context.stack[*offset] = value;
}
Location::Field {
object_register,
field_name,
} => {
context.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(&context.registers, *index_register, offset);
context.registers[*array_register]
.expect_array()
.borrow_mut()
.write_element(index, value);
}
}
} }
pub fn run_byte_code(state: &mut DvmState, context: &DvmContext, byte_code: &[u8]) { fn immediate_to_value(immediate: &Immediate) -> DvmValue {
let mut iter = byte_code.iter().cloned(); match immediate {
while let Some(op_code) = iter.next() { Immediate::Byte(b) => DvmValue::Byte(*b),
match op_code { Immediate::Int(i) => DvmValue::Int(*i),
MOV_BYTE => { Immediate::Long(l) => DvmValue::Long(*l),
todo!() Immediate::Double(d) => DvmValue::Double(*d),
} Immediate::Usize(s) => DvmValue::USize(*s),
MOV_INT => { Immediate::Boolean(b) => DvmValue::Boolean(*b),
let target_register = next_8!(iter, usize); Immediate::ConstantPointer {
let operand = next_32_le!(iter, u32) as i32; raw_pointer,
state.registers[target_register] = DvmValue::Int(operand); length,
} } => DvmValue::ConstantPointer(DvmPointer {
MOV_LONG => { raw_pointer: *raw_pointer,
let target_register = next_8!(iter, usize); length: *length,
let operand = next_64_le!(iter, u64) as i64; }),
state.registers[target_register] = DvmValue::Long(operand); Immediate::Empty => DvmValue::Empty,
} }
MOV_DOUBLE => { }
let target_register = next_8!(iter, usize);
let operand = next_64_le!(iter, u64) as f64;
state.registers[target_register] = DvmValue::Double(operand);
}
MOV_REGISTER => {
let target_register = next_8!(iter, usize);
let source_register = next_8!(iter, usize);
let source_value =
std::mem::replace(&mut state.registers[source_register], DvmValue::Uninit);
state.registers[target_register] = source_value;
}
LOAD => {
let target_register = next_8!(iter, usize);
let source_register = next_8!(iter, usize);
let source_object_field_index = next_usize_le!(iter);
let source_object = state // TODO: find all possible unwraps/expects and wrap with call to dvm_panic
.registers pub fn run(instructions: &[Instruction], context: &mut DvmContext) {
.get(source_register) let mut index = 0;
.unwrap() while index < instructions.len() {
.expect_object(); context.current_instruction = Some(instructions[index].clone());
index += 1;
state.registers[target_register] = match &instructions[index] {
read_field_by_index(source_object_field_index, &source_object); Instruction::MoveImmediate {
destination,
immediate,
} => {
write_to_destination(context, destination, immediate_to_value(immediate));
} }
STORE => { Instruction::Copy {
let target_register = next_8!(iter, usize); source,
let target_object_field_index = next_usize_le!(iter); destination,
let source_register = next_8!(iter, usize); } => {
let value = copy_from_source(context, source);
let target_object = state write_to_destination(context, destination, value);
.registers
.get(target_register)
.unwrap()
.expect_object();
let source_value =
std::mem::replace(&mut state.registers[source_register], DvmValue::Uninit);
write_field_by_index(target_object_field_index, &target_object, source_value);
} }
ALLOC => { Instruction::Push { source_register } => {
let target_register = next_8!(iter, usize); context
let impl_name = read_string!(iter); .stack
.push(context.registers[*source_register].clone());
}
Instruction::Pop {
destination_register,
} => {
let value = context.stack.pop().expect("Stack underflow");
context.registers[*destination_register] = value;
}
Instruction::AllocateObject {
implementation_name,
destination_register,
} => {
let implementation = context let implementation = context
.libs .implementations
.iter() .get(implementation_name.as_str())
.find_map(|lib| { .unwrap();
lib.implementations() let object = DvmObject::new(implementation.clone());
.iter() context.registers[*destination_register] =
.find(|implementation| implementation.fqn() == impl_name) DvmValue::Object(Rc::new(RefCell::new(object)));
})
.unwrap_or_else(|| {
dvm_panic!(&format!("Implementation not found: {}", impl_name), state)
})
.clone();
let dm_alloc_object = DvmObject::new(implementation);
state.registers[target_register] = DvmValue::from_object(dm_alloc_object);
} }
DEALLOC => { Instruction::InvokeStatic {
let target_register = next_8!(iter, usize); function_name,
let target_object = source_code_location,
std::mem::replace(&mut state.registers[target_register], DvmValue::Uninit) } => {
.expect_object(); let static_function = context
drop(target_object); // explicit .static_functions
state.registers[target_register] = DvmValue::Uninit; .get(function_name.as_str())
}
MOV_CONST => {
// Decode
let address_register = next_8!(iter, usize);
let size_register = next_8!(iter, usize);
let lib_name = read_string!(iter);
let const_id = next_32_le!(iter, usize);
// Get constant
let Some(lib) = context.libs.iter().find(|lib| lib.name() == lib_name) else {
dvm_panic!(
&format!("Could not find lib with name: {}", lib_name),
state
);
};
let constant = lib.constants().get(const_id).unwrap();
let DmConstant::String(s) = constant;
let pointer = s.as_ptr();
// Store the pointer metadata in target registers.
state.registers[address_register] = DvmValue::USize(pointer as usize);
state.registers[size_register] = DvmValue::USize(s.len());
}
ALLOC_RAW => {
// Allocates a raw number of bytes, with the number of bytes determined by the value
// of the source register.
let address_register = next_8!(iter, usize);
let source_register = next_8!(iter, usize);
let size = state.registers.get(source_register).unwrap().expect_long() as usize;
let layout = Layout::from_size_align(size, 1).unwrap();
let raw_ptr = unsafe { alloc(layout) };
state.registers[address_register] = DvmValue::USize(raw_ptr as usize);
}
DEALLOC_RAW => {
// Deallocates a DvmValue::Pointer(DvmPointer) in the target register.
let address_register = next_8!(iter, usize);
let size_register = next_8!(iter, usize);
let address = state
.registers
.get(address_register)
.unwrap() .unwrap()
.expect_usize(); .clone();
let size = state.registers.get(size_register).unwrap().expect_usize(); push_call_frame(function_name.clone(), source_code_location.clone(), context);
let layout = Layout::from_size_align(size, 1).unwrap(); run(static_function.instructions(), context);
unsafe { pop_call_frame(context);
dealloc(address as *mut u8, layout);
}
state.registers[address_register] = DvmValue::Uninit;
state.registers[size_register] = DvmValue::Uninit;
} }
PLATFORM_CALL => { Instruction::InvokeObject {
// Calls a platform function. The result of the platform call is stored in the object_register,
// specified return register. function_name,
let symbol_name = read_string!(iter); source_code_location,
let return_register = next_8!(iter, usize); } => {
let arg_registers = read_array_u8!(iter); let object = context.registers[*object_register].expect_object();
let object_ref = object.borrow();
let mut args = Vec::with_capacity(arg_registers.len()); let method = object_ref
for arg_register in arg_registers { .get_method(function_name.as_str())
let value = state.registers.get(arg_register as usize).unwrap(); .expect(&format!(
args.push(value.clone()); "Cannot find method {} for object {:?}",
} function_name, object
));
let platform_function = context.platform_functions.get(&symbol_name).unwrap(); let function = method.dm_fn();
state let instructions = function.instructions();
.call_stack push_call_frame(function_name.clone(), source_code_location.clone(), context);
.push(CallFrame::PlatformCall(CallFrameInfo { fqn: symbol_name })); run(instructions, context);
let call_result = platform_function(args, state, context); pop_call_frame(context);
state.registers[return_register] = call_result;
state.call_stack.pop();
} }
INVOKE_FN => { Instruction::PlatformCall {
let symbol_name = read_string!(iter); platform_function_name,
let return_register = next_8!(iter, usize); source_code_location,
let arg_registers = read_array_u8!(iter); } => {
let platform_function = context
let mut args = Vec::with_capacity(arg_registers.len()); .platform_functions
for arg_register in arg_registers { .get(platform_function_name.as_str())
let value = state.registers.get(arg_register as usize).unwrap(); .unwrap()
args.push(value.clone()); .clone();
} push_call_frame(
platform_function_name.clone(),
let dm_fn = context.function_cache.get(&symbol_name).unwrap(); source_code_location.clone(),
let call_result = call_fn(state, context, dm_fn, args); context,
state.registers[return_register] = call_result;
}
INVOKE_VIRTUAL => {
let symbol_name = read_string!(iter);
let return_register = next_8!(iter, usize);
let arg_registers = read_array_u8!(iter);
let mut args = Vec::with_capacity(arg_registers.len());
for arg_register in arg_registers {
let value = state.registers.get(arg_register as usize).unwrap();
args.push(value.clone());
}
let self_obj = args.get(0).unwrap().expect_object();
let method = self_obj
.implementation()
.find_method(&symbol_name, &self_obj)
.unwrap_or_else(|| {
dvm_panic!(
&format!("Could not find method with name: {}", symbol_name),
state
);
});
let dm_fn = method.dm_fn();
let call_result = call_fn(state, context, &dm_fn, args);
state.registers[return_register] = call_result;
}
INVOKE_DYNAMIC => {
unimplemented!("INVOKE_DYNAMIC is not yet supported and may never be.")
}
MULTIPLY => {
todo!()
}
op_code => {
dvm_panic!(
&format!("Unimplemented or unknown op code: {}", op_code),
state
); );
platform_function(context);
pop_call_frame(context);
}
Instruction::Add { .. } => {}
Instruction::Subtract { .. } => {}
Instruction::Multiply { .. } => {}
Instruction::Divide { .. } => {}
Instruction::Modulo { .. } => {}
Instruction::Power { .. } => {}
Instruction::Jump { offset } => {
update_index(&mut index, *offset);
}
Instruction::JumpEqual {
left_register,
right_register,
offset,
} => {
let left_value = &context.registers[*left_register];
let right_value = &context.registers[*right_register];
if left_value == right_value {
update_index(&mut index, *offset);
}
}
Instruction::JumpNotEqual { .. } => {}
Instruction::JumpLessThan { .. } => {}
Instruction::JumpGreaterThan { .. } => {}
Instruction::JumpLessThanEqual { .. } => {}
Instruction::JumpGreaterThanEqual { .. } => {}
Instruction::Return => {
break;
} }
} }
} }
} }
#[cfg(test)]
mod run_code_tests {
use super::*;
use crate::vm::dm_type::DmType;
use crate::vm::object_type::{DmField, DmImplementation};
macro_rules! assert_register {
( $expected: expr, $state: expr, $register_number: expr ) => {
assert_eq!($expected, $state.registers.remove($register_number));
};
}
fn setup<'a>(number_of_registers: usize) -> (DvmState, DvmContext) {
let mut state = DvmState::new();
state
.registers
.resize(number_of_registers, DvmValue::Uninit);
(state, DvmContext::new())
}
fn impl_with_object_field() -> DmImplementation {
let mut dm_impl = DmImplementation::new("ImplWithObjectField", "ImplWithObjectField", None);
let object_field = DmField::new("object_field", DmType::Object);
dm_impl.add_field(object_field);
dm_impl
}
#[test]
fn mov_1_as_int() {
let mut code = Vec::new();
add_mov_int(&mut code, 0, 1);
let (mut state, context) = setup(1);
run_byte_code(&mut state, &context, &code);
assert_register!(DvmValue::Int(1), state, 0);
}
#[test]
fn move_65535_as_int() {
let mut code = Vec::new();
add_mov_int(&mut code, 0, 0xffff);
let (mut state, context) = setup(1);
run_byte_code(&mut state, &context, &code);
assert_register!(DvmValue::Int(0xffff), state, 0);
}
#[test]
fn move_int_max_as_int() {
let mut code = Vec::new();
add_mov_int(&mut code, 0, 0x0fff_ffff);
let (mut state, context) = setup(1);
run_byte_code(&mut state, &context, &code);
assert_register!(DvmValue::Int(0x0fff_ffff), state, 0);
}
#[test]
fn move_register() {
let mut code = Vec::new();
add_mov_int(&mut code, 1, 1);
add_mov_register(&mut code, 0, 1);
let (mut state, context) = setup(2);
run_byte_code(&mut state, &context, &code);
assert_register!(DvmValue::Int(1), state, 0);
}
#[test]
fn load_object() {
let dummy_impl = impl_with_object_field();
let mut dummy_lib = DmLib::new("dummy");
dummy_lib.add_implementation(dummy_impl);
let dummy_impl_ref = dummy_lib
.implementations()
.iter()
.find(|implementation| implementation.fqn() == "dummy")
.unwrap();
let mut code = Vec::new();
add_alloc(&mut code, 0, dummy_impl_ref.fqn());
add_alloc(&mut code, 1, dummy_impl_ref.fqn());
add_store(&mut code, 0, 0, 1);
add_load(&mut code, 2, 0, 0);
let (mut state, mut context) = setup(3);
state.registers.resize(3, DvmValue::Uninit);
context.load_libs(vec![Rc::new(dummy_lib)]);
run_byte_code(&mut state, &context, &code);
assert!(state.registers[2].is_object())
}
}

View File

@ -1,52 +1,52 @@
use crate::vm::dm_type::DmType; use crate::vm::method::DvmMethod;
use crate::vm::object_type::DmImplementation; use crate::vm::object_type::DvmImplementation;
use std::alloc::{alloc, dealloc, Layout}; use crate::vm::value::DvmValue;
use std::ptr;
use std::rc::Rc; use std::rc::Rc;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq)]
pub struct DvmObject { pub struct DvmObject {
data: Vec<*mut u8>, data: Vec<DvmValue>,
implementation: Rc<DmImplementation>, implementation: Rc<DvmImplementation>,
}
fn layout_for(dm_type: &DmType) -> Layout {
match dm_type {
DmType::Byte => Layout::new::<u8>(),
DmType::Int => Layout::new::<i32>(),
DmType::Long => Layout::new::<i64>(),
DmType::USize => Layout::new::<usize>(),
DmType::Double => Layout::new::<f64>(),
DmType::Boolean => Layout::new::<bool>(),
DmType::Object => Layout::new::<Rc<DvmObject>>(),
}
} }
impl DvmObject { impl DvmObject {
pub fn new(implementation: Rc<DmImplementation>) -> Self { pub fn new(implementation: Rc<DvmImplementation>) -> Self {
let mut data = vec![ptr::null_mut(); implementation.fields().len()];
for (index, field) in implementation.fields().iter().enumerate() {
data[index] = unsafe { alloc(layout_for(field.dm_type())) }
}
DvmObject { DvmObject {
data, data: vec![DvmValue::Empty; implementation.field_count()],
implementation, implementation,
} }
} }
pub fn data(&self) -> &[*mut u8] { fn get_field_index(&self, field_name: &str) -> usize {
self.implementation
.find_field(field_name, self)
.unwrap()
.index()
}
pub fn data(&self) -> &[DvmValue] {
&self.data &self.data
} }
pub fn implementation(&self) -> &DmImplementation { pub fn implementation(&self) -> &DvmImplementation {
&self.implementation &self.implementation
} }
}
impl Drop for DvmObject { pub fn read_field(&self, field_name: &str) -> &DvmValue {
fn drop(&mut self) { &self.data[self.get_field_index(field_name)]
for (index, field) in self.implementation.fields().iter().enumerate() { }
unsafe { dealloc(self.data[index], layout_for(field.dm_type())) }
} pub fn take_field(&mut self, field_name: &str) -> DvmValue {
let field_index = self.get_field_index(field_name);
std::mem::take(&mut self.data[field_index])
}
pub fn write_field(&mut self, field_name: &str, value: DvmValue) {
let field_index = self.get_field_index(field_name);
self.data[field_index] = value;
}
pub fn get_method(&self, method_name: &str) -> Option<Rc<DvmMethod>> {
self.implementation.find_method(method_name, self)
} }
} }

View File

@ -1,189 +1,35 @@
use crate::vm::dm_type::DmType; use crate::vm::field::DvmField;
use crate::vm::function::DvmFunction;
use crate::vm::interface::DmInterface;
use crate::vm::method::DvmMethod;
use crate::vm::object::DvmObject; use crate::vm::object::DvmObject;
use std::collections::HashMap;
use std::fmt::Debug; use std::fmt::Debug;
use std::rc::Rc; use std::rc::Rc;
#[derive(Debug, Eq)] #[derive(Debug, Eq)]
pub struct DmFn { pub struct DvmImplementation {
fqn: String, fqn: String,
short_name: String,
byte_code: Vec<u8>,
number_used_registers: usize,
return_register: Option<u8>,
}
impl DmFn {
pub fn new(
fqn: &str,
short_name: &str,
byte_code: Vec<u8>,
number_used_registers: usize,
return_register: Option<u8>,
) -> Self {
DmFn {
fqn: fqn.to_string(),
short_name: short_name.to_string(),
byte_code,
number_used_registers,
return_register,
}
}
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<u8> {
&self.byte_code
}
pub fn number_used_registers(&self) -> usize {
self.number_used_registers
}
pub fn return_register(&self) -> Option<u8> {
self.return_register
}
}
impl PartialEq for DmFn {
fn eq(&self, other: &Self) -> bool {
self.fqn == other.fqn
}
}
#[derive(Debug, Eq)]
pub struct DmMethod {
dm_fn: Rc<DmFn>,
implements: Option<Rc<DmVirtualMethod>>,
}
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<Rc<DmVirtualMethod>>) -> Self {
DmMethod {
dm_fn: Rc::new(dm_fn),
implements,
}
}
pub fn dm_fn(&self) -> Rc<DmFn> {
self.dm_fn.clone()
}
}
#[derive(Debug, Eq)]
pub struct DmInterface {
fqn: String,
short_name: String,
functions: Vec<Rc<DmFn>>,
virtual_methods: Vec<DmVirtualMethod>,
}
impl PartialEq for DmInterface {
fn eq(&self, other: &Self) -> bool {
self.fqn == other.fqn
}
}
impl DmInterface {
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 functions(&self) -> &Vec<Rc<DmFn>> {
&self.functions
}
pub fn add_method(&mut self, dm_virtual_method: DmVirtualMethod) {
self.virtual_methods.push(dm_virtual_method);
}
pub fn virtual_methods(&self) -> &Vec<DmVirtualMethod> {
&self.virtual_methods
}
}
#[derive(Debug, Eq)]
pub struct DmVirtualMethod {
fqn: String,
short_name: String,
}
impl PartialEq for DmVirtualMethod {
fn eq(&self, other: &Self) -> bool {
self.fqn == other.fqn
}
}
impl DmVirtualMethod {
pub fn new(fqn: &str, short_name: &str) -> Self {
DmVirtualMethod {
fqn: fqn.to_string(),
short_name: short_name.to_string(),
}
}
pub fn fqn(&self) -> &str {
self.fqn.as_str()
}
pub fn short_name(&self) -> &str {
self.short_name.as_str()
}
}
#[derive(Debug, Eq)]
pub struct DmImplementation {
fqn: String,
short_name: String,
interface: Option<Rc<DmInterface>>, interface: Option<Rc<DmInterface>>,
functions: Vec<Rc<DmFn>>, static_functions: HashMap<String, Rc<DvmFunction>>,
methods: Vec<Rc<DmMethod>>, methods: HashMap<String, Rc<DvmMethod>>,
fields: Vec<DmField>, fields: HashMap<String, Rc<DvmField>>,
} }
impl PartialEq for DmImplementation { impl PartialEq for DvmImplementation {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.fqn == other.fqn self.fqn == other.fqn
} }
} }
impl DmImplementation { impl DvmImplementation {
pub fn new(fqn: &str, short_name: &str, interface: Option<Rc<DmInterface>>) -> Self { pub fn new(fqn: &str, interface: Option<Rc<DmInterface>>) -> Self {
DmImplementation { DvmImplementation {
fqn: fqn.to_string(), fqn: fqn.to_string(),
short_name: short_name.to_string(),
interface, interface,
functions: Vec::new(), static_functions: HashMap::new(),
methods: Vec::new(), methods: HashMap::new(),
fields: Vec::new(), fields: HashMap::new(),
} }
} }
@ -191,81 +37,30 @@ impl DmImplementation {
self.fqn.as_str() self.fqn.as_str()
} }
pub fn short_name(&self) -> &str { pub fn add_function(&mut self, function: DvmFunction) {
self.short_name.as_str() self.static_functions
.insert(function.fqn().to_string(), Rc::new(function));
} }
pub fn interface(&self) -> Option<Rc<DmInterface>> { pub fn add_method(&mut self, dvm_method: DvmMethod) {
self.interface.clone() self.methods
.insert(dvm_method.fqn().to_string(), Rc::new(dvm_method));
} }
pub fn functions(&self) -> &Vec<Rc<DmFn>> { pub fn find_method(&self, name: &str, self_object: &DvmObject) -> Option<Rc<DvmMethod>> {
&self.functions self.methods.get(name).cloned()
} }
pub fn add_function(&mut self, dm_fn: DmFn) { pub fn add_field(&mut self, dm_field: DvmField) {
self.functions.push(Rc::new(dm_fn)); self.fields
.insert(dm_field.name().to_string(), Rc::new(dm_field));
} }
pub fn methods(&self) -> &Vec<Rc<DmMethod>> { pub fn find_field(&self, name: &str, self_object: &DvmObject) -> Option<Rc<DvmField>> {
&self.methods self.fields.get(name).cloned()
} }
pub fn add_method(&mut self, dm_method: DmMethod) { pub fn field_count(&self) -> usize {
self.methods.push(Rc::new(dm_method)); self.fields.len()
}
pub fn find_method(&self, name: &str, self_object: &DvmObject) -> Option<&DmMethod> {
for method in &self.methods {
if method.dm_fn.fqn == name {
return Some(method);
} else if let Some(implements) = &method.implements {
if implements.fqn == name {
return Some(method);
}
}
}
None
}
pub fn fields(&self) -> &Vec<DmField> {
&self.fields
}
pub fn add_field(&mut self, dm_field: DmField) {
self.fields.push(dm_field);
}
pub fn find_field(&self, name: &str, self_object: &DvmObject) -> Option<&DmField> {
self.fields.iter().find(|field| field.name == name)
}
}
#[derive(Debug, Eq)]
pub struct DmField {
name: String,
dm_type: DmType,
}
impl PartialEq for DmField {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
}
}
impl DmField {
pub fn new(name: &str, dm_type: DmType) -> Self {
DmField {
name: name.to_string(),
dm_type,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn dm_type(&self) -> &DmType {
&self.dm_type
} }
} }

View File

@ -1,26 +1,22 @@
use crate::vm::dvm_value::DvmValue;
use crate::vm::mem::read_field_by_name;
use crate::vm::object::DvmObject; use crate::vm::object::DvmObject;
use crate::vm::{DvmContext, DvmState}; use crate::vm::value::DvmValue;
use crate::vm::DvmContext;
use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
pub fn dm_print(args: Vec<DvmValue>, _state: &mut DvmState, _context: &DvmContext) -> DvmValue { /// ```deimos
if args.len() != 1 { /// // Signature
return DvmValue::Void; // TODO: make exception /// print(m: &(Byte | Int | Long | Double | USize | Boolean | Object | Empty))
} /// ```
print!("{}", get_string(&args[0])); pub fn dm_print(context: &mut DvmContext) {
DvmValue::Void print!("{}", get_string(context.pop_stack()));
} }
pub fn dm_println(args: Vec<DvmValue>, _state: &mut DvmState, _context: &DvmContext) -> DvmValue { pub fn dm_println(context: &mut DvmContext) {
if args.len() != 1 { println!("{}", get_string(context.pop_stack()));
return DvmValue::Uninit;
}
println!("{}", get_string(&args[0]));
DvmValue::Void
} }
fn get_string(dvm_value: &DvmValue) -> String { fn get_string(dvm_value: DvmValue) -> String {
match dvm_value { match dvm_value {
DvmValue::Byte(b) => b.to_string(), DvmValue::Byte(b) => b.to_string(),
DvmValue::Int(i) => i.to_string(), DvmValue::Int(i) => i.to_string(),
@ -29,32 +25,30 @@ fn get_string(dvm_value: &DvmValue) -> String {
DvmValue::Boolean(b) => b.to_string(), DvmValue::Boolean(b) => b.to_string(),
DvmValue::Object(object) => object_to_string(object.clone()), DvmValue::Object(object) => object_to_string(object.clone()),
DvmValue::USize(u) => u.to_string(), DvmValue::USize(u) => u.to_string(),
DvmValue::Uninit => String::from("Uninit"), DvmValue::Array(a) => a.borrow().to_string(),
DvmValue::Void => String::from("Void"), DvmValue::ConstantPointer(p) => p.to_string(),
DvmValue::Empty => String::from("Empty"),
} }
} }
fn object_to_string(alloc_object_rc: Rc<DvmObject>) -> String { fn object_to_string(object: Rc<RefCell<DvmObject>>) -> String {
if alloc_object_rc.implementation().fqn() == "std::core::StringImpl" { if object.borrow().implementation().fqn() == "std::core::StringImpl" {
extract_string_from_string(alloc_object_rc.clone()) extract_string_from_string(object.clone())
} else { } else {
todo!("what happens if we don't have a String?") todo!("what happens if we don't have a String?")
} }
} }
fn extract_string_from_string(string_object: Rc<DvmObject>) -> String { fn extract_string_from_string(string_object: Rc<RefCell<DvmObject>>) -> String {
let bytes_object = read_field_by_name("bytes", &string_object).expect_object(); let object_ref = string_object.borrow();
if bytes_object.implementation().fqn() != "std::core::ArrayImpl" { let bytes_field_value = object_ref.read_field("bytes");
panic!("String.bytes field is not a std::core::ArrayImpl"); if let DvmValue::ConstantPointer(bytes_ptr) = bytes_field_value {
let mut v = Vec::new();
for i in 0..bytes_ptr.length {
v.push(unsafe { bytes_ptr.raw_pointer.add(i).read() });
}
String::from_utf8(v).unwrap()
} else {
panic!("StringImpl.bytes field is not a DvmValue::ConstantPointer.");
} }
let address = read_field_by_name("ptr_address", &bytes_object).expect_usize();
let size = read_field_by_name("ptr_size", &bytes_object).expect_usize();
let raw_bytes_pointer = address as *mut u8;
let mut v: Vec<u8> = Vec::new();
for i in 0..size {
v.push(unsafe { raw_bytes_pointer.add(i).read() });
}
String::from_utf8(v).unwrap()
} }

13
src/vm/pointer.rs Normal file
View File

@ -0,0 +1,13 @@
use std::fmt::Display;
#[derive(Debug, Clone, PartialEq)]
pub struct DvmPointer {
pub raw_pointer: *const u8,
pub length: usize,
}
impl Display for DvmPointer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "DvmPointer({:p}, {})", self.raw_pointer, self.length)
}
}

15
src/vm/type.rs Normal file
View File

@ -0,0 +1,15 @@
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum DvmType {
// Primitives
Byte,
Int,
Long,
Double,
Boolean,
USize,
// Other
Object,
Array,
ConstantPointer
}

196
src/vm/value.rs Normal file
View File

@ -0,0 +1,196 @@
use crate::vm::array::DvmArray;
use crate::vm::object::DvmObject;
use crate::vm::pointer::DvmPointer;
use std::cell::RefCell;
use std::fmt::{Display, Formatter};
use std::rc::Rc;
#[derive(Debug, PartialEq, Clone)]
pub enum DvmValue {
// Primitives
Byte(u8),
Int(i32),
Long(i64),
Double(f64),
USize(usize),
Boolean(bool),
// Object
Object(Rc<RefCell<DvmObject>>),
// Array
Array(Rc<RefCell<DvmArray>>),
// Pointer to constant u8
ConstantPointer(DvmPointer),
// Null
Empty,
}
impl Default for DvmValue {
fn default() -> Self {
DvmValue::Empty
}
}
impl Display for DvmValue {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
DvmValue::Byte(b) => write!(f, "Byte({:x})", b),
DvmValue::Int(i) => write!(f, "Int({})", i),
DvmValue::Long(i) => write!(f, "Long({})", i),
DvmValue::Double(i) => write!(f, "Double({})", i),
DvmValue::USize(u) => write!(f, "USize({})", u),
DvmValue::Boolean(b) => write!(f, "Boolean({})", b),
DvmValue::Object(o) => write!(f, "Object({:p})", &o),
DvmValue::Array(a) => write!(f, "Array({:p})", &a),
DvmValue::ConstantPointer(p) => write!(f, "ConstantPointer({})", p),
DvmValue::Empty => write!(f, "Empty"),
}
}
}
impl DvmValue {
pub fn from_object(object: DvmObject) -> Self {
DvmValue::Object(Rc::new(RefCell::new(object)))
}
pub fn expect_byte(&self) -> u8 {
if let DvmValue::Byte(b) = self {
*b
} else {
panic!("Expected DvmValue::Byte, but found DvmValue::{:?}", self);
}
}
pub fn expect_int(&self) -> i32 {
if let DvmValue::Int(i) = self {
*i
} else {
panic!("Expected DvmValue::Int, but found DvmValue::{:?}", self);
}
}
pub fn expect_long(&self) -> i64 {
if let DvmValue::Long(l) = self {
*l
} else {
panic!("Expected DvmValue::Long, but found DvmValue::{:?}", self);
}
}
pub fn is_long(&self) -> bool {
match self {
DvmValue::Long(_) => true,
_ => false,
}
}
pub fn expect_double(&self) -> f64 {
if let DvmValue::Double(d) = self {
*d
} else {
panic!("Expected DvmValue::Double, but found DvmValue::{:?}", self);
}
}
pub fn expect_object(&self) -> Rc<RefCell<DvmObject>> {
if let DvmValue::Object(o) = self {
o.clone()
} else {
panic!("Expected DvmValue::Object, but found DvmValue::{:?}", self);
}
}
pub fn map_object<T>(
&self,
mapper: impl FnOnce(Rc<RefCell<DvmObject>>) -> T,
on_non_object: impl FnOnce(&Self) -> T,
) -> T {
if let DvmValue::Object(o) = self {
mapper(o.clone())
} else {
on_non_object(self)
}
}
pub fn is_object(&self) -> bool {
match self {
DvmValue::Object(_) => true,
_ => false,
}
}
pub fn expect_array(&self) -> Rc<RefCell<DvmArray>> {
if let DvmValue::Array(a) = self {
a.clone()
} else {
panic!("Expected DvmValue::Array, but found DvmValue::{:?}", self);
}
}
pub fn is_array(&self) -> bool {
match self {
DvmValue::Array(_) => true,
_ => false,
}
}
pub fn map_array<T>(
&self,
mapper: impl FnOnce(Rc<RefCell<DvmArray>>) -> T,
on_non_array: impl FnOnce(&Self) -> T,
) -> T {
if let DvmValue::Array(a) = self {
mapper(a.clone())
} else {
on_non_array(self)
}
}
pub fn expect_usize(&self) -> usize {
if let DvmValue::USize(u) = self {
*u
} else {
panic!("Expected DvmValue::USize, but found DvmValue::{:?}", self);
}
}
pub fn is_usize(&self) -> bool {
match self {
DvmValue::USize(_) => true,
_ => false,
}
}
pub fn as_usize(&self) -> Option<usize> {
match self {
DvmValue::Byte(b) => Some(*b as usize),
DvmValue::Int(i) => Some(*i as usize),
DvmValue::Long(l) => Some(*l as usize),
DvmValue::Double(d) => Some(*d as usize),
DvmValue::USize(u) => Some(*u),
_ => None,
}
}
pub fn expect_boolean(&self) -> bool {
if let DvmValue::Boolean(b) = self {
*b
} else {
panic!("Expected DvmValue::Boolean, but found DvmValue::{:?}", self);
}
}
pub fn expect_constant_pointer(&self) -> DvmPointer {
if let DvmValue::ConstantPointer(ptr) = self {
ptr.clone()
} else {
panic!(
"Expected DvmValue::ConstantPointer, but found DvmValue::{:?}",
self
);
}
}
}

28
src/vm/virtual_method.rs Normal file
View File

@ -0,0 +1,28 @@
#[derive(Debug, Eq)]
pub struct DmVirtualMethod {
fqn: String,
short_name: String,
}
impl PartialEq for DmVirtualMethod {
fn eq(&self, other: &Self) -> bool {
self.fqn == other.fqn
}
}
impl DmVirtualMethod {
pub fn new(fqn: &str, short_name: &str) -> Self {
DmVirtualMethod {
fqn: fqn.to_string(),
short_name: short_name.to_string(),
}
}
pub fn fqn(&self) -> &str {
self.fqn.as_str()
}
pub fn short_name(&self) -> &str {
self.short_name.as_str()
}
}