Skip to content

Commit

Permalink
Support builder methods, add ffi integration tests
Browse files Browse the repository at this point in the history
Signed-off-by: Marin Veršić <marin.versic101@gmail.com>
  • Loading branch information
mversic committed May 9, 2022
1 parent c55d9a7 commit 1810caf
Show file tree
Hide file tree
Showing 14 changed files with 391 additions and 95 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ vendored = ["openssl-sys"]
ffi = ["iroha_ffi"]

[dependencies]
iroha_ffi = { path = "../ffi", version = "=2.0.0-pre-rc.3", optional = true }
iroha_ffi = { path = "../ffi", version = "=2.0.0-pre-rc.4", optional = true }
iroha_schema = { path = "../schema" }

derive_more = { version = "0.99.16", default-features = false, features = ["deref", "deref_mut", "display"] }
Expand Down
18 changes: 8 additions & 10 deletions data_model/src/permissions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ impl PermissionToken {
}
}

/// Add parameters to the `PermissionToken` replacing any previously defined
#[inline]
#[must_use]
pub fn with_params(mut self, params: impl IntoIterator<Item = (Name, Value)>) -> Self {
self.params = params.into_iter().collect();
self
}

/// Return a reference to the parameter corresponding to the given name
#[inline]
pub fn get_param(&self, name: &Name) -> Option<&Value> {
Expand All @@ -69,16 +77,6 @@ impl PermissionToken {
}
}

impl PermissionToken {
/// Add parameters to the `PermissionToken` replacing any previously defined
#[inline]
#[must_use]
pub fn with_params(mut self, params: impl IntoIterator<Item = (Name, Value)>) -> Self {
self.params = params.into_iter().collect();
self
}
}

