wasmtime/
values.rs

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