Skip to content

Commit

Permalink
improve wasm types FFI serialization
Browse files Browse the repository at this point in the history
Signed-off-by: Marin Veršić <[email protected]>
  • Loading branch information
mversic committed Sep 8, 2022
1 parent 43c42f7 commit 1922773
Show file tree
Hide file tree
Showing 11 changed files with 616 additions and 551 deletions.
23 changes: 18 additions & 5 deletions ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,25 @@ authors = ["Iroha 2 team <https://github.com/orgs/soramitsu/teams/iroha2>"]
edition = "2021"

[features]
# TODO: fix defaults
default = ["non_robust_types", "by_ref"]
# TODO: Rename to non_robust_ref_mut
non_robust_types = []
default = ["by_ref"]

# When this feature is inactive references are cloned to prevent the foreign code from
# modifying the given reference which would cause instant UB. If the user wishes to
# send references as is across FFI they should enable this feature in their crate
#
# # Example
#
# When `by_ref` is active:
# &T -> *const T
#
# When `by_ref` is not active:
# &T -> T
by_ref = []
wasm = []

# Enables mutable references of non-robust transmutable types in FFI, non-transmutable are always disallowed.
# When handing out non-robust mutable references across FFI, it's possible that the other
# side will modify the given reference and store type's trap representation inside it.
non_robust_ref_mut = ["by_ref"]

