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::object_type::{DmField, DmFn, DmImplementation, DmInterface, DmMethod};
use deimos::vm::method::DvmMethod;
use deimos::vm::object_type::DvmImplementation;
use deimos::vm::op_codes::{
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::r#type::DvmType;
use deimos::vm::{call_fn, DvmContext, DvmState};
use std::rc::Rc;
@ -13,12 +17,12 @@ fn main() {
// - write a single lib with a main()
// - call the main fn
// fn main() { println "Hello, World!" }
// std/core/array lib
let mut array_lib = DmLib::new("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);
let array_int_rc = array_lib
.interfaces()
@ -27,15 +31,11 @@ fn main() {
.unwrap();
// std::core::ArrayImpl
let mut array_impl = DmImplementation::new(
"std::core::ArrayImpl",
"ArrayImpl",
Some(array_int_rc.clone()),
);
let mut array_impl = DvmImplementation::new("std::core::ArrayImpl", Some(array_int_rc.clone()));
let array_impl_ptr_address_fld = DmField::new("ptr_address", DmType::USize);
let array_impl_ptr_size_fld = DmField::new("ptr_size", DmType::USize);
let array_impl_length_fld = DmField::new("length", DmType::Long);
let array_impl_ptr_address_fld = DvmField::new("ptr_address", DvmType::USize);
let array_impl_ptr_size_fld = DvmField::new("ptr_size", DvmType::USize);
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_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, 2, 3);
let array_impl_ctor_0_fn = DmFn::new(
let array_impl_ctor_0_fn = DvmFunction::new(
"std::core::ArrayImpl::_ctor_0",
"_ctor_0",
array_impl_ctor_0_bytecode,
4,
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_lib.add_implementation(array_impl);
@ -68,7 +68,7 @@ fn main() {
// 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);
let string_int_rc = string_lib
.interfaces()
@ -76,13 +76,10 @@ fn main() {
.find(|interface| interface.fqn() == "std::core::String")
.unwrap();
let mut string_impl = DmImplementation::new(
"std::core::StringImpl",
"StringImpl",
Some(string_int_rc.clone()),
);
let mut string_impl =
DvmImplementation::new("std::core::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);
// std::core::String::_ctor_0(
@ -92,7 +89,7 @@ fn main() {
let mut string_ctor_0_bytecode: Vec<u8> = Vec::new();
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",
"_ctor_0",
string_ctor_0_bytecode,
@ -100,7 +97,7 @@ fn main() {
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);
@ -148,7 +145,7 @@ fn main() {
// Dealloc ArrayImpl
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);

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 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;
pub struct DmLib {
name: String,
constants: Vec<DmConstant>,
interfaces: Vec<Rc<DmInterface>>,
implementations: Vec<Rc<DmImplementation>>,
functions: Vec<Rc<DmFn>>,
implementations: Vec<Rc<DvmImplementation>>,
functions: Vec<Rc<DvmFunction>>,
}
impl DmLib {
@ -45,19 +47,19 @@ impl DmLib {
self.interfaces.push(Rc::new(interface));
}
pub fn implementations(&self) -> &Vec<Rc<DmImplementation>> {
pub fn implementations(&self) -> &Vec<Rc<DvmImplementation>> {
&self.implementations
}
pub fn add_implementation(&mut self, implementation: DmImplementation) {
pub fn add_implementation(&mut self, implementation: DvmImplementation) {
self.implementations.push(Rc::new(implementation));
}
pub fn functions(&self) -> &Vec<Rc<DmFn>> {
pub fn functions(&self) -> &Vec<Rc<DvmFunction>> {
&self.functions
}
pub fn add_function(&mut self, function: DmFn) {
pub fn add_function(&mut self, function: DvmFunction) {
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;
pub mod dvm_value;
mod array;
mod field;
mod function;
mod instruction;
mod interface;
pub mod lib;
pub mod mem;
mod method;
pub mod object;
pub mod object_type;
pub mod op_codes;
pub mod platform;
mod pointer;
pub mod r#type;
pub mod util;
pub mod value;
mod virtual_method;
use crate::vm::dvm_value::DvmValue;
use crate::vm::lib::{DmConstant, DmLib};
use crate::vm::mem::{read_field_by_index, write_field_by_index};
use crate::vm::instruction::{Immediate, Instruction, Location, SourceCodeLocation};
use crate::vm::object::DvmObject;
use crate::vm::object_type::DmFn;
use op_codes::*;
use std::alloc::{alloc, dealloc, Layout};
use crate::vm::object_type::DvmImplementation;
use crate::vm::pointer::DvmPointer;
use crate::vm::value::DvmValue;
use function::DvmFunction;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::Display;
use std::rc::Rc;
pub type PlatformFunction =
fn(args: Vec<DvmValue>, state: &mut DvmState, context: &DvmContext) -> DvmValue;
#[derive(Debug)]
enum CallFrame {
PlatformCall(CallFrameInfo),
DeimosCall(CallFrameInfo),
fn dvm_panic(context: &DvmContext, message: &str) {
println!("----");
println!("Deimos Virtual Machine: Panic!");
println!("{}", message);
println!("----");
println!("Current Instruction: {:?}", context.current_instruction);
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)]
struct CallFrameInfo {
pub fqn: String,
macro_rules! dvm_panic {
($context: expr, $message: expr) => {
dvm_panic($context, $message);
panic!()
};
}
pub type PlatformFunction = fn(context: &mut DvmContext);
pub struct DvmContext {
libs: Vec<Rc<DmLib>>,
function_cache: HashMap<String, Rc<DmFn>>,
call_stack: Vec<DvmCall>,
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>,
}
impl DvmContext {
pub fn new() -> Self {
DvmContext {
libs: Vec::new(),
function_cache: HashMap::new(),
call_stack: vec![],
current_instruction: None,
registers: vec![],
stack: vec![],
implementations: HashMap::new(),
static_functions: HashMap::new(),
platform_functions: HashMap::new(),
}
}
pub fn load_libs(&mut self, libs: Vec<Rc<DmLib>>) {
for lib in libs {
for lib_fn in lib.functions() {
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 fn pop_stack(&mut self) -> DvmValue {
self.stack.pop().unwrap_or_else(|| {
dvm_panic!(self, "Stack underflow!");
})
}
}
pub struct DvmState {
call_stack: Vec<CallFrame>,
registers: Vec<DvmValue>,
register_state_stack: Vec<Vec<DvmValue>>,
pub struct DvmCall {
function_name: Rc<String>,
source_code_location: SourceCodeLocation,
}
impl DvmState {
pub fn new() -> Self {
DvmState {
call_stack: Vec::new(),
registers: Vec::new(),
register_state_stack: Vec::new(),
}
impl Display for DvmCall {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"{} in {} @ {}:{}",
self.function_name,
self.source_code_location.source_file_name,
self.source_code_location.line,
self.source_code_location.char
))
}
}
macro_rules! dump_state {
( $state: expr ) => {
println!("Registers: ");
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()
fn update_index(index: &mut usize, offset: isize) {
if offset.is_negative() {
*index -= offset.abs() as usize;
} else {
DvmValue::Void
*index += offset as usize;
}
}
macro_rules! next_8 {
( $byte_code: expr, $T: ident ) => {
$byte_code.next().unwrap() as $T
};
fn push_call_frame(
function_name: Rc<String>,
source_code_location: SourceCodeLocation,
context: &mut DvmContext,
) {
context.call_stack.push(DvmCall {
function_name,
source_code_location,
});
}
macro_rules! next_32_le {
( $byte_code: expr, $T: ident ) => {
next_8!($byte_code, $T)
+ (next_8!($byte_code, $T) << 8)
+ (next_8!($byte_code, $T) << 16)
+ (next_8!($byte_code, $T) << 24)
};
fn pop_call_frame(context: &mut DvmContext) {
context.call_stack.pop();
}
macro_rules! next_64_le {
( $byte_code: expr, $T: ident ) => {
next_8!($byte_code, $T)
+ (next_8!($byte_code, $T) << 8)
+ (next_8!($byte_code, $T) << 16)
+ (next_8!($byte_code, $T) << 24)
+ (next_8!($byte_code, $T) << 32)
+ (next_8!($byte_code, $T) << 40)
+ (next_8!($byte_code, $T) << 48)
+ (next_8!($byte_code, $T) << 56)
};
}
macro_rules! next_usize_le {
( $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);
fn compute_index_with_offset(
registers: &Vec<DvmValue>,
index_register: usize,
offset: &Option<isize>,
) -> usize {
let index = registers[index_register].as_usize().expect(&format!(
"Could not convert the value of index_register {} to usize; found {:?}",
index_register, registers[index_register]
));
if let Some(o) = offset {
if o.is_negative() {
index - o.unsigned_abs()
} else {
index + o.unsigned_abs()
}
u
}};
} else {
index
}
}
macro_rules! read_array_u8 {
( $iter: expr ) => {{
let arr_length = next_8!($iter, u8);
let mut arr = Vec::with_capacity(arr_length as usize);
let mut i = 0;
while i < arr_length {
arr.push(next_8!($iter, u8));
i += 1;
fn copy_from_source(context: &DvmContext, source: &Location) -> DvmValue {
match source {
Location::Register(register) => context.registers[*register].clone(),
Location::Stack { offset } => context.stack[*offset].clone(),
Location::Field {
object_register,
field_name,
} => 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 {
( $iter: expr ) => {{
let str_length = next_32_le!($iter, usize);
let mut str_raw: Vec<u8> = Vec::new();
let mut i = 0;
while i < str_length {
str_raw.push($iter.next().unwrap());
i += 1;
fn write_to_destination(context: &mut DvmContext, destination: &Location, value: DvmValue) {
match destination {
Location::Register(register) => {
context.registers[*register] = value;
}
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]) {
let mut iter = byte_code.iter().cloned();
while let Some(op_code) = iter.next() {
match op_code {
MOV_BYTE => {
todo!()
}
MOV_INT => {
let target_register = next_8!(iter, usize);
let operand = next_32_le!(iter, u32) as i32;
state.registers[target_register] = DvmValue::Int(operand);
}
MOV_LONG => {
let target_register = next_8!(iter, usize);
let operand = next_64_le!(iter, u64) as i64;
state.registers[target_register] = DvmValue::Long(operand);
}
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);
fn immediate_to_value(immediate: &Immediate) -> DvmValue {
match immediate {
Immediate::Byte(b) => DvmValue::Byte(*b),
Immediate::Int(i) => DvmValue::Int(*i),
Immediate::Long(l) => DvmValue::Long(*l),
Immediate::Double(d) => DvmValue::Double(*d),
Immediate::Usize(s) => DvmValue::USize(*s),
Immediate::Boolean(b) => DvmValue::Boolean(*b),
Immediate::ConstantPointer {
raw_pointer,
length,
} => DvmValue::ConstantPointer(DvmPointer {
raw_pointer: *raw_pointer,
length: *length,
}),
Immediate::Empty => DvmValue::Empty,
}
}
let source_object = state
.registers
.get(source_register)
.unwrap()
.expect_object();
state.registers[target_register] =
read_field_by_index(source_object_field_index, &source_object);
// TODO: find all possible unwraps/expects and wrap with call to dvm_panic
pub fn run(instructions: &[Instruction], context: &mut DvmContext) {
let mut index = 0;
while index < instructions.len() {
context.current_instruction = Some(instructions[index].clone());
index += 1;
match &instructions[index] {
Instruction::MoveImmediate {
destination,
immediate,
} => {
write_to_destination(context, destination, immediate_to_value(immediate));
}
STORE => {
let target_register = next_8!(iter, usize);
let target_object_field_index = next_usize_le!(iter);
let source_register = next_8!(iter, usize);
let target_object = state
.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);
Instruction::Copy {
source,
destination,
} => {
let value = copy_from_source(context, source);
write_to_destination(context, destination, value);
}
ALLOC => {
let target_register = next_8!(iter, usize);
let impl_name = read_string!(iter);
Instruction::Push { source_register } => {
context
.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
.libs
.iter()
.find_map(|lib| {
lib.implementations()
.iter()
.find(|implementation| implementation.fqn() == impl_name)
})
.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);
.implementations
.get(implementation_name.as_str())
.unwrap();
let object = DvmObject::new(implementation.clone());
context.registers[*destination_register] =
DvmValue::Object(Rc::new(RefCell::new(object)));
}
DEALLOC => {
let target_register = next_8!(iter, usize);
let target_object =
std::mem::replace(&mut state.registers[target_register], DvmValue::Uninit)
.expect_object();
drop(target_object); // explicit
state.registers[target_register] = DvmValue::Uninit;
}
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)
Instruction::InvokeStatic {
function_name,
source_code_location,
} => {
let static_function = context
.static_functions
.get(function_name.as_str())
.unwrap()
.expect_usize();
let size = state.registers.get(size_register).unwrap().expect_usize();
let layout = Layout::from_size_align(size, 1).unwrap();
unsafe {
dealloc(address as *mut u8, layout);
}
state.registers[address_register] = DvmValue::Uninit;
state.registers[size_register] = DvmValue::Uninit;
.clone();
push_call_frame(function_name.clone(), source_code_location.clone(), context);
run(static_function.instructions(), context);
pop_call_frame(context);
}
PLATFORM_CALL => {
// Calls a platform function. The result of the platform call is stored in the
// specified return register.
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 platform_function = context.platform_functions.get(&symbol_name).unwrap();
state
.call_stack
.push(CallFrame::PlatformCall(CallFrameInfo { fqn: symbol_name }));
let call_result = platform_function(args, state, context);
state.registers[return_register] = call_result;
state.call_stack.pop();
Instruction::InvokeObject {
object_register,
function_name,
source_code_location,
} => {
let object = context.registers[*object_register].expect_object();
let object_ref = object.borrow();
let method = object_ref
.get_method(function_name.as_str())
.expect(&format!(
"Cannot find method {} for object {:?}",
function_name, object
));
let function = method.dm_fn();
let instructions = function.instructions();
push_call_frame(function_name.clone(), source_code_location.clone(), context);
run(instructions, context);
pop_call_frame(context);
}
INVOKE_FN => {
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 dm_fn = context.function_cache.get(&symbol_name).unwrap();
let call_result = call_fn(state, context, dm_fn, args);
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
Instruction::PlatformCall {
platform_function_name,
source_code_location,
} => {
let platform_function = context
.platform_functions
.get(platform_function_name.as_str())
.unwrap()
.clone();
push_call_frame(
platform_function_name.clone(),
source_code_location.clone(),
context,
);
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::object_type::DmImplementation;
use std::alloc::{alloc, dealloc, Layout};
use std::ptr;
use crate::vm::method::DvmMethod;
use crate::vm::object_type::DvmImplementation;
use crate::vm::value::DvmValue;
use std::rc::Rc;
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, PartialEq)]
pub struct DvmObject {
data: Vec<*mut u8>,
implementation: Rc<DmImplementation>,
}
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>>(),
}
data: Vec<DvmValue>,
implementation: Rc<DvmImplementation>,
}
impl DvmObject {
pub fn new(implementation: Rc<DmImplementation>) -> 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())) }
}
pub fn new(implementation: Rc<DvmImplementation>) -> Self {
DvmObject {
data,
data: vec![DvmValue::Empty; implementation.field_count()],
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
}
pub fn implementation(&self) -> &DmImplementation {
pub fn implementation(&self) -> &DvmImplementation {
&self.implementation
}
}
impl Drop for DvmObject {
fn drop(&mut self) {
for (index, field) in self.implementation.fields().iter().enumerate() {
unsafe { dealloc(self.data[index], layout_for(field.dm_type())) }
}
pub fn read_field(&self, field_name: &str) -> &DvmValue {
&self.data[self.get_field_index(field_name)]
}
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 std::collections::HashMap;
use std::fmt::Debug;
use std::rc::Rc;
#[derive(Debug, Eq)]
pub struct DmFn {
pub struct DvmImplementation {
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>>,
functions: Vec<Rc<DmFn>>,
methods: Vec<Rc<DmMethod>>,
fields: Vec<DmField>,
static_functions: HashMap<String, Rc<DvmFunction>>,
methods: HashMap<String, Rc<DvmMethod>>,
fields: HashMap<String, Rc<DvmField>>,
}
impl PartialEq for DmImplementation {
impl PartialEq for DvmImplementation {
fn eq(&self, other: &Self) -> bool {
self.fqn == other.fqn
}
}
impl DmImplementation {
pub fn new(fqn: &str, short_name: &str, interface: Option<Rc<DmInterface>>) -> Self {
DmImplementation {
impl DvmImplementation {
pub fn new(fqn: &str, interface: Option<Rc<DmInterface>>) -> Self {
DvmImplementation {
fqn: fqn.to_string(),
short_name: short_name.to_string(),
interface,
functions: Vec::new(),
methods: Vec::new(),
fields: Vec::new(),
static_functions: HashMap::new(),
methods: HashMap::new(),
fields: HashMap::new(),
}
}
@ -191,81 +37,30 @@ impl DmImplementation {
self.fqn.as_str()
}
pub fn short_name(&self) -> &str {
self.short_name.as_str()
pub fn add_function(&mut self, function: DvmFunction) {
self.static_functions
.insert(function.fqn().to_string(), Rc::new(function));
}
pub fn interface(&self) -> Option<Rc<DmInterface>> {
self.interface.clone()
pub fn add_method(&mut self, dvm_method: DvmMethod) {
self.methods
.insert(dvm_method.fqn().to_string(), Rc::new(dvm_method));
}
pub fn functions(&self) -> &Vec<Rc<DmFn>> {
&self.functions
pub fn find_method(&self, name: &str, self_object: &DvmObject) -> Option<Rc<DvmMethod>> {
self.methods.get(name).cloned()
}
pub fn add_function(&mut self, dm_fn: DmFn) {
self.functions.push(Rc::new(dm_fn));
pub fn add_field(&mut self, dm_field: DvmField) {
self.fields
.insert(dm_field.name().to_string(), Rc::new(dm_field));
}
pub fn methods(&self) -> &Vec<Rc<DmMethod>> {
&self.methods
pub fn find_field(&self, name: &str, self_object: &DvmObject) -> Option<Rc<DvmField>> {
self.fields.get(name).cloned()
}
pub fn add_method(&mut self, dm_method: DmMethod) {
self.methods.push(Rc::new(dm_method));
}
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
pub fn field_count(&self) -> usize {
self.fields.len()
}
}

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::{DvmContext, DvmState};
use crate::vm::value::DvmValue;
use crate::vm::DvmContext;
use std::cell::RefCell;
use std::rc::Rc;
pub fn dm_print(args: Vec<DvmValue>, _state: &mut DvmState, _context: &DvmContext) -> DvmValue {
if args.len() != 1 {
return DvmValue::Void; // TODO: make exception
}
print!("{}", get_string(&args[0]));
DvmValue::Void
/// ```deimos
/// // Signature
/// print(m: &(Byte | Int | Long | Double | USize | Boolean | Object | Empty))
/// ```
pub fn dm_print(context: &mut DvmContext) {
print!("{}", get_string(context.pop_stack()));
}
pub fn dm_println(args: Vec<DvmValue>, _state: &mut DvmState, _context: &DvmContext) -> DvmValue {
if args.len() != 1 {
return DvmValue::Uninit;
}
println!("{}", get_string(&args[0]));
DvmValue::Void
pub fn dm_println(context: &mut DvmContext) {
println!("{}", get_string(context.pop_stack()));
}
fn get_string(dvm_value: &DvmValue) -> String {
fn get_string(dvm_value: DvmValue) -> String {
match dvm_value {
DvmValue::Byte(b) => b.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::Object(object) => object_to_string(object.clone()),
DvmValue::USize(u) => u.to_string(),
DvmValue::Uninit => String::from("Uninit"),
DvmValue::Void => String::from("Void"),
DvmValue::Array(a) => a.borrow().to_string(),
DvmValue::ConstantPointer(p) => p.to_string(),
DvmValue::Empty => String::from("Empty"),
}
}
fn object_to_string(alloc_object_rc: Rc<DvmObject>) -> String {
if alloc_object_rc.implementation().fqn() == "std::core::StringImpl" {
extract_string_from_string(alloc_object_rc.clone())
fn object_to_string(object: Rc<RefCell<DvmObject>>) -> String {
if object.borrow().implementation().fqn() == "std::core::StringImpl" {
extract_string_from_string(object.clone())
} else {
todo!("what happens if we don't have a String?")
}
}
fn extract_string_from_string(string_object: Rc<DvmObject>) -> String {
let bytes_object = read_field_by_name("bytes", &string_object).expect_object();
if bytes_object.implementation().fqn() != "std::core::ArrayImpl" {
panic!("String.bytes field is not a std::core::ArrayImpl");
fn extract_string_from_string(string_object: Rc<RefCell<DvmObject>>) -> String {
let object_ref = string_object.borrow();
let bytes_field_value = object_ref.read_field("bytes");
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()
}
}