diff --git a/crates/mun/src/main.rs b/crates/mun/src/main.rs index 931435f05..3266db3dc 100644 --- a/crates/mun/src/main.rs +++ b/crates/mun/src/main.rs @@ -4,9 +4,8 @@ extern crate failure; use std::time::Duration; use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; -use mun_abi::Reflection; use mun_compiler::{host_triple, Config, PathOrInline, Target}; -use mun_runtime::{invoke_fn, Runtime, RuntimeBuilder}; +use mun_runtime::{invoke_fn, ReturnTypeReflection, Runtime, RuntimeBuilder}; fn main() -> Result<(), failure::Error> { let matches = App::new("mun") diff --git a/crates/mun_abi/Cargo.toml b/crates/mun_abi/Cargo.toml index 8df84dc31..3e7363012 100644 --- a/crates/mun_abi/Cargo.toml +++ b/crates/mun_abi/Cargo.toml @@ -7,6 +7,3 @@ 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.6.1" diff --git a/crates/mun_abi/c b/crates/mun_abi/c index 420fc752f..fbcc74d0e 160000 --- a/crates/mun_abi/c +++ b/crates/mun_abi/c @@ -1 +1 @@ -Subproject commit 420fc752f88626cd21bc6ac232afb522402060b8 +Subproject commit fbcc74d0e4971c82955a7959d9dc5fda8208e578 diff --git a/crates/mun_abi/src/autogen.rs b/crates/mun_abi/src/autogen.rs index 10d58b52e..1e8522ad5 100644 --- a/crates/mun_abi/src/autogen.rs +++ b/crates/mun_abi/src/autogen.rs @@ -3,7 +3,7 @@ /* automatically generated by rust-bindgen */ #![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)] -use crate::Privacy; +use crate::{Privacy, TypeGroup}; #[doc = " Represents a globally unique identifier (GUID)."] #[doc = ""] @@ -46,12 +46,14 @@ pub struct TypeInfo { pub guid: Guid, #[doc = " Type name"] pub name: *const ::std::os::raw::c_char, + #[doc = " Type group"] + pub group: TypeGroup, } #[test] fn bindgen_test_layout_TypeInfo() { assert_eq!( ::std::mem::size_of::(), - 24usize, + 32usize, concat!("Size of: ", stringify!(TypeInfo)) ); assert_eq!( @@ -79,6 +81,16 @@ fn bindgen_test_layout_TypeInfo() { stringify!(name) ) ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).group as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(TypeInfo), + "::", + stringify!(group) + ) + ); } #[doc = " Represents a function signature."] #[doc = ""] @@ -214,8 +226,14 @@ fn bindgen_test_layout_FunctionInfo() { pub struct StructInfo { #[doc = " Struct name"] pub name: *const ::std::os::raw::c_char, + #[doc = " Struct fields' names"] + pub field_names: *const *const ::std::os::raw::c_char, #[doc = " Struct fields' information"] pub field_types: *const TypeInfo, + #[doc = " Struct fields' offsets"] + pub field_offsets: *const u16, + #[doc = " Struct fields' sizes (in bytes)"] + pub field_sizes: *const u16, #[doc = " Number of fields"] pub num_fields: u16, } @@ -223,7 +241,7 @@ pub struct StructInfo { fn bindgen_test_layout_StructInfo() { assert_eq!( ::std::mem::size_of::(), - 24usize, + 48usize, concat!("Size of: ", stringify!(StructInfo)) ); assert_eq!( @@ -242,8 +260,18 @@ fn bindgen_test_layout_StructInfo() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).field_types as *const _ as usize }, + unsafe { &(*(::std::ptr::null::())).field_names as *const _ as usize }, 8usize, + concat!( + "Offset of field: ", + stringify!(StructInfo), + "::", + stringify!(field_names) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).field_types as *const _ as usize }, + 16usize, concat!( "Offset of field: ", stringify!(StructInfo), @@ -251,9 +279,29 @@ fn bindgen_test_layout_StructInfo() { stringify!(field_types) ) ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).field_offsets as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(StructInfo), + "::", + stringify!(field_offsets) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).field_sizes as *const _ as usize }, + 32usize, + concat!( + "Offset of field: ", + stringify!(StructInfo), + "::", + stringify!(field_sizes) + ) + ); assert_eq!( unsafe { &(*(::std::ptr::null::())).num_fields as *const _ as usize }, - 16usize, + 40usize, concat!( "Offset of field: ", stringify!(StructInfo), diff --git a/crates/mun_abi/src/autogen_impl.rs b/crates/mun_abi/src/autogen_impl.rs index 4223e464d..252b671d7 100644 --- a/crates/mun_abi/src/autogen_impl.rs +++ b/crates/mun_abi/src/autogen_impl.rs @@ -80,12 +80,23 @@ unsafe impl Sync for FunctionInfo {} impl StructInfo { /// Returns the struct's name. pub fn name(&self) -> &str { - unsafe { CStr::from_ptr(self.name) } - .to_str() - .expect("Function name contains invalid UTF8") + unsafe { str::from_utf8_unchecked(CStr::from_ptr(self.name).to_bytes()) } + } + + /// Returns the struct's field names. + pub fn field_names(&self) -> impl Iterator { + let field_names = if self.num_fields == 0 { + &[] + } else { + unsafe { slice::from_raw_parts(self.field_names, self.num_fields as usize) } + }; + + field_names + .iter() + .map(|n| unsafe { str::from_utf8_unchecked(CStr::from_ptr(*n).to_bytes()) }) } - /// Returns the struct's fields' types. + /// Returns the struct's field types. pub fn field_types(&self) -> &[TypeInfo] { if self.num_fields == 0 { &[] @@ -93,6 +104,24 @@ impl StructInfo { unsafe { slice::from_raw_parts(self.field_types, self.num_fields as usize) } } } + + /// Returns the struct's field offsets. + pub fn field_offsets(&self) -> &[u16] { + if self.num_fields == 0 { + &[] + } else { + unsafe { slice::from_raw_parts(self.field_offsets, self.num_fields as usize) } + } + } + + /// Returns the struct's field sizes. + pub fn field_sizes(&self) -> &[u16] { + if self.num_fields == 0 { + &[] + } else { + unsafe { slice::from_raw_parts(self.field_sizes, self.num_fields as usize) } + } + } } impl fmt::Display for StructInfo { @@ -248,10 +277,11 @@ mod tests { use std::os::raw::c_char; use std::ptr; - fn fake_type_info(name: &CStr) -> TypeInfo { + fn fake_type_info(name: &CStr, group: TypeGroup) -> TypeInfo { TypeInfo { guid: FAKE_TYPE_GUID, name: name.as_ptr(), + group, } } @@ -260,11 +290,12 @@ mod tests { }; const FAKE_TYPE_NAME: &str = "type-name"; + const FAKE_FIELD_NAME: &str = "field-name"; #[test] fn test_type_info_name() { let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name."); - let type_info = fake_type_info(&type_name); + let type_info = fake_type_info(&type_name, TypeGroup::FundamentalTypes); assert_eq!(type_info.name(), FAKE_TYPE_NAME); } @@ -272,7 +303,7 @@ mod tests { #[test] fn test_type_info_eq() { let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name."); - let type_info = fake_type_info(&type_name); + let type_info = fake_type_info(&type_name, TypeGroup::FundamentalTypes); assert_eq!(type_info, type_info); } @@ -323,7 +354,7 @@ mod tests { #[test] fn test_fn_signature_arg_types_some() { let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name."); - let type_info = fake_type_info(&type_name); + let type_info = fake_type_info(&type_name, TypeGroup::FundamentalTypes); let arg_types = &[type_info]; let fn_name = CString::new(FAKE_FN_NAME).expect("Invalid fake fn name."); @@ -344,7 +375,7 @@ mod tests { #[test] fn test_fn_signature_return_type_some() { let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name."); - let type_info = fake_type_info(&type_name); + let type_info = fake_type_info(&type_name, TypeGroup::FundamentalTypes); let return_type = Some(&type_info); let fn_name = CString::new(FAKE_FN_NAME).expect("Invalid fake fn name."); @@ -353,11 +384,24 @@ mod tests { assert_eq!(fn_signature.return_type(), return_type); } - fn fake_struct_info(name: &CStr, field_types: &[TypeInfo]) -> StructInfo { + fn fake_struct_info( + name: &CStr, + field_names: &[*const c_char], + field_types: &[TypeInfo], + field_offsets: &[u16], + field_sizes: &[u16], + ) -> StructInfo { + assert!(field_names.len() == field_types.len()); + assert!(field_types.len() == field_offsets.len()); + assert!(field_offsets.len() == field_sizes.len()); + StructInfo { name: name.as_ptr(), + field_names: field_names.as_ptr(), field_types: field_types.as_ptr(), - num_fields: field_types.len() as u16, + field_offsets: field_offsets.as_ptr(), + field_sizes: field_sizes.as_ptr(), + num_fields: field_names.len() as u16, } } @@ -366,30 +410,57 @@ mod tests { #[test] fn test_struct_info_name() { let struct_name = CString::new(FAKE_STRUCT_NAME).expect("Invalid fake struct name."); - let struct_info = fake_struct_info(&struct_name, &[]); + let struct_info = fake_struct_info(&struct_name, &[], &[], &[], &[]); assert_eq!(struct_info.name(), FAKE_STRUCT_NAME); } #[test] - fn test_struct_info_field_types_none() { + fn test_struct_info_fields_none() { + let field_names = &[]; let field_types = &[]; + let field_offsets = &[]; + let field_sizes = &[]; let struct_name = CString::new(FAKE_STRUCT_NAME).expect("Invalid fake struct name."); - let struct_info = fake_struct_info(&struct_name, field_types); + let struct_info = fake_struct_info( + &struct_name, + field_names, + field_types, + field_offsets, + field_sizes, + ); + assert_eq!(struct_info.field_names().count(), 0); assert_eq!(struct_info.field_types(), field_types); + assert_eq!(struct_info.field_offsets(), field_offsets); + assert_eq!(struct_info.field_sizes(), field_sizes); } #[test] - fn test_struct_info_field_types_some() { + fn test_struct_info_fields_some() { + let field_name = CString::new(FAKE_FIELD_NAME).expect("Invalid fake field name."); let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name."); - let type_info = fake_type_info(&type_name); + let type_info = fake_type_info(&type_name, TypeGroup::FundamentalTypes); + let field_names = &[field_name.as_ptr()]; let field_types = &[type_info]; + let field_offsets = &[1]; + let field_sizes = &[2]; let struct_name = CString::new(FAKE_STRUCT_NAME).expect("Invalid fake struct name."); - let struct_info = fake_struct_info(&struct_name, field_types); + let struct_info = fake_struct_info( + &struct_name, + field_names, + field_types, + field_offsets, + field_sizes, + ); + for (lhs, rhs) in struct_info.field_names().zip([FAKE_FIELD_NAME].iter()) { + assert_eq!(lhs, *rhs) + } assert_eq!(struct_info.field_types(), field_types); + assert_eq!(struct_info.field_offsets(), field_offsets); + assert_eq!(struct_info.field_sizes(), field_sizes); } fn fake_module_info( @@ -430,7 +501,7 @@ mod tests { #[test] fn test_module_info_types_some() { let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name."); - let type_info = fake_type_info(&type_name); + let type_info = fake_type_info(&type_name, TypeGroup::FundamentalTypes); let return_type = Some(&type_info); let fn_name = CString::new(FAKE_FN_NAME).expect("Invalid fake fn name."); @@ -443,7 +514,7 @@ mod tests { let functions = &[fn_info]; let struct_name = CString::new(FAKE_STRUCT_NAME).expect("Invalid fake struct name"); - let struct_info = fake_struct_info(&struct_name, &[]); + let struct_info = fake_struct_info(&struct_name, &[], &[], &[], &[]); let structs = &[struct_info]; let module_path = CString::new(FAKE_MODULE_PATH).expect("Invalid fake module path."); @@ -493,7 +564,7 @@ mod tests { #[test] fn test_dispatch_table_iter_mut_some() { let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name."); - let type_info = fake_type_info(&type_name); + let type_info = fake_type_info(&type_name, TypeGroup::FundamentalTypes); let return_type = Some(&type_info); let fn_name = CString::new(FAKE_FN_NAME).expect("Invalid fake fn name."); @@ -516,9 +587,18 @@ mod tests { } #[test] - fn test_dispatch_table_ptrs_mut() { + fn test_dispatch_table_ptrs_mut_none() { + let signatures = &[]; + let fn_ptrs = &mut []; + let mut dispatch_table = fake_dispatch_table(signatures, fn_ptrs); + + assert_eq!(dispatch_table.ptrs_mut().len(), 0); + } + + #[test] + fn test_dispatch_table_ptrs_mut_some() { let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name."); - let type_info = fake_type_info(&type_name); + let type_info = fake_type_info(&type_name, TypeGroup::FundamentalTypes); let return_type = Some(&type_info); let fn_name = CString::new(FAKE_FN_NAME).expect("Invalid fake fn name."); @@ -536,9 +616,18 @@ mod tests { } #[test] - fn test_dispatch_table_signatures() { + fn test_dispatch_table_signatures_none() { + let signatures = &[]; + let fn_ptrs = &mut []; + let dispatch_table = fake_dispatch_table(signatures, fn_ptrs); + + assert_eq!(dispatch_table.signatures().len(), 0); + } + + #[test] + fn test_dispatch_table_signatures_some() { let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name."); - let type_info = fake_type_info(&type_name); + let type_info = fake_type_info(&type_name, TypeGroup::FundamentalTypes); let return_type = Some(&type_info); let fn_name = CString::new(FAKE_FN_NAME).expect("Invalid fake fn name."); @@ -561,7 +650,7 @@ mod tests { #[test] fn test_dispatch_table_get_ptr_unchecked() { let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name."); - let type_info = fake_type_info(&type_name); + let type_info = fake_type_info(&type_name, TypeGroup::FundamentalTypes); let return_type = Some(&type_info); let fn_name = CString::new(FAKE_FN_NAME).expect("Invalid fake fn name."); @@ -577,7 +666,7 @@ mod tests { #[test] fn test_dispatch_table_get_ptr_none() { let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name."); - let type_info = fake_type_info(&type_name); + let type_info = fake_type_info(&type_name, TypeGroup::FundamentalTypes); let return_type = Some(&type_info); let fn_name = CString::new(FAKE_FN_NAME).expect("Invalid fake fn name."); @@ -593,7 +682,7 @@ mod tests { #[test] fn test_dispatch_table_get_ptr_some() { let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name."); - let type_info = fake_type_info(&type_name); + let type_info = fake_type_info(&type_name, TypeGroup::FundamentalTypes); let return_type = Some(&type_info); let fn_name = CString::new(FAKE_FN_NAME).expect("Invalid fake fn name."); @@ -609,7 +698,7 @@ mod tests { #[test] fn test_dispatch_table_get_ptr_unchecked_mut() { let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name."); - let type_info = fake_type_info(&type_name); + let type_info = fake_type_info(&type_name, TypeGroup::FundamentalTypes); let return_type = Some(&type_info); let fn_name = CString::new(FAKE_FN_NAME).expect("Invalid fake fn name."); @@ -628,7 +717,7 @@ mod tests { #[test] fn test_dispatch_table_get_ptr_mut_none() { let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name."); - let type_info = fake_type_info(&type_name); + let type_info = fake_type_info(&type_name, TypeGroup::FundamentalTypes); let return_type = Some(&type_info); let fn_name = CString::new(FAKE_FN_NAME).expect("Invalid fake fn name."); @@ -644,7 +733,7 @@ mod tests { #[test] fn test_dispatch_table_get_ptr_mut_some() { let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name."); - let type_info = fake_type_info(&type_name); + let type_info = fake_type_info(&type_name, TypeGroup::FundamentalTypes); let return_type = Some(&type_info); let fn_name = CString::new(FAKE_FN_NAME).expect("Invalid fake fn name."); diff --git a/crates/mun_abi/src/lib.rs b/crates/mun_abi/src/lib.rs index 587548fb7..6d6d4e289 100644 --- a/crates/mun_abi/src/lib.rs +++ b/crates/mun_abi/src/lib.rs @@ -4,26 +4,21 @@ //! Runtime. #![warn(missing_docs)] -// Bindings are automatically generated from C on `cargo build` +// Bindings can be manually generated by running `cargo gen-abi`. mod autogen; - mod autogen_impl; -mod macros; -mod reflection; pub use autogen::*; -pub use reflection::Reflection; /// The Mun ABI prelude /// /// The *prelude* contains imports that are used almost every time. pub mod prelude { pub use crate::autogen::*; - pub use crate::reflection::Reflection; - pub use crate::Privacy; + pub use crate::{Privacy, TypeGroup}; } -/// A type that represents the privacy level of modules, functions, or variables. +/// Represents the privacy level of modules, functions, or variables. #[repr(u8)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Privacy { @@ -32,3 +27,31 @@ pub enum Privacy { /// Privately accessible Private = 1, } + +/// Represents a group of types that illicit the same characteristics. +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] +pub enum TypeGroup { + /// Fundamental types (i.e. `()`, `bool`, `float`, `int`, etc.) + FundamentalTypes = 0, + /// Struct types (i.e. record, tuple, or unit structs) + StructTypes = 1, +} + +impl TypeGroup { + /// Returns whether this is a fundamental type. + pub fn is_fundamental(self) -> bool { + match self { + TypeGroup::FundamentalTypes => true, + _ => false, + } + } + + /// Returns whether this is a struct type. + pub fn is_struct(self) -> bool { + match self { + TypeGroup::StructTypes => true, + _ => false, + } + } +} diff --git a/crates/mun_abi/src/macros.rs b/crates/mun_abi/src/macros.rs deleted file mode 100644 index e6a004f57..000000000 --- a/crates/mun_abi/src/macros.rs +++ /dev/null @@ -1,59 +0,0 @@ -#[doc(hidden)] -#[macro_export(local_inner_macros)] -macro_rules! count_args { - () => { 0 }; - ($name:ident) => { 1 }; - ($first:ident, $($rest:ident),*) => { - 1 + count_args!($($rest),*) - } -} - -/// Tries to downcast the `fn_ptr` of `FunctionInfo` to the specified function type. -/// -/// Returns an error message upon failure. -#[macro_export] -macro_rules! downcast_fn { - ($FunctionInfo:expr, fn($($T:ident),*) -> $Output:ident) => {{ - let num_args = $crate::count_args!($($T),*); - - let arg_types = $FunctionInfo.signature.arg_types(); - if arg_types.len() != num_args { - return Err(format!( - "Invalid number of arguments. Expected: {}. Found: {}.", - num_args, - arg_types.len(), - )); - } - - let mut idx = 0; - $( - if arg_types[idx].guid != $T::type_guid() { - return Err(format!( - "Invalid argument type at index {}. Expected: {}. Found: {}.", - idx, - $T::type_name(), - arg_types[idx].name(), - )); - } - idx += 1; - )* - - if let Some(return_type) = $FunctionInfo.signature.return_type() { - if return_type.guid != Output::type_guid() { - return Err(format!( - "Invalid return type. Expected: {}. Found: {}", - Output::type_name(), - return_type.name(), - )); - } - } else if <()>::type_guid() != Output::type_guid() { - return Err(format!( - "Invalid return type. Expected: {}. Found: {}", - Output::type_name(), - <()>::type_name(), - )); - } - - Ok(unsafe { core::mem::transmute($FunctionInfo.fn_ptr) }) - }} -} diff --git a/crates/mun_abi/src/reflection.rs b/crates/mun_abi/src/reflection.rs deleted file mode 100644 index 0736ed923..000000000 --- a/crates/mun_abi/src/reflection.rs +++ /dev/null @@ -1,39 +0,0 @@ -use crate::prelude::*; -use md5; - -/// A type to emulate dynamic typing across compilation units for static types. -pub trait Reflection: 'static { - /// Retrieves the type's `Guid`. - fn type_guid() -> Guid { - Guid { - b: md5::compute(Self::type_name()).0, - } - } - - /// Retrieves the type's name. - fn type_name() -> &'static str; -} - -impl Reflection for f64 { - fn type_name() -> &'static str { - "@core::float" - } -} - -impl Reflection for i64 { - fn type_name() -> &'static str { - "@core::int" - } -} - -impl Reflection for bool { - fn type_name() -> &'static str { - "@core::bool" - } -} - -impl Reflection for () { - fn type_name() -> &'static str { - "@core::empty" - } -} diff --git a/crates/mun_codegen/Cargo.toml b/crates/mun_codegen/Cargo.toml index 9858bedfa..6ed9c2395 100644 --- a/crates/mun_codegen/Cargo.toml +++ b/crates/mun_codegen/Cargo.toml @@ -9,6 +9,7 @@ license = "MIT OR Apache-2.0" description = "LLVM IR code generation for Mun" [dependencies] +abi = { path = "../mun_abi", package = "mun_abi" } hir = { path = "../mun_hir", package = "mun_hir" } mun_target = { path = "../mun_target" } mun_lld = { path = "../mun_lld" } diff --git a/crates/mun_codegen/src/code_gen.rs b/crates/mun_codegen/src/code_gen.rs index 8ec606896..29994f442 100644 --- a/crates/mun_codegen/src/code_gen.rs +++ b/crates/mun_codegen/src/code_gen.rs @@ -50,6 +50,26 @@ pub fn write_module_shared_object( .unwrap_or("unknown"), ); + // Initialize the x86 target + Target::initialize_x86(&InitializationConfig::default()); + + // Construct the LLVM target from the specified target. + let llvm_target = Target::from_triple(&target.llvm_target) + .map_err(|e| CodeGenerationError::UnknownTargetTriple(e.to_string()))?; + assembly_module.set_target(&llvm_target); + + // Construct target machine for machine code generation + let target_machine = llvm_target + .create_target_machine( + &target.llvm_target, + &target.options.cpu, + &target.options.features, + db.optimization_lvl(), + RelocMode::PIC, + CodeModel::Default, + ) + .ok_or(CodeGenerationError::CouldNotCreateTargetMachine)?; + // Generate IR for the module and clone it so that we can modify it without modifying the // cached value. let module = db.module_ir(file_id); @@ -64,34 +84,15 @@ pub fn write_module_shared_object( &module.structs, &module.dispatch_table, &assembly_module, + &target_machine, ); - // Initialize the x86 target - Target::initialize_x86(&InitializationConfig::default()); - - // Construct the LLVM target from the specified target. - let llvm_target = Target::from_triple(&target.llvm_target) - .map_err(|e| CodeGenerationError::UnknownTargetTriple(e.to_string()))?; - assembly_module.set_target(&llvm_target); - // Optimize the assembly module optimize_module(&assembly_module, db.optimization_lvl()); // Debug print the IR //println!("{}", assembly_module.print_to_string().to_string()); - // Construct target machine for machine code generation - let target_machine = llvm_target - .create_target_machine( - &target.llvm_target, - &target.options.cpu, - &target.options.features, - db.optimization_lvl(), - RelocMode::PIC, - CodeModel::Default, - ) - .ok_or(CodeGenerationError::CouldNotCreateTargetMachine)?; - // Generate object file let obj_file = { let obj = target_machine diff --git a/crates/mun_codegen/src/code_gen/abi_types.rs b/crates/mun_codegen/src/code_gen/abi_types.rs index 0ab97d407..0e158018e 100644 --- a/crates/mun_codegen/src/code_gen/abi_types.rs +++ b/crates/mun_codegen/src/code_gen/abi_types.rs @@ -4,6 +4,7 @@ use inkwell::AddressSpace; pub(super) struct AbiTypes { pub guid_type: ArrayType, + pub type_group_type: IntType, pub privacy_type: IntType, pub type_info_type: StructType, pub function_signature_type: StructType, @@ -21,6 +22,9 @@ pub(super) fn gen_abi_types(context: ContextRef) -> AbiTypes { // Construct the `MunGuid` type let guid_type = context.i8_type().array_type(16); + // Construct the `MunTypeGroup` type + let type_group_type = context.i8_type(); + // Construct the `MunPrivacy` enum let privacy_type = context.i8_type(); @@ -28,8 +32,9 @@ pub(super) fn gen_abi_types(context: ContextRef) -> AbiTypes { let type_info_type = context.opaque_struct_type("struct.MunTypeInfo"); type_info_type.set_body( &[ - guid_type.into(), // guid - str_type.into(), // name + guid_type.into(), // guid + str_type.into(), // name + type_group_type.into(), // group ], false, ); @@ -65,9 +70,12 @@ pub(super) fn gen_abi_types(context: ContextRef) -> AbiTypes { let struct_info_type = context.opaque_struct_type("struct.MunStructInfo"); struct_info_type.set_body( &[ - str_type.into(), // name - type_info_type.ptr_type(AddressSpace::Const).into(), // field_types - context.i16_type().into(), // num_fields + str_type.into(), // name + str_type.ptr_type(AddressSpace::Const).into(), // field_names + type_info_type.ptr_type(AddressSpace::Const).into(), // field_types + context.i16_type().ptr_type(AddressSpace::Const).into(), // field_offsets + context.i16_type().ptr_type(AddressSpace::Const).into(), // field_sizes + context.i16_type().into(), // num_fields ], false, ); @@ -115,6 +123,7 @@ pub(super) fn gen_abi_types(context: ContextRef) -> AbiTypes { AbiTypes { guid_type, + type_group_type, privacy_type, type_info_type, function_signature_type, diff --git a/crates/mun_codegen/src/code_gen/symbols.rs b/crates/mun_codegen/src/code_gen/symbols.rs index 91aa18180..cb3245893 100644 --- a/crates/mun_codegen/src/code_gen/symbols.rs +++ b/crates/mun_codegen/src/code_gen/symbols.rs @@ -4,10 +4,12 @@ use crate::ir::function; use crate::type_info::TypeInfo; use crate::values::{BasicValue, GlobalValue}; use crate::IrDatabase; +use abi::TypeGroup; use hir::{Ty, TypeCtor}; use inkwell::{ attributes::Attribute, module::{Linkage, Module}, + targets::TargetMachine, types::StructType, values::{FunctionValue, IntValue, PointerValue, StructValue, UnnamedAddress}, AddressSpace, @@ -17,10 +19,10 @@ use std::collections::HashMap; pub fn type_info_query(db: &impl IrDatabase, ty: Ty) -> TypeInfo { match ty { Ty::Apply(ctor) => match ctor.ctor { - TypeCtor::Float => TypeInfo::from_name("@core::float"), - TypeCtor::Int => TypeInfo::from_name("@core::int"), - TypeCtor::Bool => TypeInfo::from_name("@core::bool"), - TypeCtor::Struct(s) => TypeInfo::from_name(s.name(db).to_string()), + TypeCtor::Float => TypeInfo::new("@core::float", TypeGroup::FundamentalTypes), + TypeCtor::Int => TypeInfo::new("@core::int", TypeGroup::FundamentalTypes), + TypeCtor::Bool => TypeInfo::new("@core::bool", TypeGroup::FundamentalTypes), + TypeCtor::Struct(s) => TypeInfo::new(s.name(db).to_string(), TypeGroup::StructTypes), _ => unreachable!("{:?} unhandled", ctor), }, _ => unreachable!(), @@ -36,6 +38,7 @@ fn type_info_ir(ty: &TypeInfo, module: &Module) -> StructValue { &[ context.i8_type().const_array(&guid_values).into(), intern_string(module, &ty.name).into(), + context.i8_type().const_int(ty.group as u64, false).into(), ], false, ) @@ -223,24 +226,41 @@ fn gen_struct_info_array<'a, D: IrDatabase>( db: &D, types: &AbiTypes, module: &Module, + target: &TargetMachine, structs: impl Iterator, ) -> GlobalValue { + let target_data = target.get_target_data(); let struct_infos: Vec = structs - .map(|(s, _)| { + .map(|(s, t)| { let name_str = intern_string(&module, &s.name(db).to_string()); let fields = s.fields(db); - let num_fields = fields.len(); - let fields = gen_type_info_array( + + let field_names = fields.iter().map(|field| field.name(db).to_string()); + let (field_names, num_fields) = gen_string_array(module, field_names); + + let field_types = gen_type_info_array( db, module, types, fields.iter().map(|field| db.type_info(field.ty(db))), ); + let field_offsets = + (0..fields.len()).map(|idx| target_data.offset_of_element(t, idx as u32).unwrap()); + let (field_offsets, _) = gen_u16_array(module, field_offsets); + + let field_sizes = fields + .iter() + .map(|field| target_data.get_store_size(&db.type_ir(field.ty(db)))); + let (field_sizes, _) = gen_u16_array(module, field_sizes); + types.struct_info_type.const_named_struct(&[ name_str.into(), - fields.into(), + field_names.into(), + field_types.into(), + field_offsets.into(), + field_sizes.into(), module .get_context() .i16_type() @@ -254,6 +274,49 @@ fn gen_struct_info_array<'a, D: IrDatabase>( gen_global(module, &struct_infos, "fn.get_info.structs") } +/// Constructs a global from the specified list of strings +fn gen_string_array( + module: &Module, + strings: impl Iterator, +) -> (PointerValue, usize) { + let str_type = module.get_context().i8_type().ptr_type(AddressSpace::Const); + + let mut strings = strings.peekable(); + if strings.peek().is_none() { + (str_type.ptr_type(AddressSpace::Const).const_null(), 0) + } else { + let strings = strings + .map(|s| intern_string(module, &s)) + .collect::>(); + + let strings_ir = str_type.const_array(&strings); + ( + gen_global(module, &strings_ir, "").as_pointer_value(), + strings.len(), + ) + } +} + +/// Constructs a global from the specified list of strings +fn gen_u16_array(module: &Module, integers: impl Iterator) -> (PointerValue, usize) { + let u16_type = module.get_context().i16_type(); + + let mut integers = integers.peekable(); + if integers.peek().is_none() { + (u16_type.ptr_type(AddressSpace::Const).const_null(), 0) + } else { + let integers = integers + .map(|i| u16_type.const_int(i, false)) + .collect::>(); + + let array_ir = u16_type.const_array(&integers); + ( + gen_global(module, &array_ir, "").as_pointer_value(), + integers.len(), + ) + } +} + /// Construct a global from the specified value fn gen_global(module: &Module, value: &dyn BasicValue, name: &str) -> GlobalValue { let global = module.add_global(value.as_basic_value_enum().get_type(), None, name); @@ -327,6 +390,7 @@ pub(super) fn gen_reflection_ir( struct_map: &HashMap, dispatch_table: &DispatchTable, module: &Module, + target: &TargetMachine, ) { // Get all the types let abi_types = gen_abi_types(module.get_context()); @@ -342,7 +406,7 @@ pub(super) fn gen_reflection_ir( .i32_type() .const_int(function_map.len() as u64, false) .into(), - gen_struct_info_array(db, &abi_types, module, struct_map.iter()) + gen_struct_info_array(db, &abi_types, module, target, struct_map.iter()) .as_pointer_value() .into(), module diff --git a/crates/mun_codegen/src/type_info.rs b/crates/mun_codegen/src/type_info.rs index 7a2198f95..17f2ee66e 100644 --- a/crates/mun_codegen/src/type_info.rs +++ b/crates/mun_codegen/src/type_info.rs @@ -1,3 +1,4 @@ +use abi::TypeGroup; use std::hash::{Hash, Hasher}; pub type Guid = [u8; 16]; @@ -6,6 +7,7 @@ pub type Guid = [u8; 16]; pub struct TypeInfo { pub guid: Guid, pub name: String, + pub group: TypeGroup, } impl Hash for TypeInfo { @@ -21,10 +23,11 @@ impl PartialEq for TypeInfo { } impl TypeInfo { - pub fn from_name>(name: S) -> TypeInfo { + pub fn new>(name: S, group: TypeGroup) -> TypeInfo { TypeInfo { name: name.as_ref().to_string(), guid: md5::compute(name.as_ref()).0, + group, } } } @@ -46,49 +49,55 @@ impl HasTypeInfo for T { impl HasStaticTypeInfo for u8 { fn type_info() -> TypeInfo { - TypeInfo::from_name("core::u8") + TypeInfo::new("core::u8", TypeGroup::FundamentalTypes) } } impl HasStaticTypeInfo for u64 { fn type_info() -> TypeInfo { - TypeInfo::from_name("core::u64") + TypeInfo::new("core::u64", TypeGroup::FundamentalTypes) } } impl HasStaticTypeInfo for i64 { fn type_info() -> TypeInfo { - TypeInfo::from_name("core::i64") + TypeInfo::new("core::i64", TypeGroup::FundamentalTypes) } } impl HasStaticTypeInfo for f32 { fn type_info() -> TypeInfo { - TypeInfo::from_name("core::f32") + TypeInfo::new("core::f32", TypeGroup::FundamentalTypes) } } impl HasStaticTypeInfo for bool { fn type_info() -> TypeInfo { - TypeInfo::from_name("core::bool") + TypeInfo::new("core::bool", TypeGroup::FundamentalTypes) } } impl HasStaticTypeInfo for *mut T { fn type_info() -> TypeInfo { - TypeInfo::from_name(format!("*mut {}", T::type_info().name)) + TypeInfo::new( + format!("*mut {}", T::type_info().name), + TypeGroup::FundamentalTypes, + ) } } impl HasStaticTypeInfo for usize { fn type_info() -> TypeInfo { - TypeInfo::from_name("core::usize") + TypeInfo::new("core::usize", TypeGroup::FundamentalTypes) } } impl HasStaticTypeInfo for *const T { fn type_info() -> TypeInfo { - TypeInfo::from_name(format!("*const {}", T::type_info().name)) + TypeInfo::new( + format!("*const {}", T::type_info().name), + TypeGroup::FundamentalTypes, + ) } } diff --git a/crates/mun_runtime/Cargo.toml b/crates/mun_runtime/Cargo.toml index 9cefae0f7..b4335fa50 100644 --- a/crates/mun_runtime/Cargo.toml +++ b/crates/mun_runtime/Cargo.toml @@ -9,13 +9,13 @@ license = "MIT OR Apache-2.0" description = "A runtime for hot reloading and invoking Mun from Rust" [dependencies] +abi = { path = "../mun_abi", package = "mun_abi" } failure = "0.1.5" libloading = "0.5" -abi = { path = "../mun_abi", package = "mun_abi" } +md5= "0.7.0" notify = "4.0.12" parking_lot = "0.9" tempfile = "3" -md5="0.7.0" [dev-dependencies] mun_compiler = { path="../mun_compiler" } diff --git a/crates/mun_runtime/src/lib.rs b/crates/mun_runtime/src/lib.rs index 2bd6c27d8..7052efb83 100644 --- a/crates/mun_runtime/src/lib.rs +++ b/crates/mun_runtime/src/lib.rs @@ -7,23 +7,30 @@ mod assembly; #[macro_use] mod macros; +mod marshal; +mod reflection; +mod r#struct; #[cfg(test)] mod test; +use std::alloc::Layout; use std::collections::HashMap; +use std::ffi::CString; use std::io; use std::path::{Path, PathBuf}; use std::sync::mpsc::{channel, Receiver}; use std::time::Duration; -use abi::{FunctionInfo, FunctionSignature, Guid, Privacy, Reflection, StructInfo, TypeInfo}; +use abi::{FunctionInfo, FunctionSignature, Guid, Privacy, StructInfo, TypeGroup, TypeInfo}; use failure::Error; use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher}; +pub use crate::marshal::MarshalInto; +pub use crate::reflection::{ArgumentReflection, ReturnTypeReflection}; + pub use crate::assembly::Assembly; -use std::alloc::Layout; -use std::ffi::CString; +pub use crate::r#struct::Struct; /// Options for the construction of a [`Runtime`]. #[derive(Clone, Debug)] @@ -152,12 +159,14 @@ impl Runtime { b: md5::compute("core::u64").0, }, name: u64_type.as_ptr(), + group: TypeGroup::FundamentalTypes, }, TypeInfo { guid: Guid { b: md5::compute("core::u64").0, }, name: u64_type.as_ptr(), + group: TypeGroup::FundamentalTypes, }, ]; @@ -166,6 +175,7 @@ impl Runtime { b: md5::compute("*mut core::u8").0, }, name: ptr_mut_u8_type.as_ptr(), + group: TypeGroup::FundamentalTypes, }); let fn_info = FunctionInfo { diff --git a/crates/mun_runtime/src/macros.rs b/crates/mun_runtime/src/macros.rs index 57044dfc1..39842b3a8 100644 --- a/crates/mun_runtime/src/macros.rs +++ b/crates/mun_runtime/src/macros.rs @@ -1,3 +1,13 @@ +#[doc(hidden)] +#[macro_export(local_inner_macros)] +macro_rules! count_args { + () => { 0 }; + ($name:ident) => { 1 }; + ($first:ident, $($rest:ident),*) => { + 1 + count_args!($($rest),*) + } +} + macro_rules! invoke_fn_impl { ($( fn $FnName:ident($($Arg:tt: $T:ident),*) -> $ErrName:ident; @@ -6,7 +16,7 @@ macro_rules! invoke_fn_impl { /// An invocation error that contains the function name, a mutable reference to the /// runtime, passed arguments, and the output type. This allows the caller to retry /// the function invocation using the `Retriable` trait. - pub struct $ErrName<'r, 's, $($T: Reflection,)* Output:Reflection> { + pub struct $ErrName<'r, 's, $($T: ArgumentReflection,)* Output:ReturnTypeReflection> { msg: String, runtime: &'r mut Runtime, function_name: &'s str, @@ -14,25 +24,25 @@ macro_rules! invoke_fn_impl { output: core::marker::PhantomData, } - impl<'r, 's, $($T: Reflection,)* Output: Reflection> core::fmt::Debug for $ErrName<'r, 's, $($T,)* Output> { + impl<'r, 's, $($T: ArgumentReflection,)* Output: ReturnTypeReflection> core::fmt::Debug for $ErrName<'r, 's, $($T,)* Output> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{}", &self.msg) } } - impl<'r, 's, $($T: Reflection,)* Output: Reflection> core::fmt::Display for $ErrName<'r, 's, $($T,)* Output> { + impl<'r, 's, $($T: ArgumentReflection,)* Output: ReturnTypeReflection> core::fmt::Display for $ErrName<'r, 's, $($T,)* Output> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{}", &self.msg) } } - impl<'r, 's, $($T: Reflection,)* Output: Reflection> std::error::Error for $ErrName<'r, 's, $($T,)* Output> { + impl<'r, 's, $($T: ArgumentReflection,)* Output: ReturnTypeReflection> std::error::Error for $ErrName<'r, 's, $($T,)* Output> { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None } } - impl<'r, 's, $($T: Reflection,)* Output: Reflection> $ErrName<'r, 's, $($T,)* Output> { + impl<'r, 's, $($T: ArgumentReflection,)* Output: ReturnTypeReflection> $ErrName<'r, 's, $($T,)* Output> { /// Constructs a new invocation error. #[allow(clippy::too_many_arguments)] pub fn new(err_msg: String, runtime: &'r mut Runtime, function_name: &'s str, $($Arg: $T),*) -> Self { @@ -46,7 +56,7 @@ macro_rules! invoke_fn_impl { } } - impl<'r, 's, $($T: Reflection,)* Output: Reflection> $crate::RetryResultExt for core::result::Result> { + impl<'r, 's, $($T: ArgumentReflection,)* Output: ReturnTypeReflection> $crate::RetryResultExt for core::result::Result> { type Output = Output; fn retry(self) -> Self { @@ -78,20 +88,83 @@ macro_rules! invoke_fn_impl { /// /// If an error occurs when invoking the method, an error message is logged. The /// runtime continues looping until the cause of the error has been resolved. - #[allow(clippy::too_many_arguments)] - pub fn $FnName<'r, 's, $($T: Reflection,)* Output: Reflection>( + #[allow(clippy::too_many_arguments, unused_assignments)] + pub fn $FnName<'r, 's, $($T: ArgumentReflection,)* Output: ReturnTypeReflection>( runtime: &'r mut Runtime, function_name: &'s str, $($Arg: $T,)* ) -> core::result::Result> { - let function: core::result::Result Output, String> = runtime + match runtime .get_function_info(function_name) .ok_or(format!("Failed to obtain function '{}'", function_name)) - .and_then(|function| abi::downcast_fn!(function, fn($($T),*) -> Output)); + .and_then(|function_info| { + // Validate function signature + let num_args = $crate::count_args!($($T),*); + + let arg_types = function_info.signature.arg_types(); + if arg_types.len() != num_args { + return Err(format!( + "Invalid number of arguments. Expected: {}. Found: {}.", + num_args, + arg_types.len(), + )); + } - match function { - Ok(function) => Ok(function($($Arg),*)), - Err(e) => Err($ErrName::new(e, runtime, function_name, $($Arg),*)), + #[allow(unused_mut, unused_variables)] + let mut idx = 0; + $( + if arg_types[idx].guid != $Arg.type_guid() { + return Err(format!( + "Invalid argument type at index {}. Expected: {}. Found: {}.", + idx, + $Arg.type_name(), + arg_types[idx].name(), + )); + } + idx += 1; + )* + + if let Some(return_type) = function_info.signature.return_type() { + match return_type.group { + abi::TypeGroup::FundamentalTypes => { + if return_type.guid != Output::type_guid() { + return Err(format!( + "Invalid return type. Expected: {}. Found: {}", + Output::type_name(), + return_type.name(), + )); + } + } + abi::TypeGroup::StructTypes => { + if ::type_guid() != Output::type_guid() { + return Err(format!( + "Invalid return type. Expected: {}. Found: Struct", + Output::type_name(), + )); + } + } + } + + } else if <() as ReturnTypeReflection>::type_guid() != Output::type_guid() { + return Err(format!( + "Invalid return type. Expected: {}. Found: {}", + Output::type_name(), + <() as ReturnTypeReflection>::type_name(), + )); + } + + Ok(function_info) + }) { + Ok(function_info) => { + let function: fn($($T::Marshalled),*) -> Output::Marshalled = unsafe { + core::mem::transmute(function_info.fn_ptr) + }; + let result = function($($Arg.marshal()),*); + + // Marshall the result + Ok(result.marshal_into(runtime, function_info.signature.return_type())) + } + Err(e) => Err($ErrName::new(e, runtime, function_name, $($Arg),*)) } } } diff --git a/crates/mun_runtime/src/marshal.rs b/crates/mun_runtime/src/marshal.rs new file mode 100644 index 000000000..e83c83bfb --- /dev/null +++ b/crates/mun_runtime/src/marshal.rs @@ -0,0 +1,17 @@ +use crate::Runtime; +use abi::TypeInfo; + +/// Used to do value-to-value conversions that require runtime type information while consuming the +/// input value. +/// +/// If no `TypeInfo` is provided, the type is `()`. +pub trait MarshalInto: Sized { + /// Performs the conversion. + fn marshal_into(self, runtime: &Runtime, type_info: Option<&TypeInfo>) -> T; +} + +impl MarshalInto for T { + fn marshal_into(self, _runtime: &Runtime, _type_info: Option<&TypeInfo>) -> T { + self + } +} diff --git a/crates/mun_runtime/src/reflection.rs b/crates/mun_runtime/src/reflection.rs new file mode 100644 index 000000000..af8436545 --- /dev/null +++ b/crates/mun_runtime/src/reflection.rs @@ -0,0 +1,118 @@ +use crate::marshal::MarshalInto; +use abi::Guid; +use md5; + +/// A type to emulate dynamic typing across compilation units for static types. +pub trait ReturnTypeReflection: Sized + 'static { + /// The resulting type after marshaling. + type Marshalled: MarshalInto; + + /// Retrieves the type's `Guid`. + fn type_guid() -> Guid { + Guid { + b: md5::compute(Self::type_name()).0, + } + } + + /// Retrieves the type's name. + fn type_name() -> &'static str; +} + +/// A type to emulate dynamic typing across compilation units for statically typed values. +pub trait ArgumentReflection { + /// The resulting type after dereferencing. + type Marshalled: Sized; + + /// Retrieves the `Guid` of the value's type. + fn type_guid(&self) -> Guid { + Guid { + b: md5::compute(self.type_name()).0, + } + } + + /// Retrieves the name of the value's type. + fn type_name(&self) -> &str; + + /// Marshals the value. + fn marshal(self) -> Self::Marshalled; +} + +impl ArgumentReflection for f64 { + type Marshalled = Self; + + fn type_name(&self) -> &str { + ::type_name() + } + + fn marshal(self) -> Self::Marshalled { + self + } +} + +impl ArgumentReflection for i64 { + type Marshalled = Self; + + fn type_name(&self) -> &str { + ::type_name() + } + + fn marshal(self) -> Self::Marshalled { + self + } +} + +impl ArgumentReflection for bool { + type Marshalled = Self; + + fn type_name(&self) -> &str { + ::type_name() + } + + fn marshal(self) -> Self::Marshalled { + self + } +} + +impl ArgumentReflection for () { + type Marshalled = Self; + + fn type_name(&self) -> &str { + ::type_name() + } + + fn marshal(self) -> Self::Marshalled { + self + } +} + +impl ReturnTypeReflection for f64 { + type Marshalled = f64; + + fn type_name() -> &'static str { + "@core::float" + } +} + +impl ReturnTypeReflection for i64 { + type Marshalled = i64; + + fn type_name() -> &'static str { + "@core::int" + } +} + +impl ReturnTypeReflection for bool { + type Marshalled = bool; + + fn type_name() -> &'static str { + "@core::bool" + } +} + +impl ReturnTypeReflection for () { + type Marshalled = (); + + fn type_name() -> &'static str { + "@core::empty" + } +} diff --git a/crates/mun_runtime/src/struct.rs b/crates/mun_runtime/src/struct.rs new file mode 100644 index 000000000..717a84282 --- /dev/null +++ b/crates/mun_runtime/src/struct.rs @@ -0,0 +1,185 @@ +use crate::{ + marshal::MarshalInto, + reflection::{ArgumentReflection, ReturnTypeReflection}, + Runtime, +}; +use abi::{StructInfo, TypeInfo}; +use std::mem; + +/// Represents a Mun struct pointer. +/// +/// A byte pointer is used to make pointer arithmetic easier. +#[repr(transparent)] +#[derive(Clone)] +pub struct RawStruct(*mut u8); + +/// Type-agnostic wrapper for interoperability with a Mun struct. +/// TODO: Handle destruction of `struct(value)` +#[derive(Clone)] +pub struct Struct { + raw: RawStruct, + info: StructInfo, +} + +impl Struct { + /// Creates a struct that wraps a raw Mun struct. + pub fn new(runtime: &Runtime, type_info: &TypeInfo, raw: RawStruct) -> Result { + let struct_info = runtime.get_struct_info(type_info.name()).ok_or_else(|| { + format!( + "Could not find information for struct `{}`.", + type_info.name() + ) + })?; + + Ok(Self { + raw, + info: struct_info.clone(), + }) + } + + /// Consumes the `Struct`, returning a raw Mun struct. + pub fn into_raw(self) -> RawStruct { + self.raw + } + + /// Retrieves the value of the field corresponding to the specified `field_name`. + pub fn get(&self, field_name: &str) -> Result<&T, String> { + let field_idx = self + .info + .field_names() + .enumerate() + .find(|(_, name)| *name == field_name) + .map(|(idx, _)| idx) + .ok_or_else(|| { + format!( + "Struct `{}` does not contain field `{}`.", + self.info.name(), + field_name + ) + })?; + + let field_type = unsafe { self.info.field_types().get_unchecked(field_idx) }; + if T::type_guid() != field_type.guid { + return Err(format!( + "Mismatched types for `{}::{}`. Expected: `{}`. Found: `{}`.", + self.info.name(), + field_name, + field_type.name(), + T::type_name() + )); + } + + unsafe { + // If we found the `field_idx`, we are guaranteed to also have the `field_offset` + let offset = *self.info.field_offsets().get_unchecked(field_idx); + // self.ptr is never null + Ok(&*self.raw.0.add(offset as usize).cast::()) + } + } + + /// Replaces the value of the field corresponding to the specified `field_name` and returns the + /// old value. + pub fn replace( + &mut self, + field_name: &str, + mut value: T, + ) -> Result { + let field_idx = self + .info + .field_names() + .enumerate() + .find(|(_, name)| *name == field_name) + .map(|(idx, _)| idx) + .ok_or_else(|| { + format!( + "Struct `{}` does not contain field `{}`.", + self.info.name(), + field_name + ) + })?; + + let field_type = unsafe { self.info.field_types().get_unchecked(field_idx) }; + if value.type_guid() != field_type.guid { + return Err(format!( + "Mismatched types for `{}::{}`. Expected: `{}`. Found: `{}`.", + self.info.name(), + field_name, + field_type.name(), + value.type_name() + )); + } + + let ptr = unsafe { + // If we found the `field_idx`, we are guaranteed to also have the `field_offset` + let offset = *self.info.field_offsets().get_unchecked(field_idx); + // self.ptr is never null + &mut *self.raw.0.add(offset as usize).cast::() + }; + mem::swap(&mut value, ptr); + Ok(value) + } + + /// Sets the value of the field corresponding to the specified `field_name`. + pub fn set(&mut self, field_name: &str, value: T) -> Result<(), String> { + let field_idx = self + .info + .field_names() + .enumerate() + .find(|(_, name)| *name == field_name) + .map(|(idx, _)| idx) + .ok_or_else(|| { + format!( + "Struct `{}` does not contain field `{}`.", + self.info.name(), + field_name + ) + })?; + + let field_type = unsafe { self.info.field_types().get_unchecked(field_idx) }; + if value.type_guid() != field_type.guid { + return Err(format!( + "Mismatched types for `{}::{}`. Expected: `{}`. Found: `{}`.", + self.info.name(), + field_name, + field_type.name(), + value.type_name() + )); + } + + unsafe { + // If we found the `field_idx`, we are guaranteed to also have the `field_offset` + let offset = *self.info.field_offsets().get_unchecked(field_idx); + // self.ptr is never null + *self.raw.0.add(offset as usize).cast::() = value; + } + Ok(()) + } +} + +impl ArgumentReflection for Struct { + type Marshalled = RawStruct; + + fn type_name(&self) -> &str { + self.info.name() + } + + fn marshal(self) -> Self::Marshalled { + self.raw + } +} + +impl ReturnTypeReflection for Struct { + type Marshalled = RawStruct; + + fn type_name() -> &'static str { + "struct" + } +} + +impl MarshalInto for RawStruct { + fn marshal_into(self, runtime: &Runtime, type_info: Option<&TypeInfo>) -> Struct { + // `type_info` is only `None` for the `()` type + // the `StructInfo` for this type must have been loaded for a function to return its type + Struct::new(runtime, type_info.unwrap(), self).unwrap() + } +} diff --git a/crates/mun_runtime/src/test.rs b/crates/mun_runtime/src/test.rs index b74faf587..380823205 100644 --- a/crates/mun_runtime/src/test.rs +++ b/crates/mun_runtime/src/test.rs @@ -1,4 +1,4 @@ -use crate::{Runtime, RuntimeBuilder}; +use crate::{Runtime, RuntimeBuilder, Struct}; use mun_compiler::{ColorChoice, Config, Driver, FileId, PathOrInline, RelativePathBuf}; use std::path::PathBuf; use std::thread::sleep; @@ -302,34 +302,51 @@ fn hotreloadable() { #[test] fn compiler_valid_utf8() { use std::ffi::CStr; - use std::str; + use std::slice; let mut driver = TestDriver::new( r#" + struct Foo { + a: int, + } + fn foo(n:int):bool { false } "#, ); - let foo_func = driver.runtime.get_function_info("foo").unwrap(); + let foo_struct = driver.runtime.get_struct_info("Foo").unwrap(); + assert_eq!( + unsafe { CStr::from_ptr(foo_struct.name) }.to_str().is_ok(), + true + ); + + let field_names = + unsafe { slice::from_raw_parts(foo_struct.field_names, foo_struct.num_fields as usize) }; + for field_name in field_names { + assert_eq!( + unsafe { CStr::from_ptr(*field_name) }.to_str().is_ok(), + true + ); + } - // Check foo name for valid utf8 + let foo_func = driver.runtime.get_function_info("foo").unwrap(); assert_eq!( - str::from_utf8(&unsafe { CStr::from_ptr(foo_func.signature.name).to_bytes() }).is_err(), - false + unsafe { CStr::from_ptr(foo_func.signature.name) } + .to_str() + .is_ok(), + true ); - // Check foo arg type for valid utf8 assert_eq!( - str::from_utf8(&unsafe { CStr::from_ptr((*foo_func.signature.arg_types).name).to_bytes() }) - .is_err(), - false + unsafe { CStr::from_ptr((*foo_func.signature.arg_types).name) } + .to_str() + .is_ok(), + true ); - // Check foo return type for valid utf8 assert_eq!( - str::from_utf8(&unsafe { - CStr::from_ptr((*foo_func.signature.return_type).name).to_bytes() - }) - .is_err(), - false + unsafe { CStr::from_ptr((*foo_func.signature.return_type).name) } + .to_str() + .is_ok(), + true ); assert_invoke_eq!(bool, false, driver, "foo", 10i64); @@ -337,7 +354,8 @@ fn compiler_valid_utf8() { #[test] fn struct_info() { - use abi::{Guid, Reflection}; + use crate::ReturnTypeReflection; + use abi::Guid; let driver = TestDriver::new( r" @@ -402,3 +420,50 @@ fn field_crash() { ); assert_invoke_eq!(i64, 15, driver, "main", 10); } + +#[test] +fn marshal_struct() { + let mut driver = TestDriver::new( + r#" + struct(gc) Foo { a: int, b: bool, c: float, }; + + fn foo_new(a: int, b: bool, c: float): Foo { + Foo { a, b, c, } + } + fn foo_a(foo: Foo):int { foo.a } + fn foo_b(foo: Foo):bool { foo.b } + fn foo_c(foo: Foo):float { foo.c } + "#, + ); + + let a = 3i64; + let b = true; + let c = 1.23f64; + let mut foo: Struct = invoke_fn!(driver.runtime, "foo_new", a, b, c).unwrap(); + assert_eq!(Ok(&a), foo.get::("a")); + assert_eq!(Ok(&b), foo.get::("b")); + assert_eq!(Ok(&c), foo.get::("c")); + + let d = 6i64; + let e = false; + let f = 4.56f64; + foo.set("a", d).unwrap(); + foo.set("b", e).unwrap(); + foo.set("c", f).unwrap(); + + assert_eq!(Ok(&d), foo.get::("a")); + assert_eq!(Ok(&e), foo.get::("b")); + assert_eq!(Ok(&f), foo.get::("c")); + + assert_eq!(Ok(d), foo.replace("a", a)); + assert_eq!(Ok(e), foo.replace("b", b)); + assert_eq!(Ok(f), foo.replace("c", c)); + + assert_eq!(Ok(&a), foo.get::("a")); + assert_eq!(Ok(&b), foo.get::("b")); + assert_eq!(Ok(&c), foo.get::("c")); + + assert_invoke_eq!(i64, a, driver, "foo_a", foo.clone()); + assert_invoke_eq!(bool, b, driver, "foo_b", foo.clone()); + assert_invoke_eq!(f64, c, driver, "foo_c", foo); +} diff --git a/crates/mun_runtime_capi/ffi b/crates/mun_runtime_capi/ffi index 10061b7f8..fdacb0bee 160000 --- a/crates/mun_runtime_capi/ffi +++ b/crates/mun_runtime_capi/ffi @@ -1 +1 @@ -Subproject commit 10061b7f898ea4f3c0b1b9e1d2921e46a7109f38 +Subproject commit fdacb0beed06b02bf4576e255f222190f3c77acf diff --git a/crates/tools/src/abi.rs b/crates/tools/src/abi.rs index d4a3a2da3..06e6ec6b0 100644 --- a/crates/tools/src/abi.rs +++ b/crates/tools/src/abi.rs @@ -26,6 +26,8 @@ impl ParseCallbacks for RemoveVendorName { fn item_name(&self, original_item_name: &str) -> Option { if original_item_name == "MunPrivacy_t" { Some("Privacy".to_string()) + } else if original_item_name == "MunTypeGroup_t" { + Some("TypeGroup".to_string()) } else { Some(original_item_name.trim_start_matches("Mun").to_string()) } @@ -44,7 +46,6 @@ pub fn generate(mode: Mode) -> Result<()> { let bindings = bindgen::Builder::default() .header(input_file_str) .whitelist_type("Mun.*") - .blacklist_type("MunPrivacy.*") // Remove type aliasing on Linux .blacklist_type("__uint8_t") .blacklist_type("__uint16_t") @@ -54,7 +55,7 @@ pub fn generate(mode: Mode) -> Result<()> { .derive_copy(false) .derive_debug(false) .raw_line("#![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]") - .raw_line("use crate::Privacy;") + .raw_line("use crate::{Privacy, TypeGroup};") .generate() .map_err(|_| format_err!("Unable to generate bindings from 'mun_abi.h'"))?;