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:
Bastian Köcher
2019-11-10 21:59:30 +01:00
committed by GitHub
parent f4c1c83667
commit b691cfe093
118 changed files with 4901 additions and 2829 deletions
@@ -0,0 +1,41 @@
[package]
name = "substrate-runtime-interface"
version = "2.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
[dependencies]
wasm-interface = { package = "substrate-wasm-interface", path = "../wasm-interface", optional = true }
rstd = { package = "sr-std", path = "../sr-std", default-features = false }
substrate-runtime-interface-proc-macro = { path = "proc-macro" }
externalities = { package = "substrate-externalities", path = "../externalities", optional = true }
codec = { package = "parity-scale-codec", version = "1.0.6", default-features = false }
environmental = { version = "1.0.2", optional = true }
static_assertions = "1.0.0"
primitive-types = { version = "0.6.1", default-features = false }
[dev-dependencies]
executor = { package = "substrate-executor", path = "../executor" }
test-wasm = { package = "substrate-runtime-interface-test-wasm", path = "test-wasm" }
state_machine = { package = "substrate-state-machine", path = "../state-machine" }
primitives = { package = "substrate-primitives", path = "../primitives" }
runtime-io = { package = "sr-io", path = "../sr-io" }
[features]
default = [ "std" ]
std = [
"wasm-interface",
"rstd/std",
"codec/std",
"externalities",
"environmental",
"primitive-types/std",
]
# ATTENTION
#
# Only use when you know what you are doing.
#
# Disables static assertions in `impls.rs` that checks the word size. To prevent any footgun, the
# check is changed into a runtime check.
disable_target_static_assertions = []
@@ -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;
@@ -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 )*
}
}
)
}
@@ -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)]
| ^^^^^^^^^^
@@ -0,0 +1,8 @@
use runtime_interface::pass_by::PassByEnum;
#[derive(PassByEnum)]
enum Test {
Var0(u32),
}
fn main() {}
@@ -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() {}
@@ -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) {}
| ^^^^
@@ -0,0 +1,62 @@
// 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/>.
//! Traits required by the runtime interface from the host side.
use crate::RIType;
use wasm_interface::{FunctionContext, Result};
/// Something that can be converted into a ffi value.
pub trait IntoFFIValue: RIType {
/// Convert `self` into a ffi value.
fn into_ffi_value(self, context: &mut dyn FunctionContext) -> Result<Self::FFIType>;
}
/// Something that can be converted into a preallocated ffi value.
///
/// Every type parameter that should be given as `&mut` into a runtime interface function, needs
/// to implement this trait. After executing the host implementation of the runtime interface
/// function, the value is copied into the preallocated wasm memory.
///
/// This should only be used for types which have a fixed size, like slices. Other types like a vec
/// do not work with this interface, as we can not call into wasm to reallocate memory. So, this
/// trait should be implemented carefully.
pub trait IntoPreallocatedFFIValue: RIType {
/// As `Self` can be an unsized type, it needs to be represented by a sized type at the host.
/// This `SelfInstance` is the sized type.
type SelfInstance;
/// Convert `self_instance` into the given preallocated ffi value.
fn into_preallocated_ffi_value(
self_instance: Self::SelfInstance,
context: &mut dyn FunctionContext,
allocated: Self::FFIType,
) -> Result<()>;
}
/// Something that can be created from a ffi value.
pub trait FromFFIValue: RIType {
/// As `Self` can be an unsized type, it needs to be represented by a sized type at the host.
/// This `SelfInstance` is the sized type.
type SelfInstance;
/// Create `SelfInstance` from the given
fn from_ffi_value(
context: &mut dyn FunctionContext,
arg: Self::FFIType,
) -> Result<Self::SelfInstance>;
}
@@ -0,0 +1,491 @@
// 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/>.
//! Provides implementations for the runtime interface traits.
use crate::{RIType, Pointer, pass_by::{PassBy, Codec, Inner, PassByInner}};
#[cfg(feature = "std")]
use crate::host::*;
#[cfg(not(feature = "std"))]
use crate::wasm::*;
#[cfg(all(not(feature = "std"), not(feature = "disable_target_static_assertions")))]
use static_assertions::assert_eq_size;
#[cfg(feature = "std")]
use wasm_interface::{FunctionContext, Result};
use codec::{Encode, Decode};
use rstd::{any::TypeId, mem, vec::Vec};
#[cfg(feature = "std")]
use rstd::borrow::Cow;
#[cfg(not(feature = "std"))]
use rstd::{slice, boxed::Box};
// Make sure that our assumptions for storing a pointer + its size in `u64` is valid.
#[cfg(all(not(feature = "std"), not(feature = "disable_target_static_assertions")))]
assert_eq_size!(usize, u32);
#[cfg(all(not(feature = "std"), not(feature = "disable_target_static_assertions")))]
assert_eq_size!(*const u8, u32);
/// Converts a pointer and length into an `u64`.
pub fn pointer_and_len_to_u64(ptr: u32, len: u32) -> u64 {
// The static assertions from above are changed into a runtime check.
#[cfg(all(feature = "std", not(feature = "disable_target_static_assertions")))]
assert_eq!(4, rstd::mem::size_of::<usize>());
(u64::from(len) << 32) | u64::from(ptr)
}
/// Splits an `u64` into the pointer and length.
pub fn pointer_and_len_from_u64(val: u64) -> (u32, u32) {
// The static assertions from above are changed into a runtime check.
#[cfg(all(feature = "std", not(feature = "disable_target_static_assertions")))]
assert_eq!(4, rstd::mem::size_of::<usize>());
let ptr = (val & (!0u32 as u64)) as u32;
let len = (val >> 32) as u32;
(ptr, len)
}
/// Implement the traits for the given primitive traits.
macro_rules! impl_traits_for_primitives {
(
$(
$rty:ty, $fty:ty,
)*
) => {
$(
/// The type is passed directly.
impl RIType for $rty {
type FFIType = $fty;
}
#[cfg(not(feature = "std"))]
impl IntoFFIValue for $rty {
type Owned = ();
fn into_ffi_value(&self) -> WrappedFFIValue<$fty> {
(*self as $fty).into()
}
}
#[cfg(not(feature = "std"))]
impl FromFFIValue for $rty {
fn from_ffi_value(arg: $fty) -> $rty {
arg as $rty
}
}
#[cfg(feature = "std")]
impl FromFFIValue for $rty {
type SelfInstance = $rty;
fn from_ffi_value(_: &mut dyn FunctionContext, arg: $fty) -> Result<$rty> {
Ok(arg as $rty)
}
}
#[cfg(feature = "std")]
impl IntoFFIValue for $rty {
fn into_ffi_value(self, _: &mut dyn FunctionContext) -> Result<$fty> {
Ok(self as $fty)
}
}
)*
}
}
impl_traits_for_primitives! {
u8, u8,
u16, u16,
u32, u32,
u64, u64,
i8, i8,
i16, i16,
i32, i32,
i64, i64,
}
/// `bool` is passed as `u8`.
///
/// - `1`: true
/// - `0`: false
impl RIType for bool {
type FFIType = u8;
}
#[cfg(not(feature = "std"))]
impl IntoFFIValue for bool {
type Owned = ();
fn into_ffi_value(&self) -> WrappedFFIValue<u8> {
if *self { 1 } else { 0 }.into()
}
}
#[cfg(not(feature = "std"))]
impl FromFFIValue for bool {
fn from_ffi_value(arg: u8) -> bool {
arg == 1
}
}
#[cfg(feature = "std")]
impl FromFFIValue for bool {
type SelfInstance = bool;
fn from_ffi_value(_: &mut dyn FunctionContext, arg: u8) -> Result<bool> {
Ok(arg == 1)
}
}
#[cfg(feature = "std")]
impl IntoFFIValue for bool {
fn into_ffi_value(self, _: &mut dyn FunctionContext) -> Result<u8> {
Ok(if self { 1 } else { 0 })
}
}
/// The type is passed as `u64`.
///
/// The `u64` value is build by `length 32bit << 32 | pointer 32bit`
///
/// If `T == u8` the length and the pointer are taken directly from the `Self`.
/// Otherwise `Self` is encoded and the length and the pointer are taken from the encoded vector.
impl<T> RIType for Vec<T> {
type FFIType = u64;
}
#[cfg(feature = "std")]
impl<T: 'static + Encode> IntoFFIValue for Vec<T> {
fn into_ffi_value(self, context: &mut dyn FunctionContext) -> Result<u64> {
let vec: Cow<'_, [u8]> = if TypeId::of::<T>() == TypeId::of::<u8>() {
unsafe { Cow::Borrowed(mem::transmute(&self[..])) }
} else {
Cow::Owned(self.encode())
};
let ptr = context.allocate_memory(vec.as_ref().len() as u32)?;
context.write_memory(ptr, &vec)?;
Ok(pointer_and_len_to_u64(ptr.into(), vec.len() as u32))
}
}
#[cfg(feature = "std")]
impl<T: 'static + Decode> FromFFIValue for Vec<T> {
type SelfInstance = Vec<T>;
fn from_ffi_value(context: &mut dyn FunctionContext, arg: u64) -> Result<Vec<T>> {
<[T] as FromFFIValue>::from_ffi_value(context, arg)
}
}
#[cfg(not(feature = "std"))]
impl<T: 'static + Encode> IntoFFIValue for Vec<T> {
type Owned = Vec<u8>;
fn into_ffi_value(&self) -> WrappedFFIValue<u64, Vec<u8>> {
self[..].into_ffi_value()
}
}
#[cfg(not(feature = "std"))]
impl<T: 'static + Decode> FromFFIValue for Vec<T> {
fn from_ffi_value(arg: u64) -> Vec<T> {
let (ptr, len) = pointer_and_len_from_u64(arg);
let len = len as usize;
if TypeId::of::<T>() == TypeId::of::<u8>() {
unsafe { mem::transmute(Vec::from_raw_parts(ptr as *mut u8, len, len)) }
} else {
let slice = unsafe { slice::from_raw_parts(ptr as *const u8, len) };
Self::decode(&mut &slice[..]).expect("Host to wasm values are encoded correctly; qed")
}
}
}
/// The type is passed as `u64`.
///
/// The `u64` value is build by `length 32bit << 32 | pointer 32bit`
///
/// If `T == u8` the length and the pointer are taken directly from the `Self`.
/// Otherwise `Self` is encoded and the length and the pointer are taken from the encoded vector.
impl<T> RIType for [T] {
type FFIType = u64;
}
#[cfg(feature = "std")]
impl<T: 'static + Decode> FromFFIValue for [T] {
type SelfInstance = Vec<T>;
fn from_ffi_value(context: &mut dyn FunctionContext, arg: u64) -> Result<Vec<T>> {
let (ptr, len) = pointer_and_len_from_u64(arg);
let vec = context.read_memory(Pointer::new(ptr), len)?;
if TypeId::of::<T>() == TypeId::of::<u8>() {
Ok(unsafe { mem::transmute(vec) })
} else {
Ok(Vec::<T>::decode(&mut &vec[..]).expect("Wasm to host values are encoded correctly; qed"))
}
}
}
#[cfg(feature = "std")]
impl IntoPreallocatedFFIValue for [u8] {
type SelfInstance = Vec<u8>;
fn into_preallocated_ffi_value(
self_instance: Self::SelfInstance,
context: &mut dyn FunctionContext,
allocated: u64,
) -> Result<()> {
let (ptr, len) = pointer_and_len_from_u64(allocated);
if (len as usize) < self_instance.len() {
Err(
format!(
"Preallocated buffer is not big enough (given {} vs needed {})!",
len,
self_instance.len()
)
)
} else {
context.write_memory(Pointer::new(ptr), &self_instance)
}
}
}
#[cfg(not(feature = "std"))]
impl<T: 'static + Encode> IntoFFIValue for [T] {
type Owned = Vec<u8>;
fn into_ffi_value(&self) -> WrappedFFIValue<u64, Vec<u8>> {
if TypeId::of::<T>() == TypeId::of::<u8>() {
let slice = unsafe { mem::transmute::<&[T], &[u8]>(self) };
pointer_and_len_to_u64(slice.as_ptr() as u32, slice.len() as u32).into()
} else {
let data = self.encode();
let ffi_value = pointer_and_len_to_u64(data.as_ptr() as u32, data.len() as u32);
(ffi_value, data).into()
}
}
}
/// Implement the traits for the `[u8; N]` arrays, where `N` is the input to this macro.
macro_rules! impl_traits_for_arrays {
(
$(
$n:expr
),*
$(,)?
) => {
$(
/// The type is passed as `u32`.
///
/// The `u32` is the pointer to the array.
impl RIType for [u8; $n] {
type FFIType = u32;
}
#[cfg(not(feature = "std"))]
impl IntoFFIValue for [u8; $n] {
type Owned = ();
fn into_ffi_value(&self) -> WrappedFFIValue<u32> {
(self.as_ptr() as u32).into()
}
}
#[cfg(not(feature = "std"))]
impl FromFFIValue for [u8; $n] {
fn from_ffi_value(arg: u32) -> [u8; $n] {
let mut res = unsafe { mem::MaybeUninit::<[u8; $n]>::zeroed().assume_init() };
res.copy_from_slice(unsafe { slice::from_raw_parts(arg as *const u8, $n) });
// Make sure we free the pointer.
let _ = unsafe { Box::from_raw(arg as *mut u8) };
res
}
}
#[cfg(feature = "std")]
impl FromFFIValue for [u8; $n] {
type SelfInstance = [u8; $n];
fn from_ffi_value(context: &mut dyn FunctionContext, arg: u32) -> Result<[u8; $n]> {
let data = context.read_memory(Pointer::new(arg), $n)?;
let mut res = unsafe { mem::MaybeUninit::<[u8; $n]>::zeroed().assume_init() };
res.copy_from_slice(&data);
Ok(res)
}
}
#[cfg(feature = "std")]
impl IntoFFIValue for [u8; $n] {
fn into_ffi_value(self, context: &mut dyn FunctionContext) -> Result<u32> {
let addr = context.allocate_memory($n)?;
context.write_memory(addr, &self)?;
Ok(addr.into())
}
}
#[cfg(feature = "std")]
impl IntoPreallocatedFFIValue for [u8; $n] {
type SelfInstance = [u8; $n];
fn into_preallocated_ffi_value(
self_instance: Self::SelfInstance,
context: &mut dyn FunctionContext,
allocated: u32,
) -> Result<()> {
context.write_memory(Pointer::new(allocated), &self_instance)
}
}
)*
}
}
impl_traits_for_arrays! {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
}
impl<T: codec::Codec, E: codec::Codec> PassBy for rstd::result::Result<T, E> {
type PassBy = Codec<Self>;
}
impl<T: codec::Codec> PassBy for Option<T> {
type PassBy = Codec<Self>;
}
/// Implement `PassBy` with `Inner` for the given fixed sized hash types.
macro_rules! for_primitive_types {
{ $( $hash:ident $n:expr ),* $(,)? } => {
$(
impl PassBy for primitive_types::$hash {
type PassBy = Inner<Self, [u8; $n]>;
}
impl PassByInner for primitive_types::$hash {
type Inner = [u8; $n];
fn inner(&self) -> &Self::Inner {
&self.0
}
fn into_inner(self) -> Self::Inner {
self.0
}
fn from_inner(inner: Self::Inner) -> Self {
Self(inner)
}
}
)*
}
}
for_primitive_types! {
H160 20,
H256 32,
H512 64,
}
/// The type is passed as `u64`.
///
/// The `u64` value is build by `length 32bit << 32 | pointer 32bit`
///
/// The length and the pointer are taken directly from the `Self`.
impl RIType for str {
type FFIType = u64;
}
#[cfg(feature = "std")]
impl FromFFIValue for str {
type SelfInstance = String;
fn from_ffi_value(context: &mut dyn FunctionContext, arg: u64) -> Result<String> {
let (ptr, len) = pointer_and_len_from_u64(arg);
let vec = context.read_memory(Pointer::new(ptr), len)?;
// The data is valid utf8, as it is stored as `&str` in wasm.
String::from_utf8(vec).map_err(|_| "Invalid utf8 data provided".into())
}
}
#[cfg(not(feature = "std"))]
impl IntoFFIValue for str {
type Owned = ();
fn into_ffi_value(&self) -> WrappedFFIValue<u64, ()> {
let bytes = self.as_bytes();
pointer_and_len_to_u64(bytes.as_ptr() as u32, bytes.len() as u32).into()
}
}
#[cfg(feature = "std")]
impl<T: wasm_interface::PointerType> RIType for Pointer<T> {
type FFIType = u32;
}
/// The type is passed as `u32`.
#[cfg(not(feature = "std"))]
impl<T> RIType for Pointer<T> {
type FFIType = u32;
}
#[cfg(not(feature = "std"))]
impl<T> IntoFFIValue for Pointer<T> {
type Owned = ();
fn into_ffi_value(&self) -> WrappedFFIValue<u32> {
(*self as u32).into()
}
}
#[cfg(not(feature = "std"))]
impl<T> FromFFIValue for Pointer<T> {
fn from_ffi_value(arg: u32) -> Self {
arg as _
}
}
#[cfg(feature = "std")]
impl<T: wasm_interface::PointerType> FromFFIValue for Pointer<T> {
type SelfInstance = Self;
fn from_ffi_value(_: &mut dyn FunctionContext, arg: u32) -> Result<Self> {
Ok(Pointer::new(arg))
}
}
#[cfg(feature = "std")]
impl<T: wasm_interface::PointerType> IntoFFIValue for Pointer<T> {
fn into_ffi_value(self, _: &mut dyn FunctionContext) -> Result<u32> {
Ok(self.into())
}
}
+212
View File
@@ -0,0 +1,212 @@
// 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/>.
//! Substrate runtime interface
//!
//! This crate provides types, traits and macros around runtime interfaces. A runtime interface is
//! a fixed interface between a Substrate runtime and a Substrate node. For a native runtime the
//! interface maps to a direct function call of the implementation. For a wasm runtime the interface
//! maps to an external function call. These external functions are exported by the wasm executor
//! and they map to the same implementation as the native calls.
//!
//! # Using a type in a runtime interface
//!
//! Any type that should be used in a runtime interface as argument or return value needs to
//! implement [`RIType`]. The associated type `FFIType` is the type that is used in the FFI
//! function to represent the actual type. For example `[T]` is represented by an `u64`. The slice
//! pointer and the length will be mapped to an `u64` value. For more information, see the
//! implementation of [`RIType`] for [`T`]. The FFI function definition is used when calling from
//! the wasm runtime into the node.
//!
//! Traits are used to convert from a type to the corresponding [`RIType::FFIType`].
//! Depending on where and how a type should be used in a function signature, a combination of the
//! following traits need to be implemented:
//!
//! 1. Pass as function argument: [`wasm::IntoFFIValue`] and [`host::FromFFIValue`]
//! 2. As function return value: [`wasm::FromFFIValue`] and [`host::IntoFFIValue`]
//! 3. Pass as mutable function argument: [`host::IntoPreallocatedFFIValue`]
//!
//! The traits are implemented for most of the common types like `[T]`, `Vec<T>`, arrays and
//! primitive types.
//!
//! For custom types, we provide the [`PassBy`](pass_by::PassBy) trait and strategies that define
//! how a type is passed between the wasm runtime and the node. Each strategy also provides a derive
//! macro to simplify the implementation.
//!
//! # Performance
//!
//! To not waste any more performance when calling into the node, not all types are SCALE encoded
//! when being passed as arguments between the wasm runtime and the node. For most types that
//! are raw bytes like `Vec<u8>`, `[u8]` or `[u8; N]` we pass them directly, without SCALE encoding
//! them in front of. The implementation of [`RIType`] each type provides more information on how
//! the data is passed.
//!
//! # Declaring a runtime interface
//!
//! Declaring a runtime interface is similar to declaring a trait in Rust:
//!
//! ```
//! #[substrate_runtime_interface::runtime_interface]
//! trait RuntimeInterface {
//! fn some_function(value: &[u8]) -> bool {
//! value.iter().all(|v| *v > 125)
//! }
//! }
//! ```
//!
//! For more information on declaring a runtime interface, see
//! [`#[runtime_interface]`](attr.runtime_interface.html).
#![cfg_attr(not(feature = "std"), no_std)]
#[doc(hidden)]
#[cfg(feature = "std")]
pub use wasm_interface;
#[doc(hidden)]
pub use rstd;
pub use substrate_runtime_interface_proc_macro::runtime_interface;
#[doc(hidden)]
#[cfg(feature = "std")]
pub use externalities::{
set_and_run_with_externalities, with_externalities, Externalities, ExternalitiesExt, ExtensionStore,
};
#[doc(hidden)]
pub use codec;
pub(crate) mod impls;
#[cfg(feature = "std")]
pub mod host;
#[cfg(not(feature = "std"))]
pub mod wasm;
pub mod pass_by;
/// Something that can be used by the runtime interface as type to communicate between wasm and the
/// host.
///
/// Every type that should be used in a runtime interface function signature needs to implement
/// this trait.
pub trait RIType {
/// The ffi type that is used to represent `Self`.
#[cfg(feature = "std")]
type FFIType: wasm_interface::IntoValue + wasm_interface::TryFromValue;
#[cfg(not(feature = "std"))]
type FFIType;
}
/// A pointer that can be used in a runtime interface function signature.
#[cfg(not(feature = "std"))]
pub type Pointer<T> = *mut T;
/// A pointer that can be used in a runtime interface function signature.
#[cfg(feature = "std")]
pub type Pointer<T> = wasm_interface::Pointer<T>;
#[cfg(test)]
mod tests {
use super::*;
use test_wasm::{WASM_BINARY, test_api::HostFunctions};
use wasm_interface::HostFunctions as HostFunctionsT;
type TestExternalities = state_machine::TestExternalities<primitives::Blake2Hasher, u64>;
fn call_wasm_method<HF: HostFunctionsT>(method: &str) -> TestExternalities {
let mut ext = TestExternalities::default();
let mut ext_ext = ext.ext();
executor::call_in_wasm::<
_,
(
HF,
runtime_io::SubstrateHostFunctions,
executor::deprecated_host_interface::SubstrateExternals
)
>(
method,
&[],
executor::WasmExecutionMethod::Interpreted,
&mut ext_ext,
&WASM_BINARY[..],
8,
).expect(&format!("Executes `{}`", method));
ext
}
#[test]
fn test_return_data() {
call_wasm_method::<HostFunctions>("test_return_data");
}
#[test]
fn test_return_option_data() {
call_wasm_method::<HostFunctions>("test_return_option_data");
}
#[test]
fn test_set_storage() {
let mut ext = call_wasm_method::<HostFunctions>("test_set_storage");
let expected = "world";
assert_eq!(expected.as_bytes(), &ext.ext().storage("hello".as_bytes()).unwrap()[..]);
}
#[test]
fn test_return_value_into_mutable_reference() {
call_wasm_method::<HostFunctions>("test_return_value_into_mutable_reference");
}
#[test]
fn test_get_and_return_array() {
call_wasm_method::<HostFunctions>("test_get_and_return_array");
}
#[test]
fn test_array_as_mutable_reference() {
call_wasm_method::<HostFunctions>("test_array_as_mutable_reference");
}
#[test]
fn test_return_input_public_key() {
call_wasm_method::<HostFunctions>("test_return_input_public_key");
}
#[test]
#[should_panic(
expected = "Other(\"Instantiation: Export ext_test_api_return_input_version_1 not found\")"
)]
fn host_function_not_found() {
call_wasm_method::<()>("test_return_data");
}
#[test]
#[should_panic(
expected =
"FunctionExecution(\"ext_test_api_invalid_utf8_data_version_1\", \
\"Invalid utf8 data provided\")"
)]
fn test_invalid_utf8_data_should_return_an_error() {
call_wasm_method::<HostFunctions>("test_invalid_utf8_data_should_return_an_error");
}
#[test]
fn test_overwrite_native_function_implementation() {
call_wasm_method::<HostFunctions>("test_overwrite_native_function_implementation");
}
}
@@ -0,0 +1,384 @@
// 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/>.
//! Provides the [`PassBy`](pass_by::PassBy) trait to simplify the implementation of the
//! runtime interface traits for custom types.
//!
//! [`Codec`](pass_by::Codec), [`Inner`](pass_by::Inner) and [`Enum`](pass_by::Enum) are the
//! provided strategy implementations.
use crate::{RIType, impls::{pointer_and_len_from_u64, pointer_and_len_to_u64}};
#[cfg(feature = "std")]
use crate::host::*;
#[cfg(not(feature = "std"))]
use crate::wasm::*;
#[cfg(feature = "std")]
use wasm_interface::{FunctionContext, Pointer, Result};
use rstd::{marker::PhantomData, convert::TryFrom};
#[cfg(not(feature = "std"))]
use rstd::{slice, vec::Vec};
pub use substrate_runtime_interface_proc_macro::{PassByCodec, PassByInner, PassByEnum};
/// Something that should be passed between wasm and the host using the given strategy.
///
/// See [`Codec`], [`Inner`] or [`Enum`] for more information about the provided strategies.
pub trait PassBy: Sized {
/// The strategy that should be used to pass the type.
type PassBy: PassByImpl<Self>;
}
/// Something that provides a strategy for passing a type between wasm and the host.
///
/// This trait exposes the same functionality as [`crate::host::IntoFFIValue`] and
/// [`crate::host::FromFFIValue`] to delegate the implementation for a type to a different type.
///
/// This trait is used for the host implementation.
#[cfg(feature = "std")]
pub trait PassByImpl<T>: RIType {
/// Convert the given instance to the ffi value.
///
/// For more information see: [`crate::host::IntoFFIValue::into_ffi_value`]
fn into_ffi_value(
instance: T,
context: &mut dyn FunctionContext,
) -> Result<Self::FFIType>;
/// Create `T` from the given ffi value.
///
/// For more information see: [`crate::host::FromFFIValue::from_ffi_value`]
fn from_ffi_value(
context: &mut dyn FunctionContext,
arg: Self::FFIType,
) -> Result<T>;
}
/// Something that provides a strategy for passing a type between wasm and the host.
///
/// This trait exposes the same functionality as [`crate::wasm::IntoFFIValue`] and
/// [`crate::wasm::FromFFIValue`] to delegate the implementation for a type to a different type.
///
/// This trait is used for the wasm implementation.
#[cfg(not(feature = "std"))]
pub trait PassByImpl<T>: RIType {
/// The owned rust type that is stored with the ffi value in [`crate::wasm::WrappedFFIValue`].
type Owned;
/// Convert the given `instance` into [`crate::wasm::WrappedFFIValue`].
///
/// For more information see: [`crate::wasm::IntoFFIValue::into_ffi_value`]
fn into_ffi_value(instance: &T) -> WrappedFFIValue<Self::FFIType, Self::Owned>;
/// Create `T` from the given ffi value.
///
/// For more information see: [`crate::wasm::FromFFIValue::from_ffi_value`]
fn from_ffi_value(arg: Self::FFIType) -> T;
}
impl<T: PassBy> RIType for T {
type FFIType = <T::PassBy as RIType>::FFIType;
}
#[cfg(feature = "std")]
impl<T: PassBy> IntoFFIValue for T {
fn into_ffi_value(
self,
context: &mut dyn FunctionContext,
) -> Result<<T::PassBy as RIType>::FFIType> {
T::PassBy::into_ffi_value(self, context)
}
}
#[cfg(feature = "std")]
impl<T: PassBy> FromFFIValue for T {
type SelfInstance = Self;
fn from_ffi_value(
context: &mut dyn FunctionContext,
arg: <T::PassBy as RIType>::FFIType,
) -> Result<Self> {
T::PassBy::from_ffi_value(context, arg)
}
}
#[cfg(not(feature = "std"))]
impl<T: PassBy> IntoFFIValue for T {
type Owned = <T::PassBy as PassByImpl<T>>::Owned;
fn into_ffi_value(&self) -> WrappedFFIValue<<T::PassBy as RIType>::FFIType, Self::Owned> {
T::PassBy::into_ffi_value(self)
}
}
#[cfg(not(feature = "std"))]
impl<T: PassBy> FromFFIValue for T {
fn from_ffi_value(arg: <T::PassBy as RIType>::FFIType) -> Self {
T::PassBy::from_ffi_value(arg)
}
}
/// The implementation of the pass by codec strategy. This strategy uses a SCALE encoded
/// representation of the type between wasm and the host.
///
/// Use this type as associated type for [`PassBy`] to implement this strategy for a type.
///
/// This type expects the type that wants to implement this strategy as generic parameter.
///
/// [`PassByCodec`](derive.PassByCodec.html) is a derive macro to implement this strategy.
///
/// # Example
/// ```
/// # use substrate_runtime_interface::pass_by::{PassBy, Codec};
/// #[derive(codec::Encode, codec::Decode)]
/// struct Test;
///
/// impl PassBy for Test {
/// type PassBy = Codec<Self>;
/// }
/// ```
pub struct Codec<T: codec::Codec>(PhantomData<T>);
#[cfg(feature = "std")]
impl<T: codec::Codec> PassByImpl<T> for Codec<T> {
fn into_ffi_value(
instance: T,
context: &mut dyn FunctionContext,
) -> Result<Self::FFIType> {
let vec = instance.encode();
let ptr = context.allocate_memory(vec.len() as u32)?;
context.write_memory(ptr, &vec)?;
Ok(pointer_and_len_to_u64(ptr.into(), vec.len() as u32))
}
fn from_ffi_value(
context: &mut dyn FunctionContext,
arg: Self::FFIType,
) -> Result<T> {
let (ptr, len) = pointer_and_len_from_u64(arg);
let vec = context.read_memory(Pointer::new(ptr), len)?;
T::decode(&mut &vec[..])
.map_err(|e| format!("Could not decode value from wasm: {}", e.what()))
}
}
#[cfg(not(feature = "std"))]
impl<T: codec::Codec> PassByImpl<T> for Codec<T> {
type Owned = Vec<u8>;
fn into_ffi_value(instance: &T) -> WrappedFFIValue<Self::FFIType, Self::Owned> {
let data = instance.encode();
let ffi_value = pointer_and_len_to_u64(data.as_ptr() as u32, data.len() as u32);
(ffi_value, data).into()
}
fn from_ffi_value(arg: Self::FFIType) -> T {
let (ptr, len) = pointer_and_len_from_u64(arg);
let len = len as usize;
let slice = unsafe { slice::from_raw_parts(ptr as *const u8, len) };
T::decode(&mut &slice[..]).expect("Host to wasm values are encoded correctly; qed")
}
}
/// The type is passed as `u64`.
///
/// The `u64` value is build by `length 32bit << 32 | pointer 32bit`
///
/// `Self` is encoded and the length and the pointer are taken from the encoded vector.
impl<T: codec::Codec> RIType for Codec<T> {
type FFIType = u64;
}
/// Trait that needs to be implemented by a type that should be passed between wasm and the host,
/// by using the inner type. See [`Inner`] for more information.
pub trait PassByInner: Sized {
/// The inner type that is wrapped by `Self`.
type Inner: RIType;
/// Consumes `self` and returns the inner type.
fn into_inner(self) -> Self::Inner;
/// Returns the reference to the inner type.
fn inner(&self) -> &Self::Inner;
/// Construct `Self` from the given `inner`.
fn from_inner(inner: Self::Inner) -> Self;
}
/// The implementation of the pass by inner type strategy. The type that uses this strategy will be
/// passed between wasm and the host by using the wrapped inner type. So, this strategy is only
/// usable by newtype structs.
///
/// Use this type as associated type for [`PassBy`] to implement this strategy for a type. Besides
/// that the `PassByInner` trait need to be implemented as well.
///
/// This type expects the type that wants to use this strategy as generic parameter `T` and the
/// inner type as generic parameter `I`.
///
/// [`PassByInner`](derive.PassByInner.html) is a derive macro to implement this strategy.
///
/// # Example
/// ```
/// # use substrate_runtime_interface::pass_by::{PassBy, Inner, PassByInner};
/// struct Test([u8; 32]);
///
/// impl PassBy for Test {
/// type PassBy = Inner<Self, [u8; 32]>;
/// }
///
/// impl PassByInner for Test {
/// type Inner = [u8; 32];
///
/// fn into_inner(self) -> [u8; 32] {
/// self.0
/// }
/// fn inner(&self) -> &[u8; 32] {
/// &self.0
/// }
/// fn from_inner(inner: [u8; 32]) -> Self {
/// Self(inner)
/// }
/// }
/// ```
pub struct Inner<T: PassByInner<Inner = I>, I: RIType>(PhantomData<(T, I)>);
#[cfg(feature = "std")]
impl<T: PassByInner<Inner = I>, I: RIType> PassByImpl<T> for Inner<T, I>
where I: IntoFFIValue + FromFFIValue<SelfInstance=I>
{
fn into_ffi_value(
instance: T,
context: &mut dyn FunctionContext,
) -> Result<Self::FFIType> {
instance.into_inner().into_ffi_value(context)
}
fn from_ffi_value(
context: &mut dyn FunctionContext,
arg: Self::FFIType,
) -> Result<T> {
I::from_ffi_value(context, arg).map(T::from_inner)
}
}
#[cfg(not(feature = "std"))]
impl<T: PassByInner<Inner = I>, I: RIType> PassByImpl<T> for Inner<T, I>
where I: IntoFFIValue + FromFFIValue
{
type Owned = I::Owned;
fn into_ffi_value(instance: &T) -> WrappedFFIValue<Self::FFIType, Self::Owned> {
instance.inner().into_ffi_value()
}
fn from_ffi_value(arg: Self::FFIType) -> T {
T::from_inner(I::from_ffi_value(arg))
}
}
/// The type is passed as the inner type.
impl<T: PassByInner<Inner = I>, I: RIType> RIType for Inner<T, I> {
type FFIType = I::FFIType;
}
/// The implementation of the pass by enum strategy. This strategy uses an `u8` internally to pass
/// the enum between wasm and the host. So, this strategy only supports enums with unit variants.
///
/// Use this type as associated type for [`PassBy`] to implement this strategy for a type.
///
/// This type expects the type that wants to implement this strategy as generic parameter. Besides
/// that the type needs to implement `TryFrom<u8>` and `From<Self> for u8`.
///
/// [`PassByEnum`](derive.PassByEnum.html) is a derive macro to implement this strategy.
///
/// # Example
/// ```
/// # use substrate_runtime_interface::pass_by::{PassBy, Enum};
/// #[derive(Clone, Copy)]
/// enum Test {
/// Test1,
/// Test2,
/// }
///
/// impl From<Test> for u8 {
/// fn from(val: Test) -> u8 {
/// match val {
/// Test::Test1 => 0,
/// Test::Test2 => 1,
/// }
/// }
/// }
///
/// impl std::convert::TryFrom<u8> for Test {
/// type Error = ();
///
/// fn try_from(val: u8) -> Result<Test, ()> {
/// match val {
/// 0 => Ok(Test::Test1),
/// 1 => Ok(Test::Test2),
/// _ => Err(()),
/// }
/// }
/// }
///
/// impl PassBy for Test {
/// type PassBy = Enum<Self>;
/// }
/// ```
pub struct Enum<T: Copy + Into<u8> + TryFrom<u8>>(PhantomData<T>);
#[cfg(feature = "std")]
impl<T: Copy + Into<u8> + TryFrom<u8>> PassByImpl<T> for Enum<T> {
fn into_ffi_value(
instance: T,
_: &mut dyn FunctionContext,
) -> Result<Self::FFIType> {
Ok(instance.into())
}
fn from_ffi_value(
_: &mut dyn FunctionContext,
arg: Self::FFIType,
) -> Result<T> {
T::try_from(arg).map_err(|_| format!("Invalid enum discriminant: {}", arg))
}
}
#[cfg(not(feature = "std"))]
impl<T: Copy + Into<u8> + TryFrom<u8, Error = ()>> PassByImpl<T> for Enum<T> {
type Owned = ();
fn into_ffi_value(instance: &T) -> WrappedFFIValue<Self::FFIType, Self::Owned> {
let value: u8 = (*instance).into();
value.into()
}
fn from_ffi_value(arg: Self::FFIType) -> T {
T::try_from(arg).expect("Host to wasm provides a valid enum discriminant; qed")
}
}
/// The type is passed as `u8`.
///
/// The value is corresponds to the discriminant of the variant.
impl<T: Copy + Into<u8> + TryFrom<u8>> RIType for Enum<T> {
type FFIType = u8;
}
@@ -0,0 +1,142 @@
// 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/>.
//! Traits required by the runtime interface from the wasm side.
use crate::RIType;
use rstd::cell::Cell;
/// Something that can be created from a ffi value.
///
/// # Safety
///
/// It is unsafe behavior to call `Something::into_ffi_value().get()` and take this as input for
/// `from_ffi_value`. Implementations are safe to assume that the `arg` given to `from_ffi_value`
/// is only generated by the corresponding `host::IntoFFIValue` implementation.
pub trait FromFFIValue: Sized + RIType {
/// Create `Self` from the given ffi value.
fn from_ffi_value(arg: Self::FFIType) -> Self;
}
/// Something that can be converted into a ffi value.
pub trait IntoFFIValue: RIType {
/// The owned rust type that is stored with the ffi value in [`WrappedFFIValue`].
///
/// If no owned value is required, `()` can be used as a type.
type Owned;
/// Convert `self` into a [`WrappedFFIValue`].
fn into_ffi_value(&self) -> WrappedFFIValue<Self::FFIType, Self::Owned>;
}
/// Represents a wrapped ffi value.
///
/// It is either the ffi value itself or the ffi value plus some other owned value. By providing
/// support for storing another owned value besides the actual ffi value certain performance
/// optimizations can be applied. For example using the pointer to a `Vec<u8>`, while using the
/// pointer to a SCALE encoded `Vec<u8>` that is stored in this wrapper for any other `Vec<T>`.
pub enum WrappedFFIValue<T, O = ()> {
Wrapped(T),
WrappedAndOwned(T, O),
}
impl<T: Copy, O> WrappedFFIValue<T, O> {
/// Returns the wrapped ffi value.
pub fn get(&self) -> T {
match self {
Self::Wrapped(data) | Self::WrappedAndOwned(data, _) => *data,
}
}
}
impl<T, O> From<T> for WrappedFFIValue<T, O> {
fn from(val: T) -> Self {
WrappedFFIValue::Wrapped(val)
}
}
impl<T, O> From<(T, O)> for WrappedFFIValue<T, O> {
fn from(val: (T, O)) -> Self {
WrappedFFIValue::WrappedAndOwned(val.0, val.1)
}
}
/// The state of an exchangeable function.
#[derive(Clone, Copy)]
enum ExchangeableFunctionState {
/// Original function is present
Original,
/// The function has been replaced.
Replaced,
}
/// A function which implementation can be exchanged.
///
/// Internally this works by swapping function pointers.
pub struct ExchangeableFunction<T>(Cell<(T, ExchangeableFunctionState)>);
impl<T> ExchangeableFunction<T> {
/// Create a new instance of `ExchangeableFunction`.
pub const fn new(impl_: T) -> Self {
Self(Cell::new((impl_, ExchangeableFunctionState::Original)))
}
}
impl<T: Copy> ExchangeableFunction<T> {
/// Replace the implementation with `new_impl`.
///
/// # Panics
///
/// Panics when trying to replace an already replaced implementation.
///
/// # Returns
///
/// Returns the original implementation wrapped in [`RestoreImplementation`].
pub fn replace_implementation(&'static self, new_impl: T) -> RestoreImplementation<T> {
if let ExchangeableFunctionState::Replaced = self.0.get().1 {
panic!("Trying to replace an already replaced implementation!")
}
let old = self.0.replace((new_impl, ExchangeableFunctionState::Replaced));
RestoreImplementation(self, Some(old.0))
}
/// Restore the original implementation.
fn restore_orig_implementation(&self, orig: T) {
self.0.set((orig, ExchangeableFunctionState::Original));
}
/// Returns the internal function pointer.
pub fn get(&self) -> T {
self.0.get().0
}
}
// Wasm does not support threads, so this is safe; qed.
unsafe impl<T> Sync for ExchangeableFunction<T> {}
/// Restores a function implementation on drop.
///
/// Stores a static reference to the function object and the original implementation.
pub struct RestoreImplementation<T: 'static + Copy>(&'static ExchangeableFunction<T>, Option<T>);
impl<T: Copy> Drop for RestoreImplementation<T> {
fn drop(&mut self) {
self.0.restore_orig_implementation(self.1.take().expect("Value is only taken on drop; qed"));
}
}
@@ -0,0 +1,19 @@
[package]
name = "substrate-runtime-interface-test-wasm"
version = "2.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
build = "build.rs"
[dependencies]
runtime-interface = { package = "substrate-runtime-interface", path = "../", default-features = false }
rstd = { package = "sr-std", path = "../../sr-std", default-features = false }
runtime-io = { package = "sr-io", path = "../../sr-io", default-features = false }
primitives = { package = "substrate-primitives", path = "../../primitives", default-features = false }
[build-dependencies]
wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1.0.3", path = "../../utils/wasm-builder-runner" }
[features]
default = [ "std" ]
std = [ "runtime-interface/std", "rstd/std", "primitives/std", "runtime-io/std" ]
@@ -0,0 +1,30 @@
// 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 wasm_builder_runner::{build_current_project_with_rustflags, WasmBuilderSource};
fn main() {
build_current_project_with_rustflags(
"wasm_binary.rs",
WasmBuilderSource::CratesOrPath {
path: "../../utils/wasm-builder",
version: "1.0.6",
},
// This instructs LLD to export __heap_base as a global variable, which is used by the
// external memory allocator.
"-Clink-arg=--export=__heap_base",
);
}
@@ -0,0 +1,194 @@
// 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/>.
//! Tests for the runtime interface traits and proc macros.
#![cfg_attr(not(feature = "std"), no_std)]
use runtime_interface::runtime_interface;
#[cfg(not(feature = "std"))]
use rstd::{vec, vec::Vec, mem, convert::TryFrom};
use primitives::{sr25519::Public, wasm_export_functions};
// Inlucde the WASM binary
#[cfg(feature = "std")]
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
/// Used in the `test_array_as_mutable_reference` test.
const TEST_ARRAY: [u8; 16] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
#[runtime_interface]
pub trait TestApi {
/// Returns the input data as result.
fn return_input(data: Vec<u8>) -> Vec<u8> {
data
}
/// Set the storage at key with value.
fn set_storage(&mut self, key: &[u8], data: &[u8]) {
self.place_storage(key.to_vec(), Some(data.to_vec()));
}
/// Copy `hello` into the given mutable reference
fn return_value_into_mutable_reference(&self, data: &mut [u8]) {
let res = "hello";
data[..res.as_bytes().len()].copy_from_slice(res.as_bytes());
}
/// Returns the input data wrapped in an `Option` as result.
fn return_option_input(data: Vec<u8>) -> Option<Vec<u8>> {
Some(data)
}
/// Get an array as input and returns a subset of this array.
fn get_and_return_array(data: [u8; 34]) -> [u8; 16] {
let mut res = [0u8; 16];
res.copy_from_slice(&data[..16]);
res
}
/// Take and fill mutable array.
fn array_as_mutable_reference(data: &mut [u8; 16]) {
data.copy_from_slice(&TEST_ARRAY);
}
/// Returns the given public key as result.
fn return_input_public_key(key: Public) -> Public {
key
}
/// A function that is called with invalid utf8 data from the runtime.
///
/// This also checks that we accept `_` (wild card) argument names.
fn invalid_utf8_data(_: &str) {}
/// Overwrite the native implementation in wasm. The native implementation always returns
/// `false` and the replacement function will return always `true`.
fn overwrite_native_function_implementation() -> bool {
false
}
}
/// Two random external functions from the old runtime interface.
/// This ensures that we still inherently export these functions from the host and that we are still
/// compatible with old wasm runtimes.
extern "C" {
pub fn ext_clear_storage(key_data: *const u8, key_len: u32);
pub fn ext_keccak_256(data: *const u8, len: u32, out: *mut u8);
}
/// Make sure the old runtime interface needs to be imported.
#[no_mangle]
pub fn force_old_runtime_interface_import() {
unsafe { ext_clear_storage(rstd::ptr::null(), 0); }
unsafe { ext_keccak_256(rstd::ptr::null(), 0, rstd::ptr::null_mut()); }
}
/// This function is not used, but we require it for the compiler to include `runtime-io`.
/// `runtime-io` is required for its panic and oom handler.
#[no_mangle]
pub fn import_runtime_io() {
runtime_io::misc::print_utf8(&[]);
}
wasm_export_functions! {
fn test_return_data() {
let input = vec![1, 2, 3, 4, 5, 6];
let res = test_api::return_input(input.clone());
assert_eq!(input, res);
}
fn test_return_option_data() {
let input = vec![1, 2, 3, 4, 5, 6];
let res = test_api::return_option_input(input.clone());
assert_eq!(Some(input), res);
}
fn test_set_storage() {
let key = "hello";
let value = "world";
test_api::set_storage(key.as_bytes(), value.as_bytes());
}
fn test_return_value_into_mutable_reference() {
let mut data = vec![1, 2, 3, 4, 5, 6];
test_api::return_value_into_mutable_reference(&mut data);
let expected = "hello";
assert_eq!(expected.as_bytes(), &data[..expected.len()]);
}
fn test_get_and_return_array() {
let mut input = unsafe { mem::MaybeUninit::<[u8; 34]>::zeroed().assume_init() };
input.copy_from_slice(&[
24, 3, 23, 20, 2, 16, 32, 1, 12, 26, 27, 8, 29, 31, 6, 5, 4, 19, 10, 28, 34, 21, 18, 33, 9,
13, 22, 25, 15, 11, 30, 7, 14, 17,
]);
let res = test_api::get_and_return_array(input);
assert_eq!(&res, &input[..16]);
}
fn test_array_as_mutable_reference() {
let mut array = [0u8; 16];
test_api::array_as_mutable_reference(&mut array);
assert_eq!(array, TEST_ARRAY);
}
fn test_return_input_public_key() {
let key = Public::try_from(
&[
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32,
][..],
).unwrap();
let ret_key = test_api::return_input_public_key(key.clone());
let key_data: &[u8] = key.as_ref();
let ret_key_data: &[u8] = ret_key.as_ref();
assert_eq!(key_data, ret_key_data);
}
fn test_invalid_utf8_data_should_return_an_error() {
let data = vec![0, 159, 146, 150];
// I'm an evil hacker, trying to hack!
let data_str = unsafe { rstd::str::from_utf8_unchecked(&data) };
test_api::invalid_utf8_data(data_str);
}
fn test_overwrite_native_function_implementation() {
fn new_implementation() -> bool {
true
}
// Check native implementation
assert!(!test_api::overwrite_native_function_implementation());
let _guard = test_api::host_overwrite_native_function_implementation
.replace_implementation(new_implementation);
assert!(test_api::overwrite_native_function_implementation());
}
}