Reorganising the repository - external renames and moves (#4074)

* Adding first rough ouline of the repository structure

* Remove old CI stuff

* add title

* formatting fixes

* move node-exits job's script to scripts dir

* Move docs into subdir

* move to bin

* move maintainence scripts, configs and helpers into its own dir

* add .local to ignore

* move core->client

* start up 'test' area

* move test client

* move test runtime

* make test move compile

* Add dependencies rule enforcement.

* Fix indexing.

* Update docs to reflect latest changes

* Moving /srml->/paint

* update docs

* move client/sr-* -> primitives/

* clean old readme

* remove old broken code in rhd

* update lock

* Step 1.

* starting to untangle client

* Fix after merge.

* start splitting out client interfaces

* move children and blockchain interfaces

* Move trie and state-machine to primitives.

* Fix WASM builds.

* fixing broken imports

* more interface moves

* move backend and light to interfaces

* move CallExecutor

* move cli off client

* moving around more interfaces

* re-add consensus crates into the mix

* fix subkey path

* relieve client from executor

* starting to pull out client from grandpa

* move is_decendent_of out of client

* grandpa still depends on client directly

* lemme tests pass

* rename srml->paint

* Make it compile.

* rename interfaces->client-api

* Move keyring to primitives.

* fixup libp2p dep

* fix broken use

* allow dependency enforcement to fail

* move fork-tree

* Moving wasm-builder

* make env

* move build-script-utils

* fixup broken crate depdencies and names

* fix imports for authority discovery

* fix typo

* update cargo.lock

* fixing imports

* Fix paths and add missing crates

* re-add missing crates
This commit is contained in:
Benjamin Kampmann
2019-11-14 21:51:17 +01:00
committed by Bastian Köcher
parent becc3b0a4f
commit 60e5011c72
809 changed files with 7801 additions and 6464 deletions
@@ -0,0 +1,186 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Generates the bare function interface for a given trait definition.
//!
//! The bare functions are the ones that will be called by the user. On the native/host side, these
//! functions directly execute the provided implementation. On the wasm side, these
//! functions will prepare the parameters for the FFI boundary, call the external host function
//! exported into wasm and convert back the result.
//!
//! [`generate`](bare_function_interface::generate) is the entry point for generating for each
//! trait method one bare function.
//!
//! [`function_for_method`](bare_function_interface::function_for_method) generates the bare
//! function per trait method. Each bare function contains both implementations. The implementations
//! are feature-gated, so that one is compiled for the native and the other for the wasm side.
use crate::utils::{
generate_crate_access, create_exchangeable_host_function_ident, get_function_arguments,
get_function_argument_names, get_trait_methods,
};
use syn::{
Ident, ItemTrait, TraitItemMethod, FnArg, Signature, Result, spanned::Spanned, parse_quote,
};
use proc_macro2::{TokenStream, Span};
use quote::{quote, quote_spanned};
use std::iter;
/// Generate one bare function per trait method. The name of the bare function is equal to the name
/// of the trait method.
pub fn generate(trait_def: &ItemTrait, is_wasm_only: bool) -> Result<TokenStream> {
let trait_name = &trait_def.ident;
get_trait_methods(trait_def).try_fold(TokenStream::new(), |mut t, m| {
t.extend(function_for_method(trait_name, m, is_wasm_only)?);
Ok(t)
})
}
/// Generates the bare function implementation for the given method for the host and wasm side.
fn function_for_method(
trait_name: &Ident,
method: &TraitItemMethod,
is_wasm_only: bool,
) -> Result<TokenStream> {
let std_impl = function_std_impl(trait_name, method, is_wasm_only)?;
let no_std_impl = function_no_std_impl(method)?;
Ok(
quote! {
#std_impl
#no_std_impl
}
)
}
/// Generates the bare function implementation for `cfg(not(feature = "std"))`.
fn function_no_std_impl(method: &TraitItemMethod) -> Result<TokenStream> {
let function_name = &method.sig.ident;
let host_function_name = create_exchangeable_host_function_ident(&method.sig.ident);
let args = get_function_arguments(&method.sig);
let arg_names = get_function_argument_names(&method.sig);
let return_value = &method.sig.output;
let attrs = &method.attrs;
Ok(
quote! {
#[cfg(not(feature = "std"))]
#( #attrs )*
pub fn #function_name( #( #args, )* ) #return_value {
// Call the host function
#host_function_name.get()( #( #arg_names, )* )
}
}
)
}
/// Generates the bare function implementation for `cfg(feature = "std")`.
fn function_std_impl(
trait_name: &Ident,
method: &TraitItemMethod,
is_wasm_only: bool,
) -> Result<TokenStream> {
let function_name = &method.sig.ident;
let crate_ = generate_crate_access();
let args = get_function_arguments(&method.sig).map(FnArg::Typed).chain(
// Add the function context as last parameter when this is a wasm only interface.
iter::from_fn(||
if is_wasm_only {
Some(
parse_quote!(
mut __function_context__: &mut dyn #crate_::wasm_interface::FunctionContext
)
)
} else {
None
}
).take(1),
);
let return_value = &method.sig.output;
let attrs = &method.attrs;
// Don't make the function public accessible when this is a wasm only interface.
let vis = if is_wasm_only { quote!() } else { quote!(pub) };
let call_to_trait = generate_call_to_trait(trait_name, method, is_wasm_only);
Ok(
quote_spanned! { method.span() =>
#[cfg(feature = "std")]
#( #attrs )*
#vis fn #function_name( #( #args, )* ) #return_value {
#call_to_trait
}
}
)
}
/// Generate the call to the interface trait.
fn generate_call_to_trait(
trait_name: &Ident,
method: &TraitItemMethod,
is_wasm_only: bool,
) -> TokenStream {
let crate_ = generate_crate_access();
let method_name = &method.sig.ident;
let expect_msg = format!(
"`{}` called outside of an Externalities-provided environment.",
method_name,
);
let arg_names = get_function_argument_names(&method.sig);
if takes_self_argument(&method.sig) {
let instance = if is_wasm_only {
Ident::new("__function_context__", Span::call_site())
} else {
Ident::new("__externalities__", Span::call_site())
};
let impl_ = quote!( #trait_name::#method_name(&mut #instance, #( #arg_names, )*) );
if is_wasm_only {
quote_spanned! { method.span() => #impl_ }
} else {
quote_spanned! { method.span() =>
#crate_::with_externalities(|mut #instance| #impl_).expect(#expect_msg)
}
}
} else {
// The name of the trait the interface trait is implemented for
let impl_trait_name = if is_wasm_only {
quote!( #crate_::wasm_interface::FunctionContext )
} else {
quote!( #crate_::Externalities )
};
quote_spanned! { method.span() =>
<&mut dyn #impl_trait_name as #trait_name>::#method_name(
#( #arg_names, )*
)
}
}
}
/// Returns if the given `Signature` takes a `self` argument.
fn takes_self_argument(sig: &Signature) -> bool {
match sig.inputs.first() {
Some(FnArg::Receiver(_)) => true,
_ => false,
}
}
@@ -0,0 +1,415 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Generates the extern host functions and the implementation for these host functions.
//!
//! The extern host functions will be called by the bare function interface from the Wasm side.
//! The implementation of these host functions will be called on the host side from the Wasm
//! executor. These implementations call the bare function interface.
use crate::utils::{
generate_crate_access, create_host_function_ident, get_function_argument_names,
get_function_argument_types_without_ref, get_function_argument_types_ref_and_mut,
get_function_argument_names_and_types_without_ref, get_trait_methods, get_function_arguments,
get_function_argument_types, create_exchangeable_host_function_ident,
};
use syn::{
ItemTrait, TraitItemMethod, Result, ReturnType, Ident, TraitItem, Pat, Error, Signature,
spanned::Spanned,
};
use proc_macro2::{TokenStream, Span};
use quote::{quote, ToTokens};
use inflector::Inflector;
use std::iter::{Iterator, self};
/// Generate the extern host functions for wasm and the `HostFunctions` struct that provides the
/// implementations for the host functions on the host.
pub fn generate(trait_def: &ItemTrait, is_wasm_only: bool) -> Result<TokenStream> {
let trait_name = &trait_def.ident;
let extern_host_function_impls = get_trait_methods(trait_def)
.try_fold(TokenStream::new(), |mut t, m| {
t.extend(generate_extern_host_function(m, trait_name)?);
Ok::<_, Error>(t)
})?;
let exchangeable_host_functions = get_trait_methods(trait_def)
.try_fold(TokenStream::new(), |mut t, m| {
t.extend(generate_exchangeable_host_function(m)?);
Ok::<_, Error>(t)
})?;
let host_functions_struct = generate_host_functions_struct(trait_def, is_wasm_only)?;
Ok(
quote! {
/// The implementations of the extern host functions. This special implementation module
/// is required to change the extern host functions signature to
/// `unsafe fn name(args) -> ret` to make the function implementations exchangeable.
#[cfg(not(feature = "std"))]
mod extern_host_function_impls {
use super::*;
#extern_host_function_impls
}
#exchangeable_host_functions
#host_functions_struct
}
)
}
/// Generate the extern host function for the given method.
fn generate_extern_host_function(method: &TraitItemMethod, trait_name: &Ident) -> Result<TokenStream> {
let crate_ = generate_crate_access();
let args = get_function_arguments(&method.sig);
let arg_types = get_function_argument_types_without_ref(&method.sig);
let arg_types2 = get_function_argument_types_without_ref(&method.sig);
let arg_names = get_function_argument_names(&method.sig);
let arg_names2 = get_function_argument_names(&method.sig);
let arg_names3 = get_function_argument_names(&method.sig);
let function = &method.sig.ident;
let ext_function = create_host_function_ident(&method.sig.ident, trait_name);
let doc_string = format!(
" Default extern host function implementation for [`super::{}`].",
method.sig.ident,
);
let return_value = &method.sig.output;
let ffi_return_value = match method.sig.output {
ReturnType::Default => quote!(),
ReturnType::Type(_, ref ty) => quote! {
-> <#ty as #crate_::RIType>::FFIType
},
};
let convert_return_value = match return_value {
ReturnType::Default => quote!(),
ReturnType::Type(_, ref ty) => quote! {
<#ty as #crate_::wasm::FromFFIValue>::from_ffi_value(result)
}
};
Ok(
quote! {
#[doc = #doc_string]
pub fn #function ( #( #args ),* ) #return_value {
extern "C" {
/// The extern function.
pub fn #ext_function (
#( #arg_names: <#arg_types as #crate_::RIType>::FFIType ),*
) #ffi_return_value;
}
// Generate all wrapped ffi values.
#(
let #arg_names2 = <#arg_types2 as #crate_::wasm::IntoFFIValue>::into_ffi_value(
&#arg_names2,
);
)*
let result = unsafe { #ext_function( #( #arg_names3.get() ),* ) };
#convert_return_value
}
}
)
}
/// Generate the host exchangeable function for the given method.
fn generate_exchangeable_host_function(method: &TraitItemMethod) -> Result<TokenStream> {
let crate_ = generate_crate_access();
let arg_types = get_function_argument_types(&method.sig);
let function = &method.sig.ident;
let exchangeable_function = create_exchangeable_host_function_ident(&method.sig.ident);
let doc_string = format!(" Exchangeable host function used by [`{}`].", method.sig.ident);
let output = &method.sig.output;
Ok(
quote! {
#[cfg(not(feature = "std"))]
#[allow(non_upper_case_globals)]
#[doc = #doc_string]
pub static #exchangeable_function : #crate_::wasm::ExchangeableFunction<
fn ( #( #arg_types ),* ) #output
> = #crate_::wasm::ExchangeableFunction::new(extern_host_function_impls::#function);
}
)
}
/// Generate the `HostFunctions` struct that implements `wasm-interface::HostFunctions` to provide
/// implementations for the extern host functions.
fn generate_host_functions_struct(trait_def: &ItemTrait, is_wasm_only: bool) -> Result<TokenStream> {
let crate_ = generate_crate_access();
let host_functions = trait_def
.items
.iter()
.filter_map(|i| match i {
TraitItem::Method(ref method) => Some(method),
_ => None,
})
.map(|m| generate_host_function_implementation(&trait_def.ident, m, is_wasm_only))
.collect::<Result<Vec<_>>>()?;
Ok(
quote! {
/// Provides implementations for the extern host functions.
#[cfg(feature = "std")]
pub struct HostFunctions;
#[cfg(feature = "std")]
impl #crate_::wasm_interface::HostFunctions for HostFunctions {
fn host_functions() -> Vec<&'static dyn #crate_::wasm_interface::Function> {
vec![ #( #host_functions ),* ]
}
}
}
)
}
/// Generates the host function struct that implements `wasm_interface::Function` and returns a static
/// reference to this struct.
///
/// When calling from wasm into the host, we will call the `execute` function that calls the native
/// implementation of the function.
fn generate_host_function_implementation(
trait_name: &Ident,
method: &TraitItemMethod,
is_wasm_only: bool,
) -> Result<TokenStream> {
let name = create_host_function_ident(&method.sig.ident, trait_name).to_string();
let struct_name = Ident::new(&name.to_pascal_case(), Span::call_site());
let crate_ = generate_crate_access();
let signature = generate_wasm_interface_signature_for_host_function(&method.sig)?;
let wasm_to_ffi_values = generate_wasm_to_ffi_values(
&method.sig,
trait_name,
).collect::<Result<Vec<_>>>()?;
let ffi_to_host_values = generate_ffi_to_host_value(&method.sig).collect::<Result<Vec<_>>>()?;
let host_function_call = generate_host_function_call(&method.sig, is_wasm_only);
let into_preallocated_ffi_value = generate_into_preallocated_ffi_value(&method.sig)?;
let convert_return_value = generate_return_value_into_wasm_value(&method.sig);
Ok(
quote! {
{
struct #struct_name;
#[allow(unused)]
impl #crate_::wasm_interface::Function for #struct_name {
fn name(&self) -> &str {
#name
}
fn signature(&self) -> #crate_::wasm_interface::Signature {
#signature
}
fn execute(
&self,
__function_context__: &mut dyn #crate_::wasm_interface::FunctionContext,
args: &mut dyn Iterator<Item = #crate_::wasm_interface::Value>,
) -> std::result::Result<Option<#crate_::wasm_interface::Value>, String> {
#( #wasm_to_ffi_values )*
#( #ffi_to_host_values )*
#host_function_call
#into_preallocated_ffi_value
#convert_return_value
}
}
&#struct_name as &dyn #crate_::wasm_interface::Function
}
}
)
}
/// Generate the `wasm_interface::Signature` for the given host function `sig`.
fn generate_wasm_interface_signature_for_host_function(sig: &Signature) -> Result<TokenStream> {
let crate_ = generate_crate_access();
let return_value = match &sig.output {
ReturnType::Type(_, ty) =>
quote! {
Some( <<#ty as #crate_::RIType>::FFIType as #crate_::wasm_interface::IntoValue>::VALUE_TYPE )
},
ReturnType::Default => quote!( None ),
};
let arg_types = get_function_argument_types_without_ref(sig)
.map(|ty| quote! {
<<#ty as #crate_::RIType>::FFIType as #crate_::wasm_interface::IntoValue>::VALUE_TYPE
});
Ok(
quote! {
#crate_::wasm_interface::Signature {
args: std::borrow::Cow::Borrowed(&[ #( #arg_types ),* ][..]),
return_value: #return_value,
}
}
)
}
/// Generate the code that converts the wasm values given to `HostFunctions::execute` into the FFI
/// values.
fn generate_wasm_to_ffi_values<'a>(
sig: &'a Signature,
trait_name: &'a Ident,
) -> impl Iterator<Item = Result<TokenStream>> + 'a {
let crate_ = generate_crate_access();
let function_name = &sig.ident;
let error_message = format!(
"Number of arguments given to `{}` does not match the expected number of arguments!",
function_name,
);
get_function_argument_names_and_types_without_ref(sig)
.map(move |(name, ty)| {
let try_from_error = format!(
"Could not instantiate `{}` from wasm value while executing `{}` from interface `{}`!",
name.to_token_stream(),
function_name,
trait_name,
);
let var_name = generate_ffi_value_var_name(&name)?;
Ok(quote! {
let val = args.next().ok_or_else(|| #error_message)?;
let #var_name = <
<#ty as #crate_::RIType>::FFIType as #crate_::wasm_interface::TryFromValue
>::try_from_value(val).ok_or_else(|| #try_from_error)?;
})
})
}
/// Generate the code to convert the ffi values on the host to the host values using `FromFFIValue`.
fn generate_ffi_to_host_value<'a>(
sig: &'a Signature,
) -> impl Iterator<Item = Result<TokenStream>> + 'a {
let mut_access = get_function_argument_types_ref_and_mut(sig);
let crate_ = generate_crate_access();
get_function_argument_names_and_types_without_ref(sig)
.zip(mut_access.map(|v| v.and_then(|m| m.1)))
.map(move |((name, ty), mut_access)| {
let ffi_value_var_name = generate_ffi_value_var_name(&name)?;
Ok(
quote! {
let #mut_access #name = <#ty as #crate_::host::FromFFIValue>::from_ffi_value(
__function_context__,
#ffi_value_var_name,
)?;
}
)
})
}
/// Generate the code to call the host function and the ident that stores the result.
fn generate_host_function_call(sig: &Signature, is_wasm_only: bool) -> TokenStream {
let host_function_name = &sig.ident;
let result_var_name = generate_host_function_result_var_name(&sig.ident);
let ref_and_mut = get_function_argument_types_ref_and_mut(sig).map(|ram|
ram.map(|(vr, vm)| quote!(#vr #vm))
);
let names = get_function_argument_names(sig);
let var_access = names.zip(ref_and_mut)
.map(|(n, ref_and_mut)| {
quote!( #ref_and_mut #n )
})
// If this is a wasm only interface, we add the function context as last parameter.
.chain(
iter::from_fn(|| if is_wasm_only { Some(quote!(__function_context__)) } else { None })
.take(1)
);
quote! {
let #result_var_name = #host_function_name ( #( #var_access ),* );
}
}
/// Generate the variable name that stores the result of the host function.
fn generate_host_function_result_var_name(name: &Ident) -> Ident {
Ident::new(&format!("{}_result", name), Span::call_site())
}
/// Generate the variable name that stores the FFI value.
fn generate_ffi_value_var_name(pat: &Pat) -> Result<Ident> {
match pat {
Pat::Ident(pat_ident) => {
if let Some(by_ref) = pat_ident.by_ref {
Err(Error::new(by_ref.span(), "`ref` not supported!"))
} else if let Some(sub_pattern) = &pat_ident.subpat {
Err(Error::new(sub_pattern.0.span(), "Not supported!"))
} else {
Ok(Ident::new(&format!("{}_ffi_value", pat_ident.ident), Span::call_site()))
}
}
_ => Err(Error::new(pat.span(), "Not supported as variable name!"))
}
}
/// Generate code that copies data from the host back to preallocated wasm memory.
///
/// Any argument that is given as `&mut` is interpreted as preallocated memory and it is expected
/// that the type implements `IntoPreAllocatedFFIValue`.
fn generate_into_preallocated_ffi_value(sig: &Signature) -> Result<TokenStream> {
let crate_ = generate_crate_access();
let ref_and_mut = get_function_argument_types_ref_and_mut(sig).map(|ram|
ram.and_then(|(vr, vm)| vm.map(|v| (vr, v)))
);
let names_and_types = get_function_argument_names_and_types_without_ref(sig);
ref_and_mut.zip(names_and_types)
.filter_map(|(ram, (name, ty))| ram.map(|_| (name, ty)))
.map(|(name, ty)| {
let ffi_var_name = generate_ffi_value_var_name(&name)?;
Ok(
quote! {
<#ty as #crate_::host::IntoPreallocatedFFIValue>::into_preallocated_ffi_value(
#name,
__function_context__,
#ffi_var_name,
)?;
}
)
})
.collect()
}
/// Generate the code that converts the return value into the appropriate wasm value.
fn generate_return_value_into_wasm_value(sig: &Signature) -> TokenStream {
let crate_ = generate_crate_access();
match &sig.output {
ReturnType::Default => quote!( Ok(None) ),
ReturnType::Type(_, ty) => {
let result_var_name = generate_host_function_result_var_name(&sig.ident);
quote! {
<#ty as #crate_::host::IntoFFIValue>::into_ffi_value(
#result_var_name,
__function_context__,
).map(#crate_::wasm_interface::IntoValue::into_value).map(Some)
}
}
}
}
@@ -0,0 +1,65 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use crate::utils::generate_runtime_interface_include;
use proc_macro2::{Span, TokenStream};
use syn::{Ident, ItemTrait, Result};
use inflector::Inflector;
use quote::quote;
mod bare_function_interface;
mod host_function_interface;
mod trait_decl_impl;
/// Custom keywords supported by the `runtime_interface` attribute.
pub mod keywords {
// Custom keyword `wasm_only` that can be given as attribute to [`runtime_interface`].
syn::custom_keyword!(wasm_only);
}
/// Implementation of the `runtime_interface` attribute.
///
/// It expects the trait definition the attribute was put above and if this should be an wasm only
/// interface.
pub fn runtime_interface_impl(trait_def: ItemTrait, is_wasm_only: bool) -> Result<TokenStream> {
let bare_functions = bare_function_interface::generate(&trait_def, is_wasm_only)?;
let crate_include = generate_runtime_interface_include();
let mod_name = Ident::new(&trait_def.ident.to_string().to_snake_case(), Span::call_site());
let trait_decl_impl = trait_decl_impl::process(&trait_def, is_wasm_only)?;
let host_functions = host_function_interface::generate(&trait_def, is_wasm_only)?;
let vis = trait_def.vis;
let attrs = &trait_def.attrs;
let res = quote! {
#( #attrs )*
#vis mod #mod_name {
use super::*;
#crate_include
#bare_functions
#trait_decl_impl
#host_functions
}
};
Ok(res)
}
@@ -0,0 +1,146 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Checks the trait declaration, makes the trait declaration module local, removes all method
//! default implementations and implements the trait for `&mut dyn Externalities`.
use crate::utils::{generate_crate_access, get_function_argument_types_without_ref};
use syn::{
ItemTrait, TraitItemMethod, Result, TraitItem, Error, fold::{self, Fold}, spanned::Spanned,
Visibility, Receiver, Type, Generics,
};
use proc_macro2::TokenStream;
use quote::quote;
/// Process the given trait definition, by checking that the definition is valid, fold it to the
/// essential definition and implement this essential definition for `dyn Externalities`.
pub fn process(trait_def: &ItemTrait, is_wasm_only: bool) -> Result<TokenStream> {
let impl_trait = impl_trait_for_externalities(trait_def, is_wasm_only)?;
let essential_trait_def = ToEssentialTraitDef::convert(trait_def.clone())?;
Ok(
quote! {
#impl_trait
#essential_trait_def
}
)
}
/// Converts the given trait definition into the essential trait definition without method
/// default implementations and visibility set to inherited.
struct ToEssentialTraitDef {
/// All errors found while doing the conversion.
errors: Vec<Error>,
}
impl ToEssentialTraitDef {
/// Convert the given trait definition to the essential trait definition.
fn convert(trait_def: ItemTrait) -> Result<ItemTrait> {
let mut folder = ToEssentialTraitDef {
errors: Vec::new(),
};
let res = folder.fold_item_trait(trait_def);
if let Some(first_error) = folder.errors.pop() {
Err(
folder.errors.into_iter().fold(first_error, |mut o, n| {
o.combine(n);
o
})
)
} else {
Ok(res)
}
}
fn push_error<S: Spanned>(&mut self, span: &S, msg: &str) {
self.errors.push(Error::new(span.span(), msg));
}
fn error_on_generic_parameters(&mut self, generics: &Generics) {
if let Some(param) = generics.params.first() {
self.push_error(param, "Generic parameters not supported.");
}
}
}
impl Fold for ToEssentialTraitDef {
fn fold_trait_item_method(&mut self, mut method: TraitItemMethod) -> TraitItemMethod {
if method.default.take().is_none() {
self.push_error(&method, "Methods need to have an implementation.");
}
let arg_types = get_function_argument_types_without_ref(&method.sig);
arg_types.filter_map(|ty|
match *ty {
Type::ImplTrait(impl_trait) => Some(impl_trait),
_ => None
}
).for_each(|invalid| self.push_error(&invalid, "`impl Trait` syntax not supported."));
self.error_on_generic_parameters(&method.sig.generics);
fold::fold_trait_item_method(self, method)
}
fn fold_item_trait(&mut self, mut trait_def: ItemTrait) -> ItemTrait {
self.error_on_generic_parameters(&trait_def.generics);
trait_def.vis = Visibility::Inherited;
fold::fold_item_trait(self, trait_def)
}
fn fold_receiver(&mut self, receiver: Receiver) -> Receiver {
if receiver.reference.is_none() {
self.push_error(&receiver, "Taking `Self` by value is not allowed.");
}
fold::fold_receiver(self, receiver)
}
}
/// Implements the given trait definition for `dyn Externalities`.
fn impl_trait_for_externalities(trait_def: &ItemTrait, is_wasm_only: bool) -> Result<TokenStream> {
let trait_ = &trait_def.ident;
let crate_ = generate_crate_access();
let methods = trait_def
.items
.iter()
.filter_map(|i| match i {
TraitItem::Method(ref method) => Some(method),
_ => None,
});
let impl_type = if is_wasm_only {
quote!( &mut dyn #crate_::wasm_interface::FunctionContext )
} else {
quote!( &mut dyn #crate_::Externalities )
};
Ok(
quote! {
#[cfg(feature = "std")]
impl #trait_ for #impl_type {
#( #methods )*
}
}
)
}