dusk_wasmtime/runtime/
values.rs

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