diff --git a/Cargo.toml b/Cargo.toml index 26eb80f2ff9..64be76ef3f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -135,12 +135,13 @@ parity-scale-codec = { version = "3.6.5", default-features = false } json5 = "0.4.1" [workspace.lints] +rustdoc.private_doc_tests = "deny" + rust.anonymous_parameters = "deny" rust.future_incompatible = "deny" rust.missing_copy_implementations = "deny" rust.missing_docs = "deny" rust.nonstandard_style = "deny" -rust.private_doc_tests = "deny" rust.rust_2018_idioms = "deny" rust.trivial_casts = "deny" rust.trivial_numeric_casts = "deny" diff --git a/ffi/derive/src/convert.rs b/ffi/derive/src/convert.rs index 046c6cfd2a2..9a3c256e337 100644 --- a/ffi/derive/src/convert.rs +++ b/ffi/derive/src/convert.rs @@ -254,7 +254,6 @@ pub fn derive_ffi_type(emitter: &mut Emitter, input: &syn2::DeriveInput) -> Toke }; let name = &input.ident; - if let darling::ast::Data::Enum(variants) = &input.data { if variants.is_empty() { emit!( diff --git a/ffi/derive/src/wrapper.rs b/ffi/derive/src/wrapper.rs index 0096502cdc2..01aec489542 100644 --- a/ffi/derive/src/wrapper.rs +++ b/ffi/derive/src/wrapper.rs @@ -382,6 +382,7 @@ fn gen_impl_ffi(name: &Ident, generics: &syn2::Generics) -> TokenStream { type Ref<#lifetime> = &#lifetime iroha_ffi::Extern where #(#lifetime_bounded_where_clause),*; type RefMut<#lifetime> = &#lifetime mut iroha_ffi::Extern where #(#lifetime_bounded_where_clause),*; type Box = Box; + type SliceBox = Box<[iroha_ffi::Extern]>; type SliceRef<#lifetime> = &#lifetime [iroha_ffi::ir::Transparent] where #(#lifetime_bounded_where_clause),*; type SliceRefMut<#lifetime> = &#lifetime mut [iroha_ffi::ir::Transparent] where #(#lifetime_bounded_where_clause),*; type Vec = Vec; diff --git a/ffi/src/ir.rs b/ffi/src/ir.rs index db49fae2b4a..6c99c9dbf69 100644 --- a/ffi/src/ir.rs +++ b/ffi/src/ir.rs @@ -105,6 +105,8 @@ pub trait IrTypeFamily { Self: 'itm; /// [`Ir`] type that [`Box`] is mapped into type Box; + /// [`Ir`] type that `Box<[T]>` is mapped into + type SliceBox; /// [`Ir`] type that `&[T]` is mapped into type SliceRef<'itm> where @@ -124,6 +126,7 @@ impl IrTypeFamily for R { // NOTE: Unused type RefMut<'itm> = () where Self: 'itm; type Box = Box; + type SliceBox = Box<[Self]>; type SliceRef<'itm> = &'itm [Self] where Self: 'itm; // NOTE: Unused type SliceRefMut<'itm> = () where Self: 'itm; @@ -134,6 +137,7 @@ impl IrTypeFamily for Robust { type Ref<'itm> = Transparent; type RefMut<'itm> = Transparent; type Box = Box; + type SliceBox = Box<[Self]>; type SliceRef<'itm> = &'itm [Self]; type SliceRefMut<'itm> = &'itm mut [Self]; type Vec = Vec; @@ -143,6 +147,7 @@ impl IrTypeFamily for Opaque { type Ref<'itm> = Transparent; type RefMut<'itm> = Transparent; type Box = Box; + type SliceBox = Box<[Self]>; type SliceRef<'itm> = &'itm [Self]; type SliceRefMut<'itm> = &'itm mut [Self]; type Vec = Vec; @@ -152,6 +157,7 @@ impl IrTypeFamily for Transparent { type Ref<'itm> = Self; type RefMut<'itm> = Self; type Box = Box; + type SliceBox = Box<[Self]>; type SliceRef<'itm> = &'itm [Self]; type SliceRefMut<'itm> = &'itm mut [Self]; type Vec = Vec; @@ -161,6 +167,7 @@ impl IrTypeFamily for &Extern { type Ref<'itm> = &'itm Self where Self: 'itm; type RefMut<'itm> = &'itm mut Self where Self: 'itm; type Box = Box; + type SliceBox = Box<[Self]>; type SliceRef<'itm> = &'itm [Self] where Self: 'itm; type SliceRefMut<'itm> = &'itm mut [Self] where Self: 'itm; type Vec = Vec; @@ -170,6 +177,7 @@ impl IrTypeFamily for &mut Extern { type Ref<'itm> = &'itm Self where Self: 'itm; type RefMut<'itm> = &'itm mut Self where Self: 'itm; type Box = Box; + type SliceBox = Box<[Self]>; type SliceRef<'itm> = &'itm [Self] where Self: 'itm; type SliceRefMut<'itm> = &'itm mut [Self] where Self: 'itm; type Vec = Vec; @@ -209,6 +217,12 @@ where { type Type = ::Box; } +impl Ir for Box<[R]> +where + R::Type: IrTypeFamily, +{ + type Type = ::SliceBox; +} impl<'itm, R: Ir> Ir for &'itm [R] where R::Type: IrTypeFamily, diff --git a/ffi/src/repr_c.rs b/ffi/src/repr_c.rs index f7efe9d08d8..96964f6304b 100644 --- a/ffi/src/repr_c.rs +++ b/ffi/src/repr_c.rs @@ -687,6 +687,53 @@ impl COutPtrRead> for Box { } } +impl CType> for Box<[R]> { + type ReprC = SliceMut; +} +impl CTypeConvert<'_, Box<[Robust]>, SliceMut> for Box<[R]> { + type RustStore = Self; + type FfiStore = (); + + fn into_repr_c(self, store: &mut Self::RustStore) -> SliceMut { + *store = self; + SliceMut::from_slice(Some(store.as_mut())) + } + + unsafe fn try_from_repr_c(source: SliceMut, _: &mut ()) -> Result { + source + .into_rust() + .ok_or(FfiReturn::ArgIsNull) + .map(|slice| (&*slice).into()) + } +} + +impl CWrapperType> for Box<[R]> { + type InputType = Self; + type ReturnType = Self; +} +impl COutPtr> for Box<[R]> { + type OutPtr = OutBoxedSlice; +} +impl COutPtrWrite> for Box<[R]> { + unsafe fn write_out(self, out_ptr: *mut Self::OutPtr) { + let mut store = Box::default(); + CTypeConvert::, _>::into_repr_c(self, &mut store); + out_ptr.write(OutBoxedSlice::from_boxed_slice(Some(store))); + } +} +impl COutPtrRead> for Box<[R]> { + unsafe fn try_read_out(out_ptr: Self::OutPtr) -> Result { + let slice = SliceMut::from_raw_parts_mut(out_ptr.as_mut_ptr(), out_ptr.len()); + let res = CTypeConvert::, _>::try_from_repr_c(slice, &mut ()); + + if !out_ptr.deallocate() { + return Err(FfiReturn::TrapRepresentation); + } + + res + } +} + impl CType<&[Robust]> for &[R] { type ReprC = SliceRef; } @@ -868,6 +915,55 @@ impl COutPtrWrite> for Box { } } +impl CType> for Box<[R]> { + type ReprC = SliceMut<*mut R>; +} +impl CTypeConvert<'_, Box<[Opaque]>, SliceMut<*mut R>> for Box<[R]> { + type RustStore = Box<[*mut R]>; + type FfiStore = (); + + fn into_repr_c(self, store: &mut Self::RustStore) -> SliceMut<*mut R> { + let mut boxed_slice = ManuallyDrop::new(self); + + let (ptr, len) = (boxed_slice.as_mut_ptr(), boxed_slice.len()); + let boxed_slice = unsafe { Vec::from_raw_parts(ptr, len, len) }; + + *store = boxed_slice + .into_iter() + .map(|a: R| Box::new(a)) + .map(Box::into_raw) + .collect(); + + SliceMut::from_slice(Some(store)) + } + + unsafe fn try_from_repr_c(source: SliceMut<*mut R>, _: &mut ()) -> Result { + source + .into_rust() + .ok_or(FfiReturn::ArgIsNull)? + .iter() + .map(|&item| { + if let Some(item) = item.as_mut() { + return Ok(*Box::from_raw(item)); + } + + Err(FfiReturn::ArgIsNull) + }) + .collect::>() + } +} + +impl COutPtr> for Box<[R]> { + type OutPtr = OutBoxedSlice<*mut R>; +} +impl COutPtrWrite> for Box<[R]> { + unsafe fn write_out(self, out_ptr: *mut Self::OutPtr) { + let mut store = Box::default(); + CTypeConvert::, _>::into_repr_c(self, &mut store); + out_ptr.write(OutBoxedSlice::from_boxed_slice(Some(store))); + } +} + impl CType<&[Opaque]> for &[R] { type ReprC = SliceRef<*const R>; } @@ -1081,6 +1177,10 @@ impl<'itm, R: External> CWrapperType> for Box<&'itm R> { type InputType = Box>; type ReturnType = Box>; } +impl<'itm, R: External> CWrapperType> for Box<[&'itm R]> { + type InputType = Box<[R::RefType<'itm>]>; + type ReturnType = Box<[R::RefType<'itm>]>; +} impl<'itm, R: External> CWrapperType<&'itm [&'itm Extern]> for &'itm [&'itm R] { type InputType = &'itm [R::RefType<'itm>]; type ReturnType = &'itm [R::RefType<'itm>]; @@ -1106,6 +1206,10 @@ impl<'itm, R: External> CWrapperType> for Box<&'itm mut R> type InputType = Box>; type ReturnType = Box>; } +impl<'itm, R: External> CWrapperType> for Box<[&'itm mut R]> { + type InputType = Box<[R::RefMutType<'itm>]>; + type ReturnType = Box<[R::RefMutType<'itm>]>; +} impl<'itm, R: External> CWrapperType<&'itm [&'itm mut Extern]> for &'itm [&'itm mut R] { type InputType = &'itm [R::RefMutType<'itm>]; type ReturnType = &'itm [R::RefMutType<'itm>]; @@ -1269,6 +1373,63 @@ where } } +impl CType> for Box<[R]> +where + Box<[R::Target]>: FfiType, +{ + type ReprC = as FfiType>::ReprC; +} +impl<'itm, R: Transmute, C: ReprC> CTypeConvert<'itm, Box<[Transparent]>, C> for Box<[R]> +where + Box<[R::Target]>: FfiConvert<'itm, C>, +{ + type RustStore = as FfiConvert<'itm, C>>::RustStore; + type FfiStore = as FfiConvert<'itm, C>>::FfiStore; + + fn into_repr_c(self, store: &'itm mut Self::RustStore) -> C { + transmute_into_target_boxed_slice(self).into_ffi(store) + } + + unsafe fn try_from_repr_c(source: C, store: &'itm mut Self::FfiStore) -> Result { + >::try_from_ffi(source, store) + .and_then(|output| transmute_from_target_boxed_slice(output)) + } +} + +impl CWrapperType> for Box<[R]> +where + Box<[R::Target]>: FfiWrapperType, + as FfiWrapperType>::InputType: WrapperTypeOf, + as FfiWrapperType>::ReturnType: WrapperTypeOf, +{ + type InputType = < as FfiWrapperType>::InputType as WrapperTypeOf>::Type; + type ReturnType = + < as FfiWrapperType>::ReturnType as WrapperTypeOf>::Type; +} +impl COutPtr> for Box<[R]> +where + Box<[R::Target]>: FfiOutPtr, +{ + type OutPtr = as FfiOutPtr>::OutPtr; +} +impl COutPtrWrite> for Box<[R]> +where + Box<[R::Target]>: FfiOutPtrWrite, +{ + unsafe fn write_out(self, out_ptr: *mut Self::OutPtr) { + FfiOutPtrWrite::write_out(transmute_into_target_boxed_slice(self), out_ptr); + } +} +impl COutPtrRead> for Box<[R]> +where + Box<[R::Target]>: FfiOutPtrRead, +{ + unsafe fn try_read_out(out_ptr: Self::OutPtr) -> Result { + >::try_read_out(out_ptr) + .and_then(|output| transmute_from_target_boxed_slice(output)) + } +} + impl<'slice, R: Transmute> CType<&'slice [Transparent]> for &'slice [R] where &'slice [R::Target]: FfiType, @@ -1453,6 +1614,11 @@ unsafe impl NonLocal> for Box where { } // SAFETY: Type doesn't return a reference to the store if the inner type doesn't +unsafe impl NonLocal> for Box<[R]> where + Box<[R::Target]>: Ir + NonLocal< as Ir>::Type> +{ +} +// SAFETY: Type doesn't return a reference to the store if the inner type doesn't unsafe impl<'slice, R: Transmute> NonLocal<&'slice [Transparent]> for &'slice [R] where &'slice [R::Target]: Ir + NonLocal<<&'slice [R::Target] as Ir>::Type> { @@ -1506,6 +1672,24 @@ unsafe fn transmute_from_target_box(source: Box) -> Res Ok(Box::from_raw(Box::into_raw(source).cast::())) } +fn transmute_into_target_boxed_slice(mut source: Box<[R]>) -> Box<[R::Target]> { + let (ptr, len) = (source.as_mut_ptr().cast::(), source.len()); + // SAFETY: `R` is guaranteed to be transmutable into `R::Target` + unsafe { Box::from_raw(core::slice::from_raw_parts_mut(ptr, len)) } +} +unsafe fn transmute_from_target_boxed_slice( + mut source: Box<[R::Target]>, +) -> Result> { + if !source.iter().all(|item| R::is_valid(item)) { + return Err(FfiReturn::TrapRepresentation); + } + + Ok(Box::from_raw(core::slice::from_raw_parts_mut( + source.as_mut_ptr().cast(), + source.len(), + ))) +} + fn transmute_into_target_slice_ref(source: &[R]) -> &[R::Target] { let (ptr, len) = (source.as_ptr().cast::(), source.len()); // SAFETY: `R` is guaranteed to be transmutable into `R::Target` diff --git a/ffi/src/slice.rs b/ffi/src/slice.rs index 1583e915e0b..b62712e3900 100644 --- a/ffi/src/slice.rs +++ b/ffi/src/slice.rs @@ -165,6 +165,17 @@ impl OutBoxedSlice { self.1 } + /// Create [`Self`] from a `Vec` + pub fn from_boxed_slice(source: Option>) -> Self { + source.map_or_else( + || Self(core::ptr::null_mut(), 0), + |boxed_slice| { + let mut boxed_slice = core::mem::ManuallyDrop::new(boxed_slice); + Self(boxed_slice.as_mut_ptr(), boxed_slice.len()) + }, + ) + } + /// Create [`Self`] from a `Vec` pub fn from_vec(source: Option>) -> Self { source.map_or_else( diff --git a/ffi/tests/ffi_export.rs b/ffi/tests/ffi_export.rs index 4d0e8857a9d..da6ebaa4d20 100644 --- a/ffi/tests/ffi_export.rs +++ b/ffi/tests/ffi_export.rs @@ -128,6 +128,12 @@ impl OpaqueStruct { } } +#[ffi_export] +/// Take and return boxed slice +pub fn freestanding_with_boxed_slice(item: Box<[u8]>) -> Box<[u8]> { + item +} + #[ffi_export] /// Take and return byte pub fn freestanding_with_option(item: Option) -> Option { @@ -428,6 +434,29 @@ fn return_option() { } } +#[test] +#[webassembly_test::webassembly_test] +fn take_and_return_boxed_slice() { + let input: Box<[u8]> = [12u8, 42u8].into(); + let mut output = MaybeUninit::new(OutBoxedSlice::from_raw_parts(core::ptr::null_mut(), 0)); + let mut in_store = Default::default(); + + unsafe { + assert_eq!( + FfiReturn::Ok, + __freestanding_with_boxed_slice( + FfiConvert::into_ffi(input, &mut in_store), + output.as_mut_ptr() + ) + ); + + let output = output.assume_init(); + assert_eq!(output.len(), 2); + let boxed_slice = Box::<[u8]>::try_read_out(output).expect("Valid"); + assert_eq!(boxed_slice, [12u8, 42u8].into()); + } +} + #[test] #[webassembly_test::webassembly_test] fn take_and_return_option_without_niche() { diff --git a/ffi/tests/ffi_import.rs b/ffi/tests/ffi_import.rs index 570a5ada0dd..4d560620ad9 100644 --- a/ffi/tests/ffi_import.rs +++ b/ffi/tests/ffi_import.rs @@ -25,6 +25,11 @@ pub fn freestanding_returns_local_slice(input: &[(u32, u32)]) -> &[(u32, u32)] { unreachable!("replaced by ffi_import") } +#[ffi_import] +pub fn freestanding_returns_boxed_slice(input: Box<[u32]>) -> Box<[u32]> { + unreachable!("replaced by ffi_import") +} + #[ffi_import] pub fn freestanding_returns_iterator( input: impl IntoIterator, @@ -76,6 +81,14 @@ fn vec_of_tuples_is_coppied_when_returned() { assert_eq!(in_tuple, *out_tuple); } +#[test] +#[webassembly_test::webassembly_test] +fn boxed_slice_of_primitives() { + let in_boxed_slice = vec![420_u32, 420_u32].into_boxed_slice(); + let out_boxed_slice: Box<[u32]> = freestanding_returns_boxed_slice(in_boxed_slice.clone()); + assert_eq!(in_boxed_slice, out_boxed_slice); +} + #[test] #[webassembly_test::webassembly_test] fn return_iterator() { @@ -152,6 +165,16 @@ mod ffi { FfiReturn::Ok } + #[no_mangle] + unsafe extern "C" fn __freestanding_returns_boxed_slice( + input: SliceMut, + output: *mut OutBoxedSlice, + ) -> FfiReturn { + let input = input.into_rust().map(|slice| (&*slice).into()); + output.write(OutBoxedSlice::from_boxed_slice(input)); + FfiReturn::Ok + } + #[no_mangle] unsafe extern "C" fn __freestanding_returns_iterator( input: SliceMut, diff --git a/primitives/src/const_vec.rs b/primitives/src/const_vec.rs index f53d1b0d657..30974346c16 100644 --- a/primitives/src/const_vec.rs +++ b/primitives/src/const_vec.rs @@ -21,7 +21,8 @@ ffi::ffi_item! { #[repr(transparent)] pub struct ConstVec(Box<[T]>); - ffi_type(opaque) + // SAFETY: `ConstVec` has no trap representation in ConstVec + ffi_type(unsafe {robust}) } impl ConstVec {