mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 13:27:57 +00:00
372708d7a2
* sp-api: Make the generated code act based on `std` in `sp-api` Instead of letting the macro generate code that checks if the `std` feature is enabled, it will now generate code that checks if the `std` feature is enabled for the `sp-api` crate. The old implementation basically required that the crate in which the macro was used, had a `std` feature. Now we don't have this requirement anymore and act accordingly the feature in `sp-api` directly. * Missing feature! --------- Co-authored-by: parity-processbot <>
776 lines
23 KiB
Rust
776 lines
23 KiB
Rust
// This file is part of Substrate.
|
|
|
|
// Copyright (C) Parity Technologies (UK) Ltd.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
use crate::{
|
|
common::API_VERSION_ATTRIBUTE,
|
|
runtime_metadata::generate_impl_runtime_metadata,
|
|
utils::{
|
|
extract_all_signature_types, extract_block_type_from_trait_path, extract_impl_trait,
|
|
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,
|
|
},
|
|
};
|
|
|
|
use proc_macro2::{Span, TokenStream};
|
|
|
|
use quote::quote;
|
|
|
|
use syn::{
|
|
fold::{self, Fold},
|
|
parse::{Error, Parse, ParseStream, Result},
|
|
parse_macro_input, parse_quote,
|
|
spanned::Spanned,
|
|
Attribute, Ident, ImplItem, ItemImpl, Path, Signature, Type, TypePath,
|
|
};
|
|
|
|
use std::collections::HashSet;
|
|
|
|
/// The structure used for parsing the runtime api implementations.
|
|
struct RuntimeApiImpls {
|
|
impls: Vec<ItemImpl>,
|
|
}
|
|
|
|
impl Parse for RuntimeApiImpls {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let mut impls = Vec::new();
|
|
|
|
while !input.is_empty() {
|
|
impls.push(ItemImpl::parse(input)?);
|
|
}
|
|
|
|
if impls.is_empty() {
|
|
Err(Error::new(Span::call_site(), "No api implementation given!"))
|
|
} else {
|
|
Ok(Self { impls })
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Generates the call to the implementation of the requested function.
|
|
/// The generated code includes decoding of the input arguments and encoding of the output.
|
|
fn generate_impl_call(
|
|
signature: &Signature,
|
|
runtime: &Type,
|
|
input: &Ident,
|
|
impl_trait: &Path,
|
|
) -> Result<TokenStream> {
|
|
let params =
|
|
extract_parameter_names_types_and_borrows(signature, AllowSelfRefInParameters::No)?;
|
|
|
|
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!(
|
|
if !#input.is_empty() {
|
|
panic!(
|
|
"Bad input data provided to {}: expected no parameters, but input buffer is not empty.",
|
|
#fn_name_str
|
|
);
|
|
}
|
|
)
|
|
} 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!(
|
|
#decode_params
|
|
|
|
#[allow(deprecated)]
|
|
<#runtime as #impl_trait>::#fn_name(#( #pborrow #pnames2 ),*)
|
|
))
|
|
}
|
|
|
|
/// Generate all the implementation calls for the given functions.
|
|
fn generate_impl_calls(
|
|
impls: &[ItemImpl],
|
|
input: &Ident,
|
|
) -> Result<Vec<(Ident, Ident, TokenStream, Vec<Attribute>)>> {
|
|
let mut impl_calls = Vec::new();
|
|
|
|
for impl_ in impls {
|
|
let trait_api_ver = extract_api_version(&impl_.attrs, impl_.span())?;
|
|
let impl_trait_path = extract_impl_trait(impl_, RequireQualifiedTraitPath::Yes)?;
|
|
let impl_trait = extend_with_runtime_decl_path(impl_trait_path.clone());
|
|
let impl_trait = extend_with_api_version(impl_trait, trait_api_ver);
|
|
let impl_trait_ident = &impl_trait_path
|
|
.segments
|
|
.last()
|
|
.ok_or_else(|| Error::new(impl_trait_path.span(), "Empty trait path not possible!"))?
|
|
.ident;
|
|
|
|
for item in &impl_.items {
|
|
if let ImplItem::Fn(method) = item {
|
|
let impl_call =
|
|
generate_impl_call(&method.sig, &impl_.self_ty, input, &impl_trait)?;
|
|
|
|
impl_calls.push((
|
|
impl_trait_ident.clone(),
|
|
method.sig.ident.clone(),
|
|
impl_call,
|
|
filter_cfg_attrs(&impl_.attrs),
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(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();
|
|
let impl_calls =
|
|
generate_impl_calls(impls, &data)?
|
|
.into_iter()
|
|
.map(|(trait_, fn_name, impl_, attrs)| {
|
|
let name = prefix_function_with_trait(&trait_, &fn_name);
|
|
quote!(
|
|
#( #attrs )*
|
|
#name => Some(#c::Encode::encode(&{ #impl_ })),
|
|
)
|
|
});
|
|
|
|
Ok(quote!(
|
|
#c::std_enabled! {
|
|
pub fn dispatch(method: &str, mut #data: &[u8]) -> Option<Vec<u8>> {
|
|
match method {
|
|
#( #impl_calls )*
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
))
|
|
}
|
|
|
|
/// 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();
|
|
|
|
let impl_calls =
|
|
generate_impl_calls(impls, &input)?
|
|
.into_iter()
|
|
.map(|(trait_, fn_name, impl_, attrs)| {
|
|
let fn_name =
|
|
Ident::new(&prefix_function_with_trait(&trait_, &fn_name), Span::call_site());
|
|
|
|
quote!(
|
|
#c::std_disabled! {
|
|
#( #attrs )*
|
|
#[no_mangle]
|
|
pub unsafe fn #fn_name(input_data: *mut u8, input_len: usize) -> u64 {
|
|
let mut #input = if input_len == 0 {
|
|
&[0u8; 0]
|
|
} else {
|
|
unsafe {
|
|
#c::slice::from_raw_parts(input_data, input_len)
|
|
}
|
|
};
|
|
|
|
#c::init_runtime_logger();
|
|
|
|
let output = (move || { #impl_ })();
|
|
#c::to_substrate_wasm_fn_return_value(&output)
|
|
}
|
|
}
|
|
)
|
|
});
|
|
|
|
Ok(quote!( #( #impl_calls )* ))
|
|
}
|
|
|
|
fn generate_runtime_api_base_structures() -> Result<TokenStream> {
|
|
let crate_ = generate_crate_access();
|
|
|
|
Ok(quote!(
|
|
pub struct RuntimeApi {}
|
|
/// Implements all runtime apis for the client side.
|
|
#crate_::std_enabled! {
|
|
pub struct RuntimeApiImpl<Block: #crate_::BlockT, C: #crate_::CallApiAt<Block> + 'static> {
|
|
call: &'static C,
|
|
commit_on_success: std::cell::RefCell<bool>,
|
|
changes: std::cell::RefCell<#crate_::OverlayedChanges>,
|
|
storage_transaction_cache: std::cell::RefCell<
|
|
#crate_::StorageTransactionCache<Block, C::StateBackend>
|
|
>,
|
|
recorder: std::option::Option<#crate_::ProofRecorder<Block>>,
|
|
}
|
|
|
|
impl<Block: #crate_::BlockT, C: #crate_::CallApiAt<Block>> #crate_::ApiExt<Block> for
|
|
RuntimeApiImpl<Block, C>
|
|
{
|
|
type StateBackend = C::StateBackend;
|
|
|
|
fn execute_in_transaction<F: FnOnce(&Self) -> #crate_::TransactionOutcome<R>, R>(
|
|
&self,
|
|
call: F,
|
|
) -> R where Self: Sized {
|
|
self.start_transaction();
|
|
|
|
*std::cell::RefCell::borrow_mut(&self.commit_on_success) = false;
|
|
let res = call(self);
|
|
*std::cell::RefCell::borrow_mut(&self.commit_on_success) = true;
|
|
|
|
self.commit_or_rollback(std::matches!(res, #crate_::TransactionOutcome::Commit(_)));
|
|
|
|
res.into_inner()
|
|
}
|
|
|
|
fn has_api<A: #crate_::RuntimeApiInfo + ?Sized>(
|
|
&self,
|
|
at: <Block as #crate_::BlockT>::Hash,
|
|
) -> std::result::Result<bool, #crate_::ApiError> where Self: Sized {
|
|
#crate_::CallApiAt::<Block>::runtime_version_at(self.call, at)
|
|
.map(|v| #crate_::RuntimeVersion::has_api_with(&v, &A::ID, |v| v == A::VERSION))
|
|
}
|
|
|
|
fn has_api_with<A: #crate_::RuntimeApiInfo + ?Sized, P: Fn(u32) -> bool>(
|
|
&self,
|
|
at: <Block as #crate_::BlockT>::Hash,
|
|
pred: P,
|
|
) -> std::result::Result<bool, #crate_::ApiError> where Self: Sized {
|
|
#crate_::CallApiAt::<Block>::runtime_version_at(self.call, at)
|
|
.map(|v| #crate_::RuntimeVersion::has_api_with(&v, &A::ID, pred))
|
|
}
|
|
|
|
fn api_version<A: #crate_::RuntimeApiInfo + ?Sized>(
|
|
&self,
|
|
at: <Block as #crate_::BlockT>::Hash,
|
|
) -> std::result::Result<Option<u32>, #crate_::ApiError> where Self: Sized {
|
|
#crate_::CallApiAt::<Block>::runtime_version_at(self.call, at)
|
|
.map(|v| #crate_::RuntimeVersion::api_version(&v, &A::ID))
|
|
}
|
|
|
|
fn record_proof(&mut self) {
|
|
self.recorder = std::option::Option::Some(std::default::Default::default());
|
|
}
|
|
|
|
fn proof_recorder(&self) -> std::option::Option<#crate_::ProofRecorder<Block>> {
|
|
std::clone::Clone::clone(&self.recorder)
|
|
}
|
|
|
|
fn extract_proof(
|
|
&mut self,
|
|
) -> std::option::Option<#crate_::StorageProof> {
|
|
let recorder = std::option::Option::take(&mut self.recorder);
|
|
std::option::Option::map(recorder, |recorder| {
|
|
#crate_::ProofRecorder::<Block>::drain_storage_proof(recorder)
|
|
})
|
|
}
|
|
|
|
fn into_storage_changes(
|
|
&self,
|
|
backend: &Self::StateBackend,
|
|
parent_hash: Block::Hash,
|
|
) -> core::result::Result<
|
|
#crate_::StorageChanges<C::StateBackend, Block>,
|
|
String
|
|
> where Self: Sized {
|
|
let state_version = #crate_::CallApiAt::<Block>::runtime_version_at(self.call, std::clone::Clone::clone(&parent_hash))
|
|
.map(|v| #crate_::RuntimeVersion::state_version(&v))
|
|
.map_err(|e| format!("Failed to get state version: {}", e))?;
|
|
|
|
#crate_::OverlayedChanges::into_storage_changes(
|
|
std::cell::RefCell::take(&self.changes),
|
|
backend,
|
|
core::cell::RefCell::take(&self.storage_transaction_cache),
|
|
state_version,
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<Block: #crate_::BlockT, C> #crate_::ConstructRuntimeApi<Block, C>
|
|
for RuntimeApi
|
|
where
|
|
C: #crate_::CallApiAt<Block> + 'static,
|
|
{
|
|
type RuntimeApi = RuntimeApiImpl<Block, C>;
|
|
|
|
fn construct_runtime_api<'a>(
|
|
call: &'a C,
|
|
) -> #crate_::ApiRef<'a, Self::RuntimeApi> {
|
|
RuntimeApiImpl {
|
|
call: unsafe { std::mem::transmute(call) },
|
|
commit_on_success: true.into(),
|
|
changes: std::default::Default::default(),
|
|
recorder: std::default::Default::default(),
|
|
storage_transaction_cache: std::default::Default::default(),
|
|
}.into()
|
|
}
|
|
}
|
|
|
|
impl<Block: #crate_::BlockT, C: #crate_::CallApiAt<Block>> RuntimeApiImpl<Block, C> {
|
|
fn commit_or_rollback(&self, commit: bool) {
|
|
let proof = "\
|
|
We only close a transaction when we opened one ourself.
|
|
Other parts of the runtime that make use of transactions (state-machine)
|
|
also balance their transactions. The runtime cannot close client initiated
|
|
transactions; qed";
|
|
if *std::cell::RefCell::borrow(&self.commit_on_success) {
|
|
let res = if commit {
|
|
let res = if let Some(recorder) = &self.recorder {
|
|
#crate_::ProofRecorder::<Block>::commit_transaction(&recorder)
|
|
} else {
|
|
Ok(())
|
|
};
|
|
|
|
let res2 = #crate_::OverlayedChanges::commit_transaction(
|
|
&mut std::cell::RefCell::borrow_mut(&self.changes)
|
|
);
|
|
|
|
// Will panic on an `Err` below, however we should call commit
|
|
// on the recorder and the changes together.
|
|
std::result::Result::and(res, std::result::Result::map_err(res2, drop))
|
|
} else {
|
|
let res = if let Some(recorder) = &self.recorder {
|
|
#crate_::ProofRecorder::<Block>::rollback_transaction(&recorder)
|
|
} else {
|
|
Ok(())
|
|
};
|
|
|
|
let res2 = #crate_::OverlayedChanges::rollback_transaction(
|
|
&mut std::cell::RefCell::borrow_mut(&self.changes)
|
|
);
|
|
|
|
// Will panic on an `Err` below, however we should call commit
|
|
// on the recorder and the changes together.
|
|
std::result::Result::and(res, std::result::Result::map_err(res2, drop))
|
|
};
|
|
|
|
std::result::Result::expect(res, proof);
|
|
}
|
|
}
|
|
|
|
fn start_transaction(&self) {
|
|
if !*std::cell::RefCell::borrow(&self.commit_on_success) {
|
|
return
|
|
}
|
|
|
|
#crate_::OverlayedChanges::start_transaction(
|
|
&mut std::cell::RefCell::borrow_mut(&self.changes)
|
|
);
|
|
if let Some(recorder) = &self.recorder {
|
|
#crate_::ProofRecorder::<Block>::start_transaction(&recorder);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
))
|
|
}
|
|
|
|
/// Extend the given trait path with module that contains the declaration of the trait for the
|
|
/// runtime.
|
|
fn extend_with_runtime_decl_path(mut trait_: Path) -> Path {
|
|
let runtime = {
|
|
let trait_name = &trait_
|
|
.segments
|
|
.last()
|
|
.as_ref()
|
|
.expect("Trait path should always contain at least one item; qed")
|
|
.ident;
|
|
|
|
generate_runtime_mod_name_for_trait(trait_name)
|
|
};
|
|
|
|
let pos = trait_.segments.len() - 1;
|
|
trait_.segments.insert(pos, runtime.into());
|
|
trait_
|
|
}
|
|
|
|
fn extend_with_api_version(mut trait_: Path, version: Option<u64>) -> Path {
|
|
let version = if let Some(v) = version {
|
|
v
|
|
} else {
|
|
// nothing to do
|
|
return trait_
|
|
};
|
|
|
|
let trait_name = &mut trait_
|
|
.segments
|
|
.last_mut()
|
|
.expect("Trait path should always contain at least one item; qed")
|
|
.ident;
|
|
*trait_name = versioned_trait_name(trait_name, version);
|
|
|
|
trait_
|
|
}
|
|
|
|
/// Generates the implementations of the apis for the runtime.
|
|
fn generate_api_impl_for_runtime(impls: &[ItemImpl]) -> Result<TokenStream> {
|
|
let mut impls_prepared = Vec::new();
|
|
|
|
// We put `runtime` before each trait to get the trait that is intended for the runtime and
|
|
// we put the `RuntimeBlock` as first argument for the trait generics.
|
|
for impl_ in impls.iter() {
|
|
let trait_api_ver = extract_api_version(&impl_.attrs, impl_.span())?;
|
|
|
|
let mut impl_ = impl_.clone();
|
|
let trait_ = extract_impl_trait(&impl_, RequireQualifiedTraitPath::Yes)?.clone();
|
|
let trait_ = extend_with_runtime_decl_path(trait_);
|
|
let trait_ = extend_with_api_version(trait_, trait_api_ver);
|
|
|
|
impl_.trait_.as_mut().unwrap().1 = trait_;
|
|
impl_.attrs = filter_cfg_attrs(&impl_.attrs);
|
|
impls_prepared.push(impl_);
|
|
}
|
|
|
|
Ok(quote!( #( #impls_prepared )* ))
|
|
}
|
|
|
|
/// Auxiliary data structure that is used to convert `impl Api for Runtime` to
|
|
/// `impl Api for RuntimeApi`.
|
|
/// This requires us to replace the runtime `Block` with the node `Block`,
|
|
/// `impl Api for Runtime` with `impl Api for RuntimeApi` and replace the method implementations
|
|
/// with code that calls into the runtime.
|
|
struct ApiRuntimeImplToApiRuntimeApiImpl<'a> {
|
|
runtime_block: &'a TypePath,
|
|
}
|
|
|
|
impl<'a> ApiRuntimeImplToApiRuntimeApiImpl<'a> {
|
|
/// Process the given item implementation.
|
|
fn process(mut self, input: ItemImpl) -> ItemImpl {
|
|
let mut input = self.fold_item_impl(input);
|
|
|
|
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`
|
|
// function.
|
|
input.items.clear();
|
|
input.items.push(parse_quote! {
|
|
fn __runtime_api_internal_call_api_at(
|
|
&self,
|
|
at: <__SrApiBlock__ as #crate_::BlockT>::Hash,
|
|
context: #crate_::ExecutionContext,
|
|
params: std::vec::Vec<u8>,
|
|
fn_name: &dyn Fn(#crate_::RuntimeVersion) -> &'static str,
|
|
) -> std::result::Result<std::vec::Vec<u8>, #crate_::ApiError> {
|
|
self.start_transaction();
|
|
|
|
let res = (|| {
|
|
let version = #crate_::CallApiAt::<__SrApiBlock__>::runtime_version_at(
|
|
self.call,
|
|
at,
|
|
)?;
|
|
|
|
let params = #crate_::CallApiAtParams {
|
|
at,
|
|
function: (*fn_name)(version),
|
|
arguments: params,
|
|
overlayed_changes: &self.changes,
|
|
storage_transaction_cache: &self.storage_transaction_cache,
|
|
context,
|
|
recorder: &self.recorder,
|
|
};
|
|
|
|
#crate_::CallApiAt::<__SrApiBlock__>::call_api_at(
|
|
self.call,
|
|
params,
|
|
)
|
|
})();
|
|
|
|
self.commit_or_rollback(std::result::Result::is_ok(&res));
|
|
|
|
res
|
|
}
|
|
});
|
|
|
|
input
|
|
}
|
|
}
|
|
|
|
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!(__SrApiBlock__) } else { input };
|
|
|
|
fold::fold_type_path(self, new_ty_path)
|
|
}
|
|
|
|
fn fold_item_impl(&mut self, mut input: ItemImpl) -> ItemImpl {
|
|
// All this `UnwindSafe` magic below here is required for this rust bug:
|
|
// https://github.com/rust-lang/rust/issues/24159
|
|
// 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();
|
|
|
|
// Implement the trait for the `RuntimeApiImpl`
|
|
input.self_ty =
|
|
Box::new(parse_quote!( RuntimeApiImpl<__SrApiBlock__, RuntimeApiImplCall> ));
|
|
|
|
input.generics.params.push(parse_quote!(
|
|
__SrApiBlock__: #crate_::BlockT + std::panic::UnwindSafe +
|
|
std::panic::RefUnwindSafe
|
|
));
|
|
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<__SrApiBlock__>>
|
|
});
|
|
|
|
where_clause.predicates.push(parse_quote! { &'static RuntimeApiImplCall: Send });
|
|
|
|
// Require that all types used in the function signatures are unwind safe.
|
|
extract_all_signature_types(&input.items).iter().for_each(|i| {
|
|
where_clause.predicates.push(parse_quote! {
|
|
#i: std::panic::UnwindSafe + std::panic::RefUnwindSafe
|
|
});
|
|
});
|
|
|
|
where_clause.predicates.push(parse_quote! {
|
|
__SrApiBlock__::Header: std::panic::UnwindSafe + std::panic::RefUnwindSafe
|
|
});
|
|
|
|
input.attrs = filter_cfg_attrs(&input.attrs);
|
|
|
|
fold::fold_item_impl(self, input)
|
|
}
|
|
}
|
|
|
|
/// Generate the implementations of the runtime apis for the `RuntimeApi` type.
|
|
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_path = extract_impl_trait(impl_, RequireQualifiedTraitPath::Yes)?;
|
|
let runtime_block = extract_block_type_from_trait_path(impl_trait_path)?;
|
|
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 processed_impl =
|
|
ApiRuntimeImplToApiRuntimeApiImpl { runtime_block }.process(impl_.clone());
|
|
|
|
result.push(processed_impl);
|
|
}
|
|
|
|
let crate_ = generate_crate_access();
|
|
|
|
Ok(quote!( #crate_::std_enabled! { #( #result )* } ))
|
|
}
|
|
|
|
fn populate_runtime_api_versions(
|
|
result: &mut Vec<TokenStream>,
|
|
sections: &mut Vec<TokenStream>,
|
|
attrs: Vec<Attribute>,
|
|
id: Path,
|
|
version: TokenStream,
|
|
crate_access: &TokenStream,
|
|
) {
|
|
result.push(quote!(
|
|
#( #attrs )*
|
|
(#id, #version)
|
|
));
|
|
|
|
sections.push(quote!(
|
|
#crate_access::std_disabled! {
|
|
#( #attrs )*
|
|
const _: () = {
|
|
// All sections with the same name are going to be merged by concatenation.
|
|
#[link_section = "runtime_apis"]
|
|
static SECTION_CONTENTS: [u8; 12] = #crate_access::serialize_runtime_api_info(#id, #version);
|
|
};
|
|
}
|
|
));
|
|
}
|
|
|
|
/// Generates `RUNTIME_API_VERSIONS` that holds all version information about the implemented
|
|
/// runtime apis.
|
|
fn generate_runtime_api_versions(impls: &[ItemImpl]) -> Result<TokenStream> {
|
|
let mut result = Vec::<TokenStream>::with_capacity(impls.len());
|
|
let mut sections = Vec::<TokenStream>::with_capacity(impls.len());
|
|
let mut processed_traits = HashSet::new();
|
|
|
|
let c = generate_crate_access();
|
|
|
|
for impl_ in impls {
|
|
let api_ver = extract_api_version(&impl_.attrs, impl_.span())?.map(|a| a as u32);
|
|
|
|
let mut path = extend_with_runtime_decl_path(
|
|
extract_impl_trait(impl_, RequireQualifiedTraitPath::Yes)?.clone(),
|
|
);
|
|
// Remove the trait
|
|
let trait_ = path
|
|
.segments
|
|
.pop()
|
|
.expect("extract_impl_trait already checks that this is valid; qed")
|
|
.into_value()
|
|
.ident;
|
|
|
|
let span = trait_.span();
|
|
if !processed_traits.insert(trait_) {
|
|
return Err(Error::new(
|
|
span,
|
|
"Two traits with the same name detected! \
|
|
The trait name is used to generate its ID. \
|
|
Please rename one trait at the declaration!",
|
|
))
|
|
}
|
|
|
|
let id: Path = parse_quote!( #path ID );
|
|
let version = quote!( #path VERSION );
|
|
let attrs = filter_cfg_attrs(&impl_.attrs);
|
|
|
|
let api_ver = api_ver.map(|a| quote!( #a )).unwrap_or_else(|| version);
|
|
populate_runtime_api_versions(&mut result, &mut sections, attrs, id, api_ver, &c)
|
|
}
|
|
|
|
Ok(quote!(
|
|
const RUNTIME_API_VERSIONS: #c::ApisVec = #c::create_apis_vec!([ #( #result ),* ]);
|
|
|
|
#( #sections )*
|
|
))
|
|
}
|
|
|
|
/// The implementation of the `impl_runtime_apis!` macro.
|
|
pub fn impl_runtime_apis_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|
// Parse all impl blocks
|
|
let RuntimeApiImpls { impls: api_impls } = parse_macro_input!(input as RuntimeApiImpls);
|
|
|
|
impl_runtime_apis_impl_inner(&api_impls)
|
|
.unwrap_or_else(|e| e.to_compile_error())
|
|
.into()
|
|
}
|
|
|
|
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 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)?;
|
|
let runtime_metadata = generate_impl_runtime_metadata(api_impls)?;
|
|
|
|
let impl_ = quote!(
|
|
#base_runtime_api
|
|
|
|
#api_impls_for_runtime
|
|
|
|
#api_impls_for_runtime_api
|
|
|
|
#runtime_api_versions
|
|
|
|
#runtime_metadata
|
|
|
|
pub mod api {
|
|
use super::*;
|
|
|
|
#dispatch_impl
|
|
|
|
#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.
|
|
fn filter_cfg_attrs(attrs: &[Attribute]) -> Vec<Attribute> {
|
|
attrs.iter().filter(|a| a.path().is_ident("cfg")).cloned().collect()
|
|
}
|
|
|
|
// Extracts the value of `API_VERSION_ATTRIBUTE` and handles errors.
|
|
// Returns:
|
|
// - Err if the version is malformed
|
|
// - Some(u64) if the version is set
|
|
// - None if the version is not set (this is valid).
|
|
fn extract_api_version(attrs: &Vec<Attribute>, span: Span) -> Result<Option<u64>> {
|
|
// First fetch all `API_VERSION_ATTRIBUTE` values (should be only one)
|
|
let api_ver = attrs
|
|
.iter()
|
|
.filter(|a| a.path().is_ident(API_VERSION_ATTRIBUTE))
|
|
.collect::<Vec<_>>();
|
|
|
|
if api_ver.len() > 1 {
|
|
return Err(Error::new(
|
|
span,
|
|
format!(
|
|
"Found multiple #[{}] attributes for an API implementation. \
|
|
Each runtime API can have only one version.",
|
|
API_VERSION_ATTRIBUTE
|
|
),
|
|
))
|
|
}
|
|
|
|
// Parse the runtime version if there exists one.
|
|
api_ver.first().map(|v| parse_runtime_api_version(v)).transpose()
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn filter_non_cfg_attributes() {
|
|
let cfg_std: Attribute = parse_quote!(#[cfg(feature = "std")]);
|
|
let cfg_benchmarks: Attribute = parse_quote!(#[cfg(feature = "runtime-benchmarks")]);
|
|
|
|
let attrs = vec![
|
|
cfg_std.clone(),
|
|
parse_quote!(#[derive(Debug)]),
|
|
parse_quote!(#[test]),
|
|
cfg_benchmarks.clone(),
|
|
parse_quote!(#[allow(non_camel_case_types)]),
|
|
];
|
|
|
|
let filtered = filter_cfg_attrs(&attrs);
|
|
assert_eq!(filtered.len(), 2);
|
|
assert_eq!(cfg_std, filtered[0]);
|
|
assert_eq!(cfg_benchmarks, filtered[1]);
|
|
}
|
|
}
|