/// The prelude re-exports most commonly used traits, structs and macros from this module.
pub mod prelude {
pub use super::{PermissionToken, Permissions};
Expand Down
4 changes: 2 additions & 2 deletions ffi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
name = "iroha_ffi"
version = "2.0.0-pre-rc.3"
version = "2.0.0-pre-rc.4"
authors = ["Iroha 2 team <https://github.com/orgs/soramitsu/teams/iroha2>"]
edition = "2021"

[dependencies]
iroha_ffi_derive = { version = "=2.0.0-pre-rc.3", path = "derive" }
iroha_ffi_derive = { version = "=2.0.0-pre-rc.4", path = "derive" }
4 changes: 2 additions & 2 deletions ffi/derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "iroha_ffi_derive"
version = "2.0.0-pre-rc.3"
version = "2.0.0-pre-rc.4"
authors = ["Iroha 2 team <https://github.com/orgs/soramitsu/teams/iroha2>"]
edition = "2021"

Expand All @@ -15,7 +15,7 @@ proc-macro2 = "1.0"
proc-macro-error = "1.0"

[dev-dependencies]
iroha_ffi = { version = "=2.0.0-pre-rc.3", path = "../" }
iroha_ffi = { version = "=2.0.0-pre-rc.4", path = "../" }

trybuild = "1.0.53"

204 changes: 133 additions & 71 deletions ffi/derive/src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use heck::ToSnakeCase;
use proc_macro2::Span;
use proc_macro_error::{abort, abort_call_site, OptionExt};
use quote::quote;
use syn::{parse_quote, visit::Visit, visit_mut::VisitMut, Ident, Type};
use syn::{
parse_quote, visit::Visit, visit_mut::VisitMut, Ident, PathArguments::AngleBracketed, Type,
};

pub struct ImplDescriptor {
/// Resolved type of the `Self` type
Expand Down Expand Up @@ -54,7 +56,7 @@ impl quote::ToTokens for FfiFnDescriptor {
.map_or_else(Vec::new, |self_arg| vec![self_arg]);

let fn_args = &self.input_args;
let ret_arg = &self.output_arg;
let ret_arg = self.output_arg();
let fn_body = self.get_fn_body();

tokens.extend(quote! {
Expand Down Expand Up @@ -165,11 +167,6 @@ impl FfiFnDescriptor {
Ident::new("handle", Span::call_site())
}

/// Produces name of the return type argument from a set of predefined conventions
fn get_output_arg_name() -> Ident {
Ident::new("output", Span::call_site())
}

fn add_input_arg(&mut self) {
let ffi_name = self.curr_arg_name.take().expect_or_abort("Defined");
let (src_type, ffi_type) = self.curr_arg_ty.take().expect_or_abort("Defined");
Expand All @@ -181,14 +178,27 @@ impl FfiFnDescriptor {
});
}

/// Produces name of the return type. Name of the self argument is used for dummy output type.
/// Dummy output type is a type which is not present in the FFI function signature. Dummy
/// type is used to signal that the self type passes through the method being transcribed
fn get_output_arg_name(&self, output_ffi_type: &Type) -> Ident {
if let Some(self_arg) = &self.self_arg {
if &self_arg.ffi_type == output_ffi_type {
return self_arg.ffi_name.clone();
}
}

Ident::new("output", Span::call_site())
}

fn add_output_arg(&mut self) {
let (src_type, ffi_type) = self.curr_arg_ty.take().expect_or_abort("Defined");

assert!(self.curr_arg_name.is_none());
assert!(self.output_arg.is_none());

self.output_arg = Some(FfiFnArgDescriptor {
ffi_name: Self::get_output_arg_name(),
ffi_name: self.get_output_arg_name(&ffi_type),
src_type,
ffi_type: parse_quote! { *mut #ffi_type },
});
Expand Down Expand Up @@ -221,7 +231,7 @@ impl FfiFnDescriptor {
}
}

if let Some(output_arg) = &self.output_arg {
if let Some(output_arg) = self.output_arg() {
if let Some(stmt) = output_arg.get_ptr_null_check_stmt() {
stmts.push(stmt);
}
Expand All @@ -240,20 +250,38 @@ impl FfiFnDescriptor {
stmts
}

/// Return output argument if present and not dummy
fn output_arg(&self) -> Option<&FfiFnArgDescriptor> {
self.output_arg.as_ref().and_then(|output_arg| {
if let Some(self_arg) = &self.self_arg {
if self_arg.ffi_name == output_arg.ffi_name {
return None;
}
}

Some(output_arg)
})
}

fn get_ffi_to_src_conversion_stmts(&self) -> Vec<syn::Stmt> {
let mut stmts = vec![];

if let Some(self_arg) = &self.self_arg {
let arg_name = &self_arg.ffi_name;

stmts.push(match &self_arg.src_type {
// NOTE: Takes ownership
// TODO: Better implement without ownership transfer, because others don't take
// ownership, or make others take ownership?
Type::Path(_) => parse_quote! { let #arg_name = *Box::from_raw(#arg_name); },
Type::Reference(_) => parse_quote! { let #arg_name = &*#arg_name; },
match &self_arg.src_type {
Type::Path(_) => stmts.push(parse_quote! {
let _handle = #arg_name.read();
}),
Type::Reference(type_) => {
stmts.push(if type_.mutability.is_some() {
parse_quote! { let #arg_name = &mut *#arg_name; }
} else {
parse_quote! { let #arg_name = &*#arg_name; }
});
}
_ => unreachable!("Self can only be taken by value or by reference"),
});
}
}

for arg in &self.input_args {
Expand All @@ -267,17 +295,20 @@ impl FfiFnDescriptor {
let method_name = &self.method_name;
let self_type = &self.self_ty;

let self_arg_name = self
.self_arg
.as_ref()
.map_or_else(Vec::new, |self_arg| vec![&self_arg.ffi_name]);
let self_arg_name = self.self_arg.as_ref().map_or_else(Vec::new, |self_arg| {
if matches!(self_arg.src_type, Type::Path(_)) {
return vec![Ident::new("_handle", Span::call_site())];
}

vec![self_arg.ffi_name.clone()]
});

let fn_arg_names = self.input_args.iter().map(|arg| &arg.ffi_name);
parse_quote! { let method_res = #self_type::#method_name(#(#self_arg_name,)* #(#fn_arg_names),*); }
}

fn get_src_to_ffi_conversion_stmts(&self) -> Vec<syn::Stmt> {
if let Some(output_arg) = &self.output_arg {
if let Some(output_arg) = self.output_arg() {
return output_arg.get_src_to_ffi_conversion_stmts();
}

Expand Down Expand Up @@ -699,16 +730,26 @@ impl<'ast> syn::visit::Visit<'ast> for FfiFnDescriptor {
if let Some((Type::Path(src_ty), Type::Ptr(ffi_ty))) = self.curr_arg_ty.as_mut() {
let ffi_ptr_subty = &ffi_ty.elem;

if is_option_type(src_ty) {
return;
if !is_option_type(src_ty) {
*ffi_ty = parse_quote! { *mut #ffi_ptr_subty };
}

*ffi_ty = parse_quote! { *mut #ffi_ptr_subty };
}

self.add_output_arg();
}
}

if let Some(self_arg) = &self.self_arg {
let self_src_type = &self_arg.src_type;

if matches!(self_src_type, Type::Path(_)) {
let output_arg = self.output_arg.as_ref();

if output_arg.map_or(true, |out_arg| self_arg.ffi_name != out_arg.ffi_name) {
abort!(self_src_type, "Methods which consume self not supported");
}
}
}
}

fn visit_type_array(&mut self, _: &'ast syn::TypeArray) {
Expand All @@ -726,57 +767,78 @@ impl<'ast> syn::visit::Visit<'ast> for FfiFnDescriptor {
}

if let syn::TypeParamBound::Trait(trait_) = &node.bounds[0] {
let is_output_arg = self.curr_arg_name.is_none();
let last_seg = trait_.path.segments.last().expect_or_abort("Defined");

if trait_.lifetimes.is_some() {
abort_call_site!("Lifetime bound not supported for the `impl trait` argument");
}

let last_seg = trait_.path.segments.last().expect_or_abort("Defined");
let is_into_iterator = !is_output_arg && last_seg.ident == "IntoIterator";
let is_exact_size_iterator = is_output_arg && last_seg.ident == "ExactSizeIterator";

if !is_into_iterator && !is_exact_size_iterator {
abort_call_site!(
"Only IntoIterator and ExactSizeIterator supported in `impl trait`"
);
}

let item = if let syn::PathArguments::AngleBracketed(arguments) = &last_seg.arguments {
if arguments.args.is_empty() {
abort_call_site!("{} missing generic argument `Item`", last_seg.ident);
}
if let syn::GenericArgument::Binding(arg) = &arguments.args[0] {
if arg.ident != "Item" {
abort_call_site!(
"Only `Item` supported in arguments to {}",
last_seg.ident
);
}

&arg.ty
} else {
abort_call_site!("Only `Item` supported in arguments to {}", last_seg.ident);
let is_output_arg = self.curr_arg_name.is_none();
match last_seg.ident.to_string().as_str() {
"IntoIterator" | "ExactSizeIterator" => {
let item = if let AngleBracketed(arguments) = &last_seg.arguments {
if arguments.args.is_empty() {
abort_call_site!("{} missing generic argument `Item`", last_seg.ident);
}
if let syn::GenericArgument::Binding(arg) = &arguments.args[0] {
if arg.ident != "Item" {
abort_call_site!(
"Only `Item` supported in arguments to {}",
last_seg.ident
);
}

&arg.ty
} else {
abort_call_site!(
"Only `Item` supported in arguments to {}",
last_seg.ident
);
}
} else {
abort_call_site!("{} must be parametrized with `Item`", last_seg.ident);
};

self.visit_type(item);
self.curr_arg_ty = {
let ffi_subty = self.curr_arg_ty.as_ref().map(|ty| &ty.1);

Some((
Type::ImplTrait(node.clone()),
if is_output_arg {
parse_quote! { *mut #ffi_subty }
} else {
parse_quote! { *const #ffi_subty }
},
))
};
}
} else {
abort_call_site!("{} must be parametrized with `Item`", last_seg.ident);
};

self.visit_type(item);
self.curr_arg_ty = {
let ffi_subty = self.curr_arg_ty.as_ref().map(|ty| &ty.1);

Some((
Type::ImplTrait(node.clone()),
if is_output_arg {
parse_quote! { *mut #ffi_subty }
"Into" => {
let item = if let AngleBracketed(arguments) = &last_seg.arguments {
match &arguments.args[0] {
syn::GenericArgument::Type(arg) => arg,
_ => unreachable!("Into not parametrized"),
}
} else {
parse_quote! { *const #ffi_subty }
},
))
};
} else {
abort_call_site!("Lifetime bound not supported for the `impl trait` argument");
unreachable!("Into not parametrized")
};

self.visit_type(item);
self.curr_arg_ty = {
let ffi_subty = self.curr_arg_ty.as_ref().map(|ty| &ty.1);

Some((
Type::ImplTrait(node.clone()),
if is_output_arg {
parse_quote! { *mut #ffi_subty }
} else {
parse_quote! { *const #ffi_subty }
},
))
};
}
_ => abort!(trait_, "Unsupported `impl trait`"),
}
}
}
fn visit_type_infer(&mut self, _: &'ast syn::TypeInfer) {
Expand All @@ -802,7 +864,7 @@ impl<'ast> syn::visit::Visit<'ast> for FfiFnDescriptor {

let mut ffi_type = node.clone();
if last_seg.ident == "Option" {
let item = if let syn::PathArguments::AngleBracketed(arguments) = &last_seg.arguments {
let item = if let AngleBracketed(arguments) = &last_seg.arguments {
if let syn::GenericArgument::Type(ty) = &arguments.args[0] {
ty
} else {
Expand Down Expand Up @@ -854,7 +916,7 @@ impl<'ast> syn::visit::Visit<'ast> for FfiFnDescriptor {

self.visit_type(&*node.elem);
if node.mutability.is_some() {
abort!(node.mutability, "Mutable references not supported");
abort!(node, "Mutable references not supported");
}

// NOTE: Owned opaque types make double indirection
Expand Down
Loading

0 comments on commit 1810caf

Please sign in to comment.