Compare commits

...

5 Commits

Author SHA1 Message Date
Jesse Brault
cf0c44e627 Update of semantics of Move Register operation. 2024-12-28 16:42:43 -06:00
Jesse Brault
7aa93c3986 Clean up op codes. 2024-12-28 15:35:35 -06:00
Jesse Brault
9c4ca23765 Remove deprecated op codes. 2024-12-28 15:14:24 -06:00
Jesse Brault
420db38f70 Properly implement load and store instructions. 2024-12-28 15:10:56 -06:00
Jesse Brault
376ac2fa3a Rename DmAllocObject to DvmObject. 2024-12-28 10:23:05 -06:00
11 changed files with 247 additions and 462 deletions

View File

@ -91,7 +91,7 @@ The operand is converted to a `bool` at runtime by the virtual machine.
=== Move Register
Copies the value in one register to another.
Moves or copies (see below) the value in one register to another.
[source]
----
@ -101,60 +101,12 @@ mov_register(
)
----
=== Load Byte
For `Byte`, `Int`, `Long`, `USize`, `Double`, and `Boolean`, the value is **copied** to the register. For `Object`
values, the value is **moved**, meaning that register takes ownership of the referenced object.
Loads the target register with the `u8` stored at the offset from the base of the object stored in the source register.
=== Load
[source]
----
load_byte(
target_register: u8,
source_register: u8,
offset: usize
)
----
The source register must contain a `DvmValue::Object` at runtime.
=== Load Int
Loads the target register with the `i32` stored at the offset from the base of the object stored in the source register.
[source]
----
load_int(
target_register: u8,
source_register: u8,
offset: usize
)
----
The source register must contain a `DvmValue::Object` at runtime.
=== Load Long
Loads the target register with the `i64` stored at the offset from the base of the object stored in the source register.
[source]
----
load_long(
target_register: u8,
source_register: u8,
offset: usize
)
----
The source register must contain a `DvmValue::Object` at runtime.
=== Load Double
=== Load USize
=== Load Boolean
=== Load Object
Loads the target register with the `DvmValue::Object` stored at the offset from the base of the object stored in the
Loads the target register with the `DvmValue` inner value stored in the field at `field_index` in the object in the
source register.
[source]
@ -162,16 +114,15 @@ source register.
load_object(
target_register: u8,
source_register: u8,
offset: usize
field_index: usize
)
----
The source register must contain a `DvmValue::Object` at runtime, and the data at the offset from that object's base
must be a valid `Rc<DvmObject>`.
The source register must contain a `DvmValue::Object` at runtime.
=== Store
Stores the value contained in the source register to the offset from the base of the object contained in the target
Stores the value contained in the source register to the field at `field_index` in the object contained in the target
register.
[source]

View File

