mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 07:41:08 +00:00
Reorganising the repository - external renames and moves (#4074)
* Adding first rough ouline of the repository structure * Remove old CI stuff * add title * formatting fixes * move node-exits job's script to scripts dir * Move docs into subdir * move to bin * move maintainence scripts, configs and helpers into its own dir * add .local to ignore * move core->client * start up 'test' area * move test client * move test runtime * make test move compile * Add dependencies rule enforcement. * Fix indexing. * Update docs to reflect latest changes * Moving /srml->/paint * update docs * move client/sr-* -> primitives/ * clean old readme * remove old broken code in rhd * update lock * Step 1. * starting to untangle client * Fix after merge. * start splitting out client interfaces * move children and blockchain interfaces * Move trie and state-machine to primitives. * Fix WASM builds. * fixing broken imports * more interface moves * move backend and light to interfaces * move CallExecutor * move cli off client * moving around more interfaces * re-add consensus crates into the mix * fix subkey path * relieve client from executor * starting to pull out client from grandpa * move is_decendent_of out of client * grandpa still depends on client directly * lemme tests pass * rename srml->paint * Make it compile. * rename interfaces->client-api * Move keyring to primitives. * fixup libp2p dep * fix broken use * allow dependency enforcement to fail * move fork-tree * Moving wasm-builder * make env * move build-script-utils * fixup broken crate depdencies and names * fix imports for authority discovery * fix typo * update cargo.lock * fixing imports * Fix paths and add missing crates * re-add missing crates
This commit is contained in:
committed by
Bastian Köcher
parent
becc3b0a4f
commit
60e5011c72
@@ -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 = "../../client/executor" }
|
||||
test-wasm = { package = "substrate-runtime-interface-test-wasm", path = "test-wasm" }
|
||||
state_machine = { package = "substrate-state-machine", path = "../../primitives/state-machine" }
|
||||
primitives = { package = "substrate-primitives", path = "../core" }
|
||||
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,265 @@
|
||||
// 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`.
|
||||
/// ///
|
||||
/// /// `TRAIT_NAME` is converted into snake case.
|
||||
/// ///
|
||||
/// /// The type for each argument of the exported function depends on
|
||||
/// /// `<ARGUMENT_TYPE as RIType>::FFIType`.
|
||||
/// ///
|
||||
/// /// `data` holds the pointer and the length to the `[u8]` slice.
|
||||
/// pub fn ext_Interface_call_some_complex_code_version_1(data: u64) -> u64;
|
||||
/// /// `optional` holds the pointer and the length of the encoded value.
|
||||
/// pub fn ext_Interface_set_or_clear_version_1(optional: u64);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// /// The type is actually `ExchangeableFunction` (from `substrate-runtime-interface`).
|
||||
/// ///
|
||||
/// /// This can be used to replace the implementation of the `call_some_complex_code` function.
|
||||
/// /// Instead of calling into the host, the callee will automatically call the other
|
||||
/// /// implementation.
|
||||
/// ///
|
||||
/// /// To replace the implementation:
|
||||
/// ///
|
||||
/// /// `host_call_some_complex_code.replace_implementation(some_other_impl)`
|
||||
/// pub static host_call_some_complex_code: () = ();
|
||||
/// pub static host_set_or_clear: () = ();
|
||||
///
|
||||
/// pub fn call_some_complex_code(data: &[u8]) -> Vec<u8> {
|
||||
/// // This is the actual call: `host_call_some_complex_code.get()(data)`
|
||||
/// //
|
||||
/// // But that does not work for several reasons in this example, so we just return an
|
||||
/// // empty vector.
|
||||
/// Vec::new()
|
||||
/// }
|
||||
///
|
||||
/// pub fn set_or_clear(optional: Option<Vec<u8>>) {
|
||||
/// // Same as above
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Argument types
|
||||
///
|
||||
/// The macro supports any kind of argument type, as long as it implements `RIType` and the required
|
||||
/// `FromFFIValue`/`IntoFFIValue` from `substrate-runtime-interface`. The macro will convert each
|
||||
/// argument to the corresponding FFI representation and will call into the host using this FFI
|
||||
/// representation. On the host each argument is converted back to the native representation and
|
||||
/// the native implementation is called. Any return value is handled in the same way.
|
||||
///
|
||||
/// # Wasm only interfaces
|
||||
///
|
||||
/// Some interfaces are only required from within the wasm runtime e.g. the allocator interface.
|
||||
/// To support this, the macro can be called like `#[runtime_interface(wasm_only)]`. This instructs
|
||||
/// the macro to make two significant changes to the generated code:
|
||||
///
|
||||
/// 1. The generated functions are not callable from the native side.
|
||||
/// 2. The trait as shown above is not implemented for `Externalities` and is instead implemented
|
||||
/// for `FunctionExecutor` (from `substrate-wasm-interface`).
|
||||
#[proc_macro_attribute]
|
||||
pub fn runtime_interface(
|
||||
attrs: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
let trait_def = parse_macro_input!(input as ItemTrait);
|
||||
let wasm_only = parse_macro_input!(attrs as Option<runtime_interface::keywords::wasm_only>);
|
||||
|
||||
runtime_interface::runtime_interface_impl(trait_def, wasm_only.is_some())
|
||||
.unwrap_or_else(|e| e.to_compile_error())
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Derive macro for implementing `PassBy` with the `Codec` strategy.
|
||||
///
|
||||
/// This requires that the type implements `Encode` and `Decode` from `parity-scale-codec`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use runtime_interface::pass_by::PassByCodec;
|
||||
/// # use codec::{Encode, Decode};
|
||||
/// #[derive(PassByCodec, Encode, Decode)]
|
||||
/// struct EncodableType {
|
||||
/// name: Vec<u8>,
|
||||
/// param: u32,
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_derive(PassByCodec)]
|
||||
pub fn pass_by_codec(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
pass_by::codec_derive_impl(input).unwrap_or_else(|e| e.to_compile_error()).into()
|
||||
}
|
||||
|
||||
/// Derive macro for implementing `PassBy` with the `Inner` strategy.
|
||||
///
|
||||
/// Besides implementing `PassBy`, this derive also implements the helper trait `PassByInner`.
|
||||
///
|
||||
/// The type is required to be a struct with just one field. The field type needs to implement
|
||||
/// the required traits to pass it between the wasm and the native side. (See the runtime interface
|
||||
/// crate for more information about these traits.)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use runtime_interface::pass_by::PassByInner;
|
||||
/// #[derive(PassByInner)]
|
||||
/// struct Data([u8; 32]);
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// # use runtime_interface::pass_by::PassByInner;
|
||||
/// #[derive(PassByInner)]
|
||||
/// struct Data {
|
||||
/// data: [u8; 32],
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_derive(PassByInner)]
|
||||
pub fn pass_by_inner(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
pass_by::inner_derive_impl(input).unwrap_or_else(|e| e.to_compile_error()).into()
|
||||
}
|
||||
|
||||
/// Derive macro for implementing `PassBy` with the `Enum` strategy.
|
||||
///
|
||||
/// Besides implementing `PassBy`, this derive also implements `TryFrom<u8>` and `From<Self> for u8`
|
||||
/// for the type.
|
||||
///
|
||||
/// The type is required to be an enum with only unit variants and at maximum `256` variants. Also
|
||||
/// it is required that the type implements `Copy`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use runtime_interface::pass_by::PassByEnum;
|
||||
/// #[derive(PassByEnum, Copy, Clone)]
|
||||
/// enum Data {
|
||||
/// Okay,
|
||||
/// NotOkay,
|
||||
/// // This will not work with the derive.
|
||||
/// //Why(u32),
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_derive(PassByEnum)]
|
||||
pub fn pass_by_enum(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
pass_by::enum_derive_impl(input).unwrap_or_else(|e| e.to_compile_error()).into()
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Derive macro implementation of `PassBy` with the associated type set to `Codec`.
|
||||
//!
|
||||
//! It is required that the type implements `Encode` and `Decode` from the `parity-scale-codec`
|
||||
//! crate.
|
||||
|
||||
use crate::utils::{generate_crate_access, generate_runtime_interface_include};
|
||||
|
||||
use syn::{DeriveInput, Result, Generics, parse_quote};
|
||||
|
||||
use quote::quote;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
/// The derive implementation for `PassBy` with `Codec`.
|
||||
pub fn derive_impl(mut input: DeriveInput) -> Result<TokenStream> {
|
||||
add_trait_bounds(&mut input.generics);
|
||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
let crate_include = generate_runtime_interface_include();
|
||||
let crate_ = generate_crate_access();
|
||||
let ident = input.ident;
|
||||
|
||||
let res = quote! {
|
||||
const _: () = {
|
||||
#crate_include
|
||||
|
||||
impl #impl_generics #crate_::pass_by::PassBy for #ident #ty_generics #where_clause {
|
||||
type PassBy = #crate_::pass_by::Codec<#ident>;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Add the `codec::Codec` trait bound to every type parameter.
|
||||
fn add_trait_bounds(generics: &mut Generics) {
|
||||
let crate_ = generate_crate_access();
|
||||
|
||||
generics.type_params_mut()
|
||||
.for_each(|type_param| type_param.bounds.push(parse_quote!(#crate_::codec::Codec)));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Derive macro implementation of `PassBy` with the associated type set to `Enum`.
|
||||
//!
|
||||
//! Besides `PassBy`, `TryFrom<u8>` and `From<Self> for u8` are implemented for the type.
|
||||
|
||||
use crate::utils::{generate_crate_access, generate_runtime_interface_include};
|
||||
|
||||
use syn::{DeriveInput, Result, Data, Fields, Error, Ident};
|
||||
|
||||
use quote::quote;
|
||||
|
||||
use proc_macro2::{TokenStream, Span};
|
||||
|
||||
/// The derive implementation for `PassBy` with `Enum`.
|
||||
pub fn derive_impl(input: DeriveInput) -> Result<TokenStream> {
|
||||
let crate_include = generate_runtime_interface_include();
|
||||
let crate_ = generate_crate_access();
|
||||
let ident = input.ident;
|
||||
let enum_fields = get_enum_field_idents(&input.data)?
|
||||
.enumerate()
|
||||
.map(|(i, v)| {
|
||||
let i = i as u8;
|
||||
|
||||
v.map(|v| (quote!(#i => Ok(#ident::#v)), quote!(#ident::#v => #i)))
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
let try_from_variants = enum_fields.iter().map(|i| &i.0);
|
||||
let into_variants = enum_fields.iter().map(|i| &i.1);
|
||||
|
||||
let res = quote! {
|
||||
const _: () = {
|
||||
#crate_include
|
||||
|
||||
impl #crate_::pass_by::PassBy for #ident {
|
||||
type PassBy = #crate_::pass_by::Enum<#ident>;
|
||||
}
|
||||
|
||||
impl #crate_::rstd::convert::TryFrom<u8> for #ident {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(inner: u8) -> #crate_::rstd::result::Result<Self, ()> {
|
||||
match inner {
|
||||
#( #try_from_variants, )*
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<#ident> for u8 {
|
||||
fn from(var: #ident) -> u8 {
|
||||
match var {
|
||||
#( #into_variants ),*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Get the enum fields idents of the given `data` object as iterator.
|
||||
///
|
||||
/// Returns an error if the number of variants is greater than `256`, the given `data` is not an
|
||||
/// enum or a variant is not an unit.
|
||||
fn get_enum_field_idents<'a>(data: &'a Data) -> Result<impl Iterator<Item = Result<&'a Ident>>> {
|
||||
match data {
|
||||
Data::Enum(d) => {
|
||||
if d.variants.len() <= 256 {
|
||||
Ok(
|
||||
d.variants.iter().map(|v| if let Fields::Unit = v.fields {
|
||||
Ok(&v.ident)
|
||||
} else {
|
||||
Err(Error::new(
|
||||
Span::call_site(),
|
||||
"`PassByEnum` only supports unit variants.",
|
||||
))
|
||||
})
|
||||
)
|
||||
} else {
|
||||
Err(Error::new(Span::call_site(), "`PassByEnum` only supports `256` variants."))
|
||||
}
|
||||
},
|
||||
_ => Err(Error::new(Span::call_site(), "`PassByEnum` only supports enums as input type."))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Derive macro implementation of `PassBy` with the associated type set to `Inner` and of the
|
||||
//! helper trait `PassByInner`.
|
||||
//!
|
||||
//! It is required that the type is a newtype struct, otherwise an error is generated.
|
||||
|
||||
use crate::utils::{generate_crate_access, generate_runtime_interface_include};
|
||||
|
||||
use syn::{DeriveInput, Result, Generics, parse_quote, Type, Data, Error, Fields, Ident};
|
||||
|
||||
use quote::quote;
|
||||
|
||||
use proc_macro2::{TokenStream, Span};
|
||||
|
||||
/// The derive implementation for `PassBy` with `Inner` and `PassByInner`.
|
||||
pub fn derive_impl(mut input: DeriveInput) -> Result<TokenStream> {
|
||||
add_trait_bounds(&mut input.generics);
|
||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
let crate_include = generate_runtime_interface_include();
|
||||
let crate_ = generate_crate_access();
|
||||
let ident = input.ident;
|
||||
let (inner_ty, inner_name) = extract_inner_ty_and_name(&input.data)?;
|
||||
|
||||
let access_inner = match inner_name {
|
||||
Some(ref name) => quote!(self.#name),
|
||||
None => quote!(self.0),
|
||||
};
|
||||
|
||||
let from_inner = match inner_name {
|
||||
Some(name) => quote!(Self { #name: inner }),
|
||||
None => quote!(Self(inner)),
|
||||
};
|
||||
|
||||
let res = quote! {
|
||||
const _: () = {
|
||||
#crate_include
|
||||
|
||||
impl #impl_generics #crate_::pass_by::PassBy for #ident #ty_generics #where_clause {
|
||||
type PassBy = #crate_::pass_by::Inner<#ident, #inner_ty>;
|
||||
}
|
||||
|
||||
impl #impl_generics #crate_::pass_by::PassByInner for #ident #ty_generics #where_clause {
|
||||
type Inner = #inner_ty;
|
||||
|
||||
fn into_inner(self) -> Self::Inner {
|
||||
#access_inner
|
||||
}
|
||||
|
||||
fn inner(&self) -> &Self::Inner {
|
||||
&#access_inner
|
||||
}
|
||||
|
||||
fn from_inner(inner: Self::Inner) -> Self {
|
||||
#from_inner
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Add the `RIType` trait bound to every type parameter.
|
||||
fn add_trait_bounds(generics: &mut Generics) {
|
||||
let crate_ = generate_crate_access();
|
||||
|
||||
generics.type_params_mut()
|
||||
.for_each(|type_param| type_param.bounds.push(parse_quote!(#crate_::RIType)));
|
||||
}
|
||||
|
||||
/// Extract the inner type and optional name from given input data.
|
||||
///
|
||||
/// It also checks that the input data is a newtype struct.
|
||||
fn extract_inner_ty_and_name(data: &Data) -> Result<(Type, Option<Ident>)> {
|
||||
if let Data::Struct(ref struct_data) = data {
|
||||
match struct_data.fields {
|
||||
Fields::Named(ref named) if named.named.len() == 1 => {
|
||||
let field = &named.named[0];
|
||||
return Ok((field.ty.clone(), field.ident.clone()))
|
||||
},
|
||||
Fields::Unnamed(ref unnamed) if unnamed.unnamed.len() == 1 => {
|
||||
let field = &unnamed.unnamed[0];
|
||||
return Ok((field.ty.clone(), field.ident.clone()))
|
||||
}
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
Err(
|
||||
Error::new(
|
||||
Span::call_site(),
|
||||
"Only newtype/one field structs are supported by `PassByInner`!",
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! All the `PassBy*` derive implementations.
|
||||
|
||||
mod codec;
|
||||
mod enum_;
|
||||
mod inner;
|
||||
|
||||
pub use self::codec::derive_impl as codec_derive_impl;
|
||||
pub use enum_::derive_impl as enum_derive_impl;
|
||||
pub use inner::derive_impl as inner_derive_impl;
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Generates the bare function interface for a given trait definition.
|
||||
//!
|
||||
//! The bare functions are the ones that will be called by the user. On the native/host side, these
|
||||
//! functions directly execute the provided implementation. On the wasm side, these
|
||||
//! functions will prepare the parameters for the FFI boundary, call the external host function
|
||||
//! exported into wasm and convert back the result.
|
||||
//!
|
||||
//! [`generate`](bare_function_interface::generate) is the entry point for generating for each
|
||||
//! trait method one bare function.
|
||||
//!
|
||||
//! [`function_for_method`](bare_function_interface::function_for_method) generates the bare
|
||||
//! function per trait method. Each bare function contains both implementations. The implementations
|
||||
//! are feature-gated, so that one is compiled for the native and the other for the wasm side.
|
||||
|
||||
use crate::utils::{
|
||||
generate_crate_access, create_exchangeable_host_function_ident, get_function_arguments,
|
||||
get_function_argument_names, get_trait_methods,
|
||||
};
|
||||
|
||||
use syn::{
|
||||
Ident, ItemTrait, TraitItemMethod, FnArg, Signature, Result, spanned::Spanned, parse_quote,
|
||||
};
|
||||
|
||||
use proc_macro2::{TokenStream, Span};
|
||||
|
||||
use quote::{quote, quote_spanned};
|
||||
|
||||
use std::iter;
|
||||
|
||||
/// Generate one bare function per trait method. The name of the bare function is equal to the name
|
||||
/// of the trait method.
|
||||
pub fn generate(trait_def: &ItemTrait, is_wasm_only: bool) -> Result<TokenStream> {
|
||||
let trait_name = &trait_def.ident;
|
||||
get_trait_methods(trait_def).try_fold(TokenStream::new(), |mut t, m| {
|
||||
t.extend(function_for_method(trait_name, m, is_wasm_only)?);
|
||||
Ok(t)
|
||||
})
|
||||
}
|
||||
|
||||
/// Generates the bare function implementation for the given method for the host and wasm side.
|
||||
fn function_for_method(
|
||||
trait_name: &Ident,
|
||||
method: &TraitItemMethod,
|
||||
is_wasm_only: bool,
|
||||
) -> Result<TokenStream> {
|
||||
let std_impl = function_std_impl(trait_name, method, is_wasm_only)?;
|
||||
let no_std_impl = function_no_std_impl(method)?;
|
||||
|
||||
Ok(
|
||||
quote! {
|
||||
#std_impl
|
||||
|
||||
#no_std_impl
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Generates the bare function implementation for `cfg(not(feature = "std"))`.
|
||||
fn function_no_std_impl(method: &TraitItemMethod) -> Result<TokenStream> {
|
||||
let function_name = &method.sig.ident;
|
||||
let host_function_name = create_exchangeable_host_function_ident(&method.sig.ident);
|
||||
let args = get_function_arguments(&method.sig);
|
||||
let arg_names = get_function_argument_names(&method.sig);
|
||||
let return_value = &method.sig.output;
|
||||
let attrs = &method.attrs;
|
||||
|
||||
Ok(
|
||||
quote! {
|
||||
#[cfg(not(feature = "std"))]
|
||||
#( #attrs )*
|
||||
pub fn #function_name( #( #args, )* ) #return_value {
|
||||
// Call the host function
|
||||
#host_function_name.get()( #( #arg_names, )* )
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Generates the bare function implementation for `cfg(feature = "std")`.
|
||||
fn function_std_impl(
|
||||
trait_name: &Ident,
|
||||
method: &TraitItemMethod,
|
||||
is_wasm_only: bool,
|
||||
) -> Result<TokenStream> {
|
||||
let function_name = &method.sig.ident;
|
||||
let crate_ = generate_crate_access();
|
||||
let args = get_function_arguments(&method.sig).map(FnArg::Typed).chain(
|
||||
// Add the function context as last parameter when this is a wasm only interface.
|
||||
iter::from_fn(||
|
||||
if is_wasm_only {
|
||||
Some(
|
||||
parse_quote!(
|
||||
mut __function_context__: &mut dyn #crate_::wasm_interface::FunctionContext
|
||||
)
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
).take(1),
|
||||
);
|
||||
let return_value = &method.sig.output;
|
||||
let attrs = &method.attrs;
|
||||
// Don't make the function public accessible when this is a wasm only interface.
|
||||
let vis = if is_wasm_only { quote!() } else { quote!(pub) };
|
||||
let call_to_trait = generate_call_to_trait(trait_name, method, is_wasm_only);
|
||||
|
||||
Ok(
|
||||
quote_spanned! { method.span() =>
|
||||
#[cfg(feature = "std")]
|
||||
#( #attrs )*
|
||||
#vis fn #function_name( #( #args, )* ) #return_value {
|
||||
#call_to_trait
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Generate the call to the interface trait.
|
||||
fn generate_call_to_trait(
|
||||
trait_name: &Ident,
|
||||
method: &TraitItemMethod,
|
||||
is_wasm_only: bool,
|
||||
) -> TokenStream {
|
||||
let crate_ = generate_crate_access();
|
||||
let method_name = &method.sig.ident;
|
||||
let expect_msg = format!(
|
||||
"`{}` called outside of an Externalities-provided environment.",
|
||||
method_name,
|
||||
);
|
||||
let arg_names = get_function_argument_names(&method.sig);
|
||||
|
||||
if takes_self_argument(&method.sig) {
|
||||
let instance = if is_wasm_only {
|
||||
Ident::new("__function_context__", Span::call_site())
|
||||
} else {
|
||||
Ident::new("__externalities__", Span::call_site())
|
||||
};
|
||||
|
||||
let impl_ = quote!( #trait_name::#method_name(&mut #instance, #( #arg_names, )*) );
|
||||
|
||||
if is_wasm_only {
|
||||
quote_spanned! { method.span() => #impl_ }
|
||||
} else {
|
||||
quote_spanned! { method.span() =>
|
||||
#crate_::with_externalities(|mut #instance| #impl_).expect(#expect_msg)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// The name of the trait the interface trait is implemented for
|
||||
let impl_trait_name = if is_wasm_only {
|
||||
quote!( #crate_::wasm_interface::FunctionContext )
|
||||
} else {
|
||||
quote!( #crate_::Externalities )
|
||||
};
|
||||
|
||||
quote_spanned! { method.span() =>
|
||||
<&mut dyn #impl_trait_name as #trait_name>::#method_name(
|
||||
#( #arg_names, )*
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns if the given `Signature` takes a `self` argument.
|
||||
fn takes_self_argument(sig: &Signature) -> bool {
|
||||
match sig.inputs.first() {
|
||||
Some(FnArg::Receiver(_)) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
+415
@@ -0,0 +1,415 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Generates the extern host functions and the implementation for these host functions.
|
||||
//!
|
||||
//! The extern host functions will be called by the bare function interface from the Wasm side.
|
||||
//! The implementation of these host functions will be called on the host side from the Wasm
|
||||
//! executor. These implementations call the bare function interface.
|
||||
|
||||
use crate::utils::{
|
||||
generate_crate_access, create_host_function_ident, get_function_argument_names,
|
||||
get_function_argument_types_without_ref, get_function_argument_types_ref_and_mut,
|
||||
get_function_argument_names_and_types_without_ref, get_trait_methods, get_function_arguments,
|
||||
get_function_argument_types, create_exchangeable_host_function_ident,
|
||||
};
|
||||
|
||||
use syn::{
|
||||
ItemTrait, TraitItemMethod, Result, ReturnType, Ident, TraitItem, Pat, Error, Signature,
|
||||
spanned::Spanned,
|
||||
};
|
||||
|
||||
use proc_macro2::{TokenStream, Span};
|
||||
|
||||
use quote::{quote, ToTokens};
|
||||
|
||||
use inflector::Inflector;
|
||||
|
||||
use std::iter::{Iterator, self};
|
||||
|
||||
/// Generate the extern host functions for wasm and the `HostFunctions` struct that provides the
|
||||
/// implementations for the host functions on the host.
|
||||
pub fn generate(trait_def: &ItemTrait, is_wasm_only: bool) -> Result<TokenStream> {
|
||||
let trait_name = &trait_def.ident;
|
||||
let extern_host_function_impls = get_trait_methods(trait_def)
|
||||
.try_fold(TokenStream::new(), |mut t, m| {
|
||||
t.extend(generate_extern_host_function(m, trait_name)?);
|
||||
Ok::<_, Error>(t)
|
||||
})?;
|
||||
let exchangeable_host_functions = get_trait_methods(trait_def)
|
||||
.try_fold(TokenStream::new(), |mut t, m| {
|
||||
t.extend(generate_exchangeable_host_function(m)?);
|
||||
Ok::<_, Error>(t)
|
||||
})?;
|
||||
let host_functions_struct = generate_host_functions_struct(trait_def, is_wasm_only)?;
|
||||
|
||||
Ok(
|
||||
quote! {
|
||||
/// The implementations of the extern host functions. This special implementation module
|
||||
/// is required to change the extern host functions signature to
|
||||
/// `unsafe fn name(args) -> ret` to make the function implementations exchangeable.
|
||||
#[cfg(not(feature = "std"))]
|
||||
mod extern_host_function_impls {
|
||||
use super::*;
|
||||
|
||||
#extern_host_function_impls
|
||||
}
|
||||
|
||||
#exchangeable_host_functions
|
||||
|
||||
#host_functions_struct
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Generate the extern host function for the given method.
|
||||
fn generate_extern_host_function(method: &TraitItemMethod, trait_name: &Ident) -> Result<TokenStream> {
|
||||
let crate_ = generate_crate_access();
|
||||
let args = get_function_arguments(&method.sig);
|
||||
let arg_types = get_function_argument_types_without_ref(&method.sig);
|
||||
let arg_types2 = get_function_argument_types_without_ref(&method.sig);
|
||||
let arg_names = get_function_argument_names(&method.sig);
|
||||
let arg_names2 = get_function_argument_names(&method.sig);
|
||||
let arg_names3 = get_function_argument_names(&method.sig);
|
||||
let function = &method.sig.ident;
|
||||
let ext_function = create_host_function_ident(&method.sig.ident, trait_name);
|
||||
let doc_string = format!(
|
||||
" Default extern host function implementation for [`super::{}`].",
|
||||
method.sig.ident,
|
||||
);
|
||||
let return_value = &method.sig.output;
|
||||
|
||||
let ffi_return_value = match method.sig.output {
|
||||
ReturnType::Default => quote!(),
|
||||
ReturnType::Type(_, ref ty) => quote! {
|
||||
-> <#ty as #crate_::RIType>::FFIType
|
||||
},
|
||||
};
|
||||
|
||||
let convert_return_value = match return_value {
|
||||
ReturnType::Default => quote!(),
|
||||
ReturnType::Type(_, ref ty) => quote! {
|
||||
<#ty as #crate_::wasm::FromFFIValue>::from_ffi_value(result)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(
|
||||
quote! {
|
||||
#[doc = #doc_string]
|
||||
pub fn #function ( #( #args ),* ) #return_value {
|
||||
extern "C" {
|
||||
/// The extern function.
|
||||
pub fn #ext_function (
|
||||
#( #arg_names: <#arg_types as #crate_::RIType>::FFIType ),*
|
||||
) #ffi_return_value;
|
||||
}
|
||||
|
||||
// Generate all wrapped ffi values.
|
||||
#(
|
||||
let #arg_names2 = <#arg_types2 as #crate_::wasm::IntoFFIValue>::into_ffi_value(
|
||||
&#arg_names2,
|
||||
);
|
||||
)*
|
||||
|
||||
let result = unsafe { #ext_function( #( #arg_names3.get() ),* ) };
|
||||
|
||||
#convert_return_value
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Generate the host exchangeable function for the given method.
|
||||
fn generate_exchangeable_host_function(method: &TraitItemMethod) -> Result<TokenStream> {
|
||||
let crate_ = generate_crate_access();
|
||||
let arg_types = get_function_argument_types(&method.sig);
|
||||
let function = &method.sig.ident;
|
||||
let exchangeable_function = create_exchangeable_host_function_ident(&method.sig.ident);
|
||||
let doc_string = format!(" Exchangeable host function used by [`{}`].", method.sig.ident);
|
||||
let output = &method.sig.output;
|
||||
|
||||
Ok(
|
||||
quote! {
|
||||
#[cfg(not(feature = "std"))]
|
||||
#[allow(non_upper_case_globals)]
|
||||
#[doc = #doc_string]
|
||||
pub static #exchangeable_function : #crate_::wasm::ExchangeableFunction<
|
||||
fn ( #( #arg_types ),* ) #output
|
||||
> = #crate_::wasm::ExchangeableFunction::new(extern_host_function_impls::#function);
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Generate the `HostFunctions` struct that implements `wasm-interface::HostFunctions` to provide
|
||||
/// implementations for the extern host functions.
|
||||
fn generate_host_functions_struct(trait_def: &ItemTrait, is_wasm_only: bool) -> Result<TokenStream> {
|
||||
let crate_ = generate_crate_access();
|
||||
let host_functions = trait_def
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(|i| match i {
|
||||
TraitItem::Method(ref method) => Some(method),
|
||||
_ => None,
|
||||
})
|
||||
.map(|m| generate_host_function_implementation(&trait_def.ident, m, is_wasm_only))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
Ok(
|
||||
quote! {
|
||||
/// Provides implementations for the extern host functions.
|
||||
#[cfg(feature = "std")]
|
||||
pub struct HostFunctions;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl #crate_::wasm_interface::HostFunctions for HostFunctions {
|
||||
fn host_functions() -> Vec<&'static dyn #crate_::wasm_interface::Function> {
|
||||
vec![ #( #host_functions ),* ]
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Generates the host function struct that implements `wasm_interface::Function` and returns a static
|
||||
/// reference to this struct.
|
||||
///
|
||||
/// When calling from wasm into the host, we will call the `execute` function that calls the native
|
||||
/// implementation of the function.
|
||||
fn generate_host_function_implementation(
|
||||
trait_name: &Ident,
|
||||
method: &TraitItemMethod,
|
||||
is_wasm_only: bool,
|
||||
) -> Result<TokenStream> {
|
||||
let name = create_host_function_ident(&method.sig.ident, trait_name).to_string();
|
||||
let struct_name = Ident::new(&name.to_pascal_case(), Span::call_site());
|
||||
let crate_ = generate_crate_access();
|
||||
let signature = generate_wasm_interface_signature_for_host_function(&method.sig)?;
|
||||
let wasm_to_ffi_values = generate_wasm_to_ffi_values(
|
||||
&method.sig,
|
||||
trait_name,
|
||||
).collect::<Result<Vec<_>>>()?;
|
||||
let ffi_to_host_values = generate_ffi_to_host_value(&method.sig).collect::<Result<Vec<_>>>()?;
|
||||
let host_function_call = generate_host_function_call(&method.sig, is_wasm_only);
|
||||
let into_preallocated_ffi_value = generate_into_preallocated_ffi_value(&method.sig)?;
|
||||
let convert_return_value = generate_return_value_into_wasm_value(&method.sig);
|
||||
|
||||
Ok(
|
||||
quote! {
|
||||
{
|
||||
struct #struct_name;
|
||||
|
||||
#[allow(unused)]
|
||||
impl #crate_::wasm_interface::Function for #struct_name {
|
||||
fn name(&self) -> &str {
|
||||
#name
|
||||
}
|
||||
|
||||
fn signature(&self) -> #crate_::wasm_interface::Signature {
|
||||
#signature
|
||||
}
|
||||
|
||||
fn execute(
|
||||
&self,
|
||||
__function_context__: &mut dyn #crate_::wasm_interface::FunctionContext,
|
||||
args: &mut dyn Iterator<Item = #crate_::wasm_interface::Value>,
|
||||
) -> std::result::Result<Option<#crate_::wasm_interface::Value>, String> {
|
||||
#( #wasm_to_ffi_values )*
|
||||
#( #ffi_to_host_values )*
|
||||
#host_function_call
|
||||
#into_preallocated_ffi_value
|
||||
#convert_return_value
|
||||
}
|
||||
}
|
||||
|
||||
&#struct_name as &dyn #crate_::wasm_interface::Function
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Generate the `wasm_interface::Signature` for the given host function `sig`.
|
||||
fn generate_wasm_interface_signature_for_host_function(sig: &Signature) -> Result<TokenStream> {
|
||||
let crate_ = generate_crate_access();
|
||||
let return_value = match &sig.output {
|
||||
ReturnType::Type(_, ty) =>
|
||||
quote! {
|
||||
Some( <<#ty as #crate_::RIType>::FFIType as #crate_::wasm_interface::IntoValue>::VALUE_TYPE )
|
||||
},
|
||||
ReturnType::Default => quote!( None ),
|
||||
};
|
||||
let arg_types = get_function_argument_types_without_ref(sig)
|
||||
.map(|ty| quote! {
|
||||
<<#ty as #crate_::RIType>::FFIType as #crate_::wasm_interface::IntoValue>::VALUE_TYPE
|
||||
});
|
||||
|
||||
Ok(
|
||||
quote! {
|
||||
#crate_::wasm_interface::Signature {
|
||||
args: std::borrow::Cow::Borrowed(&[ #( #arg_types ),* ][..]),
|
||||
return_value: #return_value,
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Generate the code that converts the wasm values given to `HostFunctions::execute` into the FFI
|
||||
/// values.
|
||||
fn generate_wasm_to_ffi_values<'a>(
|
||||
sig: &'a Signature,
|
||||
trait_name: &'a Ident,
|
||||
) -> impl Iterator<Item = Result<TokenStream>> + 'a {
|
||||
let crate_ = generate_crate_access();
|
||||
let function_name = &sig.ident;
|
||||
let error_message = format!(
|
||||
"Number of arguments given to `{}` does not match the expected number of arguments!",
|
||||
function_name,
|
||||
);
|
||||
|
||||
get_function_argument_names_and_types_without_ref(sig)
|
||||
.map(move |(name, ty)| {
|
||||
let try_from_error = format!(
|
||||
"Could not instantiate `{}` from wasm value while executing `{}` from interface `{}`!",
|
||||
name.to_token_stream(),
|
||||
function_name,
|
||||
trait_name,
|
||||
);
|
||||
|
||||
let var_name = generate_ffi_value_var_name(&name)?;
|
||||
|
||||
Ok(quote! {
|
||||
let val = args.next().ok_or_else(|| #error_message)?;
|
||||
let #var_name = <
|
||||
<#ty as #crate_::RIType>::FFIType as #crate_::wasm_interface::TryFromValue
|
||||
>::try_from_value(val).ok_or_else(|| #try_from_error)?;
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Generate the code to convert the ffi values on the host to the host values using `FromFFIValue`.
|
||||
fn generate_ffi_to_host_value<'a>(
|
||||
sig: &'a Signature,
|
||||
) -> impl Iterator<Item = Result<TokenStream>> + 'a {
|
||||
let mut_access = get_function_argument_types_ref_and_mut(sig);
|
||||
let crate_ = generate_crate_access();
|
||||
|
||||
get_function_argument_names_and_types_without_ref(sig)
|
||||
.zip(mut_access.map(|v| v.and_then(|m| m.1)))
|
||||
.map(move |((name, ty), mut_access)| {
|
||||
let ffi_value_var_name = generate_ffi_value_var_name(&name)?;
|
||||
|
||||
Ok(
|
||||
quote! {
|
||||
let #mut_access #name = <#ty as #crate_::host::FromFFIValue>::from_ffi_value(
|
||||
__function_context__,
|
||||
#ffi_value_var_name,
|
||||
)?;
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Generate the code to call the host function and the ident that stores the result.
|
||||
fn generate_host_function_call(sig: &Signature, is_wasm_only: bool) -> TokenStream {
|
||||
let host_function_name = &sig.ident;
|
||||
let result_var_name = generate_host_function_result_var_name(&sig.ident);
|
||||
let ref_and_mut = get_function_argument_types_ref_and_mut(sig).map(|ram|
|
||||
ram.map(|(vr, vm)| quote!(#vr #vm))
|
||||
);
|
||||
let names = get_function_argument_names(sig);
|
||||
|
||||
let var_access = names.zip(ref_and_mut)
|
||||
.map(|(n, ref_and_mut)| {
|
||||
quote!( #ref_and_mut #n )
|
||||
})
|
||||
// If this is a wasm only interface, we add the function context as last parameter.
|
||||
.chain(
|
||||
iter::from_fn(|| if is_wasm_only { Some(quote!(__function_context__)) } else { None })
|
||||
.take(1)
|
||||
);
|
||||
|
||||
quote! {
|
||||
let #result_var_name = #host_function_name ( #( #var_access ),* );
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate the variable name that stores the result of the host function.
|
||||
fn generate_host_function_result_var_name(name: &Ident) -> Ident {
|
||||
Ident::new(&format!("{}_result", name), Span::call_site())
|
||||
}
|
||||
|
||||
/// Generate the variable name that stores the FFI value.
|
||||
fn generate_ffi_value_var_name(pat: &Pat) -> Result<Ident> {
|
||||
match pat {
|
||||
Pat::Ident(pat_ident) => {
|
||||
if let Some(by_ref) = pat_ident.by_ref {
|
||||
Err(Error::new(by_ref.span(), "`ref` not supported!"))
|
||||
} else if let Some(sub_pattern) = &pat_ident.subpat {
|
||||
Err(Error::new(sub_pattern.0.span(), "Not supported!"))
|
||||
} else {
|
||||
Ok(Ident::new(&format!("{}_ffi_value", pat_ident.ident), Span::call_site()))
|
||||
}
|
||||
}
|
||||
_ => Err(Error::new(pat.span(), "Not supported as variable name!"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate code that copies data from the host back to preallocated wasm memory.
|
||||
///
|
||||
/// Any argument that is given as `&mut` is interpreted as preallocated memory and it is expected
|
||||
/// that the type implements `IntoPreAllocatedFFIValue`.
|
||||
fn generate_into_preallocated_ffi_value(sig: &Signature) -> Result<TokenStream> {
|
||||
let crate_ = generate_crate_access();
|
||||
let ref_and_mut = get_function_argument_types_ref_and_mut(sig).map(|ram|
|
||||
ram.and_then(|(vr, vm)| vm.map(|v| (vr, v)))
|
||||
);
|
||||
let names_and_types = get_function_argument_names_and_types_without_ref(sig);
|
||||
|
||||
ref_and_mut.zip(names_and_types)
|
||||
.filter_map(|(ram, (name, ty))| ram.map(|_| (name, ty)))
|
||||
.map(|(name, ty)| {
|
||||
let ffi_var_name = generate_ffi_value_var_name(&name)?;
|
||||
|
||||
Ok(
|
||||
quote! {
|
||||
<#ty as #crate_::host::IntoPreallocatedFFIValue>::into_preallocated_ffi_value(
|
||||
#name,
|
||||
__function_context__,
|
||||
#ffi_var_name,
|
||||
)?;
|
||||
}
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Generate the code that converts the return value into the appropriate wasm value.
|
||||
fn generate_return_value_into_wasm_value(sig: &Signature) -> TokenStream {
|
||||
let crate_ = generate_crate_access();
|
||||
|
||||
match &sig.output {
|
||||
ReturnType::Default => quote!( Ok(None) ),
|
||||
ReturnType::Type(_, ty) => {
|
||||
let result_var_name = generate_host_function_result_var_name(&sig.ident);
|
||||
|
||||
quote! {
|
||||
<#ty as #crate_::host::IntoFFIValue>::into_ffi_value(
|
||||
#result_var_name,
|
||||
__function_context__,
|
||||
).map(#crate_::wasm_interface::IntoValue::into_value).map(Some)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::utils::generate_runtime_interface_include;
|
||||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
|
||||
use syn::{Ident, ItemTrait, Result};
|
||||
|
||||
use inflector::Inflector;
|
||||
|
||||
use quote::quote;
|
||||
|
||||
mod bare_function_interface;
|
||||
mod host_function_interface;
|
||||
mod trait_decl_impl;
|
||||
|
||||
/// Custom keywords supported by the `runtime_interface` attribute.
|
||||
pub mod keywords {
|
||||
// Custom keyword `wasm_only` that can be given as attribute to [`runtime_interface`].
|
||||
syn::custom_keyword!(wasm_only);
|
||||
}
|
||||
|
||||
/// Implementation of the `runtime_interface` attribute.
|
||||
///
|
||||
/// It expects the trait definition the attribute was put above and if this should be an wasm only
|
||||
/// interface.
|
||||
pub fn runtime_interface_impl(trait_def: ItemTrait, is_wasm_only: bool) -> Result<TokenStream> {
|
||||
let bare_functions = bare_function_interface::generate(&trait_def, is_wasm_only)?;
|
||||
let crate_include = generate_runtime_interface_include();
|
||||
let mod_name = Ident::new(&trait_def.ident.to_string().to_snake_case(), Span::call_site());
|
||||
let trait_decl_impl = trait_decl_impl::process(&trait_def, is_wasm_only)?;
|
||||
let host_functions = host_function_interface::generate(&trait_def, is_wasm_only)?;
|
||||
let vis = trait_def.vis;
|
||||
let attrs = &trait_def.attrs;
|
||||
|
||||
let res = quote! {
|
||||
#( #attrs )*
|
||||
#vis mod #mod_name {
|
||||
use super::*;
|
||||
#crate_include
|
||||
|
||||
#bare_functions
|
||||
|
||||
#trait_decl_impl
|
||||
|
||||
#host_functions
|
||||
}
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
+146
@@ -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() {}
|
||||
+11
@@ -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() {}
|
||||
+5
@@ -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() {}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
error: `PassByEnum` only supports enums as input type.
|
||||
--> $DIR/pass_by_enum_with_struct.rs:3:10
|
||||
|
|
||||
3 | #[derive(PassByEnum)]
|
||||
| ^^^^^^^^^^
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
use runtime_interface::pass_by::PassByEnum;
|
||||
|
||||
#[derive(PassByEnum)]
|
||||
enum Test {
|
||||
Var0(u32),
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
error: `PassByEnum` only supports unit variants.
|
||||
--> $DIR/pass_by_enum_with_value_variant.rs:3:10
|
||||
|
|
||||
3 | #[derive(PassByEnum)]
|
||||
| ^^^^^^^^^^
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
use runtime_interface::pass_by::PassByInner;
|
||||
|
||||
#[derive(PassByInner)]
|
||||
struct Test {
|
||||
data: u32,
|
||||
data2: u32,
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
error: Only newtype/one field structs are supported by `PassByInner`!
|
||||
--> $DIR/pass_by_inner_with_two_fields.rs:3:10
|
||||
|
|
||||
3 | #[derive(PassByInner)]
|
||||
| ^^^^^^^^^^^
|
||||
@@ -0,0 +1,8 @@
|
||||
use runtime_interface::runtime_interface;
|
||||
|
||||
#[runtime_interface]
|
||||
trait Test {
|
||||
fn test(self) {}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: Taking `Self` by value is not allowed.
|
||||
--> $DIR/take_self_by_value.rs:5:10
|
||||
|
|
||||
5 | fn test(self) {}
|
||||
| ^^^^
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
@@ -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 = "../../core", default-features = false }
|
||||
|
||||
[build-dependencies]
|
||||
wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1.0.3", path = "../../../client/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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user