mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 07:41:08 +00:00
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:
@@ -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()));
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user