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