tetcore_wasm_interface/
lib.rs

1// This file is part of Tetcore.
2
3// Copyright (C) 2019-2021 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 tetcore_std::{
23	vec,
24	borrow::Cow, marker::PhantomData, mem, iter::Iterator, result, vec::Vec,
25};
26
27#[cfg(feature = "std")]
28mod twasmi_impl;
29
30/// Result type used by traits in this crate.
31#[cfg(feature = "std")]
32pub type Result<T> = result::Result<T, String>;
33#[cfg(not(feature = "std"))]
34pub type Result<T> = result::Result<T, &'static str>;
35
36/// Value types supported by Tetcore on the boundary between host/Wasm.
37#[derive(Copy, Clone, PartialEq, Debug, Eq)]
38pub enum ValueType {
39	/// An `i32` value type.
40	I32,
41	/// An `i64` value type.
42	I64,
43	/// An `f32` value type.
44	F32,
45	/// An `f64` value type.
46	F64,
47}
48
49impl From<ValueType> for u8 {
50	fn from(val: ValueType) -> u8 {
51		match val {
52			ValueType::I32 => 0,
53			ValueType::I64 => 1,
54			ValueType::F32 => 2,
55			ValueType::F64 => 3,
56		}
57	}
58}
59
60impl tetcore_std::convert::TryFrom<u8> for ValueType {
61	type Error = ();
62
63	fn try_from(val: u8) -> tetcore_std::result::Result<ValueType, ()> {
64		match val {
65			0 => Ok(Self::I32),
66			1 => Ok(Self::I64),
67			2 => Ok(Self::F32),
68			3 => Ok(Self::F64),
69			_ => Err(()),
70		}
71	}
72}
73
74/// Values supported by Tetcore on the boundary between host/Wasm.
75#[derive(PartialEq, Debug, Clone, Copy, codec::Encode, codec::Decode)]
76pub enum Value {
77	/// A 32-bit integer.
78	I32(i32),
79	/// A 64-bit integer.
80	I64(i64),
81	/// A 32-bit floating-point number stored as raw bit pattern.
82	///
83	/// You can materialize this value using `f32::from_bits`.
84	F32(u32),
85	/// A 64-bit floating-point number stored as raw bit pattern.
86	///
87	/// You can materialize this value using `f64::from_bits`.
88	F64(u64),
89}
90
91impl Value {
92	/// Returns the type of this value.
93	pub fn value_type(&self) -> ValueType {
94		match self {
95			Value::I32(_) => ValueType::I32,
96			Value::I64(_) => ValueType::I64,
97			Value::F32(_) => ValueType::F32,
98			Value::F64(_) => ValueType::F64,
99		}
100	}
101
102	/// Return `Self` as `i32`.
103	pub fn as_i32(&self) -> Option<i32> {
104		match self {
105			Self::I32(val) => Some(*val),
106			_ => None,
107		}
108	}
109}
110
111/// Provides `Sealed` trait to prevent implementing trait `PointerType` outside of this crate.
112mod private {
113	pub trait Sealed {}
114
115	impl Sealed for u8 {}
116	impl Sealed for u16 {}
117	impl Sealed for u32 {}
118	impl Sealed for u64 {}
119}
120
121/// Something that can be wrapped in a wasm `Pointer`.
122///
123/// This trait is sealed.
124pub trait PointerType: Sized + private::Sealed {
125	/// The size of the type in wasm.
126	const SIZE: u32 = mem::size_of::<Self>() as u32;
127}
128
129impl PointerType for u8 {}
130impl PointerType for u16 {}
131impl PointerType for u32 {}
132impl PointerType for u64 {}
133
134/// Type to represent a pointer in wasm at the host.
135#[derive(Debug, PartialEq, Eq, Clone, Copy)]
136pub struct Pointer<T: PointerType> {
137	ptr: u32,
138	_marker: PhantomData<T>,
139}
140
141impl<T: PointerType> Pointer<T> {
142	/// Create a new instance of `Self`.
143	pub fn new(ptr: u32) -> Self {
144		Self {
145			ptr,
146			_marker: Default::default(),
147		}
148	}
149
150	/// Calculate the offset from this pointer.
151	///
152	/// `offset` is in units of `T`. So, `3` means `3 * mem::size_of::<T>()` as offset to the pointer.
153	///
154	/// Returns an `Option` to respect that the pointer could probably overflow.
155	pub fn offset(self, offset: u32) -> Option<Self> {
156		offset.checked_mul(T::SIZE).and_then(|o| self.ptr.checked_add(o)).map(|ptr| {
157			Self {
158				ptr,
159				_marker: Default::default(),
160			}
161		})
162	}
163
164	/// Create a null pointer.
165	pub fn null() -> Self {
166		Self::new(0)
167	}
168
169	/// Cast this pointer of type `T` to a pointer of type `R`.
170	pub fn cast<R: PointerType>(self) -> Pointer<R> {
171		Pointer::new(self.ptr)
172	}
173}
174
175impl<T: PointerType> From<u32> for Pointer<T> {
176	fn from(ptr: u32) -> Self {
177		Pointer::new(ptr)
178	}
179}
180
181impl<T: PointerType> From<Pointer<T>> for u32 {
182	fn from(ptr: Pointer<T>) -> Self {
183		ptr.ptr
184	}
185}
186
187impl<T: PointerType> From<Pointer<T>> for u64 {
188	fn from(ptr: Pointer<T>) -> Self {
189		u64::from(ptr.ptr)
190	}
191}
192
193impl<T: PointerType> From<Pointer<T>> for usize {
194	fn from(ptr: Pointer<T>) -> Self {
195		ptr.ptr as _
196	}
197}
198
199impl<T: PointerType> IntoValue for Pointer<T> {
200	const VALUE_TYPE: ValueType = ValueType::I32;
201	fn into_value(self) -> Value { Value::I32(self.ptr as _) }
202}
203
204impl<T: PointerType> TryFromValue for Pointer<T> {
205	fn try_from_value(val: Value) -> Option<Self> {
206		match val {
207			Value::I32(val) => Some(Self::new(val as _)),
208			_ => None,
209		}
210	}
211}
212
213/// The word size used in wasm. Normally known as `usize` in Rust.
214pub type WordSize = u32;
215
216/// The Signature of a function
217#[derive(Eq, PartialEq, Debug, Clone)]
218pub struct Signature {
219	/// The arguments of a function.
220	pub args: Cow<'static, [ValueType]>,
221	/// The optional return value of a function.
222	pub return_value: Option<ValueType>,
223}
224
225impl Signature {
226	/// Create a new instance of `Signature`.
227	pub fn new<T: Into<Cow<'static, [ValueType]>>>(args: T, return_value: Option<ValueType>) -> Self {
228		Self {
229			args: args.into(),
230			return_value,
231		}
232	}
233
234	/// Create a new instance of `Signature` with the given `args` and without any return value.
235	pub fn new_with_args<T: Into<Cow<'static, [ValueType]>>>(args: T) -> Self {
236		Self {
237			args: args.into(),
238			return_value: None,
239		}
240	}
241}
242
243/// A trait that requires `RefUnwindSafe` when `feature = std`.
244#[cfg(feature = "std")]
245pub trait MaybeRefUnwindSafe: std::panic::RefUnwindSafe {}
246#[cfg(feature = "std")]
247impl<T: std::panic::RefUnwindSafe> MaybeRefUnwindSafe for T {}
248
249/// A trait that requires `RefUnwindSafe` when `feature = std`.
250#[cfg(not(feature = "std"))]
251pub trait MaybeRefUnwindSafe {}
252#[cfg(not(feature = "std"))]
253impl<T> MaybeRefUnwindSafe for T {}
254
255/// Something that provides a function implementation on the host for a wasm function.
256pub trait Function: MaybeRefUnwindSafe + Send + Sync {
257	/// Returns the name of this function.
258	fn name(&self) -> &str;
259	/// Returns the signature of this function.
260	fn signature(&self) -> Signature;
261	/// Execute this function with the given arguments.
262	fn execute(
263		&self,
264		context: &mut dyn FunctionContext,
265		args: &mut dyn Iterator<Item = Value>,
266	) -> Result<Option<Value>>;
267}
268
269impl PartialEq for dyn Function {
270	fn eq(&self, other: &Self) -> bool {
271		other.name() == self.name() && other.signature() == self.signature()
272	}
273}
274
275/// Context used by `Function` to interact with the allocator and the memory of the wasm instance.
276pub trait FunctionContext {
277	/// Read memory from `address` into a vector.
278	fn read_memory(&self, address: Pointer<u8>, size: WordSize) -> Result<Vec<u8>> {
279		let mut vec = vec![0; size as usize];
280		self.read_memory_into(address, &mut vec)?;
281		Ok(vec)
282	}
283	/// Read memory into the given `dest` buffer from `address`.
284	fn read_memory_into(&self, address: Pointer<u8>, dest: &mut [u8]) -> Result<()>;
285	/// Write the given data at `address` into the memory.
286	fn write_memory(&mut self, address: Pointer<u8>, data: &[u8]) -> Result<()>;
287	/// Allocate a memory instance of `size` bytes.
288	fn allocate_memory(&mut self, size: WordSize) -> Result<Pointer<u8>>;
289	/// Deallocate a given memory instance.
290	fn deallocate_memory(&mut self, ptr: Pointer<u8>) -> Result<()>;
291	/// Provides access to the sandbox.
292	fn sandbox(&mut self) -> &mut dyn Sandbox;
293}
294
295/// Sandbox memory identifier.
296pub type MemoryId = u32;
297
298/// Something that provides access to the sandbox.
299pub trait Sandbox {
300	/// Get sandbox memory from the `memory_id` instance at `offset` into the given buffer.
301	fn memory_get(
302		&mut self,
303		memory_id: MemoryId,
304		offset: WordSize,
305		buf_ptr: Pointer<u8>,
306		buf_len: WordSize,
307	) -> Result<u32>;
308	/// Set sandbox memory from the given value.
309	fn memory_set(
310		&mut self,
311		memory_id: MemoryId,
312		offset: WordSize,
313		val_ptr: Pointer<u8>,
314		val_len: WordSize,
315	) -> Result<u32>;
316	/// Delete a memory instance.
317	fn memory_teardown(&mut self, memory_id: MemoryId) -> Result<()>;
318	/// Create a new memory instance with the given `initial` size and the `maximum` size.
319	/// The size is given in wasm pages.
320	fn memory_new(&mut self, initial: u32, maximum: u32) -> Result<MemoryId>;
321	/// Invoke an exported function by a name.
322	fn invoke(
323		&mut self,
324		instance_id: u32,
325		export_name: &str,
326		args: &[u8],
327		return_val: Pointer<u8>,
328		return_val_len: WordSize,
329		state: u32,
330	) -> Result<u32>;
331	/// Delete a sandbox instance.
332	fn instance_teardown(&mut self, instance_id: u32) -> Result<()>;
333	/// Create a new sandbox instance.
334	fn instance_new(
335		&mut self,
336		dispatch_thunk_id: u32,
337		wasm: &[u8],
338		raw_env_def: &[u8],
339		state: u32,
340	) -> Result<u32>;
341
342	/// Get the value from a global with the given `name`. The sandbox is determined by the
343	/// given `instance_idx` instance.
344	///
345	/// Returns `Some(_)` when the requested global variable could be found.
346	fn get_global_val(&self, instance_idx: u32, name: &str) -> Result<Option<Value>>;
347}
348
349/// Something that provides implementations for host functions.
350pub trait HostFunctions: 'static {
351	/// Returns the host functions `Self` provides.
352	fn host_functions() -> Vec<&'static dyn Function>;
353}
354
355#[impl_trait_for_tuples::impl_for_tuples(30)]
356impl HostFunctions for Tuple {
357	fn host_functions() -> Vec<&'static dyn Function> {
358		let mut host_functions = Vec::new();
359
360		for_tuples!( #( host_functions.extend(Tuple::host_functions()); )* );
361
362		host_functions
363	}
364}
365
366/// Something that can be converted into a wasm compatible `Value`.
367pub trait IntoValue {
368	/// The type of the value in wasm.
369	const VALUE_TYPE: ValueType;
370
371	/// Convert `self` into a wasm `Value`.
372	fn into_value(self) -> Value;
373}
374
375/// Something that can may be created from a wasm `Value`.
376pub trait TryFromValue: Sized {
377	/// Try to convert the given `Value` into `Self`.
378	fn try_from_value(val: Value) -> Option<Self>;
379}
380
381macro_rules! impl_into_and_from_value {
382	(
383		$(
384			$type:ty, $( < $gen:ident >, )? $value_variant:ident,
385		)*
386	) => {
387		$(
388			impl $( <$gen> )? IntoValue for $type {
389				const VALUE_TYPE: ValueType = ValueType::$value_variant;
390				fn into_value(self) -> Value { Value::$value_variant(self as _) }
391			}
392
393			impl $( <$gen> )? TryFromValue for $type {
394				fn try_from_value(val: Value) -> Option<Self> {
395					match val {
396						Value::$value_variant(val) => Some(val as _),
397						_ => None,
398					}
399				}
400			}
401		)*
402	}
403}
404
405impl_into_and_from_value! {
406	u8, I32,
407	u16, I32,
408	u32, I32,
409	u64, I64,
410	i8, I32,
411	i16, I32,
412	i32, I32,
413	i64, I64,
414}
415
416/// Something that can write a primitive to wasm memory location.
417pub trait WritePrimitive<T: PointerType> {
418	/// Write the given value `t` to the given memory location `ptr`.
419	fn write_primitive(&mut self, ptr: Pointer<T>, t: T) -> Result<()>;
420}
421
422impl WritePrimitive<u32> for &mut dyn FunctionContext {
423	fn write_primitive(&mut self, ptr: Pointer<u32>, t: u32) -> Result<()> {
424		let r = t.to_le_bytes();
425		self.write_memory(ptr.cast(), &r)
426	}
427}
428
429impl WritePrimitive<u64> for &mut dyn FunctionContext {
430	fn write_primitive(&mut self, ptr: Pointer<u64>, t: u64) -> Result<()> {
431		let r = t.to_le_bytes();
432		self.write_memory(ptr.cast(), &r)
433	}
434}
435
436/// Something that can read a primitive from a wasm memory location.
437pub trait ReadPrimitive<T: PointerType> {
438	/// Read a primitive from the given memory location `ptr`.
439	fn read_primitive(&self, ptr: Pointer<T>) -> Result<T>;
440}
441
442impl ReadPrimitive<u32> for &mut dyn FunctionContext {
443	fn read_primitive(&self, ptr: Pointer<u32>) -> Result<u32> {
444		let mut r = [0u8; 4];
445		self.read_memory_into(ptr.cast(), &mut r)?;
446		Ok(u32::from_le_bytes(r))
447	}
448}
449
450impl ReadPrimitive<u64> for &mut dyn FunctionContext {
451	fn read_primitive(&self, ptr: Pointer<u64>) -> Result<u64> {
452		let mut r = [0u8; 8];
453		self.read_memory_into(ptr.cast(), &mut r)?;
454		Ok(u64::from_le_bytes(r))
455	}
456}
457
458/// Typed value that can be returned from a function.
459///
460/// Basically a `TypedValue` plus `Unit`, for functions which return nothing.
461#[derive(Clone, Copy, PartialEq, codec::Encode, codec::Decode, Debug)]
462pub enum ReturnValue {
463	/// For returning nothing.
464	Unit,
465	/// For returning some concrete value.
466	Value(Value),
467}
468
469impl From<Value> for ReturnValue {
470	fn from(v: Value) -> ReturnValue {
471		ReturnValue::Value(v)
472	}
473}
474
475impl ReturnValue {
476	/// Maximum number of bytes `ReturnValue` might occupy when serialized with `SCALE`.
477	///
478	/// Breakdown:
479	///  1 byte for encoding unit/value variant
480	///  1 byte for encoding value type
481	///  8 bytes for encoding the biggest value types available in wasm: f64, i64.
482	pub const ENCODED_MAX_SIZE: usize = 10;
483}
484
485#[cfg(test)]
486mod tests {
487	use super::*;
488	use codec::Encode;
489
490	#[test]
491	fn pointer_offset_works() {
492		let ptr = Pointer::<u32>::null();
493
494		assert_eq!(ptr.offset(10).unwrap(), Pointer::new(40));
495		assert_eq!(ptr.offset(32).unwrap(), Pointer::new(128));
496
497		let ptr = Pointer::<u64>::null();
498
499		assert_eq!(ptr.offset(10).unwrap(), Pointer::new(80));
500		assert_eq!(ptr.offset(32).unwrap(), Pointer::new(256));
501	}
502
503
504	#[test]
505	fn return_value_encoded_max_size() {
506		let encoded = ReturnValue::Value(Value::I64(-1)).encode();
507		assert_eq!(encoded.len(), ReturnValue::ENCODED_MAX_SIZE);
508	}
509}