Skip to content

Commit

Permalink
[feature] #4060: add support for boxed slices in FFI (#4062)
Browse files Browse the repository at this point in the history
Signed-off-by: Marin Veršić <marin.versic101@gmail.com>
  • Loading branch information
mversic committed Nov 21, 2023
1 parent 0511141 commit 73f677b
Show file tree
Hide file tree
Showing 9 changed files with 263 additions and 3 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 0 additions & 1 deletion ffi/derive/src/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!(
Expand Down
1 change: 1 addition & 0 deletions ffi/derive/src/wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<iroha_ffi::Extern>;
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<iroha_ffi::ir::Transparent>;
Expand Down
14 changes: 14 additions & 0 deletions ffi/src/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ pub trait IrTypeFamily {
Self: 'itm;
/// [`Ir`] type that [`Box<T>`] 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
Expand All @@ -124,6 +126,7 @@ impl<R: Cloned> IrTypeFamily for R {
// NOTE: Unused
type RefMut<'itm> = () where Self: 'itm;
type Box = Box<Self>;
type SliceBox = Box<[Self]>;
type SliceRef<'itm> = &'itm [Self] where Self: 'itm;
// NOTE: Unused
type SliceRefMut<'itm> = () where Self: 'itm;
Expand All @@ -134,6 +137,7 @@ impl IrTypeFamily for Robust {
type Ref<'itm> = Transparent;
type RefMut<'itm> = Transparent;
type Box = Box<Self>;
type SliceBox = Box<[Self]>;
type SliceRef<'itm> = &'itm [Self];
type SliceRefMut<'itm> = &'itm mut [Self];
type Vec = Vec<Self>;
Expand All @@ -143,6 +147,7 @@ impl IrTypeFamily for Opaque {
type Ref<'itm> = Transparent;
type RefMut<'itm> = Transparent;
type Box = Box<Self>;
type SliceBox = Box<[Self]>;
type SliceRef<'itm> = &'itm [Self];
type SliceRefMut<'itm> = &'itm mut [Self];
type Vec = Vec<Self>;
Expand All @@ -152,6 +157,7 @@ impl IrTypeFamily for Transparent {
type Ref<'itm> = Self;
type RefMut<'itm> = Self;
type Box = Box<Self>;
type SliceBox = Box<[Self]>;
type SliceRef<'itm> = &'itm [Self];
type SliceRefMut<'itm> = &'itm mut [Self];
type Vec = Vec<Self>;
Expand All @@ -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<Self>;
type SliceBox = Box<[Self]>;
type SliceRef<'itm> = &'itm [Self] where Self: 'itm;
type SliceRefMut<'itm> = &'itm mut [Self] where Self: 'itm;
type Vec = Vec<Self>;
Expand All @@ -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<Self>;
type SliceBox = Box<[Self]>;
type SliceRef<'itm> = &'itm [Self] where Self: 'itm;
type SliceRefMut<'itm> = &'itm mut [Self] where Self: 'itm;
type Vec = Vec<Self>;
Expand Down Expand Up @@ -209,6 +217,12 @@ where
{
type Type = <R::Type as IrTypeFamily>::Box;
}
impl<R: Ir> Ir for Box<[R]>
where
R::Type: IrTypeFamily,
{
type Type = <R::Type as IrTypeFamily>::SliceBox;
}
impl<'itm, R: Ir> Ir for &'itm [R]
where
R::Type: IrTypeFamily,
Expand Down
181 changes: 181 additions & 0 deletions ffi/src/repr_c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,53 @@ impl<R: ReprC> COutPtrRead<Box<Robust>> for Box<R> {
}
}

impl<R: ReprC> CType<Box<[Robust]>> for Box<[R]> {
type ReprC = SliceMut<R>;
}
impl<R: ReprC> CTypeConvert<'_, Box<[Robust]>, SliceMut<R>> for Box<[R]> {
type RustStore = Self;
type FfiStore = ();

fn into_repr_c(self, store: &mut Self::RustStore) -> SliceMut<R> {
*store = self;
SliceMut::from_slice(Some(store.as_mut()))
}

unsafe fn try_from_repr_c(source: SliceMut<R>, _: &mut ()) -> Result<Self> {
source
.into_rust()
.ok_or(FfiReturn::ArgIsNull)
.map(|slice| (&*slice).into())
}
}

impl<R: ReprC> CWrapperType<Box<[Robust]>> for Box<[R]> {
type InputType = Self;
type ReturnType = Self;
}
impl<R: ReprC> COutPtr<Box<[Robust]>> for Box<[R]> {
type OutPtr = OutBoxedSlice<R>;
}
impl<R: ReprC> COutPtrWrite<Box<[Robust]>> for Box<[R]> {
unsafe fn write_out(self, out_ptr: *mut Self::OutPtr) {
let mut store = Box::default();
CTypeConvert::<Box<[Robust]>, _>::into_repr_c(self, &mut store);
out_ptr.write(OutBoxedSlice::from_boxed_slice(Some(store)));
}
}
impl<R: ReprC> COutPtrRead<Box<[Robust]>> for Box<[R]> {
unsafe fn try_read_out(out_ptr: Self::OutPtr) -> Result<Self> {
let slice = SliceMut::from_raw_parts_mut(out_ptr.as_mut_ptr(), out_ptr.len());
let res = CTypeConvert::<Box<[Robust]>, _>::try_from_repr_c(slice, &mut ());

if !out_ptr.deallocate() {
return Err(FfiReturn::TrapRepresentation);
}

res
}
}

impl<R: ReprC> CType<&[Robust]> for &[R] {
type ReprC = SliceRef<R>;
}
Expand Down Expand Up @@ -868,6 +915,50 @@ impl<R> COutPtrWrite<Box<Opaque>> for Box<R> {
}
}

impl<R> CType<Box<[Opaque]>> for Box<[R]> {
type ReprC = SliceMut<*mut R>;
}
impl<R> 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> {
*store = Vec::from(self)
.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<Self> {
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::<core::result::Result<_, _>>()
}
}

impl<R> COutPtr<Box<[Opaque]>> for Box<[R]> {
type OutPtr = OutBoxedSlice<*mut R>;
}
impl<R> COutPtrWrite<Box<[Opaque]>> for Box<[R]> {
unsafe fn write_out(self, out_ptr: *mut Self::OutPtr) {
let mut store = Box::default();
CTypeConvert::<Box<[Opaque]>, _>::into_repr_c(self, &mut store);
out_ptr.write(OutBoxedSlice::from_boxed_slice(Some(store)));
}
}

impl<R> CType<&[Opaque]> for &[R] {
type ReprC = SliceRef<*const R>;
}
Expand Down Expand Up @@ -1081,6 +1172,10 @@ impl<'itm, R: External> CWrapperType<Box<&'itm Extern>> for Box<&'itm R> {
type InputType = Box<R::RefType<'itm>>;
type ReturnType = Box<R::RefType<'itm>>;
}
impl<'itm, R: External> CWrapperType<Box<[&'itm Extern]>> 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>];
Expand All @@ -1106,6 +1201,10 @@ impl<'itm, R: External> CWrapperType<Box<&'itm mut Extern>> for Box<&'itm mut R>
type InputType = Box<R::RefMutType<'itm>>;
type ReturnType = Box<R::RefMutType<'itm>>;
}
impl<'itm, R: External> CWrapperType<Box<[&'itm mut Extern]>> 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>];
Expand Down Expand Up @@ -1269,6 +1368,63 @@ where
}
}

