mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-14 16:51:03 +00:00
Statically register host WASM functions (#10394)
* Statically register host WASM functions * Fix `substrate-test-client` compilation * Move `ExtendedHostFunctions` to `sp-wasm-interface` * Fix `sp-runtime-interface` tests' compilation * Fix `sc-executor-wasmtime` tests' compilation * Use `runtime_interface` macro in `test-runner` * Fix `sc-executor` tests' compilation * Reformatting/`rustfmt` * Add an extra comment regarding the `H` generic arg in `create_runtime` * Even more `rustfmt` * Depend on `wasmtime` without default features in `sp-wasm-interface` * Bump version of `sp-wasm-interface` to 4.0.1 * Bump `sp-wasm-interface` in `Cargo.lock` too * Bump all of the `sp-wasm-interface` requirements to 4.0.1 Maybe this will appease cargo-unleash? * Revert "Bump all of the `sp-wasm-interface` requirements to 4.0.1" This reverts commit 0f7ccf8e0f371542861121b145ab87af6541ac30. * Make `cargo-unleash` happy (maybe) * Use `cargo-unleash` to bump the crates' versions * Align to review comments
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "sp-wasm-interface"
|
||||
version = "4.0.0"
|
||||
version = "4.1.0-dev"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
@@ -15,10 +15,12 @@ targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
wasmi = { version = "0.9.1", optional = true }
|
||||
wasmtime = { version = "0.31.0", optional = true, default-features = false }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
impl-trait-for-tuples = "0.2.1"
|
||||
sp-std = { version = "4.0.0", path = "../std", default-features = false }
|
||||
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] }
|
||||
|
||||
[features]
|
||||
default = [ "std" ]
|
||||
std = [ "wasmi", "sp-std/std", "codec/std" ]
|
||||
std = [ "wasmi", "sp-std/std", "codec/std", "log" ]
|
||||
|
||||
@@ -24,6 +24,25 @@ use sp_std::{borrow::Cow, iter::Iterator, marker::PhantomData, mem, result, vec,
|
||||
#[cfg(feature = "std")]
|
||||
mod wasmi_impl;
|
||||
|
||||
#[cfg(not(all(feature = "std", feature = "wasmtime")))]
|
||||
#[macro_export]
|
||||
macro_rules! if_wasmtime_is_enabled {
|
||||
($($token:tt)*) => {};
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "std", feature = "wasmtime"))]
|
||||
#[macro_export]
|
||||
macro_rules! if_wasmtime_is_enabled {
|
||||
($($token:tt)*) => {
|
||||
$($token)*
|
||||
}
|
||||
}
|
||||
|
||||
if_wasmtime_is_enabled! {
|
||||
// Reexport wasmtime so that its types are accessible from the procedural macro.
|
||||
pub use wasmtime;
|
||||
}
|
||||
|
||||
/// Result type used by traits in this crate.
|
||||
#[cfg(feature = "std")]
|
||||
pub type Result<T> = result::Result<T, String>;
|
||||
@@ -105,7 +124,8 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides `Sealed` trait to prevent implementing trait `PointerType` outside of this crate.
|
||||
/// Provides `Sealed` trait to prevent implementing trait `PointerType` and `WasmTy` outside of this
|
||||
/// crate.
|
||||
mod private {
|
||||
pub trait Sealed {}
|
||||
|
||||
@@ -113,6 +133,9 @@ mod private {
|
||||
impl Sealed for u16 {}
|
||||
impl Sealed for u32 {}
|
||||
impl Sealed for u64 {}
|
||||
|
||||
impl Sealed for i32 {}
|
||||
impl Sealed for i64 {}
|
||||
}
|
||||
|
||||
/// Something that can be wrapped in a wasm `Pointer`.
|
||||
@@ -338,10 +361,48 @@ pub trait Sandbox {
|
||||
fn get_global_val(&self, instance_idx: u32, name: &str) -> Result<Option<Value>>;
|
||||
}
|
||||
|
||||
if_wasmtime_is_enabled! {
|
||||
/// A trait used to statically register host callbacks with the WASM executor,
|
||||
/// so that they call be called from within the runtime with minimal overhead.
|
||||
///
|
||||
/// This is used internally to interface the wasmtime-based executor with the
|
||||
/// host functions' definitions generated through the runtime interface macro,
|
||||
/// and is not meant to be used directly.
|
||||
pub trait HostFunctionRegistry {
|
||||
type State;
|
||||
type Error;
|
||||
type FunctionContext: FunctionContext;
|
||||
|
||||
/// Wraps the given `caller` in a type which implements `FunctionContext`
|
||||
/// and calls the given `callback`.
|
||||
fn with_function_context<R>(
|
||||
caller: wasmtime::Caller<Self::State>,
|
||||
callback: impl FnOnce(&mut dyn FunctionContext) -> R,
|
||||
) -> R;
|
||||
|
||||
/// Registers a given host function with the WASM executor.
|
||||
///
|
||||
/// The function has to be statically callable, and all of its arguments
|
||||
/// and its return value have to be compatible with WASM FFI.
|
||||
fn register_static<Params, Results>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
func: impl wasmtime::IntoFunc<Self::State, Params, Results> + 'static,
|
||||
) -> core::result::Result<(), Self::Error>;
|
||||
}
|
||||
}
|
||||
|
||||
/// Something that provides implementations for host functions.
|
||||
pub trait HostFunctions: 'static {
|
||||
pub trait HostFunctions: 'static + Send + Sync {
|
||||
/// Returns the host functions `Self` provides.
|
||||
fn host_functions() -> Vec<&'static dyn Function>;
|
||||
|
||||
if_wasmtime_is_enabled! {
|
||||
/// Statically registers the host functions.
|
||||
fn register_static<T>(registry: &mut T) -> core::result::Result<(), T::Error>
|
||||
where
|
||||
T: HostFunctionRegistry;
|
||||
}
|
||||
}
|
||||
|
||||
#[impl_trait_for_tuples::impl_for_tuples(30)]
|
||||
@@ -353,8 +414,146 @@ impl HostFunctions for Tuple {
|
||||
|
||||
host_functions
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "std", feature = "wasmtime"))]
|
||||
fn register_static<T>(registry: &mut T) -> core::result::Result<(), T::Error>
|
||||
where
|
||||
T: HostFunctionRegistry,
|
||||
{
|
||||
for_tuples!(
|
||||
#( Tuple::register_static(registry)?; )*
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper which merges two sets of host functions, and allows the second set to override
|
||||
/// the host functions from the first set.
|
||||
pub struct ExtendedHostFunctions<Base, Overlay> {
|
||||
phantom: PhantomData<(Base, Overlay)>,
|
||||
}
|
||||
|
||||
impl<Base, Overlay> HostFunctions for ExtendedHostFunctions<Base, Overlay>
|
||||
where
|
||||
Base: HostFunctions,
|
||||
Overlay: HostFunctions,
|
||||
{
|
||||
fn host_functions() -> Vec<&'static dyn Function> {
|
||||
let mut base = Base::host_functions();
|
||||
let overlay = Overlay::host_functions();
|
||||
base.retain(|host_fn| {
|
||||
!overlay.iter().any(|ext_host_fn| host_fn.name() == ext_host_fn.name())
|
||||
});
|
||||
base.extend(overlay);
|
||||
base
|
||||
}
|
||||
|
||||
if_wasmtime_is_enabled! {
|
||||
fn register_static<T>(registry: &mut T) -> core::result::Result<(), T::Error>
|
||||
where
|
||||
T: HostFunctionRegistry,
|
||||
{
|
||||
struct Proxy<'a, T> {
|
||||
registry: &'a mut T,
|
||||
seen_overlay: std::collections::HashSet<String>,
|
||||
seen_base: std::collections::HashSet<String>,
|
||||
overlay_registered: bool,
|
||||
}
|
||||
|
||||
impl<'a, T> HostFunctionRegistry for Proxy<'a, T>
|
||||
where
|
||||
T: HostFunctionRegistry,
|
||||
{
|
||||
type State = T::State;
|
||||
type Error = T::Error;
|
||||
type FunctionContext = T::FunctionContext;
|
||||
|
||||
fn with_function_context<R>(
|
||||
caller: wasmtime::Caller<Self::State>,
|
||||
callback: impl FnOnce(&mut dyn FunctionContext) -> R,
|
||||
) -> R {
|
||||
T::with_function_context(caller, callback)
|
||||
}
|
||||
|
||||
fn register_static<Params, Results>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
func: impl wasmtime::IntoFunc<Self::State, Params, Results> + 'static,
|
||||
) -> core::result::Result<(), Self::Error> {
|
||||
if self.overlay_registered {
|
||||
if !self.seen_base.insert(fn_name.to_owned()) {
|
||||
log::warn!(
|
||||
target: "extended_host_functions",
|
||||
"Duplicate base host function: '{}'",
|
||||
fn_name,
|
||||
);
|
||||
|
||||
// TODO: Return an error here?
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
if self.seen_overlay.contains(fn_name) {
|
||||
// Was already registered when we went through the overlay, so just ignore it.
|
||||
log::debug!(
|
||||
target: "extended_host_functions",
|
||||
"Overriding base host function: '{}'",
|
||||
fn_name,
|
||||
);
|
||||
|
||||
return Ok(())
|
||||
}
|
||||
} else if !self.seen_overlay.insert(fn_name.to_owned()) {
|
||||
log::warn!(
|
||||
target: "extended_host_functions",
|
||||
"Duplicate overlay host function: '{}'",
|
||||
fn_name,
|
||||
);
|
||||
|
||||
// TODO: Return an error here?
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
self.registry.register_static(fn_name, func)
|
||||
}
|
||||
}
|
||||
|
||||
let mut proxy = Proxy {
|
||||
registry,
|
||||
seen_overlay: Default::default(),
|
||||
seen_base: Default::default(),
|
||||
overlay_registered: false,
|
||||
};
|
||||
|
||||
// The functions from the `Overlay` can override those from the `Base`,
|
||||
// so `Overlay` is registered first, and then we skip those functions
|
||||
// in `Base` if they were already registered from the `Overlay`.
|
||||
Overlay::register_static(&mut proxy)?;
|
||||
proxy.overlay_registered = true;
|
||||
Base::register_static(&mut proxy)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for types directly usable at the WASM FFI boundary without any conversion at all.
|
||||
///
|
||||
/// This trait is sealed and should not be implemented downstream.
|
||||
#[cfg(all(feature = "std", feature = "wasmtime"))]
|
||||
pub trait WasmTy: wasmtime::WasmTy + private::Sealed {}
|
||||
|
||||
/// A trait for types directly usable at the WASM FFI boundary without any conversion at all.
|
||||
///
|
||||
/// This trait is sealed and should not be implemented downstream.
|
||||
#[cfg(not(all(feature = "std", feature = "wasmtime")))]
|
||||
pub trait WasmTy: private::Sealed {}
|
||||
|
||||
impl WasmTy for i32 {}
|
||||
impl WasmTy for u32 {}
|
||||
impl WasmTy for i64 {}
|
||||
impl WasmTy for u64 {}
|
||||
|
||||
/// Something that can be converted into a wasm compatible `Value`.
|
||||
pub trait IntoValue {
|
||||
/// The type of the value in wasm.
|
||||
|
||||
Reference in New Issue
Block a user