From 46db3b3a37324b9fab96ed8fb03a568db8c24c40 Mon Sep 17 00:00:00 2001 From: Wodann Date: Tue, 21 Apr 2020 11:10:51 +0200 Subject: [PATCH 1/5] refactor: conform RuntimeBuilder to builder pattern --- crates/mun/src/main.rs | 10 ++++++---- crates/mun_runtime/src/lib.rs | 15 ++++++--------- crates/mun_runtime/tests/util.rs | 6 ++++-- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/crates/mun/src/main.rs b/crates/mun/src/main.rs index c2b14b660..50d44ca3a 100644 --- a/crates/mun/src/main.rs +++ b/crates/mun/src/main.rs @@ -169,14 +169,16 @@ fn compiler_options(matches: &ArgMatches) -> Result Result { - let mut builder = RuntimeBuilder::new( + let builder = RuntimeBuilder::new( matches.value_of("LIBRARY").unwrap(), // Safe because its a required arg ); - if let Some(delay) = matches.value_of("delay") { + let builder = if let Some(delay) = matches.value_of("delay") { let delay: u64 = delay.parse()?; - builder.set_delay(Duration::from_millis(delay)); - } + builder.set_delay(Duration::from_millis(delay)) + } else { + builder + }; builder.spawn() } diff --git a/crates/mun_runtime/src/lib.rs b/crates/mun_runtime/src/lib.rs index 17ce98b4a..9d55a36c7 100644 --- a/crates/mun_runtime/src/lib.rs +++ b/crates/mun_runtime/src/lib.rs @@ -64,30 +64,27 @@ pub struct RuntimeBuilder { impl RuntimeBuilder { /// Constructs a new `RuntimeBuilder` for the shared library at `library_path`. pub fn new>(library_path: P) -> Self { - let mut result = Self { + Self { options: RuntimeOptions { library_path: library_path.into(), delay: Duration::from_millis(10), user_functions: Default::default(), }, - }; - - result.insert_fn( + } + .insert_fn( "new", new as extern "C" fn(*const abi::TypeInfo, *mut ffi::c_void) -> *const *mut ffi::c_void, - ); - - result + ) } /// Sets the `delay`. - pub fn set_delay(&mut self, delay: Duration) -> &mut Self { + pub fn set_delay(mut self, delay: Duration) -> Self { self.options.delay = delay; self } /// Adds a custom user function to the dispatch table. - pub fn insert_fn, F: IntoFunctionInfo>(&mut self, name: S, func: F) -> &mut Self { + pub fn insert_fn, F: IntoFunctionInfo>(mut self, name: S, func: F) -> Self { self.options .user_functions .push(func.into(name, abi::Privacy::Public)); diff --git a/crates/mun_runtime/tests/util.rs b/crates/mun_runtime/tests/util.rs index 8f5fcb313..fb5ca33a0 100644 --- a/crates/mun_runtime/tests/util.rs +++ b/crates/mun_runtime/tests/util.rs @@ -90,8 +90,10 @@ impl TestDriver { /// Adds a custom user function to the dispatch table. pub fn insert_fn, F: IntoFunctionInfo>(mut self, name: S, func: F) -> Self { - match &mut self.runtime { - RuntimeOrBuilder::Builder(builder) => builder.insert_fn(name, func), + self.runtime = match self.runtime { + RuntimeOrBuilder::Builder(builder) => { + RuntimeOrBuilder::Builder(builder.insert_fn(name, func)) + } _ => unreachable!(), }; self From 8e91352fa58962db058ed0ce5783be219b117e58 Mon Sep 17 00:00:00 2001 From: Wodann Date: Thu, 23 Apr 2020 15:34:44 +0200 Subject: [PATCH 2/5] refactor: move types from runtime to abi The HasStaticTypeInfo trait and FunctionInfoStorage struct are closely related to the ABI, and multiple crates depend on them. Hence, it was decided to move them up the dependency chain to mun_abi. --- crates/mun_abi/Cargo.toml | 5 ++ .../src/function_info.rs} | 34 ++++++------- crates/mun_abi/src/lib.rs | 7 ++- .../src/static_type_map.rs | 0 .../{mun_runtime => mun_abi}/src/type_info.rs | 51 ++++++++++--------- crates/mun_runtime/Cargo.toml | 3 +- crates/mun_runtime/src/lib.rs | 16 ++---- crates/mun_runtime/src/reflection.rs | 2 +- 8 files changed, 58 insertions(+), 60 deletions(-) rename crates/{mun_runtime/src/function.rs => mun_abi/src/function_info.rs} (72%) rename crates/{mun_runtime => mun_abi}/src/static_type_map.rs (100%) rename crates/{mun_runtime => mun_abi}/src/type_info.rs (81%) diff --git a/crates/mun_abi/Cargo.toml b/crates/mun_abi/Cargo.toml index 3e7363012..94c426629 100644 --- a/crates/mun_abi/Cargo.toml +++ b/crates/mun_abi/Cargo.toml @@ -7,3 +7,8 @@ homepage = "https://mun-lang.org" repository = "https://github.com/mun-lang/mun" license = "MIT OR Apache-2.0" description = "Rust wrapper for the Mun ABI" + +[dependencies] +md5 = "0.7.0" +once_cell = "1.3.1" +parking_lot = "0.10" diff --git a/crates/mun_runtime/src/function.rs b/crates/mun_abi/src/function_info.rs similarity index 72% rename from crates/mun_runtime/src/function.rs rename to crates/mun_abi/src/function_info.rs index 098474713..79d5b49b8 100644 --- a/crates/mun_runtime/src/function.rs +++ b/crates/mun_abi/src/function_info.rs @@ -1,23 +1,23 @@ -use std::ffi::CString; -use std::ptr; - -use crate::type_info::HasStaticTypeInfo; +use crate::{FunctionInfo, FunctionSignature, HasStaticTypeInfo, Privacy, TypeInfo}; +use std::{ffi::CString, ptr}; +/// Owned storage for C-style `FunctionInfo`. pub struct FunctionInfoStorage { _name: CString, - _type_infos: Vec<&'static abi::TypeInfo>, + _type_infos: Vec<&'static TypeInfo>, } impl FunctionInfoStorage { + /// Constructs a new `FunctionInfo`, the data of which is stored in a `FunctionInfoStorage`. pub fn new_function( name: &str, - args: &[&'static abi::TypeInfo], - ret: Option<&'static abi::TypeInfo>, - privacy: abi::Privacy, + args: &[&'static TypeInfo], + ret: Option<&'static TypeInfo>, + privacy: Privacy, fn_ptr: *const std::ffi::c_void, - ) -> (abi::FunctionInfo, FunctionInfoStorage) { + ) -> (FunctionInfo, FunctionInfoStorage) { let name = CString::new(name).unwrap(); - let type_infos: Vec<&'static abi::TypeInfo> = args.iter().copied().collect(); + let type_infos: Vec<&'static TypeInfo> = args.iter().copied().collect(); let num_arg_types = type_infos.len() as u16; let return_type = if let Some(ty) = ret { @@ -26,8 +26,8 @@ impl FunctionInfoStorage { ptr::null() }; - let fn_info = abi::FunctionInfo { - signature: abi::FunctionSignature { + let fn_info = FunctionInfo { + signature: FunctionSignature { name: name.as_ptr(), arg_types: type_infos.as_ptr() as *const *const _, return_type, @@ -49,11 +49,7 @@ impl FunctionInfoStorage { /// A value-to-`FunctionInfo` conversion that consumes the input value. pub trait IntoFunctionInfo { /// Performs the conversion. - fn into>( - self, - name: S, - privacy: abi::Privacy, - ) -> (abi::FunctionInfo, FunctionInfoStorage); + fn into>(self, name: S, privacy: Privacy) -> (FunctionInfo, FunctionInfoStorage); } macro_rules! into_function_info_impl { @@ -64,7 +60,7 @@ macro_rules! into_function_info_impl { impl<$R: HasStaticTypeInfo, $($T: HasStaticTypeInfo,)*> IntoFunctionInfo for extern "C" fn($($T),*) -> $R { - fn into>(self, name: S, privacy: abi::Privacy) -> (abi::FunctionInfo, FunctionInfoStorage) { + fn into>(self, name: S, privacy: Privacy) -> (FunctionInfo, FunctionInfoStorage) { FunctionInfoStorage::new_function( name.as_ref(), &[$($T::type_info(),)*], @@ -78,7 +74,7 @@ macro_rules! into_function_info_impl { impl<$($T: HasStaticTypeInfo,)*> IntoFunctionInfo for extern "C" fn($($T),*) { - fn into>(self, name: S, privacy: abi::Privacy) -> (abi::FunctionInfo, FunctionInfoStorage) { + fn into>(self, name: S, privacy: Privacy) -> (FunctionInfo, FunctionInfoStorage) { FunctionInfoStorage::new_function( name.as_ref(), &[$($T::type_info(),)*], diff --git a/crates/mun_abi/src/lib.rs b/crates/mun_abi/src/lib.rs index 050fc2b3d..f4b4c958a 100644 --- a/crates/mun_abi/src/lib.rs +++ b/crates/mun_abi/src/lib.rs @@ -7,15 +7,20 @@ // Bindings can be manually generated by running `cargo gen-abi`. mod autogen; mod autogen_impl; +mod function_info; +mod static_type_map; +mod type_info; pub use autogen::*; +pub use function_info::{FunctionInfoStorage, IntoFunctionInfo}; +pub use type_info::HasStaticTypeInfo; /// The Mun ABI prelude /// /// The *prelude* contains imports that are used almost every time. pub mod prelude { pub use crate::autogen::*; - pub use crate::{Privacy, StructMemoryKind, TypeGroup}; + pub use crate::{HasStaticTypeInfo, IntoFunctionInfo, Privacy, StructMemoryKind, TypeGroup}; } /// Represents the kind of memory management a struct uses. diff --git a/crates/mun_runtime/src/static_type_map.rs b/crates/mun_abi/src/static_type_map.rs similarity index 100% rename from crates/mun_runtime/src/static_type_map.rs rename to crates/mun_abi/src/static_type_map.rs diff --git a/crates/mun_runtime/src/type_info.rs b/crates/mun_abi/src/type_info.rs similarity index 81% rename from crates/mun_runtime/src/type_info.rs rename to crates/mun_abi/src/type_info.rs index cef4ff4c2..666205595 100644 --- a/crates/mun_runtime/src/type_info.rs +++ b/crates/mun_abi/src/type_info.rs @@ -1,18 +1,18 @@ -use super::static_type_map::StaticTypeMap; +use crate::{static_type_map::StaticTypeMap, Guid, TypeGroup, TypeInfo}; use once_cell::sync::OnceCell; use std::convert::TryInto; use std::ffi::{CStr, CString}; use std::sync::Once; -/// A trait that defines that for a type we can statically return a abi::TypeInfo +/// A trait that defines that for a type we can statically return a `TypeInfo`. pub trait HasStaticTypeInfo { /// Returns a reference to the TypeInfo for the type - fn type_info() -> &'static abi::TypeInfo; + fn type_info() -> &'static TypeInfo; } /// A trait that defines that for a type we can statically return the name that would be used in a -/// abi::TypeInfo. This is useful for opaque types that we do not know the full details of but we -/// could use it as a pointer type +/// `TypeInfo`. This is useful for opaque types that we do not know the full details of but we could +/// use it as a pointer type pub trait HasStaticTypeInfoName { /// Returns the type info name for the type fn type_name() -> &'static CStr; @@ -27,8 +27,8 @@ impl HasStaticTypeInfoName for T { /// Every type that has at least a type name also has a valid pointer type name impl HasStaticTypeInfo for *const T { - fn type_info() -> &'static abi::TypeInfo { - static mut VALUE: Option> = None; + fn type_info() -> &'static TypeInfo { + static mut VALUE: Option> = None; static INIT: Once = Once::new(); let map = unsafe { @@ -41,16 +41,16 @@ impl HasStaticTypeInfo for *const T { &map.call_once::(|| { let name = CString::new(format!("*const {}", T::type_name().to_str().unwrap())).unwrap(); - let guid = abi::Guid { + let guid = Guid { b: md5::compute(&name.as_bytes()).0, }; let name_ptr = name.as_ptr(); ( name, - abi::TypeInfo { + TypeInfo { guid, name: name_ptr, - group: abi::TypeGroup::FundamentalTypes, + group: TypeGroup::FundamentalTypes, size_in_bits: (std::mem::size_of::<*const T>() * 8) .try_into() .expect("size of T is larger than the maximum allowed ABI size. Please file a bug."), @@ -66,8 +66,8 @@ impl HasStaticTypeInfo for *const T { /// Every type that has at least a type name also has a valid pointer type name impl HasStaticTypeInfo for *mut T { - fn type_info() -> &'static abi::TypeInfo { - static mut VALUE: Option> = None; + fn type_info() -> &'static TypeInfo { + static mut VALUE: Option> = None; static INIT: Once = Once::new(); let map = unsafe { @@ -79,16 +79,16 @@ impl HasStaticTypeInfo for *mut T { &map.call_once::(|| { let name = CString::new(format!("*mut {}", T::type_name().to_str().unwrap())).unwrap(); - let guid = abi::Guid { + let guid = Guid { b: md5::compute(&name.as_bytes()).0, }; let name_ptr = name.as_ptr(); ( name, - abi::TypeInfo { + TypeInfo { guid, name: name_ptr, - group: abi::TypeGroup::FundamentalTypes, + group: TypeGroup::FundamentalTypes, size_in_bits: (std::mem::size_of::<*const T>() * 8) .try_into() .expect("size of T is larger than the maximum allowed ABI size. Please file a bug."), @@ -108,17 +108,17 @@ macro_rules! impl_basic_type_info { ),+) => { $( impl HasStaticTypeInfo for $ty { - fn type_info() -> &'static abi::TypeInfo { - static TYPE_INFO: OnceCell = OnceCell::new(); + fn type_info() -> &'static TypeInfo { + static TYPE_INFO: OnceCell = OnceCell::new(); TYPE_INFO.get_or_init(|| { static TYPE_INFO_NAME: OnceCell = OnceCell::new(); let type_info_name: &'static CString = TYPE_INFO_NAME .get_or_init(|| CString::new(format!("core::{}", stringify!($ty))).unwrap()); - abi::TypeInfo { - guid: abi::Guid{ b: md5::compute(&type_info_name.as_bytes()).0 }, + TypeInfo { + guid: Guid{ b: md5::compute(&type_info_name.as_bytes()).0 }, name: type_info_name.as_ptr(), - group: abi::TypeGroup::FundamentalTypes, + group: TypeGroup::FundamentalTypes, size_in_bits: (std::mem::size_of::<$ty>() * 8) .try_into() .expect("size of T is larger than the maximum allowed ABI size. Please file a bug."), @@ -153,33 +153,34 @@ macro_rules! impl_has_type_info_name { impl_basic_type_info!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, f32, f64, bool); impl_has_type_info_name!( - std::ffi::c_void => "core::void" + std::ffi::c_void => "core::void", + TypeInfo => "TypeInfo" ); #[cfg(target_pointer_width = "64")] impl HasStaticTypeInfo for usize { - fn type_info() -> &'static abi::TypeInfo { + fn type_info() -> &'static TypeInfo { u64::type_info() } } #[cfg(target_pointer_width = "64")] impl HasStaticTypeInfo for isize { - fn type_info() -> &'static abi::TypeInfo { + fn type_info() -> &'static TypeInfo { i64::type_info() } } #[cfg(target_pointer_width = "32")] impl HasStaticTypeInfo for usize { - fn type_info() -> &'static abi::TypeInfo { + fn type_info() -> &'static TypeInfo { u32::type_info() } } #[cfg(target_pointer_width = "32")] impl HasStaticTypeInfo for isize { - fn type_info() -> &'static abi::TypeInfo { + fn type_info() -> &'static TypeInfo { i32::type_info() } } diff --git a/crates/mun_runtime/Cargo.toml b/crates/mun_runtime/Cargo.toml index affb129ed..d46442cb9 100644 --- a/crates/mun_runtime/Cargo.toml +++ b/crates/mun_runtime/Cargo.toml @@ -12,12 +12,11 @@ description = "A runtime for hot reloading and invoking Mun from Rust" abi = { path = "../mun_abi", package = "mun_abi" } failure = "0.1.7" libloading = "0.5" -md5= "0.7.0" +md5 = "0.7.0" memory = { path = "../mun_memory", package = "mun_memory" } notify = "4.0.12" parking_lot = "0.10" tempfile = "3" -once_cell = "1.3.1" rustc-hash = "1.1" [dev-dependencies] diff --git a/crates/mun_runtime/src/lib.rs b/crates/mun_runtime/src/lib.rs index 9d55a36c7..f2e063616 100644 --- a/crates/mun_runtime/src/lib.rs +++ b/crates/mun_runtime/src/lib.rs @@ -5,19 +5,15 @@ #![warn(missing_docs)] mod assembly; -mod function; #[macro_use] mod macros; #[macro_use] -mod type_info; mod garbage_collector; mod marshal; mod reflection; -mod static_type_map; mod r#struct; use failure::Error; -use function::FunctionInfoStorage; use garbage_collector::GarbageCollector; use memory::gc::{self, GcRuntime}; use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher}; @@ -36,15 +32,11 @@ use std::{ pub use crate::{ assembly::Assembly, - function::IntoFunctionInfo, marshal::Marshal, r#struct::StructRef, reflection::{ArgumentReflection, ReturnTypeReflection}, }; - -impl_has_type_info_name!( - abi::TypeInfo => "TypeInfo" -); +pub use abi::IntoFunctionInfo; /// Options for the construction of a [`Runtime`]. pub struct RuntimeOptions { @@ -53,7 +45,7 @@ pub struct RuntimeOptions { /// Delay during which filesystem events are collected, deduplicated, and after which emitted. pub delay: Duration, /// Custom user injected functions - pub user_functions: Vec<(abi::FunctionInfo, FunctionInfoStorage)>, + pub user_functions: Vec<(abi::FunctionInfo, abi::FunctionInfoStorage)>, } /// A builder for the [`Runtime`]. @@ -84,7 +76,7 @@ impl RuntimeBuilder { } /// Adds a custom user function to the dispatch table. - pub fn insert_fn, F: IntoFunctionInfo>(mut self, name: S, func: F) -> Self { + pub fn insert_fn, F: abi::IntoFunctionInfo>(mut self, name: S, func: F) -> Self { self.options .user_functions .push(func.into(name, abi::Privacy::Public)); @@ -173,7 +165,7 @@ pub struct Runtime { watcher: RecommendedWatcher, watcher_rx: Receiver, gc: Arc, - _user_functions: Vec, + _user_functions: Vec, } /// Retrieve the allocator using the provided handle. diff --git a/crates/mun_runtime/src/reflection.rs b/crates/mun_runtime/src/reflection.rs index a08734ef7..6f6db7212 100644 --- a/crates/mun_runtime/src/reflection.rs +++ b/crates/mun_runtime/src/reflection.rs @@ -1,5 +1,5 @@ -use crate::type_info::HasStaticTypeInfo; use crate::{marshal::Marshal, Runtime, StructRef}; +use abi::HasStaticTypeInfo; /// Returns whether the specified argument type matches the `type_info`. pub fn equals_argument_type<'r, 'e, 'f, T: ArgumentReflection>( From 233ff0cca050b24eaa18a7297abbda73788168b4 Mon Sep 17 00:00:00 2001 From: Wodann Date: Thu, 23 Apr 2020 15:39:56 +0200 Subject: [PATCH 3/5] refactor(memory): simplify new_fundamental test util --- crates/mun_memory/tests/diff/mod.rs | 4 +- crates/mun_memory/tests/diff/primitives.rs | 16 +++---- crates/mun_memory/tests/diff/structs.rs | 56 +++++++++++----------- crates/mun_memory/tests/diff/util.rs | 15 ++---- 4 files changed, 42 insertions(+), 49 deletions(-) diff --git a/crates/mun_memory/tests/diff/mod.rs b/crates/mun_memory/tests/diff/mod.rs index a845572c9..738e498ee 100644 --- a/crates/mun_memory/tests/diff/mod.rs +++ b/crates/mun_memory/tests/diff/mod.rs @@ -8,8 +8,8 @@ use util::*; #[test] fn add() { - let int = TypeInfo::new_fundamental::(INT_NAME, INT_GUID); - let float = TypeInfo::new_fundamental::(FLOAT_NAME, FLOAT_GUID); + let int = TypeInfo::new_fundamental::(); + let float = TypeInfo::new_fundamental::(); let struct1 = TypeInfo::new_struct( STRUCT1_NAME, STRUCT1_GUID, diff --git a/crates/mun_memory/tests/diff/primitives.rs b/crates/mun_memory/tests/diff/primitives.rs index 09123b93a..5a1cf9f61 100644 --- a/crates/mun_memory/tests/diff/primitives.rs +++ b/crates/mun_memory/tests/diff/primitives.rs @@ -3,8 +3,8 @@ use mun_memory::diff::{diff, Diff}; #[test] fn add() { - let int = TypeInfo::new_fundamental::(INT_NAME, INT_GUID); - let float = TypeInfo::new_fundamental::(FLOAT_NAME, FLOAT_GUID); + let int = TypeInfo::new_fundamental::(); + let float = TypeInfo::new_fundamental::(); let old = &[&int]; let new = &[&int, &float]; @@ -16,8 +16,8 @@ fn add() { #[test] fn remove() { - let int = TypeInfo::new_fundamental::(INT_NAME, INT_GUID); - let float = TypeInfo::new_fundamental::(FLOAT_NAME, FLOAT_GUID); + let int = TypeInfo::new_fundamental::(); + let float = TypeInfo::new_fundamental::(); let old = &[&int, &float]; let new = &[&float]; @@ -29,8 +29,8 @@ fn remove() { #[test] fn replace() { - let int = TypeInfo::new_fundamental::(INT_NAME, INT_GUID); - let float = TypeInfo::new_fundamental::(FLOAT_NAME, FLOAT_GUID); + let int = TypeInfo::new_fundamental::(); + let float = TypeInfo::new_fundamental::(); let old = &[&int]; let new = &[&float]; @@ -45,8 +45,8 @@ fn replace() { #[test] fn swap() { - let int = TypeInfo::new_fundamental::(INT_NAME, INT_GUID); - let float = TypeInfo::new_fundamental::(FLOAT_NAME, FLOAT_GUID); + let int = TypeInfo::new_fundamental::(); + let float = TypeInfo::new_fundamental::(); let old = &[&int, &float]; let new = &[&float, &int]; diff --git a/crates/mun_memory/tests/diff/structs.rs b/crates/mun_memory/tests/diff/structs.rs index 3a2d82906..01d5d5541 100644 --- a/crates/mun_memory/tests/diff/structs.rs +++ b/crates/mun_memory/tests/diff/structs.rs @@ -14,8 +14,8 @@ fn assert_eq_struct(result: &[TypeInfo], expected: &[TypeInfo]) { #[test] fn add() { - let int = TypeInfo::new_fundamental::(INT_NAME, INT_GUID); - let float = TypeInfo::new_fundamental::(FLOAT_NAME, FLOAT_GUID); + let int = TypeInfo::new_fundamental::(); + let float = TypeInfo::new_fundamental::(); let struct1 = TypeInfo::new_struct( STRUCT1_NAME, @@ -41,8 +41,8 @@ fn add() { #[test] fn remove() { - let int = TypeInfo::new_fundamental::(INT_NAME, INT_GUID); - let float = TypeInfo::new_fundamental::(FLOAT_NAME, FLOAT_GUID); + let int = TypeInfo::new_fundamental::(); + let float = TypeInfo::new_fundamental::(); let struct1 = TypeInfo::new_struct( STRUCT1_NAME, @@ -65,8 +65,8 @@ fn remove() { #[test] fn replace() { - let int = TypeInfo::new_fundamental::(INT_NAME, INT_GUID); - let float = TypeInfo::new_fundamental::(FLOAT_NAME, FLOAT_GUID); + let int = TypeInfo::new_fundamental::(); + let float = TypeInfo::new_fundamental::(); let struct1 = TypeInfo::new_struct( STRUCT1_NAME, @@ -92,8 +92,8 @@ fn replace() { #[test] fn swap() { - let int = TypeInfo::new_fundamental::(INT_NAME, INT_GUID); - let float = TypeInfo::new_fundamental::(FLOAT_NAME, FLOAT_GUID); + let int = TypeInfo::new_fundamental::(); + let float = TypeInfo::new_fundamental::(); let struct1 = TypeInfo::new_struct( STRUCT1_NAME, @@ -125,8 +125,8 @@ fn swap() { #[test] fn add_field1() { - let int = TypeInfo::new_fundamental::(INT_NAME, INT_GUID); - let float = TypeInfo::new_fundamental::(FLOAT_NAME, FLOAT_GUID); + let int = TypeInfo::new_fundamental::(); + let float = TypeInfo::new_fundamental::(); let struct1 = TypeInfo::new_struct( STRUCT1_NAME, @@ -156,8 +156,8 @@ fn add_field1() { #[test] fn add_field2() { - let int = TypeInfo::new_fundamental::(INT_NAME, INT_GUID); - let float = TypeInfo::new_fundamental::(FLOAT_NAME, FLOAT_GUID); + let int = TypeInfo::new_fundamental::(); + let float = TypeInfo::new_fundamental::(); let struct1 = TypeInfo::new_struct( STRUCT1_NAME, @@ -187,8 +187,8 @@ fn add_field2() { #[test] fn add_field3() { - let int = TypeInfo::new_fundamental::(INT_NAME, INT_GUID); - let float = TypeInfo::new_fundamental::(FLOAT_NAME, FLOAT_GUID); + let int = TypeInfo::new_fundamental::(); + let float = TypeInfo::new_fundamental::(); let struct1 = TypeInfo::new_struct( STRUCT1_NAME, @@ -218,8 +218,8 @@ fn add_field3() { #[test] fn remove_field1() { - let int = TypeInfo::new_fundamental::(INT_NAME, INT_GUID); - let float = TypeInfo::new_fundamental::(FLOAT_NAME, FLOAT_GUID); + let int = TypeInfo::new_fundamental::(); + let float = TypeInfo::new_fundamental::(); let struct1 = TypeInfo::new_struct( STRUCT1_NAME, @@ -248,8 +248,8 @@ fn remove_field1() { #[test] fn remove_field2() { - let int = TypeInfo::new_fundamental::(INT_NAME, INT_GUID); - let float = TypeInfo::new_fundamental::(FLOAT_NAME, FLOAT_GUID); + let int = TypeInfo::new_fundamental::(); + let float = TypeInfo::new_fundamental::(); let struct1 = TypeInfo::new_struct( STRUCT1_NAME, @@ -278,8 +278,8 @@ fn remove_field2() { #[test] fn remove_field3() { - let int = TypeInfo::new_fundamental::(INT_NAME, INT_GUID); - let float = TypeInfo::new_fundamental::(FLOAT_NAME, FLOAT_GUID); + let int = TypeInfo::new_fundamental::(); + let float = TypeInfo::new_fundamental::(); let struct1 = TypeInfo::new_struct( STRUCT1_NAME, @@ -308,8 +308,8 @@ fn remove_field3() { #[test] fn swap_fields() { - let int = TypeInfo::new_fundamental::(INT_NAME, INT_GUID); - let float = TypeInfo::new_fundamental::(FLOAT_NAME, FLOAT_GUID); + let int = TypeInfo::new_fundamental::(); + let float = TypeInfo::new_fundamental::(); let struct1 = TypeInfo::new_struct( STRUCT1_NAME, @@ -343,8 +343,8 @@ fn swap_fields() { #[test] fn swap_fields2() { - let int = TypeInfo::new_fundamental::(INT_NAME, INT_GUID); - let float = TypeInfo::new_fundamental::(FLOAT_NAME, FLOAT_GUID); + let int = TypeInfo::new_fundamental::(); + let float = TypeInfo::new_fundamental::(); let struct1 = TypeInfo::new_struct( STRUCT1_NAME, @@ -390,8 +390,8 @@ fn swap_fields2() { #[test] fn rename_field1() { - let int = TypeInfo::new_fundamental::(INT_NAME, INT_GUID); - let float = TypeInfo::new_fundamental::(FLOAT_NAME, FLOAT_GUID); + let int = TypeInfo::new_fundamental::(); + let float = TypeInfo::new_fundamental::(); let struct1 = TypeInfo::new_struct( STRUCT1_NAME, @@ -424,8 +424,8 @@ fn rename_field1() { #[test] fn rename_field2() { - let int = TypeInfo::new_fundamental::(INT_NAME, INT_GUID); - let float = TypeInfo::new_fundamental::(FLOAT_NAME, FLOAT_GUID); + let int = TypeInfo::new_fundamental::(); + let float = TypeInfo::new_fundamental::(); let struct1 = TypeInfo::new_struct( STRUCT1_NAME, diff --git a/crates/mun_memory/tests/diff/util.rs b/crates/mun_memory/tests/diff/util.rs index 4c9eacc2a..030df3652 100644 --- a/crates/mun_memory/tests/diff/util.rs +++ b/crates/mun_memory/tests/diff/util.rs @@ -5,14 +5,6 @@ use mun_memory::{ }; use std::alloc::Layout; -pub const INT_NAME: &str = "int"; -pub const INT_GUID: abi::Guid = abi::Guid { - b: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], -}; -pub const FLOAT_NAME: &str = "float"; -pub const FLOAT_GUID: abi::Guid = abi::Guid { - b: [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], -}; pub const STRUCT1_NAME: &str = "struct1"; pub const STRUCT1_GUID: abi::Guid = abi::Guid { b: [ @@ -70,10 +62,11 @@ pub struct TypeInfo { } impl TypeInfo { - pub fn new_fundamental(name: &str, guid: abi::Guid) -> Self { + pub fn new_fundamental() -> Self { + let type_info = T::type_info(); Self { - name: name.to_string(), - guid, + name: type_info.name().to_string(), + guid: type_info.guid, group: abi::TypeGroup::FundamentalTypes, layout: Layout::new::(), tail: TypeInfoTail::Empty, From 62aa7e4e4a4e04911cbf1a187fca474a65fd8563 Mon Sep 17 00:00:00 2001 From: Wodann Date: Thu, 23 Apr 2020 15:42:54 +0200 Subject: [PATCH 4/5] feat: add casting of fundamental types to memory mapping --- crates/mun_memory/Cargo.toml | 1 + crates/mun_memory/src/cast.rs | 250 ++++++++++++++++++++++++ crates/mun_memory/src/gc/mark_sweep.rs | 24 ++- crates/mun_memory/src/lib.rs | 1 + crates/mun_memory/tests/diff/structs.rs | 44 +++++ crates/mun_memory/tests/diff/util.rs | 6 +- crates/mun_runtime/tests/memory.rs | 70 +++++++ 7 files changed, 385 insertions(+), 11 deletions(-) create mode 100644 crates/mun_memory/src/cast.rs diff --git a/crates/mun_memory/Cargo.toml b/crates/mun_memory/Cargo.toml index eed4796f8..08da8940d 100644 --- a/crates/mun_memory/Cargo.toml +++ b/crates/mun_memory/Cargo.toml @@ -10,6 +10,7 @@ edition = "2018" abi = { path = "../mun_abi", package = "mun_abi" } once_cell = "1.3.1" parking_lot = "0.10" +lazy_static = "1.4.0" [dev-dependencies] paste = "0.1" diff --git a/crates/mun_memory/src/cast.rs b/crates/mun_memory/src/cast.rs new file mode 100644 index 000000000..47c0a2c7e --- /dev/null +++ b/crates/mun_memory/src/cast.rs @@ -0,0 +1,250 @@ +use abi::HasStaticTypeInfo; +use lazy_static::lazy_static; +use std::{collections::HashMap, ptr::NonNull}; + +type CastFn = fn(NonNull, NonNull); + +macro_rules! insert_cast_fn { + { $table:ident, $A:ty, $B:ty } => { + $table.insert( + (<$A>::type_info().guid, <$B>::type_info().guid), + cast_from_to::<$A, $B> as CastFn, + ) + } +} + +lazy_static! { + static ref CAST_FN_TABLE: HashMap<(abi::Guid, abi::Guid), CastFn> = { + let mut table = HashMap::new(); + insert_cast_fn!(table, f32, f64); + insert_cast_fn!(table, i8, i16); + insert_cast_fn!(table, i8, i32); + insert_cast_fn!(table, i8, i64); + insert_cast_fn!(table, i8, i128); + insert_cast_fn!(table, i16, i32); + insert_cast_fn!(table, i16, i64); + insert_cast_fn!(table, i16, i128); + insert_cast_fn!(table, i32, i64); + insert_cast_fn!(table, i32, i128); + insert_cast_fn!(table, i64, i128); + insert_cast_fn!(table, u8, i16); + insert_cast_fn!(table, u8, u16); + insert_cast_fn!(table, u8, i32); + insert_cast_fn!(table, u8, u32); + insert_cast_fn!(table, u8, i64); + insert_cast_fn!(table, u8, u64); + insert_cast_fn!(table, u8, i128); + insert_cast_fn!(table, u8, u128); + insert_cast_fn!(table, u16, i32); + insert_cast_fn!(table, u16, u32); + insert_cast_fn!(table, u16, i64); + insert_cast_fn!(table, u16, u64); + insert_cast_fn!(table, u16, i128); + insert_cast_fn!(table, u16, u128); + insert_cast_fn!(table, u32, i64); + insert_cast_fn!(table, u32, u64); + insert_cast_fn!(table, u32, i128); + insert_cast_fn!(table, u32, u128); + insert_cast_fn!(table, u64, i128); + insert_cast_fn!(table, u64, u128); + table + }; +} + +fn cast_from_to(src: NonNull, dest: NonNull) +where + A: Copy + Into, +{ + let value = unsafe { *src.cast::().as_ref() }; + unsafe { *dest.cast::().as_mut() = value.into() }; +} + +pub fn try_cast_from_to( + old_guid: abi::Guid, + new_guid: abi::Guid, + src: NonNull, + dest: NonNull, +) -> bool { + if let Some(cast_fn) = CAST_FN_TABLE.get(&(old_guid, new_guid)) { + cast_fn(src, dest); + true + } else { + false + } +} + +#[cfg(test)] +mod tests { + use super::try_cast_from_to; + use abi::HasStaticTypeInfo; + use std::ptr::NonNull; + + fn assert_cast(a: A, mut b: B) + where + A: Copy + Into + HasStaticTypeInfo, + B: PartialEq + std::fmt::Debug + HasStaticTypeInfo, + { + assert!(try_cast_from_to( + A::type_info().guid, + B::type_info().guid, + unsafe { NonNull::new_unchecked(&a as *const _ as *mut _) }, + unsafe { NonNull::new_unchecked(&mut b as *mut _) }.cast::(), + )); + assert_eq!(b, a.into()); + } + + #[test] + fn cast_f32_to_f64() { + assert_cast(3.14f32, 0f64); + } + + #[test] + fn cast_i8_to_i16() { + assert_cast(-5i8, 0i16); + } + + #[test] + fn cast_i8_to_i32() { + assert_cast(-5i8, 0i32); + } + + #[test] + fn cast_i8_to_i64() { + assert_cast(-5i8, 0i64); + } + + #[test] + fn cast_i8_to_i128() { + assert_cast(-5i8, 0i128); + } + + #[test] + fn cast_i16_to_i32() { + assert_cast(-5i16, 0i32); + } + + #[test] + fn cast_i16_to_i64() { + assert_cast(-5i16, 0i64); + } + + #[test] + fn cast_i16_to_i128() { + assert_cast(-5i16, 0i128); + } + + #[test] + fn cast_i32_to_i64() { + assert_cast(-5i32, 0i64); + } + + #[test] + fn cast_i32_to_i128() { + assert_cast(-5i32, 0i128); + } + + #[test] + fn cast_i64_to_i128() { + assert_cast(-5i64, 0i128); + } + + #[test] + fn cast_u8_to_i16() { + assert_cast(5u8, 0i16); + } + + #[test] + fn cast_u8_to_u16() { + assert_cast(5u8, 0u16); + } + + #[test] + fn cast_u8_to_i32() { + assert_cast(5u8, 0i32); + } + + #[test] + fn cast_u8_to_u32() { + assert_cast(5u8, 0u32); + } + + #[test] + fn cast_u8_to_i64() { + assert_cast(5u8, 0i64); + } + + #[test] + fn cast_u8_to_u64() { + assert_cast(5u8, 0u64); + } + + #[test] + fn cast_u8_to_i128() { + assert_cast(5u8, 0i128); + } + + #[test] + fn cast_u8_to_u128() { + assert_cast(5u8, 0u128); + } + + #[test] + fn cast_u16_to_i32() { + assert_cast(5u16, 0i32); + } + + #[test] + fn cast_u16_to_u32() { + assert_cast(5u16, 0u32); + } + + #[test] + fn cast_u16_to_i64() { + assert_cast(5u16, 0i64); + } + + #[test] + fn cast_u16_to_u64() { + assert_cast(5u16, 0u64); + } + + #[test] + fn cast_u16_to_i128() { + assert_cast(5u16, 0i128); + } + + #[test] + fn cast_u16_to_u128() { + assert_cast(5u16, 0u128); + } + + #[test] + fn cast_u32_to_i64() { + assert_cast(5u32, 0i64); + } + + #[test] + fn cast_u32_to_u64() { + assert_cast(5u32, 0u64); + } + + #[test] + fn cast_u32_to_i128() { + assert_cast(5u32, 0i128); + } + + #[test] + fn cast_u32_to_u128() { + assert_cast(5u32, 0u128); + } + + #[test] + fn cast_u64_to_i128() { + assert_cast(5u64, 0i128); + } + + #[test] + fn cast_u64_to_u128() { + assert_cast(5u64, 0u128); + } +} diff --git a/crates/mun_memory/src/gc/mark_sweep.rs b/crates/mun_memory/src/gc/mark_sweep.rs index a518be2b5..b98c1add2 100644 --- a/crates/mun_memory/src/gc/mark_sweep.rs +++ b/crates/mun_memory/src/gc/mark_sweep.rs @@ -1,8 +1,9 @@ use crate::{ + cast, diff::{Diff, FieldDiff}, gc::{Event, GcPtr, GcRuntime, Observer, RawGcPtr, Stats, TypeTrace}, mapping::{self, field_mapping, FieldMappingDesc, MemoryMapper}, - TypeFields, TypeLayout, + TypeDesc, TypeFields, TypeLayout, }; use once_cell::unsync::OnceCell; use parking_lot::RwLock; @@ -11,6 +12,7 @@ use std::{ hash::Hash, ops::Deref, pin::Pin, + ptr::NonNull, }; /// Implements a simple mark-sweep type garbage collector. @@ -198,7 +200,7 @@ where impl MemoryMapper for MarkSweep where - T: TypeLayout + TypeFields + TypeTrace + Clone + Eq + Hash, + T: TypeDesc + TypeLayout + TypeFields + TypeTrace + Clone + Eq + Hash, O: Observer, { fn map_memory(&self, old: &[T], new: &[T], diff: &[Diff]) -> Vec { @@ -256,12 +258,12 @@ where struct TypeData<'a, T> { old_fields: Vec<(&'a str, T)>, - _new_fields: Vec<(&'a str, T)>, + new_fields: Vec<(&'a str, T)>, old_offsets: &'a [u16], new_offsets: &'a [u16], } - fn map_fields<'a, T: TypeLayout + TypeFields + TypeTrace + Clone + Eq>( + fn map_fields<'a, T: TypeDesc + TypeLayout + TypeFields + TypeTrace + Clone + Eq>( object_info: &mut Pin>>, old_ty: &'a T, new_ty: &'a T, @@ -270,12 +272,12 @@ where ) { let TypeData { old_fields, - _new_fields, + new_fields, old_offsets, new_offsets, } = type_data.get_or_init(|| TypeData { old_fields: old_ty.fields(), - _new_fields: new_ty.fields(), + new_fields: new_ty.fields(), old_offsets: old_ty.offsets(), new_offsets: new_ty.offsets(), }); @@ -296,7 +298,15 @@ where }; let old_field = unsafe { old_fields.get_unchecked(old_index) }; if action == mapping::Action::Cast { - panic!("The Mun Runtime doesn't currently support casting"); + let new_field = unsafe { new_fields.get_unchecked(new_index) }; + if !cast::try_cast_from_to( + *old_field.1.guid(), + *new_field.1.guid(), + unsafe { NonNull::new_unchecked(src) }, + unsafe { NonNull::new_unchecked(dest) }, + ) { + panic!("The Mun Runtime doesn't currently support casting from '{}' to '{}'", old_field.1.name(), new_field.1.name()); + } } else { unsafe { std::ptr::copy_nonoverlapping(src, dest, old_field.1.layout().size()) diff --git a/crates/mun_memory/src/lib.rs b/crates/mun_memory/src/lib.rs index a5628f531..b076a6121 100644 --- a/crates/mun_memory/src/lib.rs +++ b/crates/mun_memory/src/lib.rs @@ -1,5 +1,6 @@ use std::alloc::Layout; +mod cast; pub mod diff; pub mod gc; pub mod mapping; diff --git a/crates/mun_memory/tests/diff/structs.rs b/crates/mun_memory/tests/diff/structs.rs index 01d5d5541..3f2774c2a 100644 --- a/crates/mun_memory/tests/diff/structs.rs +++ b/crates/mun_memory/tests/diff/structs.rs @@ -388,6 +388,50 @@ fn swap_fields2() { assert_eq_struct(&apply_diff(old, new, diff), &vec![struct2.clone()]); } +#[test] +fn cast_field() { + let int = TypeInfo::new_fundamental::(); + let float = TypeInfo::new_fundamental::(); + + let struct1 = TypeInfo::new_struct( + STRUCT1_NAME, + STRUCT1_GUID, + StructInfo::new(&[("a", &int), ("b", &float), ("c", &float)]), + ); + let struct2 = TypeInfo::new_struct( + STRUCT1_NAME, + STRUCT2_GUID, + StructInfo::new(&[("a", &float), ("b", &int), ("c", &int)]), + ); + + let old = &[&struct1]; + let new = &[&struct2]; + + let diff = diff(old, new); + assert_eq!( + diff, + vec![Diff::Edit { + diff: vec![ + FieldDiff::Edit { + index: 0, + kind: FieldEditKind::ConvertType, + }, + FieldDiff::Edit { + index: 1, + kind: FieldEditKind::ConvertType, + }, + FieldDiff::Edit { + index: 2, + kind: FieldEditKind::ConvertType, + } + ], + old_index: 0, + new_index: 0, + }] + ); + assert_eq_struct(&apply_diff(old, new, diff), &vec![struct2.clone()]); +} + #[test] fn rename_field1() { let int = TypeInfo::new_fundamental::(); diff --git a/crates/mun_memory/tests/diff/util.rs b/crates/mun_memory/tests/diff/util.rs index 030df3652..7c9375eb3 100644 --- a/crates/mun_memory/tests/diff/util.rs +++ b/crates/mun_memory/tests/diff/util.rs @@ -247,10 +247,8 @@ fn apply_mapping<'t>(old: &mut TypeInfo, new: &TypeInfo, mapping: &[FieldDiff]) new_field: &(String, TypeInfo), ) { match *kind { - FieldEditKind::ConvertType => panic!("Casting is currently not supported"), - FieldEditKind::Rename => { - old_field.0 = new_field.0.clone(); - } + FieldEditKind::ConvertType => old_field.1 = new_field.1.clone(), + FieldEditKind::Rename => old_field.0 = new_field.0.clone(), } } diff --git a/crates/mun_runtime/tests/memory.rs b/crates/mun_runtime/tests/memory.rs index 67233a4bb..27443cde0 100644 --- a/crates/mun_runtime/tests/memory.rs +++ b/crates/mun_runtime/tests/memory.rs @@ -232,6 +232,76 @@ fn map_struct_remove_field3() { assert_eq!(foo.get::("a").unwrap(), a); } +#[test] +fn map_struct_cast_fields1() { + let mut driver = TestDriver::new( + r#" + struct Foo( + u8, + i16, + u32, + i64, + f32, + ) + + pub fn foo_new(a: u8, b: i16, c: u32, d: i64, e: f32) -> Foo { + Foo(a, b, c, d, e) + } + "#, + ); + + let a = 1u8; + let b = -2i16; + let c = 3u32; + let d = -4i64; + let e = 3.14f32; + let foo: StructRef = invoke_fn!(driver.runtime_mut(), "foo_new", a, b, c, d, e).unwrap(); + + driver.update( + r#" + struct Foo( + u16, + i32, + u64, + i128, + f64, + ) + "#, + ); + assert_eq!(foo.get::("0").unwrap(), a.into()); + assert_eq!(foo.get::("1").unwrap(), b.into()); + assert_eq!(foo.get::("2").unwrap(), c.into()); + assert_eq!(foo.get::("3").unwrap(), d.into()); + assert_eq!(foo.get::("4").unwrap(), e.into()); +} + +#[test] +#[should_panic] +fn map_struct_cast_fields2() { + let mut driver = TestDriver::new( + r#" + struct Foo( + i16, + ) + + pub fn foo_new(a: i16) -> Foo { + Foo(a) + } + "#, + ); + + let a = -2i16; + let _foo: StructRef = invoke_fn!(driver.runtime_mut(), "foo_new", a).unwrap(); + + driver.update( + r#" + struct Foo( + u16, // Cannot convert from `i16` to `u16` + ) + "#, + ); +} + #[test] fn map_struct_swap_fields1() { let mut driver = TestDriver::new( From 6b4c47d7d26e5ebf61c2ac0c2138e46ed9d3c474 Mon Sep 17 00:00:00 2001 From: Wodann Date: Tue, 28 Apr 2020 13:14:19 +0200 Subject: [PATCH 5/5] feat(memory): zero initialize upon failed cast --- crates/mun_memory/src/gc/mark_sweep.rs | 2 +- crates/mun_runtime/tests/memory.rs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/mun_memory/src/gc/mark_sweep.rs b/crates/mun_memory/src/gc/mark_sweep.rs index b98c1add2..9df89a567 100644 --- a/crates/mun_memory/src/gc/mark_sweep.rs +++ b/crates/mun_memory/src/gc/mark_sweep.rs @@ -305,7 +305,7 @@ where unsafe { NonNull::new_unchecked(src) }, unsafe { NonNull::new_unchecked(dest) }, ) { - panic!("The Mun Runtime doesn't currently support casting from '{}' to '{}'", old_field.1.name(), new_field.1.name()); + // Failed to cast. Use the previously zero-initialized value instead } } else { unsafe { diff --git a/crates/mun_runtime/tests/memory.rs b/crates/mun_runtime/tests/memory.rs index 27443cde0..2f7ac05d2 100644 --- a/crates/mun_runtime/tests/memory.rs +++ b/crates/mun_runtime/tests/memory.rs @@ -276,7 +276,6 @@ fn map_struct_cast_fields1() { } #[test] -#[should_panic] fn map_struct_cast_fields2() { let mut driver = TestDriver::new( r#" @@ -291,7 +290,7 @@ fn map_struct_cast_fields2() { ); let a = -2i16; - let _foo: StructRef = invoke_fn!(driver.runtime_mut(), "foo_new", a).unwrap(); + let foo: StructRef = invoke_fn!(driver.runtime_mut(), "foo_new", a).unwrap(); driver.update( r#" @@ -300,6 +299,8 @@ fn map_struct_cast_fields2() { ) "#, ); + + assert_eq!(foo.get::("0").unwrap(), 0); } #[test]