@ -2,7 +2,7 @@ use deimos::vm::dm_type::DmType;
use deimos::vm::lib::{DmConstant, DmLib};
use deimos::vm::object_type::{DmField, DmFn, DmImplementation, DmInterface, DmMethod};
use deimos::vm::op_codes::{
add_alloc, add_invoke_fn, add_mov_const, add_mov_register_to, add_platform_call,
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::{call_fn, DvmContext, DvmState};
@ -28,9 +28,9 @@ fn main() {
Some(array_int_rc.clone()),
);
let array_impl_ptr_address_fld = DmField::new("ptr_address", DmType::USize, 0);
let array_impl_ptr_size_fld = DmField::new("ptr_size", DmType::USize, 8);
let array_impl_length_fld = DmField::new("length", DmType::Long, 16);
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);
array_impl.fields.push(array_impl_ptr_address_fld);
array_impl.fields.push(array_impl_ptr_size_fld);
@ -44,9 +44,9 @@ fn main() {
// )
let mut array_impl_ctor_0_bytecode: Vec<u8> = Vec::new();
add_mov_register_to(&mut array_impl_ctor_0_bytecode, 0, 0, 1);
add_mov_register_to(&mut array_impl_ctor_0_bytecode, 0, 8, 2);
add_mov_register_to(&mut array_impl_ctor_0_bytecode, 0, 16, 3);
add_store(&mut array_impl_ctor_0_bytecode, 0, 0, 1);
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(
"std::core::ArrayImpl::_ctor_0",
@ -70,7 +70,7 @@ fn main() {
let mut string_impl = DmImplementation::new("std::core::StringImpl", "StringImpl", None);
let bytes_field = DmField::new("bytes", DmType::Object, 0);
let bytes_field = DmField::new("bytes", DmType::Object);
string_impl.fields.push(bytes_field);
// std::core::String::_ctor_0(
@ -78,7 +78,7 @@ fn main() {
// r1: Array<Byte> bytes
// )
let mut string_ctor_0_bytecode: Vec<u8> = Vec::new();
add_mov_register_to(&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(
"std::core::StringImpl::_ctor_0",
@ -91,7 +91,6 @@ fn main() {
let string_ctor_0_method = DmMethod::new(string_ctor_0_fn, None);
string_impl.methods.push(Rc::new(string_ctor_0_method));
let core_string_impl_size = string_impl.size_in_bytes();
string_lib.interfaces.push(Rc::new(string_int));
string_lib.implementations.push(Rc::new(string_impl));
@ -110,12 +109,7 @@ fn main() {
add_mov_const(&mut main_byte_code, 0, 1, "greeting", 0);
// Alloc Array<Byte> greeting bytes
add_alloc(
&mut main_byte_code,
2,
array_impl_rc.size_in_bytes() as u32,
"std::core::ArrayImpl",
);
add_alloc(&mut main_byte_code, 2, "std::core::ArrayImpl");
// Call ArrayImpl(ptr_address, ptr_size, length)
add_invoke_fn(
@ -126,12 +120,7 @@ fn main() {
);
// Alloc StringImpl greeting
add_alloc(
&mut main_byte_code,
4,
core_string_impl_size as u32,
"std::core::StringImpl",
);
add_alloc(&mut main_byte_code, 4, "std::core::StringImpl");
// Call StringImpl(greeting_bytes)
add_invoke_fn(
@ -144,6 +133,12 @@ fn main() {
// Call println(greeting)
add_platform_call(&mut main_byte_code, "std::core::println", 6, &[4]);
// Dealloc StringImpl
add_dealloc(&mut main_byte_code, 4);
// Dealloc ArrayImpl
add_dealloc(&mut main_byte_code, 2);
let main_dm_fn = DmFn::new("main", "main", main_byte_code, 7, None);
greeting_lib.functions.push(Rc::new(main_dm_fn));

View File

@ -7,7 +7,6 @@ pub enum DmType {
Boolean,
Object,
USize,
Unit,
}
impl DmType {
@ -20,7 +19,6 @@ impl DmType {
DmType::Boolean => size_of::<bool>(),
DmType::Object => size_of::<usize>(),
DmType::USize => size_of::<usize>(),
DmType::Unit => todo!("Need to determine size of Unit... is it dependent on the size of the thing which is Unit?")
}
}
}

View File

@ -1,4 +1,4 @@
use crate::vm::object::DmAllocObject;
use crate::vm::object::DvmObject;
use std::rc::Rc;
#[derive(Debug, Clone, PartialEq)]
@ -8,7 +8,7 @@ pub enum DvmValue {
Long(i64),
Double(f64),
Boolean(bool),
Object(Rc<DmAllocObject>),
Object(Rc<DvmObject>),
USize(usize),
Uninit,
Void,
@ -23,7 +23,7 @@ impl DvmValue {
}
}
pub fn expect_object(&self) -> Rc<DmAllocObject> {
pub fn expect_object(&self) -> Rc<DvmObject> {
if let DvmValue::Object(o) = self {
o.clone()
} else {
@ -31,6 +31,13 @@ impl DvmValue {
}
}
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

View File

@ -1,70 +1,86 @@
use crate::vm::dm_type::DmType;
use crate::vm::dvm_value::DvmValue;
use crate::vm::object::DmAllocObject;
use crate::vm::object_type::DmField;
use crate::vm::object::DvmObject;
use std::rc::Rc;
pub unsafe fn get_field_value(dm_field: &DmField, self_object: &DmAllocObject) -> DvmValue {
let data_size = dm_field.dm_type().size_in_bytes();
let mut raw_data: Vec<u8> = Vec::with_capacity(data_size);
for i in dm_field.data_offset()..(dm_field.data_offset() + data_size) {
raw_data.push(self_object.data.offset(i as isize).read());
}
match dm_field.dm_type() {
DmType::Byte => DvmValue::Byte(raw_data[0]),
DmType::Int => DvmValue::Int(i32::from_ne_bytes(
raw_data[0..data_size].try_into().unwrap(),
)),
DmType::Long => DvmValue::Long(i64::from_ne_bytes(
raw_data[0..data_size].try_into().unwrap(),
)),
DmType::Double => DvmValue::Double(f64::from_ne_bytes(
raw_data[0..data_size].try_into().unwrap(),
)),
DmType::Boolean => DvmValue::Boolean(raw_data[0] != 0),
DmType::Object => {
// read the pointer's (address) value
let address = usize::from_ne_bytes(raw_data[0..data_size].try_into().unwrap());
DvmValue::Object(Rc::from_raw(address as *const DmAllocObject))
}
DmType::USize => DvmValue::USize(usize::from_ne_bytes(
raw_data[0..data_size].try_into().unwrap(),
)),
DmType::Unit => DvmValue::Uninit,
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()),
}
}
macro_rules! read_array {
( $T: ty, $raw_data: expr, $length: expr ) => {{
let t_size = size_of::<$T>();
let mut arr: Vec<$T> = Vec::with_capacity($length);
for i in 0..$length {
arr.push(<$T>::from_ne_bytes(
$raw_data[(i * t_size)..(i * t_size + t_size)]
.try_into()
.unwrap(),
))
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);
}
arr
}};
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."),
}
}
fn read_i32_array(raw_data: &Vec<u8>, length: usize) -> Vec<i32> {
read_array!(i32, raw_data, length)
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) };
}
fn read_i64_array(raw_data: &Vec<u8>, length: usize) -> Vec<i64> {
read_array!(i64, raw_data, length)
}
fn read_f64_array(raw_data: &Vec<u8>, length: usize) -> Vec<f64> {
read_array!(f64, raw_data, length)
}
fn read_bool_array(raw_data: &Vec<u8>) -> Vec<bool> {
raw_data.iter().map(|b| *b == 1).collect()
}
fn read_usize_array(raw_data: &Vec<u8>, length: usize) -> Vec<usize> {
read_array!(usize, raw_data, length)
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) };
}

View File

@ -11,7 +11,8 @@ pub mod util;
use crate::vm::dvm_value::DvmValue;
use crate::vm::lib::{DmConstant, DmLib};
use crate::vm::object::DmAllocObject;
use crate::vm::mem::{read_field_by_index, write_field_by_index};
use crate::vm::object::DvmObject;
use crate::vm::object_type::DmFn;
use op_codes::*;
use std::alloc::{alloc, dealloc, Layout};
@ -273,6 +274,9 @@ 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;
@ -294,73 +298,37 @@ pub fn run_byte_code(state: &mut DvmState, context: &DvmContext, byte_code: &[u8
let source_value = state.registers.get(source_register).unwrap();
state.registers[target_register] = source_value.clone();
}
LOAD_OBJECT => {
LOAD => {
let target_register = next_8!(iter, usize);
let source_register = next_8!(iter, usize);
let offset = next_usize_le!(iter);
let source_object = state.registers
let source_object_field_index = next_usize_le!(iter);
let source_object = state
.registers
.get(source_register)
.unwrap()
.expect_object();
let object = unsafe {
Rc::from_raw(source_object.data.add(offset).cast::<DmAllocObject>())
};
state.registers[target_register] = DvmValue::Object(object);
state.registers[target_register] =
read_field_by_index(source_object_field_index, &source_object);
}
STORE => {
let target_register = next_8!(iter, usize);
let offset = next_usize_le!(iter);
let target_object_field_index = next_usize_le!(iter);
let source_register = next_8!(iter, usize);
let target_alloc_object = state
let target_object = state
.registers
.get(target_register)
.unwrap()
.expect_object();
let source_value = std::mem::replace(&mut state.registers[source_register], DvmValue::Uninit);
match source_value {
DvmValue::Int(i) => {
write_bytes!(target_alloc_object.data, offset, i.to_ne_bytes());
}
DvmValue::Byte(b) => unsafe {
target_alloc_object.data.add(offset).write(b);
},
DvmValue::Long(l) => {
write_bytes!(target_alloc_object.data, offset, l.to_ne_bytes());
}
DvmValue::Double(d) => {
write_bytes!(target_alloc_object.data, offset, d.to_ne_bytes());
}
DvmValue::Boolean(b) => unsafe {
target_alloc_object.data.add(offset).write(b as u8);
},
DvmValue::Object(source_object) => {
let source_object_ptr = Rc::into_raw(source_object);
write_bytes!(
target_alloc_object.data,
offset,
(source_object_ptr as usize).to_ne_bytes()
);
}
DvmValue::USize(u) => {
write_bytes!(target_alloc_object.data, offset, u.to_ne_bytes());
}
DvmValue::Uninit => {
panic!("Cannot store DvmValue::Uninit to object.")
}
DvmValue::Void => {
panic!("Cannot store DvmValue::Void to 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 => {
let target_register = next_8!(iter, usize);
let alloc_size = next_32_le!(iter, usize);
let impl_name = read_string!(iter);
let implementation = context
@ -373,86 +341,19 @@ pub fn run_byte_code(state: &mut DvmState, context: &DvmContext, byte_code: &[u8
})
.expect(&format!("Implementation not found: {}", impl_name));
let dm_alloc_object = DmAllocObject::new(alloc_size, implementation.clone());
let dm_alloc_object = DvmObject::new(implementation.clone());
state.registers[target_register] = DvmValue::Object(Rc::new(dm_alloc_object));
}
DEALLOC => {
let target_register = next_8!(iter, usize);
let dm_alloc_object = state
let dvm_object = state
.registers
.get(target_register)
.unwrap()
.expect_object();
drop(dm_alloc_object); // explicit
drop(dvm_object); // explicit
state.registers[target_register] = DvmValue::Uninit;
}
MOV_INT_TO => {
let target_register = next_8!(iter, usize);
let offset = next_32_le!(iter, usize);
let operand = next_32_le!(iter, u32) as i32;
let dm_alloc_object = state
.registers
.get(target_register)
.unwrap()
.expect_object();
write_bytes!(dm_alloc_object.data, offset, operand.to_ne_bytes());
}
MOV_LONG_TO => {
todo!()
}
MOV_DOUBLE_TO => {
todo!()
}
MOV_REGISTER_TO => {
// Moves the value from the source register to the object pointed to by the target
// register, at the given offset.
let target_register = next_8!(iter, usize);
let offset = next_32_le!(iter, usize);
let source_register = next_8!(iter, usize);
let target_alloc_object = state
.registers
.get(target_register)
.unwrap()
.expect_object();
let source_value = state.registers.get(source_register).unwrap();
match source_value {
DvmValue::Int(i) => {
write_bytes!(target_alloc_object.data, offset, i.to_ne_bytes());
}
DvmValue::Byte(b) => unsafe {
target_alloc_object.data.add(offset).write(*b);
},
DvmValue::Long(l) => {
write_bytes!(target_alloc_object.data, offset, l.to_ne_bytes());
}
DvmValue::Double(d) => {
write_bytes!(target_alloc_object.data, offset, d.to_ne_bytes());
}
DvmValue::Boolean(b) => unsafe {
target_alloc_object.data.add(offset).write(*b as u8);
},
DvmValue::Object(source_object) => {
let source_object_ptr = Rc::into_raw(source_object.clone());
write_bytes!(
target_alloc_object.data,
offset,
(source_object_ptr as usize).to_ne_bytes()
);
}
DvmValue::USize(us) => {
write_bytes!(target_alloc_object.data, offset, us.to_ne_bytes());
}
DvmValue::Uninit => {
panic!("Cannot move DvmValue::Uninit to object.")
}
DvmValue::Void => {
panic!("Cannot move DvmValue::Void to object.")
}
}
}
MOV_CONST => {
// Decode
let address_register = next_8!(iter, usize);
@ -564,7 +465,7 @@ pub fn run_byte_code(state: &mut DvmState, context: &DvmContext, byte_code: &[u8
let self_obj = args.get(0).unwrap().expect_object();
let method = self_obj
.implementation
.implementation()
.get_method(&symbol_name, &self_obj)
.expect(&format!("Could not find method: {}", symbol_name));
let dm_fn = method.dm_fn();
@ -575,9 +476,6 @@ pub fn run_byte_code(state: &mut DvmState, context: &DvmContext, byte_code: &[u8
INVOKE_DYNAMIC => {
unimplemented!("INVOKE_DYNAMIC is not yet supported and may never be.")
}
MOV_SIZE_OF => {
todo!()
}
MULTIPLY => {
todo!()
}
@ -590,9 +488,9 @@ pub fn run_byte_code(state: &mut DvmState, context: &DvmContext, byte_code: &[u8
#[cfg(test)]
mod run_code_tests {
use super::*;
use crate::vm::dm_type::DmType;
use crate::vm::object_type::{DmField, DmImplementation};
use super::*;
macro_rules! assert_register {
( $expected: expr, $state: expr, $register_number: expr ) => {
@ -603,13 +501,17 @@ mod run_code_tests {
};
}
fn setup() -> (DvmState, DvmContext) {
(DvmState::new(), DvmContext::new())
fn setup(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, 0);
let object_field = DmField::new("object_field", DmType::Object);
dm_impl.fields.push(object_field);
dm_impl
}
@ -618,7 +520,7 @@ mod run_code_tests {
fn mov_1_as_int() {
let mut code = Vec::new();
add_mov_int(&mut code, 0, 1);
let (mut state, context) = setup();
let (mut state, context) = setup(1);
run_byte_code(&mut state, &context, &code);
assert_register!(DvmValue::Int(1), state, 0);
}
@ -627,7 +529,7 @@ mod run_code_tests {
fn move_65535_as_int() {
let mut code = Vec::new();
add_mov_int(&mut code, 0, 0xffff);
let (mut state, context) = setup();
let (mut state, context) = setup(1);
run_byte_code(&mut state, &context, &code);
assert_register!(DvmValue::Int(0xffff), state, 0);
}
@ -636,7 +538,7 @@ mod run_code_tests {
fn move_int_max_as_int() {
let mut code = Vec::new();
add_mov_int(&mut code, 0, 0x0fff_ffff);
let (mut state, context) = setup();
let (mut state, context) = setup(1);
run_byte_code(&mut state, &context, &code);
assert_register!(DvmValue::Int(0x0fff_ffff), state, 0);
}
@ -646,34 +548,29 @@ mod run_code_tests {
let mut code = Vec::new();
add_mov_int(&mut code, 1, 1);
add_mov_register(&mut code, 0, 1);
let (mut state, context) = setup();
state.registers.resize(2, DvmValue::Uninit);
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 dummy_impl_rc = Rc::new(dummy_impl);
let dummy_impl_object_field = dummy_impl_rc.fields.iter().find(|field| {
field.name() == "object_field"
}).unwrap();
let mut dummy_lib = DmLib::new("dummy");
dummy_lib.implementations.push(dummy_impl_rc.clone());
let mut code = Vec::new();
add_alloc(&mut code, 0, dummy_impl_rc.size_in_bytes() as u32, &dummy_impl_rc.fqn);
add_alloc(&mut code, 1, dummy_impl_rc.size_in_bytes() as u32, &dummy_impl_rc.fqn);
add_store(&mut code, 0, dummy_impl_object_field.data_offset(), 1);
add_load_object(&mut code, 2, 0, dummy_impl_object_field.data_offset());
let (mut state, mut context) = setup();
add_alloc(&mut code, 0, &dummy_impl_rc.fqn);
add_alloc(&mut code, 1, &dummy_impl_rc.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);
let referenced_object = state.registers.get(1).unwrap().expect_object();
assert_register!(DvmValue::Object(referenced_object), state, 2);
assert!(state.registers[2].is_object())
}
}

View File

@ -1,34 +1,52 @@
use crate::vm::dm_type::DmType;
use crate::vm::object_type::DmImplementation;
use std::alloc::{alloc, dealloc, Layout};
use std::collections::HashSet;
use std::ptr;
use std::rc::Rc;
#[derive(Debug, PartialEq, Eq)]
pub struct DmAllocObject {
pub data: *mut u8,
pub units: HashSet<usize>,
pub size: usize,
pub layout: Layout,
pub implementation: Rc<DmImplementation>,
pub struct DvmObject {
data: Vec<*mut u8>,
implementation: Rc<DmImplementation>,
}
impl DmAllocObject {
pub fn new(size: usize, implementation: Rc<DmImplementation>) -> Self {
let layout = Layout::from_size_align(size, 1).unwrap();
DmAllocObject {
data: unsafe { alloc(layout) },
units: HashSet::new(),
size,
layout,
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 {
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())) }
}
DvmObject {
data,
implementation,
}
}
pub fn data(&self) -> &[*mut u8] {
&self.data
}
pub fn implementation(&self) -> &DmImplementation {
&self.implementation
}
}
impl Drop for DmAllocObject {
impl Drop for DvmObject {
fn drop(&mut self) {
unsafe {
dealloc(self.data, self.layout);
for (index, field) in self.implementation.fields.iter().enumerate() {
unsafe { dealloc(self.data[index], layout_for(field.dm_type())) }
}
}
}

View File

@ -1,5 +1,5 @@
use crate::vm::dm_type::DmType;
use crate::vm::object::DmAllocObject;
use crate::vm::object::DvmObject;
use std::fmt::Debug;
use std::rc::Rc;
@ -131,14 +131,14 @@ impl DmInterface {
self.virtual_methods.push(Rc::new(dm_virtual_method));
}
pub fn get_method(&self, name: &str, self_object: &DmAllocObject) -> Option<Rc<DmMethod>> {
pub fn get_method(&self, name: &str, self_object: &DvmObject) -> Option<Rc<DmMethod>> {
if self
.virtual_methods
.iter()
.find(|&dm_fn| dm_fn.fqn == name)
.is_some()
{
return self_object.implementation.get_method(name, self_object);
return self_object.implementation().get_method(name, self_object);
}
None
}
@ -147,7 +147,7 @@ impl DmInterface {
self.virtual_methods.clone()
}
pub fn get_property(&self, name: &str, self_object: &DmAllocObject) -> Option<&DmProperty> {
pub fn get_property(&self, name: &str, self_object: &DvmObject) -> Option<&DmProperty> {
todo!()
}
}
@ -196,7 +196,7 @@ impl DmImplementation {
self.interface.clone()
}
pub fn get_method(&self, name: &str, self_object: &DmAllocObject) -> Option<Rc<DmMethod>> {
pub fn get_method(&self, name: &str, self_object: &DvmObject) -> Option<Rc<DmMethod>> {
for method in &self.methods {
if method.dm_fn.fqn == name {
return Some(method.clone());
@ -209,17 +209,13 @@ impl DmImplementation {
None
}
pub fn get_property(&self, name: &str, self_object: &DmAllocObject) -> Option<Rc<DmProperty>> {
pub fn get_property(&self, name: &str, self_object: &DvmObject) -> Option<Rc<DmProperty>> {
todo!()
}
pub fn get_field(&self, name: &str, self_object: &DmAllocObject) -> Option<&DmField> {
pub fn get_field(&self, name: &str, self_object: &DvmObject) -> Option<&DmField> {
self.fields.iter().find(|field| field.name == name)
}
pub fn size_in_bytes(&self) -> usize {
self.fields.iter().map(|field| field.size_in_bytes()).sum()
}
}
#[derive(Debug, Eq)]
@ -239,7 +235,6 @@ impl PartialEq for DmProperty {
pub struct DmField {
name: String,
dm_type: DmType,
data_offset: usize,
}
impl PartialEq for DmField {
@ -249,11 +244,10 @@ impl PartialEq for DmField {
}
impl DmField {
pub fn new(name: &str, dm_type: DmType, data_offset: usize) -> Self {
pub fn new(name: &str, dm_type: DmType) -> Self {
DmField {
name: name.to_string(),
dm_type,
data_offset,
}
}
@ -264,12 +258,4 @@ impl DmField {
pub fn dm_type(&self) -> &DmType {
&self.dm_type
}
pub fn data_offset(&self) -> usize {
self.data_offset
}
pub fn size_in_bytes(&self) -> usize {
self.dm_type.size_in_bytes()
}
}

View File

@ -1,59 +1,39 @@
use crate::push_bytes;
pub const MOV_BYTE: u8 = 0x01;
pub const MOV_INT: u8 = 0x02;
pub const MOV_LONG: u8 = 0x03;
pub const MOV_U_SIZE: u8 = 0x04;
pub const MOV_DOUBLE: u8 = 0x05;
pub const MOV_BOOLEAN: u8 = 0x06;
pub const MOV_REGISTER: u8 = 0x07;
pub const MOV_CONST: u8 = 0x08;
/// ## mov(register: u8, operand: u32)
/// - 0: opcode
/// - 1: register
/// - 2..5: operand
pub const MOV_INT: u8 = 0x01;
pub const LOAD: u8 = 0x10;
pub const STORE: u8 = 0x11;
pub const MOV_LONG: u8 = 0x02;
pub const MOV_DOUBLE: u8 = 0x03;
pub const ALLOC: u8 = 0x20;
pub const DEALLOC: u8 = 0x21;
pub const ALLOC_RAW: u8 = 0x22;
pub const DEALLOC_RAW: u8 = 0x23;
/// ## mov(target_register: u8, source_register: u8)
/// 0: opcode
/// 1: target_register
/// 2: source_register
pub const MOV_REGISTER: u8 = 0x04;
pub const PLATFORM_CALL: u8 = 0x30;
pub const INVOKE_FN: u8 = 0x31;
pub const INVOKE_VIRTUAL: u8 = 0x32;
pub const INVOKE_DYNAMIC: u8 = 0x33;
/// ## alloc(register: u8, size: u32)
/// 0: opcode
/// 1: register
/// 2..5: size
pub const ALLOC: u8 = 0x05;
pub const ADD: u8 = 0x40;
pub const SUBTRACT: u8 = 0x41;
pub const MULTIPLY: u8 = 0x42;
pub const DIVIDE: u8 = 0x43;
pub const MODULO: u8 = 0x44;
pub const POWER: u8 = 0x45;
/// ## dealloc(register: u8)
/// 0: opcode
/// 1: register
pub const DEALLOC: u8 = 0x06;
/// ## mov_int_to(register: u8, offset: u32, operand: u32)
/// 0: opcode
/// 1: register
/// 2..5: offset
/// 6..9: operand
pub const MOV_INT_TO: u8 = 0x07;
pub const MOV_LONG_TO: u8 = 0x08;
pub const MOV_DOUBLE_TO: u8 = 0x09;
pub const MOV_REGISTER_TO: u8 = 0x0a;
pub const MOV_CONST: u8 = 0x0b;
pub const ALLOC_RAW: u8 = 0x0d;
pub const DEALLOC_RAW: u8 = 0x0e;
pub const PLATFORM_CALL: u8 = 0x10;
pub const INVOKE_FN: u8 = 0x11;
pub const INVOKE_VIRTUAL: u8 = 0x12;
pub const INVOKE_DYNAMIC: u8 = 0x14;
pub const MOV_SIZE_OF: u8 = 0x30;
pub const MULTIPLY: u8 = 0x40;
pub const LOAD_OBJECT: u8 = 0x50;
pub const STORE: u8 = 0x60;
macro_rules! push_bytes {
( $dest: expr, $src: expr ) => {
for b in $src {
$dest.push(b);
}
};
}
macro_rules! push_number {
( $dest: expr, $num: expr ) => {
@ -79,10 +59,9 @@ pub fn add_mov_register(code: &mut Vec<u8>, target_register: u8, source_register
code.push(source_register);
}
pub fn add_alloc(code: &mut Vec<u8>, target_register: u8, size: u32, implementation_name: &str) {
pub fn add_alloc(code: &mut Vec<u8>, target_register: u8, implementation_name: &str) {
code.push(ALLOC);
code.push(target_register);
push_number!(code, size);
push_number!(code, implementation_name.len() as u32);
push_string!(code, implementation_name);
}
@ -98,46 +77,17 @@ pub fn add_dealloc(code: &mut Vec<u8>, register: u8) {
code.push(register);
}
pub fn add_mov_int_to(code: &mut Vec<u8>, register: u8, offset: u32, operand: u32) {
code.push(MOV_INT_TO);
code.push(register);
push_number!(code, offset);
push_number!(code, operand);
}
pub fn add_mov_register_to(
code: &mut Vec<u8>,
target_register: u8,
offset: u32,
source_register: u8,
) {
code.push(MOV_REGISTER_TO);
code.push(target_register);
push_number!(code, offset);
code.push(source_register);
}
pub fn add_load_object(
code: &mut Vec<u8>,
target_register: u8,
source_register: u8,
offset: usize
) {
code.push(LOAD_OBJECT);
pub fn add_load(code: &mut Vec<u8>, target_register: u8, source_register: u8, field_index: usize) {
code.push(LOAD);
code.push(target_register);
code.push(source_register);
push_number!(code, offset);
push_number!(code, field_index);
}
pub fn add_store(
code: &mut Vec<u8>,
target_register: u8,
offset: usize,
source_register: u8,
) {
pub fn add_store(code: &mut Vec<u8>, target_register: u8, field_index: usize, source_register: u8) {
code.push(STORE);
code.push(target_register);
push_number!(code, offset);
push_number!(code, field_index);
code.push(source_register);
}
@ -183,12 +133,6 @@ pub fn add_invoke_fn(code: &mut Vec<u8>, fn_name: &str, return_register: u8, arg
}
}
pub fn add_mov_size_of(code: &mut Vec<u8>, target_register: u8, source_register: u8) {
code.push(MOV_SIZE_OF);
code.push(target_register);
code.push(source_register);
}
pub fn add_multiply(
code: &mut Vec<u8>,
target_register: u8,

View File

@ -1,6 +1,6 @@
use crate::vm::dvm_value::DvmValue;
use crate::vm::mem::get_field_value;
use crate::vm::object::DmAllocObject;
use crate::vm::mem::read_field_by_name;
use crate::vm::object::DvmObject;
use crate::vm::{DvmContext, DvmState};
use std::rc::Rc;
@ -8,9 +8,7 @@ pub fn dm_print(args: Vec<DvmValue>, _state: &mut DvmState, _context: &DvmContex
if args.len() != 1 {
return DvmValue::Void; // TODO: make exception
}
unsafe {
print!("{}", get_string(&args[0]));
}
print!("{}", get_string(&args[0]));
DvmValue::Void
}
@ -18,13 +16,11 @@ pub fn dm_println(args: Vec<DvmValue>, _state: &mut DvmState, _context: &DvmCont
if args.len() != 1 {
return DvmValue::Uninit;
}
unsafe {
println!("{}", get_string(&args[0]));
}
println!("{}", get_string(&args[0]));
DvmValue::Void
}
unsafe 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(),
@ -38,35 +34,22 @@ unsafe fn get_string(dvm_value: &DvmValue) -> String {
}
}
fn object_to_string(alloc_object_rc: Rc<DmAllocObject>) -> String {
if alloc_object_rc.implementation.fqn == "std::core::StringImpl" {
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())
} else {
todo!("what happens if we don't have a String?")
}
}
fn extract_string_from_string(string_object: Rc<DmAllocObject>) -> String {
let bytes_field = string_object
.implementation
.get_field("bytes", &string_object)
.expect("Could not get String.bytes field.");
let bytes_object = unsafe { get_field_value(&bytes_field, &string_object) }.expect_object();
if bytes_object.implementation.fqn != "std::core::ArrayImpl" {
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");
}
let ptr_address_field = bytes_object
.implementation
.get_field("ptr_address", &bytes_object)
.expect("Could not get ArrayImpl.ptr_address field.");
let address = unsafe { get_field_value(&ptr_address_field, &bytes_object) }.expect_usize();
let ptr_size_field = bytes_object
.implementation
.get_field("ptr_size", &bytes_object)
.expect("Could not get ArrayImpl.ptr_size field.");
let size = unsafe { get_field_value(&ptr_size_field, &bytes_object) }.expect_usize();
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();

View File

@ -22,15 +22,5 @@ macro_rules! get_64_le {
};
}
#[macro_export]
macro_rules! push_bytes {
( $dest: expr, $src: expr ) => {
for b in $src {
$dest.push(b);
}
};
}
pub use get_32_le;
pub use get_64_le;
pub use push_bytes;