#![cfg_attr(not(feature = "std"), no_std)]
use sp_std::{iter::Iterator, marker::PhantomData, result, vec, vec::Vec};
pub use sp_wasm_interface_common::{self as common, Value, ValueType, Pointer, PointerType, IntoValue, TryFromValue, ReturnValue, WordSize, MemoryId, HostPointer, Signature};
if_wasmtime_is_enabled! {
mod host_state;
pub use host_state::HostState;
mod store_data;
pub use store_data::StoreData;
mod memory_wrapper;
pub use memory_wrapper::MemoryWrapper;
pub mod util;
}
#[cfg(not(all(feature = "std", feature = "wasmtime")))]
pub struct StoreData;
#[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! {
pub use wasmtime;
pub use anyhow;
}
#[cfg(feature = "std")]
pub type Result<T> = result::Result<T, String>;
#[cfg(not(feature = "std"))]
pub type Result<T> = result::Result<T, &'static str>;
mod private {
pub trait Sealed {}
impl Sealed for u8 {}
impl Sealed for u16 {}
impl Sealed for u32 {}
impl Sealed for u64 {}
impl Sealed for i32 {}
impl Sealed for i64 {}
pub(super) struct Token;
}
#[cfg(feature = "std")]
pub trait MaybeRefUnwindSafe: std::panic::RefUnwindSafe {}
#[cfg(feature = "std")]
impl<T: std::panic::RefUnwindSafe> MaybeRefUnwindSafe for T {}
#[cfg(not(feature = "std"))]
pub trait MaybeRefUnwindSafe {}
#[cfg(not(feature = "std"))]
impl<T> MaybeRefUnwindSafe for T {}
pub trait Function: MaybeRefUnwindSafe + Send + Sync {
fn name(&self) -> &str;
fn signature(&self) -> Signature;
fn execute(
&self,
context: &mut dyn FunctionContext,
args: &mut dyn Iterator<Item = Value>,
) -> Result<Option<Value>>;
}
impl PartialEq for dyn Function {
fn eq(&self, other: &Self) -> bool {
other.name() == self.name() && other.signature() == self.signature()
}
}
#[cfg(not(all(feature = "std", feature = "wasmtime")))]
pub struct Caller<'a, T>(PhantomData<&'a T>);
#[cfg(all(feature = "std", feature = "wasmtime"))]
pub use wasmtime::Caller;
pub struct FunctionContextToken(private::Token);
impl FunctionContextToken {
fn new() -> Self {
Self(private::Token)
}
}
pub trait FunctionContext {
fn read_memory(&self, address: Pointer<u8>, size: WordSize) -> Result<Vec<u8>> {
let mut vec = vec![0; size as usize];
self.read_memory_into(address, &mut vec)?;
Ok(vec)
}
fn read_memory_into(&self, address: Pointer<u8>, dest: &mut [u8]) -> Result<()>;
fn write_memory(&mut self, address: Pointer<u8>, data: &[u8]) -> Result<()>;
fn allocate_memory(&mut self, size: WordSize) -> Result<Pointer<u8>>;
fn deallocate_memory(&mut self, ptr: Pointer<u8>) -> Result<()>;
fn register_panic_error_message(&mut self, message: &str);
fn with_caller_mut_impl(&mut self, _: FunctionContextToken, context: *mut (), callback: fn(*mut (), &mut Caller<StoreData>));
}
pub fn with_caller_mut<T: FnMut(&mut Caller<StoreData>)>(context: &mut dyn FunctionContext, mut callback: T) {
let callback: *mut T = &mut callback;
context.with_caller_mut_impl(FunctionContextToken::new(), callback.cast(), |callback, caller| {
let callback: *mut T = callback.cast();
let callback: &mut T = unsafe { callback.as_mut().expect("we own the value, obtain mutable reference to it and cast to pointer (correct (not null) and aligned properly); qed") };
callback(caller);
})
}
if_wasmtime_is_enabled! {
pub trait HostFunctionRegistry {
type State;
type Error;
type FunctionContext: FunctionContext;
fn with_function_context<R>(
caller: wasmtime::Caller<Self::State>,
callback: impl FnOnce(&mut dyn FunctionContext) -> R,
) -> R;
fn register_static<Params, Results>(
&mut self,
fn_name: &str,
func: impl wasmtime::IntoFunc<Self::State, Params, Results> + 'static,
) -> core::result::Result<(), Self::Error>;
}
}
pub trait HostFunctions: 'static + Send + Sync {
fn host_functions() -> Vec<&'static dyn Function>;
if_wasmtime_is_enabled! {
fn register_static<T>(registry: &mut T) -> core::result::Result<(), T::Error>
where
T: HostFunctionRegistry;
}
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl HostFunctions for Tuple {
fn host_functions() -> Vec<&'static dyn Function> {
let mut host_functions = Vec::new();
for_tuples!( #( host_functions.extend(Tuple::host_functions()); )* );
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(())
}
}
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,
);
return Ok(())
}
if self.seen_overlay.contains(fn_name) {
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,
);
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,
};
Overlay::register_static(&mut proxy)?;
proxy.overlay_registered = true;
Base::register_static(&mut proxy)?;
Ok(())
}
}
}
#[cfg(all(feature = "std", feature = "wasmtime"))]
pub trait WasmTy: wasmtime::WasmTy + private::Sealed {}
#[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 {}