sp_wasm_interface_common/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Types and traits for interfacing between the host and the wasm runtime.
19
20#![cfg_attr(not(feature = "std"), no_std)]
21
22use sp_std::borrow::Cow;
23use core::{mem, marker::PhantomData};
24
25pub mod util;
26
27#[cfg(feature = "wasmi")]
28pub use wasmi;
29
30#[cfg(feature = "wasmi")]
31pub mod wasmi_impl;
32
33/// Value types supported by Substrate on the boundary between host/Wasm.
34#[derive(Copy, Clone, PartialEq, Debug, Eq)]
35pub enum ValueType {
36	/// An `i32` value type.
37	I32,
38	/// An `i64` value type.
39	I64,
40	/// An `f32` value type.
41	F32,
42	/// An `f64` value type.
43	F64,
44}
45
46impl From<ValueType> for u8 {
47	fn from(val: ValueType) -> u8 {
48		match val {
49			ValueType::I32 => 0,
50			ValueType::I64 => 1,
51			ValueType::F32 => 2,
52			ValueType::F64 => 3,
53		}
54	}
55}
56
57impl TryFrom<u8> for ValueType {
58	type Error = ();
59
60	fn try_from(val: u8) -> core::result::Result<ValueType, ()> {
61		match val {
62			0 => Ok(Self::I32),
63			1 => Ok(Self::I64),
64			2 => Ok(Self::F32),
65			3 => Ok(Self::F64),
66			_ => Err(()),
67		}
68	}
69}
70
71/// Values supported by Substrate on the boundary between host/Wasm.
72#[derive(PartialEq, Debug, Clone, Copy, codec::Encode, codec::Decode)]
73pub enum Value {
74	/// A 32-bit integer.
75	I32(i32),
76	/// A 64-bit integer.
77	I64(i64),
78	/// A 32-bit floating-point number stored as raw bit pattern.
79	///
80	/// You can materialize this value using `f32::from_bits`.
81	F32(u32),
82	/// A 64-bit floating-point number stored as raw bit pattern.
83	///
84	/// You can materialize this value using `f64::from_bits`.
85	F64(u64),
86}
87
88impl Value {
89	/// Returns the type of this value.
90	pub fn value_type(&self) -> ValueType {
91		match self {
92			Value::I32(_) => ValueType::I32,
93			Value::I64(_) => ValueType::I64,
94			Value::F32(_) => ValueType::F32,
95			Value::F64(_) => ValueType::F64,
96		}
97	}
98
99	/// Return `Self` as `i32`.
100	pub fn as_i32(&self) -> Option<i32> {
101		match self {
102			Self::I32(val) => Some(*val),
103			_ => None,
104		}
105	}
106}
107
108/// Something that can be converted into a wasm compatible `Value`.
109pub trait IntoValue {
110	/// The type of the value in wasm.
111	const VALUE_TYPE: ValueType;
112
113	/// Convert `self` into a wasm `Value`.
114	fn into_value(self) -> Value;
115}
116
117/// Something that can may be created from a wasm `Value`.
118pub trait TryFromValue: Sized {
119	/// Try to convert the given `Value` into `Self`.
120	fn try_from_value(val: Value) -> Option<Self>;
121}
122
123macro_rules! impl_into_and_from_value {
124	(
125		$(
126			$type:ty, $( < $gen:ident >, )? $value_variant:ident,
127		)*
128	) => {
129		$(
130			impl $( <$gen> )? IntoValue for $type {
131				const VALUE_TYPE: ValueType = ValueType::$value_variant;
132				fn into_value(self) -> Value { Value::$value_variant(self as _) }
133			}
134
135			impl $( <$gen> )? TryFromValue for $type {
136				fn try_from_value(val: Value) -> Option<Self> {
137					match val {
138						Value::$value_variant(val) => Some(val as _),
139						_ => None,
140					}
141				}
142			}
143		)*
144	}
145}
146
147impl_into_and_from_value! {
148	u8, I32,
149	u16, I32,
150	u32, I32,
151	u64, I64,
152	i8, I32,
153	i16, I32,
154	i32, I32,
155	i64, I64,
156}
157
158/// Provides `Sealed` trait to prevent implementing trait `PointerType` and `WasmTy` outside of this
159/// crate.
160mod private {
161	pub trait Sealed {}
162
163	impl Sealed for u8 {}
164	impl Sealed for u16 {}
165	impl Sealed for u32 {}
166	impl Sealed for u64 {}
167
168	impl Sealed for i32 {}
169	impl Sealed for i64 {}
170}
171
172/// Something that can be wrapped in a wasm `Pointer`.
173///
174/// This trait is sealed.
175pub trait PointerType: Sized + private::Sealed {
176	/// The size of the type in wasm.
177	const SIZE: u32 = mem::size_of::<Self>() as u32;
178}
179
180impl PointerType for u8 {}
181impl PointerType for u16 {}
182impl PointerType for u32 {}
183impl PointerType for u64 {}
184
185/// Type to represent a pointer in wasm at the host.
186#[derive(Debug, PartialEq, Eq, Clone, Copy)]
187pub struct Pointer<T: PointerType> {
188	ptr: u32,
189	_marker: PhantomData<T>,
190}
191
192impl<T: PointerType> Pointer<T> {
193	/// Create a new instance of `Self`.
194	pub fn new(ptr: u32) -> Self {
195		Self { ptr, _marker: Default::default() }
196	}
197
198	/// Calculate the offset from this pointer.
199	///
200	/// `offset` is in units of `T`. So, `3` means `3 * mem::size_of::<T>()` as offset to the
201	/// pointer.
202	///
203	/// Returns an `Option` to respect that the pointer could probably overflow.
204	pub fn offset(self, offset: u32) -> Option<Self> {
205		offset
206			.checked_mul(T::SIZE)
207			.and_then(|o| self.ptr.checked_add(o))
208			.map(|ptr| Self { ptr, _marker: Default::default() })
209	}
210
211	/// Create a null pointer.
212	pub fn null() -> Self {
213		Self::new(0)
214	}
215
216	/// Cast this pointer of type `T` to a pointer of type `R`.
217	pub fn cast<R: PointerType>(self) -> Pointer<R> {
218		Pointer::new(self.ptr)
219	}
220}
221
222impl<T: PointerType> From<u32> for Pointer<T> {
223	fn from(ptr: u32) -> Self {
224		Pointer::new(ptr)
225	}
226}
227
228impl<T: PointerType> From<Pointer<T>> for u32 {
229	fn from(ptr: Pointer<T>) -> Self {
230		ptr.ptr
231	}
232}
233
234impl<T: PointerType> From<Pointer<T>> for u64 {
235	fn from(ptr: Pointer<T>) -> Self {
236		u64::from(ptr.ptr)
237	}
238}
239
240impl<T: PointerType> From<Pointer<T>> for usize {
241	fn from(ptr: Pointer<T>) -> Self {
242		ptr.ptr as _
243	}
244}
245
246// trait implementations for Pointer.
247impl<T: PointerType> IntoValue for Pointer<T> {
248	const VALUE_TYPE: ValueType = ValueType::I32;
249	fn into_value(self) -> Value {
250		Value::I32(self.ptr as _)
251	}
252}
253
254impl<T: PointerType> TryFromValue for Pointer<T> {
255	fn try_from_value(val: Value) -> Option<Self> {
256		match val {
257			Value::I32(val) => Some(Self::new(val as _)),
258			_ => None,
259		}
260	}
261}
262
263/// The Signature of a function
264#[derive(Eq, PartialEq, Debug, Clone)]
265pub struct Signature {
266	/// The arguments of a function.
267	pub args: Cow<'static, [ValueType]>,
268	/// The optional return value of a function.
269	pub return_value: Option<ValueType>,
270}
271
272impl Signature {
273	/// Create a new instance of `Signature`.
274	pub fn new<T: Into<Cow<'static, [ValueType]>>>(
275		args: T,
276		return_value: Option<ValueType>,
277	) -> Self {
278		Self { args: args.into(), return_value }
279	}
280
281	/// Create a new instance of `Signature` with the given `args` and without any return value.
282	pub fn new_with_args<T: Into<Cow<'static, [ValueType]>>>(args: T) -> Self {
283		Self { args: args.into(), return_value: None }
284	}
285}
286
287/// The word size used in wasm. Normally known as `usize` in Rust.
288pub type WordSize = u32;
289
290/// Sandbox memory identifier.
291pub type MemoryId = u32;
292
293/// Host pointer: suit both for 32-bit and 64-bit archs.
294pub type HostPointer = u64;
295
296/// Typed value that can be returned from a function.
297///
298/// Basically a `TypedValue` plus `Unit`, for functions which return nothing.
299#[derive(Clone, Copy, PartialEq, codec::Encode, codec::Decode, Debug)]
300pub enum ReturnValue {
301	/// For returning nothing.
302	Unit,
303	/// For returning some concrete value.
304	Value(Value),
305}
306
307impl From<Value> for ReturnValue {
308	fn from(v: Value) -> ReturnValue {
309		ReturnValue::Value(v)
310	}
311}
312
313impl ReturnValue {
314	/// Maximum number of bytes `ReturnValue` might occupy when serialized with `SCALE`.
315	///
316	/// Breakdown:
317	///  1 byte for encoding unit/value variant
318	///  1 byte for encoding value type
319	///  8 bytes for encoding the biggest value types available in wasm: f64, i64.
320	pub const ENCODED_MAX_SIZE: usize = 10;
321}
322
323#[cfg(test)]
324mod tests {
325	use super::*;
326	use codec::Encode;
327
328	#[test]
329	fn pointer_offset_works() {
330		let ptr = Pointer::<u32>::null();
331
332		assert_eq!(ptr.offset(10).unwrap(), Pointer::new(40));
333		assert_eq!(ptr.offset(32).unwrap(), Pointer::new(128));
334
335		let ptr = Pointer::<u64>::null();
336
337		assert_eq!(ptr.offset(10).unwrap(), Pointer::new(80));
338		assert_eq!(ptr.offset(32).unwrap(), Pointer::new(256));
339	}
340
341	#[test]
342	fn return_value_encoded_max_size() {
343		let encoded = ReturnValue::Value(Value::I64(-1)).encode();
344		assert_eq!(encoded.len(), ReturnValue::ENCODED_MAX_SIZE);
345	}
346}