Make runtime api calls native when possible (#1302)

* Add simple benchmark for the runtime api

* Make the executor support native calls

* Some documentation

* Hide behind `feature = "std"`

* Rework the native calls

* Make all tests compile again

* Make every parameter using the Block serialized/deserialized in the native call

* Forward `UnwindSafe` requirement

* Remove debug stuff

* Add some documentation

* Fixes warnings

* Fixes errors after master rebase

* Fixes compilation after master rebase

* Fixes compilation after rebase
This commit is contained in:
Bastian Köcher
2019-01-21 14:32:53 +01:00
committed by Gav Wood
parent f0dbcf5401
commit 010e63116f
37 changed files with 1152 additions and 363 deletions
@@ -92,24 +92,6 @@ mod adding_at_parameter {
*/
}
mod adding_parameter_with_type_reference {
/*!
```compile_fail
#[macro_use]
extern crate substrate_client;
extern crate sr_primitives as runtime_primitives;
decl_runtime_apis! {
pub trait Api {
fn test(data: &u64);
}
}
fn main() {}
```
*/
}
mod invalid_api_version {
/*!
```compile_fail
@@ -16,24 +16,28 @@
use utils::{
generate_crate_access, generate_hidden_includes, generate_runtime_mod_name_for_trait,
fold_fn_decl_for_client_side, unwrap_or_error
fold_fn_decl_for_client_side, unwrap_or_error, extract_parameter_names_types_and_borrows,
generate_native_call_generator_fn_name
};
use proc_macro;
use proc_macro2::TokenStream;
use proc_macro2::{TokenStream, Span};
use quote::quote;
use syn::{
spanned::Spanned, parse_macro_input, parse::{Parse, ParseStream, Result, Error},
spanned::Spanned, parse_macro_input, parse::{Parse, ParseStream, Result, Error}, ReturnType,
fold::{self, Fold}, FnDecl, parse_quote, ItemTrait, Generics, GenericParam, Attribute,
visit::{Visit, self}, FnArg, Pat, TraitBound, Type, Meta, NestedMeta, Lit
visit::{Visit, self}, FnArg, Pat, TraitBound, Meta, NestedMeta, Lit, TraitItem, Ident, Type
};
use std::collections::HashMap;
use blake2_rfc;
/// The ident used for the block generic parameter.
const BLOCK_GENERIC_IDENT: &str = "Block";
/// Unique identifier used to make the hidden includes unique for this macro.
const HIDDEN_INCLUDES_ID: &str = "DECL_RUNTIME_APIS";
@@ -88,6 +92,167 @@ fn remove_supported_attributes(attrs: &mut Vec<Attribute>) -> HashMap<&'static s
result
}
/// Visits the ast and checks if `Block` ident is used somewhere.
struct IsUsingBlock {
result: bool,
}
impl<'ast> Visit<'ast> for IsUsingBlock {
fn visit_ident(&mut self, i: &'ast Ident) {
if i == BLOCK_GENERIC_IDENT {
self.result = true;
}
}
}
/// Visits the ast and checks if `Block` ident is used somewhere.
fn type_is_using_block(ty: &Type) -> bool {
let mut visitor = IsUsingBlock { result: false };
visitor.visit_type(ty);
visitor.result
}
/// Visits the ast and checks if `Block` ident is used somewhere.
fn return_type_is_using_block(ty: &ReturnType) -> bool {
let mut visitor = IsUsingBlock { result: false };
visitor.visit_return_type(ty);
visitor.result
}
/// Replace all occurences of `Block` with `NodeBlock`
struct ReplaceBlockWithNodeBlock {}
impl Fold for ReplaceBlockWithNodeBlock {
fn fold_ident(&mut self, input: Ident) -> Ident {
if input == BLOCK_GENERIC_IDENT {
Ident::new("NodeBlock", Span::call_site())
} else {
input
}
}
}
/// Replace all occurences of `Block` with `NodeBlock`
fn fn_arg_replace_block_with_node_block(fn_arg: FnArg) -> FnArg {
let mut replace = ReplaceBlockWithNodeBlock {};
fold::fold_fn_arg(&mut replace, fn_arg)
}
/// Replace all occurences of `Block` with `NodeBlock`
fn return_type_replace_block_with_node_block(return_type: ReturnType) -> ReturnType {
let mut replace = ReplaceBlockWithNodeBlock {};
fold::fold_return_type(&mut replace, return_type)
}
fn generate_native_call_generators(decl: &ItemTrait) -> Result<TokenStream> {
let fns = decl.items.iter().filter_map(|i| match i {
TraitItem::Method(ref m) => Some(&m.sig),
_ => None,
});
let mut result = Vec::new();
let trait_ = &decl.ident;
let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID);
// Auxilariy function that is used to convert between types that use different block types.
// The function expects that both a convertable by encoding the one and decoding the other.
result.push(quote!(
fn convert_between_block_types
<I: #crate_::runtime_api::Encode, R: #crate_::runtime_api::Decode>(input: &I) -> R
{
<R as #crate_::runtime_api::Decode>::decode(
&mut &#crate_::runtime_api::Encode::encode(input)[..]
).unwrap()
}
));
// Generate a native call generator for each function of the given trait.
for fn_ in fns {
let params = extract_parameter_names_types_and_borrows(&fn_.decl)?;
let trait_fn_name = &fn_.ident;
let fn_name = generate_native_call_generator_fn_name(&fn_.ident);
let output = return_type_replace_block_with_node_block(fn_.decl.output.clone());
// Every type that is using the `Block` generic parameter, we need to encode/decode,
// to make it compatible between the runtime/node.
let conversions = params.iter().filter(|v| type_is_using_block(&v.1)).map(|(n, t, _)| {
quote!(
let #n: #t = convert_between_block_types(&#n);
)
});
// Same as for the input types, we need to check if we also need to convert the output,
// before returning it.
let output_conversion = if return_type_is_using_block(&fn_.decl.output) {
quote!( convert_between_block_types(&res) )
} else {
quote!( res )
};
let input_names = params.iter().map(|v| &v.0);
// If the type is using the block generic type, we will encode/decode it to make it
// compatible. To ensure that we forward it by ref/value, we use the value given by the
// the user. Otherwise if it is not using the block, we don't need to add anything.
let input_borrows = params
.iter()
.map(|v| if type_is_using_block(&v.1) { v.2.clone() } else { quote!() });
// Replace all `Block` with `NodeBlock`, add `'a` lifetime to references and collect
// all the function inputs.
let fn_inputs = fn_
.decl
.inputs
.iter()
.map(|v| fn_arg_replace_block_with_node_block(v.clone()))
.map(|v| match v {
FnArg::Captured(ref arg) => {
let mut arg = arg.clone();
match arg.ty {
Type::Reference(ref mut r) => {
r.lifetime = Some(parse_quote!( 'a ));
},
_ => {}
}
FnArg::Captured(arg)
},
r => r.clone(),
});
let (impl_generics, ty_generics, where_clause) = decl.generics.split_for_impl();
// We need to parse them again, to get an easy access to the actual parameters.
let mut impl_generics: Generics = parse_quote!(#impl_generics);
let impl_generics_params = impl_generics.params.iter().map(|p| {
match p {
GenericParam::Type(ref ty) => {
let mut ty = ty.clone();
ty.bounds.push(parse_quote!( 'a ));
GenericParam::Type(ty)
},
// We should not see anything different than type params here.
r => r.clone(),
}
});
// Generate the generator function
result.push(quote!(
#[cfg(any(feature = "std", test))]
pub fn #fn_name<
'a, ApiImpl: #trait_ #ty_generics, NodeBlock: #crate_::runtime_api::BlockT
#(, #impl_generics_params)*
>(
#( #fn_inputs ),*
) -> impl FnOnce() #output + 'a #where_clause {
move || {
#( #conversions )*
let res = ApiImpl::#trait_fn_name(#( #input_borrows #input_names ),*);
#output_conversion
}
}
));
}
Ok(quote!( #( #result )* ))
}
/// Generate the decleration of the trait for the runtime.
fn generate_runtime_decls(decls: &[ItemTrait]) -> TokenStream {
let mut result = Vec::new();
@@ -101,9 +266,11 @@ fn generate_runtime_decls(decls: &[ItemTrait]) -> TokenStream {
generate_runtime_api_version(v as u32)
}));
let id = generate_runtime_api_id(&decl.ident.to_string());
let native_call_generators = unwrap_or_error(generate_native_call_generators(&decl));
result.push(quote!(
#[doc(hidden)]
#[allow(dead_code)]
pub mod #mod_name {
use super::*;
@@ -112,6 +279,8 @@ fn generate_runtime_decls(decls: &[ItemTrait]) -> TokenStream {
pub #api_version
pub #id
#native_call_generators
}
));
}
@@ -143,6 +312,7 @@ impl<'a> Fold for ToClientSideDecl<'a> {
*self.found_attributes = remove_supported_attributes(&mut input.attrs);
// Check if this is the `Core` runtime api trait.
let is_core_trait = self.found_attributes.contains_key(CORE_TRAIT_ATTRIBUTE);
let block_ident = Ident::new(BLOCK_GENERIC_IDENT, Span::call_site());
if is_core_trait {
// Add all the supertraits we want to have for `Core`.
@@ -151,13 +321,12 @@ impl<'a> Fold for ToClientSideDecl<'a> {
'static
+ Send
+ Sync
+ #crate_::runtime_api::ConstructRuntimeApi<Block>
+ #crate_::runtime_api::ApiExt<Block>
+ #crate_::runtime_api::ApiExt<#block_ident>
);
} else {
// Add the `Core` runtime api as super trait.
let crate_ = &self.crate_;
input.supertraits.push(parse_quote!( #crate_::runtime_api::Core<Block> ));
input.supertraits.push(parse_quote!( #crate_::runtime_api::Core<#block_ident> ));
}
// The client side trait is only required when compiling with the feature `std` or `test`.
@@ -291,19 +460,6 @@ impl<'ast> Visit<'ast> for CheckTraitDecl {
},
_ => {}
}
match arg.ty {
Type::Reference(ref reference) => {
self.errors.push(
Error::new(
reference.span(),
"Do not use type references as arguments. The client side \
declaration will take all arguments as reference automatically."
)
)
},
_ => {},
}
},
FnArg::SelfRef(_) | FnArg::SelfValue(_) => {
self.errors.push(Error::new(input.span(), "Self values are not supported."))
@@ -323,7 +479,7 @@ impl<'ast> Visit<'ast> for CheckTraitDecl {
fn visit_generic_param(&mut self, input: &'ast GenericParam) {
match input {
GenericParam::Type(ty) if &ty.ident == "Block" => {
GenericParam::Type(ty) if &ty.ident == BLOCK_GENERIC_IDENT => {
self.errors.push(
Error::new(
input.span(),
@@ -340,7 +496,7 @@ impl<'ast> Visit<'ast> for CheckTraitDecl {
fn visit_trait_bound(&mut self, input: &'ast TraitBound) {
if let Some(last_ident) = input.path.segments.last().map(|v| &v.value().ident) {
if last_ident == "BlockT" || last_ident == "Block" {
if last_ident == "BlockT" || last_ident == BLOCK_GENERIC_IDENT {
self.errors.push(
Error::new(
input.span(),
@@ -16,7 +16,8 @@
use utils::{
unwrap_or_error, generate_crate_access, generate_hidden_includes,
generate_runtime_mod_name_for_trait, fold_fn_decl_for_client_side
generate_runtime_mod_name_for_trait, fold_fn_decl_for_client_side, generate_unique_pattern,
extract_parameter_names_types_and_borrows, generate_native_call_generator_fn_name
};
use proc_macro;
@@ -25,9 +26,9 @@ use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{
spanned::Spanned, parse_macro_input, Ident, Type, ItemImpl, MethodSig, FnArg, Path,
spanned::Spanned, parse_macro_input, Ident, Type, ItemImpl, MethodSig, Path,
ImplItem, parse::{Parse, ParseStream, Result, Error}, PathArguments, GenericArgument, TypePath,
fold::{self, Fold}, FnDecl, parse_quote, Pat
fold::{self, Fold}, FnDecl, parse_quote, FnArg
};
use std::{collections::HashSet, iter};
@@ -60,47 +61,17 @@ fn generate_impl_call(
input: &Ident,
impl_trait: &Path
) -> Result<TokenStream> {
let mut pnames = Vec::new();
let mut ptypes = Vec::new();
let mut generated_pattern_counter = 0;
for input in signature.decl.inputs.iter() {
match input {
FnArg::Captured(arg) => {
match &arg.ty {
Type::Reference(_) => {
return Err(
Error::new(
arg.ty.span(),
"No type references are allowed in the api traits!"
)
)
},
_ => {},
}
pnames.push(
generate_unique_pattern(arg.pat.clone(), &mut generated_pattern_counter)
);
ptypes.push(&arg.ty);
},
_ => {
return Err(
Error::new(
input.span(),
"Only function arguments with the following \
pattern are accepted: `name: type`!"
)
)
}
}
}
let params = extract_parameter_names_types_and_borrows(&signature.decl)?;
let c = generate_crate_access(HIDDEN_INCLUDES_ID);
let c_iter = iter::repeat(&c);
let fn_name = &signature.ident;
let fn_name_str = iter::repeat(fn_name.to_string());
let input = iter::repeat(input);
let pnames2 = pnames.clone();
let pnames = params.iter().map(|v| &v.0);
let pnames2 = params.iter().map(|v| &v.0);
let ptypes = params.iter().map(|v| &v.1);
let pborrow = params.iter().map(|v| &v.2);
Ok(
quote!(
@@ -111,7 +82,7 @@ fn generate_impl_call(
};
)*
let output = <#runtime as #impl_trait>::#fn_name(#( #pnames2 ),*);
let output = <#runtime as #impl_trait>::#fn_name(#( #pborrow #pnames2 ),*);
#c::runtime_api::Encode::encode(&output)
).into()
)
@@ -294,10 +265,11 @@ fn generate_runtime_api_base_structures(impls: &[ItemImpl]) -> Result<TokenStrea
let (block, block_id) = generate_node_block_and_block_id_ty(runtime);
Ok(quote!(
pub struct RuntimeApi {}
/// Implements all runtime apis for the client side.
#[cfg(any(feature = "std", test))]
pub struct RuntimeApi {
call: ::std::ptr::NonNull<#crate_::runtime_api::CallRuntimeAt<#block>>,
pub struct RuntimeApiImpl<C: #crate_::runtime_api::CallRuntimeAt<#block> + 'static> {
call: &'static C,
commit_on_success: ::std::cell::RefCell<bool>,
initialised_block: ::std::cell::RefCell<Option<#block_id>>,
changes: ::std::cell::RefCell<#crate_::runtime_api::OverlayedChanges>,
@@ -307,12 +279,14 @@ fn generate_runtime_api_base_structures(impls: &[ItemImpl]) -> Result<TokenStrea
// `ApiRef` object and `ApiRef` also has an associated lifetime. This lifetimes makes it
// impossible to move `RuntimeApi` into another thread.
#[cfg(any(feature = "std", test))]
unsafe impl Send for RuntimeApi {}
unsafe impl<C: #crate_::runtime_api::CallRuntimeAt<#block>> Send for RuntimeApiImpl<C> {}
#[cfg(any(feature = "std", test))]
unsafe impl Sync for RuntimeApi {}
unsafe impl<C: #crate_::runtime_api::CallRuntimeAt<#block>> Sync for RuntimeApiImpl<C> {}
#[cfg(any(feature = "std", test))]
impl #crate_::runtime_api::ApiExt<#block> for RuntimeApi {
impl<C: #crate_::runtime_api::CallRuntimeAt<#block>> #crate_::runtime_api::ApiExt<#block>
for RuntimeApiImpl<C>
{
fn map_api_result<F: FnOnce(&Self) -> ::std::result::Result<R, E>, R, E>(
&self,
map_call: F
@@ -330,22 +304,21 @@ fn generate_runtime_api_base_structures(impls: &[ItemImpl]) -> Result<TokenStrea
&self,
at: &#block_id
) -> #crate_::error::Result<bool> where Self: Sized {
unsafe { self.call.as_ref().runtime_version_at(at) }.map(|r| r.has_api::<A>())
self.call.runtime_version_at(at).map(|r| r.has_api::<A>())
}
}
#[cfg(any(feature = "std", test))]
impl #crate_::runtime_api::ConstructRuntimeApi<#block> for RuntimeApi {
fn construct_runtime_api<'a, T: #crate_::runtime_api::CallRuntimeAt<#block>>(
call: &'a T
) -> #crate_::runtime_api::ApiRef<'a, Self> where Self: Sized {
RuntimeApi {
call: unsafe {
::std::ptr::NonNull::new_unchecked(
call as
&#crate_::runtime_api::CallRuntimeAt<#block> as *const _ as *mut _
)
},
impl<C: #crate_::runtime_api::CallRuntimeAt<#block> + 'static>
#crate_::runtime_api::ConstructRuntimeApi<#block, C> for RuntimeApi
{
type RuntimeApi = RuntimeApiImpl<C>;
fn construct_runtime_api<'a>(
call: &'a C,
) -> #crate_::runtime_api::ApiRef<'a, Self::RuntimeApi> {
RuntimeApiImpl {
call: unsafe { ::std::mem::transmute(call) },
commit_on_success: true.into(),
initialised_block: None.into(),
changes: Default::default(),
@@ -354,25 +327,39 @@ fn generate_runtime_api_base_structures(impls: &[ItemImpl]) -> Result<TokenStrea
}
#[cfg(any(feature = "std", test))]
impl RuntimeApi {
fn call_api_at<A: #crate_::runtime_api::Encode, R: #crate_::runtime_api::Decode>(
impl<C: #crate_::runtime_api::CallRuntimeAt<#block>> RuntimeApiImpl<C> {
fn call_api_at<
R: #crate_::runtime_api::Encode + #crate_::runtime_api::Decode + PartialEq,
NC: FnOnce() -> R + ::std::panic::UnwindSafe,
>(
&self,
at: &#block_id,
function: &'static str,
args: &A
args: Vec<u8>,
native_call: NC,
) -> #crate_::error::Result<R> {
let res = unsafe {
self.call.as_ref().call_api_at(
self.call.call_api_at(
at,
function,
args.encode(),
args,
&mut *self.changes.borrow_mut(),
&mut *self.initialised_block.borrow_mut()
&mut *self.initialised_block.borrow_mut(),
Some(native_call),
).and_then(|r|
R::decode(&mut &r[..])
.ok_or_else(||
#crate_::error::ErrorKind::CallResultDecode(function).into()
)
match r {
#crate_::runtime_api::NativeOrEncoded::Native(n) => {
Ok(n)
},
#crate_::runtime_api::NativeOrEncoded::Encoded(r) => {
R::decode(&mut &r[..])
.ok_or_else(||
#crate_::error::ErrorKind::CallResultDecode(
function
).into()
)
}
}
)
};
@@ -431,21 +418,6 @@ fn generate_api_impl_for_runtime(impls: &[ItemImpl]) -> Result<TokenStream> {
Ok(quote!( #( #impls_prepared )* ))
}
/// Generate an unique pattern based on the given counter, if the given pattern is a `_`.
fn generate_unique_pattern(pat: Pat, counter: &mut u32) -> Pat {
match pat {
Pat::Wild(_) => {
let generated_name = Ident::new(
&format!("impl_runtime_api_generated_name_{}", counter),
pat.span()
);
*counter += 1;
parse_quote!( #generated_name )
},
_ => pat,
}
}
/// Auxilariy data structure that is used to convert `impl Api for Runtime` to
/// `impl Api for RuntimeApi`.
@@ -457,6 +429,9 @@ struct ApiRuntimeImplToApiRuntimeApiImpl<'a> {
runtime_block: &'a TypePath,
node_block_id: &'a TokenStream,
impl_trait_ident: &'a Ident,
runtime_mod_path: &'a Path,
runtime_type: &'a Type,
trait_generic_arguments: &'a [GenericArgument]
}
impl<'a> Fold for ApiRuntimeImplToApiRuntimeApiImpl<'a> {
@@ -482,30 +457,63 @@ impl<'a> Fold for ApiRuntimeImplToApiRuntimeApiImpl<'a> {
}
fn fold_impl_item_method(&mut self, mut input: syn::ImplItemMethod) -> syn::ImplItemMethod {
{
let block = {
let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID);
let mut generated_name_counter = 0;
// Replace `_` with unique patterns and collect all patterns.
let arg_names = input.sig.decl.inputs.iter_mut().filter_map(|i| match i {
FnArg::Captured(ref mut arg) => Some(&mut arg.pat),
_ => None,
}).map(|p| {
*p = generate_unique_pattern(p.clone(), &mut generated_name_counter);
p
});
let name = prefix_function_with_trait(self.impl_trait_ident, &input.sig.ident);
p.clone()
}).collect::<Vec<_>>();
let runtime_mod_path = self.runtime_mod_path;
let runtime = self.runtime_type;
let arg_names2 = arg_names.clone();
let fn_name = prefix_function_with_trait(self.impl_trait_ident, &input.sig.ident);
let native_call_generator_ident =
generate_native_call_generator_fn_name(&input.sig.ident);
let trait_generic_arguments = self.trait_generic_arguments;
let node_block = self.node_block;
// Generate the new method implementation that calls into the runime.
input.block = parse_quote!( { self.call_api_at(at, #name, &( #( #arg_names ),* )) } );
}
parse_quote!(
{
let args = #crate_::runtime_api::Encode::encode(&( #( &#arg_names ),* ));
self.call_api_at(
at,
#fn_name,
args,
#runtime_mod_path #native_call_generator_ident ::
<#runtime, #node_block #(, #trait_generic_arguments )*> (
#( #arg_names2 ),*
)
)
}
)
};
fold::fold_impl_item_method(self, input)
let mut input = fold::fold_impl_item_method(self, input);
// We need to set the block, after we modified the rest of the ast, otherwise we would
// modify our generated block as well.
input.block = block;
input
}
fn fold_item_impl(&mut self, mut input: ItemImpl) -> ItemImpl {
// Implement the trait for the `RuntimeApi`
input.self_ty = Box::new(parse_quote!( RuntimeApi ));
// Implement the trait for the `RuntimeApiImpl`
input.self_ty = Box::new(parse_quote!( RuntimeApiImpl<RuntimeApiImplCall> ));
// The implementation for the `RuntimeApi` is only required when compiling with the feature
// `std` or `test`.
let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID);
let block = self.node_block;
input.generics.params.push(
parse_quote!( RuntimeApiImplCall: #crate_::runtime_api::CallRuntimeAt<#block> + 'static )
);
// The implementation for the `RuntimeApiImpl` is only required when compiling with
// the feature `std` or `test`.
input.attrs.push(parse_quote!( #[cfg(any(feature = "std", test))] ));
fold::fold_item_impl(self, input)
@@ -517,21 +525,33 @@ fn generate_api_impl_for_runtime_api(impls: &[ItemImpl]) -> Result<TokenStream>
let mut result = Vec::with_capacity(impls.len());
for impl_ in impls {
let impl_trait = extract_impl_trait(&impl_)?;
let impl_trait_ident = &impl_trait
let impl_trait_path = extract_impl_trait(&impl_)?;
let impl_trait = &impl_trait_path
.segments
.last()
.ok_or_else(|| Error::new(impl_trait.span(), "Empty trait path not possible!"))?
.value()
.ident;
let runtime_block = extract_runtime_block_ident(impl_trait)?;
.ok_or_else(|| Error::new(impl_trait_path.span(), "Empty trait path not possible!"))?
.into_value();
let impl_trait_ident = &impl_trait.ident;
let runtime_block = extract_runtime_block_ident(impl_trait_path)?;
let (node_block, node_block_id) = generate_node_block_and_block_id_ty(&impl_.self_ty);
let runtime_type = &impl_.self_ty;
let mut runtime_mod_path = extend_with_runtime_decl_path(impl_trait_path.clone());
// remove the trait to get just the module path
runtime_mod_path.segments.pop();
let trait_generic_arguments = match impl_trait.arguments {
PathArguments::Parenthesized(_) | PathArguments::None => vec![],
PathArguments::AngleBracketed(ref b) => b.args.iter().cloned().collect(),
};
let mut visitor = ApiRuntimeImplToApiRuntimeApiImpl {
runtime_block,
node_block: &node_block,
node_block_id: &node_block_id,
impl_trait_ident: &impl_trait_ident,
runtime_mod_path: &runtime_mod_path,
runtime_type: &*runtime_type,
trait_generic_arguments: &trait_generic_arguments,
};
result.push(visitor.fold_item_impl(impl_.clone()));
+59 -14
View File
@@ -15,7 +15,7 @@
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use proc_macro2::{TokenStream, Span};
use syn::{Result, Ident, FnDecl, parse_quote, Type, FnArg};
use syn::{Result, Ident, FnDecl, parse_quote, Type, Pat, spanned::Spanned, FnArg, Error};
use quote::quote;
use std::env;
@@ -64,19 +64,6 @@ pub fn fold_fn_decl_for_client_side(
block_id: &TokenStream,
crate_: &TokenStream
) -> FnDecl {
// Add `&` to all parameter types.
input.inputs
.iter_mut()
.filter_map(|i| match i {
FnArg::Captured(ref mut arg) => Some(&mut arg.ty),
_ => None,
})
.filter_map(|i| match i {
Type::Reference(_) => None,
r => Some(r),
})
.for_each(|i| *i = parse_quote!( &#i ));
// Add `&self, at:& BlockId` as parameters to each function at the beginning.
input.inputs.insert(0, parse_quote!( at: &#block_id ));
input.inputs.insert(0, parse_quote!( &self ));
@@ -95,3 +82,61 @@ pub fn fold_fn_decl_for_client_side(
input
}
/// Generate an unique pattern based on the given counter, if the given pattern is a `_`.
pub fn generate_unique_pattern(pat: Pat, counter: &mut u32) -> Pat {
match pat {
Pat::Wild(_) => {
let generated_name = Ident::new(
&format!("runtime_api_generated_name_{}", counter),
pat.span()
);
*counter += 1;
parse_quote!( #generated_name )
},
_ => pat,
}
}
/// Extracts the name, the type and `&` or ``(if it is a reference or not)
/// for each parameter in the given function declaration.
pub fn extract_parameter_names_types_and_borrows(fn_decl: &FnDecl)
-> Result<Vec<(Pat, Type, TokenStream)>>
{
let mut result = Vec::new();
let mut generated_pattern_counter = 0;
for input in fn_decl.inputs.iter() {
match input {
FnArg::Captured(arg) => {
let (ty, borrow) = match &arg.ty {
Type::Reference(t) => {
let ty = &t.elem;
(parse_quote!( #ty ), quote!( & ))
},
t => { (t.clone(), quote!()) },
};
let name =
generate_unique_pattern(arg.pat.clone(), &mut generated_pattern_counter);
result.push((name, ty, borrow));
},
_ => {
return Err(
Error::new(
input.span(),
"Only function arguments with the following \
pattern are accepted: `name: type`!"
)
)
}
}
}
Ok(result)
}
/// Generates the name for the native call generator function.
pub fn generate_native_call_generator_fn_name(fn_name: &Ident) -> Ident {
Ident::new(&format!("{}_native_call_generator", fn_name.to_string()), Span::call_site())
}