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