wasmtime/runtime/
values.rs

1use crate::store::{AutoAssertNoGc, StoreOpaque};
2use crate::{
3    AnyRef, ArrayRef, AsContext, AsContextMut, ExnRef, ExternRef, Func, HeapType, RefType, Rooted,
4    StructRef, V128, ValType, prelude::*,
5};
6use core::ptr;
7use wasmtime_environ::WasmHeapTopType;
8
9pub use crate::runtime::vm::ValRaw;
10
11/// Possible runtime values that a WebAssembly module can either consume or
12/// produce.
13///
14/// Note that we inline the `enum Ref { ... }` variants into `enum Val { ... }`
15/// here as a size optimization.
16#[derive(Debug, Clone, Copy)]
17pub enum Val {
18    // NB: the ordering here is intended to match the ordering in
19    // `ValType` to improve codegen when learning the type of a value.
20    //
21    /// A 32-bit integer.
22    I32(i32),
23
24    /// A 64-bit integer.
25    I64(i64),
26
27    /// A 32-bit float.
28    ///
29    /// Note that the raw bits of the float are stored here, and you can use
30    /// `f32::from_bits` to create an `f32` value.
31    F32(u32),
32
33    /// A 64-bit float.
34    ///
35    /// Note that the raw bits of the float are stored here, and you can use
36    /// `f64::from_bits` to create an `f64` value.
37    F64(u64),
38
39    /// A 128-bit number.
40    V128(V128),
41
42    /// A function reference.
43    FuncRef(Option<Func>),
44
45    /// An external reference.
46    ExternRef(Option<Rooted<ExternRef>>),
47
48    /// An internal reference.
49    AnyRef(Option<Rooted<AnyRef>>),
50
51    /// An exception reference.
52    ExnRef(Option<Rooted<ExnRef>>),
53}
54
55macro_rules! accessors {
56    ($bind:ident $(($variant:ident($ty:ty) $get:ident $unwrap:ident $cvt:expr))*) => ($(
57        /// Attempt to access the underlying value of this `Val`, returning
58        /// `None` if it is not the correct type.
59        #[inline]
60        pub fn $get(&self) -> Option<$ty> {
61            if let Val::$variant($bind) = self {
62                Some($cvt)
63            } else {
64                None
65            }
66        }
67
68        /// Returns the underlying value of this `Val`, panicking if it's the
69        /// wrong type.
70        ///
71        /// # Panics
72        ///
73        /// Panics if `self` is not of the right type.
74        #[inline]
75        pub fn $unwrap(&self) -> $ty {
76            self.$get().expect(concat!("expected ", stringify!($ty)))
77        }
78    )*)
79}
80
81impl Val {
82    /// Returns the null reference for the given heap type.
83    #[inline]
84    pub fn null_ref(heap_type: &HeapType) -> Val {
85        Ref::null(&heap_type).into()
86    }
87
88    /// Returns the null function reference value.
89    ///
90    /// The return value has type `(ref null nofunc)` aka `nullfuncref` and is a
91    /// subtype of all function references.
92    #[inline]
93    pub const fn null_func_ref() -> Val {
94        Val::FuncRef(None)
95    }
96
97    /// Returns the null function reference value.
98    ///
99    /// The return value has type `(ref null extern)` aka `nullexternref` and is
100    /// a subtype of all external references.
101    #[inline]
102    pub const fn null_extern_ref() -> Val {
103        Val::ExternRef(None)
104    }
105
106    /// Returns the null function reference value.
107    ///
108    /// The return value has type `(ref null any)` aka `nullref` and is a
109    /// subtype of all internal references.
110    #[inline]
111    pub const fn null_any_ref() -> Val {
112        Val::AnyRef(None)
113    }
114
115    pub(crate) const fn null_top(top: WasmHeapTopType) -> Val {
116        match top {
117            WasmHeapTopType::Func => Val::FuncRef(None),
118            WasmHeapTopType::Extern => Val::ExternRef(None),
119            WasmHeapTopType::Any => Val::AnyRef(None),
120            WasmHeapTopType::Exn => Val::ExnRef(None),
121            WasmHeapTopType::Cont => todo!(), // FIXME(#10248)
122        }
123    }
124
125    /// Returns the default value for the given type, if any exists.
126    ///
127    /// Returns `None` if there is no default value for the given type (for
128    /// example, non-nullable reference types do not have a default value).
129    pub fn default_for_ty(ty: &ValType) -> Option<Val> {
130        match ty {
131            ValType::I32 => Some(Val::I32(0)),
132            ValType::I64 => Some(Val::I64(0)),
133            ValType::F32 => Some(Val::F32(0)),
134            ValType::F64 => Some(Val::F64(0)),
135            ValType::V128 => Some(Val::V128(V128::from(0))),
136            ValType::Ref(ref_ty) => {
137                if ref_ty.is_nullable() {
138                    Some(Val::null_ref(ref_ty.heap_type()))
139                } else {
140                    None
141                }
142            }
143        }
144    }
145
146    /// Returns the corresponding [`ValType`] for this `Val`.
147    ///
148    /// # Errors
149    ///
150    /// Returns an error if this value is a GC reference that has since been
151    /// unrooted.
152    ///
153    /// # Panics
154    ///
155    /// Panics if this value is associated with a different store.
156    #[inline]
157    pub fn ty(&self, store: impl AsContext) -> Result<ValType> {
158        self.load_ty(&store.as_context().0)
159    }
160
161    #[inline]
162    pub(crate) fn load_ty(&self, store: &StoreOpaque) -> Result<ValType> {
163        Ok(match self {
164            Val::I32(_) => ValType::I32,
165            Val::I64(_) => ValType::I64,
166            Val::F32(_) => ValType::F32,
167            Val::F64(_) => ValType::F64,
168            Val::V128(_) => ValType::V128,
169            Val::ExternRef(Some(_)) => ValType::EXTERNREF,
170            Val::ExternRef(None) => ValType::NULLFUNCREF,
171            Val::FuncRef(None) => ValType::NULLFUNCREF,
172            Val::FuncRef(Some(f)) => ValType::Ref(RefType::new(
173                false,
174                HeapType::ConcreteFunc(f.load_ty(store)),
175            )),
176            Val::AnyRef(None) => ValType::NULLREF,
177            Val::AnyRef(Some(a)) => ValType::Ref(RefType::new(false, a._ty(store)?)),
178            Val::ExnRef(None) => ValType::NULLEXNREF,
179            Val::ExnRef(Some(e)) => ValType::Ref(RefType::new(false, e._ty(store)?.into())),
180        })
181    }
182
183    /// Does this value match the given type?
184    ///
185    /// Returns an error is an underlying `Rooted` has been unrooted.
186    ///
187    /// # Panics
188    ///
189    /// Panics if this value is not associated with the given store.
190    pub fn matches_ty(&self, store: impl AsContext, ty: &ValType) -> Result<bool> {
191        self._matches_ty(&store.as_context().0, ty)
192    }
193
194    pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &ValType) -> Result<bool> {
195        assert!(self.comes_from_same_store(store));
196        assert!(ty.comes_from_same_engine(store.engine()));
197        Ok(match (self, ty) {
198            (Val::I32(_), ValType::I32)
199            | (Val::I64(_), ValType::I64)
200            | (Val::F32(_), ValType::F32)
201            | (Val::F64(_), ValType::F64)
202            | (Val::V128(_), ValType::V128) => true,
203
204            (Val::FuncRef(f), ValType::Ref(ref_ty)) => Ref::from(*f)._matches_ty(store, ref_ty)?,
205            (Val::ExternRef(e), ValType::Ref(ref_ty)) => {
206                Ref::from(*e)._matches_ty(store, ref_ty)?
207            }
208            (Val::AnyRef(a), ValType::Ref(ref_ty)) => Ref::from(*a)._matches_ty(store, ref_ty)?,
209            (Val::ExnRef(e), ValType::Ref(ref_ty)) => Ref::from(*e)._matches_ty(store, ref_ty)?,
210
211            (Val::I32(_), _)
212            | (Val::I64(_), _)
213            | (Val::F32(_), _)
214            | (Val::F64(_), _)
215            | (Val::V128(_), _)
216            | (Val::FuncRef(_), _)
217            | (Val::ExternRef(_), _)
218            | (Val::AnyRef(_), _)
219            | (Val::ExnRef(_), _) => false,
220        })
221    }
222
223    pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &ValType) -> Result<()> {
224        if !self.comes_from_same_store(store) {
225            bail!("value used with wrong store")
226        }
227        if !ty.comes_from_same_engine(store.engine()) {
228            bail!("type used with wrong engine")
229        }
230        if self._matches_ty(store, ty)? {
231            Ok(())
232        } else {
233            let actual_ty = self.load_ty(store)?;
234            bail!("type mismatch: expected {ty}, found {actual_ty}")
235        }
236    }
237
238    /// Convenience method to convert this [`Val`] into a [`ValRaw`].
239    ///
240    /// Returns an error if this value is a GC reference and the GC reference
241    /// has been unrooted.
242    ///
243    /// # Safety
244    ///
245    /// The returned [`ValRaw`] does not carry type information and is only safe
246    /// to use within the context of this store itself. For more information see
247    /// [`ExternRef::to_raw`] and [`Func::to_raw`].
248    pub fn to_raw(&self, store: impl AsContextMut) -> Result<ValRaw> {
249        match self {
250            Val::I32(i) => Ok(ValRaw::i32(*i)),
251            Val::I64(i) => Ok(ValRaw::i64(*i)),
252            Val::F32(u) => Ok(ValRaw::f32(*u)),
253            Val::F64(u) => Ok(ValRaw::f64(*u)),
254            Val::V128(b) => Ok(ValRaw::v128(b.as_u128())),
255            Val::ExternRef(e) => Ok(ValRaw::externref(match e {
256                None => 0,
257                Some(e) => e.to_raw(store)?,
258            })),
259            Val::AnyRef(e) => Ok(ValRaw::anyref(match e {
260                None => 0,
261                Some(e) => e.to_raw(store)?,
262            })),
263            Val::ExnRef(e) => Ok(ValRaw::exnref(match e {
264                None => 0,
265                Some(e) => e.to_raw(store)?,
266            })),
267            Val::FuncRef(f) => Ok(ValRaw::funcref(match f {
268                Some(f) => f.to_raw(store),
269                None => ptr::null_mut(),
270            })),
271        }
272    }
273
274    /// Convenience method to convert a [`ValRaw`] into a [`Val`].
275    ///
276    /// # Unsafety
277    ///
278    /// This method is unsafe for the reasons that [`ExternRef::from_raw`] and
279    /// [`Func::from_raw`] are unsafe. Additionally there's no guarantee
280    /// otherwise that `raw` should have the type `ty` specified.
281    pub unsafe fn from_raw(mut store: impl AsContextMut, raw: ValRaw, ty: ValType) -> Val {
282        let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
283        // SAFETY: `_from_raw` has the same contract as this function.
284        unsafe { Self::_from_raw(&mut store, raw, &ty) }
285    }
286
287    /// Same as [`Self::from_raw`], but with a monomorphic store.
288    pub(crate) unsafe fn _from_raw(
289        store: &mut AutoAssertNoGc<'_>,
290        raw: ValRaw,
291        ty: &ValType,
292    ) -> Val {
293        match ty {
294            ValType::I32 => Val::I32(raw.get_i32()),
295            ValType::I64 => Val::I64(raw.get_i64()),
296            ValType::F32 => Val::F32(raw.get_f32()),
297            ValType::F64 => Val::F64(raw.get_f64()),
298            ValType::V128 => Val::V128(raw.get_v128().into()),
299            ValType::Ref(ref_ty) => {
300                let ref_ = match ref_ty.heap_type() {
301                    // SAFETY: it's a safety contract of this function that the
302                    // funcref is valid and owned by the provided store.
303                    HeapType::Func | HeapType::ConcreteFunc(_) => unsafe {
304                        Func::_from_raw(store, raw.get_funcref()).into()
305                    },
306
307                    HeapType::NoFunc => Ref::Func(None),
308
309                    HeapType::NoCont | HeapType::ConcreteCont(_) | HeapType::Cont => {
310                        // TODO(#10248): Required to support stack switching in the embedder API.
311                        unimplemented!()
312                    }
313
314                    HeapType::Extern => ExternRef::_from_raw(store, raw.get_externref()).into(),
315
316                    HeapType::NoExtern => Ref::Extern(None),
317
318                    HeapType::Any
319                    | HeapType::Eq
320                    | HeapType::I31
321                    | HeapType::Array
322                    | HeapType::ConcreteArray(_)
323                    | HeapType::Struct
324                    | HeapType::ConcreteStruct(_) => {
325                        AnyRef::_from_raw(store, raw.get_anyref()).into()
326                    }
327
328                    HeapType::Exn | HeapType::ConcreteExn(_) => {
329                        ExnRef::_from_raw(store, raw.get_exnref()).into()
330                    }
331                    HeapType::NoExn => Ref::Exn(None),
332
333                    HeapType::None => Ref::Any(None),
334                };
335                assert!(
336                    ref_ty.is_nullable() || !ref_.is_null(),
337                    "if the type is not nullable, we shouldn't get null; got \
338                     type = {ref_ty}, ref = {ref_:?}"
339                );
340                ref_.into()
341            }
342        }
343    }
344
345    accessors! {
346        e
347        (I32(i32) i32 unwrap_i32 *e)
348        (I64(i64) i64 unwrap_i64 *e)
349        (F32(f32) f32 unwrap_f32 f32::from_bits(*e))
350        (F64(f64) f64 unwrap_f64 f64::from_bits(*e))
351        (FuncRef(Option<&Func>) func_ref unwrap_func_ref e.as_ref())
352        (ExternRef(Option<&Rooted<ExternRef>>) extern_ref unwrap_extern_ref e.as_ref())
353        (AnyRef(Option<&Rooted<AnyRef>>) any_ref unwrap_any_ref e.as_ref())
354        (V128(V128) v128 unwrap_v128 *e)
355    }
356
357    /// Get this value's underlying reference, if any.
358    #[inline]
359    pub fn ref_(self) -> Option<Ref> {
360        match self {
361            Val::FuncRef(f) => Some(Ref::Func(f)),
362            Val::ExternRef(e) => Some(Ref::Extern(e)),
363            Val::AnyRef(a) => Some(Ref::Any(a)),
364            Val::ExnRef(e) => Some(Ref::Exn(e)),
365            Val::I32(_) | Val::I64(_) | Val::F32(_) | Val::F64(_) | Val::V128(_) => None,
366        }
367    }
368
369    /// Attempt to access the underlying `externref` value of this `Val`.
370    ///
371    /// If this is not an `externref`, then `None` is returned.
372    ///
373    /// If this is a null `externref`, then `Some(None)` is returned.
374    ///
375    /// If this is a non-null `externref`, then `Some(Some(..))` is returned.
376    #[inline]
377    pub fn externref(&self) -> Option<Option<&Rooted<ExternRef>>> {
378        match self {
379            Val::ExternRef(None) => Some(None),
380            Val::ExternRef(Some(e)) => Some(Some(e)),
381            _ => None,
382        }
383    }
384
385    /// Returns the underlying `externref` value of this `Val`, panicking if it's the
386    /// wrong type.
387    ///
388    /// If this is a null `externref`, then `None` is returned.
389    ///
390    /// If this is a non-null `externref`, then `Some(..)` is returned.
391    ///
392    /// # Panics
393    ///
394    /// Panics if `self` is not a (nullable) `externref`.
395    #[inline]
396    pub fn unwrap_externref(&self) -> Option<&Rooted<ExternRef>> {
397        self.externref().expect("expected externref")
398    }
399
400    /// Attempt to access the underlying `anyref` value of this `Val`.
401    ///
402    /// If this is not an `anyref`, then `None` is returned.
403    ///
404    /// If this is a null `anyref`, then `Some(None)` is returned.
405    ///
406    /// If this is a non-null `anyref`, then `Some(Some(..))` is returned.
407    #[inline]
408    pub fn anyref(&self) -> Option<Option<&Rooted<AnyRef>>> {
409        match self {
410            Val::AnyRef(None) => Some(None),
411            Val::AnyRef(Some(e)) => Some(Some(e)),
412            _ => None,
413        }
414    }
415
416    /// Returns the underlying `anyref` value of this `Val`, panicking if it's the
417    /// wrong type.
418    ///
419    /// If this is a null `anyref`, then `None` is returned.
420    ///
421    /// If this is a non-null `anyref`, then `Some(..)` is returned.
422    ///
423    /// # Panics
424    ///
425    /// Panics if `self` is not a (nullable) `anyref`.
426    #[inline]
427    pub fn unwrap_anyref(&self) -> Option<&Rooted<AnyRef>> {
428        self.anyref().expect("expected anyref")
429    }
430
431    /// Attempt to access the underlying `exnref` value of this `Val`.
432    ///
433    /// If this is not an `exnref`, then `None` is returned.
434    ///
435    /// If this is a null `exnref`, then `Some(None)` is returned.
436    ///
437    /// If this is a non-null `exnref`, then `Some(Some(..))` is returned.
438    #[inline]
439    pub fn exnref(&self) -> Option<Option<&Rooted<ExnRef>>> {
440        match self {
441            Val::ExnRef(None) => Some(None),
442            Val::ExnRef(Some(e)) => Some(Some(e)),
443            _ => None,
444        }
445    }
446
447    /// Returns the underlying `exnref` value of this `Val`, panicking if it's the
448    /// wrong type.
449    ///
450    /// If this is a null `exnref`, then `None` is returned.
451    ///
452    /// If this is a non-null `exnref`, then `Some(..)` is returned.
453    ///
454    /// # Panics
455    ///
456    /// Panics if `self` is not a (nullable) `exnref`.
457    #[inline]
458    pub fn unwrap_exnref(&self) -> Option<&Rooted<ExnRef>> {
459        self.exnref().expect("expected exnref")
460    }
461
462    /// Attempt to access the underlying `funcref` value of this `Val`.
463    ///
464    /// If this is not an `funcref`, then `None` is returned.
465    ///
466    /// If this is a null `funcref`, then `Some(None)` is returned.
467    ///
468    /// If this is a non-null `funcref`, then `Some(Some(..))` is returned.
469    #[inline]
470    pub fn funcref(&self) -> Option<Option<&Func>> {
471        match self {
472            Val::FuncRef(None) => Some(None),
473            Val::FuncRef(Some(f)) => Some(Some(f)),
474            _ => None,
475        }
476    }
477
478    /// Returns the underlying `funcref` value of this `Val`, panicking if it's the
479    /// wrong type.
480    ///
481    /// If this is a null `funcref`, then `None` is returned.
482    ///
483    /// If this is a non-null `funcref`, then `Some(..)` is returned.
484    ///
485    /// # Panics
486    ///
487    /// Panics if `self` is not a (nullable) `funcref`.
488    #[inline]
489    pub fn unwrap_funcref(&self) -> Option<&Func> {
490        self.funcref().expect("expected funcref")
491    }
492
493    #[inline]
494    pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
495        match self {
496            Val::FuncRef(Some(f)) => f.comes_from_same_store(store),
497            Val::FuncRef(None) => true,
498
499            Val::ExternRef(Some(x)) => x.comes_from_same_store(store),
500            Val::ExternRef(None) => true,
501
502            Val::AnyRef(Some(a)) => a.comes_from_same_store(store),
503            Val::AnyRef(None) => true,
504
505            Val::ExnRef(Some(e)) => e.comes_from_same_store(store),
506            Val::ExnRef(None) => true,
507
508            // Integers, floats, and vectors have no association with any
509            // particular store, so they're always considered as "yes I came
510            // from that store",
511            Val::I32(_) | Val::I64(_) | Val::F32(_) | Val::F64(_) | Val::V128(_) => true,
512        }
513    }
514}
515
516impl From<i32> for Val {
517    #[inline]
518    fn from(val: i32) -> Val {
519        Val::I32(val)
520    }
521}
522
523impl From<i64> for Val {
524    #[inline]
525    fn from(val: i64) -> Val {
526        Val::I64(val)
527    }
528}
529
530impl From<f32> for Val {
531    #[inline]
532    fn from(val: f32) -> Val {
533        Val::F32(val.to_bits())
534    }
535}
536
537impl From<f64> for Val {
538    #[inline]
539    fn from(val: f64) -> Val {
540        Val::F64(val.to_bits())
541    }
542}
543
544impl From<Ref> for Val {
545    #[inline]
546    fn from(val: Ref) -> Val {
547        match val {
548            Ref::Extern(e) => Val::ExternRef(e),
549            Ref::Func(f) => Val::FuncRef(f),
550            Ref::Any(a) => Val::AnyRef(a),
551            Ref::Exn(e) => Val::ExnRef(e),
552        }
553    }
554}
555
556impl From<Rooted<ExternRef>> for Val {
557    #[inline]
558    fn from(val: Rooted<ExternRef>) -> Val {
559        Val::ExternRef(Some(val))
560    }
561}
562
563impl From<Option<Rooted<ExternRef>>> for Val {
564    #[inline]
565    fn from(val: Option<Rooted<ExternRef>>) -> Val {
566        Val::ExternRef(val)
567    }
568}
569
570impl From<Rooted<AnyRef>> for Val {
571    #[inline]
572    fn from(val: Rooted<AnyRef>) -> Val {
573        Val::AnyRef(Some(val))
574    }
575}
576
577impl From<Option<Rooted<AnyRef>>> for Val {
578    #[inline]
579    fn from(val: Option<Rooted<AnyRef>>) -> Val {
580        Val::AnyRef(val)
581    }
582}
583
584impl From<Rooted<StructRef>> for Val {
585    #[inline]
586    fn from(val: Rooted<StructRef>) -> Val {
587        Val::AnyRef(Some(val.into()))
588    }
589}
590
591impl From<Option<Rooted<StructRef>>> for Val {
592    #[inline]
593    fn from(val: Option<Rooted<StructRef>>) -> Val {
594        Val::AnyRef(val.map(Into::into))
595    }
596}
597
598impl From<Rooted<ArrayRef>> for Val {
599    #[inline]
600    fn from(val: Rooted<ArrayRef>) -> Val {
601        Val::AnyRef(Some(val.into()))
602    }
603}
604
605impl From<Option<Rooted<ArrayRef>>> for Val {
606    #[inline]
607    fn from(val: Option<Rooted<ArrayRef>>) -> Val {
608        Val::AnyRef(val.map(Into::into))
609    }
610}
611
612impl From<Rooted<ExnRef>> for Val {
613    #[inline]
614    fn from(val: Rooted<ExnRef>) -> Val {
615        Val::ExnRef(Some(val))
616    }
617}
618
619impl From<Option<Rooted<ExnRef>>> for Val {
620    #[inline]
621    fn from(val: Option<Rooted<ExnRef>>) -> Val {
622        Val::ExnRef(val)
623    }
624}
625
626impl From<Func> for Val {
627    #[inline]
628    fn from(val: Func) -> Val {
629        Val::FuncRef(Some(val))
630    }
631}
632
633impl From<Option<Func>> for Val {
634    #[inline]
635    fn from(val: Option<Func>) -> Val {
636        Val::FuncRef(val)
637    }
638}
639
640impl From<u128> for Val {
641    #[inline]
642    fn from(val: u128) -> Val {
643        Val::V128(val.into())
644    }
645}
646
647impl From<V128> for Val {
648    #[inline]
649    fn from(val: V128) -> Val {
650        Val::V128(val)
651    }
652}
653
654/// A reference.
655///
656/// References come in three broad flavors:
657///
658/// 1. Function references. These are references to a function that can be
659///    invoked.
660///
661/// 2. External references. These are references to data that is external
662///    and opaque to the Wasm guest, provided by the host.
663///
664/// 3. Internal references. These are references to allocations inside the
665///    Wasm's heap, such as structs and arrays. These are part of the GC
666///    proposal, and not yet implemented in Wasmtime.
667///
668/// At the Wasm level, there are nullable and non-nullable variants of each type
669/// of reference. Both variants are represented with `Ref` at the Wasmtime API
670/// level. For example, values of both `(ref extern)` and `(ref null extern)`
671/// types will be represented as `Ref::Extern(Option<ExternRef>)` in the
672/// Wasmtime API. Nullable references are represented as `Option<Ref>` where
673/// null references are represented as `None`. Wasm can construct null
674/// references via the `ref.null <heap-type>` instruction.
675///
676/// References are non-forgable: Wasm cannot create invalid references, for
677/// example, by claiming that the integer `0xbad1bad2` is actually a reference.
678#[derive(Debug, Clone)]
679pub enum Ref {
680    // NB: We have a variant for each of the type hierarchies defined in Wasm,
681    // and push the `Option` that provides nullability into each variant. This
682    // allows us to get the most-precise type of any reference value, whether it
683    // is null or not, without any additional metadata.
684    //
685    // Consider if we instead had the nullability inside `Val::Ref` and each of
686    // the `Ref` variants did not have an `Option`:
687    //
688    //     enum Val {
689    //         Ref(Option<Ref>),
690    //         // Etc...
691    //     }
692    //     enum Ref {
693    //         Func(Func),
694    //         External(ExternRef),
695    //         // Etc...
696    //     }
697    //
698    // In this scenario, what type would we return from `Val::ty` for
699    // `Val::Ref(None)`? Because Wasm has multiple separate type hierarchies,
700    // there is no single common bottom type for all the different kinds of
701    // references. So in this scenario, `Val::Ref(None)` doesn't have enough
702    // information to reconstruct the value's type. That's a problem for us
703    // because we need to get a value's type at various times all over the code
704    // base.
705    //
706    /// A first-class reference to a WebAssembly function.
707    ///
708    /// The host, or the Wasm guest, can invoke this function.
709    ///
710    /// The host can create function references via [`Func::new`] or
711    /// [`Func::wrap`].
712    ///
713    /// The Wasm guest can create non-null function references via the
714    /// `ref.func` instruction, or null references via the `ref.null func`
715    /// instruction.
716    Func(Option<Func>),
717
718    /// A reference to an value outside of the Wasm heap.
719    ///
720    /// These references are opaque to the Wasm itself. Wasm can't create
721    /// non-null external references, nor do anything with them accept pass them
722    /// around as function arguments and returns and place them into globals and
723    /// tables.
724    ///
725    /// Wasm can create null external references via the `ref.null extern`
726    /// instruction.
727    Extern(Option<Rooted<ExternRef>>),
728
729    /// An internal reference.
730    ///
731    /// The `AnyRef` type represents WebAssembly `anyref` values. These can be
732    /// references to `struct`s and `array`s or inline/unboxed 31-bit
733    /// integers.
734    ///
735    /// Unlike `externref`, Wasm guests can directly allocate `anyref`s, and
736    /// does not need to rely on the host to do that.
737    Any(Option<Rooted<AnyRef>>),
738
739    /// An exception-object reference.
740    ///
741    /// The `ExnRef` type represents WebAssembly `exnref`
742    /// values. These are references to exception objects as caught by
743    /// `catch_ref` clauses on `try_table` instructions, or as
744    /// allocated via the host API.
745    Exn(Option<Rooted<ExnRef>>),
746}
747
748impl From<Func> for Ref {
749    #[inline]
750    fn from(f: Func) -> Ref {
751        Ref::Func(Some(f))
752    }
753}
754
755impl From<Option<Func>> for Ref {
756    #[inline]
757    fn from(f: Option<Func>) -> Ref {
758        Ref::Func(f)
759    }
760}
761
762impl From<Rooted<ExternRef>> for Ref {
763    #[inline]
764    fn from(e: Rooted<ExternRef>) -> Ref {
765        Ref::Extern(Some(e))
766    }
767}
768
769impl From<Option<Rooted<ExternRef>>> for Ref {
770    #[inline]
771    fn from(e: Option<Rooted<ExternRef>>) -> Ref {
772        Ref::Extern(e)
773    }
774}
775
776impl From<Rooted<AnyRef>> for Ref {
777    #[inline]
778    fn from(e: Rooted<AnyRef>) -> Ref {
779        Ref::Any(Some(e))
780    }
781}
782
783impl From<Option<Rooted<AnyRef>>> for Ref {
784    #[inline]
785    fn from(e: Option<Rooted<AnyRef>>) -> Ref {
786        Ref::Any(e)
787    }
788}
789
790impl From<Rooted<StructRef>> for Ref {
791    #[inline]
792    fn from(e: Rooted<StructRef>) -> Ref {
793        Ref::Any(Some(e.into()))
794    }
795}
796
797impl From<Option<Rooted<StructRef>>> for Ref {
798    #[inline]
799    fn from(e: Option<Rooted<StructRef>>) -> Ref {
800        Ref::Any(e.map(Into::into))
801    }
802}
803
804impl From<Rooted<ArrayRef>> for Ref {
805    #[inline]
806    fn from(e: Rooted<ArrayRef>) -> Ref {
807        Ref::Any(Some(e.into()))
808    }
809}
810
811impl From<Option<Rooted<ArrayRef>>> for Ref {
812    #[inline]
813    fn from(e: Option<Rooted<ArrayRef>>) -> Ref {
814        Ref::Any(e.map(Into::into))
815    }
816}
817
818impl From<Rooted<ExnRef>> for Ref {
819    #[inline]
820    fn from(e: Rooted<ExnRef>) -> Ref {
821        Ref::Exn(Some(e))
822    }
823}
824
825impl From<Option<Rooted<ExnRef>>> for Ref {
826    #[inline]
827    fn from(e: Option<Rooted<ExnRef>>) -> Ref {
828        Ref::Exn(e)
829    }
830}
831
832impl Ref {
833    /// Create a null reference to the given heap type.
834    #[inline]
835    pub fn null(heap_type: &HeapType) -> Self {
836        match heap_type.top() {
837            HeapType::Any => Ref::Any(None),
838            HeapType::Extern => Ref::Extern(None),
839            HeapType::Func => Ref::Func(None),
840            ty => unreachable!("not a heap type: {ty:?}"),
841        }
842    }
843
844    /// Is this a null reference?
845    #[inline]
846    pub fn is_null(&self) -> bool {
847        match self {
848            Ref::Any(None) | Ref::Extern(None) | Ref::Func(None) | Ref::Exn(None) => true,
849            Ref::Any(Some(_)) | Ref::Extern(Some(_)) | Ref::Func(Some(_)) | Ref::Exn(Some(_)) => {
850                false
851            }
852        }
853    }
854
855    /// Is this a non-null reference?
856    #[inline]
857    pub fn is_non_null(&self) -> bool {
858        !self.is_null()
859    }
860
861    /// Is this an `extern` reference?
862    #[inline]
863    pub fn is_extern(&self) -> bool {
864        matches!(self, Ref::Extern(_))
865    }
866
867    /// Get the underlying `extern` reference, if any.
868    ///
869    /// Returns `None` if this `Ref` is not an `extern` reference, eg it is a
870    /// `func` reference.
871    ///
872    /// Returns `Some(None)` if this `Ref` is a null `extern` reference.
873    ///
874    /// Returns `Some(Some(_))` if this `Ref` is a non-null `extern` reference.
875    #[inline]
876    pub fn as_extern(&self) -> Option<Option<&Rooted<ExternRef>>> {
877        match self {
878            Ref::Extern(e) => Some(e.as_ref()),
879            _ => None,
880        }
881    }
882
883    /// Get the underlying `extern` reference, panicking if this is a different
884    /// kind of reference.
885    ///
886    /// Returns `None` if this `Ref` is a null `extern` reference.
887    ///
888    /// Returns `Some(_)` if this `Ref` is a non-null `extern` reference.
889    #[inline]
890    pub fn unwrap_extern(&self) -> Option<&Rooted<ExternRef>> {
891        self.as_extern()
892            .expect("Ref::unwrap_extern on non-extern reference")
893    }
894
895    /// Is this an `any` reference?
896    #[inline]
897    pub fn is_any(&self) -> bool {
898        matches!(self, Ref::Any(_))
899    }
900
901    /// Get the underlying `any` reference, if any.
902    ///
903    /// Returns `None` if this `Ref` is not an `any` reference, eg it is a
904    /// `func` reference.
905    ///
906    /// Returns `Some(None)` if this `Ref` is a null `any` reference.
907    ///
908    /// Returns `Some(Some(_))` if this `Ref` is a non-null `any` reference.
909    #[inline]
910    pub fn as_any(&self) -> Option<Option<&Rooted<AnyRef>>> {
911        match self {
912            Ref::Any(e) => Some(e.as_ref()),
913            _ => None,
914        }
915    }
916
917    /// Get the underlying `any` reference, panicking if this is a different
918    /// kind of reference.
919    ///
920    /// Returns `None` if this `Ref` is a null `any` reference.
921    ///
922    /// Returns `Some(_)` if this `Ref` is a non-null `any` reference.
923    #[inline]
924    pub fn unwrap_any(&self) -> Option<&Rooted<AnyRef>> {
925        self.as_any().expect("Ref::unwrap_any on non-any reference")
926    }
927
928    /// Is this an `exn` reference?
929    #[inline]
930    pub fn is_exn(&self) -> bool {
931        matches!(self, Ref::Exn(_))
932    }
933
934    /// Get the underlying `exn` reference, if any.
935    ///
936    /// Returns `None` if this `Ref` is not an `exn` reference, eg it is a
937    /// `func` reference.
938    ///
939    /// Returns `Some(None)` if this `Ref` is a null `exn` reference.
940    ///
941    /// Returns `Some(Some(_))` if this `Ref` is a non-null `exn` reference.
942    #[inline]
943    pub fn as_exn(&self) -> Option<Option<&Rooted<ExnRef>>> {
944        match self {
945            Ref::Exn(e) => Some(e.as_ref()),
946            _ => None,
947        }
948    }
949
950    /// Get the underlying `exn` reference, panicking if this is a different
951    /// kind of reference.
952    ///
953    /// Returns `None` if this `Ref` is a null `exn` reference.
954    ///
955    /// Returns `Some(_)` if this `Ref` is a non-null `exn` reference.
956    #[inline]
957    pub fn unwrap_exn(&self) -> Option<&Rooted<ExnRef>> {
958        self.as_exn().expect("Ref::unwrap_exn on non-exn reference")
959    }
960
961    /// Is this a `func` reference?
962    #[inline]
963    pub fn is_func(&self) -> bool {
964        matches!(self, Ref::Func(_))
965    }
966
967    /// Get the underlying `func` reference, if any.
968    ///
969    /// Returns `None` if this `Ref` is not an `func` reference, eg it is an
970    /// `extern` reference.
971    ///
972    /// Returns `Some(None)` if this `Ref` is a null `func` reference.
973    ///
974    /// Returns `Some(Some(_))` if this `Ref` is a non-null `func` reference.
975    #[inline]
976    pub fn as_func(&self) -> Option<Option<&Func>> {
977        match self {
978            Ref::Func(f) => Some(f.as_ref()),
979            _ => None,
980        }
981    }
982
983    /// Get the underlying `func` reference, panicking if this is a different
984    /// kind of reference.
985    ///
986    /// Returns `None` if this `Ref` is a null `func` reference.
987    ///
988    /// Returns `Some(_)` if this `Ref` is a non-null `func` reference.
989    #[inline]
990    pub fn unwrap_func(&self) -> Option<&Func> {
991        self.as_func()
992            .expect("Ref::unwrap_func on non-func reference")
993    }
994
995    /// Get the type of this reference.
996    ///
997    /// # Errors
998    ///
999    /// Return an error if this reference has been unrooted.
1000    ///
1001    /// # Panics
1002    ///
1003    /// Panics if this reference is associated with a different store.
1004    pub fn ty(&self, store: impl AsContext) -> Result<RefType> {
1005        self.load_ty(&store.as_context().0)
1006    }
1007
1008    pub(crate) fn load_ty(&self, store: &StoreOpaque) -> Result<RefType> {
1009        assert!(self.comes_from_same_store(store));
1010        Ok(RefType::new(
1011            self.is_null(),
1012            // NB: We choose the most-specific heap type we can here and let
1013            // subtyping do its thing if callers are matching against a
1014            // `HeapType::Func`.
1015            match self {
1016                Ref::Extern(None) => HeapType::NoExtern,
1017                Ref::Extern(Some(_)) => HeapType::Extern,
1018
1019                Ref::Func(None) => HeapType::NoFunc,
1020                Ref::Func(Some(f)) => HeapType::ConcreteFunc(f.load_ty(store)),
1021
1022                Ref::Any(None) => HeapType::None,
1023                Ref::Any(Some(a)) => a._ty(store)?,
1024
1025                Ref::Exn(None) => HeapType::None,
1026                Ref::Exn(Some(e)) => e._ty(store)?.into(),
1027            },
1028        ))
1029    }
1030
1031    /// Does this reference value match the given type?
1032    ///
1033    /// Returns an error if the underlying `Rooted` has been unrooted.
1034    ///
1035    /// # Panics
1036    ///
1037    /// Panics if this reference is not associated with the given store.
1038    pub fn matches_ty(&self, store: impl AsContext, ty: &RefType) -> Result<bool> {
1039        self._matches_ty(&store.as_context().0, ty)
1040    }
1041
1042    pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &RefType) -> Result<bool> {
1043        assert!(self.comes_from_same_store(store));
1044        assert!(ty.comes_from_same_engine(store.engine()));
1045        if self.is_null() && !ty.is_nullable() {
1046            return Ok(false);
1047        }
1048        Ok(match (self, ty.heap_type()) {
1049            (Ref::Extern(_), HeapType::Extern) => true,
1050            (Ref::Extern(None), HeapType::NoExtern) => true,
1051            (Ref::Extern(_), _) => false,
1052
1053            (Ref::Func(_), HeapType::Func) => true,
1054            (Ref::Func(None), HeapType::NoFunc | HeapType::ConcreteFunc(_)) => true,
1055            (Ref::Func(Some(f)), HeapType::ConcreteFunc(func_ty)) => f._matches_ty(store, func_ty),
1056            (Ref::Func(_), _) => false,
1057
1058            (Ref::Any(_), HeapType::Any) => true,
1059            (Ref::Any(Some(a)), HeapType::I31) => a._is_i31(store)?,
1060            (Ref::Any(Some(a)), HeapType::Struct) => a._is_struct(store)?,
1061            (Ref::Any(Some(a)), HeapType::ConcreteStruct(_ty)) => match a._as_struct(store)? {
1062                None => false,
1063                Some(s) => s._matches_ty(store, _ty)?,
1064            },
1065            (Ref::Any(Some(a)), HeapType::Eq) => a._is_eqref(store)?,
1066            (Ref::Any(Some(a)), HeapType::Array) => a._is_array(store)?,
1067            (Ref::Any(Some(a)), HeapType::ConcreteArray(_ty)) => match a._as_array(store)? {
1068                None => false,
1069                Some(a) => a._matches_ty(store, _ty)?,
1070            },
1071            (
1072                Ref::Any(None),
1073                HeapType::None
1074                | HeapType::I31
1075                | HeapType::ConcreteStruct(_)
1076                | HeapType::Struct
1077                | HeapType::ConcreteArray(_)
1078                | HeapType::Array
1079                | HeapType::Eq,
1080            ) => true,
1081            (Ref::Any(_), _) => false,
1082
1083            (Ref::Exn(_), HeapType::Exn) => true,
1084            (Ref::Exn(None), HeapType::NoExn | HeapType::ConcreteExn(_)) => true,
1085            (Ref::Exn(Some(e)), HeapType::ConcreteExn(_)) => {
1086                e._matches_ty(store, &ty.heap_type())?
1087            }
1088            (Ref::Exn(_), _) => false,
1089        })
1090    }
1091
1092    pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &RefType) -> Result<()> {
1093        if !self.comes_from_same_store(store) {
1094            bail!("reference used with wrong store")
1095        }
1096        if !ty.comes_from_same_engine(store.engine()) {
1097            bail!("type used with wrong engine")
1098        }
1099        if self._matches_ty(store, ty)? {
1100            Ok(())
1101        } else {
1102            let actual_ty = self.load_ty(store)?;
1103            bail!("type mismatch: expected {ty}, found {actual_ty}")
1104        }
1105    }
1106
1107    pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
1108        match self {
1109            Ref::Func(Some(f)) => f.comes_from_same_store(store),
1110            Ref::Func(None) => true,
1111            Ref::Extern(Some(x)) => x.comes_from_same_store(store),
1112            Ref::Extern(None) => true,
1113            Ref::Any(Some(a)) => a.comes_from_same_store(store),
1114            Ref::Any(None) => true,
1115            Ref::Exn(Some(e)) => e.comes_from_same_store(store),
1116            Ref::Exn(None) => true,
1117        }
1118    }
1119}
1120
1121#[cfg(test)]
1122mod tests {
1123    use crate::*;
1124
1125    #[test]
1126    fn size_of_val() {
1127        // Try to keep tabs on the size of `Val` and make sure we don't grow its
1128        // size.
1129        let expected = if cfg!(target_arch = "x86_64")
1130            || cfg!(target_arch = "aarch64")
1131            || cfg!(target_arch = "s390x")
1132            || cfg!(target_arch = "riscv64")
1133            || cfg!(target_arch = "arm")
1134        {
1135            24
1136        } else if cfg!(target_arch = "x86") {
1137            20
1138        } else {
1139            panic!("unsupported architecture")
1140        };
1141        assert_eq!(std::mem::size_of::<Val>(), expected);
1142    }
1143
1144    #[test]
1145    fn size_of_ref() {
1146        // Try to keep tabs on the size of `Ref` and make sure we don't grow its
1147        // size.
1148        let expected = if cfg!(target_arch = "x86_64")
1149            || cfg!(target_arch = "aarch64")
1150            || cfg!(target_arch = "s390x")
1151            || cfg!(target_arch = "riscv64")
1152            || cfg!(target_arch = "arm")
1153        {
1154            24
1155        } else if cfg!(target_arch = "x86") {
1156            20
1157        } else {
1158            panic!("unsupported architecture")
1159        };
1160        assert_eq!(std::mem::size_of::<Ref>(), expected);
1161    }
1162
1163    #[test]
1164    #[should_panic]
1165    fn val_matches_ty_wrong_engine() {
1166        let e1 = Engine::default();
1167        let e2 = Engine::default();
1168
1169        let t1 = FuncType::new(&e1, None, None);
1170        let t2 = FuncType::new(&e2, None, None);
1171
1172        let mut s1 = Store::new(&e1, ());
1173        let f = Func::new(&mut s1, t1.clone(), |_caller, _args, _results| Ok(()));
1174
1175        // Should panic.
1176        let _ = Val::FuncRef(Some(f)).matches_ty(
1177            &s1,
1178            &ValType::Ref(RefType::new(true, HeapType::ConcreteFunc(t2))),
1179        );
1180    }
1181
1182    #[test]
1183    #[should_panic]
1184    fn ref_matches_ty_wrong_engine() {
1185        let e1 = Engine::default();
1186        let e2 = Engine::default();
1187
1188        let t1 = FuncType::new(&e1, None, None);
1189        let t2 = FuncType::new(&e2, None, None);
1190
1191        let mut s1 = Store::new(&e1, ());
1192        let f = Func::new(&mut s1, t1.clone(), |_caller, _args, _results| Ok(()));
1193
1194        // Should panic.
1195        let _ = Ref::Func(Some(f)).matches_ty(&s1, &RefType::new(true, HeapType::ConcreteFunc(t2)));
1196    }
1197}