From c5884cc7438f7e1a3a8dbdd784e3e2b6f70f9983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marin=20Ver=C5=A1i=C4=87?= Date: Thu, 4 Aug 2022 14:44:41 +0200 Subject: [PATCH] replace client flag with ffi_export/ffi_import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marin Veršić --- Cargo.lock | 12 +- crypto/Cargo.toml | 6 +- crypto/build.rs | 14 + crypto/src/lib.rs | 95 ++++--- data_model/Cargo.toml | 6 +- data_model/build.rs | 14 + data_model/src/account.rs | 149 ++++++----- data_model/src/asset.rs | 252 ++++++++++-------- data_model/src/domain.rs | 142 +++++----- data_model/src/lib.rs | 110 +++++--- data_model/src/metadata.rs | 4 +- data_model/src/name.rs | 10 +- data_model/src/permissions.rs | 63 +++-- data_model/src/role.rs | 74 ++--- ffi/Cargo.toml | 3 - ffi/derive/Cargo.toml | 3 - ffi/derive/src/convert.rs | 140 ++++++---- ffi/derive/src/ffi_fn.rs | 35 +-- ffi/derive/src/impl_visitor.rs | 154 +++-------- ffi/derive/src/lib.rs | 130 ++++++--- ffi/derive/src/util.rs | 22 +- ffi/derive/src/wrapper.rs | 16 +- ffi/derive/tests/ui_fail/derive_skip_field.rs | 2 +- .../tests/ui_fail/derive_skip_struct.rs | 2 +- ffi/derive/tests/ui_pass/shared_fns.rs | 22 +- ffi/derive/tests/ui_pass/valid.rs | 22 +- ffi/src/handle.rs | 22 +- ffi/src/lib.rs | 17 ++ ffi/tests/ffi_export.rs | 21 +- ffi/tests/gen_shared_fns.rs | 17 +- ffi/tests/getset.rs | 7 +- primitives/tests/ui.rs | 4 +- 32 files changed, 859 insertions(+), 731 deletions(-) create mode 100644 crypto/build.rs create mode 100644 data_model/build.rs diff --git a/Cargo.lock b/Cargo.lock index 65118b68485..ba7322655e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2843,9 +2843,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.39" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" +checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" dependencies = [ "unicode-ident", ] @@ -2881,9 +2881,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.18" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] @@ -3576,9 +3576,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.95" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942" +checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" dependencies = [ "proc-macro2", "quote", diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index 5a6feb4e897..2266ccc1846 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -13,11 +13,11 @@ default = ["std"] std = ["ursa"] # Force static linking vendored = ["openssl-sys"] -## Replace structures and methods with FFI equivalents. Facilitates dynamic linkage (mainly used in smartcontracts) -#ffi = ["iroha_ffi/client"] # TODO: Doesn't produce valid code yet +# 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_api = ["std"] +ffi_export = ["std"] [dependencies] iroha_primitives = { path = "../primitives", version = "=2.0.0-pre-rc.7", default-features = false } diff --git a/crypto/build.rs b/crypto/build.rs new file mode 100644 index 00000000000..c6b3b4910d9 --- /dev/null +++ b/crypto/build.rs @@ -0,0 +1,14 @@ +//! Build script +//! +//! Warn if `ffi_import` and `ffi_export` features are active at the same time + +fn main() { + let ffi_import = std::env::var_os("CARGO_FEATURE_FFI_IMPORT").is_some(); + let ffi_export = std::env::var_os("CARGO_FEATURE_FFI_EXPORT").is_some(); + + #[allow(clippy::print_stderr)] + if ffi_import && ffi_export { + eprintln!("cargo:warning=Features `ffi_export` and `ffi_import` are mutually exclusive"); + eprintln!("cargo:warning=When both active, `ffi_import` feature takes precedence"); + } +} diff --git a/crypto/src/lib.rs b/crypto/src/lib.rs index f2b5fb83380..b999b8374c9 100644 --- a/crypto/src/lib.rs +++ b/crypto/src/lib.rs @@ -12,17 +12,17 @@ mod signature; mod varint; #[cfg(not(feature = "std"))] -use alloc::{boxed::Box, format, string::String, vec::Vec}; +use alloc::{alloc, boxed::Box, format, string::String, vec::Vec}; use core::{fmt, str::FromStr}; +#[cfg(feature = "std")] +use std::alloc::alloc; #[cfg(feature = "base64")] pub use base64; use derive_more::{DebugCustom, Display}; use getset::Getters; pub use hash::*; -#[cfg(any(feature = "ffi_api", feature = "ffi"))] -use iroha_ffi::ffi_export; -use iroha_ffi::{ffi, IntoFfi, TryFromReprC}; +use iroha_ffi::{IntoFfi, TryFromReprC}; use iroha_primitives::conststr::ConstString; use iroha_schema::IntoSchema; pub use merkle::MerkleTree; @@ -64,22 +64,24 @@ pub struct NoSuchAlgorithm; #[cfg(feature = "std")] impl std::error::Error for NoSuchAlgorithm {} -/// Algorithm for hashing -#[derive(Debug, Clone, Copy, PartialEq, Eq, Display, IntoFfi, TryFromReprC)] -#[repr(u8)] -pub enum Algorithm { - /// Ed25519 - #[display(fmt = "{}", "ED_25519")] - Ed25519, - /// Secp256k1 - #[display(fmt = "{}", "SECP_256_K1")] - Secp256k1, - /// BlsNormal - #[display(fmt = "{}", "BLS_NORMAL")] - BlsNormal, - /// BlsSmall - #[display(fmt = "{}", "BLS_SMALL")] - BlsSmall, +ffi::ffi_item! { + /// Algorithm for hashing + #[derive(Debug, Clone, Copy, PartialEq, Eq, Display, IntoFfi, TryFromReprC)] + #[repr(u8)] + pub enum Algorithm { + /// Ed25519 + #[display(fmt = "{}", "ED_25519")] + Ed25519, + /// Secp256k1 + #[display(fmt = "{}", "SECP_256_K1")] + Secp256k1, + /// BlsNormal + #[display(fmt = "{}", "BLS_NORMAL")] + BlsNormal, + /// BlsSmall + #[display(fmt = "{}", "BLS_SMALL")] + BlsSmall, + } } impl Default for Algorithm { @@ -164,7 +166,7 @@ impl KeyGenConfiguration { } } -ffi! { +ffi::ffi_item! { /// Pair of Public and Private keys. #[derive(Debug, Clone, PartialEq, Eq, Getters, Serialize, IntoFfi, TryFromReprC)] #[getset(get = "pub")] @@ -364,7 +366,7 @@ impl From for KeyParseError { #[cfg(feature = "std")] impl std::error::Error for KeyParseError {} -ffi! { +ffi::ffi_item! { /// Public Key used in signatures. #[derive(DebugCustom, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encode, IntoFfi, TryFromReprC, IntoSchema)] #[debug( @@ -379,7 +381,11 @@ ffi! { } } -#[cfg_attr(any(feature = "ffi_api", feature = "ffi"), ffi_export)] +#[cfg_attr( + all(feature = "ffi_export", not(feature = "ffi_import")), + iroha_ffi::ffi_export +)] +#[cfg_attr(feature = "ffi_import", iroha_ffi::ffi_import)] impl PublicKey { /// Key payload pub fn payload(&self) -> &[u8] { @@ -496,7 +502,7 @@ impl Decode for PublicKey { } } -ffi! { +ffi::ffi_item! { /// Private Key used in signatures. #[derive(DebugCustom, Clone, PartialEq, Eq, Display, Serialize, IntoFfi, TryFromReprC)] #[debug(fmt = "{{ digest: {digest_function}, payload: {:X?}}}", payload)] @@ -511,7 +517,11 @@ ffi! { } } -#[cfg_attr(any(feature = "ffi_api", feature = "ffi"), ffi_export)] +#[cfg_attr( + all(feature = "ffi_export", not(feature = "ffi_import")), + iroha_ffi::ffi_export +)] +#[cfg_attr(feature = "ffi_import", iroha_ffi::ffi_import)] impl PrivateKey { /// Key payload pub fn payload(&self) -> &[u8] { @@ -577,16 +587,39 @@ impl<'de> Deserialize<'de> for PrivateKey { } } -#[cfg(any(feature = "ffi_api", feature = "ffi"))] -mod ffi { +pub mod ffi { + //! Definitions and implementations of FFI related functionalities + use super::*; + macro_rules! ffi_item { + ($it: item) => { + #[cfg(not(feature = "ffi_import"))] + $it + + #[cfg(feature = "ffi_import")] + iroha_ffi::ffi! { $it } + }; + } + + #[cfg(any(feature = "ffi_export", feature = "ffi_import"))] + macro_rules! ffi_fn { + ($macro_name: ident) => { + iroha_ffi::$macro_name! { Clone: KeyPair, PublicKey, PrivateKey } + iroha_ffi::$macro_name! { Eq: KeyPair, PublicKey, PrivateKey } + iroha_ffi::$macro_name! { Ord: PublicKey } + iroha_ffi::$macro_name! { Drop: KeyPair, PublicKey, PrivateKey } + }; + } + iroha_ffi::handles! {KeyPair, PublicKey, PrivateKey} - iroha_ffi::ffi_fn! { Clone: KeyPair, PublicKey, PrivateKey} - iroha_ffi::ffi_fn! { Eq: KeyPair, PublicKey, PrivateKey} - iroha_ffi::ffi_fn! { Ord: PublicKey } - iroha_ffi::ffi_fn! { Drop: KeyPair, PublicKey, PrivateKey} + #[cfg(feature = "ffi_import")] + ffi_fn! {decl_ffi_fn} + #[cfg(all(feature = "ffi_export", not(feature = "ffi_import")))] + ffi_fn! {def_ffi_fn} + + pub(crate) use ffi_item; } /// The prelude re-exports most commonly used traits, structs and macros from this crate. diff --git a/data_model/Cargo.toml b/data_model/Cargo.toml index 476a87594a3..7f978f11106 100644 --- a/data_model/Cargo.toml +++ b/data_model/Cargo.toml @@ -22,11 +22,11 @@ default = ["std"] # Disabled for WASM interoperability, to reduce the binary size. # Please refer to https://docs.rust-embedded.org/book/intro/no-std.html std = ["iroha_macro/std", "iroha_version/std", "iroha_version/warp", "iroha_crypto/std", "iroha_primitives/std", "thiserror", "strum/std", "dashmap", "tokio"] -## Replace structures and methods with FFI equivalents. Facilitates dynamic linkage (mainly used in smartcontracts) -#ffi = ["iroha_ffi/client"] # TODO: Doesn't produce valid code yet. +# Replace structures and methods with FFI equivalents to facilitate dynamic linkage (mainly used in smartcontracts) +ffi_import = ["iroha_crypto/ffi_import"] # Expose FFI API for dynamic linking (Internal use only) -ffi_api = ["std"] +ffi_export = ["std", "iroha_crypto/ffi_export"] # Expose API for mutating structures (Internal use only) mutable_api = [] diff --git a/data_model/build.rs b/data_model/build.rs new file mode 100644 index 00000000000..2d55ca605eb --- /dev/null +++ b/data_model/build.rs @@ -0,0 +1,14 @@ +//! Build script +//! +//! Warn if `ffi_import` and `ffi_export` features are active at the same time + +fn main() { + let ffi_import = std::env::var_os("CARGO_FEATURE_FFI_IMPORT").is_some(); + let ffi_export = std::env::var_os("CARGO_FEATURE_FFI_EXPORT").is_some(); + + #[allow(clippy::print_stderr)] + if ffi_import && ffi_export { + println!("cargo:warning=Features `ffi_export` and `ffi_import` are mutually exclusive"); + println!("cargo:warning=When both active, `ffi_import` feature takes precedence"); + } +} diff --git a/data_model/src/account.rs b/data_model/src/account.rs index 909f205ed28..96f210c8654 100644 --- a/data_model/src/account.rs +++ b/data_model/src/account.rs @@ -2,6 +2,7 @@ #[cfg(not(feature = "std"))] use alloc::{ + alloc, boxed::Box, collections::{btree_map, btree_set}, format, @@ -10,13 +11,14 @@ use alloc::{ }; use core::str::FromStr; #[cfg(feature = "std")] -use std::collections::{btree_map, btree_set}; +use std::{ + alloc::alloc, + collections::{btree_map, btree_set}, +}; use derive_more::Display; use getset::{Getters, MutGetters, Setters}; use iroha_data_model_derive::IdOrdEqHash; -#[cfg(any(feature = "ffi_api", feature = "ffi"))] -use iroha_ffi::ffi_export; use iroha_ffi::{IntoFfi, TryFromReprC}; use iroha_schema::IntoSchema; use parity_scale_codec::{Decode, Encode}; @@ -28,6 +30,7 @@ use crate::{ asset::{prelude::AssetId, AssetsMap}, domain::prelude::*, expression::{ContainsAny, ContextValue, EvaluatesTo}, + ffi::ffi_item, metadata::Metadata, permissions::{PermissionToken, Permissions}, prelude::Asset, @@ -128,30 +131,32 @@ impl Default for SignatureCheckCondition { } } -/// Builder which should be submitted in a transaction to create a new [`Account`] -#[derive( - Debug, - Display, - Clone, - IdOrdEqHash, - Decode, - Encode, - Deserialize, - Serialize, - IntoFfi, - TryFromReprC, - IntoSchema, -)] -#[id(type = "::Id")] -#[allow(clippy::multiple_inherent_impl)] -#[display(fmt = "[{id}]")] -pub struct NewAccount { - /// Identification - id: ::Id, - /// Signatories, i.e. signatures attached to this message. - signatories: Signatories, - /// Metadata that should be submitted with the builder - metadata: Metadata, +ffi_item! { + /// Builder which should be submitted in a transaction to create a new [`Account`] + #[derive( + Debug, + Display, + Clone, + IdOrdEqHash, + Decode, + Encode, + Deserialize, + Serialize, + IntoFfi, + TryFromReprC, + IntoSchema, + )] + #[id(type = "::Id")] + #[allow(clippy::multiple_inherent_impl)] + #[display(fmt = "[{id}]")] + pub struct NewAccount { + /// Identification + id: ::Id, + /// Signatories, i.e. signatures attached to this message. + signatories: Signatories, + /// Metadata that should be submitted with the builder + metadata: Metadata, + } } #[cfg(feature = "mutable_api")] @@ -197,7 +202,11 @@ impl NewAccount { } } -#[cfg_attr(any(feature = "ffi_api", feature = "ffi"), ffi_export)] +#[cfg_attr( + all(feature = "ffi_export", not(feature = "ffi_import")), + iroha_ffi::ffi_export +)] +#[cfg_attr(feature = "ffi_import", iroha_ffi::ffi_import)] impl NewAccount { /// Add [`Metadata`] to the account replacing previously defined #[must_use] @@ -207,44 +216,46 @@ impl NewAccount { } } -/// Account entity is an authority which is used to execute `Iroha Special Instructions`. -#[derive( - Debug, - Display, - Clone, - IdOrdEqHash, - Getters, - MutGetters, - Setters, - Decode, - Encode, - Deserialize, - Serialize, - IntoFfi, - TryFromReprC, - IntoSchema, -)] -#[allow(clippy::multiple_inherent_impl)] -#[display(fmt = "({id})")] // TODO: Add more? -#[id(type = "Id")] -pub struct Account { - /// An Identification of the [`Account`]. - id: ::Id, - /// Asset's in this [`Account`]. - assets: AssetsMap, - /// [`Account`]'s signatories. - signatories: Signatories, - /// Permissions tokens of this account - permission_tokens: Permissions, - /// Condition which checks if the account has the right signatures. - #[getset(get = "pub")] - #[cfg_attr(feature = "mutable_api", getset(set = "pub"))] - signature_check_condition: SignatureCheckCondition, - /// Metadata of this account as a key-value store. - #[cfg_attr(feature = "mutable_api", getset(get_mut = "pub"))] - metadata: Metadata, - /// Roles of this account, they are tags for sets of permissions stored in `World`. - roles: RoleIds, +ffi_item! { + /// Account entity is an authority which is used to execute `Iroha Special Instructions`. + #[derive( + Debug, + Display, + Clone, + IdOrdEqHash, + Getters, + MutGetters, + Setters, + Decode, + Encode, + Deserialize, + Serialize, + IntoFfi, + TryFromReprC, + IntoSchema, + )] + #[allow(clippy::multiple_inherent_impl)] + #[display(fmt = "({id})")] // TODO: Add more? + #[id(type = "Id")] + pub struct Account { + /// An Identification of the [`Account`]. + id: ::Id, + /// Assets in this [`Account`]. + assets: AssetsMap, + /// [`Account`]'s signatories. + signatories: Signatories, + /// Permission tokens of this account + permission_tokens: Permissions, + /// Condition which checks if the account has the right signatures. + #[getset(get = "pub")] + #[cfg_attr(feature = "mutable_api", getset(set = "pub"))] + signature_check_condition: SignatureCheckCondition, + /// Metadata of this account as a key-value store. + #[cfg_attr(feature = "mutable_api", getset(get_mut = "pub"))] + metadata: Metadata, + /// Roles of this account, they are tags for sets of permissions stored in `World`. + roles: RoleIds, + } } impl HasMetadata for Account { @@ -257,7 +268,11 @@ impl Registered for Account { type With = NewAccount; } -#[cfg_attr(any(feature = "ffi_api", feature = "ffi"), ffi_export)] +#[cfg_attr( + all(feature = "ffi_export", not(feature = "ffi_import")), + iroha_ffi::ffi_export +)] +#[cfg_attr(feature = "ffi_import", iroha_ffi::ffi_import)] impl Account { /// Construct builder for [`Account`] identifiable by [`Id`] containing the given signatories. #[must_use] diff --git a/data_model/src/asset.rs b/data_model/src/asset.rs index 5881ee290be..d81c94feb05 100644 --- a/data_model/src/asset.rs +++ b/data_model/src/asset.rs @@ -2,16 +2,16 @@ //! instructions implementations. #[cfg(not(feature = "std"))] -use alloc::{boxed::Box, collections::btree_map, format, string::String, vec::Vec}; +use alloc::{alloc, boxed::Box, collections::btree_map, format, string::String, vec::Vec}; use core::{cmp::Ordering, str::FromStr}; #[cfg(feature = "std")] +use std::alloc::alloc; +#[cfg(feature = "std")] use std::collections::btree_map; use derive_more::Display; use getset::{Getters, MutGetters}; use iroha_data_model_derive::IdOrdEqHash; -#[cfg(any(feature = "ffi_api", feature = "ffi"))] -use iroha_ffi::ffi_export; use iroha_ffi::{IntoFfi, TryFromReprC}; use iroha_macro::FromVariant; use iroha_primitives::{fixed, fixed::Fixed}; @@ -21,8 +21,8 @@ use serde::{Deserialize, Serialize}; use strum::EnumString; use crate::{ - account::prelude::*, domain::prelude::*, metadata::Metadata, HasMetadata, Identifiable, Name, - ParseError, Registered, TryAsMut, TryAsRef, Value, + account::prelude::*, domain::prelude::*, ffi::ffi_item, metadata::Metadata, HasMetadata, + Identifiable, Name, ParseError, Registered, TryAsMut, TryAsRef, Value, }; /// [`AssetsMap`] provides an API to work with collection of key ([`Id`]) - value @@ -48,31 +48,34 @@ pub enum MintabilityError { #[cfg(feature = "std")] impl std::error::Error for MintabilityError {} -/// An entry in [`AssetDefinitionsMap`]. -#[derive( - Debug, - Clone, - PartialEq, - Eq, - Getters, - MutGetters, - Decode, - Encode, - Deserialize, - Serialize, - IntoFfi, - TryFromReprC, - IntoSchema, -)] -#[cfg_attr(any(feature = "ffi_api", feature = "ffi"), ffi_export)] -#[allow(clippy::multiple_inherent_impl)] -#[getset(get = "pub")] -pub struct AssetDefinitionEntry { - /// Asset definition. - #[cfg_attr(feature = "mutable_api", getset(get_mut = "pub"))] - definition: AssetDefinition, - /// The account that registered this asset. - registered_by: ::Id, +ffi_item! { + /// An entry in [`AssetDefinitionsMap`]. + #[derive( + Debug, + Clone, + PartialEq, + Eq, + Getters, + MutGetters, + Decode, + Encode, + Deserialize, + Serialize, + IntoFfi, + TryFromReprC, + IntoSchema, + )] + #[cfg_attr(all(feature = "ffi_export", not(feature = "ffi_import")), iroha_ffi::ffi_export)] + #[cfg_attr(feature = "ffi_import", iroha_ffi::ffi_import)] + #[allow(clippy::multiple_inherent_impl)] + #[getset(get = "pub")] + pub struct AssetDefinitionEntry { + /// Asset definition. + #[cfg_attr(feature = "mutable_api", getset(get_mut = "pub"))] + definition: AssetDefinition, + /// The account that registered this asset. + registered_by: ::Id, + } } impl PartialOrd for AssetDefinitionEntry { @@ -89,7 +92,11 @@ impl Ord for AssetDefinitionEntry { } } -#[cfg_attr(any(feature = "ffi_api", feature = "ffi"), ffi_export)] +#[cfg_attr( + all(feature = "ffi_export", not(feature = "ffi_import")), + iroha_ffi::ffi_export +)] +#[cfg_attr(feature = "ffi_import", iroha_ffi::ffi_import)] impl AssetDefinitionEntry { /// Constructor. pub const fn new( @@ -114,37 +121,39 @@ impl AssetDefinitionEntry { } } -/// Asset definition defines type of that asset. -#[derive( - Debug, - Display, - Clone, - IdOrdEqHash, - Getters, - MutGetters, - Decode, - Encode, - Deserialize, - Serialize, - IntoFfi, - TryFromReprC, - IntoSchema, -)] -#[display(fmt = "{id} {value_type}{mintable}")] -#[allow(clippy::multiple_inherent_impl)] -#[id(type = "DefinitionId")] -pub struct AssetDefinition { - /// An Identification of the [`AssetDefinition`]. - id: ::Id, - /// Type of [`AssetValue`] - #[getset(get = "pub")] - value_type: AssetValueType, - /// Is the asset mintable - #[getset(get = "pub")] - mintable: Mintable, - /// Metadata of this asset definition as a key-value store. - #[cfg_attr(feature = "mutable_api", getset(get_mut = "pub"))] - metadata: Metadata, +ffi_item! { + /// Asset definition defines type of that asset. + #[derive( + Debug, + Display, + Clone, + IdOrdEqHash, + Getters, + MutGetters, + Decode, + Encode, + Deserialize, + Serialize, + IntoFfi, + TryFromReprC, + IntoSchema, + )] + #[display(fmt = "{id} {value_type}{mintable}")] + #[allow(clippy::multiple_inherent_impl)] + #[id(type = "DefinitionId")] + pub struct AssetDefinition { + /// An Identification of the [`AssetDefinition`]. + id: ::Id, + /// Type of [`AssetValue`] + #[getset(get = "pub")] + value_type: AssetValueType, + /// Is the asset mintable + #[getset(get = "pub")] + mintable: Mintable, + /// Metadata of this asset definition as a key-value store. + #[cfg_attr(feature = "mutable_api", getset(get_mut = "pub"))] + metadata: Metadata, + } } impl HasMetadata for AssetDefinition { @@ -187,32 +196,35 @@ pub enum Mintable { // TODO: Support more variants using bit-compacted tag, and `u32` mintability tokens. } -/// Asset represents some sort of commodity or value. -/// All possible variants of [`Asset`] entity's components. -#[derive( - Debug, - Display, - Clone, - IdOrdEqHash, - Getters, - Decode, - Encode, - Deserialize, - Serialize, - IntoFfi, - TryFromReprC, - IntoSchema, -)] -#[cfg_attr(any(feature = "ffi_api", feature = "ffi"), ffi_export)] -#[display(fmt = "{id}: {value}")] -#[getset(get = "pub")] -#[id(type = "Id")] -pub struct Asset { - /// Component Identification. - #[getset(skip)] - id: ::Id, - /// Asset's Quantity. - value: AssetValue, +ffi_item! { + /// Asset represents some sort of commodity or value. + /// All possible variants of [`Asset`] entity's components. + #[derive( + Debug, + Display, + Clone, + IdOrdEqHash, + Getters, + Decode, + Encode, + Deserialize, + Serialize, + IntoFfi, + TryFromReprC, + IntoSchema, + )] + #[cfg_attr(all(feature = "ffi_export", not(feature = "ffi_import")), iroha_ffi::ffi_export)] + #[cfg_attr(feature = "ffi_import", iroha_ffi::ffi_import)] + #[display(fmt = "{id}: {value}")] + #[getset(get = "pub")] + #[id(type = "Id")] + pub struct Asset { + /// Component Identification. + #[getset(skip)] + id: ::Id, + /// Asset's Quantity. + value: AssetValue, + } } /// Asset's inner value type. @@ -397,28 +409,30 @@ pub struct Id { pub account_id: ::Id, } -/// Builder which can be submitted in a transaction to create a new [`AssetDefinition`] -#[derive( - Debug, - Display, - Clone, - IdOrdEqHash, - Decode, - Encode, - Deserialize, - Serialize, - IntoFfi, - TryFromReprC, - IntoSchema, -)] -#[id(type = "::Id")] -#[display(fmt = "{id} {mintable}{value_type}")] -#[allow(clippy::multiple_inherent_impl)] -pub struct NewAssetDefinition { - id: ::Id, - value_type: AssetValueType, - mintable: Mintable, - metadata: Metadata, +ffi_item! { + /// Builder which can be submitted in a transaction to create a new [`AssetDefinition`] + #[derive( + Debug, + Display, + Clone, + IdOrdEqHash, + Decode, + Encode, + Deserialize, + Serialize, + IntoFfi, + TryFromReprC, + IntoSchema, + )] + #[id(type = "::Id")] + #[display(fmt = "{id} {mintable}{value_type}")] + #[allow(clippy::multiple_inherent_impl)] + pub struct NewAssetDefinition { + id: ::Id, + value_type: AssetValueType, + mintable: Mintable, + metadata: Metadata, + } } #[cfg(feature = "mutable_api")] @@ -461,7 +475,11 @@ impl NewAssetDefinition { } } -#[cfg_attr(any(feature = "ffi_api", feature = "ffi"), ffi_export)] +#[cfg_attr( + all(feature = "ffi_export", not(feature = "ffi_import")), + iroha_ffi::ffi_export +)] +#[cfg_attr(feature = "ffi_import", iroha_ffi::ffi_import)] impl NewAssetDefinition { /// Set mintability to [`Mintable::Once`] #[inline] @@ -480,7 +498,11 @@ impl NewAssetDefinition { } } -#[cfg_attr(any(feature = "ffi_api", feature = "ffi"), ffi_export)] +#[cfg_attr( + all(feature = "ffi_export", not(feature = "ffi_import")), + iroha_ffi::ffi_export +)] +#[cfg_attr(feature = "ffi_import", iroha_ffi::ffi_import)] impl AssetDefinition { /// Construct builder for [`AssetDefinition`] identifiable by [`Id`]. #[must_use] @@ -528,7 +550,11 @@ impl AssetDefinition { } } -#[cfg_attr(any(feature = "ffi_api", feature = "ffi"), ffi_export)] +#[cfg_attr( + all(feature = "ffi_export", not(feature = "ffi_import")), + iroha_ffi::ffi_export +)] +#[cfg_attr(feature = "ffi_import", iroha_ffi::ffi_import)] impl Asset { /// Constructor pub fn new( diff --git a/data_model/src/domain.rs b/data_model/src/domain.rs index 58649e4ebe8..9fae1b2e30f 100644 --- a/data_model/src/domain.rs +++ b/data_model/src/domain.rs @@ -6,15 +6,15 @@ //! the Genesis block. #[cfg(not(feature = "std"))] -use alloc::{boxed::Box, format, string::String, vec::Vec}; +use alloc::{alloc, boxed::Box, format, string::String, vec::Vec}; use core::str::FromStr; +#[cfg(feature = "std")] +use std::alloc::alloc; use derive_more::{Display, FromStr}; use getset::{Getters, MutGetters}; use iroha_crypto::PublicKey; use iroha_data_model_derive::IdOrdEqHash; -#[cfg(any(feature = "ffi_api", feature = "ffi"))] -use iroha_ffi::ffi_export; use iroha_ffi::{IntoFfi, TryFromReprC}; use iroha_primitives::conststr::ConstString; use iroha_schema::IntoSchema; @@ -24,6 +24,7 @@ use serde::{Deserialize, Serialize}; use crate::{ account::{Account, AccountsMap}, asset::AssetDefinitionsMap, + ffi::ffi_item, metadata::Metadata, prelude::{AssetDefinition, AssetDefinitionEntry}, HasMetadata, Identifiable, Name, ParseError, Registered, @@ -72,30 +73,32 @@ impl From for Domain { } } -/// Builder which can be submitted in a transaction to create a new [`Domain`] -#[derive( - Debug, - Display, - Clone, - IdOrdEqHash, - Decode, - Encode, - Deserialize, - Serialize, - IntoFfi, - TryFromReprC, - IntoSchema, -)] -#[id(type = "::Id")] -#[allow(clippy::multiple_inherent_impl)] -#[display(fmt = "[{id}]")] -pub struct NewDomain { - /// The identification associated with the domain builder. - id: ::Id, - /// The (IPFS) link to the logo of this domain. - logo: Option, - /// Metadata associated with the domain builder. - metadata: Metadata, +ffi_item! { + /// Builder which can be submitted in a transaction to create a new [`Domain`] + #[derive( + Debug, + Display, + Clone, + IdOrdEqHash, + Decode, + Encode, + Deserialize, + Serialize, + IntoFfi, + TryFromReprC, + IntoSchema, + )] + #[id(type = "::Id")] + #[allow(clippy::multiple_inherent_impl)] + #[display(fmt = "[{id}]")] + pub struct NewDomain { + /// The identification associated with the domain builder. + id: ::Id, + /// The (IPFS) link to the logo of this domain. + logo: Option, + /// Metadata associated with the domain builder. + metadata: Metadata, + } } #[cfg(feature = "mutable_api")] @@ -139,7 +142,11 @@ impl NewDomain { } } -#[cfg_attr(any(feature = "ffi_api", feature = "ffi"), ffi_export)] +#[cfg_attr( + all(feature = "ffi_export", not(feature = "ffi_import")), + iroha_ffi::ffi_export +)] +#[cfg_attr(feature = "ffi_import", iroha_ffi::ffi_import)] impl NewDomain { /// Add [`logo`](IpfsPath) to the domain replacing previously defined value #[must_use] @@ -156,41 +163,44 @@ impl NewDomain { } } -/// Named group of [`Account`] and [`Asset`](`crate::asset::Asset`) entities. -#[derive( - Debug, - Display, - Clone, - IdOrdEqHash, - Getters, - MutGetters, - Decode, - Encode, - Deserialize, - Serialize, - IntoFfi, - TryFromReprC, - IntoSchema, -)] -#[cfg_attr(any(feature = "ffi_api", feature = "ffi"), ffi_export)] -#[allow(clippy::multiple_inherent_impl)] -#[display(fmt = "[{id}]")] -#[id(type = "Id")] -pub struct Domain { - /// Identification of this [`Domain`]. - id: ::Id, - /// [`Account`]s of the domain. - accounts: AccountsMap, - /// [`Asset`](AssetDefinition)s defined of the `Domain`. - asset_definitions: AssetDefinitionsMap, - /// IPFS link to the `Domain` logo - // FIXME: Getter implemented manually because `getset` - // returns &Option when it should return Option<&T> - logo: Option, - /// [`Metadata`] of this `Domain` as a key-value store. - #[getset(get = "pub")] - #[cfg_attr(feature = "mutable_api", getset(get_mut = "pub"))] - metadata: Metadata, +ffi_item! { + /// Named group of [`Account`] and [`Asset`](`crate::asset::Asset`) entities. + #[derive( + Debug, + Display, + Clone, + IdOrdEqHash, + Getters, + MutGetters, + Decode, + Encode, + Deserialize, + Serialize, + IntoFfi, + TryFromReprC, + IntoSchema, + )] + #[cfg_attr(all(feature = "ffi_export", not(feature = "ffi_import")), iroha_ffi::ffi_export)] + #[cfg_attr(feature = "ffi_import", iroha_ffi::ffi_import)] + #[allow(clippy::multiple_inherent_impl)] + #[display(fmt = "[{id}]")] + #[id(type = "Id")] + pub struct Domain { + /// Identification of this [`Domain`]. + id: ::Id, + /// [`Account`]s of the domain. + accounts: AccountsMap, + /// [`Asset`](AssetDefinition)s defined of the `Domain`. + asset_definitions: AssetDefinitionsMap, + /// IPFS link to the `Domain` logo + // FIXME: Getter implemented manually because `getset` + // returns &Option when it should return Option<&T> + logo: Option, + /// [`Metadata`] of this `Domain` as a key-value store. + #[getset(get = "pub")] + #[cfg_attr(feature = "mutable_api", getset(get_mut = "pub"))] + metadata: Metadata, + } } impl HasMetadata for Domain { @@ -204,7 +214,11 @@ impl Registered for Domain { type With = NewDomain; } -#[cfg_attr(any(feature = "ffi_api", feature = "ffi"), ffi_export)] +#[cfg_attr( + all(feature = "ffi_export", not(feature = "ffi_import")), + iroha_ffi::ffi_export +)] +#[cfg_attr(feature = "ffi_import", iroha_ffi::ffi_import)] impl Domain { /// Construct builder for [`Domain`] identifiable by [`Id`]. pub fn new(id: ::Id) -> ::With { diff --git a/data_model/src/lib.rs b/data_model/src/lib.rs index adaea657c7b..52b00230df1 100644 --- a/data_model/src/lib.rs +++ b/data_model/src/lib.rs @@ -16,6 +16,8 @@ use alloc::{ vec::Vec, }; use core::{convert::AsRef, fmt, fmt::Debug, ops::RangeInclusive}; +#[cfg(feature = "std")] +use std::alloc::alloc; use block_value::{BlockHeaderValue, BlockValue}; #[cfg(not(target_arch = "aarch64"))] @@ -25,7 +27,10 @@ use events::FilterBox; use iroha_crypto::{Hash, PublicKey}; use iroha_ffi::{IntoFfi, TryFromReprC}; use iroha_macro::{error::ErrorTryFromEnum, FromVariant}; -use iroha_primitives::{fixed, small}; +use iroha_primitives::{ + fixed, + small::{Array as SmallArray, SmallVec}, +}; use iroha_schema::{IntoSchema, MetaMap}; use parity_scale_codec::{Decode, Encode}; use prelude::TransactionQueryResult; @@ -590,11 +595,11 @@ impl From for Value { } } -impl From> for Value +impl From> for Value where A::Item: Into, { - fn from(sv: small::SmallVec) -> Self { + fn from(sv: SmallVec) -> Self { // This looks inefficient, but `Value` can only hold a // heap-allocated `Vec` (it's recursive) and the vector // conversions only do a heap allocation (if that). @@ -811,7 +816,7 @@ impl TryFrom for BlockValue { } } -impl TryFrom for small::SmallVec +impl TryFrom for SmallVec where Value: TryInto, { @@ -822,7 +827,7 @@ where return vec .into_iter() .map(TryInto::try_into) - .collect::, _>>() + .collect::, _>>() .map_err(|_e| Self::Error::default()); } Err(Self::Error::default()) @@ -909,47 +914,63 @@ pub fn current_time() -> core::time::Duration { .expect("Failed to get the current system time") } -#[cfg(any(feature = "ffi_api", feature = "ffi"))] -mod ffi { +pub mod ffi { + //! Definitions and implementations of FFI related functionalities + use super::*; - iroha_ffi::handles! { - account::Account, - asset::Asset, - domain::Domain, - metadata::Metadata, - permissions::PermissionToken, - role::Role, - Name, - } + macro_rules! ffi_item { + ($it: item) => { + #[cfg(not(feature = "ffi_import"))] + $it - iroha_ffi::ffi_fn! { pub Clone: - account::Account, - asset::Asset, - domain::Domain, - metadata::Metadata, - permissions::PermissionToken, - role::Role, - Name, - } - iroha_ffi::ffi_fn! { pub Eq: - account::Account, - asset::Asset, - domain::Domain, - metadata::Metadata, - permissions::PermissionToken, - role::Role, - Name, + #[cfg(feature = "ffi_import")] + iroha_ffi::ffi! { $it } + }; } - iroha_ffi::ffi_fn! { pub Ord: - account::Account, - asset::Asset, - domain::Domain, - permissions::PermissionToken, - role::Role, - Name, + + #[cfg(any(feature = "ffi_export", feature = "ffi_import"))] + macro_rules! ffi_fn { + ($macro_name: ident) => { + iroha_ffi::$macro_name! { pub Clone: + account::Account, + asset::Asset, + domain::Domain, + metadata::Metadata, + permissions::PermissionToken, + role::Role, + Name, + } + iroha_ffi::$macro_name! { pub Eq: + account::Account, + asset::Asset, + domain::Domain, + metadata::Metadata, + permissions::PermissionToken, + role::Role, + Name, + } + iroha_ffi::$macro_name! { pub Ord: + account::Account, + asset::Asset, + domain::Domain, + permissions::PermissionToken, + role::Role, + Name, + } + iroha_ffi::$macro_name! { pub Drop: + account::Account, + asset::Asset, + domain::Domain, + metadata::Metadata, + permissions::PermissionToken, + role::Role, + Name, + } + }; } - iroha_ffi::ffi_fn! { pub Drop: + + iroha_ffi::handles! { account::Account, asset::Asset, domain::Domain, @@ -958,6 +979,13 @@ mod ffi { role::Role, Name, } + + #[cfg(feature = "ffi_import")] + ffi_fn! {decl_ffi_fn} + #[cfg(all(feature = "ffi_export", not(feature = "ffi_import")))] + ffi_fn! {def_ffi_fn} + + pub(crate) use ffi_item; } pub mod prelude { diff --git a/data_model/src/metadata.rs b/data_model/src/metadata.rs index ab270e18946..751e7309222 100644 --- a/data_model/src/metadata.rs +++ b/data_model/src/metadata.rs @@ -2,10 +2,10 @@ //! transactions and assets. #[cfg(not(feature = "std"))] -use alloc::{boxed::Box, collections::btree_map, format, string::String, vec::Vec}; +use alloc::{alloc, boxed::Box, collections::btree_map, format, string::String, vec::Vec}; use core::borrow::Borrow; #[cfg(feature = "std")] -use std::collections::btree_map; +use std::{alloc::alloc, collections::btree_map}; use derive_more::Display; use iroha_ffi::{IntoFfi, TryFromReprC}; diff --git a/data_model/src/name.rs b/data_model/src/name.rs index 15a5548edf0..ee6ed95812a 100644 --- a/data_model/src/name.rs +++ b/data_model/src/name.rs @@ -1,8 +1,10 @@ //! This module contains [`Name`](`crate::name::Name`) structure //! and related implementations and trait implementations. #[cfg(not(feature = "std"))] -use alloc::{boxed::Box, format, string::String, vec::Vec}; +use alloc::{alloc, boxed::Box, format, string::String, vec::Vec}; use core::{ops::RangeInclusive, str::FromStr}; +#[cfg(feature = "std")] +use std::alloc::alloc; use derive_more::{DebugCustom, Display}; use iroha_ffi::{IntoFfi, TryFromReprC}; @@ -100,9 +102,9 @@ impl FromStr for Name { /// /// All of the given pointers must be valid #[no_mangle] -#[cfg(feature = "ffi_api")] #[allow(non_snake_case, unsafe_code)] -unsafe extern "C" fn Name__from_str<'itm>( +#[cfg(all(feature = "ffi_export", not(feature = "ffi_import")))] +pub unsafe extern "C" fn Name__from_str<'itm>( candidate: <&'itm str as iroha_ffi::TryFromReprC<'itm>>::Source, out_ptr: <::Target as iroha_ffi::Output>::OutPtr, ) -> iroha_ffi::FfiReturn { @@ -195,7 +197,7 @@ mod tests { #[test] #[allow(unsafe_code)] - #[cfg(feature = "ffi_api")] + #[cfg(all(feature = "ffi_export", not(feature = "ffi_import")))] fn ffi_name_from_str() -> Result<(), ParseError> { use iroha_ffi::Handle; let candidate = "Name"; diff --git a/data_model/src/permissions.rs b/data_model/src/permissions.rs index 615dfd55fb7..6533414f7f4 100644 --- a/data_model/src/permissions.rs +++ b/data_model/src/permissions.rs @@ -2,6 +2,7 @@ #[cfg(not(feature = "std"))] use alloc::{ + alloc, boxed::Box, collections::{btree_map, btree_set}, format, @@ -9,46 +10,50 @@ use alloc::{ vec::Vec, }; #[cfg(feature = "std")] -use std::collections::{btree_map, btree_set}; +use std::{ + alloc::alloc, + collections::{btree_map, btree_set}, +}; use getset::Getters; -#[cfg(any(feature = "ffi_api", feature = "ffi"))] -use iroha_ffi::ffi_export; use iroha_ffi::{IntoFfi, TryFromReprC}; use iroha_schema::IntoSchema; use parity_scale_codec::{Decode, Encode}; use serde::{Deserialize, Serialize}; -use crate::{utils::format_comma_separated, Name, Value}; +use crate::{ffi::ffi_item, utils::format_comma_separated, Name, Value}; /// Collection of [`PermissionToken`]s pub type Permissions = btree_set::BTreeSet; -/// Stored proof of the account having a permission for a certain action. -#[derive( - Debug, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - Getters, - Decode, - Encode, - Deserialize, - Serialize, - IntoFfi, - TryFromReprC, - IntoSchema, -)] -#[cfg_attr(any(feature = "ffi_api", feature = "ffi"), ffi_export)] -#[getset(get = "pub")] -pub struct PermissionToken { - /// Name of the permission rule given to account. - name: Name, - /// Params identifying how this rule applies. - #[getset(skip)] - params: btree_map::BTreeMap, +ffi_item! { + /// Stored proof of the account having a permission for a certain action. + #[derive( + Debug, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Getters, + Decode, + Encode, + Deserialize, + Serialize, + IntoFfi, + TryFromReprC, + IntoSchema, + )] + #[cfg_attr(all(feature = "ffi_export", not(feature = "ffi_import")), iroha_ffi::ffi_export)] + #[cfg_attr(feature = "ffi_import", iroha_ffi::ffi_import)] + #[getset(get = "pub")] + pub struct PermissionToken { + /// Name of the permission rule given to account. + name: Name, + /// Params identifying how this rule applies. + #[getset(skip)] + params: btree_map::BTreeMap, + } } impl core::fmt::Display for PermissionToken { diff --git a/data_model/src/role.rs b/data_model/src/role.rs index f6f9bdcc3bf..e4bbd52cac6 100644 --- a/data_model/src/role.rs +++ b/data_model/src/role.rs @@ -1,21 +1,20 @@ //! Structures, traits and impls related to `Role`s. #[cfg(not(feature = "std"))] -use alloc::{boxed::Box, collections::btree_set, format, string::String, vec::Vec}; +use alloc::{alloc, boxed::Box, collections::btree_set, format, string::String, vec::Vec}; #[cfg(feature = "std")] -use std::collections::btree_set; +use std::{alloc::alloc, collections::btree_set}; use derive_more::{Constructor, Display, FromStr}; use getset::Getters; use iroha_data_model_derive::IdOrdEqHash; -#[cfg(any(feature = "ffi_api", feature = "ffi"))] -use iroha_ffi::ffi_export; use iroha_ffi::{IntoFfi, TryFromReprC}; use iroha_schema::IntoSchema; use parity_scale_codec::{Decode, Encode}; use serde::{Deserialize, Serialize}; use crate::{ + ffi::ffi_item, permissions::{PermissionToken, Permissions}, Identifiable, Name, Registered, }; @@ -48,35 +47,42 @@ pub struct Id { pub name: Name, } -/// Role is a tag for a set of permission tokens. -#[derive( - Debug, - Display, - Clone, - IdOrdEqHash, - Getters, - Decode, - Encode, - Deserialize, - Serialize, - IntoFfi, - TryFromReprC, - IntoSchema, -)] -#[cfg_attr(any(feature = "ffi_api", feature = "ffi"), ffi_export)] -#[display(fmt = "{id}")] -#[getset(get = "pub")] -#[id(type = "Id")] -pub struct Role { - /// Unique name of the role. - #[getset(skip)] - id: ::Id, - /// Permission tokens. - #[getset(skip)] - permissions: Permissions, +ffi_item! { + /// Role is a tag for a set of permission tokens. + #[derive( + Debug, + Display, + Clone, + IdOrdEqHash, + Getters, + Decode, + Encode, + Deserialize, + Serialize, + IntoFfi, + TryFromReprC, + IntoSchema, + )] + #[cfg_attr(all(feature = "ffi_export", not(feature = "ffi_import")), iroha_ffi::ffi_export)] + #[cfg_attr(feature = "ffi_import", iroha_ffi::ffi_import)] + #[display(fmt = "{id}")] + #[getset(get = "pub")] + #[id(type = "Id")] + pub struct Role { + /// Unique name of the role. + #[getset(skip)] + id: ::Id, + /// Permission tokens. + #[getset(skip)] + permissions: Permissions, + } } -#[cfg_attr(any(feature = "ffi_api", feature = "ffi"), ffi_export)] +#[cfg_attr( + all(feature = "ffi_export", not(feature = "ffi_import")), + iroha_ffi::ffi_export +)] +#[cfg_attr(feature = "ffi_import", iroha_ffi::ffi_import)] impl Role { /// Constructor. #[inline] @@ -127,7 +133,11 @@ impl crate::Registrable for NewRole { } } -#[cfg_attr(any(feature = "ffi_api", feature = "ffi"), ffi_export)] +#[cfg_attr( + all(feature = "ffi_export", not(feature = "ffi_import")), + iroha_ffi::ffi_export +)] +#[cfg_attr(feature = "ffi_import", iroha_ffi::ffi_import)] impl NewRole { /// Add permission to the [`Role`] #[must_use] diff --git a/ffi/Cargo.toml b/ffi/Cargo.toml index 016d7106647..f55c7ed380c 100644 --- a/ffi/Cargo.toml +++ b/ffi/Cargo.toml @@ -9,6 +9,3 @@ iroha_ffi_derive = { version = "=2.0.0-pre-rc.7", path = "derive" } [dev-dependencies] getset = "0.1.2" - -[features] -#client = ["iroha_ffi_derive/client"] diff --git a/ffi/derive/Cargo.toml b/ffi/derive/Cargo.toml index 28aacfdc62a..088333627de 100644 --- a/ffi/derive/Cargo.toml +++ b/ffi/derive/Cargo.toml @@ -7,9 +7,6 @@ edition = "2021" [lib] proc-macro = true -[features] -#client = [] - [dependencies] syn = { version = "1.0", features = ["full", "visit", "visit-mut", "extra-traits"] } quote = "1.0" diff --git a/ffi/derive/src/convert.rs b/ffi/derive/src/convert.rs index 0d4e86671c7..c8fd79f7c1e 100644 --- a/ffi/derive/src/convert.rs +++ b/ffi/derive/src/convert.rs @@ -5,9 +5,16 @@ use syn::{parse_quote, DeriveInput, Ident}; use crate::{enum_size, find_attr, is_fieldless_enum, is_opaque}; -pub fn derive_try_from_ffi(input: &DeriveInput) -> TokenStream2 { +pub fn derive_try_from_repr_c(input: &DeriveInput) -> TokenStream2 { + if !matches!(input.vis, syn::Visibility::Public(_)) { + abort!(input.vis, "Only public items are supported"); + } + if !input.generics.params.is_empty() { + abort!(input.generics, "Generics are not supported"); + } + if is_opaque(input) { - return derive_try_from_ffi_for_opaque_item(&input.ident); + return derive_try_from_repr_c_for_opaque_item(input); } match &input.data { @@ -15,19 +22,26 @@ pub fn derive_try_from_ffi(input: &DeriveInput) -> TokenStream2 { let repr = find_attr(&input.attrs, "repr"); if is_fieldless_enum(&input.ident, item, &repr) { - derive_try_from_ffi_for_fieldless_enum(&input.ident, item, &repr) + derive_try_from_repr_c_for_fieldless_enum(&input.ident, item, &repr) } else { - derive_try_from_ffi_for_item(&input.ident) + derive_try_from_repr_c_for_item(&input.ident) } } - syn::Data::Struct(_) => derive_try_from_ffi_for_item(&input.ident), + syn::Data::Struct(_) => derive_try_from_repr_c_for_item(&input.ident), syn::Data::Union(item) => abort!(item.union_token, "Unions are not supported"), } } pub fn derive_into_ffi(input: &DeriveInput) -> TokenStream2 { + if !matches!(input.vis, syn::Visibility::Public(_)) { + abort!(input.vis, "Only public items are supported"); + } + if !input.generics.params.is_empty() { + abort!(input.generics, "Generics are not supported"); + } + if is_opaque(input) { - return derive_into_ffi_for_opaque_item(&input.ident); + return derive_into_ffi_for_opaque_item(input); } match &input.data { @@ -63,41 +77,44 @@ fn variant_discriminants(enum_: &syn::DataEnum) -> Vec { }) } -fn derive_try_from_ffi_for_opaque_item(name: &Ident) -> TokenStream2 { - #[cfg(not(feature = "client"))] - let owned_try_from_repr_c = 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); +fn derive_try_from_repr_c_for_opaque_item(input: &DeriveInput) -> TokenStream2 { + let name = &input.ident; + + let owned_try_from_repr_c = if is_opaque_wrapper(input) { + 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); + } + + // TODO: Casting from non opaque to opaque. + Ok(Self{__opaque_ptr: source.cast()}) } - - Ok(*Box::from_raw(source)) } } - }; - #[cfg(feature = "client")] - let owned_try_from_repr_c = 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); + } else { + 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)) } - - // TODO: Casting from non opaque to opaque. - Ok(Self(source.cast())) } } }; @@ -148,13 +165,13 @@ fn derive_try_from_ffi_for_opaque_item(name: &Ident) -> TokenStream2 { } } -fn derive_try_from_ffi_for_item(_: &Ident) -> TokenStream2 { +fn derive_try_from_repr_c_for_item(_: &Ident) -> TokenStream2 { quote! { // TODO: } } -fn derive_try_from_ffi_for_fieldless_enum( +fn derive_try_from_repr_c_for_fieldless_enum( enum_name: &Ident, enum_: &syn::DataEnum, repr: &[syn::NestedMeta], @@ -256,25 +273,34 @@ fn derive_try_from_ffi_for_fieldless_enum( } } -fn derive_into_ffi_for_opaque_item(name: &Ident) -> TokenStream2 { - #[cfg(not(feature = "client"))] - let owned_into_ffi = quote! { - impl iroha_ffi::IntoFfi for #name { - type Target = *mut Self; +fn derive_into_ffi_for_opaque_item(input: &DeriveInput) -> TokenStream2 { + let name = &input.ident; - fn into_ffi(self) -> Self::Target { - Box::into_raw(Box::new(self)) + let owned_into_ffi = if is_opaque_wrapper(input) { + quote! { + impl iroha_ffi::IntoFfi for #name { + type Target = *mut Self; + + fn into_ffi(self) -> Self::Target { + // TODO: Casting from non opaque to opaque. + (*core::mem::ManuallyDrop::new(self)).__opaque_ptr + } } } - }; - #[cfg(feature = "client")] - let owned_into_ffi = quote! { - impl iroha_ffi::IntoFfi for #name { - type Target = *mut Self; - - fn into_ffi(self) -> Self::Target { - // TODO: Casting from non opaque to opaque. - (*core::mem::ManuallyDrop::new(self)).ptr + } else { + quote! { + impl iroha_ffi::IntoFfi for #name { + type Target = *mut Self; + + fn into_ffi(self) -> Self::Target { + let layout = core::alloc::Layout::for_value(&self); + + unsafe { + let ptr: Self::Target = alloc(layout).cast(); + ptr.write(self); + ptr + } + } } } }; @@ -343,3 +369,7 @@ fn derive_into_ffi_for_fieldless_enum(enum_name: &Ident, repr: &[syn::NestedMeta } } } + +fn is_opaque_wrapper(input: &DeriveInput) -> bool { + !crate::find_attr(&input.attrs, "opaque_wrapper").is_empty() +} diff --git a/ffi/derive/src/ffi_fn.rs b/ffi/derive/src/ffi_fn.rs index 943986d9d48..ad7a36db10a 100644 --- a/ffi/derive/src/ffi_fn.rs +++ b/ffi/derive/src/ffi_fn.rs @@ -4,24 +4,16 @@ use syn::Ident; use crate::impl_visitor::{ffi_output_arg, Arg, FnDescriptor}; -pub fn generate(fn_descriptor: &FnDescriptor) -> TokenStream { +pub fn gen_declaration(fn_descriptor: &FnDescriptor) -> TokenStream { let ffi_fn_name = gen_fn_name(fn_descriptor.self_ty_name(), &fn_descriptor.sig.ident); - #[cfg(not(feature = "client"))] - return crate::ffi_fn::gen_fn_definition(&ffi_fn_name, fn_descriptor); - #[cfg(feature = "client")] - return crate::ffi_fn::gen_fn_declaration(&ffi_fn_name, fn_descriptor); -} - -#[cfg(feature = "client")] -fn gen_fn_declaration(ffi_fn_name: &Ident, fn_descriptor: &FnDescriptor) -> TokenStream { let self_ty = fn_descriptor .self_ty .as_ref() .and_then(syn::Path::get_ident); let ffi_fn_doc = gen_doc(self_ty, &fn_descriptor.sig.ident); - let fn_signature = gen_signature(ffi_fn_name, fn_descriptor); + let fn_signature = gen_signature(&ffi_fn_name, fn_descriptor); quote! { extern { @@ -31,21 +23,22 @@ fn gen_fn_declaration(ffi_fn_name: &Ident, fn_descriptor: &FnDescriptor) -> Toke } } -#[cfg(not(feature = "client"))] -fn gen_fn_definition(ffi_fn_name: &Ident, fn_descriptor: &FnDescriptor) -> TokenStream { +pub fn gen_definition(fn_descriptor: &FnDescriptor) -> TokenStream { + let ffi_fn_name = gen_fn_name(fn_descriptor.self_ty_name(), &fn_descriptor.sig.ident); + let self_ty = fn_descriptor .self_ty .as_ref() .and_then(syn::Path::get_ident); let ffi_fn_doc = gen_doc(self_ty, &fn_descriptor.sig.ident); - let fn_signature = gen_signature(ffi_fn_name, fn_descriptor); + let fn_signature = gen_signature(&ffi_fn_name, fn_descriptor); let ffi_fn_body = gen_body(fn_descriptor); quote! { #[no_mangle] #[doc = #ffi_fn_doc] - unsafe extern "C" #fn_signature { + pub unsafe extern "C" #fn_signature { #[allow(clippy::shadow_unrelated)] let res = std::panic::catch_unwind(|| { let fn_body = || #ffi_fn_body; @@ -109,14 +102,13 @@ fn gen_signature(ffi_fn_name: &Ident, fn_descriptor: &FnDescriptor) -> TokenStre } } -fn gen_input_arg(arg: &impl Arg) -> TokenStream { +fn gen_input_arg(arg: &Arg) -> TokenStream { let arg_name = arg.name(); - let arg_type = arg.ffi_type_resolved(); + let arg_type = arg.ffi_type_resolved(false); quote! { #arg_name: #arg_type } } -#[cfg(not(feature = "client"))] fn gen_body(fn_descriptor: &FnDescriptor) -> syn::Block { let input_conversions = gen_input_conversion_stmts(fn_descriptor); let method_call_stmt = gen_method_call_stmt(fn_descriptor); @@ -131,14 +123,13 @@ fn gen_body(fn_descriptor: &FnDescriptor) -> syn::Block { }} } -fn gen_out_ptr_arg(arg: &impl Arg) -> TokenStream { +fn gen_out_ptr_arg(arg: &Arg) -> TokenStream { let arg_name = arg.name(); - let arg_type = arg.ffi_type_resolved(); + let arg_type = arg.ffi_type_resolved(true); quote! { #arg_name: <#arg_type as iroha_ffi::Output>::OutPtr } } -#[cfg(not(feature = "client"))] fn gen_input_conversion_stmts(fn_descriptor: &FnDescriptor) -> TokenStream { let mut stmts = quote! {}; @@ -159,7 +150,6 @@ fn gen_input_conversion_stmts(fn_descriptor: &FnDescriptor) -> TokenStream { stmts } -#[cfg(not(feature = "client"))] fn gen_method_call_stmt(fn_descriptor: &FnDescriptor) -> TokenStream { let ident = &fn_descriptor.sig.ident; let self_type = &fn_descriptor.self_ty; @@ -192,7 +182,6 @@ fn gen_method_call_stmt(fn_descriptor: &FnDescriptor) -> TokenStream { ) } -#[cfg(not(feature = "client"))] fn gen_output_assignment_stmts(fn_descriptor: &FnDescriptor) -> TokenStream { if let Some(output_arg) = &fn_descriptor.output_arg { if let Some(receiver) = &fn_descriptor.receiver { @@ -210,7 +199,7 @@ fn gen_output_assignment_stmts(fn_descriptor: &FnDescriptor) -> TokenStream { } } - let (arg_name, arg_type) = (output_arg.name(), output_arg.ffi_type_resolved()); + let (arg_name, arg_type) = (output_arg.name(), output_arg.ffi_type_resolved(true)); let output_arg_conversion = crate::util::gen_arg_src_to_ffi(output_arg, true); return quote! { diff --git a/ffi/derive/src/impl_visitor.rs b/ffi/derive/src/impl_visitor.rs index 420c5e8413e..d253a7a78fd 100644 --- a/ffi/derive/src/impl_visitor.rs +++ b/ffi/derive/src/impl_visitor.rs @@ -3,27 +3,8 @@ use proc_macro2::Span; use proc_macro_error::{abort, OptionExt}; use syn::{parse_quote, visit::Visit, visit_mut::VisitMut, Ident, Type}; -pub trait Arg { - fn name(&self) -> &Ident; - fn src_type(&self) -> &Type; - fn src_type_resolved(&self) -> Type; - fn ffi_type_resolved(&self) -> Type; -} - #[derive(Constructor)] -pub struct Receiver { - self_ty: Option, - name: Ident, - type_: Type, -} - -pub struct InputArg { - self_ty: Option, - name: Ident, - type_: Type, -} - -pub struct ReturnArg { +pub struct Arg { self_ty: Option, name: Ident, type_: Type, @@ -36,107 +17,54 @@ pub struct ImplDescriptor<'ast> { pub fns: Vec, } -impl InputArg { - pub fn new(self_ty: Option, name: Ident, type_: Type) -> Self { - Self { - self_ty, - name, - type_, - } - } -} - -impl ReturnArg { - pub fn new(self_ty: Option, name: Ident, type_: Type) -> Self { - Self { - self_ty, - name, - type_, - } - } -} - -impl Arg for Receiver { - fn name(&self) -> &Ident { +impl Arg { + pub fn name(&self) -> &Ident { &self.name } - fn src_type(&self) -> &Type { + pub fn src_type(&self) -> &Type { &self.type_ } - fn src_type_resolved(&self) -> Type { + pub fn src_type_resolved(&self) -> Type { resolve_src_type(self.self_ty.as_ref(), self.type_.clone()) } - fn ffi_type_resolved(&self) -> Type { - resolve_ffi_type(self.self_ty.as_ref(), self.type_.clone(), false) - } -} + pub fn ffi_type_resolved(&self, is_output: bool) -> Type { + let mut arg_type = self.type_.clone(); -impl Arg for InputArg { - fn name(&self) -> &Ident { - &self.name - } - fn src_type(&self) -> &Type { - &self.type_ - } - fn src_type_resolved(&self) -> Type { - resolve_src_type(self.self_ty.as_ref(), self.type_.clone()) - } - fn ffi_type_resolved(&self) -> Type { - resolve_ffi_type(self.self_ty.as_ref(), self.type_.clone(), false) - } -} + ImplTraitResolver.visit_type_mut(&mut arg_type); + if let Some(self_ty) = self.self_ty.as_ref() { + SelfResolver::new(self_ty).visit_type_mut(&mut arg_type); + } -impl Arg for ReturnArg { - fn name(&self) -> &Ident { - &self.name - } - fn src_type(&self) -> &Type { - &self.type_ - } - fn src_type_resolved(&self) -> Type { - resolve_src_type(self.self_ty.as_ref(), self.type_.clone()) - } - fn ffi_type_resolved(&self) -> Type { - resolve_ffi_type(self.self_ty.as_ref(), self.type_.clone(), true) - } -} + if is_output { + if let Some(result_type) = unwrap_result_type(&arg_type) { + return parse_quote! {<#result_type as iroha_ffi::IntoFfi>::Target}; + } -fn resolve_src_type(self_type: Option<&syn::Path>, mut arg_type: Type) -> Type { - ImplTraitResolver.visit_type_mut(&mut arg_type); + return parse_quote! {<#arg_type as iroha_ffi::IntoFfi>::Target}; + } - if let Some(self_ty) = self_type { - SelfResolver::new(self_ty).visit_type_mut(&mut arg_type); - } + if let Type::Reference(ref_type) = &arg_type { + let elem = &ref_type.elem; - arg_type + return if ref_type.mutability.is_some() { + parse_quote! {<&'itm mut #elem as iroha_ffi::TryFromReprC<'itm>>::Source} + } else { + parse_quote! {<&'itm #elem as iroha_ffi::TryFromReprC<'itm>>::Source} + }; + } + + parse_quote! {<#arg_type as iroha_ffi::TryFromReprC<'itm>>::Source} + } } -fn resolve_ffi_type(self_type: Option<&syn::Path>, mut arg_type: Type, is_output: bool) -> Type { +fn resolve_src_type(self_type: Option<&syn::Path>, mut arg_type: Type) -> Type { ImplTraitResolver.visit_type_mut(&mut arg_type); if let Some(self_ty) = self_type { SelfResolver::new(self_ty).visit_type_mut(&mut arg_type); } - if is_output { - if let Some(result_type) = unwrap_result_type(&arg_type) { - return parse_quote! {<#result_type as iroha_ffi::IntoFfi>::Target}; - } - - return parse_quote! {<#arg_type as iroha_ffi::IntoFfi>::Target}; - } - - if let Type::Reference(ref_type) = &arg_type { - let elem = &ref_type.elem; - - return if ref_type.mutability.is_some() { - parse_quote! {<&'itm mut #elem as iroha_ffi::TryFromReprC<'itm>>::Source} - } else { - parse_quote! {<&'itm #elem as iroha_ffi::TryFromReprC<'itm>>::Source} - }; - } - - parse_quote! {<#arg_type as iroha_ffi::TryFromReprC<'itm>>::Source} + arg_type } pub struct FnDescriptor { @@ -149,11 +77,11 @@ pub struct FnDescriptor { pub sig: syn::Signature, /// Receiver argument, i.e. `self` - pub receiver: Option, + pub receiver: Option, /// Input fn arguments - pub input_args: Vec, + pub input_args: Vec, /// Output fn argument - pub output_arg: Option, + pub output_arg: Option, } struct ImplVisitor<'ast> { @@ -176,11 +104,11 @@ struct FnVisitor<'ast> { sig: Option<&'ast syn::Signature>, /// Receiver argument, i.e. `self` - receiver: Option, + receiver: Option, /// Input fn arguments - input_args: Vec, + input_args: Vec, /// Output fn argument - output_arg: Option, + output_arg: Option, /// Name of the argument being visited curr_arg_name: Option<&'ast Ident>, @@ -277,7 +205,7 @@ impl<'ast> FnVisitor<'ast> { fn add_input_arg(&mut self, src_type: &'ast Type) { let arg_name = self.curr_arg_name.take().expect_or_abort("Defined").clone(); - self.input_args.push(InputArg::new( + self.input_args.push(Arg::new( self.self_ty.map(Clone::clone), arg_name, src_type.clone(), @@ -296,7 +224,7 @@ impl<'ast> FnVisitor<'ast> { // NOTE: `Self` is first consumed and then returned in the same method let name = core::mem::replace(&mut receiver.name, parse_quote! {irrelevant}); - *receiver = Receiver::new( + *receiver = Arg::new( self.self_ty.map(Clone::clone), name, parse_quote! {#self_src_ty}, @@ -314,7 +242,7 @@ impl<'ast> FnVisitor<'ast> { assert!(self.curr_arg_name.is_none()); assert!(self.output_arg.is_none()); - self.output_arg = Some(ReturnArg::new( + self.output_arg = Some(Arg::new( self.self_ty.map(Clone::clone), self.gen_output_arg_name(src_type), src_type.clone(), @@ -424,7 +352,7 @@ impl<'ast> Visit<'ast> for FnVisitor<'ast> { ); let handle_name = Ident::new("__handle", Span::call_site()); - self.receiver = Some(Receiver::new( + self.receiver = Some(Arg::new( self.self_ty.map(Clone::clone), handle_name, src_type, @@ -583,7 +511,7 @@ pub fn unwrap_result_type(node: &Type) -> Option<&Type> { None } -pub fn ffi_output_arg(fn_descriptor: &FnDescriptor) -> Option<&crate::impl_visitor::ReturnArg> { +pub fn ffi_output_arg(fn_descriptor: &FnDescriptor) -> Option<&Arg> { fn_descriptor.output_arg.as_ref().and_then(|output_arg| { if let Some(receiver) = &fn_descriptor.receiver { if receiver.name() == output_arg.name() { diff --git a/ffi/derive/src/lib.rs b/ffi/derive/src/lib.rs index 6dfdd9c2d69..f3b431d3312 100644 --- a/ffi/derive/src/lib.rs +++ b/ffi/derive/src/lib.rs @@ -7,14 +7,14 @@ use proc_macro_error::abort; use quote::quote; use syn::{parse_macro_input, Item, NestedMeta}; -use crate::convert::{derive_into_ffi, derive_try_from_ffi}; +use crate::convert::{derive_into_ffi, derive_try_from_repr_c}; mod convert; mod ffi_fn; mod impl_visitor; mod util; -#[cfg(feature = "client")] -mod wrapper; +// TODO: Should be enabled in https://github.com/hyperledger/iroha/issues/2231 +//mod wrapper; struct FfiItems(Vec); @@ -37,64 +37,52 @@ impl quote::ToTokens for FfiItems { } } -/// Derive implementations of traits required to convert to and from an FFI-compatible type +/// Replace struct/enum definition with opaque pointer. This applies to structs/enums that +/// are converted to an opaque pointer when sent across FFI but does not affect any other +/// item wrapped with this macro (e.g. fieldless enums). This is so that most of the time +/// users can safely wrap all of their structs with this macro and not be concerned with the +/// cognitive load of figuring out which structs are converted to opaque pointers. #[proc_macro] #[proc_macro_error::proc_macro_error] pub fn ffi(input: TokenStream) -> TokenStream { let items = parse_macro_input!(input as FfiItems).0; - #[cfg(feature = "client")] - let items = items.iter().map(|item| { - if is_opaque(item) { - wrapper::wrap_as_opaque(item) - } else { - quote! {#item} - } - }); + // TODO: Should be fixed in https://github.com/hyperledger/iroha/issues/2231 + //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 -#[proc_macro_derive(IntoFfi)] +#[proc_macro_derive(IntoFfi, attributes(opaque_wrapper))] #[proc_macro_error::proc_macro_error] pub fn into_ffi_derive(input: TokenStream) -> TokenStream { let item = parse_macro_input!(input as syn::DeriveInput); let into_ffi_derive = derive_into_ffi(&item); - - if !matches!(item.vis, syn::Visibility::Public(_)) { - abort!(item.vis, "Only public items are supported"); - } - - if !item.generics.params.is_empty() { - abort!(item.generics, "Generics are not supported"); - } - quote! { #into_ffi_derive }.into() } /// Derive implementation of [`TryFromReprC`] trait -#[proc_macro_derive(TryFromReprC)] +#[proc_macro_derive(TryFromReprC, attributes(opaque_wrapper))] #[proc_macro_error::proc_macro_error] pub fn try_from_repr_c_derive(input: TokenStream) -> TokenStream { let item = parse_macro_input!(input as syn::DeriveInput); - let try_from_ffi_derive = derive_try_from_ffi(&item); - - if !matches!(item.vis, syn::Visibility::Public(_)) { - abort!(item.vis, "Only public items are supported"); - } - - if !item.generics.params.is_empty() { - abort!(item.generics, "Generics are not supported"); - } - - quote! { #try_from_ffi_derive }.into() + let try_from_repr_c_derive = derive_try_from_repr_c(&item); + quote! { #try_from_repr_c_derive }.into() } /// Generate FFI functions /// /// # Example: /// ```rust +/// use std::alloc::alloc; +/// /// use getset::Getters; /// use iroha_ffi::{slice::OutSliceRef, FfiReturn, IntoFfi, TryFromReprC}; /// @@ -121,7 +109,7 @@ pub fn try_from_repr_c_derive(input: TokenStream) -> TokenStream { /// } /// } /// -/// // The following functions will be derived: +/// /* The following functions will be derived: /// extern "C" fn Foo__new(id: u8, output: *mut Foo) -> FfiReturn { /// /* function implementation */ /// FfiReturn::Ok @@ -133,7 +121,7 @@ pub fn try_from_repr_c_derive(input: TokenStream) -> TokenStream { /// extern "C" fn Foo__id(handle: *const Foo, output: *mut u8) -> FfiReturn { /// /* function implementation */ /// FfiReturn::Ok -/// } +/// } */ /// ``` #[proc_macro_attribute] #[proc_macro_error::proc_macro_error] @@ -141,11 +129,69 @@ 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::generate); + let ffi_fns = impl_descriptor.fns.iter().map(ffi_fn::gen_definition); + + quote! { + #item + #(#ffi_fns)* + } + } + Item::Struct(item) => { + let derived_methods = util::gen_derived_methods(&item); + let ffi_fns = derived_methods.iter().map(ffi_fn::gen_definition); + + if !matches!(item.vis, syn::Visibility::Public(_)) { + abort!(item.vis, "Only public structs allowed in FFI"); + } + if !item.generics.params.is_empty() { + abort!(item.generics, "Generics are not supported"); + } + + quote! { + #item + #(#ffi_fns)* + } + } + Item::Fn(item) => { + 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(&item); + let ffi_fn = ffi_fn::gen_definition(&fn_descriptor); + quote! { + #item + + #ffi_fn + } + } + item => abort!(item, "Item not supported"), + } + .into() +} + +#[proc_macro_attribute] +#[proc_macro_error::proc_macro_error] +pub fn ffi_import(_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); // TODO: Should be fixed in https://github.com/hyperledger/iroha/issues/2231 - #[cfg(feature = "client")] - let item = wrapper::wrap_impl_item(&impl_descriptor.fns); + //let item = wrapper::wrap_impl_item(&impl_descriptor.fns); quote! { #item @@ -154,7 +200,7 @@ pub fn ffi_export(_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::generate); + let ffi_fns = derived_methods.iter().map(ffi_fn::gen_declaration); if !matches!(item.vis, syn::Visibility::Public(_)) { abort!(item.vis, "Only public structs allowed in FFI"); @@ -165,9 +211,7 @@ pub fn ffi_export(_attr: TokenStream, item: TokenStream) -> TokenStream { // TODO: Remove getset attributes to prevent code generation // Should be fixed in https://github.com/hyperledger/iroha/issues/2231 - //#[cfg(feature = "client")] //let impl_block = Some(wrapper::wrap_impl_item(&derived_methods)); - //#[cfg(not(feature = "client"))] //let impl_block: Option = None; quote! { @@ -193,7 +237,7 @@ pub fn ffi_export(_attr: TokenStream, item: TokenStream) -> TokenStream { } let fn_descriptor = FnDescriptor::from(&item); - let ffi_fn = ffi_fn::generate(&fn_descriptor); + let ffi_fn = ffi_fn::gen_declaration(&fn_descriptor); quote! { #item diff --git a/ffi/derive/src/util.rs b/ffi/derive/src/util.rs index 68940f2989c..7f797272237 100644 --- a/ffi/derive/src/util.rs +++ b/ffi/derive/src/util.rs @@ -5,9 +5,7 @@ use proc_macro_error::{abort, OptionExt}; use quote::quote; use syn::{parse_quote, Ident, Type}; -use crate::impl_visitor::{ - find_doc_attr, unwrap_result_type, Arg, FnDescriptor, InputArg, Receiver, ReturnArg, -}; +use crate::impl_visitor::{find_doc_attr, unwrap_result_type, Arg, FnDescriptor}; /// Type of accessor method derived for a structure #[derive(Clone, Copy, PartialEq, Eq, Hash)] @@ -40,7 +38,7 @@ pub fn gen_derived_methods(item: &syn::ItemStruct) -> Vec { ffi_derives } -pub fn gen_arg_ffi_to_src(arg: &impl Arg, is_output: bool) -> TokenStream { +pub fn gen_arg_ffi_to_src(arg: &Arg, is_output: bool) -> TokenStream { let (arg_name, src_type) = (arg.name(), arg.src_type_resolved()); if is_output { @@ -75,7 +73,7 @@ pub fn gen_arg_ffi_to_src(arg: &impl Arg, is_output: bool) -> TokenStream { } #[allow(clippy::expect_used)] -pub fn gen_arg_src_to_ffi(arg: &impl Arg, is_output: bool) -> TokenStream { +pub fn gen_arg_src_to_ffi(arg: &Arg, is_output: bool) -> TokenStream { let (arg_name, src_type) = (arg.name(), arg.src_type()); let mut resolve_impl_trait = None; @@ -130,7 +128,7 @@ pub fn gen_arg_src_to_ffi(arg: &impl Arg, is_output: bool) -> TokenStream { } } -/// Parses `getset` attributes to find out which methods it derives +/// Parse `getset` attributes to find out which methods it derives fn parse_derives(attrs: &[syn::Attribute]) -> Option> { attrs .iter() @@ -189,19 +187,19 @@ fn gen_derived_method(item_name: &Ident, field: &syn::Field, derive: Derive) -> let (receiver, input_args, output_arg) = match derive { Derive::Setter => ( - Receiver::new(self_ty.clone(), handle_name, parse_quote! {&mut Self}), - vec![InputArg::new(self_ty.clone(), field_name, field_ty)], + Arg::new(self_ty.clone(), handle_name, parse_quote! {&mut Self}), + vec![Arg::new(self_ty.clone(), field_name, field_ty)], None, ), Derive::Getter => ( - Receiver::new(self_ty.clone(), handle_name, parse_quote! {&Self}), + Arg::new(self_ty.clone(), handle_name, parse_quote! {&Self}), Vec::new(), - Some(ReturnArg::new(self_ty.clone(), field_name, field_ty)), + Some(Arg::new(self_ty.clone(), field_name, field_ty)), ), Derive::MutGetter => ( - Receiver::new(self_ty.clone(), handle_name, parse_quote! {&mut Self}), + Arg::new(self_ty.clone(), handle_name, parse_quote! {&mut Self}), Vec::new(), - Some(ReturnArg::new(self_ty.clone(), field_name, field_ty)), + Some(Arg::new(self_ty.clone(), field_name, field_ty)), ), }; diff --git a/ffi/derive/src/wrapper.rs b/ffi/derive/src/wrapper.rs index 2994d81dff6..5a6c1b02028 100644 --- a/ffi/derive/src/wrapper.rs +++ b/ffi/derive/src/wrapper.rs @@ -23,18 +23,10 @@ pub fn wrap_as_opaque(input: &syn::DeriveInput) -> TokenStream { syn::Data::Enum(_) | syn::Data::Struct(_) => { quote! { #(#attrs)* - #vis #item_type #ident{ - ptr: [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)>, - } + #[repr(transparent)] + #vis #item_type #ident { + __opaque_ptr: *mut iroha_ffi::Opaque + }; } } syn::Data::Union(_) => { diff --git a/ffi/derive/tests/ui_fail/derive_skip_field.rs b/ffi/derive/tests/ui_fail/derive_skip_field.rs index 1b8d3f18e5a..5cdbd6ebcea 100644 --- a/ffi/derive/tests/ui_fail/derive_skip_field.rs +++ b/ffi/derive/tests/ui_fail/derive_skip_field.rs @@ -1,4 +1,4 @@ -use core::mem::MaybeUninit; +use std::{alloc::alloc, mem::MaybeUninit}; use getset::{Getters, Setters}; use iroha_ffi::{ffi, ffi_export, IntoFfi, TryFromReprC}; diff --git a/ffi/derive/tests/ui_fail/derive_skip_struct.rs b/ffi/derive/tests/ui_fail/derive_skip_struct.rs index 0c5b8ccb3f8..e7fe2b182f4 100644 --- a/ffi/derive/tests/ui_fail/derive_skip_struct.rs +++ b/ffi/derive/tests/ui_fail/derive_skip_struct.rs @@ -1,4 +1,4 @@ -use core::mem::MaybeUninit; +use std::{alloc::alloc, mem::MaybeUninit}; use getset::{MutGetters, Setters}; use iroha_ffi::{ffi_export, IntoFfi, TryFromReprC}; diff --git a/ffi/derive/tests/ui_pass/shared_fns.rs b/ffi/derive/tests/ui_pass/shared_fns.rs index 404f41de042..5b31a26406a 100644 --- a/ffi/derive/tests/ui_pass/shared_fns.rs +++ b/ffi/derive/tests/ui_pass/shared_fns.rs @@ -1,4 +1,6 @@ -use iroha_ffi::{ffi, ffi_export, ffi_fn, handles, IntoFfi, TryFromReprC}; +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)] @@ -13,10 +15,10 @@ ffi! { } handles! {0, FfiStruct1, FfiStruct2} -ffi_fn! {Drop: FfiStruct1, FfiStruct2} -ffi_fn! {Clone: FfiStruct1, FfiStruct2} -ffi_fn! {Eq: FfiStruct1, FfiStruct2} -ffi_fn! {Ord: 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 { @@ -26,7 +28,6 @@ impl FfiStruct1 { } } -#[cfg(not(feature = "client"))] fn main() { use core::mem::MaybeUninit; @@ -81,12 +82,3 @@ fn main() { __drop(FfiStruct1::ID, cloned.into_ffi().cast()); } } - -#[cfg(feature = "client")] -fn main() { - let name = String::from("X"); - let ffi_struct1 = FfiStruct1::new(name); - let cloned = Clone::clone(ffi_struct1); - assert_eq!(ffi_struct1, cloned); - assert!(ffi_struct1.cmp(cloned)) -} diff --git a/ffi/derive/tests/ui_pass/valid.rs b/ffi/derive/tests/ui_pass/valid.rs index fc0528040ce..8954b71e0ae 100644 --- a/ffi/derive/tests/ui_pass/valid.rs +++ b/ffi/derive/tests/ui_pass/valid.rs @@ -1,7 +1,7 @@ -use std::collections::BTreeMap; +use std::{alloc::alloc, collections::BTreeMap}; use getset::Getters; -use iroha_ffi::{ffi_export, ffi_fn, handles, IntoFfi, TryFromReprC}; +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); @@ -21,7 +21,7 @@ pub struct FfiStruct { } handles! {0, FfiStruct} -ffi_fn! {Drop: FfiStruct} +def_ffi_fn! {Drop: FfiStruct} #[ffi_export] impl FfiStruct { @@ -58,7 +58,6 @@ pub fn ffi_duplicate_with_name(a: &FfiStruct, name: Name) -> FfiStruct { result } -#[cfg(not(feature = "client"))] fn main() { use core::mem::MaybeUninit; @@ -116,18 +115,3 @@ fn main() { assert_eq!(result.get_param(&Name("Nomen")), Some(&Value("Omen"))); } } - -#[cfg(feature = "client")] -fn main() { - let name = Name("X"); - - let mut ffi_struct: FfiStruct::new(name); - - let in_params = vec![(Name("Nomen"), Value("Omen"))]; - FfiStruct::with_params(&mut ffi_struct, in_params); - - let param: Option<&Value> = FfiStruct::get_param(&ffi_struct, name); - let params: Option> = FfiStruct::params(ffi_struct); - - ffi_duplicate_with_name(&FfiStruct, Name); -} diff --git a/ffi/src/handle.rs b/ffi/src/handle.rs index b128ccc2177..28ad428d1bd 100644 --- a/ffi/src/handle.rs +++ b/ffi/src/handle.rs @@ -3,7 +3,7 @@ /// Type of the handle id pub type Id = u8; -/// Implement [`$crate::Handle`] for given types with the given initial handle id. Ids are +/// Implement [`crate::Handle`] for given types with the given initial handle id. Ids are /// assigned incrementally to every type in the macro invocation. Check the following example: /// /// ```rust @@ -12,9 +12,9 @@ pub type Id = u8; /// struct Bar1; /// struct Bar2; /// -/// handles! {0, Foo, Bar} +/// iroha_ffi::handles! {0, Foo1, Foo2, Bar1, Bar2} /// -/// will produce: +/// /* will produce: /// impl Handle for Foo1 { /// const ID: Id = 0; /// } @@ -26,7 +26,7 @@ pub type Id = u8; /// } /// impl Handle for Bar2 { /// const ID: Id = 3; -/// } +/// } */ /// ``` #[macro_export] macro_rules! handles { @@ -52,8 +52,7 @@ macro_rules! handles { /// Generate FFI equivalent implementation of the requested trait method (e.g. Clone, Eq, Ord) #[macro_export] -#[cfg(not(feature = "client"))] -macro_rules! ffi_fn { +macro_rules! def_ffi_fn { (@catch_unwind $block:block ) => { match std::panic::catch_unwind(|| $block) { Ok(res) => match res { @@ -79,7 +78,7 @@ macro_rules! ffi_fn { handle_ptr: *const core::ffi::c_void, output_ptr: *mut *mut core::ffi::c_void ) -> $crate::FfiReturn { - $crate::ffi_fn!(@catch_unwind { + $crate::def_ffi_fn!(@catch_unwind { use core::borrow::Borrow; // False positive - doesn't compile otherwise @@ -116,7 +115,7 @@ macro_rules! ffi_fn { right_handle_ptr: *const core::ffi::c_void, output_ptr: *mut u8, ) -> $crate::FfiReturn { - $crate::ffi_fn!(@catch_unwind { + $crate::def_ffi_fn!(@catch_unwind { use core::borrow::Borrow; // False positive - doesn't compile otherwise @@ -155,7 +154,7 @@ macro_rules! ffi_fn { right_handle_ptr: *const core::ffi::c_void, output_ptr: *mut i8, ) -> $crate::FfiReturn { - $crate::ffi_fn!(@catch_unwind { + $crate::def_ffi_fn!(@catch_unwind { use core::borrow::Borrow; // False positive - doesn't compile otherwise @@ -192,7 +191,7 @@ macro_rules! ffi_fn { handle_id: $crate::handle::Id, handle_ptr: *mut core::ffi::c_void, ) -> $crate::FfiReturn { - $crate::ffi_fn!(@catch_unwind { + $crate::def_ffi_fn!(@catch_unwind { match handle_id { $( <$other as $crate::Handle>::ID => { let handle_ptr = handle_ptr.cast::<$other>(); @@ -210,8 +209,7 @@ macro_rules! ffi_fn { /// Generate the declaration of FFI functions for the requested trait method (e.g. Clone, Eq, Ord) #[macro_export] -#[cfg(feature = "client")] -macro_rules! ffi_fn { +macro_rules! decl_ffi_fn { ( $vis:vis Clone: $( $other:ty ),+ $(,)? ) => { extern { /// FFI function equivalent of [`Clone::clone`] diff --git a/ffi/src/lib.rs b/ffi/src/lib.rs index e539a5a3f9b..091371f6efb 100644 --- a/ffi/src/lib.rs +++ b/ffi/src/lib.rs @@ -240,6 +240,23 @@ 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 diff --git a/ffi/tests/ffi_export.rs b/ffi/tests/ffi_export.rs index a7f6bd08b30..d0a04227013 100644 --- a/ffi/tests/ffi_export.rs +++ b/ffi/tests/ffi_export.rs @@ -1,11 +1,14 @@ -#![cfg(not(feature = "client"))] #![allow(unsafe_code, clippy::restriction, clippy::pedantic)] -use std::{collections::BTreeMap, mem::MaybeUninit}; +use std::{ + alloc::{alloc, Layout}, + collections::BTreeMap, + mem::MaybeUninit, +}; use iroha_ffi::{ - ffi_export, ffi_fn, handles, slice::OutBoxedSlice, AsReprCRef, FfiReturn, FfiTuple2, Handle, - IntoFfi, TryFromReprC, + def_ffi_fn, ffi_export, handles, slice::OutBoxedSlice, AsReprCRef, FfiReturn, FfiTuple2, + Handle, IntoFfi, TryFromReprC, }; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, IntoFfi, TryFromReprC)] @@ -30,7 +33,7 @@ fn get_default_params() -> [(Name, Value); 2] { } handles! {0, FfiStruct} -ffi_fn! {Drop: FfiStruct} +def_ffi_fn! {Drop: FfiStruct} #[ffi_export] impl FfiStruct { @@ -121,7 +124,6 @@ fn get_new_struct_with_params() -> FfiStruct { } #[test] -#[cfg(not(feature = "client"))] fn constructor() { let ffi_struct = get_new_struct(); @@ -137,7 +139,6 @@ fn constructor() { } #[test] -#[cfg(not(feature = "client"))] fn builder_method() { let ffi_struct = get_new_struct_with_params(); @@ -156,7 +157,6 @@ fn builder_method() { } #[test] -#[cfg(not(feature = "client"))] fn consume_self() { let ffi_struct = get_new_struct(); @@ -169,7 +169,6 @@ fn consume_self() { } #[test] -#[cfg(not(feature = "client"))] #[allow(trivial_casts)] fn into_iter_item_impl_into() { let tokens = vec![ @@ -197,7 +196,6 @@ fn into_iter_item_impl_into() { } #[test] -#[cfg(not(feature = "client"))] fn return_option() { let ffi_struct = get_new_struct_with_params(); @@ -234,7 +232,6 @@ fn return_option() { } #[test] -#[cfg(not(feature = "client"))] fn empty_return_iterator() { let ffi_struct = get_new_struct_with_params(); let mut params_len = MaybeUninit::new(0); @@ -255,7 +252,6 @@ fn empty_return_iterator() { } #[test] -#[cfg(not(feature = "client"))] fn return_iterator() { let ffi_struct = get_new_struct_with_params(); let mut params_len = MaybeUninit::new(0); @@ -288,7 +284,6 @@ fn return_iterator() { } #[test] -#[cfg(not(feature = "client"))] fn return_result() { let mut output = MaybeUninit::new(0); diff --git a/ffi/tests/gen_shared_fns.rs b/ffi/tests/gen_shared_fns.rs index d9f475b91f8..a65a05a9eb6 100644 --- a/ffi/tests/gen_shared_fns.rs +++ b/ffi/tests/gen_shared_fns.rs @@ -1,9 +1,12 @@ -#![cfg(not(feature = "client"))] #![allow(unsafe_code, clippy::restriction)] -use std::{cmp::Ordering, mem::MaybeUninit}; +use std::{ + alloc::{alloc, Layout}, + cmp::Ordering, + mem::MaybeUninit, +}; -use iroha_ffi::{ffi, ffi_export, ffi_fn, handles, FfiReturn, Handle, IntoFfi, TryFromReprC}; +use iroha_ffi::{def_ffi_fn, ffi, ffi_export, handles, FfiReturn, Handle, IntoFfi, TryFromReprC}; ffi! { #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, IntoFfi, TryFromReprC)] @@ -18,10 +21,10 @@ ffi! { } handles! {0, FfiStruct1, FfiStruct2} -ffi_fn! {Drop: FfiStruct1, FfiStruct2} -ffi_fn! {Clone: FfiStruct1, FfiStruct2} -ffi_fn! {Eq: FfiStruct1, FfiStruct2} -ffi_fn! {Ord: 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 { diff --git a/ffi/tests/getset.rs b/ffi/tests/getset.rs index c56e9434553..1dd3f27cdc6 100644 --- a/ffi/tests/getset.rs +++ b/ffi/tests/getset.rs @@ -1,7 +1,9 @@ -#![cfg(not(feature = "client"))] #![allow(unsafe_code, clippy::restriction, clippy::pedantic)] -use std::mem::MaybeUninit; +use std::{ + alloc::{alloc, Layout}, + mem::MaybeUninit, +}; use getset::{Getters, MutGetters, Setters}; use iroha_ffi::{ffi_export, IntoFfi, TryFromReprC}; @@ -22,7 +24,6 @@ pub struct FfiStruct { } #[test] -#[cfg(not(feature = "client"))] fn getset_get() { let init_name = Name("Name".to_owned()); let ffi_struct = &mut FfiStruct { diff --git a/primitives/tests/ui.rs b/primitives/tests/ui.rs index e283469d5f8..f15eb517c30 100644 --- a/primitives/tests/ui.rs +++ b/primitives/tests/ui.rs @@ -2,5 +2,7 @@ use trybuild::TestCases; #[test] fn ui() { - TestCases::new().compile_fail("tests/ui_fail/*.rs"); + let test_cases = TestCases::new(); + test_cases.pass("tests/ui_pass/*.rs"); + test_cases.compile_fail("tests/ui_fail/*.rs"); }