mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-27 15:07:59 +00:00
Substrate runtime interface 2.0 (#4057)
* Adds first version of traits for generating the host functions * First steps of the procedural macro * Implements generation of the host extern functions * Prefix ext host function with snake case trait name * Implement host functions implementation on the host * Change `HostFunctions` interface * Implement `HostFunctions` for tuples * Make `WasmExecutor` generic over the host functions * Begin to add a test and make it compile * Make the test succeed * Add test to ensure that host functions are not found * It's alive! Make the `set_storage` test work * Add test for mutable references * Code cleanup and documentation etc * Add marker trait for types that should be passed as SCALE encoded * Inherit the visibility from the trait and more improvements * More impls and move them into their own file * Code simplification by dropping one trait * Give it a better name * Implement traits for arrays * Refactor code to support pass by codec/inner * Docs * Implement pass by inner for some crypto types and add a test * Implement exchangeable function support * Rewrite sr-io with as runtime interface * Start reworking after master merge * Adds `PassByCodec` derive * Adds `PassByInner` derive * Fix compilation errors * More implementations * Implement runtime interface traits for `str` * Make `sr-io` compile again * Fix more compilation errors * More progress on getting stuff back to compile * More compilation fixes * Fix warnings * Remove le conversions * Add support for `wasm_only` interfaces * Implement `Allocator` interface * Improve error message * Move `WasmAllocator` to `sr-io` and more clean ups * Use correct function signature for wasm functions * Store the host functions with the Wasm runtime * Docs update * Fix compilation after master merge * Remove `sr-io/without_std` * Make `srml-support` tests run again * More compilation error fixes * Use correct doc syntax * Fix test-runtime * Fix compilation * Catch native panics when executing the wasm runtime As with the native runtime, we now catch all native panics when we execute the wasm runtime. The panics inside the wasm runtime were already catched before by the wasm executor automatically, but any panic in the host functions could bring down the node. The recent switch to execute the native counterpart of the host function in `sr-io`, makes this change required. The native `sr-io` functions just `panic` when something is not provided or any other error occured. * Fix compilation * Don't panic in a panic * Move `sr-sandbox` to new runtime interface * Fixes tests after sandbox changes * Make sure we detect invalid utf8 * Fixes after master merge * Adds pass by enum strategy * Fix wasmtime integration * Some macro structure clean up * Rework and test exchangebale host functions * PassBy derive macros documentation * Docs for `runtime_interface` macro * Support wild card argument names * Adds ui tests * Make sure that we are backwards compatible to the old runtime interfaces * Documentation * Fixes after latest master merge * Make `wasmtime` happy * Make `full_crypto` work * Make the new interface versionable * Rename `Sanboxing` to `Sandbox` * Don't finalize in test while importing * Fix Performance regression * Fix test
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
[package]
|
||||
name = "substrate-runtime-interface-proc-macro"
|
||||
version = "2.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = { version = "1.0.5", features = [ "full", "visit", "fold", "extra-traits" ] }
|
||||
quote = "1.0.2"
|
||||
proc-macro2 = "1.0.3"
|
||||
Inflector = "0.11.4"
|
||||
proc-macro-crate = "0.1.4"
|
||||
|
||||
[dev-dependencies]
|
||||
runtime-interface = { package = "substrate-runtime-interface", path = ".." }
|
||||
codec = { package = "parity-scale-codec", version = "1.0.6", features = [ "derive" ] }
|
||||
externalities = { package = "substrate-externalities", path = "../../externalities" }
|
||||
rustversion = "1.0.0"
|
||||
trybuild = "1.0.17"
|
||||
|
||||
# We actually don't need the `std` feature in this crate, but the tests require it.
|
||||
[features]
|
||||
default = [ "std" ]
|
||||
std = []
|
||||
@@ -0,0 +1,260 @@
|
||||
// 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/>.
|
||||
|
||||
//! This crate provides procedural macros for usage within the context of the Substrate runtime
|
||||
//! interface.
|
||||
//!
|
||||
//! The following macros are provided:
|
||||
//!
|
||||
//! 1. The [`#[runtime_interface]`](attr.runtime_interface.html) attribute macro for generating the
|
||||
//! runtime interfaces.
|
||||
//! 2. The [`PassByCodec`](derive.PassByCodec.html) derive macro for implementing `PassBy` with `Codec`.
|
||||
//! 3. The [`PassByEnum`](derive.PassByInner.html) derive macro for implementing `PassBy` with `Enum`.
|
||||
//! 4. The [`PassByInner`](derive.PassByInner.html) derive macro for implementing `PassBy` with `Inner`.
|
||||
|
||||
extern crate proc_macro;
|
||||
|
||||
use syn::{parse_macro_input, ItemTrait, DeriveInput};
|
||||
|
||||
mod pass_by;
|
||||
mod runtime_interface;
|
||||
mod utils;
|
||||
|
||||
/// Attribute macro for transforming a trait declaration into a runtime interface.
|
||||
///
|
||||
/// A runtime interface is a fixed interface between a Substrate compatible runtime and the native
|
||||
/// node. This interface is callable from a native and a wasm runtime. The macro will generate the
|
||||
/// corresponding code for the native implementation and the code for calling from the wasm
|
||||
/// side to the native implementation.
|
||||
///
|
||||
/// The macro expects the runtime interface declaration as trait declaration:
|
||||
///
|
||||
/// ```
|
||||
/// # use runtime_interface::runtime_interface;
|
||||
///
|
||||
/// #[runtime_interface]
|
||||
/// trait Interface {
|
||||
/// /// A function that can be called from native/wasm.
|
||||
/// ///
|
||||
/// /// The implementation given to this function is only compiled on native.
|
||||
/// fn call_some_complex_code(data: &[u8]) -> Vec<u8> {
|
||||
/// // Here you could call some rather complex code that only compiles on native or
|
||||
/// // is way faster in native than executing it in wasm.
|
||||
/// Vec::new()
|
||||
/// }
|
||||
///
|
||||
/// /// A function can take a `&self` or `&mut self` argument to get access to the
|
||||
/// /// `Externalities`. (The generated method does not require
|
||||
/// /// this argument, so the function can be called just with the `optional` argument)
|
||||
/// fn set_or_clear(&mut self, optional: Option<Vec<u8>>) {
|
||||
/// match optional {
|
||||
/// Some(value) => self.set_storage([1, 2, 3, 4].to_vec(), value),
|
||||
/// None => self.clear_storage(&[1, 2, 3, 4]),
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// The given example will generate roughly the following code for native:
|
||||
///
|
||||
/// ```
|
||||
/// // The name of the trait is converted to snake case and used as mod name.
|
||||
/// //
|
||||
/// // Be aware that this module is not `public`, the visibility of the module is determined based
|
||||
/// // on the visibility of the trait declaration.
|
||||
/// mod interface {
|
||||
/// trait Interface {
|
||||
/// fn call_some_complex_code(data: &[u8]) -> Vec<u8>;
|
||||
/// fn set_or_clear(&mut self, optional: Option<Vec<u8>>);
|
||||
/// }
|
||||
///
|
||||
/// impl Interface for &mut dyn externalities::Externalities {
|
||||
/// fn call_some_complex_code(data: &[u8]) -> Vec<u8> { Vec::new() }
|
||||
/// fn set_or_clear(&mut self, optional: Option<Vec<u8>>) {
|
||||
/// match optional {
|
||||
/// Some(value) => self.set_storage([1, 2, 3, 4].to_vec(), value),
|
||||
/// None => self.clear_storage(&[1, 2, 3, 4]),
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// pub fn call_some_complex_code(data: &[u8]) -> Vec<u8> {
|
||||
/// <&mut dyn externalities::Externalities as Interface>::call_some_complex_code(data)
|
||||
/// }
|
||||
///
|
||||
/// pub fn set_or_clear(optional: Option<Vec<u8>>) {
|
||||
/// externalities::with_externalities(|mut ext| Interface::set_or_clear(&mut ext, optional))
|
||||
/// .expect("`set_or_clear` called outside of an Externalities-provided environment.")
|
||||
/// }
|
||||
///
|
||||
/// /// This type implements the `HostFunctions` trait (from `substrate-wasm-interface`) and
|
||||
/// /// provides the host implementation for the wasm side. The host implementation converts the
|
||||
/// /// arguments from wasm to native and calls the corresponding native function.
|
||||
/// ///
|
||||
/// /// This type needs to be passed to the wasm executor, so that the host functions will be
|
||||
/// /// registered in the executor.
|
||||
/// pub struct HostFunctions;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// The given example will generate roughly the following code for wasm:
|
||||
///
|
||||
/// ```
|
||||
/// mod interface {
|
||||
/// mod extern_host_functions_impls {
|
||||
/// extern "C" {
|
||||
/// /// Every function is exported as `ext_TRAIT_NAME_FUNCTION_NAME_version_VERSION`.
|
||||
/// ///
|
||||
/// /// The type for each argument of the exported function depends on
|
||||
/// /// `<ARGUMENT_TYPE as RIType>::FFIType`.
|
||||
/// pub fn ext_Interface_call_some_complex_code_version_1(data: u64);
|
||||
/// pub fn ext_Interface_set_or_clear_version_1(optional: u64);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// /// The type is actually `ExchangeableFunction` (from `substrate-runtime-interface`).
|
||||
/// ///
|
||||
/// /// This can be used to replace the implementation of the `call_some_complex_code` function.
|
||||
/// /// Instead of calling into the host, the callee will automatically call the other
|
||||
/// /// implementation.
|
||||
/// ///
|
||||
/// /// To replace the implementation:
|
||||
/// ///
|
||||
/// /// `host_call_some_complex_code.replace_implementation(some_other_impl)`
|
||||
/// pub static host_call_some_complex_code: () = ();
|
||||
/// pub static host_set_or_clear: () = ();
|
||||
///
|
||||
/// pub fn call_some_complex_code(data: &[u8]) -> Vec<u8> {
|
||||
/// // This is the actual call: `host_call_some_complex_code.get()(data)`
|
||||
/// //
|
||||
/// // But that does not work for several reasons in this example, so we just return an
|
||||
/// // empty vector.
|
||||
/// Vec::new()
|
||||
/// }
|
||||
///
|
||||
/// pub fn set_or_clear(optional: Option<Vec<u8>>) {
|
||||
/// // Same as above
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Argument types
|
||||
///
|
||||
/// The macro supports any kind of argument type, as long as it implements `RIType` and the required
|
||||
/// `FromFFIValue`/`IntoFFIValue` from `substrate-runtime-interface`. The macro will convert each
|
||||
/// argument to the corresponding FFI representation and will call into the host using this FFI
|
||||
/// representation. On the host each argument is converted back to the native representation and
|
||||
/// the native implementation is called. Any return value is handled in the same way.
|
||||
///
|
||||
/// # Wasm only interfaces
|
||||
///
|
||||
/// Some interfaces are only required from within the wasm runtime e.g. the allocator interface.
|
||||
/// To support this, the macro can be called like `#[runtime_interface(wasm_only)]`. This instructs
|
||||
/// the macro to make two significant changes to the generated code:
|
||||
///
|
||||
/// 1. The generated functions are not callable from the native side.
|
||||
/// 2. The trait as shown above is not implemented for `Externalities` and is instead implemented
|
||||
/// for `FunctionExecutor` (from `substrate-wasm-interface`).
|
||||
#[proc_macro_attribute]
|
||||
pub fn runtime_interface(
|
||||
attrs: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
let trait_def = parse_macro_input!(input as ItemTrait);
|
||||
let wasm_only = parse_macro_input!(attrs as Option<runtime_interface::keywords::wasm_only>);
|
||||
|
||||
runtime_interface::runtime_interface_impl(trait_def, wasm_only.is_some())
|
||||
.unwrap_or_else(|e| e.to_compile_error())
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Derive macro for implementing `PassBy` with the `Codec` strategy.
|
||||
///
|
||||
/// This requires that the type implements `Encode` and `Decode` from `parity-scale-codec`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use runtime_interface::pass_by::PassByCodec;
|
||||
/// # use codec::{Encode, Decode};
|
||||
/// #[derive(PassByCodec, Encode, Decode)]
|
||||
/// struct EncodableType {
|
||||
/// name: Vec<u8>,
|
||||
/// param: u32,
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_derive(PassByCodec)]
|
||||
pub fn pass_by_codec(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
pass_by::codec_derive_impl(input).unwrap_or_else(|e| e.to_compile_error()).into()
|
||||
}
|
||||
|
||||
/// Derive macro for implementing `PassBy` with the `Inner` strategy.
|
||||
///
|
||||
/// Besides implementing `PassBy`, this derive also implements the helper trait `PassByInner`.
|
||||
///
|
||||
/// The type is required to be a struct with just one field. The field type needs to implement
|
||||
/// the required traits to pass it between the wasm and the native side. (See the runtime interface
|
||||
/// crate for more information about these traits.)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use runtime_interface::pass_by::PassByInner;
|
||||
/// #[derive(PassByInner)]
|
||||
/// struct Data([u8; 32]);
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// # use runtime_interface::pass_by::PassByInner;
|
||||
/// #[derive(PassByInner)]
|
||||
/// struct Data {
|
||||
/// data: [u8; 32],
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_derive(PassByInner)]
|
||||
pub fn pass_by_inner(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
pass_by::inner_derive_impl(input).unwrap_or_else(|e| e.to_compile_error()).into()
|
||||
}
|
||||
|
||||
/// Derive macro for implementing `PassBy` with the `Enum` strategy.
|
||||
///
|
||||
/// Besides implementing `PassBy`, this derive also implements `TryFrom<u8>` and `From<Self> for u8`
|
||||
/// for the type.
|
||||
///
|
||||
/// The type is required to be an enum with only unit variants and at maximum `256` variants. Also
|
||||
/// it is required that the type implements `Copy`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use runtime_interface::pass_by::PassByEnum;
|
||||
/// #[derive(PassByEnum, Copy, Clone)]
|
||||
/// enum Data {
|
||||
/// Okay,
|
||||
/// NotOkay,
|
||||
/// // This will not work with the derive.
|
||||
/// //Why(u32),
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_derive(PassByEnum)]
|
||||
pub fn pass_by_enum(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
pass_by::enum_derive_impl(input).unwrap_or_else(|e| e.to_compile_error()).into()
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// 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/>.
|
||||
|
||||
//! Derive macro implementation of `PassBy` with the associated type set to `Codec`.
|
||||
//!
|
||||
//! It is required that the type implements `Encode` and `Decode` from the `parity-scale-codec`
|
||||
//! crate.
|
||||
|
||||
use crate::utils::{generate_crate_access, generate_runtime_interface_include};
|
||||
|
||||
use syn::{DeriveInput, Result, Generics, parse_quote};
|
||||
|
||||
use quote::quote;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
/// The derive implementation for `PassBy` with `Codec`.
|
||||
pub fn derive_impl(mut input: DeriveInput) -> Result<TokenStream> {
|
||||
add_trait_bounds(&mut input.generics);
|
||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
let crate_include = generate_runtime_interface_include();
|
||||
let crate_ = generate_crate_access();
|
||||
let ident = input.ident;
|
||||
|
||||
let res = quote! {
|
||||
const _: () = {
|
||||
#crate_include
|
||||
|
||||
impl #impl_generics #crate_::pass_by::PassBy for #ident #ty_generics #where_clause {
|
||||
type PassBy = #crate_::pass_by::Codec<#ident>;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Add the `codec::Codec` trait bound to every type parameter.
|
||||
fn add_trait_bounds(generics: &mut Generics) {
|
||||
let crate_ = generate_crate_access();
|
||||
|
||||
generics.type_params_mut()
|
||||
.for_each(|type_param| type_param.bounds.push(parse_quote!(#crate_::codec::Codec)));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
// 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/>.
|
||||
|
||||
//! Derive macro implementation of `PassBy` with the associated type set to `Enum`.
|
||||
//!
|
||||
//! Besides `PassBy`, `TryFrom<u8>` and `From<Self> for u8` are implemented for the type.
|
||||
|
||||
use crate::utils::{generate_crate_access, generate_runtime_interface_include};
|
||||
|
||||
use syn::{DeriveInput, Result, Data, Fields, Error, Ident};
|
||||
|
||||
use quote::quote;
|
||||
|
||||
use proc_macro2::{TokenStream, Span};
|
||||
|
||||
/// The derive implementation for `PassBy` with `Enum`.
|
||||
pub fn derive_impl(input: DeriveInput) -> Result<TokenStream> {
|
||||
let crate_include = generate_runtime_interface_include();
|
||||
let crate_ = generate_crate_access();
|
||||
let ident = input.ident;
|
||||
let enum_fields = get_enum_field_idents(&input.data)?
|
||||
.enumerate()
|
||||
.map(|(i, v)| {
|
||||
let i = i as u8;
|
||||
|
||||
v.map(|v| (quote!(#i => Ok(#ident::#v)), quote!(#ident::#v => #i)))
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
let try_from_variants = enum_fields.iter().map(|i| &i.0);
|
||||
let into_variants = enum_fields.iter().map(|i| &i.1);
|
||||
|
||||
let res = quote! {
|
||||
const _: () = {
|
||||
#crate_include
|
||||
|
||||
impl #crate_::pass_by::PassBy for #ident {
|
||||
type PassBy = #crate_::pass_by::Enum<#ident>;
|
||||
}
|
||||
|
||||
impl #crate_::rstd::convert::TryFrom<u8> for #ident {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(inner: u8) -> #crate_::rstd::result::Result<Self, ()> {
|
||||
match inner {
|
||||
#( #try_from_variants, )*
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<#ident> for u8 {
|
||||
fn from(var: #ident) -> u8 {
|
||||
match var {
|
||||
#( #into_variants ),*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Get the enum fields idents of the given `data` object as iterator.
|
||||
///
|
||||
/// Returns an error if the number of variants is greater than `256`, the given `data` is not an
|
||||
/// enum or a variant is not an unit.
|
||||
fn get_enum_field_idents<'a>(data: &'a Data) -> Result<impl Iterator<Item = Result<&'a Ident>>> {
|
||||
match data {
|
||||
Data::Enum(d) => {
|
||||
if d.variants.len() <= 256 {
|
||||
Ok(
|
||||
d.variants.iter().map(|v| if let Fields::Unit = v.fields {
|
||||
Ok(&v.ident)
|
||||
} else {
|
||||
Err(Error::new(
|
||||
Span::call_site(),
|
||||
"`PassByEnum` only supports unit variants.",
|
||||
))
|
||||
})
|
||||
)
|
||||
} else {
|
||||
Err(Error::new(Span::call_site(), "`PassByEnum` only supports `256` variants."))
|
||||
}
|
||||
},
|
||||
_ => Err(Error::new(Span::call_site(), "`PassByEnum` only supports enums as input type."))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
// 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/>.
|
||||
|
||||
//! Derive macro implementation of `PassBy` with the associated type set to `Inner` and of the
|
||||
//! helper trait `PassByInner`.
|
||||
//!
|
||||
//! It is required that the type is a newtype struct, otherwise an error is generated.
|
||||
|
||||
use crate::utils::{generate_crate_access, generate_runtime_interface_include};
|
||||
|
||||
use syn::{DeriveInput, Result, Generics, parse_quote, Type, Data, Error, Fields, Ident};
|
||||
|
||||
use quote::quote;
|
||||
|
||||
use proc_macro2::{TokenStream, Span};
|
||||
|
||||
/// The derive implementation for `PassBy` with `Inner` and `PassByInner`.
|
||||
pub fn derive_impl(mut input: DeriveInput) -> Result<TokenStream> {
|
||||
add_trait_bounds(&mut input.generics);
|
||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
let crate_include = generate_runtime_interface_include();
|
||||
let crate_ = generate_crate_access();
|
||||
let ident = input.ident;
|
||||
let (inner_ty, inner_name) = extract_inner_ty_and_name(&input.data)?;
|
||||
|
||||
let access_inner = match inner_name {
|
||||
Some(ref name) => quote!(self.#name),
|
||||
None => quote!(self.0),
|
||||
};
|
||||
|
||||
let from_inner = match inner_name {
|
||||
Some(name) => quote!(Self { #name: inner }),
|
||||
None => quote!(Self(inner)),
|
||||
};
|
||||
|
||||
let res = quote! {
|
||||
const _: () = {
|
||||
#crate_include
|
||||
|
||||
impl #impl_generics #crate_::pass_by::PassBy for #ident #ty_generics #where_clause {
|
||||
type PassBy = #crate_::pass_by::Inner<#ident, #inner_ty>;
|
||||
}
|
||||
|
||||
impl #impl_generics #crate_::pass_by::PassByInner for #ident #ty_generics #where_clause {
|
||||
type Inner = #inner_ty;
|
||||
|
||||
fn into_inner(self) -> Self::Inner {
|
||||
#access_inner
|
||||
}
|
||||
|
||||
fn inner(&self) -> &Self::Inner {
|
||||
&#access_inner
|
||||
}
|
||||
|
||||
fn from_inner(inner: Self::Inner) -> Self {
|
||||
#from_inner
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Add the `RIType` trait bound to every type parameter.
|
||||
fn add_trait_bounds(generics: &mut Generics) {
|
||||
let crate_ = generate_crate_access();
|
||||
|
||||
generics.type_params_mut()
|
||||
.for_each(|type_param| type_param.bounds.push(parse_quote!(#crate_::RIType)));
|
||||
}
|
||||
|
||||
/// Extract the inner type and optional name from given input data.
|
||||
///
|
||||
/// It also checks that the input data is a newtype struct.
|
||||
fn extract_inner_ty_and_name(data: &Data) -> Result<(Type, Option<Ident>)> {
|
||||
if let Data::Struct(ref struct_data) = data {
|
||||
match struct_data.fields {
|
||||
Fields::Named(ref named) if named.named.len() == 1 => {
|
||||
let field = &named.named[0];
|
||||
return Ok((field.ty.clone(), field.ident.clone()))
|
||||
},
|
||||
Fields::Unnamed(ref unnamed) if unnamed.unnamed.len() == 1 => {
|
||||
let field = &unnamed.unnamed[0];
|
||||
return Ok((field.ty.clone(), field.ident.clone()))
|
||||
}
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
Err(
|
||||
Error::new(
|
||||
Span::call_site(),
|
||||
"Only newtype/one field structs are supported by `PassByInner`!",
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// 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/>.
|
||||
|
||||
//! All the `PassBy*` derive implementations.
|
||||
|
||||
mod codec;
|
||||
mod enum_;
|
||||
mod inner;
|
||||
|
||||
pub use self::codec::derive_impl as codec_derive_impl;
|
||||
pub use enum_::derive_impl as enum_derive_impl;
|
||||
pub use inner::derive_impl as inner_derive_impl;
|
||||
+186
@@ -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,
|
||||
}
|
||||
}
|
||||
+415
@@ -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 )*
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
// 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/>
|
||||
|
||||
//! Util function used by this crate.
|
||||
|
||||
use proc_macro2::{TokenStream, Span};
|
||||
|
||||
use syn::{
|
||||
Ident, Error, Signature, Pat, PatType, FnArg, Type, token, TraitItemMethod, ItemTrait,
|
||||
TraitItem, parse_quote, spanned::Spanned,
|
||||
};
|
||||
|
||||
use proc_macro_crate::crate_name;
|
||||
|
||||
use std::env;
|
||||
|
||||
use quote::quote;
|
||||
|
||||
use inflector::Inflector;
|
||||
|
||||
/// Generates the include for the runtime-interface crate.
|
||||
pub fn generate_runtime_interface_include() -> TokenStream {
|
||||
if env::var("CARGO_PKG_NAME").unwrap() == "substrate-runtime-interface" {
|
||||
TokenStream::new()
|
||||
} else {
|
||||
match crate_name("substrate-runtime-interface") {
|
||||
Ok(crate_name) => {
|
||||
let crate_name = Ident::new(&crate_name, Span::call_site());
|
||||
quote!(
|
||||
#[doc(hidden)]
|
||||
extern crate #crate_name as proc_macro_runtime_interface;
|
||||
)
|
||||
},
|
||||
Err(e) => {
|
||||
let err = Error::new(Span::call_site(), &e).to_compile_error();
|
||||
quote!( #err )
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the access to the `substrate-runtime-interface` crate.
|
||||
pub fn generate_crate_access() -> TokenStream {
|
||||
if env::var("CARGO_PKG_NAME").unwrap() == "substrate-runtime-interface" {
|
||||
quote!( substrate_runtime_interface )
|
||||
} else {
|
||||
quote!( proc_macro_runtime_interface )
|
||||
}
|
||||
}
|
||||
|
||||
/// Create the exchangeable host function identifier for the given function name.
|
||||
pub fn create_exchangeable_host_function_ident(name: &Ident) -> Ident {
|
||||
Ident::new(&format!("host_{}", name), Span::call_site())
|
||||
}
|
||||
|
||||
/// Create the host function identifier for the given function name.
|
||||
pub fn create_host_function_ident(name: &Ident, trait_name: &Ident) -> Ident {
|
||||
Ident::new(
|
||||
&format!(
|
||||
"ext_{}_{}_version_1",
|
||||
trait_name.to_string().to_snake_case(),
|
||||
name,
|
||||
),
|
||||
Span::call_site(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the function arguments of the given `Signature`, minus any `self` arguments.
|
||||
pub fn get_function_arguments<'a>(sig: &'a Signature) -> impl Iterator<Item = PatType> + 'a {
|
||||
sig.inputs
|
||||
.iter()
|
||||
.filter_map(|a| match a {
|
||||
FnArg::Receiver(_) => None,
|
||||
FnArg::Typed(pat_type) => Some(pat_type),
|
||||
})
|
||||
.enumerate()
|
||||
.map(|(i, arg)| {
|
||||
let mut res = arg.clone();
|
||||
if let Pat::Wild(wild) = &*arg.pat {
|
||||
let ident = Ident::new(
|
||||
&format!("__runtime_interface_generated_{}_", i),
|
||||
wild.span(),
|
||||
);
|
||||
|
||||
res.pat = Box::new(parse_quote!( #ident ))
|
||||
}
|
||||
|
||||
res
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the function argument names of the given `Signature`, minus any `self`.
|
||||
pub fn get_function_argument_names<'a>(sig: &'a Signature) -> impl Iterator<Item = Box<Pat>> + 'a {
|
||||
get_function_arguments(sig).map(|pt| pt.pat)
|
||||
}
|
||||
|
||||
/// Returns the function argument types of the given `Signature`, minus any `Self` type.
|
||||
pub fn get_function_argument_types<'a>(sig: &'a Signature) -> impl Iterator<Item = Box<Type>> + 'a {
|
||||
get_function_arguments(sig).map(|pt| pt.ty)
|
||||
}
|
||||
|
||||
/// Returns the function argument types, minus any `Self` type. If any of the arguments
|
||||
/// is a reference, the underlying type without the ref is returned.
|
||||
pub fn get_function_argument_types_without_ref<'a>(
|
||||
sig: &'a Signature,
|
||||
) -> impl Iterator<Item = Box<Type>> + 'a {
|
||||
get_function_arguments(sig)
|
||||
.map(|pt| pt.ty)
|
||||
.map(|ty| match *ty {
|
||||
Type::Reference(type_ref) => type_ref.elem,
|
||||
_ => ty,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the function argument names and types, minus any `self`. If any of the arguments
|
||||
/// is a reference, the underlying type without the ref is returned.
|
||||
pub fn get_function_argument_names_and_types_without_ref<'a>(
|
||||
sig: &'a Signature,
|
||||
) -> impl Iterator<Item = (Box<Pat>, Box<Type>)> + 'a {
|
||||
get_function_arguments(sig)
|
||||
.map(|pt| match *pt.ty {
|
||||
Type::Reference(type_ref) => (pt.pat, type_ref.elem),
|
||||
_ => (pt.pat, pt.ty),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the `&`/`&mut` for all function argument types, minus the `self` arg. If a function
|
||||
/// argument is not a reference, `None` is returned.
|
||||
pub fn get_function_argument_types_ref_and_mut<'a>(
|
||||
sig: &'a Signature,
|
||||
) -> impl Iterator<Item = Option<(token::And, Option<token::Mut>)>> + 'a {
|
||||
get_function_arguments(sig)
|
||||
.map(|pt| pt.ty)
|
||||
.map(|ty| match *ty {
|
||||
Type::Reference(type_ref) => Some((type_ref.and_token, type_ref.mutability)),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns an iterator over all trait methods for the given trait definition.
|
||||
pub fn get_trait_methods<'a>(trait_def: &'a ItemTrait) -> impl Iterator<Item = &'a TraitItemMethod> {
|
||||
trait_def
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(|i| match i {
|
||||
TraitItem::Method(ref method) => Some(method),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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 std::env;
|
||||
|
||||
#[rustversion::attr(not(stable), ignore)]
|
||||
#[test]
|
||||
fn ui() {
|
||||
// As trybuild is using `cargo check`, we don't need the real WASM binaries.
|
||||
env::set_var("BUILD_DUMMY_WASM_BINARY", "1");
|
||||
|
||||
let t = trybuild::TestCases::new();
|
||||
t.compile_fail("tests/ui/*.rs");
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
use runtime_interface::runtime_interface;
|
||||
|
||||
#[runtime_interface]
|
||||
trait Test<T> {
|
||||
fn test<R>() {}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,11 @@
|
||||
error: Generic parameters not supported.
|
||||
--> $DIR/no_generic_parameters.rs:5:10
|
||||
|
|
||||
5 | fn test<R>() {}
|
||||
| ^
|
||||
|
||||
error: Generic parameters not supported.
|
||||
--> $DIR/no_generic_parameters.rs:4:12
|
||||
|
|
||||
4 | trait Test<T> {
|
||||
| ^
|
||||
@@ -0,0 +1,8 @@
|
||||
use runtime_interface::runtime_interface;
|
||||
|
||||
#[runtime_interface]
|
||||
trait Test {
|
||||
fn test();
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: Methods need to have an implementation.
|
||||
--> $DIR/no_method_implementation.rs:5:2
|
||||
|
|
||||
5 | fn test();
|
||||
| ^^
|
||||
@@ -0,0 +1,6 @@
|
||||
use runtime_interface::pass_by::PassByEnum;
|
||||
|
||||
#[derive(PassByEnum)]
|
||||
struct Test;
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: `PassByEnum` only supports enums as input type.
|
||||
--> $DIR/pass_by_enum_with_struct.rs:3:10
|
||||
|
|
||||
3 | #[derive(PassByEnum)]
|
||||
| ^^^^^^^^^^
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
use runtime_interface::pass_by::PassByEnum;
|
||||
|
||||
#[derive(PassByEnum)]
|
||||
enum Test {
|
||||
Var0(u32),
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
error: `PassByEnum` only supports unit variants.
|
||||
--> $DIR/pass_by_enum_with_value_variant.rs:3:10
|
||||
|
|
||||
3 | #[derive(PassByEnum)]
|
||||
| ^^^^^^^^^^
|
||||
@@ -0,0 +1,9 @@
|
||||
use runtime_interface::pass_by::PassByInner;
|
||||
|
||||
#[derive(PassByInner)]
|
||||
struct Test {
|
||||
data: u32,
|
||||
data2: u32,
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
error: Only newtype/one field structs are supported by `PassByInner`!
|
||||
--> $DIR/pass_by_inner_with_two_fields.rs:3:10
|
||||
|
|
||||
3 | #[derive(PassByInner)]
|
||||
| ^^^^^^^^^^^
|
||||
@@ -0,0 +1,8 @@
|
||||
use runtime_interface::runtime_interface;
|
||||
|
||||
#[runtime_interface]
|
||||
trait Test {
|
||||
fn test(self) {}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: Taking `Self` by value is not allowed.
|
||||
--> $DIR/take_self_by_value.rs:5:10
|
||||
|
|
||||
5 | fn test(self) {}
|
||||
| ^^^^
|
||||
Reference in New Issue
Block a user