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