impl<R: Transmute> CType<Box<[Transparent]>> for Box<[R]>
where
Box<[R::Target]>: FfiType,
{
type ReprC = <Box<[R::Target]> 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 = <Box<[R::Target]> as FfiConvert<'itm, C>>::RustStore;
type FfiStore = <Box<[R::Target]> 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<Self> {
<Box<[R::Target]>>::try_from_ffi(source, store)
.and_then(|output| transmute_from_target_boxed_slice(output))
}
}

impl<R: Transmute> CWrapperType<Box<[Transparent]>> for Box<[R]>
where
Box<[R::Target]>: FfiWrapperType,
<Box<[R::Target]> as FfiWrapperType>::InputType: WrapperTypeOf<Self>,
<Box<[R::Target]> as FfiWrapperType>::ReturnType: WrapperTypeOf<Self>,
{
type InputType = <<Box<[R::Target]> as FfiWrapperType>::InputType as WrapperTypeOf<Self>>::Type;
type ReturnType =
<<Box<[R::Target]> as FfiWrapperType>::ReturnType as WrapperTypeOf<Self>>::Type;
}
impl<R: Transmute> COutPtr<Box<[Transparent]>> for Box<[R]>
where
Box<[R::Target]>: FfiOutPtr,
{
type OutPtr = <Box<[R::Target]> as FfiOutPtr>::OutPtr;
}
impl<R: Transmute> COutPtrWrite<Box<[Transparent]>> 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<R: Transmute> COutPtrRead<Box<[Transparent]>> for Box<[R]>
where
Box<[R::Target]>: FfiOutPtrRead,
{
unsafe fn try_read_out(out_ptr: Self::OutPtr) -> Result<Self> {
<Box<[R::Target]>>::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,
Expand Down Expand Up @@ -1453,6 +1609,11 @@ unsafe impl<R: Transmute> NonLocal<Box<Transparent>> for Box<R> where
{
}
// SAFETY: Type doesn't return a reference to the store if the inner type doesn't
unsafe impl<R: Transmute> NonLocal<Box<[Transparent]>> for Box<[R]> where
Box<[R::Target]>: Ir + NonLocal<<Box<[R::Target]> 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>
{
Expand Down Expand Up @@ -1506,6 +1667,26 @@ unsafe fn transmute_from_target_box<R: Transmute>(source: Box<R::Target>) -> Res
Ok(Box::from_raw(Box::into_raw(source).cast::<R>()))
}

#[allow(clippy::boxed_local)]
fn transmute_into_target_boxed_slice<R: Transmute>(mut source: Box<[R]>) -> Box<[R::Target]> {
let (ptr, len) = (source.as_mut_ptr().cast::<R::Target>(), source.len());
// SAFETY: `R` is guaranteed to be transmutable into `R::Target`
unsafe { Box::from_raw(core::slice::from_raw_parts_mut(ptr, len)) }
}
#[allow(clippy::boxed_local)]
unsafe fn transmute_from_target_boxed_slice<R: Transmute>(
mut source: Box<[R::Target]>,
) -> Result<Box<[R]>> {
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<R: Transmute>(source: &[R]) -> &[R::Target] {
let (ptr, len) = (source.as_ptr().cast::<R::Target>(), source.len());
// SAFETY: `R` is guaranteed to be transmutable into `R::Target`
Expand Down
11 changes: 11 additions & 0 deletions ffi/src/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,17 @@ impl<C: ReprC> OutBoxedSlice<C> {
self.1
}

/// Create [`Self`] from a `Box<[T]>`
pub fn from_boxed_slice(source: Option<Box<[C]>>) -> 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<T>`
pub fn from_vec(source: Option<Vec<C>>) -> Self {
source.map_or_else(
Expand Down
29 changes: 29 additions & 0 deletions ffi/tests/ffi_export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8>) -> Option<u8> {
Expand Down Expand Up @@ -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() {
Expand Down
Loading

0 comments on commit 73f677b

Please sign in to comment.