From 012c5d6970146a6615b60daeb7b2185c4b4adb5f Mon Sep 17 00:00:00 2001 From: Wodann Date: Sat, 11 Jan 2020 16:29:36 +0100 Subject: [PATCH 1/5] test(abi): add missing test cases --- crates/mun_abi/src/autogen_impl.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/crates/mun_abi/src/autogen_impl.rs b/crates/mun_abi/src/autogen_impl.rs index 4223e464d..f049dc230 100644 --- a/crates/mun_abi/src/autogen_impl.rs +++ b/crates/mun_abi/src/autogen_impl.rs @@ -516,7 +516,16 @@ 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); @@ -536,7 +545,16 @@ 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); From 94623d4ec544ad00bbce406487d5a534b819bced Mon Sep 17 00:00:00 2001 From: Wodann Date: Sun, 12 Jan 2020 09:49:28 +0100 Subject: [PATCH 2/5] feat(abi): add additional struct field information --- crates/mun_abi/c | 2 +- crates/mun_abi/src/autogen.rs | 42 ++++++++- crates/mun_abi/src/autogen_impl.rs | 94 +++++++++++++++++--- crates/mun_codegen/src/code_gen.rs | 19 ++-- crates/mun_codegen/src/code_gen/abi_types.rs | 9 +- crates/mun_codegen/src/code_gen/symbols.rs | 72 +++++++++++++-- crates/mun_runtime/src/test.rs | 47 ++++++---- 7 files changed, 237 insertions(+), 48 deletions(-) diff --git a/crates/mun_abi/c b/crates/mun_abi/c index 420fc752f..4ee1af429 160000 --- a/crates/mun_abi/c +++ b/crates/mun_abi/c @@ -1 +1 @@ -Subproject commit 420fc752f88626cd21bc6ac232afb522402060b8 +Subproject commit 4ee1af429d996df33f9895e0ed3ea8cbcde34a8e diff --git a/crates/mun_abi/src/autogen.rs b/crates/mun_abi/src/autogen.rs index 10d58b52e..7cf215502 100644 --- a/crates/mun_abi/src/autogen.rs +++ b/crates/mun_abi/src/autogen.rs @@ -214,8 +214,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 +229,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 +248,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 +267,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 f049dc230..fc5bb0c41 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 fields' types. + /// 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 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 { @@ -260,6 +289,7 @@ mod tests { }; const FAKE_TYPE_NAME: &str = "type-name"; + const FAKE_FIELD_NAME: &str = "field-name"; #[test] fn test_type_info_name() { @@ -353,11 +383,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 +409,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 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( @@ -443,7 +513,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."); diff --git a/crates/mun_codegen/src/code_gen.rs b/crates/mun_codegen/src/code_gen.rs index 8ec606896..717806337 100644 --- a/crates/mun_codegen/src/code_gen.rs +++ b/crates/mun_codegen/src/code_gen.rs @@ -57,15 +57,6 @@ pub fn write_module_shared_object( .link_in_module(module.llvm_module.clone()) .map_err(|e| CodeGenerationError::ModuleLinkerError(e.to_string()))?; - // Generate the `get_info` method. - symbols::gen_reflection_ir( - db, - &module.functions, - &module.structs, - &module.dispatch_table, - &assembly_module, - ); - // Initialize the x86 target Target::initialize_x86(&InitializationConfig::default()); @@ -92,6 +83,16 @@ pub fn write_module_shared_object( ) .ok_or(CodeGenerationError::CouldNotCreateTargetMachine)?; + // Generate the `get_info` method. + symbols::gen_reflection_ir( + db, + &module.functions, + &module.structs, + &module.dispatch_table, + &assembly_module, + &target_machine, + ); + // 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..2de08cac6 100644 --- a/crates/mun_codegen/src/code_gen/abi_types.rs +++ b/crates/mun_codegen/src/code_gen/abi_types.rs @@ -65,9 +65,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, ); diff --git a/crates/mun_codegen/src/code_gen/symbols.rs b/crates/mun_codegen/src/code_gen/symbols.rs index 91aa18180..a0bb57a92 100644 --- a/crates/mun_codegen/src/code_gen/symbols.rs +++ b/crates/mun_codegen/src/code_gen/symbols.rs @@ -8,6 +8,7 @@ use hir::{Ty, TypeCtor}; use inkwell::{ attributes::Attribute, module::{Linkage, Module}, + targets::TargetMachine, types::StructType, values::{FunctionValue, IntValue, PointerValue, StructValue, UnnamedAddress}, AddressSpace, @@ -223,24 +224,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 +272,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 +388,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 +404,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_runtime/src/test.rs b/crates/mun_runtime/src/test.rs index b74faf587..f25ce93b4 100644 --- a/crates/mun_runtime/src/test.rs +++ b/crates/mun_runtime/src/test.rs @@ -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 + ); - // Check foo name for valid utf8 + 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 + ); + } + + 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); From 03c7618df10d63644f499765724eea055dd65c5e Mon Sep 17 00:00:00 2001 From: Wodann Date: Mon, 13 Jan 2020 19:48:17 +0100 Subject: [PATCH 3/5] feat(abi): add type group to type information to distinguish between structs and other types --- crates/mun_abi/c | 2 +- crates/mun_abi/src/autogen.rs | 16 ++++++++-- crates/mun_abi/src/autogen_impl.rs | 33 ++++++++++---------- crates/mun_abi/src/lib.rs | 32 +++++++++++++++++-- crates/mun_codegen/Cargo.toml | 1 + crates/mun_codegen/src/code_gen/abi_types.rs | 10 ++++-- crates/mun_codegen/src/code_gen/symbols.rs | 10 +++--- crates/mun_codegen/src/type_info.rs | 27 ++++++++++------ crates/mun_runtime/src/lib.rs | 7 ++++- crates/mun_runtime_capi/ffi | 2 +- crates/tools/src/abi.rs | 5 +-- 11 files changed, 105 insertions(+), 40 deletions(-) diff --git a/crates/mun_abi/c b/crates/mun_abi/c index 4ee1af429..fbcc74d0e 160000 --- a/crates/mun_abi/c +++ b/crates/mun_abi/c @@ -1 +1 @@ -Subproject commit 4ee1af429d996df33f9895e0ed3ea8cbcde34a8e +Subproject commit fbcc74d0e4971c82955a7959d9dc5fda8208e578 diff --git a/crates/mun_abi/src/autogen.rs b/crates/mun_abi/src/autogen.rs index 7cf215502..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 = ""] diff --git a/crates/mun_abi/src/autogen_impl.rs b/crates/mun_abi/src/autogen_impl.rs index fc5bb0c41..252b671d7 100644 --- a/crates/mun_abi/src/autogen_impl.rs +++ b/crates/mun_abi/src/autogen_impl.rs @@ -277,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, } } @@ -294,7 +295,7 @@ mod tests { #[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); } @@ -302,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); } @@ -353,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."); @@ -374,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."); @@ -439,7 +440,7 @@ mod tests { 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]; @@ -500,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."); @@ -563,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."); @@ -597,7 +598,7 @@ mod tests { #[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."); @@ -626,7 +627,7 @@ mod tests { #[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."); @@ -649,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."); @@ -665,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."); @@ -681,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."); @@ -697,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."); @@ -716,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."); @@ -732,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..0cbcc0e5e 100644 --- a/crates/mun_abi/src/lib.rs +++ b/crates/mun_abi/src/lib.rs @@ -20,10 +20,10 @@ pub use reflection::Reflection; 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 +32,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_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/abi_types.rs b/crates/mun_codegen/src/code_gen/abi_types.rs index 2de08cac6..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, ); @@ -118,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 a0bb57a92..cb3245893 100644 --- a/crates/mun_codegen/src/code_gen/symbols.rs +++ b/crates/mun_codegen/src/code_gen/symbols.rs @@ -4,6 +4,7 @@ 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, @@ -18,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!(), @@ -37,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, ) 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/src/lib.rs b/crates/mun_runtime/src/lib.rs index 2bd6c27d8..125894631 100644 --- a/crates/mun_runtime/src/lib.rs +++ b/crates/mun_runtime/src/lib.rs @@ -17,7 +17,9 @@ 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, Reflection, StructInfo, TypeGroup, TypeInfo, +}; use failure::Error; use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher}; @@ -152,12 +154,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 +170,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_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'"))?; From caef098b5602f1507ae237eb5a3c3810f81749c7 Mon Sep 17 00:00:00 2001 From: Wodann Date: Sun, 12 Jan 2020 20:32:23 +0100 Subject: [PATCH 4/5] feat(runtime): add marshalling of struct types --- crates/mun/src/main.rs | 3 +- crates/mun_abi/Cargo.toml | 3 - crates/mun_abi/src/lib.rs | 7 +- crates/mun_abi/src/macros.rs | 59 --------- crates/mun_abi/src/reflection.rs | 39 ------ crates/mun_codegen/src/code_gen.rs | 26 ++-- crates/mun_runtime/Cargo.toml | 4 +- crates/mun_runtime/src/lib.rs | 15 ++- crates/mun_runtime/src/macros.rs | 99 ++++++++++++-- crates/mun_runtime/src/marshal.rs | 17 +++ crates/mun_runtime/src/reflection.rs | 118 +++++++++++++++++ crates/mun_runtime/src/struct.rs | 185 +++++++++++++++++++++++++++ crates/mun_runtime/src/test.rs | 3 +- 13 files changed, 435 insertions(+), 143 deletions(-) delete mode 100644 crates/mun_abi/src/macros.rs delete mode 100644 crates/mun_abi/src/reflection.rs create mode 100644 crates/mun_runtime/src/marshal.rs create mode 100644 crates/mun_runtime/src/reflection.rs create mode 100644 crates/mun_runtime/src/struct.rs 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/src/lib.rs b/crates/mun_abi/src/lib.rs index 0cbcc0e5e..6d6d4e289 100644 --- a/crates/mun_abi/src/lib.rs +++ b/crates/mun_abi/src/lib.rs @@ -4,22 +4,17 @@ //! 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, TypeGroup}; } 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/src/code_gen.rs b/crates/mun_codegen/src/code_gen.rs index 717806337..29994f442 100644 --- a/crates/mun_codegen/src/code_gen.rs +++ b/crates/mun_codegen/src/code_gen.rs @@ -50,13 +50,6 @@ pub fn write_module_shared_object( .unwrap_or("unknown"), ); - // 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); - assembly_module - .link_in_module(module.llvm_module.clone()) - .map_err(|e| CodeGenerationError::ModuleLinkerError(e.to_string()))?; - // Initialize the x86 target Target::initialize_x86(&InitializationConfig::default()); @@ -65,12 +58,6 @@ pub fn write_module_shared_object( .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( @@ -83,6 +70,13 @@ pub fn write_module_shared_object( ) .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); + assembly_module + .link_in_module(module.llvm_module.clone()) + .map_err(|e| CodeGenerationError::ModuleLinkerError(e.to_string()))?; + // Generate the `get_info` method. symbols::gen_reflection_ir( db, @@ -93,6 +87,12 @@ pub fn write_module_shared_object( &target_machine, ); + // Optimize the assembly module + optimize_module(&assembly_module, db.optimization_lvl()); + + // Debug print the IR + //println!("{}", assembly_module.print_to_string().to_string()); + // Generate object file let obj_file = { let obj = target_machine 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 125894631..7052efb83 100644 --- a/crates/mun_runtime/src/lib.rs +++ b/crates/mun_runtime/src/lib.rs @@ -7,25 +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, TypeGroup, 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)] 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 f25ce93b4..7a31f5258 100644 --- a/crates/mun_runtime/src/test.rs +++ b/crates/mun_runtime/src/test.rs @@ -354,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" From 6e85fa8a322de6ae5199b2a3076b3da96a6003a6 Mon Sep 17 00:00:00 2001 From: Wodann Date: Tue, 14 Jan 2020 15:42:28 +0100 Subject: [PATCH 5/5] test(struct): add test for marshalling structs --- crates/mun_runtime/src/test.rs | 49 +++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/crates/mun_runtime/src/test.rs b/crates/mun_runtime/src/test.rs index 7a31f5258..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; @@ -420,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); +}