// Copyright 2019-2020 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 .
//! Types and traits for interfacing between the host and the wasm runtime.
#![cfg_attr(not(feature = "std"), no_std)]
use sp_std::{
borrow::Cow, marker::PhantomData, mem, iter::Iterator, result, vec::Vec,
};
#[cfg(feature = "std")]
mod wasmi_impl;
/// Result type used by traits in this crate.
#[cfg(feature = "std")]
pub type Result = result::Result;
#[cfg(not(feature = "std"))]
pub type Result = result::Result;
/// Value types supported by Substrate on the boundary between host/Wasm.
#[derive(Copy, Clone, PartialEq, Debug, Eq)]
pub enum ValueType {
/// An `i32` value type.
I32,
/// An `i64` value type.
I64,
/// An `f32` value type.
F32,
/// An `f64` value type.
F64,
}
impl From for u8 {
fn from(val: ValueType) -> u8 {
match val {
ValueType::I32 => 0,
ValueType::I64 => 1,
ValueType::F32 => 2,
ValueType::F64 => 3,
}
}
}
impl sp_std::convert::TryFrom for ValueType {
type Error = ();
fn try_from(val: u8) -> sp_std::result::Result {
match val {
0 => Ok(Self::I32),
1 => Ok(Self::I64),
2 => Ok(Self::F32),
3 => Ok(Self::F64),
_ => Err(()),
}
}
}
/// Values supported by Substrate on the boundary between host/Wasm.
#[derive(PartialEq, Debug, Clone, Copy, codec::Encode, codec::Decode)]
pub enum Value {
/// A 32-bit integer.
I32(i32),
/// A 64-bit integer.
I64(i64),
/// A 32-bit floating-point number stored as raw bit pattern.
///
/// You can materialize this value using `f32::from_bits`.
F32(u32),
/// A 64-bit floating-point number stored as raw bit pattern.
///
/// You can materialize this value using `f64::from_bits`.
F64(u64),
}
impl Value {
/// Returns the type of this value.
pub fn value_type(&self) -> ValueType {
match self {
Value::I32(_) => ValueType::I32,
Value::I64(_) => ValueType::I64,
Value::F32(_) => ValueType::F32,
Value::F64(_) => ValueType::F64,
}
}
/// Return `Self` as `i32`.
pub fn as_i32(&self) -> Option {
match self {
Self::I32(val) => Some(*val),
_ => None,
}
}
}
/// Provides `Sealed` trait to prevent implementing trait `PointerType` outside of this crate.
mod private {
pub trait Sealed {}
impl Sealed for u8 {}
impl Sealed for u16 {}
impl Sealed for u32 {}
impl Sealed for u64 {}
}
/// Something that can be wrapped in a wasm `Pointer`.
///
/// This trait is sealed.
pub trait PointerType: Sized {
/// The size of the type in wasm.
const SIZE: u32 = mem::size_of::() as u32;
}
impl PointerType for u8 {}
impl PointerType for u16 {}
impl PointerType for u32 {}
impl PointerType for u64 {}
/// Type to represent a pointer in wasm at the host.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct Pointer {
ptr: u32,
_marker: PhantomData,
}
impl Pointer {
/// Create a new instance of `Self`.
pub fn new(ptr: u32) -> Self {
Self {
ptr,
_marker: Default::default(),
}
}
/// Calculate the offset from this pointer.
///
/// `offset` is in units of `T`. So, `3` means `3 * mem::size_of::()` as offset to the pointer.
///
/// Returns an `Option` to respect that the pointer could probably overflow.
pub fn offset(self, offset: u32) -> Option {
offset.checked_mul(T::SIZE).and_then(|o| self.ptr.checked_add(o)).map(|ptr| {
Self {
ptr,
_marker: Default::default(),
}
})
}
/// Create a null pointer.
pub fn null() -> Self {
Self::new(0)
}
/// Cast this pointer of type `T` to a pointer of type `R`.
pub fn cast(self) -> Pointer {
Pointer::new(self.ptr)
}
}
impl From> for u32 {
fn from(ptr: Pointer) -> Self {
ptr.ptr
}
}
impl From> for u64 {
fn from(ptr: Pointer) -> Self {
u64::from(ptr.ptr)
}
}
impl From> for usize {
fn from(ptr: Pointer) -> Self {
ptr.ptr as _
}
}
impl IntoValue for Pointer {
const VALUE_TYPE: ValueType = ValueType::I32;
fn into_value(self) -> Value { Value::I32(self.ptr as _) }
}
impl TryFromValue for Pointer {
fn try_from_value(val: Value) -> Option {
match val {
Value::I32(val) => Some(Self::new(val as _)),
_ => None,
}
}
}
/// The word size used in wasm. Normally known as `usize` in Rust.
pub type WordSize = u32;
/// The Signature of a function
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct Signature {
/// The arguments of a function.
pub args: Cow<'static, [ValueType]>,
/// The optional return value of a function.
pub return_value: Option,
}
impl Signature {
/// Create a new instance of `Signature`.
pub fn new>>(args: T, return_value: Option) -> Self {
Self {
args: args.into(),
return_value,
}
}
/// Create a new instance of `Signature` with the given `args` and without any return value.
pub fn new_with_args>>(args: T) -> Self {
Self {
args: args.into(),
return_value: None,
}
}
}
/// A trait that requires `RefUnwindSafe` when `feature = std`.
#[cfg(feature = "std")]
pub trait MaybeRefUnwindSafe: std::panic::RefUnwindSafe {}
#[cfg(feature = "std")]
impl MaybeRefUnwindSafe for T {}
/// A trait that requires `RefUnwindSafe` when `feature = std`.
#[cfg(not(feature = "std"))]
pub trait MaybeRefUnwindSafe {}
#[cfg(not(feature = "std"))]
impl MaybeRefUnwindSafe for T {}
/// Something that provides a function implementation on the host for a wasm function.
pub trait Function: MaybeRefUnwindSafe + Send + Sync {
/// Returns the name of this function.
fn name(&self) -> &str;
/// Returns the signature of this function.
fn signature(&self) -> Signature;
/// Execute this function with the given arguments.
fn execute(
&self,
context: &mut dyn FunctionContext,
args: &mut dyn Iterator,
) -> Result