substrate_wasmtime/
values.rs

1use crate::r#ref::ExternRef;
2use crate::{Func, Store, ValType};
3use anyhow::{bail, Result};
4use std::ptr;
5use wasmtime_runtime::{self as runtime, VMExternRef};
6
7/// Possible runtime values that a WebAssembly module can either consume or
8/// produce.
9#[derive(Debug, Clone)]
10pub enum Val {
11    /// A 32-bit integer
12    I32(i32),
13
14    /// A 64-bit integer
15    I64(i64),
16
17    /// A 32-bit float.
18    ///
19    /// Note that the raw bits of the float are stored here, and you can use
20    /// `f32::from_bits` to create an `f32` value.
21    F32(u32),
22
23    /// A 64-bit float.
24    ///
25    /// Note that the raw bits of the float are stored here, and you can use
26    /// `f64::from_bits` to create an `f64` value.
27    F64(u64),
28
29    /// An `externref` value which can hold opaque data to the Wasm instance
30    /// itself.
31    ///
32    /// `ExternRef(None)` is the null external reference, created by `ref.null
33    /// extern` in Wasm.
34    ExternRef(Option<ExternRef>),
35
36    /// A first-class reference to a WebAssembly function.
37    ///
38    /// `FuncRef(None)` is the null function reference, created by `ref.null
39    /// func` in Wasm.
40    FuncRef(Option<Func>),
41
42    /// A 128-bit number
43    V128(u128),
44}
45
46macro_rules! accessors {
47    ($bind:ident $(($variant:ident($ty:ty) $get:ident $unwrap:ident $cvt:expr))*) => ($(
48        /// Attempt to access the underlying value of this `Val`, returning
49        /// `None` if it is not the correct type.
50        pub fn $get(&self) -> Option<$ty> {
51            if let Val::$variant($bind) = self {
52                Some($cvt)
53            } else {
54                None
55            }
56        }
57
58        /// Returns the underlying value of this `Val`, panicking if it's the
59        /// wrong type.
60        ///
61        /// # Panics
62        ///
63        /// Panics if `self` is not of the right type.
64        pub fn $unwrap(&self) -> $ty {
65            self.$get().expect(concat!("expected ", stringify!($ty)))
66        }
67    )*)
68}
69
70impl Val {
71    /// Returns a null `externref` value.
72    pub fn null() -> Val {
73        Val::ExternRef(None)
74    }
75
76    /// Returns the corresponding [`ValType`] for this `Val`.
77    pub fn ty(&self) -> ValType {
78        match self {
79            Val::I32(_) => ValType::I32,
80            Val::I64(_) => ValType::I64,
81            Val::F32(_) => ValType::F32,
82            Val::F64(_) => ValType::F64,
83            Val::ExternRef(_) => ValType::ExternRef,
84            Val::FuncRef(_) => ValType::FuncRef,
85            Val::V128(_) => ValType::V128,
86        }
87    }
88
89    pub(crate) unsafe fn write_value_to(self, store: &Store, p: *mut u128) {
90        match self {
91            Val::I32(i) => ptr::write(p as *mut i32, i),
92            Val::I64(i) => ptr::write(p as *mut i64, i),
93            Val::F32(u) => ptr::write(p as *mut u32, u),
94            Val::F64(u) => ptr::write(p as *mut u64, u),
95            Val::V128(b) => ptr::write(p as *mut u128, b),
96            Val::ExternRef(None) => ptr::write(p, 0),
97            Val::ExternRef(Some(x)) => {
98                let externref_ptr = x.inner.as_raw();
99                store
100                    .externref_activations_table()
101                    .insert_with_gc(x.inner, store.stack_map_registry());
102                ptr::write(p as *mut *mut u8, externref_ptr)
103            }
104            Val::FuncRef(f) => ptr::write(
105                p as *mut *mut runtime::VMCallerCheckedAnyfunc,
106                if let Some(f) = f {
107                    f.caller_checked_anyfunc().as_ptr()
108                } else {
109                    ptr::null_mut()
110                },
111            ),
112        }
113    }
114
115    pub(crate) unsafe fn read_value_from(store: &Store, p: *const u128, ty: &ValType) -> Val {
116        match ty {
117            ValType::I32 => Val::I32(ptr::read(p as *const i32)),
118            ValType::I64 => Val::I64(ptr::read(p as *const i64)),
119            ValType::F32 => Val::F32(ptr::read(p as *const u32)),
120            ValType::F64 => Val::F64(ptr::read(p as *const u64)),
121            ValType::V128 => Val::V128(ptr::read(p as *const u128)),
122            ValType::ExternRef => {
123                let raw = ptr::read(p as *const *mut u8);
124                if raw.is_null() {
125                    Val::ExternRef(None)
126                } else {
127                    Val::ExternRef(Some(ExternRef {
128                        inner: VMExternRef::clone_from_raw(raw),
129                    }))
130                }
131            }
132            ValType::FuncRef => {
133                let func = ptr::read(p as *const *mut runtime::VMCallerCheckedAnyfunc);
134                from_checked_anyfunc(func, store)
135            }
136        }
137    }
138
139    accessors! {
140        e
141        (I32(i32) i32 unwrap_i32 *e)
142        (I64(i64) i64 unwrap_i64 *e)
143        (F32(f32) f32 unwrap_f32 f32::from_bits(*e))
144        (F64(f64) f64 unwrap_f64 f64::from_bits(*e))
145        (FuncRef(Option<&Func>) funcref unwrap_funcref e.as_ref())
146        (V128(u128) v128 unwrap_v128 *e)
147    }
148
149    /// Attempt to access the underlying `externref` value of this `Val`.
150    ///
151    /// If this is not an `externref`, then `None` is returned.
152    ///
153    /// If this is a null `externref`, then `Some(None)` is returned.
154    ///
155    /// If this is a non-null `externref`, then `Some(Some(..))` is returned.
156    pub fn externref(&self) -> Option<Option<ExternRef>> {
157        match self {
158            Val::ExternRef(e) => Some(e.clone()),
159            _ => None,
160        }
161    }
162
163    /// Returns the underlying `externref` value of this `Val`, panicking if it's the
164    /// wrong type.
165    ///
166    /// If this is a null `externref`, then `None` is returned.
167    ///
168    /// If this is a non-null `externref`, then `Some(..)` is returned.
169    ///
170    /// # Panics
171    ///
172    /// Panics if `self` is not a (nullable) `externref`.
173    pub fn unwrap_externref(&self) -> Option<ExternRef> {
174        self.externref().expect("expected externref")
175    }
176
177    pub(crate) fn into_table_element(self) -> Result<runtime::TableElement> {
178        match self {
179            Val::FuncRef(Some(f)) => Ok(runtime::TableElement::FuncRef(
180                f.caller_checked_anyfunc().as_ptr(),
181            )),
182            Val::FuncRef(None) => Ok(runtime::TableElement::FuncRef(ptr::null_mut())),
183            Val::ExternRef(Some(x)) => Ok(runtime::TableElement::ExternRef(Some(x.inner))),
184            Val::ExternRef(None) => Ok(runtime::TableElement::ExternRef(None)),
185            _ => bail!("value does not match table element type"),
186        }
187    }
188
189    pub(crate) fn comes_from_same_store(&self, store: &Store) -> bool {
190        match self {
191            Val::FuncRef(Some(f)) => Store::same(store, f.store()),
192            Val::FuncRef(None) => true,
193
194            // Integers, floats, vectors, and `externref`s have no association
195            // with any particular store, so they're always considered as "yes I
196            // came from that store",
197            Val::I32(_)
198            | Val::I64(_)
199            | Val::F32(_)
200            | Val::F64(_)
201            | Val::V128(_)
202            | Val::ExternRef(_) => true,
203        }
204    }
205}
206
207impl From<i32> for Val {
208    fn from(val: i32) -> Val {
209        Val::I32(val)
210    }
211}
212
213impl From<i64> for Val {
214    fn from(val: i64) -> Val {
215        Val::I64(val)
216    }
217}
218
219impl From<f32> for Val {
220    fn from(val: f32) -> Val {
221        Val::F32(val.to_bits())
222    }
223}
224
225impl From<f64> for Val {
226    fn from(val: f64) -> Val {
227        Val::F64(val.to_bits())
228    }
229}
230
231impl From<ExternRef> for Val {
232    fn from(val: ExternRef) -> Val {
233        Val::ExternRef(Some(val))
234    }
235}
236
237impl From<Option<ExternRef>> for Val {
238    fn from(val: Option<ExternRef>) -> Val {
239        Val::ExternRef(val)
240    }
241}
242
243impl From<Option<Func>> for Val {
244    fn from(val: Option<Func>) -> Val {
245        Val::FuncRef(val)
246    }
247}
248
249impl From<Func> for Val {
250    fn from(val: Func) -> Val {
251        Val::FuncRef(Some(val))
252    }
253}
254
255pub(crate) fn into_checked_anyfunc(
256    val: Val,
257    store: &Store,
258) -> Result<*mut wasmtime_runtime::VMCallerCheckedAnyfunc> {
259    if !val.comes_from_same_store(store) {
260        bail!("cross-`Store` values are not supported");
261    }
262    Ok(match val {
263        Val::FuncRef(None) => ptr::null_mut(),
264        Val::FuncRef(Some(f)) => f.caller_checked_anyfunc().as_ptr(),
265        _ => bail!("val is not funcref"),
266    })
267}
268
269pub(crate) unsafe fn from_checked_anyfunc(
270    anyfunc: *mut wasmtime_runtime::VMCallerCheckedAnyfunc,
271    store: &Store,
272) -> Val {
273    Val::FuncRef(Func::from_caller_checked_anyfunc(store, anyfunc))
274}