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