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