Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] #4060: add support for boxed slices in FFI #4062

Merged
merged 1 commit into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
DCNick3 marked this conversation as resolved.
Show resolved Hide resolved

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());
DCNick3 marked this conversation as resolved.
Show resolved Hide resolved
// 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
Loading