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