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