[dependencies]
iroha_ffi_derive = { version = "=2.0.0-pre-rc.7", path = "derive" }
Expand Down
250 changes: 191 additions & 59 deletions ffi/derive/src/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ mod struct_ {
super::split_for_impl_with_type_params(&mut generics, &[]);

quote! {
impl<#generic_params> iroha_ffi::ir::NonTransmutable for #name #ty_generics #where_clause {}
impl<#generic_params> iroha_ffi::ir::NonTransmute for #name #ty_generics #where_clause {}

impl<#generic_params> iroha_ffi::option::Niche<*mut Self> for #name #ty_generics #where_clause {
const NICHE_VALUE: *mut Self = core::ptr::null_mut();
Expand Down Expand Up @@ -111,23 +111,23 @@ mod struct_ {
type ReprC = *const #name #ty_generics;

unsafe fn is_valid(source: Self::ReprC) -> iroha_ffi::FfiReturn {
if source.is_null() {
return iroha_ffi::FfiReturn::ArgIsNull;
if let Some(_) = source.as_ref() {
return iroha_ffi::FfiReturn::Ok;
}

return iroha_ffi::FfiReturn::Ok;
iroha_ffi::FfiReturn::ArgIsNull
}
}

unsafe impl<#lifetime, #generic_params> iroha_ffi::ir::Transmute for &#lifetime mut #name #ty_generics #where_clause {
type ReprC = *mut #name #ty_generics;

unsafe fn is_valid(source: Self::ReprC) -> iroha_ffi::FfiReturn {
if source.is_null() {
return iroha_ffi::FfiReturn::ArgIsNull;
if let Some(_) = source.as_mut() {
return iroha_ffi::FfiReturn::Ok;
}

return iroha_ffi::FfiReturn::Ok;
iroha_ffi::FfiReturn::ArgIsNull
}
}

Expand Down Expand Up @@ -167,11 +167,11 @@ mod struct_ {
Box::into_raw(Box::new(self))
}
unsafe fn try_from_repr_c(source: Self::ReprC, _: &mut ()) -> iroha_ffi::Result<Self> {
if source.is_null() {
return Err(iroha_ffi::FfiReturn::ArgIsNull);
if let Some(_) = source.as_mut() {
return Ok(*Box::from_raw(source));
}

Ok(*Box::from_raw(source))
Err(iroha_ffi::FfiReturn::ArgIsNull)
}
}

Expand All @@ -198,22 +198,7 @@ mod enum_ {
let (ffi_type, largest_discriminant, is_wasm_primitive) = enum_size(enum_name, repr);
let (discriminants, discriminant_decls) = gen_discriminants(enum_name, enum_, &ffi_type);

let (cfg_not_wasm, cfg_wasm) = if !is_wasm_primitive {
(
quote! { #[cfg(not(feature = "wasm"))] },
quote! {
#[cfg(feature = "wasm")]
impl iroha_ffi::Non<iroha_ffi::ir::Transmute<Self>> for #enum_name {}

// TODO:
//#[cfg(feature = "wasm")]
//impl iroha_ffi::Non<NonWasm<Self>> for #enum_name {}
},
)
} else {
(quote! {}, quote! {})
};

// TODO: most of the times niche trait can be implemented and in edge cases robust trait
// TODO: How to find niche value in an enum? Preferably as const expression
//let max_discriminant = discriminants.len();
//let niche_impl = if max_discriminant < largest_discriminant {
Expand All @@ -224,58 +209,206 @@ mod enum_ {
// }
//} else { quote! {}};

let target_family_wasm_impls = if !is_wasm_primitive {
quote! {
#[cfg(target_family = "wasm")]
impl iroha_ffi::ir::NonTransmute for #enum_name {}

#[cfg(target_family = "wasm")]
unsafe impl iroha_ffi::ir::Transmute for &#enum_name {
type ReprC = *const #ffi_type;

unsafe fn is_valid(source: Self::ReprC) -> iroha_ffi::FfiReturn {
if let Some(item) = source.as_ref() {
if !is_valid_fieldless_enum(*source) {
return iroha_ffi::FfiReturn::TrapRepresentation;
}
return iroha_ffi::FfiReturn::Ok;
}
iroha_ffi::FfiReturn::ArgIsNull
}
}
#[cfg(target_family = "wasm")]
unsafe impl iroha_ffi::ir::Transmute for &mut #enum_name {
type ReprC = *mut #ffi_type;
unsafe fn is_valid(source: Self::ReprC) -> iroha_ffi::FfiReturn {
<&#enum_name as iroha_ffi::ir::Transmute>::is_valid(source)
}
}
#[cfg(target_family = "wasm")]
impl iroha_ffi::ir::Ir for #enum_name {
type Type = Self;
fn into_ir(self) -> Self::Type {
self
}
fn from_ir(source: Self::Type) -> Self {
source
}
}
#[cfg(target_family = "wasm")]
impl<'itm> iroha_ffi::ir::Ir for &'itm #enum_name {
type Type = iroha_ffi::ir::Transmutable<Self, *const #ffi_type>;
fn into_ir(self) -> Self::Type {
self.into()
}
fn from_ir(source: Self::Type) -> Self {
unsafe {source.into_inner_ir()}
}
}
#[cfg(target_family = "wasm")]
impl<'itm> iroha_ffi::ir::Ir for &'itm mut #enum_name {
type Type = iroha_ffi::ir::Transmutable<Self, *mut #ffi_type>;
fn into_ir(self) -> Self::Type {
self.into()
}
fn from_ir(source: Self::Type) -> Self {
unsafe {source.into_inner_ir()}
}
}
#[cfg(target_family = "wasm")]
impl<'itm> iroha_ffi::repr_c::CType<'itm> for #enum_name {
type ReprC = <#ffi_type as iroha_ffi::FfiType<'itm>>::ReprC;
type RustStore = ();
type FfiStore = ();
fn into_repr_c(self, _: &mut ()) -> Self::ReprC {
(self as #ffi_type).into_ffi(&mut ())
}
unsafe fn try_from_repr_c(source: Self::ReprC, _: &mut ()) -> iroha_ffi::Result<Self> {
let source: #ffi_type = FfiType::try_from_ffi(source, &mut ())?;
if !is_valid_fieldless_enum(source) {
return Err(iroha_ffi::FfiReturn::TrapRepresentation);
}
Ok(core::mem::transmute::<#ffi_type, #enum_name>(source))
}
}
#[cfg(target_family = "wasm")]
impl iroha_ffi::ir::IrVec for #enum_name {
fn into_ir(vec: Vec<Self>) -> Vec<Self::Type> {
vec
}
fn from_ir(source: Vec<Self::Type>) -> Vec<Self> {
source
}
}
#[cfg(target_family = "wasm")]
impl iroha_ffi::ir::IrSliceRef for #enum_name {
fn into_ir(slice_ref: &[Self]) -> &[Self::Type] {
slice_ref
}
fn from_ir(source: &[Self::Type]) -> &[Self] {
source
}
}
#[cfg(target_family = "wasm")]
impl iroha_ffi::ir::IrSliceMut for #enum_name {
fn into_ir(slice_mut: &mut [Self]) -> &mut [Self::Type] {
slice_mut
}
fn from_ir(source: &mut [Self::Type]) -> &mut [Self] {
source
}
}
}
} else {
quote! {}
};

