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