wasmtime/runtime/
values.rs

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