wasmtime/runtime/gc/enabled/
eqref.rs

1//! Working with GC `eqref`s.
2
3use crate::{
4    AnyRef, ArrayRef, ArrayType, AsContext, AsContextMut, GcRefImpl, GcRootIndex, HeapType, I31,
5    OwnedRooted, RefType, Rooted, StructRef, StructType, ValRaw, ValType, WasmTy,
6    prelude::*,
7    runtime::vm::VMGcRef,
8    store::{AutoAssertNoGc, StoreOpaque},
9};
10use core::mem::{self, MaybeUninit};
11use wasmtime_environ::VMGcKind;
12
13/// A reference to a GC-managed object that can be tested for equality.
14///
15/// The WebAssembly reference types that can be tested for equality, and
16/// therefore are `eqref`s, include `structref`s, `arrayref`s, and
17/// `i31ref`s. `funcref`s, `exnref`s, and `externref`s cannot be tested for
18/// equality by Wasm, and are not `eqref`s.
19///
20/// Use the [`Rooted::ref_eq`][Rooted::ref_eq] method to actually test two
21/// references for equality.
22///
23/// Like all WebAssembly references, these are opaque to and unforgeable by
24/// Wasm: they cannot be faked and Wasm cannot, for example, cast the integer
25/// `0x12345678` into a reference, pretend it is a valid `eqref`, and trick the
26/// host into dereferencing it and segfaulting or worse.
27///
28/// Note that you can also use `Rooted<EqRef>` and `OwnedRooted<EqRef>` as a
29/// type parameter with [`Func::typed`][crate::Func::typed]- and
30/// [`Func::wrap`][crate::Func::wrap]-style APIs.
31///
32/// # Example
33///
34/// ```
35/// use wasmtime::*;
36///
37/// # fn foo() -> Result<()> {
38/// let mut config = Config::new();
39/// config.wasm_function_references(true);
40/// config.wasm_gc(true);
41///
42/// let engine = Engine::new(&config)?;
43/// let mut store = Store::new(&engine, ());
44///
45/// // Define a module that exports a function that returns a new `eqref` each
46/// // time it is invoked.
47/// let module = Module::new(&engine, r#"
48///     (module
49///         (global $g (mut i32) (i32.const 0))
50///         (func (export "new-eqref") (result (ref eq))
51///             ;; Increment $g.
52///             global.get $g
53///             i32.const 1
54///             i32.add
55///             global.set $g
56///
57///             ;; Create an `i31ref`, which is a kind of `eqref`, from $g.
58///             global.get $g
59///             ref.i31
60///         )
61///     )
62/// "#)?;
63///
64/// // Instantiate the module.
65/// let instance = Instance::new(&mut store, &module, &[])?;
66///
67/// // Get the exported function.
68/// let new_eqref = instance.get_typed_func::<(), Rooted<EqRef>>(&mut store, "new-eqref")?;
69///
70/// {
71///     let mut scope = RootScope::new(&mut store);
72///
73///     // Call the function to get an `eqref`.
74///     let x = new_eqref.call(&mut scope, ())?;
75///
76///     // `x` is equal to itself!
77///     assert!(Rooted::ref_eq(&scope, &x, &x)?);
78///
79///     // Call the function again to get a new, different `eqref`.
80///     let y = new_eqref.call(&mut scope, ())?;
81///
82///     // `x` is not equal to `y`!
83///     assert!(!Rooted::ref_eq(&scope, &x, &y)?);
84/// }
85/// # Ok(())
86/// # }
87/// # foo().unwrap();
88/// ```
89#[derive(Debug)]
90#[repr(transparent)]
91pub struct EqRef {
92    pub(super) inner: GcRootIndex,
93}
94
95impl From<Rooted<StructRef>> for Rooted<EqRef> {
96    #[inline]
97    fn from(s: Rooted<StructRef>) -> Self {
98        s.to_eqref()
99    }
100}
101
102impl From<OwnedRooted<StructRef>> for OwnedRooted<EqRef> {
103    #[inline]
104    fn from(s: OwnedRooted<StructRef>) -> Self {
105        s.to_eqref()
106    }
107}
108
109impl From<Rooted<ArrayRef>> for Rooted<EqRef> {
110    #[inline]
111    fn from(s: Rooted<ArrayRef>) -> Self {
112        s.to_eqref()
113    }
114}
115
116impl From<OwnedRooted<ArrayRef>> for OwnedRooted<EqRef> {
117    #[inline]
118    fn from(s: OwnedRooted<ArrayRef>) -> Self {
119        s.to_eqref()
120    }
121}
122
123unsafe impl GcRefImpl for EqRef {
124    fn transmute_ref(index: &GcRootIndex) -> &Self {
125        // Safety: `EqRef` is a newtype of a `GcRootIndex`.
126        let me: &Self = unsafe { mem::transmute(index) };
127
128        // Assert we really are just a newtype of a `GcRootIndex`.
129        assert!(matches!(
130            me,
131            Self {
132                inner: GcRootIndex { .. },
133            }
134        ));
135
136        me
137    }
138}
139
140impl Rooted<EqRef> {
141    /// Upcast this `eqref` into an `anyref`.
142    #[inline]
143    pub fn to_anyref(self) -> Rooted<AnyRef> {
144        self.unchecked_cast()
145    }
146}
147
148impl OwnedRooted<EqRef> {
149    /// Upcast this `eqref` into an `anyref`.
150    #[inline]
151    pub fn to_anyref(self) -> OwnedRooted<AnyRef> {
152        self.unchecked_cast()
153    }
154}
155
156impl EqRef {
157    /// Create a new `Rooted<AnyRef>` from the given GC reference.
158    ///
159    /// `gc_ref` should point to a valid `anyref` and should belong to the
160    /// store's GC heap. Failure to uphold these invariants is memory safe but
161    /// will lead to general incorrectness such as panics or wrong results.
162    pub(crate) fn from_cloned_gc_ref(
163        store: &mut AutoAssertNoGc<'_>,
164        gc_ref: VMGcRef,
165    ) -> Rooted<Self> {
166        debug_assert!(
167            gc_ref.is_i31()
168                || store
169                    .unwrap_gc_store()
170                    .header(&gc_ref)
171                    .kind()
172                    .matches(VMGcKind::EqRef)
173        );
174        Rooted::new(store, gc_ref)
175    }
176
177    #[inline]
178    pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
179        self.inner.comes_from_same_store(store)
180    }
181
182    /// Get the type of this reference.
183    ///
184    /// # Errors
185    ///
186    /// Return an error if this reference has been unrooted.
187    ///
188    /// # Panics
189    ///
190    /// Panics if this reference is associated with a different store.
191    pub fn ty(&self, store: impl AsContext) -> Result<HeapType> {
192        self._ty(store.as_context().0)
193    }
194
195    pub(crate) fn _ty(&self, store: &StoreOpaque) -> Result<HeapType> {
196        let gc_ref = self.inner.try_gc_ref(store)?;
197        if gc_ref.is_i31() {
198            return Ok(HeapType::I31);
199        }
200
201        let header = store.require_gc_store()?.header(gc_ref);
202
203        if header.kind().matches(VMGcKind::StructRef) {
204            return Ok(HeapType::ConcreteStruct(
205                StructType::from_shared_type_index(store.engine(), header.ty().unwrap()),
206            ));
207        }
208
209        if header.kind().matches(VMGcKind::ArrayRef) {
210            return Ok(HeapType::ConcreteArray(ArrayType::from_shared_type_index(
211                store.engine(),
212                header.ty().unwrap(),
213            )));
214        }
215
216        unreachable!("no other kinds of `eqref`s")
217    }
218
219    /// Does this `eqref` match the given type?
220    ///
221    /// That is, is this object's type a subtype of the given type?
222    ///
223    /// # Errors
224    ///
225    /// Return an error if this reference has been unrooted.
226    ///
227    /// # Panics
228    ///
229    /// Panics if this reference is associated with a different store.
230    pub fn matches_ty(&self, store: impl AsContext, ty: &HeapType) -> Result<bool> {
231        self._matches_ty(store.as_context().0, ty)
232    }
233
234    pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &HeapType) -> Result<bool> {
235        assert!(self.comes_from_same_store(store));
236        Ok(self._ty(store)?.matches(ty))
237    }
238
239    pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &HeapType) -> Result<()> {
240        if !self.comes_from_same_store(store) {
241            bail!("function used with wrong store");
242        }
243        if self._matches_ty(store, ty)? {
244            Ok(())
245        } else {
246            let actual_ty = self._ty(store)?;
247            bail!("type mismatch: expected `(ref {ty})`, found `(ref {actual_ty})`")
248        }
249    }
250
251    /// Construct an `eqref` from an `i31`.
252    ///
253    /// # Example
254    ///
255    /// ```
256    /// # use wasmtime::*;
257    /// # fn _foo() -> Result<()> {
258    /// let mut store = Store::<()>::default();
259    ///
260    /// // Create an `i31`.
261    /// let i31 = I31::wrapping_u32(999);
262    ///
263    /// // Convert it into an `eqref`.
264    /// let eqref = EqRef::from_i31(&mut store, i31);
265    /// # Ok(())
266    /// # }
267    /// ```
268    pub fn from_i31(mut store: impl AsContextMut, value: I31) -> Rooted<Self> {
269        let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
270        Self::_from_i31(&mut store, value)
271    }
272
273    pub(crate) fn _from_i31(store: &mut AutoAssertNoGc<'_>, value: I31) -> Rooted<Self> {
274        let gc_ref = VMGcRef::from_i31(value.runtime_i31());
275        Rooted::new(store, gc_ref)
276    }
277
278    /// Is this `eqref` an `i31`?
279    ///
280    /// # Errors
281    ///
282    /// Return an error if this reference has been unrooted.
283    ///
284    /// # Panics
285    ///
286    /// Panics if this reference is associated with a different store.
287    pub fn is_i31(&self, store: impl AsContext) -> Result<bool> {
288        self._is_i31(store.as_context().0)
289    }
290
291    pub(crate) fn _is_i31(&self, store: &StoreOpaque) -> Result<bool> {
292        assert!(self.comes_from_same_store(store));
293        let gc_ref = self.inner.try_gc_ref(store)?;
294        Ok(gc_ref.is_i31())
295    }
296
297    /// Downcast this `eqref` to an `i31`.
298    ///
299    /// If this `eqref` is an `i31`, then `Some(_)` is returned.
300    ///
301    /// If this `eqref` is not an `i31`, then `None` is returned.
302    ///
303    /// # Errors
304    ///
305    /// Return an error if this reference has been unrooted.
306    ///
307    /// # Panics
308    ///
309    /// Panics if this reference is associated with a different store.
310    pub fn as_i31(&self, store: impl AsContext) -> Result<Option<I31>> {
311        self._as_i31(store.as_context().0)
312    }
313
314    pub(crate) fn _as_i31(&self, store: &StoreOpaque) -> Result<Option<I31>> {
315        assert!(self.comes_from_same_store(store));
316        let gc_ref = self.inner.try_gc_ref(store)?;
317        Ok(gc_ref.as_i31().map(Into::into))
318    }
319
320    /// Downcast this `eqref` to an `i31`, panicking if this `eqref` is not an
321    /// `i31`.
322    ///
323    /// # Errors
324    ///
325    /// Return an error if this reference has been unrooted.
326    ///
327    /// # Panics
328    ///
329    /// Panics if this reference is associated with a different store, or if
330    /// this `eqref` is not an `i31`.
331    pub fn unwrap_i31(&self, store: impl AsContext) -> Result<I31> {
332        Ok(self.as_i31(store)?.expect("EqRef::unwrap_i31 on non-i31"))
333    }
334
335    /// Is this `eqref` a `structref`?
336    ///
337    /// # Errors
338    ///
339    /// Return an error if this reference has been unrooted.
340    ///
341    /// # Panics
342    ///
343    /// Panics if this reference is associated with a different store.
344    pub fn is_struct(&self, store: impl AsContext) -> Result<bool> {
345        self._is_struct(store.as_context().0)
346    }
347
348    pub(crate) fn _is_struct(&self, store: &StoreOpaque) -> Result<bool> {
349        let gc_ref = self.inner.try_gc_ref(store)?;
350        Ok(!gc_ref.is_i31()
351            && store
352                .require_gc_store()?
353                .kind(gc_ref)
354                .matches(VMGcKind::StructRef))
355    }
356
357    /// Downcast this `eqref` to a `structref`.
358    ///
359    /// If this `eqref` is a `structref`, then `Some(_)` is returned.
360    ///
361    /// If this `eqref` is not a `structref`, then `None` is returned.
362    ///
363    /// # Errors
364    ///
365    /// Return an error if this reference has been unrooted.
366    ///
367    /// # Panics
368    ///
369    /// Panics if this reference is associated with a different store.
370    pub fn as_struct(&self, store: impl AsContext) -> Result<Option<Rooted<StructRef>>> {
371        self._as_struct(store.as_context().0)
372    }
373
374    pub(crate) fn _as_struct(&self, store: &StoreOpaque) -> Result<Option<Rooted<StructRef>>> {
375        if self._is_struct(store)? {
376            Ok(Some(Rooted::from_gc_root_index(self.inner)))
377        } else {
378            Ok(None)
379        }
380    }
381
382    /// Downcast this `eqref` to a `structref`, panicking if this `eqref` is
383    /// not a `structref`.
384    ///
385    /// # Errors
386    ///
387    /// Return an error if this reference has been unrooted.
388    ///
389    /// # Panics
390    ///
391    /// Panics if this reference is associated with a different store, or if
392    /// this `eqref` is not a `struct`.
393    pub fn unwrap_struct(&self, store: impl AsContext) -> Result<Rooted<StructRef>> {
394        self._unwrap_struct(store.as_context().0)
395    }
396
397    pub(crate) fn _unwrap_struct(&self, store: &StoreOpaque) -> Result<Rooted<StructRef>> {
398        Ok(self
399            ._as_struct(store)?
400            .expect("EqRef::unwrap_struct on non-structref"))
401    }
402
403    /// Is this `eqref` an `arrayref`?
404    ///
405    /// # Errors
406    ///
407    /// Return an error if this reference has been unrooted.
408    ///
409    /// # Panics
410    ///
411    /// Panics if this reference is associated with a different store.
412    pub fn is_array(&self, store: impl AsContext) -> Result<bool> {
413        self._is_array(store.as_context().0)
414    }
415
416    pub(crate) fn _is_array(&self, store: &StoreOpaque) -> Result<bool> {
417        let gc_ref = self.inner.try_gc_ref(store)?;
418        Ok(!gc_ref.is_i31()
419            && store
420                .require_gc_store()?
421                .kind(gc_ref)
422                .matches(VMGcKind::ArrayRef))
423    }
424
425    /// Downcast this `eqref` to an `arrayref`.
426    ///
427    /// If this `eqref` is an `arrayref`, then `Some(_)` is returned.
428    ///
429    /// If this `eqref` is not an `arrayref`, then `None` is returned.
430    ///
431    /// # Errors
432    ///
433    /// Return an error if this reference has been unrooted.
434    ///
435    /// # Panics
436    ///
437    /// Panics if this reference is associated with a different store.
438    pub fn as_array(&self, store: impl AsContext) -> Result<Option<Rooted<ArrayRef>>> {
439        self._as_array(store.as_context().0)
440    }
441
442    pub(crate) fn _as_array(&self, store: &StoreOpaque) -> Result<Option<Rooted<ArrayRef>>> {
443        if self._is_array(store)? {
444            Ok(Some(Rooted::from_gc_root_index(self.inner)))
445        } else {
446            Ok(None)
447        }
448    }
449
450    /// Downcast this `eqref` to an `arrayref`, panicking if this `eqref` is
451    /// not an `arrayref`.
452    ///
453    /// # Errors
454    ///
455    /// Return an error if this reference has been unrooted.
456    ///
457    /// # Panics
458    ///
459    /// Panics if this reference is associated with a different store, or if
460    /// this `eqref` is not an `array`.
461    pub fn unwrap_array(&self, store: impl AsContext) -> Result<Rooted<ArrayRef>> {
462        self._unwrap_array(store.as_context().0)
463    }
464
465    pub(crate) fn _unwrap_array(&self, store: &StoreOpaque) -> Result<Rooted<ArrayRef>> {
466        Ok(self
467            ._as_array(store)?
468            .expect("EqRef::unwrap_array on non-arrayref"))
469    }
470}
471
472unsafe impl WasmTy for Rooted<EqRef> {
473    #[inline]
474    fn valtype() -> ValType {
475        ValType::Ref(RefType::new(false, HeapType::Eq))
476    }
477
478    #[inline]
479    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
480        self.comes_from_same_store(store)
481    }
482
483    #[inline]
484    fn dynamic_concrete_type_check(
485        &self,
486        store: &StoreOpaque,
487        _nullable: bool,
488        ty: &HeapType,
489    ) -> Result<()> {
490        self.ensure_matches_ty(store, ty)
491    }
492
493    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
494        self.wasm_ty_store(store, ptr, ValRaw::anyref)
495    }
496
497    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
498        Self::wasm_ty_load(store, ptr.get_anyref(), EqRef::from_cloned_gc_ref)
499    }
500}
501
502unsafe impl WasmTy for Option<Rooted<EqRef>> {
503    #[inline]
504    fn valtype() -> ValType {
505        ValType::EQREF
506    }
507
508    #[inline]
509    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
510        self.map_or(true, |x| x.comes_from_same_store(store))
511    }
512
513    #[inline]
514    fn dynamic_concrete_type_check(
515        &self,
516        store: &StoreOpaque,
517        nullable: bool,
518        ty: &HeapType,
519    ) -> Result<()> {
520        match self {
521            Some(s) => Rooted::<EqRef>::dynamic_concrete_type_check(s, store, nullable, ty),
522            None => {
523                ensure!(
524                    nullable,
525                    "expected a non-null reference, but found a null reference"
526                );
527                Ok(())
528            }
529        }
530    }
531
532    #[inline]
533    fn is_vmgcref_and_points_to_object(&self) -> bool {
534        self.is_some()
535    }
536
537    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
538        <Rooted<EqRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
539    }
540
541    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
542        <Rooted<EqRef>>::wasm_ty_option_load(store, ptr.get_anyref(), EqRef::from_cloned_gc_ref)
543    }
544}
545
546unsafe impl WasmTy for OwnedRooted<EqRef> {
547    #[inline]
548    fn valtype() -> ValType {
549        ValType::Ref(RefType::new(false, HeapType::Eq))
550    }
551
552    #[inline]
553    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
554        self.comes_from_same_store(store)
555    }
556
557    #[inline]
558    fn dynamic_concrete_type_check(
559        &self,
560        store: &StoreOpaque,
561        _: bool,
562        ty: &HeapType,
563    ) -> Result<()> {
564        self.ensure_matches_ty(store, ty)
565    }
566
567    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
568        self.wasm_ty_store(store, ptr, ValRaw::anyref)
569    }
570
571    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
572        Self::wasm_ty_load(store, ptr.get_anyref(), EqRef::from_cloned_gc_ref)
573    }
574}
575
576unsafe impl WasmTy for Option<OwnedRooted<EqRef>> {
577    #[inline]
578    fn valtype() -> ValType {
579        ValType::EQREF
580    }
581
582    #[inline]
583    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
584        self.as_ref()
585            .map_or(true, |x| x.comes_from_same_store(store))
586    }
587
588    #[inline]
589    fn dynamic_concrete_type_check(
590        &self,
591        store: &StoreOpaque,
592        nullable: bool,
593        ty: &HeapType,
594    ) -> Result<()> {
595        match self {
596            Some(s) => OwnedRooted::<EqRef>::dynamic_concrete_type_check(s, store, nullable, ty),
597            None => {
598                ensure!(
599                    nullable,
600                    "expected a non-null reference, but found a null reference"
601                );
602                Ok(())
603            }
604        }
605    }
606
607    #[inline]
608    fn is_vmgcref_and_points_to_object(&self) -> bool {
609        self.is_some()
610    }
611
612    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
613        <OwnedRooted<EqRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
614    }
615
616    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
617        <OwnedRooted<EqRef>>::wasm_ty_option_load(
618            store,
619            ptr.get_anyref(),
620            EqRef::from_cloned_gc_ref,
621        )
622    }
623}