Skip to content

Commit

Permalink
sp-api: Support expanding the macro code (paritytech#13573)
Browse files Browse the repository at this point in the history
* sp-api: Support expanding the macro code

This pr introduces the `expander` crate to expand the generated source code into a file. This gives
better error reporting when trying to fix issues in the macro itself as Rustc will point to the line
in this file. The feature can be enabled by setting `SP_API_EXPAND=1` at compile time.

Besides that the generated code is changed to fix warnings in the exanped version.

* Fixes
  • Loading branch information
bkchr authored and ukint-vs committed Apr 10, 2023
1 parent ba354c0 commit 14a76ef
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 101 deletions.
21 changes: 21 additions & 0 deletions Cargo.lock

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

4 changes: 3 additions & 1 deletion primitives/api/proc-macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ syn = { version = "1.0.98", features = ["full", "fold", "extra-traits", "visit"]
proc-macro2 = "1.0.37"
blake2 = { version = "0.10.4", default-features = false }
proc-macro-crate = "1.1.3"
expander = "1.0.0"
Inflector = "0.11.4"

# Required for the doc tests
[features]
default = [ "std" ]
default = ["std"]
std = []
3 changes: 0 additions & 3 deletions primitives/api/proc-macro/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@
/// The ident used for the block generic parameter.
pub const BLOCK_GENERIC_IDENT: &str = "Block";

/// Unique identifier used to make the hidden includes unique for this macro.
pub const HIDDEN_INCLUDES_ID: &str = "DECL_RUNTIME_APIS";

/// The `core_trait` attribute.
pub const CORE_TRAIT_ATTRIBUTE: &str = "core_trait";
/// The `api_version` attribute.
Expand Down
35 changes: 20 additions & 15 deletions primitives/api/proc-macro/src/decl_runtime_apis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@

use crate::utils::{
extract_parameter_names_types_and_borrows, fold_fn_decl_for_client_side, generate_crate_access,
generate_hidden_includes, generate_runtime_mod_name_for_trait, parse_runtime_api_version,
prefix_function_with_trait, replace_wild_card_parameter_names, return_type_extract_type,
versioned_trait_name, AllowSelfRefInParameters,
generate_runtime_mod_name_for_trait, parse_runtime_api_version, prefix_function_with_trait,
replace_wild_card_parameter_names, return_type_extract_type, versioned_trait_name,
AllowSelfRefInParameters,
};

use crate::common::{
API_VERSION_ATTRIBUTE, BLOCK_GENERIC_IDENT, CHANGED_IN_ATTRIBUTE, CORE_TRAIT_ATTRIBUTE,
HIDDEN_INCLUDES_ID, RENAMED_ATTRIBUTE, SUPPORTED_ATTRIBUTE_NAMES,
RENAMED_ATTRIBUTE, SUPPORTED_ATTRIBUTE_NAMES,
};

use proc_macro2::{Span, TokenStream};
Expand Down Expand Up @@ -62,7 +62,7 @@ impl Parse for RuntimeApiDecls {

/// Extend the given generics with `Block: BlockT` as first generic parameter.
fn extend_generics_with_block(generics: &mut Generics) {
let c = generate_crate_access(HIDDEN_INCLUDES_ID);
let c = generate_crate_access();

generics.lt_token = Some(Default::default());
generics.params.insert(0, parse_quote!( Block: #c::BlockT ));
Expand Down Expand Up @@ -298,7 +298,7 @@ fn generate_runtime_decls(decls: &[ItemTrait]) -> Result<TokenStream> {
#[allow(dead_code)]
#[allow(deprecated)]
pub mod #mod_name {
use super::*;
pub use super::*;

#( #versioned_api_traits )*

Expand Down Expand Up @@ -495,10 +495,10 @@ impl<'a> ToClientSideDecl<'a> {
__runtime_api_at_param__,
#context,
__runtime_api_impl_params_encoded__,
&|version| {
&|_version| {
#(
// Check if we need to call the function by an old name.
if version.apis.iter().any(|(s, v)| {
if _version.apis.iter().any(|(s, v)| {
s == &#runtime_mod::ID && *v < #versions
}) {
return #old_names
Expand Down Expand Up @@ -569,7 +569,7 @@ fn generate_runtime_api_version(version: u32) -> TokenStream {
/// Generates the implementation of `RuntimeApiInfo` for the given trait.
fn generate_runtime_info_impl(trait_: &ItemTrait, version: u64) -> TokenStream {
let trait_name = &trait_.ident;
let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID);
let crate_ = generate_crate_access();
let id = generate_runtime_api_id(&trait_name.to_string());
let version = generate_runtime_api_version(version as u32);

Expand Down Expand Up @@ -620,7 +620,7 @@ fn generate_client_side_decls(decls: &[ItemTrait]) -> Result<TokenStream> {
for decl in decls {
let decl = decl.clone();

let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID);
let crate_ = generate_crate_access();
let block_hash = quote!( <Block as #crate_::BlockT>::Hash );
let mut found_attributes = HashMap::new();
let mut errors = Vec::new();
Expand Down Expand Up @@ -777,15 +777,20 @@ pub fn decl_runtime_apis_impl(input: proc_macro::TokenStream) -> proc_macro::Tok
fn decl_runtime_apis_impl_inner(api_decls: &[ItemTrait]) -> Result<TokenStream> {
check_trait_decls(api_decls)?;

let hidden_includes = generate_hidden_includes(HIDDEN_INCLUDES_ID);
let runtime_decls = generate_runtime_decls(api_decls)?;
let client_side_decls = generate_client_side_decls(api_decls)?;

Ok(quote!(
#hidden_includes

let decl = quote! {
#runtime_decls

#client_side_decls
))
};

let decl = expander::Expander::new("decl_runtime_apis")
.dry(std::env::var("SP_API_EXPAND").is_err())
.verbose(true)
.write_to_out_dir(decl)
.expect("Does not fail because of IO in OUT_DIR; qed");

Ok(decl)
}
93 changes: 57 additions & 36 deletions primitives/api/proc-macro/src/impl_runtime_apis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

use crate::utils::{
extract_all_signature_types, extract_block_type_from_trait_path, extract_impl_trait,
extract_parameter_names_types_and_borrows, generate_crate_access, generate_hidden_includes,
extract_parameter_names_types_and_borrows, generate_crate_access,
generate_runtime_mod_name_for_trait, parse_runtime_api_version, prefix_function_with_trait,
versioned_trait_name, AllowSelfRefInParameters, RequireQualifiedTraitPath,
};
Expand All @@ -38,9 +38,6 @@ use syn::{

use std::collections::HashSet;

/// Unique identifier used to make the hidden includes unique for this macro.
const HIDDEN_INCLUDES_ID: &str = "IMPL_RUNTIME_APIS";

/// The structure used for parsing the runtime api implementations.
struct RuntimeApiImpls {
impls: Vec<ItemImpl>,
Expand Down Expand Up @@ -73,23 +70,41 @@ fn generate_impl_call(
let params =
extract_parameter_names_types_and_borrows(signature, AllowSelfRefInParameters::No)?;

let c = generate_crate_access(HIDDEN_INCLUDES_ID);
let c = generate_crate_access();
let fn_name = &signature.ident;
let fn_name_str = fn_name.to_string();
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);

let decode_params = if params.is_empty() {
quote!()
} else {
let let_binding = if params.len() == 1 {
quote! {
let #( #pnames )* : #( #ptypes )*
}
} else {
quote! {
let ( #( #pnames ),* ) : ( #( #ptypes ),* )
}
};

quote!(
#let_binding =
match #c::DecodeLimit::decode_all_with_depth_limit(
#c::MAX_EXTRINSIC_DEPTH,
&mut #input,
) {
Ok(res) => res,
Err(e) => panic!("Bad input data provided to {}: {}", #fn_name_str, e),
};
)
};

Ok(quote!(
let (#( #pnames ),*) : ( #( #ptypes ),* ) =
match #c::DecodeLimit::decode_all_with_depth_limit(
#c::MAX_EXTRINSIC_DEPTH,
&mut #input,
) {
Ok(res) => res,
Err(e) => panic!("Bad input data provided to {}: {}", #fn_name_str, e),
};
#decode_params

#[allow(deprecated)]
<#runtime as #impl_trait>::#fn_name(#( #pborrow #pnames2 ),*)
Expand Down Expand Up @@ -134,8 +149,8 @@ fn generate_impl_calls(

/// Generate the dispatch function that is used in native to call into the runtime.
fn generate_dispatch_function(impls: &[ItemImpl]) -> Result<TokenStream> {
let data = Ident::new("__sp_api__input_data", Span::call_site());
let c = generate_crate_access(HIDDEN_INCLUDES_ID);
let data = Ident::new("_sp_api_input_data_", Span::call_site());
let c = generate_crate_access();
let impl_calls =
generate_impl_calls(impls, &data)?
.into_iter()
Expand All @@ -161,7 +176,7 @@ fn generate_dispatch_function(impls: &[ItemImpl]) -> Result<TokenStream> {
/// Generate the interface functions that are used to call into the runtime in wasm.
fn generate_wasm_interface(impls: &[ItemImpl]) -> Result<TokenStream> {
let input = Ident::new("input", Span::call_site());
let c = generate_crate_access(HIDDEN_INCLUDES_ID);
let c = generate_crate_access();

let impl_calls =
generate_impl_calls(impls, &input)?
Expand Down Expand Up @@ -195,7 +210,7 @@ fn generate_wasm_interface(impls: &[ItemImpl]) -> Result<TokenStream> {
}

fn generate_runtime_api_base_structures() -> Result<TokenStream> {
let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID);
let crate_ = generate_crate_access();

Ok(quote!(
pub struct RuntimeApi {}
Expand Down Expand Up @@ -414,7 +429,7 @@ impl<'a> ApiRuntimeImplToApiRuntimeApiImpl<'a> {
fn process(mut self, input: ItemImpl) -> ItemImpl {
let mut input = self.fold_item_impl(input);

let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID);
let crate_ = generate_crate_access();

// Delete all functions, because all of them are default implemented by
// `decl_runtime_apis!`. We only need to implement the `__runtime_api_internal_call_api_at`
Expand All @@ -423,7 +438,7 @@ impl<'a> ApiRuntimeImplToApiRuntimeApiImpl<'a> {
input.items.push(parse_quote! {
fn __runtime_api_internal_call_api_at(
&self,
at: <__SR_API_BLOCK__ as #crate_::BlockT>::Hash,
at: <__SrApiBlock__ as #crate_::BlockT>::Hash,
context: #crate_::ExecutionContext,
params: std::vec::Vec<u8>,
fn_name: &dyn Fn(#crate_::RuntimeVersion) -> &'static str,
Expand All @@ -435,7 +450,7 @@ impl<'a> ApiRuntimeImplToApiRuntimeApiImpl<'a> {
}

let res = (|| {
let version = #crate_::CallApiAt::<__SR_API_BLOCK__>::runtime_version_at(
let version = #crate_::CallApiAt::<__SrApiBlock__>::runtime_version_at(
self.call,
at,
)?;
Expand All @@ -450,7 +465,7 @@ impl<'a> ApiRuntimeImplToApiRuntimeApiImpl<'a> {
recorder: &self.recorder,
};

#crate_::CallApiAt::<__SR_API_BLOCK__>::call_api_at(
#crate_::CallApiAt::<__SrApiBlock__>::call_api_at(
self.call,
params,
)
Expand All @@ -469,7 +484,7 @@ impl<'a> ApiRuntimeImplToApiRuntimeApiImpl<'a> {
impl<'a> Fold for ApiRuntimeImplToApiRuntimeApiImpl<'a> {
fn fold_type_path(&mut self, input: TypePath) -> TypePath {
let new_ty_path =
if input == *self.runtime_block { parse_quote!(__SR_API_BLOCK__) } else { input };
if input == *self.runtime_block { parse_quote!(__SrApiBlock__) } else { input };

fold::fold_type_path(self, new_ty_path)
}
Expand All @@ -480,25 +495,26 @@ impl<'a> Fold for ApiRuntimeImplToApiRuntimeApiImpl<'a> {
// Before we directly had the final block type and rust could determine that it is unwind
// safe, but now we just have a generic parameter `Block`.

let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID);
let crate_ = generate_crate_access();

// Implement the trait for the `RuntimeApiImpl`
input.self_ty =
Box::new(parse_quote!( RuntimeApiImpl<__SR_API_BLOCK__, RuntimeApiImplCall> ));
Box::new(parse_quote!( RuntimeApiImpl<__SrApiBlock__, RuntimeApiImplCall> ));

input.generics.params.push(parse_quote!(
__SR_API_BLOCK__: #crate_::BlockT + std::panic::UnwindSafe +
__SrApiBlock__: #crate_::BlockT + std::panic::UnwindSafe +
std::panic::RefUnwindSafe
));
input.generics.params.push(
parse_quote!( RuntimeApiImplCall: #crate_::CallApiAt<__SR_API_BLOCK__> + 'static ),
);
input
.generics
.params
.push(parse_quote!( RuntimeApiImplCall: #crate_::CallApiAt<__SrApiBlock__> + 'static ));

let where_clause = input.generics.make_where_clause();

where_clause.predicates.push(parse_quote! {
RuntimeApiImplCall::StateBackend:
#crate_::StateBackend<#crate_::HashFor<__SR_API_BLOCK__>>
#crate_::StateBackend<#crate_::HashFor<__SrApiBlock__>>
});

where_clause.predicates.push(parse_quote! { &'static RuntimeApiImplCall: Send });
Expand All @@ -511,7 +527,7 @@ impl<'a> Fold for ApiRuntimeImplToApiRuntimeApiImpl<'a> {
});

where_clause.predicates.push(parse_quote! {
__SR_API_BLOCK__::Header: std::panic::UnwindSafe + std::panic::RefUnwindSafe
__SrApiBlock__::Header: std::panic::UnwindSafe + std::panic::RefUnwindSafe
});

input.attrs = filter_cfg_attrs(&input.attrs);
Expand Down Expand Up @@ -574,7 +590,7 @@ fn generate_runtime_api_versions(impls: &[ItemImpl]) -> Result<TokenStream> {
let mut sections = Vec::<TokenStream>::with_capacity(impls.len());
let mut processed_traits = HashSet::new();

let c = generate_crate_access(HIDDEN_INCLUDES_ID);
let c = generate_crate_access();

for impl_ in impls {
let api_ver = extract_api_version(&impl_.attrs, impl_.span())?.map(|a| a as u32);
Expand Down Expand Up @@ -629,14 +645,11 @@ fn impl_runtime_apis_impl_inner(api_impls: &[ItemImpl]) -> Result<TokenStream> {
let dispatch_impl = generate_dispatch_function(api_impls)?;
let api_impls_for_runtime = generate_api_impl_for_runtime(api_impls)?;
let base_runtime_api = generate_runtime_api_base_structures()?;
let hidden_includes = generate_hidden_includes(HIDDEN_INCLUDES_ID);
let runtime_api_versions = generate_runtime_api_versions(api_impls)?;
let wasm_interface = generate_wasm_interface(api_impls)?;
let api_impls_for_runtime_api = generate_api_impl_for_runtime_api(api_impls)?;

Ok(quote!(
#hidden_includes

let impl_ = quote!(
#base_runtime_api

#api_impls_for_runtime
Expand All @@ -652,7 +665,15 @@ fn impl_runtime_apis_impl_inner(api_impls: &[ItemImpl]) -> Result<TokenStream> {

#wasm_interface
}
))
);

let impl_ = expander::Expander::new("impl_runtime_apis")
.dry(std::env::var("SP_API_EXPAND").is_err())
.verbose(true)
.write_to_out_dir(impl_)
.expect("Does not fail because of IO in OUT_DIR; qed");

Ok(impl_)
}

// Filters all attributes except the cfg ones.
Expand Down
Loading

0 comments on commit 14a76ef

Please sign in to comment.