let cfg_not_wasm = if !is_wasm_primitive {
quote! { #[cfg(not(target_family = "wasm"))] }
} else {
quote! {}
};

quote! {
//#niche_impl
//impl<'itm> Niche<<#ffi_type as $crate::FfiType<'itm>>::ReprC> for #enum_name {
// const NICHE_VALUE: <#ffi_type as $crate::FfiType<'itm>>::ReprC = $niche_val;
//}

fn is_valid_fieldless_enum(source: #ffi_type) -> bool {
#(#discriminant_decls)*

match source {
#( | #discriminants )* => true,
_ => false,
}
}

#cfg_not_wasm
unsafe impl iroha_ffi::ir::Transmute for #enum_name {
type ReprC = #ffi_type;

unsafe fn is_valid(source: Self::ReprC) -> iroha_ffi::FfiReturn {
#(#discriminant_decls)*

match source {
#( | #discriminants )* => iroha_ffi::FfiReturn::Ok,
_ => iroha_ffi::FfiReturn::TrapRepresentation,
unsafe fn is_valid(source: Self::ReprC) -> FfiReturn {
if !is_valid_fieldless_enum(source) {
return iroha_ffi::FfiReturn::TrapRepresentation;
}

iroha_ffi::FfiReturn::Ok
}
}

#cfg_not_wasm
impl iroha_ffi::ir::Ir for #enum_name {
type Type = iroha_ffi::ir::Transmutable<Self, #ffi_type>;

fn into_ir(self) -> Self::Type {
self.into()
}

fn from_ir(source: Self::Type) -> Self {
unsafe {source.into_inner_ir()}
unsafe { source.into_inner_ir() }
}
}

//#cfg_wasm
//impl<'itm> iroha_ffi::repr_c::CType<'itm> for #enum_name {
// type ReprC = <#ffi_type as iroha_ffi::FfiType<'itm>>::ReprC;
// type RustStore = ();
// type FfiStore = ();

// fn into_repr_c(self, _: &mut ()) -> Self::ReprC {
// (self as #ffi_type).into_repr_c(&mut ())
// }
// unsafe fn try_from_repr_c(source: Self::ReprC, _: &mut ()) -> iroha_ffi::Result<Self> {
// let is_valid = <Self as iroha_ffi::ir::Transmute>::is_valid(source);

// if is_valid != iroha_ffi::FfiReturn::Ok {
// return Err(is_valid)
// }

// unimplemented!()
// //Ok(core::mem::transmute::<#ffi_type, #enum_name>(source))
// }
//}

#cfg_not_wasm
iroha_ffi::impl_transmute_for_primitive! {#enum_name}
impl iroha_ffi::ir::IrVec for #enum_name {
fn into_ir(vec: Vec<Self>) -> Vec<Self::Type> {
let mut vec = core::mem::ManuallyDrop::new(vec);
unsafe {Vec::from_raw_parts(vec.as_mut_ptr().cast(), vec.len(), vec.capacity())}
}
fn from_ir(source: Vec<Self::Type>) -> Vec<Self> {
let mut source = core::mem::ManuallyDrop::new(source);
unsafe {Vec::from_raw_parts(source.as_mut_ptr().cast(), source.len(), source.capacity())}
}
}
#cfg_not_wasm
impl iroha_ffi::ir::IrSliceRef for #enum_name {
fn into_ir(slice_ref: &[Self]) -> &[Self::Type] {
unsafe {core::slice::from_raw_parts(slice_ref.as_ptr().cast(), slice_ref.len())}
}
fn from_ir(source: &[Self::Type]) -> &[Self] {
unsafe {core::slice::from_raw_parts(source.as_ptr().cast(), source.len())}
}
}
#cfg_not_wasm
impl iroha_ffi::ir::IrSliceMut for #enum_name {
fn into_ir(slice_mut: &mut [Self]) -> &mut [Self::Type] {
unsafe {core::slice::from_raw_parts_mut(slice_mut.as_mut_ptr().cast(), slice_mut.len())}
}
fn from_ir(source: &mut [Self::Type]) -> &mut [Self] {
unsafe {core::slice::from_raw_parts_mut(source.as_mut_ptr().cast(), source.len())}
}
}

#cfg_wasm
#target_family_wasm_impls
}
}

Expand Down Expand Up @@ -321,8 +454,7 @@ mod enum_ {
#repr_c_enum

// TODO: Maybe it could be transmutable if all variants are transmutable and the enum is `repr(C)`
impl<#generic_params> iroha_ffi::ir::NonTransmutable for #enum_name #ty_generics #where_clause {}
impl<#generic_params> iroha_ffi::ir::GenericIrImpl for #enum_name #ty_generics #where_clause {}
impl<#generic_params> iroha_ffi::ir::NonTransmute for #enum_name #ty_generics #where_clause {}

impl<#lifetime, #generic_params> iroha_ffi::repr_c::CType<#lifetime> for #enum_name #ty_generics #where_clause {
type ReprC = #repr_c_enum_name<#lifetime>;
Expand Down
Loading

0 comments on commit 1922773

Please sign in to comment.