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