wasmtime/runtime/
values.rs

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