wasmtime/runtime/
values.rs

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