diff --git a/Cargo.lock b/Cargo.lock index ba7322655e0..e746848945e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2070,6 +2070,7 @@ version = "2.0.0-pre-rc.7" dependencies = [ "derive_more", "fixnum", + "iroha_ffi", "iroha_macro", "iroha_schema", "parity-scale-codec", @@ -3989,9 +3990,9 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] name = "trybuild" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "764b9e244b482a9b81bde596aa37aa6f1347bf8007adab25e59f901b32b4e0a0" +checksum = "e7f408301c7480f9e6294eb779cfc907f54bd901a9660ef24d7f233ed5376485" dependencies = [ "glob", "once_cell", diff --git a/crypto/src/hash.rs b/crypto/src/hash.rs index ad450c685f4..d8e169c60b2 100644 --- a/crypto/src/hash.rs +++ b/crypto/src/hash.rs @@ -1,8 +1,11 @@ +#[cfg(feature = "std")] +use std::alloc::alloc; #[cfg(not(feature = "std"))] -use alloc::{format, string::String, vec, vec::Vec}; +use alloc::{alloc::alloc, format, string::String, vec, vec::Vec}; use core::{hash, marker::PhantomData}; use derive_more::{DebugCustom, Deref, DerefMut, Display}; +use iroha_ffi::{IntoFfi, TryFromReprC}; use iroha_schema::prelude::*; use parity_scale_codec::{Decode, Encode}; use serde::{Deserialize, Serialize}; @@ -12,26 +15,33 @@ use ursa::blake2::{ VarBlake2b, }; -/// Hash of Iroha entities. Currently supports only blake2b-32. -#[derive( - Clone, - Copy, - Display, - DebugCustom, - Hash, - Eq, - PartialEq, - Ord, - PartialOrd, - Decode, - Encode, - Deserialize, - Serialize, - IntoSchema, -)] -#[display(fmt = "{}", "hex::encode(_0)")] -#[debug(fmt = "{{ Hash({}) }}", "hex::encode(_0)")] -pub struct Hash([u8; Self::LENGTH]); +use crate::ffi; + +ffi::ffi_item! { + /// Hash of Iroha entities. Currently supports only blake2b-32. + #[derive( + Clone, + Copy, + Display, + DebugCustom, + Hash, + Eq, + PartialEq, + Ord, + PartialOrd, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + IntoFfi, + TryFromReprC, + )] + #[repr(transparent)] + #[display(fmt = "{}", "hex::encode(_0)")] + #[debug(fmt = "{{ Hash({}) }}", "hex::encode(_0)")] + pub struct Hash([u8; Self::LENGTH]); +} impl Hash { /// Length of hash @@ -99,6 +109,7 @@ impl From> for Hash { #[display(fmt = "{}", _0)] #[debug(fmt = "{{ {} {_0} }}", "core::any::type_name::()")] #[serde(transparent)] +#[repr(transparent)] pub struct HashOf( #[deref] #[deref_mut] diff --git a/data_model/src/asset.rs b/data_model/src/asset.rs index 17f518ebc9b..18ed9686b95 100644 --- a/data_model/src/asset.rs +++ b/data_model/src/asset.rs @@ -278,7 +278,6 @@ pub enum AssetValueType { TryFromReprC, IntoSchema, )] -#[repr(u8)] pub enum AssetValue { /// Asset's Quantity. #[display(fmt = "{_0}q")] diff --git a/data_model/src/block_value.rs b/data_model/src/block_value.rs index 11f281f05aa..a9e0664c4c9 100644 --- a/data_model/src/block_value.rs +++ b/data_model/src/block_value.rs @@ -1,41 +1,58 @@ //! This module contains [`BlockValue`] and [`BlockHeaderValue`] structures, their implementation and related traits and //! instructions implementations. #[cfg(not(feature = "std"))] -use alloc::{format, string::String, vec::Vec}; +use alloc::{alloc::alloc, format, string::String, vec::Vec}; use core::cmp::Ordering; +#[cfg(feature = "std")] +use std::alloc::alloc; use derive_more::Display; use iroha_crypto::{Hash, HashOf, MerkleTree}; +use iroha_ffi::{IntoFfi, TryFromReprC}; use iroha_schema::IntoSchema; use parity_scale_codec::{Decode, Encode}; use serde::{Deserialize, Serialize}; use crate::{ events::Event, + ffi::ffi_item, transaction::{VersionedRejectedTransaction, VersionedTransaction, VersionedValidTransaction}, }; -/// Block header -#[derive( - Debug, Clone, Display, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema, -)] -#[display(fmt = "Block №{height} (hash: {transactions_hash});")] -pub struct BlockHeaderValue { - /// Unix time (in milliseconds) of block forming by a peer. - pub timestamp: u128, - /// a number of blocks in the chain up to the block. - pub height: u64, - /// Hash of a previous block in the chain. - /// Is an array of zeros for the first block. - pub previous_block_hash: Hash, - /// Hash of merkle tree root of the tree of valid transactions' hashes. - pub transactions_hash: HashOf>, - /// Hash of merkle tree root of the tree of rejected transactions' hashes. - pub rejected_transactions_hash: HashOf>, - /// Hashes of the blocks that were rejected by consensus. - pub invalidated_blocks_hashes: Vec, - /// Hash of the most recent block - pub current_block_hash: Hash, +ffi_item! { + /// Block header + #[derive( + Debug, + Clone, + Display, + PartialEq, + Eq, + Decode, + Encode, + Deserialize, + Serialize, + IntoFfi, + TryFromReprC, + IntoSchema, + )] + #[display(fmt = "Block №{height} (hash: {transactions_hash});")] + pub struct BlockHeaderValue { + /// Unix time (in milliseconds) of block forming by a peer. + pub timestamp: u128, + /// a number of blocks in the chain up to the block. + pub height: u64, + /// Hash of a previous block in the chain. + /// Is an array of zeros for the first block. + pub previous_block_hash: Hash, + /// Hash of merkle tree root of the tree of valid transactions' hashes. + pub transactions_hash: HashOf>, + /// Hash of merkle tree root of the tree of rejected transactions' hashes. + pub rejected_transactions_hash: HashOf>, + /// Hashes of the blocks that were rejected by consensus. + pub invalidated_blocks_hashes: Vec, + /// Hash of the most recent block + pub current_block_hash: Hash, + } } impl PartialOrd for BlockHeaderValue { diff --git a/data_model/src/events/data/filters.rs b/data_model/src/events/data/filters.rs index 1509e465b9b..bdd4d766af5 100644 --- a/data_model/src/events/data/filters.rs +++ b/data_model/src/events/data/filters.rs @@ -2,11 +2,45 @@ use core::{fmt::Debug, hash::Hash}; +use iroha_ffi::{IntoFfi, ReprC, TryFromReprC}; + use super::*; /// Filter for all events pub type EventFilter = FilterOpt; +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct ReprCEventFilter { + tag: u8, + //payload: >::Source, +} + +impl<'itm> iroha_ffi::Output for ReprCEventFilter { + type OutPtr = *mut Self; +} + +// TODO: Temporary manual implementation +unsafe impl ReprC for ReprCEventFilter {} +impl<'itm> TryFromReprC<'itm> for EventFilter { + type Source = ReprCEventFilter; + type Store = (); + + unsafe fn try_from_repr_c( + source: Self::Source, + store: &'itm mut Self::Store, + ) -> iroha_ffi::Result { + unimplemented!() + } +} +impl IntoFfi for EventFilter { + type Target = ReprCEventFilter; + + fn into_ffi(source: Self::Target) -> Self::Target { + unimplemented!() + } +} + #[derive( Clone, PartialEq, diff --git a/data_model/src/events/execute_trigger.rs b/data_model/src/events/execute_trigger.rs index ddb1c524c99..a4982df735b 100644 --- a/data_model/src/events/execute_trigger.rs +++ b/data_model/src/events/execute_trigger.rs @@ -1,5 +1,12 @@ //! Trigger execution event and filter +#[cfg(not(feature = "std"))] +use alloc::alloc::alloc; +#[cfg(feature = "std")] +use std::alloc::alloc; + +use iroha_ffi::{IntoFfi, TryFromReprC}; + use super::*; use crate::prelude::*; @@ -36,6 +43,8 @@ impl Event { Hash, Serialize, Deserialize, + IntoFfi, + TryFromReprC, )] pub struct EventFilter { /// Id of trigger catch executions of diff --git a/data_model/src/events/mod.rs b/data_model/src/events/mod.rs index d3692d2263c..574897c3f6c 100644 --- a/data_model/src/events/mod.rs +++ b/data_model/src/events/mod.rs @@ -3,6 +3,7 @@ #[cfg(not(feature = "std"))] use alloc::{boxed::Box, format, string::String, vec::Vec}; +use iroha_ffi::{IntoFfi, TryFromReprC}; use iroha_macro::FromVariant; use iroha_schema::prelude::*; use iroha_version::prelude::*; @@ -160,6 +161,8 @@ pub trait Filter { Hash, Serialize, Deserialize, + IntoFfi, + TryFromReprC, )] pub enum FilterBox { /// Listen to pipeline events with filter. diff --git a/data_model/src/events/pipeline.rs b/data_model/src/events/pipeline.rs index bb7c524cc63..76be3643837 100644 --- a/data_model/src/events/pipeline.rs +++ b/data_model/src/events/pipeline.rs @@ -1,9 +1,12 @@ //! Pipeline events. #[cfg(not(feature = "std"))] -use alloc::{format, string::String, vec::Vec}; +use alloc::{alloc::alloc, format, string::String, vec::Vec}; +#[cfg(feature = "std")] +use std::alloc::alloc; use iroha_crypto::Hash; +use iroha_ffi::{IntoFfi, TryFromReprC}; use iroha_macro::FromVariant; use iroha_schema::prelude::IntoSchema; use parity_scale_codec::{Decode, Encode}; @@ -28,6 +31,8 @@ pub use crate::transaction::RejectionReason as PipelineRejectionReason; Hash, Serialize, Deserialize, + IntoFfi, + TryFromReprC, )] pub struct EventFilter { /// If `Some::` filters by the [`EntityKind`]. If `None` accepts all the [`EntityKind`]. diff --git a/data_model/src/events/time.rs b/data_model/src/events/time.rs index 04084767bc3..15b98bd492d 100644 --- a/data_model/src/events/time.rs +++ b/data_model/src/events/time.rs @@ -1,6 +1,12 @@ //! Time event and filter +#[cfg(not(feature = "std"))] +use alloc::alloc::alloc; use core::{ops::Range, time::Duration}; +#[cfg(feature = "std")] +use std::alloc::alloc; + +use iroha_ffi::{IntoFfi, TryFromReprC}; use super::*; @@ -41,7 +47,10 @@ impl Event { Hash, Serialize, Deserialize, + IntoFfi, + TryFromReprC, )] +#[repr(transparent)] pub struct EventFilter(pub ExecutionTime); impl Filter for EventFilter { diff --git a/data_model/src/lib.rs b/data_model/src/lib.rs index fbd7c73dace..22b2cdb072a 100644 --- a/data_model/src/lib.rs +++ b/data_model/src/lib.rs @@ -264,6 +264,8 @@ impl std::error::Error for EnumTryAsError; IntoSchema, )] #[allow(clippy::enum_variant_names)] -#[repr(u8)] pub enum Value { /// [`u32`] integer. U32(u32), @@ -459,11 +464,9 @@ pub enum Value { /// [`fixed::Fixed`] value Fixed(fixed::Fixed), /// [`Vec`] of `Value`. - Vec( - #[skip_from] - #[skip_try_from] - Vec, - ), + #[skip_from] + #[skip_try_from] + Vec(ValueVec), /// Recursive inclusion of LimitedMetadata, LimitedMetadata(metadata::Metadata), /// `Id` of `Asset`, `Account`, etc. @@ -490,49 +493,77 @@ pub enum Value { BlockHeader(BlockHeaderValue), } -/// Cross-platform wrapper for `BlockValue`. -#[cfg(not(target_arch = "aarch64"))] +/// Vector of [`Value`]s #[derive( - AsRef, - Clone, Debug, - Decode, - Deref, - Deserialize, - Encode, - Eq, - From, - Into, - Ord, + Clone, PartialEq, + Eq, PartialOrd, - Serialize, -)] -#[serde(transparent)] -pub struct BlockValueWrapper(BlockValue); - -/// Cross-platform wrapper for `BlockValue`. -#[cfg(target_arch = "aarch64")] -#[derive( - AsRef, - Clone, - Debug, + Ord, Decode, - Deref, - Deserialize, Encode, - Eq, - From, - Ord, - PartialEq, - PartialOrd, + Deserialize, Serialize, + IntoFfi, + TryFromReprC, + IntoSchema, )] -#[as_ref(forward)] -#[deref(forward)] -#[from(forward)] -#[serde(transparent)] -pub struct BlockValueWrapper(Box); +pub struct ValueVec(Vec); + +#[cfg(not(target_arch = "aarch64"))] +ffi::ffi_item! { + /// Cross-platform wrapper for `BlockValue`. + #[derive( + AsRef, + Clone, + Debug, + Decode, + Deref, + Deserialize, + Encode, + Eq, + From, + Into, + Ord, + PartialEq, + PartialOrd, + Serialize, + IntoFfi, + TryFromReprC, + )] + #[repr(transparent)] + #[serde(transparent)] + pub struct BlockValueWrapper(BlockValue); +} + +#[cfg(target_arch = "aarch64")] +ffi::ffi_item! { + /// Cross-platform wrapper for `BlockValue`. + #[derive( + AsRef, + Clone, + Debug, + Decode, + Deref, + Deserialize, + Encode, + Eq, + From, + Ord, + PartialEq, + PartialOrd, + Serialize, + IntoFfi, + TryFromReprC, + )] + #[as_ref(forward)] + #[deref(forward)] + #[from(forward)] + #[repr(transparent)] + #[serde(transparent)] + pub struct BlockValueWrapper(Box); +} #[cfg(target_arch = "aarch64")] impl From for BlockValue { diff --git a/data_model/src/peer.rs b/data_model/src/peer.rs index 8df1c129b2d..d9970934ee5 100644 --- a/data_model/src/peer.rs +++ b/data_model/src/peer.rs @@ -1,42 +1,59 @@ //! This module contains [`Peer`] structure and related implementations and traits implementations. #[cfg(not(feature = "std"))] -use alloc::{format, string::String, vec::Vec}; +use alloc::{alloc::alloc, format, string::String, vec::Vec}; use core::{ cmp::Ordering, hash::{Hash, Hasher}, }; +#[cfg(feature = "std")] +use std::alloc::alloc; use derive_more::Display; use iroha_data_model_derive::IdOrdEqHash; +use iroha_ffi::{IntoFfi, TryFromReprC}; use iroha_schema::IntoSchema; use parity_scale_codec::{Decode, Encode}; use serde::{Deserialize, Serialize}; -use crate::{Identifiable, PublicKey, Registered, Value}; +use crate::{ffi, Identifiable, PublicKey, Registered, Value}; -/// Peer represents Iroha instance. -#[derive( - Debug, Display, Clone, IdOrdEqHash, Decode, Encode, Deserialize, Serialize, IntoSchema, -)] -#[display(fmt = "@@{}", "id.address")] -#[id(type = "Id")] -pub struct Peer { - /// Peer Identification. - pub id: ::Id, +ffi::ffi_item! { + /// Peer represents Iroha instance. + #[derive( + Debug, + Display, + Clone, + IdOrdEqHash, + Decode, + Encode, + Deserialize, + Serialize, + IntoFfi, + TryFromReprC, + IntoSchema, + )] + #[display(fmt = "@@{}", "id.address")] + #[id(type = "Id")] + pub struct Peer { + /// Peer Identification. + pub id: ::Id, + } } -/// Peer's identification. -/// -/// Equality is tested by `public_key` field only. -/// Each peer should have a unique public key. -#[derive(Debug, Display, Clone, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema)] -#[display(fmt = "{public_key}@@{address}")] -pub struct Id { - /// Address of the [`Peer`]'s entrypoint. - pub address: String, - /// Public Key of the [`Peer`]. - pub public_key: PublicKey, +ffi::ffi_item! { + /// Peer's identification. + /// + /// Equality is tested by `public_key` field only. + /// Each peer should have a unique public key. + #[derive(Debug, Display, Clone, Eq, Decode, Encode, Deserialize, Serialize, IntoFfi, TryFromReprC, IntoSchema)] + #[display(fmt = "{public_key}@@{address}")] + pub struct Id { + /// Address of the [`Peer`]'s entrypoint. + pub address: String, + /// Public Key of the [`Peer`]. + pub public_key: PublicKey, + } } impl PartialEq for Id { diff --git a/data_model/src/transaction.rs b/data_model/src/transaction.rs index d90ede27b3a..f0a0a08d38a 100644 --- a/data_model/src/transaction.rs +++ b/data_model/src/transaction.rs @@ -1,17 +1,22 @@ //! [`Transaction`] structures and related implementations. #[cfg(not(feature = "std"))] -use alloc::{boxed::Box, collections::btree_set, format, string::String, vec, vec::Vec}; +use alloc::{ + alloc::alloc, boxed::Box, collections::btree_set, format, string::String, vec, vec::Vec, +}; use core::{ cmp::Ordering, fmt::{Display, Formatter, Result as FmtResult}, iter::IntoIterator, }; #[cfg(feature = "std")] +use std::alloc::alloc; +#[cfg(feature = "std")] use std::{collections::btree_set, time::Duration, vec}; use derive_more::Display; use iroha_crypto::{Hash, SignatureOf, SignaturesOf}; +use iroha_ffi::{IntoFfi, TryFromReprC}; use iroha_macro::FromVariant; use iroha_schema::IntoSchema; use iroha_version::{declare_versioned, declare_versioned_with_scale, version, version_with_scale}; @@ -20,7 +25,7 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "warp")] use warp::{reply::Response, Reply}; -use crate::{account::Account, isi::Instruction, metadata::UnlimitedMetadata, Identifiable}; +use crate::{account::Account, ffi, isi::Instruction, metadata::UnlimitedMetadata, Identifiable}; /// Default maximum number of instructions and expressions per transaction pub const DEFAULT_MAX_INSTRUCTION_NUMBER: u64 = 2_u64.pow(12); @@ -195,6 +200,8 @@ declare_versioned!( PartialEq, Eq, FromVariant, + IntoFfi, + TryFromReprC, IntoSchema, ); @@ -256,7 +263,7 @@ impl From for VersionedTransaction { /// via network. Direct usage in business logic is strongly /// prohibited. Before any interactions `accept`. #[version(n = 1, versioned = "VersionedTransaction")] -#[derive(Debug, Clone, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema)] +#[derive(Debug, Clone, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoFfi, TryFromReprC, IntoSchema)] pub struct Transaction { /// [`Transaction`] payload. pub payload: Payload, @@ -401,7 +408,19 @@ impl IntoIterator for PendingTransactions { } /// Transaction Value used in Instructions and Queries -#[derive(Debug, Clone, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema)] +#[derive( + Debug, + Clone, + PartialEq, + Eq, + Decode, + Encode, + Deserialize, + Serialize, + IntoFfi, + TryFromReprC, + IntoSchema, +)] pub enum TransactionValue { /// Committed transaction Transaction(Box), @@ -439,13 +458,15 @@ impl PartialOrd for TransactionValue { } } -/// `TransactionQueryResult` is used in `FindAllTransactions` query -#[derive(Debug, Clone, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema)] -pub struct TransactionQueryResult { - /// Transaction - pub tx_value: TransactionValue, - /// The hash of the block to which `tx` belongs to - pub block_hash: Hash, +ffi::ffi_item! { + /// `TransactionQueryResult` is used in `FindAllTransactions` query + #[derive(Debug, Clone, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoFfi, TryFromReprC, IntoSchema)] + pub struct TransactionQueryResult { + /// Transaction + pub tx_value: TransactionValue, + /// The hash of the block to which `tx` belongs to + pub block_hash: Hash, + } } impl TransactionQueryResult { @@ -532,7 +553,7 @@ impl Txn for ValidTransaction { } } -declare_versioned!(VersionedRejectedTransaction 1..2, Debug, Clone, PartialEq, Eq, FromVariant, IntoSchema); +declare_versioned!(VersionedRejectedTransaction 1..2, Debug, Clone, PartialEq, Eq, FromVariant, IntoFfi, TryFromReprC, IntoSchema); impl VersionedRejectedTransaction { /// Converts from `&VersionedRejectedTransaction` to V1 reference @@ -573,7 +594,7 @@ impl Txn for VersionedRejectedTransaction { /// [`RejectedTransaction`] represents transaction rejected by some validator at some stage of the pipeline. #[version(n = 1, versioned = "VersionedRejectedTransaction")] -#[derive(Debug, Clone, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema)] +#[derive(Debug, Clone, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoFfi, TryFromReprC, IntoSchema)] pub struct RejectedTransaction { /// The [`Transaction`]'s payload. pub payload: Payload, diff --git a/data_model/src/trigger/mod.rs b/data_model/src/trigger/mod.rs index 5f7ba5adf98..0ceaeef3fb2 100644 --- a/data_model/src/trigger/mod.rs +++ b/data_model/src/trigger/mod.rs @@ -1,21 +1,44 @@ //! Structures traits and impls related to `Trigger`s. #[cfg(not(feature = "std"))] -use alloc::{format, string::String, vec::Vec}; +use alloc::{alloc::alloc, format, string::String, vec::Vec}; use core::{cmp, str::FromStr}; +#[cfg(feature = "std")] +use std::alloc::alloc; use derive_more::Display; +use iroha_ffi::{IntoFfi, TryFromReprC}; use iroha_schema::IntoSchema; use parity_scale_codec::{Decode, Encode}; use serde::{Deserialize, Serialize}; use crate::{ - events::prelude::*, metadata::Metadata, prelude::Domain, transaction::Executable, Identifiable, - Name, ParseError, Registered, + events::prelude::*, ffi, metadata::Metadata, prelude::Domain, transaction::Executable, + Identifiable, Name, ParseError, Registered, }; pub mod set; +impl<'itm> TryFromReprC<'itm> for Trigger { + type Source = *mut Trigger; + type Store = (); + + unsafe fn try_from_repr_c( + source: Self::Source, + store: &'itm mut Self::Store, + ) -> iroha_ffi::Result { + unimplemented!() + } +} + +impl IntoFfi for Trigger { + type Target = *mut Trigger; + + fn into_ffi(source: Self::Target) -> Self::Target { + unimplemented!() + } +} + /// Type which is used for registering a `Trigger`. #[derive( Debug, Display, Clone, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema, @@ -145,26 +168,30 @@ impl Identifiable for Trigger { } } -/// Identification of a `Trigger`. -#[derive( - Debug, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - Decode, - Encode, - Deserialize, - Serialize, - IntoSchema, -)] -pub struct Id { - /// Name given to trigger by its creator. - pub name: Name, - /// DomainId of domain of the trigger. - pub domain_id: Option<::Id>, +ffi::ffi_item! { + /// Identification of a `Trigger`. + #[derive( + Debug, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Decode, + Encode, + Deserialize, + Serialize, + IntoFfi, + TryFromReprC, + IntoSchema, + )] + pub struct Id { + /// Name given to trigger by its creator. + pub name: Name, + /// DomainId of domain of the trigger. + pub domain_id: Option<::Id>, + } } impl Id { diff --git a/ffi/derive/src/convert.rs b/ffi/derive/src/convert.rs index d442f7b8038..f73d1a8e436 100644 --- a/ffi/derive/src/convert.rs +++ b/ffi/derive/src/convert.rs @@ -1,9 +1,9 @@ use proc_macro2::TokenStream as TokenStream2; use proc_macro_error::abort; use quote::quote; -use syn::{parse_quote, DeriveInput, Ident}; +use syn::{DeriveInput, Ident}; -use crate::{enum_size, find_attr, is_fieldless_enum, is_opaque}; +use crate::{find_attr, is_opaque}; pub fn derive_try_from_repr_c(input: &DeriveInput) -> TokenStream2 { if !matches!(input.vis, syn::Visibility::Public(_)) { @@ -13,25 +13,26 @@ pub fn derive_try_from_repr_c(input: &DeriveInput) -> TokenStream2 { abort!(input.generics, "Generics are not supported"); } - if is_opaque(input) { - if is_opaque_wrapper(input) { - return derive_try_from_repr_c_for_opaque_item_wrapper(&input.ident); - } - - return derive_try_from_repr_c_for_opaque_item(&input.ident); - } - match &input.data { syn::Data::Enum(item) => { let repr = find_attr(&input.attrs, "repr"); - if is_fieldless_enum(&input.ident, item, &repr) { - derive_try_from_repr_c_for_fieldless_enum(&input.ident, item, &repr) + if enum_::is_fieldless_enum(item) { + enum_::derive_try_from_repr_c_for_fieldless_enum(&input.ident, item, &repr) } else { - derive_try_from_repr_c_for_item(&input.ident) + enum_::derive_try_from_repr_c_for_data_carrying_enum(&input.ident, &item) } } - syn::Data::Struct(_) => derive_try_from_repr_c_for_item(&input.ident), + syn::Data::Struct(_) => { + if struct_::is_opaque_wrapper(input) { + return struct_::derive_try_from_repr_c_for_opaque_struct_wrapper(&input.ident); + } + if is_opaque(input) { + return struct_::derive_try_from_repr_c_for_opaque_struct(&input.ident); + } + + derive_try_from_repr_c_for_item(&input.ident) + } syn::Data::Union(item) => abort!(item.union_token, "Unions are not supported"), } } @@ -44,449 +45,763 @@ pub fn derive_into_ffi(input: &DeriveInput) -> TokenStream2 { abort!(input.generics, "Generics are not supported"); } - if is_opaque(input) { - if is_opaque_wrapper(input) { - return derive_into_ffi_for_opaque_item_wrapper(&input.ident); - } - - return derive_into_ffi_for_opaque_item(&input.ident); - } - match &input.data { syn::Data::Enum(item) => { let repr = find_attr(&input.attrs, "repr"); - if is_fieldless_enum(&input.ident, item, &repr) { - derive_into_ffi_for_fieldless_enum(&input.ident, &repr) + if enum_::is_fieldless_enum(item) { + enum_::derive_into_ffi_for_fieldless_enum(&input.ident, &repr) } else { - derive_into_ffi_for_item(&input.ident) + enum_::derive_into_ffi_for_data_carrying_enum(&input.ident, &item) } } - syn::Data::Struct(_) => derive_into_ffi_for_item(&input.ident), + syn::Data::Struct(_) => { + if struct_::is_opaque_wrapper(input) { + return struct_::derive_into_ffi_for_opaque_struct_wrapper(&input.ident); + } + if is_opaque(input) { + return struct_::derive_into_ffi_for_opaque_struct(&input.ident); + } + + derive_into_ffi_for_item(&input.ident) + } syn::Data::Union(item) => abort!(item.union_token, "Unions are not supported"), } } -fn variant_discriminants(enum_: &syn::DataEnum) -> Vec { - let mut curr_discriminant: syn::Expr = parse_quote! {0}; - - enum_.variants.iter().fold(Vec::new(), |mut acc, variant| { - let discriminant = variant.discriminant.as_ref().map_or_else( - || curr_discriminant.clone(), - |discriminant| discriminant.1.clone(), - ); - - acc.push(discriminant.clone()); - curr_discriminant = parse_quote! { - 1 + #discriminant - }; - - acc - }) +fn derive_try_from_repr_c_for_item(_: &Ident) -> TokenStream2 { + quote! { + // TODO: + } } -fn derive_try_from_repr_c_for_opaque_item_wrapper(name: &Ident) -> TokenStream2 { - let opaque_item_slice_try_from_repr_c_derive = - derive_try_from_repr_c_for_opaque_item_slice(name); - let opaque_item_vec_try_from_repr_c_derive = derive_try_from_repr_c_for_opaque_item_vec(name); - +fn derive_into_ffi_for_item(_: &Ident) -> TokenStream2 { quote! { - impl<'itm> iroha_ffi::TryFromReprC<'itm> for #name { - type Source = *mut iroha_ffi::Opaque; - type Store = (); + // TODO: + } +} - unsafe fn try_from_repr_c( - source: >::Source, - _: &mut >::Store - ) -> iroha_ffi::Result { - if source.is_null() { - return Err(iroha_ffi::FfiReturn::ArgIsNull); +mod struct_ { + use proc_macro2::TokenStream as TokenStream2; + use quote::quote; + use syn::{parse_quote, DeriveInput, Ident}; + + pub fn derive_try_from_repr_c_for_opaque_struct_wrapper(name: &Ident) -> TokenStream2 { + let opaque_struct_slice_try_from_repr_c_derive = + derive_try_from_repr_c_for_opaque_struct_slice(name); + let opaque_struct_vec_try_from_repr_c_derive = + derive_try_from_repr_c_for_opaque_struct_vec(name); + + quote! { + impl<'itm> iroha_ffi::TryFromReprC<'itm> for #name { + type Source = *mut iroha_ffi::Opaque; + type Store = (); + + unsafe fn try_from_repr_c( + source: >::Source, + _: &mut >::Store + ) -> iroha_ffi::Result { + if source.is_null() { + return Err(iroha_ffi::FfiReturn::ArgIsNull); + } + + Ok(Self{__opaque_ptr: source}) } - - Ok(Self{__opaque_ptr: source}) } - } - impl<'itm> iroha_ffi::TryFromReprC<'itm> for &'itm #name { - type Source = *const iroha_ffi::Opaque; - type Store = Option<#name>; + impl<'itm> iroha_ffi::TryFromReprC<'itm> for &'itm #name { + type Source = *const iroha_ffi::Opaque; + type Store = Option<#name>; - unsafe fn try_from_repr_c( - source: >::Source, - store: &'itm mut >::Store - ) -> iroha_ffi::Result { - if source.is_null() { - return iroha_ffi::FfiReturn::ArgIsNull; - } + unsafe fn try_from_repr_c( + source: >::Source, + store: &'itm mut >::Store + ) -> iroha_ffi::Result { + if source.is_null() { + return iroha_ffi::FfiReturn::ArgIsNull; + } - *store = Some(#name{__opaque_ptr: source}); - store.as_ref().unwrap() + *store = Some(#name{__opaque_ptr: source}); + store.as_ref().unwrap() + } } - } - impl<'itm> iroha_ffi::TryFromReprC<'itm> for &'itm mut #name { - type Source = *mut iroha_ffi::Opaque; - type Store = Option<#name>; + impl<'itm> iroha_ffi::TryFromReprC<'itm> for &'itm mut #name { + type Source = *mut iroha_ffi::Opaque; + type Store = Option<#name>; - unsafe fn try_from_repr_c( - source: >::Source, - store: &'itm mut >::Store - ) -> iroha_ffi::Result { - if source.is_null() { - return iroha_ffi::FfiReturn::ArgIsNull; - } + unsafe fn try_from_repr_c( + source: >::Source, + store: &'itm mut >::Store + ) -> iroha_ffi::Result { + if source.is_null() { + return iroha_ffi::FfiReturn::ArgIsNull; + } - *store = Some(#name{__opaque_ptr: source}); - store.as_mut().unwrap() + *store = Some(#name{__opaque_ptr: source}); + store.as_mut().unwrap() + } } - } - #opaque_item_slice_try_from_repr_c_derive - #opaque_item_vec_try_from_repr_c_derive + #opaque_struct_slice_try_from_repr_c_derive + #opaque_struct_vec_try_from_repr_c_derive + } } -} -fn derive_try_from_repr_c_for_opaque_item(name: &Ident) -> TokenStream2 { - let opaque_item_slice_try_from_repr_c_derive = - derive_try_from_repr_c_for_opaque_item_slice(name); - let opaque_item_vec_try_from_repr_c_derive = derive_try_from_repr_c_for_opaque_item_vec(name); + pub fn derive_try_from_repr_c_for_opaque_struct(name: &Ident) -> TokenStream2 { + let opaque_struct_slice_try_from_repr_c_derive = + derive_try_from_repr_c_for_opaque_struct_slice(name); + let opaque_struct_vec_try_from_repr_c_derive = + derive_try_from_repr_c_for_opaque_struct_vec(name); + + quote! { + impl<'itm> iroha_ffi::TryFromReprC<'itm> for #name { + type Source = *mut Self; + type Store = (); + + unsafe fn try_from_repr_c( + source: >::Source, + _: &mut >::Store + ) -> iroha_ffi::Result { + if source.is_null() { + return Err(iroha_ffi::FfiReturn::ArgIsNull); + } + + Ok(*Box::from_raw(source)) + } + } - quote! { - impl<'itm> iroha_ffi::TryFromReprC<'itm> for #name { - type Source = *mut Self; - type Store = (); + impl<'itm> iroha_ffi::TryFromReprC<'itm> for &'itm #name { + type Source = *const #name; + type Store = (); - unsafe fn try_from_repr_c( - source: >::Source, - _: &mut >::Store - ) -> iroha_ffi::Result { - if source.is_null() { - return Err(iroha_ffi::FfiReturn::ArgIsNull); + unsafe fn try_from_repr_c( + source: >::Source, + _: &mut >::Store + ) -> iroha_ffi::Result { + source.as_ref().ok_or(iroha_ffi::FfiReturn::ArgIsNull) } - - Ok(*Box::from_raw(source)) } - } - impl<'itm> iroha_ffi::TryFromReprC<'itm> for &'itm #name { - type Source = *const #name; - type Store = (); + impl<'itm> iroha_ffi::TryFromReprC<'itm> for &'itm mut #name { + type Source = *mut #name; + type Store = (); - unsafe fn try_from_repr_c( - source: >::Source, - _: &mut >::Store - ) -> iroha_ffi::Result { - source.as_ref().ok_or(iroha_ffi::FfiReturn::ArgIsNull) + unsafe fn try_from_repr_c( + source: >::Source, + _: &mut >::Store + ) -> iroha_ffi::Result { + source.as_mut().ok_or(iroha_ffi::FfiReturn::ArgIsNull) + } } + + #opaque_struct_slice_try_from_repr_c_derive + #opaque_struct_vec_try_from_repr_c_derive } + } + + fn derive_try_from_repr_c_for_opaque_struct_slice(name: &Ident) -> TokenStream2 { + quote! { + impl<'slice> iroha_ffi::slice::TryFromReprCSliceRef<'slice> for #name { + type Source = iroha_ffi::slice::SliceRef<'slice, <&'slice Self as iroha_ffi::TryFromReprC<'slice>>::Source>; + type Store = Vec; + + unsafe fn try_from_repr_c( + source: >::Source, + store: &'slice mut >::Store + ) -> iroha_ffi::Result<&'slice [Self]> { + let source = source.into_rust().ok_or(iroha_ffi::FfiReturn::ArgIsNull)?; - impl<'itm> iroha_ffi::TryFromReprC<'itm> for &'itm mut #name { - type Source = *mut #name; - type Store = (); + for elem in source { + store.push(Clone::clone(iroha_ffi::TryFromReprC::try_from_repr_c(*elem, &mut ())?)); + } - unsafe fn try_from_repr_c( - source: >::Source, - _: &mut >::Store - ) -> iroha_ffi::Result { - source.as_mut().ok_or(iroha_ffi::FfiReturn::ArgIsNull) + Ok(store) + } } } - - #opaque_item_slice_try_from_repr_c_derive - #opaque_item_vec_try_from_repr_c_derive } -} -fn derive_try_from_repr_c_for_opaque_item_slice(name: &Ident) -> TokenStream2 { - quote! { - impl<'slice> iroha_ffi::slice::TryFromReprCSliceRef<'slice> for #name { - type Source = iroha_ffi::slice::SliceRef<'slice, <&'slice Self as iroha_ffi::TryFromReprC<'slice>>::Source>; - type Store = Vec; + fn derive_try_from_repr_c_for_opaque_struct_vec(name: &Ident) -> TokenStream2 { + quote! { + impl<'itm> iroha_ffi::owned::TryFromReprCVec<'itm> for #name { + type Source = iroha_ffi::slice::SliceRef<'itm, >::Source>; + type Store = (); - unsafe fn try_from_repr_c( - source: >::Source, - store: &'slice mut >::Store - ) -> iroha_ffi::Result<&'slice [Self]> { - let source = source.into_rust().ok_or(iroha_ffi::FfiReturn::ArgIsNull)?; + unsafe fn try_from_repr_c( + source: Self::Source, + _: &'itm mut >::Store, + ) -> iroha_ffi::Result> { + let slice = source.into_rust().ok_or(iroha_ffi::FfiReturn::ArgIsNull)?; + let mut res = Vec::with_capacity(slice.len()); - for elem in source { - store.push(Clone::clone(iroha_ffi::TryFromReprC::try_from_repr_c(*elem, &mut ())?)); - } + for elem in slice { + res.push(iroha_ffi::TryFromReprC::try_from_repr_c(*elem, &mut ())?); + } - Ok(store) + Ok(res) + } } } } -} -fn derive_try_from_repr_c_for_opaque_item_vec(name: &Ident) -> TokenStream2 { - quote! { - impl<'itm> iroha_ffi::owned::TryFromReprCVec<'itm> for #name { - type Source = iroha_ffi::slice::SliceRef<'itm, >::Source>; - type Store = (); + pub fn derive_into_ffi_for_opaque_struct_wrapper(name: &Ident) -> TokenStream2 { + let opaque_struct_slice_into_ffi_derive = derive_into_ffi_for_opaque_struct_slice(name); + let opaque_struct_vec_into_ffi_derive = derive_into_ffi_for_opaque_struct_vec(name); - unsafe fn try_from_repr_c( - source: Self::Source, - _: &'itm mut >::Store, - ) -> iroha_ffi::Result> { - let slice = source.into_rust().ok_or(iroha_ffi::FfiReturn::ArgIsNull)?; - let mut res = Vec::with_capacity(slice.len()); + quote! { + impl iroha_ffi::IntoFfi for #name { + type Target = *mut iroha_ffi::Opaque; - for elem in slice { - res.push(iroha_ffi::TryFromReprC::try_from_repr_c(*elem, &mut ())?); + fn into_ffi(self) -> Self::Target { + core::mem::ManuallyDrop::new(self).__opaque_ptr } + } - Ok(res) + impl iroha_ffi::IntoFfi for &#name { + type Target = *const iroha_ffi::Opaque; + + fn into_ffi(self) -> Self::Target { + self.__opaque_ptr + } } - } - } -} -fn derive_try_from_repr_c_for_item(_: &Ident) -> TokenStream2 { - quote! { - // TODO: + impl iroha_ffi::IntoFfi for &mut #name { + type Target = *mut iroha_ffi::Opaque; + + fn into_ffi(self) -> Self::Target { + self.__opaque_ptr + } + } + + #opaque_struct_slice_into_ffi_derive + #opaque_struct_vec_into_ffi_derive + } } -} -fn derive_try_from_repr_c_for_fieldless_enum( - enum_name: &Ident, - enum_: &syn::DataEnum, - repr: &[syn::NestedMeta], -) -> TokenStream2 { - let variant_names: Vec<_> = enum_.variants.iter().map(|v| &v.ident).collect(); - let discriminant_values = variant_discriminants(enum_); + pub fn derive_into_ffi_for_opaque_struct(name: &Ident) -> TokenStream2 { + let opaque_struct_slice_into_ffi_derive = derive_into_ffi_for_opaque_struct_slice(name); + let opaque_struct_vec_into_ffi_derive = derive_into_ffi_for_opaque_struct_vec(name); - let ffi_type = enum_size(enum_name, repr); - let (discriminants, discriminant_names) = - variant_names.iter().zip(discriminant_values.iter()).fold( - <(Vec<_>, Vec<_>)>::default(), - |mut acc, (variant_name, discriminant_value)| { - let discriminant_name = Ident::new( - &format!("{}__{}", enum_name, variant_name).to_uppercase(), - proc_macro2::Span::call_site(), - ); + quote! { + impl iroha_ffi::IntoFfi for #name { + type Target = *mut Self; - acc.0.push(quote! { - const #discriminant_name: #ffi_type = #discriminant_value; - }); - acc.1.push(discriminant_name); + fn into_ffi(self) -> Self::Target { + let layout = core::alloc::Layout::for_value(&self); - acc - }, - ); + unsafe { + let ptr: Self::Target = alloc(layout).cast(); + ptr.write(self); + ptr + } + } + } - quote! { - impl<'itm> iroha_ffi::TryFromReprC<'itm> for #enum_name { - type Source = <#ffi_type as iroha_ffi::TryFromReprC<'itm>>::Source; - type Store = (); + impl iroha_ffi::IntoFfi for &#name { + type Target = *const #name; - unsafe fn try_from_repr_c( - source: >::Source, - store: &mut >::Store - ) -> iroha_ffi::Result { - #( #discriminants )* + fn into_ffi(self) -> Self::Target { + <*const _>::from(self) + } + } - let source: #ffi_type = iroha_ffi::TryFromReprC::try_from_repr_c(source, store)?; + impl iroha_ffi::IntoFfi for &mut #name { + type Target = *mut #name; - match source { - #( #discriminant_names => Ok(#enum_name::#variant_names), )* - _ => Err(iroha_ffi::FfiReturn::TrapRepresentation), + fn into_ffi(self) -> Self::Target { + <*mut _>::from(self) } } + + #opaque_struct_slice_into_ffi_derive + #opaque_struct_vec_into_ffi_derive } - impl<'itm> iroha_ffi::TryFromReprC<'itm> for &'itm #enum_name { - type Source = *const #ffi_type; - type Store = (); + } - unsafe fn try_from_repr_c( - source: >::Source, - _: &mut >::Store - ) -> iroha_ffi::Result { - #( #discriminants )* + fn derive_into_ffi_for_opaque_struct_slice(name: &Ident) -> TokenStream2 { + quote! { + impl<'slice> iroha_ffi::slice::IntoFfiSliceRef<'slice> for #name { + type Target = iroha_ffi::owned::LocalSlice<<&'slice Self as IntoFfi>::Target>; - unsafe { match *source { - #( | #discriminant_names )* => Ok(&*(source as *const _ as *const _)), - _ => Err(iroha_ffi::FfiReturn::TrapRepresentation), - }} + fn into_ffi(source: &[Self]) -> Self::Target { + source.iter().map(iroha_ffi::IntoFfi::into_ffi).collect() + } } } - impl<'itm> iroha_ffi::TryFromReprC<'itm> for &'itm mut #enum_name { - type Source = *mut #ffi_type; - type Store = (); + } - unsafe fn try_from_repr_c( - source: >::Source, - _: &mut >::Store - ) -> iroha_ffi::Result { - #( #discriminants )* + fn derive_into_ffi_for_opaque_struct_vec(name: &Ident) -> TokenStream2 { + quote! { + impl iroha_ffi::owned::IntoFfiVec for #name { + type Target = iroha_ffi::owned::LocalSlice<<#name as iroha_ffi::IntoFfi>::Target>; - unsafe { match *source { - #( | #discriminant_names )* => Ok(&mut *(source as *mut _ as *mut _)), - _ => Err(iroha_ffi::FfiReturn::TrapRepresentation), - }} + fn into_ffi(source: Vec) -> Self::Target { + source.into_iter().map(IntoFfi::into_ffi).collect() + } } } + } - impl<'slice> iroha_ffi::slice::TryFromReprCSliceRef<'slice> for #enum_name { - type Source = iroha_ffi::slice::SliceRef<'slice, Self>; - type Store = (); + pub fn is_opaque_wrapper(input: &DeriveInput) -> bool { + let opaque_attr = parse_quote! {#[opaque_wrapper]}; + input.attrs.iter().any(|a| *a == opaque_attr) + } +} - unsafe fn try_from_repr_c( - source: >::Source, - _: &mut >::Store - ) -> iroha_ffi::Result<&'slice [Self]> { - source.into_rust().ok_or(iroha_ffi::FfiReturn::ArgIsNull) +mod enum_ { + use std::str::FromStr as _; + + use proc_macro2::TokenStream as TokenStream2; + use proc_macro_error::abort; + use quote::quote; + use syn::{parse_quote, Ident}; + + pub fn derive_try_from_repr_c_for_fieldless_enum( + enum_name: &Ident, + enum_: &syn::DataEnum, + repr: &[syn::NestedMeta], + ) -> TokenStream2 { + let tag_type = enum_size(enum_name, repr); + + let (discriminants, discriminant_decls) = gen_discriminants(enum_name, enum_, &tag_type); + let variant_names: Vec<_> = enum_.variants.iter().map(|v| &v.ident).collect(); + + quote! { + impl<'itm> iroha_ffi::TryFromReprC<'itm> for #enum_name { + type Source = <#tag_type as iroha_ffi::TryFromReprC<'itm>>::Source; + type Store = (); + + unsafe fn try_from_repr_c( + source: >::Source, + store: &mut >::Store + ) -> iroha_ffi::Result { + let source: #tag_type = iroha_ffi::TryFromReprC::try_from_repr_c(source, store)?; + + #( #discriminant_decls )* + + match source { + #( #discriminants => Ok(#enum_name::#variant_names), )* + _ => Err(iroha_ffi::FfiReturn::TrapRepresentation), + } + } } - } - impl<'slice> iroha_ffi::slice::TryFromReprCSliceMut<'slice> for #enum_name { - type Source = iroha_ffi::slice::SliceMut<'slice, #enum_name>; - type Store = (); + impl<'itm> iroha_ffi::TryFromReprC<'itm> for &'itm #enum_name { + type Source = *const #tag_type; + type Store = (); + + unsafe fn try_from_repr_c( + source: >::Source, + _: &mut >::Store + ) -> iroha_ffi::Result { + #( #discriminant_decls )* + + unsafe { match *source { + #( | #discriminants )* => Ok(&*(source as *const _ as *const _)), + _ => Err(iroha_ffi::FfiReturn::TrapRepresentation), + }} + } + } + impl<'itm> iroha_ffi::TryFromReprC<'itm> for &'itm mut #enum_name { + type Source = *mut #tag_type; + type Store = (); + + unsafe fn try_from_repr_c( + source: >::Source, + _: &mut >::Store + ) -> iroha_ffi::Result { + #( #discriminant_decls )* + + unsafe { match *source { + #( | #discriminants )* => Ok(&mut *(source as *mut _ as *mut _)), + _ => Err(iroha_ffi::FfiReturn::TrapRepresentation), + }} + } + } + + impl<'slice> iroha_ffi::slice::TryFromReprCSliceRef<'slice> for #enum_name { + type Source = iroha_ffi::slice::SliceRef<'slice, Self>; + type Store = (); - unsafe fn try_from_repr_c( - source: >::Source, - _: &mut ::Store - ) -> iroha_ffi::Result<&'slice mut [Self]> { - source.into_rust().ok_or(iroha_ffi::FfiReturn::ArgIsNull) + unsafe fn try_from_repr_c( + source: >::Source, + _: &mut >::Store + ) -> iroha_ffi::Result<&'slice [Self]> { + source.into_rust().ok_or(iroha_ffi::FfiReturn::ArgIsNull) + } + } + impl<'slice> iroha_ffi::slice::TryFromReprCSliceMut<'slice> for #enum_name { + type Source = iroha_ffi::slice::SliceMut<'slice, #enum_name>; + type Store = (); + + unsafe fn try_from_repr_c( + source: >::Source, + _: &mut ::Store + ) -> iroha_ffi::Result<&'slice mut [Self]> { + source.into_rust().ok_or(iroha_ffi::FfiReturn::ArgIsNull) + } } } } -} -fn derive_into_ffi_for_opaque_item_wrapper(name: &Ident) -> TokenStream2 { - let opaque_item_slice_into_ffi_derive = derive_into_ffi_for_opaque_item_slice(name); - let opaque_item_vec_into_ffi_derive = derive_into_ffi_for_opaque_item_vec(name); + pub fn derive_into_ffi_for_fieldless_enum( + enum_name: &Ident, + repr: &[syn::NestedMeta], + ) -> TokenStream2 { + let tag_type = enum_size(enum_name, repr); - quote! { - impl iroha_ffi::IntoFfi for #name { - type Target = *mut iroha_ffi::Opaque; + quote! { + impl iroha_ffi::IntoFfi for #enum_name { + type Target = <#tag_type as iroha_ffi::IntoFfi>::Target; - fn into_ffi(self) -> Self::Target { - core::mem::ManuallyDrop::new(self).__opaque_ptr + fn into_ffi(self) -> Self::Target { + (self as #tag_type).into_ffi() + } } - } - impl iroha_ffi::IntoFfi for &#name { - type Target = *const iroha_ffi::Opaque; + impl iroha_ffi::IntoFfi for &#enum_name { + type Target = *const #tag_type; - fn into_ffi(self) -> Self::Target { - self.__opaque_ptr + fn into_ffi(self) -> Self::Target { + self as *const #enum_name as *const #tag_type + } } - } - impl iroha_ffi::IntoFfi for &mut #name { - type Target = *mut iroha_ffi::Opaque; + impl iroha_ffi::IntoFfi for &mut #enum_name { + type Target = *mut #tag_type; - fn into_ffi(self) -> Self::Target { - self.__opaque_ptr + fn into_ffi(self) -> Self::Target { + self as *mut #enum_name as *mut #tag_type + } } } - - #opaque_item_slice_into_ffi_derive - #opaque_item_vec_into_ffi_derive } -} -fn derive_into_ffi_for_opaque_item(name: &Ident) -> TokenStream2 { - let opaque_item_slice_into_ffi_derive = derive_into_ffi_for_opaque_item_slice(name); - let opaque_item_vec_into_ffi_derive = derive_into_ffi_for_opaque_item_vec(name); + pub fn derive_try_from_repr_c_for_data_carrying_enum( + enum_name: &Ident, + enum_: &syn::DataEnum, + ) -> TokenStream2 { + let (repr_c_enum_name, repr_c_enum) = gen_repr_c_enum(enum_name, enum_, false); + let tag_type = gen_enum_tag_type(enum_name, enum_); + + let enum_variants = enum_.variants.iter().enumerate().map(|(i, variant)| { + let variant_name = &variant.ident; + + match &variant.fields { + syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed, .. }) if unnamed.len() == 1 => { + let idx = TokenStream2::from_str(&format!("{i}")).expect("Valid"); + + quote! { + #enum_name::#variant_name(iroha_ffi::TryFromReprC::<'itm>::try_from_repr_c( + core::mem::ManuallyDrop::into_inner(source.payload.#variant_name), &mut store.#idx + )?) + } + } + syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed, .. }) => { + abort!(unnamed, "Only 1-sized variants are supported") + } + syn::Fields::Named(syn::FieldsNamed { named, .. }) => { + abort!(named, "Named variants are not supported") + } + syn::Fields::Unit => quote! {#enum_name::#variant_name}, + } + }); - quote! { - impl iroha_ffi::IntoFfi for #name { - type Target = *mut Self; + let store = enum_.variants.iter().map(|variant| match &variant.fields { + syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed, .. }) if unnamed.len() == 1 => { + let variant_ty = &unnamed[0].ty; + quote! {<#variant_ty as iroha_ffi::TryFromReprC<'itm>>::Store} + } + syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed, .. }) => { + abort!(unnamed, "Only 1-sized variants are supported") + } + syn::Fields::Named(syn::FieldsNamed { named, .. }) => { + abort!(named, "Named variants are not supported") + } + syn::Fields::Unit => quote! {()}, + }); + + let (discriminants, discriminant_decls) = gen_discriminants(enum_name, enum_, &tag_type); + + quote! { + #repr_c_enum - fn into_ffi(self) -> Self::Target { - let layout = core::alloc::Layout::for_value(&self); + impl<'itm> iroha_ffi::TryFromReprC<'itm> for #enum_name { + type Source = #repr_c_enum_name<'itm>; + type Store = (#(#store),*); - unsafe { - let ptr: Self::Target = alloc(layout).cast(); - ptr.write(self); - ptr + unsafe fn try_from_repr_c( + source: >::Source, + store: &'itm mut >::Store + ) -> iroha_ffi::Result { + #( #discriminant_decls )* + + match source.tag { + #( #discriminants => Ok(#enum_variants), )* + _ => Err(iroha_ffi::FfiReturn::TrapRepresentation), + } } } } + } - impl iroha_ffi::IntoFfi for &#name { - type Target = *const #name; - - fn into_ffi(self) -> Self::Target { - <*const _>::from(self) + pub fn derive_into_ffi_for_data_carrying_enum( + enum_name: &Ident, + enum_: &syn::DataEnum, + ) -> TokenStream2 { + let (repr_c_enum_name, repr_c_enum) = gen_repr_c_enum(enum_name, enum_, true); + + let enum_variants = enum_.variants.iter().enumerate().map(|(i, variant)| { + let idx = TokenStream2::from_str(&format!("{i}")).expect("Valid"); + let variant_name = &variant.ident; + + match &variant.fields { + syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed, .. }) if unnamed.len() == 1 => { + let payload_name = gen_repr_c_enum_payload_name(enum_name, true); + + quote! { + #enum_name::#variant_name(x) => { + let payload = #payload_name { + #variant_name: core::mem::ManuallyDrop::new( + iroha_ffi::IntoFfi::into_ffi(x) + ) + }; + + #repr_c_enum_name { tag: #idx, payload } + }, + } + } + syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed, .. }) => { + abort!(unnamed, "Only 1-sized variants are supported") + } + syn::Fields::Named(syn::FieldsNamed { named, .. }) => { + abort!(named, "Named variants are not supported") + } + syn::Fields::Unit => { + quote! { #enum_name::#variant_name => #repr_c_enum_name { tag: #idx }, } + } } - } + }); + + quote! { + #repr_c_enum - impl iroha_ffi::IntoFfi for &mut #name { - type Target = *mut #name; + impl iroha_ffi::IntoFfi for #enum_name { + type Target = #repr_c_enum_name; - fn into_ffi(self) -> Self::Target { - <*mut _>::from(self) + fn into_ffi(self) -> Self::Target { + match self { + #(#enum_variants)* + } + } } - } - #opaque_item_slice_into_ffi_derive - #opaque_item_vec_into_ffi_derive + impl iroha_ffi::Output for #repr_c_enum_name { + type OutPtr = *mut Self; + } + } } -} -fn derive_into_ffi_for_opaque_item_slice(name: &Ident) -> TokenStream2 { - quote! { - impl<'slice> iroha_ffi::slice::IntoFfiSliceRef<'slice> for #name { - type Target = iroha_ffi::owned::LocalSlice<<&'slice Self as IntoFfi>::Target>; + fn gen_discriminants( + enum_name: &Ident, + enum_: &syn::DataEnum, + tag_type: &TokenStream2, + ) -> (Vec, Vec) { + let variant_names: Vec<_> = enum_.variants.iter().map(|v| &v.ident).collect(); + let discriminant_values = variant_discriminants(enum_); - fn into_ffi(source: &[Self]) -> Self::Target { - source.iter().map(iroha_ffi::IntoFfi::into_ffi).collect() - } - } + variant_names.iter().zip(discriminant_values.iter()).fold( + <(Vec<_>, Vec<_>)>::default(), + |mut acc, (variant_name, discriminant_value)| { + let discriminant_name = Ident::new( + &format!("{}__{}", enum_name, variant_name).to_uppercase(), + proc_macro2::Span::call_site(), + ); + + acc.1.push(quote! { + const #discriminant_name: #tag_type = #discriminant_value; + }); + acc.0.push(discriminant_name); + + acc + }, + ) } -} -fn derive_into_ffi_for_opaque_item_vec(name: &Ident) -> TokenStream2 { - quote! { - impl iroha_ffi::owned::IntoFfiVec for #name { - type Target = iroha_ffi::owned::LocalSlice<<#name as iroha_ffi::IntoFfi>::Target>; + fn variant_discriminants(enum_: &syn::DataEnum) -> Vec { + let mut curr_discriminant: syn::Expr = parse_quote! {0}; - fn into_ffi(source: Vec) -> Self::Target { - source.into_iter().map(IntoFfi::into_ffi).collect() - } + enum_.variants.iter().fold(Vec::new(), |mut acc, variant| { + let discriminant = variant.discriminant.as_ref().map_or_else( + || curr_discriminant.clone(), + |discriminant| discriminant.1.clone(), + ); + + acc.push(discriminant.clone()); + curr_discriminant = parse_quote! { + 1 + #discriminant + }; + + acc + }) + } + + fn enum_size(enum_name: &syn::Ident, repr: &[syn::NestedMeta]) -> TokenStream2 { + use crate::is_repr_attr; + + if is_repr_attr(repr, "u8") { + quote! {u8} + } else if is_repr_attr(repr, "u16") { + quote! {u16} + } else if is_repr_attr(repr, "u32") { + quote! {u32} + } else if is_repr_attr(repr, "u64") { + quote! {u64} + } else { + abort!(enum_name, "Enum doesn't have a valid representation") } } -} -fn derive_into_ffi_for_item(_: &Ident) -> TokenStream2 { - quote! { - // TODO: + pub fn is_fieldless_enum(item: &syn::DataEnum) -> bool { + !item + .variants + .iter() + .any(|variant| !matches!(variant.fields, syn::Fields::Unit)) } -} -fn derive_into_ffi_for_fieldless_enum(enum_name: &Ident, repr: &[syn::NestedMeta]) -> TokenStream2 { - let ffi_type = enum_size(enum_name, repr); + fn gen_repr_c_enum( + enum_name: &syn::Ident, + enum_: &syn::DataEnum, + is_target: bool, + ) -> (Ident, TokenStream2) { + let (payload_name, payload) = gen_enum_payload(enum_name, &enum_, is_target); + let repr_c_enum_name = gen_repr_c_enum_name(enum_name, is_target); + let enum_tag_type = gen_enum_tag_type(enum_name, &enum_); + + let lifetime_param = if is_target { + quote! {} + } else { + quote! { <'itm> } + }; - quote! { - impl iroha_ffi::IntoFfi for #enum_name { - type Target = <#ffi_type as iroha_ffi::IntoFfi>::Target; + let derives = if is_target { + quote! {} + } else { + quote! {#[derive(Clone, Copy)]} + }; - fn into_ffi(self) -> Self::Target { - (self as #ffi_type).into_ffi() - } - } + ( + repr_c_enum_name.clone(), + quote! { + #payload + + #derives + #[repr(C)] + #[allow(non_camel_case_types)] + pub struct #repr_c_enum_name #lifetime_param { + tag: #enum_tag_type, + payload: #payload_name #lifetime_param, + } - impl iroha_ffi::IntoFfi for &#enum_name { - type Target = *const #ffi_type; + unsafe impl #lifetime_param iroha_ffi::ReprC for #repr_c_enum_name #lifetime_param {} + }, + ) + } - fn into_ffi(self) -> Self::Target { - self as *const #enum_name as *const #ffi_type + fn gen_enum_payload( + enum_name: &Ident, + enum_: &syn::DataEnum, + is_target: bool, + ) -> (Ident, TokenStream2) { + let payload_name = gen_repr_c_enum_payload_name(enum_name, is_target); + + let variants = enum_.variants.iter().map(|variant| { + let variant_ident = &variant.ident; + + match &variant.fields { + syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed, .. }) if unnamed.len() == 1 => { + let variant_ty = &unnamed[0].ty; + + if is_target { + quote! { #variant_ident: core::mem::ManuallyDrop<<#variant_ty as iroha_ffi::IntoFfi>::Target> } + } else { + quote! { #variant_ident: core::mem::ManuallyDrop<<#variant_ty as iroha_ffi::TryFromReprC<'itm>>::Source> } + } + } + syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed, .. }) => { + abort!(unnamed, "Only 1-sized variants are supported") + } + syn::Fields::Named(syn::FieldsNamed { named, .. }) => { + abort!(named, "Named variants are not supported") + } + syn::Fields::Unit => quote! {} } - } + }); + + let lifetime_param = if is_target { + quote! {} + } else { + quote! { <'itm> } + }; - impl iroha_ffi::IntoFfi for &mut #enum_name { - type Target = *mut #ffi_type; + let derives = if is_target { + quote! {} + } else { + quote! {#[derive(Clone, Copy)]} + }; - fn into_ffi(self) -> Self::Target { - self as *mut #enum_name as *mut #ffi_type - } + ( + payload_name.clone(), + quote! { + #derives + #[repr(C)] + #[allow(non_snake_case)] + #[allow(non_camel_case_types)] + union #payload_name #lifetime_param { + #(#variants),* + } + }, + ) + } + + fn gen_enum_tag_type(enum_name: &syn::Ident, enum_: &syn::DataEnum) -> TokenStream2 { + const U8_MAX: usize = u8::MAX as usize; + const U16_MAX: usize = u16::MAX as usize; + const U32_MAX: usize = u32::MAX as usize; + + // NOTE: Arms are matched in the order of declaration + #[allow(overlapping_range_endpoints)] + match enum_.variants.len() { + 0..=U8_MAX => quote! {u8}, + 0..=U16_MAX => quote! {u16}, + 0..=U32_MAX => quote! {u32}, + _ => abort!(enum_name, "Too many variants"), } } -} -fn is_opaque_wrapper(input: &DeriveInput) -> bool { - let opaque_attr = parse_quote! {#[opaque_wrapper]}; - input.attrs.iter().any(|a| *a == opaque_attr) + fn gen_repr_c_enum_name(enum_name: &Ident, is_target: bool) -> Ident { + let target = if is_target { + "IntoFfiTarget" + } else { + "TryFromReprCSource" + }; + + syn::Ident::new( + &format!("__iroha_ffi__ReprC{}{}", enum_name, target), + proc_macro2::Span::call_site(), + ) + } + + fn gen_repr_c_enum_payload_name(enum_name: &Ident, is_target: bool) -> Ident { + let target = if is_target { + "IntoFfiTarget" + } else { + "TryFromReprCSource" + }; + + syn::Ident::new( + &format!("__iroha_ffi__{}{}Payload", enum_name, target), + proc_macro2::Span::call_site(), + ) + } } diff --git a/ffi/derive/src/lib.rs b/ffi/derive/src/lib.rs index ef7d8ddf298..2d8a14c624a 100644 --- a/ffi/derive/src/lib.rs +++ b/ffi/derive/src/lib.rs @@ -2,7 +2,6 @@ use impl_visitor::{FnDescriptor, ImplDescriptor}; use proc_macro::TokenStream; -use proc_macro2::TokenStream as TokenStream2; use proc_macro_error::abort; use quote::quote; use syn::{parse_macro_input, Item, NestedMeta}; @@ -13,8 +12,7 @@ mod convert; mod ffi_fn; mod impl_visitor; mod util; -// TODO: Should be enabled in https://github.com/hyperledger/iroha/issues/2231 -//mod wrapper; +mod wrapper; struct FfiItems(Vec); @@ -45,22 +43,24 @@ impl quote::ToTokens for FfiItems { #[proc_macro] #[proc_macro_error::proc_macro_error] pub fn ffi(input: TokenStream) -> TokenStream { - let items = parse_macro_input!(input as FfiItems).0; - - // TODO: Should be fixed in https://github.com/hyperledger/iroha/issues/2231 - //items - // .iter_mut() - // .filter(|item| is_opaque(item)) - // .for_each(|item| item.attrs.push(syn::parse_quote! {#[opaque_wrapper]})); - //let items = items.iter().map(|item| { - // if is_opaque(item) { - // wrapper::wrap_as_opaque(item) - // } else { - // quote! {#item} - // } - //}); - - quote! { #(#items)* }.into() + let mut items = parse_macro_input!(input as FfiItems).0; + + items + .iter_mut() + .filter(|item| is_opaque(item)) + .for_each(|item| item.attrs.push(syn::parse_quote! {#[opaque_wrapper]})); + let items = items.iter().map(|item| { + if is_opaque(item) { + wrapper::wrap_as_opaque(item) + } else { + quote! {#item} + } + }); + + quote! { + #(#items)* + } + .into() } /// Derive implementations of traits required to convert to and from an FFI-compatible type @@ -129,12 +129,16 @@ pub fn try_from_repr_c_derive(input: TokenStream) -> TokenStream { /// ``` #[proc_macro_attribute] #[proc_macro_error::proc_macro_error] -pub fn ffi_export(_attr: TokenStream, item: TokenStream) -> TokenStream { +pub fn ffi_export(attr: TokenStream, item: TokenStream) -> TokenStream { match parse_macro_input!(item) { Item::Impl(item) => { let impl_descriptor = ImplDescriptor::from_impl(&item); let ffi_fns = impl_descriptor.fns.iter().map(ffi_fn::gen_definition); + if !attr.is_empty() { + abort!(item, "Unknown tokens in the attribute"); + } + quote! { #item #(#ffi_fns)* @@ -144,6 +148,10 @@ pub fn ffi_export(_attr: TokenStream, item: TokenStream) -> TokenStream { let derived_methods = util::gen_derived_methods(&item); let ffi_fns = derived_methods.iter().map(ffi_fn::gen_definition); + let repr = find_attr(&item.attrs, "repr"); + if is_repr_attr(&repr, "C") { + abort!(item.ident, "Only opaque structs can export FFI bindings"); + } if !matches!(item.vis, syn::Visibility::Public(_)) { abort!(item.vis, "Only public structs allowed in FFI"); } @@ -157,27 +165,28 @@ pub fn ffi_export(_attr: TokenStream, item: TokenStream) -> TokenStream { } } Item::Fn(item) => { + if !attr.is_empty() { + abort!(item, "Unknown tokens in the attribute"); + } + if item.sig.asyncness.is_some() { abort!(item.sig.asyncness, "Async functions are not supported"); } - if item.sig.unsafety.is_some() { abort!(item.sig.unsafety, "You shouldn't specify function unsafety"); } - if item.sig.abi.is_some() { abort!(item.sig.abi, "You shouldn't specify function ABI"); } - if !item.sig.generics.params.is_empty() { abort!(item.sig.generics, "Generics are not supported"); } let fn_descriptor = FnDescriptor::from_fn(&item); let ffi_fn = ffi_fn::gen_definition(&fn_descriptor); + quote! { #item - #ffi_fn } } @@ -193,9 +202,7 @@ pub fn ffi_import(_attr: TokenStream, item: TokenStream) -> TokenStream { Item::Impl(item) => { let impl_descriptor = ImplDescriptor::from_impl(&item); let ffi_fns = impl_descriptor.fns.iter().map(ffi_fn::gen_declaration); - - // TODO: Should be fixed in https://github.com/hyperledger/iroha/issues/2231 - //let item = wrapper::wrap_impl_item(&impl_descriptor.fns); + let item = wrapper::wrap_impl_item(&impl_descriptor.fns); quote! { #item @@ -205,6 +212,7 @@ pub fn ffi_import(_attr: TokenStream, item: TokenStream) -> TokenStream { Item::Struct(item) => { let derived_methods = util::gen_derived_methods(&item); let ffi_fns = derived_methods.iter().map(ffi_fn::gen_declaration); + let impl_block = wrapper::wrap_impl_item(&derived_methods); if !matches!(item.vis, syn::Visibility::Public(_)) { abort!(item.vis, "Only public structs allowed in FFI"); @@ -213,13 +221,9 @@ pub fn ffi_import(_attr: TokenStream, item: TokenStream) -> TokenStream { abort!(item.generics, "Generics are not supported"); } - // TODO: Remove getset attributes to prevent code generation - // Should be fixed in https://github.com/hyperledger/iroha/issues/2231 - //let impl_block = Some(wrapper::wrap_impl_item(&derived_methods)); - //let impl_block: Option = None; - quote! { #item + #impl_block #(#ffi_fns)* } } @@ -227,15 +231,12 @@ pub fn ffi_import(_attr: TokenStream, item: TokenStream) -> TokenStream { if item.sig.asyncness.is_some() { abort!(item.sig.asyncness, "Async functions are not supported"); } - if item.sig.unsafety.is_some() { abort!(item.sig.unsafety, "You shouldn't specify function unsafety"); } - if item.sig.abi.is_some() { abort!(item.sig.abi, "You shouldn't specify function ABI"); } - if !item.sig.generics.params.is_empty() { abort!(item.sig.generics, "Generics are not supported"); } @@ -244,7 +245,6 @@ pub fn ffi_import(_attr: TokenStream, item: TokenStream) -> TokenStream { let ffi_fn = ffi_fn::gen_declaration(&fn_descriptor); quote! { #item - #ffi_fn } } @@ -254,27 +254,15 @@ pub fn ffi_import(_attr: TokenStream, item: TokenStream) -> TokenStream { } fn is_opaque(input: &syn::DeriveInput) -> bool { - let repr = &find_attr(&input.attrs, "repr"); - - if let syn::Data::Enum(item) = &input.data { - if is_fieldless_enum(&input.ident, item, repr) { - return false; - } + if matches!(&input.data, syn::Data::Enum(_)) { + return false; } - !is_repr_attr(repr, "C") + let repr = find_attr(&input.attrs, "repr"); + !is_repr_attr(&repr, "C") } -fn is_fieldless_enum(name: &syn::Ident, item: &syn::DataEnum, repr: &[NestedMeta]) -> bool { - enum_size(name, repr); // NOTE: Verifies that repr(Int) is defined - - !item - .variants - .iter() - .any(|variant| !matches!(variant.fields, syn::Fields::Unit)) -} - -fn find_attr(attrs: &[syn::Attribute], name: &str) -> Vec { +fn find_attr(attrs: &[syn::Attribute], name: &str) -> syn::AttributeArgs { attrs .iter() .filter_map(|attr| { @@ -304,17 +292,3 @@ fn is_repr_attr(repr: &[NestedMeta], name: &str) -> bool { false }) } - -fn enum_size(enum_name: &syn::Ident, repr: &[NestedMeta]) -> TokenStream2 { - if is_repr_attr(repr, "u8") { - quote! {u8} - } else if is_repr_attr(repr, "u16") { - quote! {u16} - } else if is_repr_attr(repr, "u32") { - quote! {u32} - } else if is_repr_attr(repr, "u64") { - quote! {u64} - } else { - abort!(enum_name, "Enum doesn't have a valid representation") - } -} diff --git a/ffi/derive/src/util.rs b/ffi/derive/src/util.rs index e078652b00a..930188a4788 100644 --- a/ffi/derive/src/util.rs +++ b/ffi/derive/src/util.rs @@ -123,8 +123,8 @@ pub fn gen_arg_src_to_ffi(arg: &Arg, is_output: bool) -> TokenStream { quote! { #ffi_conversion - // NOTE: `AsReprCRef` prevents ownerhip transfer over FFI - let #arg_name = iroha_ffi::AsReprCRef::as_ref(&#arg_name); + // NOTE: `AsReprC` prevents ownerhip transfer over FFI + let #arg_name = iroha_ffi::AsReprC::as_ref(&#arg_name); } } diff --git a/ffi/derive/src/wrapper.rs b/ffi/derive/src/wrapper.rs index 5a6c1b02028..cd5c366fbc3 100644 --- a/ffi/derive/src/wrapper.rs +++ b/ffi/derive/src/wrapper.rs @@ -4,7 +4,7 @@ use quote::quote; use crate::{ ffi_fn, - impl_visitor::{Arg, FnDescriptor, ReturnArg}, + impl_visitor::{Arg, FnDescriptor}, util::{gen_arg_ffi_to_src, gen_arg_src_to_ffi}, }; @@ -88,7 +88,7 @@ fn gen_input_conversion_stmts(fn_descriptor: &FnDescriptor) -> TokenStream { } fn gen_ffi_fn_call_stmt(fn_descriptor: &FnDescriptor) -> TokenStream { - let ffi_fn_name = ffi_fn::gen_fn_name(fn_descriptor.self_ty_name(), &fn_descriptor.sig.ident); + let ffi_fn_name = ffi_fn::gen_fn_name(fn_descriptor); let arg_names: Vec<_> = fn_descriptor.input_args.iter().map(Arg::name).collect(); quote! { @@ -96,7 +96,7 @@ fn gen_ffi_fn_call_stmt(fn_descriptor: &FnDescriptor) -> TokenStream { } } -fn gen_return_stmt(arg: &ReturnArg) -> TokenStream { +fn gen_return_stmt(arg: &Arg) -> TokenStream { let (arg_name, output_arg_conversion) = (arg.name(), gen_arg_ffi_to_src(arg, true)); quote! { diff --git a/ffi/derive/tests/ui_pass/shared_fns.rs b/ffi/derive/tests/ui_pass/shared_fns.rs deleted file mode 100644 index 5b31a26406a..00000000000 --- a/ffi/derive/tests/ui_pass/shared_fns.rs +++ /dev/null @@ -1,84 +0,0 @@ -use std::alloc::alloc; - -use iroha_ffi::{def_ffi_fn, ffi, ffi_export, handles, IntoFfi, TryFromReprC}; - -ffi! { - #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, IntoFfi, TryFromReprC)] - pub struct FfiStruct1 { - name: String, - } - - #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, IntoFfi, TryFromReprC)] - pub struct FfiStruct2 { - name: String, - } -} - -handles! {0, FfiStruct1, FfiStruct2} -def_ffi_fn! {Drop: FfiStruct1, FfiStruct2} -def_ffi_fn! {Clone: FfiStruct1, FfiStruct2} -def_ffi_fn! {Eq: FfiStruct1, FfiStruct2} -def_ffi_fn! {Ord: FfiStruct1, FfiStruct2} - -#[ffi_export] -impl FfiStruct1 { - /// New - pub fn new(name: String) -> Self { - Self { name } - } -} - -fn main() { - use core::mem::MaybeUninit; - - use iroha_ffi::{AsReprCRef, Handle}; - - let name = String::from("X"); - let ffi_struct1: FfiStruct1 = unsafe { - let mut ffi_struct = MaybeUninit::<*mut FfiStruct1>::uninit(); - let name = IntoFfi::into_ffi(name); - FfiStruct1__new(name.as_ref(), ffi_struct.as_mut_ptr()); - TryFromReprC::try_from_repr_c(ffi_struct.assume_init(), &mut ()).unwrap() - }; - - unsafe { - let cloned: FfiStruct1 = { - let mut cloned: MaybeUninit<*mut FfiStruct1> = MaybeUninit::uninit(); - - __clone( - FfiStruct1::ID, - (&ffi_struct1).into_ffi().cast(), - cloned.as_mut_ptr().cast(), - ); - - TryFromReprC::try_from_repr_c(cloned.assume_init(), &mut ()).unwrap() - }; - - let mut is_equal: MaybeUninit = MaybeUninit::uninit(); - __eq( - FfiStruct1::ID, - (&ffi_struct1).into_ffi().cast(), - (&cloned).into_ffi().cast(), - is_equal.as_mut_ptr(), - ); - assert_eq!( - true, - TryFromReprC::try_from_repr_c(is_equal.assume_init(), &mut ()).unwrap() - ); - - let mut ordering: MaybeUninit = MaybeUninit::uninit(); - __ord( - FfiStruct1::ID, - (&ffi_struct1).into_ffi().cast(), - (&cloned).into_ffi().cast(), - ordering.as_mut_ptr(), - ); - assert_eq!( - core::cmp::Ordering::Equal, - TryFromReprC::try_from_repr_c(ordering.assume_init(), &mut ()).unwrap() - ); - - __drop(FfiStruct1::ID, ffi_struct1.into_ffi().cast()); - __drop(FfiStruct1::ID, cloned.into_ffi().cast()); - } -} diff --git a/ffi/derive/tests/ui_pass/valid.rs b/ffi/derive/tests/ui_pass/valid.rs deleted file mode 100644 index 8954b71e0ae..00000000000 --- a/ffi/derive/tests/ui_pass/valid.rs +++ /dev/null @@ -1,117 +0,0 @@ -use std::{alloc::alloc, collections::BTreeMap}; - -use getset::Getters; -use iroha_ffi::{def_ffi_fn, ffi_export, handles, IntoFfi, TryFromReprC}; - -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, IntoFfi, TryFromReprC)] -pub struct Name(&'static str); -#[derive(Debug, Clone, PartialEq, IntoFfi, TryFromReprC)] -pub struct Value(&'static str); - -/// FfiStruct -#[derive(Clone, Getters, IntoFfi, TryFromReprC)] -#[getset(get = "pub")] -#[ffi_export] -pub struct FfiStruct { - /// Name - name: Name, - /// Params - #[getset(skip)] - params: BTreeMap, -} - -handles! {0, FfiStruct} -def_ffi_fn! {Drop: FfiStruct} - -#[ffi_export] -impl FfiStruct { - /// New - pub fn new(name: Name) -> Self { - Self { - name, - params: BTreeMap::default(), - } - } - - /// With params - pub fn with_params(mut self, params: impl IntoIterator) -> Self { - self.params = params.into_iter().collect(); - self - } - - /// Get param - pub fn get_param(&self, name: &Name) -> Option<&Value> { - self.params.get(name) - } - - /// Params - pub fn params(&self) -> impl ExactSizeIterator { - self.params.iter() - } -} - -/// Test -#[ffi_export] -pub fn ffi_duplicate_with_name(a: &FfiStruct, name: Name) -> FfiStruct { - let mut result = a.clone(); - result.name = name; - result -} - -fn main() { - use core::mem::MaybeUninit; - - use iroha_ffi::{AsReprCRef, Handle}; - - let name = Name("X"); - let mut ffi_struct: FfiStruct = unsafe { - let mut ffi_struct = MaybeUninit::<*mut FfiStruct>::uninit(); - let name = IntoFfi::into_ffi(name.clone()); - FfiStruct__new(name, ffi_struct.as_mut_ptr()); - TryFromReprC::try_from_repr_c(ffi_struct.assume_init(), &mut ()).unwrap() - }; - - let in_params = vec![(Name("Nomen"), Value("Omen"))]; - let mut param: MaybeUninit<*const Value> = MaybeUninit::uninit(); - let mut out_params_data = Vec::with_capacity(2); - let mut data_len = MaybeUninit::::uninit(); - - let out_params = iroha_ffi::slice::OutBoxedSlice::from_uninit_slice( - Some(&mut out_params_data[..]), - &mut data_len, - ); - - unsafe { - let name = IntoFfi::into_ffi(name.clone()); - - FfiStruct__with_params( - IntoFfi::into_ffi(&mut ffi_struct), - in_params.clone().into_ffi().as_ref(), - ); - FfiStruct__get_param(IntoFfi::into_ffi(&ffi_struct), name, param.as_mut_ptr()); - FfiStruct__params(IntoFfi::into_ffi(&ffi_struct), out_params); - - let _param: Option<&Value> = - TryFromReprC::try_from_repr_c(param.assume_init(), &mut ()).unwrap(); - out_params_data.set_len(data_len.assume_init() as usize); - - __drop(FfiStruct::ID, ffi_struct.into_ffi().cast()); - } - - let ffi_struct = FfiStruct::new(Name("foo")).with_params(in_params.clone()); - let mut param: MaybeUninit<*mut FfiStruct> = MaybeUninit::uninit(); - - unsafe { - let dup_name = Name("bar"); - __ffi_duplicate_with_name( - (&ffi_struct).into_ffi(), - dup_name.clone().into_ffi(), - param.as_mut_ptr(), - ); - - let result = &mut *param.assume_init(); - - assert_eq!(result.name, dup_name); - assert_eq!(result.get_param(&Name("Nomen")), Some(&Value("Omen"))); - } -} diff --git a/ffi/src/lib.rs b/ffi/src/lib.rs index d13a7f7a2f6..3e7f2e4fc57 100644 --- a/ffi/src/lib.rs +++ b/ffi/src/lib.rs @@ -42,7 +42,8 @@ pub unsafe trait Handle { pub unsafe trait ReprC: Sized {} /// Used to do a cheap reference-to-[`ReprC`]-reference conversion -pub trait AsReprCRef<'itm> { +// TODO: With GATs it should be possible to remove the `itm parameter +pub trait AsReprC<'itm> { /// Robust C ABI compliant representation of &[`Self`] type Target: ReprC + 'itm; @@ -51,8 +52,11 @@ pub trait AsReprCRef<'itm> { } /// Conversion from a type that implements [`ReprC`]. +/// Except for opaque pointer types, ownership transfer over FFI is not permitted +// TODO: With GATs it should be possible to remove the `itm parameter pub trait TryFromReprC<'itm>: Sized + 'itm { /// Robust C ABI compliant representation of [`Self`] + // NOTE: Type is `Copy` to indicate that there can be no ownership transfer type Source: ReprC + Copy; /// Type into which state can be stored during conversion. Useful for returning @@ -86,7 +90,8 @@ pub trait IntoFfi: Sized { } /// Type that can be returned from an FFI function as an out-pointer function argument -pub trait OutPtrOf: ReprC { +// NOTE: Type is `Copy` to indicate that there can be no ownership transfer +pub trait OutPtrOf: ReprC + Copy { /// Try to write `T` into [`Self`] out-pointer and return whether or not it was successful /// /// # Errors @@ -127,10 +132,27 @@ pub enum FfiReturn { Ok = 0, } +/// Wrapper around struct/enum opaque pointer. When wrapped with the [`ffi`] macro in the +/// crate linking dynamically to some `cdylib` crate, it replaces struct/enum body definition +#[derive(Debug, Clone, Copy)] +#[repr(transparent)] +pub struct Opaque { + __data: [u8; 0], + + // Required for !Send & !Sync & !Unpin. + // + // - `*mut u8` is !Send & !Sync. It must be in `PhantomData` to not + // affect alignment. + // + // - `PhantomPinned` is !Unpin. It must be in `PhantomData` because + // its memory representation is not considered FFI-safe. + __marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>, +} + unsafe impl ReprC for *const T {} unsafe impl ReprC for *mut T {} -impl<'itm, T: ReprC + Copy + 'itm> AsReprCRef<'itm> for T +impl<'itm, T: ReprC + Copy + 'itm> AsReprC<'itm> for T where T: IntoFfi, { @@ -140,7 +162,7 @@ where *self } } -impl<'itm, T: 'itm> AsReprCRef<'itm> for *const T { +impl<'itm, T: 'itm> AsReprC<'itm> for *const T { type Target = Self; fn as_ref(&self) -> Self::Target { @@ -164,6 +186,14 @@ impl<'itm, T: ReprC> TryFromReprC<'itm> for &'itm mut T { source.as_mut().ok_or(FfiReturn::ArgIsNull) } } +impl<'itm, T: TryFromReprC<'itm>> TryFromReprC<'itm> for alloc::boxed::Box { + type Source = T::Source; + type Store = T::Store; + + unsafe fn try_from_repr_c(source: Self::Source, store: &'itm mut Self::Store) -> Result { + Ok(alloc::boxed::Box::new(TryFromReprC::try_from_repr_c(source, store)?)) + } +} impl IntoFfi for &T where @@ -185,30 +215,15 @@ where Self::Target::from(self) } } +impl IntoFfi for alloc::boxed::Box where { + type Target = T::Target; -impl OutPtrOf<*mut T> for *mut *mut T { - unsafe fn write(self, source: *mut T) -> Result<()> { - if self.is_null() { - return Err(FfiReturn::ArgIsNull); - } - - self.write(source); - Ok(()) + fn into_ffi(self) -> Self::Target { + (*self).into_ffi() } } -impl OutPtrOf<*const T> for *mut *const T { - unsafe fn write(self, source: *const T) -> Result<()> { - if self.is_null() { - return Err(FfiReturn::ArgIsNull); - } - self.write(source); - Ok(()) - } -} impl OutPtrOf for *mut T -where - T: IntoFfi, { unsafe fn write(self, source: T) -> Result<()> { if self.is_null() { @@ -244,23 +259,6 @@ where type OutPtr = *mut Self; } -/// Wrapper around struct/enum opaque pointer. When wrapped with the [`ffi`] macro in the -/// crate linking dynamically to some `cdylib`, it replaces struct/enum body definition -#[derive(Debug, Clone, Copy)] -#[repr(transparent)] -pub struct Opaque { - __data: [u8; 0], - - // Required for !Send & !Sync & !Unpin. - // - // - `*mut u8` is !Send & !Sync. It must be in `PhantomData` to not - // affect alignment. - // - // - `PhantomPinned` is !Unpin. It must be in `PhantomData` because - // its memory representation is not considered FFI-safe. - __marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>, -} - macro_rules! impl_tuple { ( ($( $ty:ident ),+ $(,)?) -> $ffi_ty:ident ) => { /// FFI-compatible tuple with n elements @@ -278,7 +276,7 @@ macro_rules! impl_tuple { unsafe impl<$($ty: ReprC),+> ReprC for $ffi_ty<$($ty),+> {} - impl<'itm, $($ty: ReprC + 'itm),+> AsReprCRef<'itm> for $ffi_ty<$($ty),+> { + impl<'itm, $($ty: ReprC + 'itm),+> AsReprC<'itm> for $ffi_ty<$($ty),+> { type Target = *const Self; fn as_ref(&self) -> Self::Target { diff --git a/ffi/src/owned.rs b/ffi/src/owned.rs index 2d939bd6b1c..81a0200db2b 100644 --- a/ffi/src/owned.rs +++ b/ffi/src/owned.rs @@ -5,7 +5,7 @@ use alloc::{borrow::ToOwned, boxed::Box, string::String, vec::Vec}; use crate::{ slice::{OutBoxedSlice, SliceRef}, - AsReprCRef, FfiReturn, IntoFfi, Output, ReprC, Result, TryFromReprC, + AsReprC, FfiReturn, IntoFfi, Output, ReprC, Result, TryFromReprC, }; /// Trait that facilitates the implementation of [`IntoFfi`] for vectors of foreign types @@ -81,14 +81,14 @@ impl FromIterator for LocalSlice { } } -impl<'itm, T: ReprC + 'itm> AsReprCRef<'itm> for Local { +impl<'itm, T: ReprC + 'itm> AsReprC<'itm> for Local { type Target = *const T; fn as_ref(&self) -> Self::Target { &(self.0) } } -impl<'slice, T: ReprC + 'slice> AsReprCRef<'slice> for LocalSlice { +impl<'slice, T: ReprC + 'slice> AsReprC<'slice> for LocalSlice { type Target = SliceRef<'slice, T>; fn as_ref(&self) -> Self::Target { diff --git a/ffi/src/primitives.rs b/ffi/src/primitives.rs index a1954315df1..877c85d725a 100644 --- a/ffi/src/primitives.rs +++ b/ffi/src/primitives.rs @@ -288,4 +288,4 @@ macro_rules! primitive_impls { )+}; } -primitive_impls! {u8, u16, u32, u64, i8, i16, i32, i64} +primitive_impls! {u8, u16, u32, u64, i8, i16, i32, i64, i128, u128} diff --git a/ffi/src/slice.rs b/ffi/src/slice.rs index ce3c1df9603..4aad24efb1f 100644 --- a/ffi/src/slice.rs +++ b/ffi/src/slice.rs @@ -3,7 +3,7 @@ use core::{marker::PhantomData, mem::MaybeUninit}; use crate::{ - owned::LocalSlice, AsReprCRef, FfiReturn, IntoFfi, OutPtrOf, Output, ReprC, Result, + owned::LocalSlice, AsReprC, FfiReturn, IntoFfi, OutPtrOf, Output, ReprC, Result, TryFromReprC, }; @@ -265,7 +265,7 @@ unsafe impl ReprC for OutSliceRef {} unsafe impl ReprC for OutSliceMut {} unsafe impl ReprC for OutBoxedSlice {} -impl<'slice, T: 'slice> AsReprCRef<'slice> for SliceRef<'slice, T> { +impl<'slice, T: 'slice> AsReprC<'slice> for SliceRef<'slice, T> { type Target = Self; fn as_ref(&self) -> Self::Target { diff --git a/ffi/tests/ffi_export.rs b/ffi/tests/ffi_export.rs index 8ad79e695e4..e58b6734dfc 100644 --- a/ffi/tests/ffi_export.rs +++ b/ffi/tests/ffi_export.rs @@ -3,36 +3,52 @@ use std::{alloc::alloc, collections::BTreeMap, mem::MaybeUninit}; use iroha_ffi::{ - def_ffi_fn, ffi_export, handles, slice::OutBoxedSlice, AsReprCRef, FfiReturn, FfiTuple2, - Handle, IntoFfi, TryFromReprC, + def_ffi_fn, ffi_export, handles, slice::OutBoxedSlice, AsReprC, FfiReturn, FfiTuple2, Handle, + IntoFfi, TryFromReprC, }; +pub trait Target { + type Target; + + fn target(self) -> Self::Target; +} + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, IntoFfi, TryFromReprC)] pub struct Name(String); #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, IntoFfi, TryFromReprC)] pub struct Value(String); -/// FfiStruct +/// Opaque structure #[derive(Clone, IntoFfi, TryFromReprC)] -#[ffi_export] -pub struct FfiStruct { +pub struct OpaqueStruct { name: Option, tokens: Vec, params: BTreeMap, } -fn get_default_params() -> [(Name, Value); 2] { - [ - (Name(String::from("Nomen")), Value(String::from("Omen"))), - (Name(String::from("Nomen2")), Value(String::from("Omen2"))), - ] +/// Fieldless enum +#[repr(u8)] +#[derive(Debug, Clone, PartialEq, IntoFfi, TryFromReprC)] +pub enum FieldlessEnum { + A, + B, + C, +} + +/// Data-carrying enum +#[derive(Clone, IntoFfi, TryFromReprC)] +pub enum DataCarryingEnum { + A(OpaqueStruct), + B(u32), + // TODO: Fix this + //C, } -handles! {0, FfiStruct} -def_ffi_fn! {Drop: FfiStruct} +handles! {0, OpaqueStruct} +def_ffi_fn! {Drop: OpaqueStruct} #[ffi_export] -impl FfiStruct { +impl OpaqueStruct { /// New pub fn new(name: Name) -> Self { Self { @@ -53,9 +69,9 @@ impl FfiStruct { } /// With params - // Note: `-> FfiStruct` used instead of `-> Self` to showcase that such signature supported by `#[ffi_export]` + // Note: `-> OpaqueStruct` used instead of `-> Self` to showcase that such signature supported by `#[ffi_export]` #[must_use] - pub fn with_params(mut self, params: impl IntoIterator) -> FfiStruct { + pub fn with_params(mut self, params: impl IntoIterator) -> OpaqueStruct { self.params = params.into_iter().collect(); self } @@ -92,18 +108,25 @@ impl FfiStruct { #[ffi_export] /// Return byte -pub fn simple(byte: u8) -> u8 { +pub fn freestanding_with_primitive(byte: u8) -> u8 { byte } -pub trait Target { - type Target; +#[ffi_export] +/// Return fieldless enum +pub fn freestanding_with_fieldless_enum(enum_: FieldlessEnum) -> FieldlessEnum { + enum_ +} - fn target(self) -> Self::Target; +#[ffi_export] +/// Return data-carrying enum +pub fn freestanding_with_data_carrying_enum(enum_: DataCarryingEnum) -> DataCarryingEnum { + enum_ } + #[ffi_export] -impl Target for FfiStruct { +impl Target for OpaqueStruct { type Target = Option; fn target(self) -> ::Target { @@ -111,7 +134,14 @@ impl Target for FfiStruct { } } -fn get_new_struct() -> FfiStruct { +fn get_default_params() -> [(Name, Value); 2] { + [ + (Name(String::from("Nomen")), Value(String::from("Omen"))), + (Name(String::from("Nomen2")), Value(String::from("Omen2"))), + ] +} + +fn get_new_struct() -> OpaqueStruct { let name = Name(String::from("X")); unsafe { @@ -119,7 +149,7 @@ fn get_new_struct() -> FfiStruct { assert_eq!( FfiReturn::Ok, - FfiStruct__new(IntoFfi::into_ffi(name), ffi_struct.as_mut_ptr()) + OpaqueStruct__new(IntoFfi::into_ffi(name), ffi_struct.as_mut_ptr()) ); let ffi_struct = ffi_struct.assume_init(); @@ -129,13 +159,13 @@ fn get_new_struct() -> FfiStruct { } #[allow(trivial_casts)] -fn get_new_struct_with_params() -> FfiStruct { +fn get_new_struct_with_params() -> OpaqueStruct { let mut ffi_struct = get_new_struct(); let params = get_default_params(); let params_ffi = params.into_ffi(); assert_eq!(FfiReturn::Ok, unsafe { - FfiStruct__with_params(IntoFfi::into_ffi(&mut ffi_struct), params_ffi.as_ref()) + OpaqueStruct__with_params(IntoFfi::into_ffi(&mut ffi_struct), params_ffi.as_ref()) }); ffi_struct @@ -151,7 +181,7 @@ fn constructor() { assert_eq!( FfiReturn::Ok, - __drop(FfiStruct::ID, ffi_struct.into_ffi().cast()) + __drop(OpaqueStruct::ID, ffi_struct.into_ffi().cast()) ); } } @@ -169,7 +199,7 @@ fn builder_method() { assert_eq!( FfiReturn::Ok, - __drop(FfiStruct::ID, ffi_struct.into_ffi().cast()) + __drop(OpaqueStruct::ID, ffi_struct.into_ffi().cast()) ); } } @@ -181,7 +211,7 @@ fn consume_self() { unsafe { assert_eq!( FfiReturn::Ok, - FfiStruct__consume_self(ffi_struct.into_ffi().cast()) + OpaqueStruct__consume_self(ffi_struct.into_ffi().cast()) ); } } @@ -200,7 +230,7 @@ fn into_iter_item_impl_into() { unsafe { assert_eq!( FfiReturn::Ok, - FfiStruct__with_tokens(IntoFfi::into_ffi(&mut ffi_struct), tokens_ffi.as_ref()) + OpaqueStruct__with_tokens(IntoFfi::into_ffi(&mut ffi_struct), tokens_ffi.as_ref()) ); assert_eq!(2, ffi_struct.tokens.len()); @@ -208,7 +238,7 @@ fn into_iter_item_impl_into() { assert_eq!( FfiReturn::Ok, - __drop(FfiStruct::ID, ffi_struct.into_ffi().cast()) + __drop(OpaqueStruct::ID, ffi_struct.into_ffi().cast()) ); } } @@ -222,7 +252,7 @@ fn return_option() { let name1 = Name(String::from("Non")); assert_eq!(FfiReturn::Ok, unsafe { - FfiStruct__get_param(IntoFfi::into_ffi(&ffi_struct), &name1, param1.as_mut_ptr()) + OpaqueStruct__get_param(IntoFfi::into_ffi(&ffi_struct), &name1, param1.as_mut_ptr()) }); let param1 = unsafe { param1.assume_init() }; assert!(param1.is_null()); @@ -233,7 +263,7 @@ fn return_option() { let name2 = Name(String::from("Nomen")); assert_eq!(FfiReturn::Ok, unsafe { - FfiStruct__get_param(IntoFfi::into_ffi(&ffi_struct), &name2, param2.as_mut_ptr()) + OpaqueStruct__get_param(IntoFfi::into_ffi(&ffi_struct), &name2, param2.as_mut_ptr()) }); unsafe { @@ -244,7 +274,7 @@ fn return_option() { assert_eq!(Some(&Value(String::from("Omen"))), param2); assert_eq!( FfiReturn::Ok, - __drop(FfiStruct::ID, ffi_struct.into_ffi().cast()) + __drop(OpaqueStruct::ID, ffi_struct.into_ffi().cast()) ); } } @@ -259,12 +289,12 @@ fn empty_return_iterator() { unsafe { assert_eq!( FfiReturn::Ok, - FfiStruct__params(IntoFfi::into_ffi(&ffi_struct), out_params) + OpaqueStruct__params(IntoFfi::into_ffi(&ffi_struct), out_params) ); assert!(params_len.assume_init() == 2); assert_eq!( FfiReturn::Ok, - __drop(FfiStruct::ID, ffi_struct.into_ffi().cast()) + __drop(OpaqueStruct::ID, ffi_struct.into_ffi().cast()) ); } } @@ -283,7 +313,7 @@ fn return_iterator() { unsafe { assert_eq!( FfiReturn::Ok, - FfiStruct__params(IntoFfi::into_ffi(&ffi_struct), out_params) + OpaqueStruct__params(IntoFfi::into_ffi(&ffi_struct), out_params) ); assert_eq!(params_len.assume_init(), 2); @@ -296,7 +326,7 @@ fn return_iterator() { assert_eq!( FfiReturn::Ok, - __drop(FfiStruct::ID, ffi_struct.into_ffi().cast()) + __drop(OpaqueStruct::ID, ffi_struct.into_ffi().cast()) ); } } @@ -308,28 +338,100 @@ fn return_result() { unsafe { assert_eq!( FfiReturn::ExecutionFail, - FfiStruct__fallible_int_output(From::from(false), output.as_mut_ptr()) + OpaqueStruct__fallible_int_output(From::from(false), output.as_mut_ptr()) ); assert_eq!(0, output.assume_init()); assert_eq!( FfiReturn::Ok, - FfiStruct__fallible_int_output(From::from(true), output.as_mut_ptr()) + OpaqueStruct__fallible_int_output(From::from(true), output.as_mut_ptr()) ); assert_eq!(42, output.assume_init()); } } +#[test] +fn primitive_conversion() { + let byte: u8 = 1; + let mut output = MaybeUninit::new(0); + + unsafe { + assert_eq!( + FfiReturn::Ok, + __freestanding_with_primitive(byte.into_ffi(), output.as_mut_ptr()) + ); + + let ret_val = output.assume_init(); + #[cfg(feature = "wasm")] + let ret_val: u8 = + TryFromReprC::try_from_repr_c(ret_val, &mut ()).expect("Conversion failed"); + + assert_eq!(1, ret_val); + } +} + +#[test] +fn fieldless_enum_conversion() { + let fieldless_enum = FieldlessEnum::A; + let mut output = MaybeUninit::new(2); + + unsafe { + assert_eq!( + FfiReturn::Ok, + __freestanding_with_fieldless_enum(fieldless_enum.into_ffi(), output.as_mut_ptr()) + ); + + let ret_val = TryFromReprC::try_from_repr_c(output.assume_init(), &mut ()); + assert_eq!(FieldlessEnum::A, ret_val.expect("Conversion failed")); + } +} + +#[test] +fn data_carrying_enum_conversion() { + let data_carrying_enum = DataCarryingEnum::A(get_new_struct()); + let mut output = MaybeUninit::new(DataCarryingEnum::B(42)); + + unsafe { + assert_eq!( + FfiReturn::Ok, + __freestanding_with_data_carrying_enum(data_carrying_enum.into_ffi(), output.as_mut_ptr()) + ); + + let ret_val = TryFromReprC::try_from_repr_c(output.assume_init(), &mut ()); + assert_eq!(data_carrying_enum, ret_val.expect("Conversion failed")); + } +} + +#[test] #[cfg(feature = "wasm")] +fn primitive_conversion_failed() { + let byte: u32 = u32::MAX; + let mut output = MaybeUninit::new(0); + + unsafe { + assert_eq!( + FfiReturn::ConversionFailed, + __freestanding_with_primitive(byte, output.as_mut_ptr()) + ); + + let ret_val = TryFromReprC::try_from_repr_c(output.assume_init(), &mut ()); + assert_eq!(0_u8, ret_val.expect("Conversion failed")); + } +} + #[test] -fn conversion_failed() { +#[cfg(feature = "wasm")] +fn fieldless_enum_conversion_failed() { let byte: u32 = u32::MAX; let mut output = MaybeUninit::new(0); unsafe { assert_eq!( FfiReturn::ConversionFailed, - __simple(byte, output.as_mut_ptr()) - ) + __freestanding_with_fieldless_enum(byte, output.as_mut_ptr()) + ); + + let ret_val = TryFromReprC::try_from_repr_c(output.assume_init(), &mut ()); + assert_eq!(FieldlessEnum::A, ret_val.expect("Conversion failed")); } } @@ -341,7 +443,7 @@ fn invoke_trait_method() { unsafe { assert_eq!( FfiReturn::Ok, - FfiStruct__Target__target(IntoFfi::into_ffi(ffi_struct), output.as_mut_ptr()) + OpaqueStruct__Target__target(IntoFfi::into_ffi(ffi_struct), output.as_mut_ptr()) ); let name = TryFromReprC::try_from_repr_c(output.assume_init(), &mut ()).unwrap(); assert_eq!(Name(String::from("X")), name); diff --git a/ffi/tests/gen_shared_fns.rs b/ffi/tests/gen_shared_fns.rs index bbdfe4c0592..36ae00590bf 100644 --- a/ffi/tests/gen_shared_fns.rs +++ b/ffi/tests/gen_shared_fns.rs @@ -2,18 +2,16 @@ use std::{alloc::alloc, cmp::Ordering, mem::MaybeUninit}; -use iroha_ffi::{def_ffi_fn, ffi, ffi_export, handles, FfiReturn, Handle, IntoFfi, TryFromReprC}; +use iroha_ffi::{def_ffi_fn, ffi_export, handles, FfiReturn, Handle, IntoFfi, TryFromReprC}; -ffi! { - #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, IntoFfi, TryFromReprC)] - pub struct FfiStruct1 { - name: String, - } +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, IntoFfi, TryFromReprC)] +pub struct FfiStruct1 { + name: String, +} - #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, IntoFfi, TryFromReprC)] - pub struct FfiStruct2 { - name: String, - } +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, IntoFfi, TryFromReprC)] +pub struct FfiStruct2 { + name: String, } handles! {0, FfiStruct1, FfiStruct2} diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index f3706b61fb3..9ce8788a042 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -21,10 +21,16 @@ default = ["std"] # Enable static linkage of the rust standard library. # Please refer to https://docs.rust-embedded.org/book/intro/no-std.html std = ["iroha_macro/std", "fixnum/std", "thiserror"] +# Replace structures and methods with FFI equivalents to facilitate dynamic linkage (mainly used in smartcontracts) +ffi_import = [] + +# Expose FFI API for dynamic linking (Internal use only) +ffi_export = ["std"] [dependencies] iroha_macro = { path = "../macro", version = "=2.0.0-pre-rc.7", default-features = false } iroha_schema = { path = "../schema", version = "=2.0.0-pre-rc.7", default-features = false } +iroha_ffi = { path = "../ffi", version = "=2.0.0-pre-rc.7" } parity-scale-codec = { version = "2.3.1", default-features = false, features = ["derive"] } fixnum = { version = "0.6.1", default-features = false, features = ["serde", "parity", "i64"]} diff --git a/primitives/src/fixed.rs b/primitives/src/fixed.rs index 85fe1856013..e364ee8eaa2 100644 --- a/primitives/src/fixed.rs +++ b/primitives/src/fixed.rs @@ -1,13 +1,16 @@ //! Types used for Fixed-point operations. Uses [`fixnum::FixedPoint`]. #[cfg(not(feature = "std"))] -use alloc::{format, string::String, vec::Vec}; +use alloc::{alloc::alloc, boxed::Box, format, string::String, vec::Vec}; +#[cfg(feature = "std")] +use std::alloc::alloc; use derive_more::Display; use fixnum::{ ops::{Bounded, CheckedAdd, CheckedSub, Zero}, ArithmeticError, }; +use iroha_ffi::{IntoFfi, TryFromReprC}; use iroha_schema::IntoSchema; use parity_scale_codec::{Decode, Encode}; use serde::{Deserialize, Serialize}; @@ -43,6 +46,8 @@ pub type FixNum = fixnum::FixedPoint; Encode, Deserialize, Serialize, + IntoFfi, + TryFromReprC, IntoSchema, )] pub struct Fixed(FixNum);