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