Skip to content

Commit

Permalink
[feature] hyperledger-iroha#2490: Implement ffi_export for freestandi…
Browse files Browse the repository at this point in the history
…ng functions (hyperledger-iroha#2538)

* [feature] hyperledger-iroha#2490: Implement ffi_export attribute for freestanding functions

Signed-off-by: Ales Tsurko <[email protected]>

* [feature] hyperledger-iroha#2490: Update gen_ffi_fn to handle free-standing functions

Signed-off-by: Ales Tsurko <[email protected]>

* [feature] hyperledger-iroha#2490: Update tests

Signed-off-by: Ales Tsurko <[email protected]>
  • Loading branch information
ales-tsurko authored and mversic committed Sep 6, 2022
1 parent 19583ce commit 10e44ab
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 34 deletions.
12 changes: 6 additions & 6 deletions ffi/derive/src/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,8 @@ fn gen_ffi_derive(item_name: &Ident, field: &syn::Field, derive: Derive) -> syn:
let (ffi_fn_args, ffi_fn_body) = match derive {
Derive::Setter => {
let (handle_arg, field_arg) = (
Receiver::new(&self_ty, handle_name, parse_quote! {&mut Self}),
InputArg::new(&self_ty, field_name, field_ty),
Receiver::new(Some(&self_ty), handle_name, parse_quote! {&mut Self}),
InputArg::new(Some(&self_ty), field_name, field_ty),
);

(
Expand All @@ -182,8 +182,8 @@ fn gen_ffi_derive(item_name: &Ident, field: &syn::Field, derive: Derive) -> syn:
let field_ty = parse_quote! {&#field_ty};

let (handle_arg, field_arg) = (
Receiver::new(&self_ty, handle_name, parse_quote! {&Self}),
ReturnArg::new(&self_ty, field_name.clone(), &field_ty),
Receiver::new(Some(&self_ty), handle_name, parse_quote! {&Self}),
ReturnArg::new(Some(&self_ty), field_name.clone(), &field_ty),
);

(
Expand All @@ -195,8 +195,8 @@ fn gen_ffi_derive(item_name: &Ident, field: &syn::Field, derive: Derive) -> syn:
let field_ty = parse_quote! {&mut #field_ty};

let (handle_arg, field_arg) = (
Receiver::new(&self_ty, handle_name, parse_quote! {&mut Self}),
ReturnArg::new(&self_ty, field_name.clone(), &field_ty),
Receiver::new(Some(&self_ty), handle_name, parse_quote! {&mut Self}),
ReturnArg::new(Some(&self_ty), field_name.clone(), &field_ty),
);

(
Expand Down
24 changes: 18 additions & 6 deletions ffi/derive/src/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,24 @@ pub fn gen_ffi_fn(fn_descriptor: &FnDescriptor) -> TokenStream {
.collect();
let output_arg = ffi_output_arg(fn_descriptor).map(gen_ffi_fn_out_ptr_arg);
let ffi_fn_body = gen_fn_body(fn_descriptor);
let path = fn_descriptor.self_ty.map_or_else(
|| fn_descriptor.method_name.to_string(),
|self_ty| {
format!(
"{}::{}",
self_ty.get_ident().expect_or_abort("Defined"),
fn_descriptor.method_name
)
},
);

let ffi_fn_doc = format!(
" FFI function equivalent of [`{}::{}`]\n \
" FFI function equivalent of [`{}`]\n \
\n \
# Safety\n \
\n \
All of the given pointers must be valid",
fn_descriptor.self_ty.get_ident().expect_or_abort("Defined"),
fn_descriptor.method_name
path
);

quote! {
Expand Down Expand Up @@ -58,8 +67,9 @@ pub fn gen_ffi_fn(fn_descriptor: &FnDescriptor) -> TokenStream {
}

fn gen_ffi_fn_name(fn_descriptor: &FnDescriptor) -> Ident {
let self_ty_name = fn_descriptor.self_ty_name();

let self_ty_name = fn_descriptor
.self_ty_name()
.map_or_else(Default::default, ToString::to_string);
Ident::new(
&format!("{}__{}", self_ty_name, fn_descriptor.method_name),
Span::call_site(),
Expand Down Expand Up @@ -114,7 +124,9 @@ fn gen_method_call_stmt(fn_descriptor: &FnDescriptor) -> TokenStream {
});

let fn_arg_names = fn_descriptor.input_args.iter().map(Arg::name);
let method_call = quote! {#self_type::#method_name(#(#self_arg_name,)* #(#fn_arg_names),*)};
let self_ty_call = self_type.map_or_else(|| quote!(), |self_ty| quote! {#self_ty::});
let fn_call = quote! {#method_name(#(#self_arg_name,)* #(#fn_arg_names),*)};
let method_call = quote! { #self_ty_call #fn_call};

fn_descriptor.output_arg.as_ref().map_or_else(
|| quote! {#method_call;},
Expand Down
55 changes: 38 additions & 17 deletions ffi/derive/src/impl_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,19 @@ pub trait Arg {

#[derive(Constructor)]
pub struct Receiver<'ast> {
self_ty: &'ast syn::Path,
self_ty: Option<&'ast syn::Path>,
name: Ident,
type_: Type,
}

pub struct InputArg<'ast> {
self_ty: &'ast syn::Path,
self_ty: Option<&'ast syn::Path>,
name: &'ast Ident,
type_: &'ast Type,
}

pub struct ReturnArg<'ast> {
self_ty: &'ast syn::Path,
self_ty: Option<&'ast syn::Path>,
name: Ident,
type_: &'ast Type,
}
Expand All @@ -37,7 +37,7 @@ pub struct ImplDescriptor<'ast> {
}

impl<'ast> InputArg<'ast> {
pub fn new(self_ty: &'ast syn::Path, name: &'ast Ident, type_: &'ast Type) -> Self {
pub fn new(self_ty: Option<&'ast syn::Path>, name: &'ast Ident, type_: &'ast Type) -> Self {
Self {
self_ty,
name,
Expand All @@ -47,7 +47,7 @@ impl<'ast> InputArg<'ast> {
}

impl<'ast> ReturnArg<'ast> {
pub fn new(self_ty: &'ast syn::Path, name: Ident, type_: &'ast Type) -> Self {
pub fn new(self_ty: Option<&'ast syn::Path>, name: Ident, type_: &'ast Type) -> Self {
Self {
self_ty,
name,
Expand Down Expand Up @@ -101,14 +101,14 @@ impl Arg for ReturnArg<'_> {
}
}

fn resolve_src_type(self_ty: &syn::Path, mut arg_type: Type) -> Type {
fn resolve_src_type(self_ty: Option<&syn::Path>, mut arg_type: Type) -> Type {
SelfResolver::new(self_ty).visit_type_mut(&mut arg_type);
ImplTraitResolver.visit_type_mut(&mut arg_type);

arg_type
}

fn resolve_ffi_type(self_ty: &syn::Path, mut arg_type: Type, is_output: bool) -> Type {
fn resolve_ffi_type(self_ty: Option<&syn::Path>, mut arg_type: Type, is_output: bool) -> Type {
SelfResolver::new(self_ty).visit_type_mut(&mut arg_type);
ImplTraitResolver.visit_type_mut(&mut arg_type);

Expand All @@ -135,7 +135,7 @@ fn resolve_ffi_type(self_ty: &syn::Path, mut arg_type: Type, is_output: bool) ->

pub struct FnDescriptor<'ast> {
/// Resolved type of the `Self` type
pub self_ty: &'ast syn::Path,
pub self_ty: Option<&'ast syn::Path>,

/// Function documentation
pub doc: syn::LitStr,
Expand All @@ -161,7 +161,7 @@ struct ImplVisitor<'ast> {

struct FnVisitor<'ast> {
/// Resolved type of the `Self` type
self_ty: &'ast syn::Path,
self_ty: Option<&'ast syn::Path>,

/// Function documentation
doc: Option<syn::LitStr>,
Expand Down Expand Up @@ -195,8 +195,8 @@ impl<'ast> ImplDescriptor<'ast> {
}

impl<'ast> FnDescriptor<'ast> {
fn from_impl_method(self_ty: &'ast syn::Path, node: &'ast syn::ImplItemMethod) -> Self {
let mut visitor = FnVisitor::new(self_ty);
pub fn from_impl_method(self_ty: &'ast syn::Path, node: &'ast syn::ImplItemMethod) -> Self {
let mut visitor = FnVisitor::new(Some(self_ty));

visitor.visit_impl_item_method(node);
FnDescriptor::from_visitor(visitor)
Expand All @@ -213,8 +213,17 @@ impl<'ast> FnDescriptor<'ast> {
}
}

pub fn self_ty_name(&self) -> &Ident {
get_ident(self.self_ty)
pub fn self_ty_name(&self) -> Option<&Ident> {
self.self_ty.map(get_ident)
}
}

impl<'ast> From<&'ast syn::ItemFn> for FnDescriptor<'ast> {
fn from(item: &'ast syn::ItemFn) -> Self {
let mut visitor = FnVisitor::new(None);

visitor.visit_item_fn(item);
Self::from_visitor(visitor)
}
}

Expand Down Expand Up @@ -242,7 +251,7 @@ impl<'ast> ImplVisitor<'ast> {
}

impl<'ast> FnVisitor<'ast> {
pub const fn new(self_ty: &'ast syn::Path) -> Self {
pub const fn new(self_ty: Option<&'ast syn::Path>) -> Self {
Self {
self_ty,

Expand Down Expand Up @@ -354,6 +363,13 @@ impl<'ast> Visit<'ast> for FnVisitor<'ast> {

self.visit_signature(&node.sig);
}
fn visit_item_fn(&mut self, node: &'ast syn::ItemFn) {
for attr in &node.attrs {
self.visit_impl_item_method_attribute(attr);
}

self.visit_signature(&node.sig);
}
fn visit_signature(&mut self, node: &'ast syn::Signature) {
if node.constness.is_some() {
// NOTE: It's irrelevant
Expand Down Expand Up @@ -449,11 +465,11 @@ impl<'ast> Visit<'ast> for FnVisitor<'ast> {

/// Visitor replaces all occurrences of `Self` in a path type with a fully qualified type
struct SelfResolver<'ast> {
self_ty: &'ast syn::Path,
self_ty: Option<&'ast syn::Path>,
}

impl<'ast> SelfResolver<'ast> {
fn new(self_ty: &'ast syn::Path) -> Self {
fn new(self_ty: Option<&'ast syn::Path>) -> Self {
Self { self_ty }
}
}
Expand All @@ -468,7 +484,12 @@ impl VisitMut for SelfResolver<'_> {
}

if node.segments[0].ident == "Self" {
let mut node_segments = self.self_ty.segments.clone();
#[allow(clippy::expect_used)]
let mut node_segments = self
.self_ty
.expect("Self type path expected")
.segments
.clone();

for segment in core::mem::take(&mut node.segments).into_iter().skip(1) {
node_segments.push(segment);
Expand Down
27 changes: 26 additions & 1 deletion ffi/derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use derive::gen_fns_from_derives;
use export::gen_ffi_fn;
use impl_visitor::ImplDescriptor;
use impl_visitor::{FnDescriptor, ImplDescriptor};
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use proc_macro_error::abort;
Expand Down Expand Up @@ -44,6 +44,31 @@ pub fn ffi_export(_attr: TokenStream, item: TokenStream) -> TokenStream {
#( #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 = gen_ffi_fn(&fn_descriptor);
quote! {
#item

#ffi_fn
}
}
item => abort!(item, "Item not supported"),
}
.into()
Expand Down
36 changes: 32 additions & 4 deletions ffi/derive/tests/ui_pass/valid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ use iroha_ffi::{
TryFromFfi, TryFromReprC,
};

#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, IntoFfi, TryFromFfi)]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, IntoFfi, TryFromFfi)]
pub struct Name(&'static str);
#[derive(Clone, IntoFfi, TryFromFfi)]
#[derive(Clone, Debug, PartialEq, IntoFfi, TryFromFfi)]
pub struct Value(&'static str);

#[ffi_export]
Expand Down Expand Up @@ -50,6 +50,14 @@ impl FfiStruct {
}
}

/// 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() {
let name = Name("X");

Expand All @@ -60,7 +68,7 @@ fn main() {
TryFromReprC::try_from_repr_c(ffi_struct.assume_init(), &mut ()).unwrap()
};

let in_params = vec![(Name("Nomen"), Value("Omen"))].into_ffi();
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::<isize>::uninit();
Expand All @@ -71,7 +79,10 @@ fn main() {
unsafe {
let name = IntoFfi::into_ffi(name.clone());

FfiStruct__with_params(IntoFfi::into_ffi(&mut ffi_struct), in_params.as_ref());
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);

Expand All @@ -81,4 +92,21 @@ fn main() {

__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")));
}
}

0 comments on commit 10e44ab

Please sign in to comment.