Skip to main content

wasmtime/runtime/
values.rs

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