Skip to main content

gsp_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 core::{marker::PhantomData, mem};
23use sp_std::borrow::Cow;
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 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 {
196            ptr,
197            _marker: Default::default(),
198        }
199    }
200
201    /// Calculate the offset from this pointer.
202    ///
203    /// `offset` is in units of `T`. So, `3` means `3 * mem::size_of::<T>()` as offset to the
204    /// pointer.
205    ///
206    /// Returns an `Option` to respect that the pointer could probably overflow.
207    pub fn offset(self, offset: u32) -> Option<Self> {
208        offset
209            .checked_mul(T::SIZE)
210            .and_then(|o| self.ptr.checked_add(o))
211            .map(|ptr| Self {
212                ptr,
213                _marker: Default::default(),
214            })
215    }
216
217    /// Create a null pointer.
218    pub fn null() -> Self {
219        Self::new(0)
220    }
221
222    /// Cast this pointer of type `T` to a pointer of type `R`.
223    pub fn cast<R: PointerType>(self) -> Pointer<R> {
224        Pointer::new(self.ptr)
225    }
226}
227
228impl<T: PointerType> From<u32> for Pointer<T> {
229    fn from(ptr: u32) -> Self {
230        Pointer::new(ptr)
231    }
232}
233
234impl<T: PointerType> From<Pointer<T>> for u32 {
235    fn from(ptr: Pointer<T>) -> Self {
236        ptr.ptr
237    }
238}
239
240impl<T: PointerType> From<Pointer<T>> for u64 {
241    fn from(ptr: Pointer<T>) -> Self {
242        u64::from(ptr.ptr)
243    }
244}
245
246impl<T: PointerType> From<Pointer<T>> for usize {
247    fn from(ptr: Pointer<T>) -> Self {
248        ptr.ptr as _
249    }
250}
251
252impl<T: PointerType> IntoValue for Pointer<T> {
253    const VALUE_TYPE: ValueType = ValueType::I32;
254    fn into_value(self) -> Value {
255        Value::I32(self.ptr as _)
256    }
257}
258
259impl<T: PointerType> TryFromValue for Pointer<T> {
260    fn try_from_value(val: Value) -> Option<Self> {
261        match val {
262            Value::I32(val) => Some(Self::new(val as _)),
263            _ => None,
264        }
265    }
266}
267
268/// The signature of a function.
269#[derive(Eq, PartialEq, Debug, Clone)]
270pub struct Signature {
271    /// The arguments of a function.
272    pub args: Cow<'static, [ValueType]>,
273    /// The optional return value of a function.
274    pub return_value: Option<ValueType>,
275}
276
277impl Signature {
278    /// Create a new instance of `Signature`.
279    pub fn new<T: Into<Cow<'static, [ValueType]>>>(
280        args: T,
281        return_value: Option<ValueType>,
282    ) -> Self {
283        Self {
284            args: args.into(),
285            return_value,
286        }
287    }
288
289    /// Create a new instance of `Signature` with the given `args` and without any return value.
290    pub fn new_with_args<T: Into<Cow<'static, [ValueType]>>>(args: T) -> Self {
291        Self {
292            args: args.into(),
293            return_value: None,
294        }
295    }
296}
297
298/// The word size used in wasm. Normally known as `usize` in Rust.
299pub type WordSize = u32;
300
301/// Sandbox memory identifier.
302pub type MemoryId = u32;
303
304/// Host pointer sized for both 32-bit and 64-bit architectures.
305pub type HostPointer = u64;
306
307/// Typed value that can be returned from a function.
308///
309/// Basically a `TypedValue` plus `Unit`, for functions which return nothing.
310#[derive(Clone, Copy, PartialEq, codec::Encode, codec::Decode, Debug)]
311pub enum ReturnValue {
312    /// For returning nothing.
313    Unit,
314    /// For returning some concrete value.
315    Value(Value),
316}
317
318impl From<Value> for ReturnValue {
319    fn from(v: Value) -> ReturnValue {
320        ReturnValue::Value(v)
321    }
322}
323
324impl ReturnValue {
325    /// Maximum number of bytes `ReturnValue` might occupy when serialized with `SCALE`.
326    ///
327    /// Breakdown:
328    ///  1 byte for encoding unit/value variant
329    ///  1 byte for encoding value type
330    ///  8 bytes for encoding the biggest value types available in wasm: f64, i64.
331    pub const ENCODED_MAX_SIZE: usize = 10;
332}
333
334#[cfg(test)]
335mod tests {
336    use super::*;
337    use codec::Encode;
338
339    #[test]
340    fn pointer_offset_works() {
341        let ptr = Pointer::<u32>::null();
342
343        assert_eq!(ptr.offset(10).unwrap(), Pointer::new(40));
344        assert_eq!(ptr.offset(32).unwrap(), Pointer::new(128));
345
346        let ptr = Pointer::<u64>::null();
347
348        assert_eq!(ptr.offset(10).unwrap(), Pointer::new(80));
349        assert_eq!(ptr.offset(32).unwrap(), Pointer::new(256));
350    }
351
352    #[test]
353    fn return_value_encoded_max_size() {
354        let encoded = ReturnValue::Value(Value::I64(-1)).encode();
355        assert_eq!(encoded.len(), ReturnValue::ENCODED_MAX_SIZE);
356    }
357}