Skip to content

Commit

Permalink
[feature] hyperledger-iroha#2490: Update gen_ffi_fn to handle free-st…
Browse files Browse the repository at this point in the history
…anding functions

Signed-off-by: Ales Tsurko <[email protected]>
  • Loading branch information
ales-tsurko committed Jul 27, 2022
1 parent eca2555 commit 7634cbb
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 91 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
46 changes: 36 additions & 10 deletions ffi/derive/src/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,28 @@ pub fn gen_ffi_fn(fn_descriptor: &FnDescriptor) -> TokenStream {
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 ffi_fn_doc = format!(
" FFI function equivalent of [`{}::{}`]\n \
let ffi_fn_doc = fn_descriptor.self_ty.map_or_else(
|| {
format!(
" 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
fn_descriptor.method_name
)
},
|self_ty| {
format!(
" FFI function equivalent of [`{}::{}`]\n \
\n \
# Safety\n \
\n \
All of the given pointers must be valid",
self_ty.get_ident().expect_or_abort("Defined"),
fn_descriptor.method_name
)
},
);

quote! {
Expand Down Expand Up @@ -58,11 +72,19 @@ 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();

Ident::new(
&format!("{}__{}", self_ty_name, fn_descriptor.method_name),
Span::call_site(),
fn_descriptor.self_ty_name().map_or_else(
|| {
Ident::new(
&format!("__{}", fn_descriptor.method_name),
Span::call_site(),
)
},
|self_ty_name| {
Ident::new(
&format!("{}__{}", self_ty_name, fn_descriptor.method_name),
Span::call_site(),
)
},
)
}

Expand Down Expand Up @@ -114,7 +136,11 @@ 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 method_call = if let Some(self_type) = self_type {
quote! {#self_type::#method_name(#(#self_arg_name,)* #(#fn_arg_names),*)}
} else {
quote! {#method_name(#(#self_arg_name,)* #(#fn_arg_names),*)}
};

fn_descriptor.output_arg.as_ref().map_or_else(
|| quote! {#method_call;},
Expand Down
42 changes: 25 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,7 +195,10 @@ impl<'ast> ImplDescriptor<'ast> {
}

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

visitor.visit_impl_item_method(node);
Expand All @@ -213,8 +216,8 @@ 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)
}
}

Expand Down Expand Up @@ -242,7 +245,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 @@ -329,7 +332,7 @@ impl<'ast> Visit<'ast> for ImplVisitor<'ast> {
syn::ImplItem::Method(method) => {
let self_ty = self.self_ty.expect_or_abort("Defined");
self.fns
.push(FnDescriptor::from_impl_method(self_ty, method))
.push(FnDescriptor::from_impl_method(Some(self_ty), method))
}
syn::ImplItem::Type(type_) => {
self.associated_types.push((&type_.ident, &type_.ty));
Expand Down Expand Up @@ -449,11 +452,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 +471,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
64 changes: 14 additions & 50 deletions ffi/derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@

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;
use quote::{format_ident, quote};
use syn::{
parse_macro_input, parse_quote, punctuated::Punctuated, token::Comma, Attribute, FnArg, Ident,
Item, ItemFn, ReturnType,
};
use quote::{quote, ToTokens};
use syn::{parse_macro_input, parse_quote, Attribute, Ident, Item};

mod derive;
mod export;
Expand Down Expand Up @@ -64,7 +61,17 @@ pub fn ffi_export(_attr: TokenStream, item: TokenStream) -> TokenStream {
abort!(item.sig.generics, "Generics are not supported");
}

gen_ffi_fn_from_fn(item)
#[allow(clippy::expect_used)]
let impl_method =
syn::parse(item.to_token_stream().into()).expect("Can't parse fn item");

let impl_descriptor = FnDescriptor::from_impl_method(None, &impl_method);
let ffi_fn = gen_ffi_fn(&impl_descriptor);
quote! {
#item

#ffi_fn
}
}
item => abort!(item, "Item not supported"),
}
Expand Down Expand Up @@ -457,46 +464,3 @@ fn enum_size(enum_name: &Ident, repr: &[syn::NestedMeta]) -> TokenStream2 {
abort!(enum_name, "Enum doesn't have a valid representation")
}
}

fn gen_ffi_fn_from_fn(item: ItemFn) -> TokenStream2 {
let signature = item.sig;
let ident = format_ident!("__{}", signature.ident);
let inputs: Punctuated<FnArg, Comma> =
signature.inputs.into_iter().map(gen_ffi_fn_arg).collect();
let output = gen_ffi_fn_output(signature.output);
let body = item.block;

let result = quote! {
#[no_mangle]
pub extern "C" fn #ident<'a>(#inputs) #output
#body
};
// println!("{}", result.to_string());

result
}

fn gen_ffi_fn_arg(arg: FnArg) -> FnArg {
let mut arg = match arg {
FnArg::Typed(arg) => arg,
_ => unreachable!(),
};

let ty = arg.ty;
let tokens: TokenStream = quote!(<#ty as iroha_ffi::TryFromReprC>::Source).into();
arg.ty = syn::parse::<Box<syn::Type>>(tokens).expect("Can't generate FFI type");

arg.into()
}

fn gen_ffi_fn_output(output: ReturnType) -> ReturnType {
let (arrow, ty) = match output {
ReturnType::Type(arrow, ty) => (arrow, ty),
ReturnType::Default => return output,
};

let ty = quote!(<#ty as iroha_ffi::IntoFfi>::Target).into();
let ty = syn::parse::<Box<syn::Type>>(ty).expect("Can't generate FFI type");

ReturnType::Type(arrow, ty)
}
17 changes: 9 additions & 8 deletions ffi/derive/tests/ui_pass/valid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,17 @@ impl FfiStruct {
}
}

/// Test
#[ffi_export]
fn ffi_func(a: i64, b: i64) -> i64 {
pub fn ffi_func(a: i64, b: i64) -> i64 {
a + b
}

#[ffi_export]
fn ffi_func2(a: String, b: &[u32]) -> String {
format!("{}{}", a, b)
}

// #[ffi_export]
// fn ffi_func2(a: String, b: &[u32]) -> String {
// format!("{}{}", a, b)
// }
//
fn main() {
let name = Name("X");

Expand Down Expand Up @@ -92,7 +93,7 @@ fn main() {
__drop(FfiStruct::ID, ffi_struct.into_ffi().cast());
}

assert_eq!(5, __ffi_func(3, 2) as i64) ;

// assert_eq!(5, __ffi_func(3, 2) as i64) ;
//
// dbg!(__ffi_func2("Iroha".into(), &[1,2,3]));
}

0 comments on commit 7634cbb

Please sign in to comment.