Skip to main content

wasmtime/runtime/gc/enabled/
anyref.rs

1//! Implementation of `anyref` in Wasmtime.
2
3use super::ExternRef;
4use crate::prelude::*;
5use crate::runtime::vm::VMGcRef;
6use crate::{
7    ArrayRef, ArrayType, AsContext, AsContextMut, EqRef, GcRefImpl, GcRootIndex, HeapType, I31,
8    OwnedRooted, RefType, Result, Rooted, StructRef, StructType, ValRaw, ValType, WasmTy, bail_bug,
9    store::{AutoAssertNoGc, StoreOpaque},
10};
11use core::mem;
12use core::mem::MaybeUninit;
13use wasmtime_environ::VMGcKind;
14
15/// An `anyref` GC reference.
16///
17/// The `AnyRef` type represents WebAssembly `anyref` values. These can be
18/// references to `struct`s and `array`s or inline/unboxed 31-bit
19/// integers. Unlike `externref`, Wasm guests can directly allocate `anyref`s.
20///
21/// Like all WebAssembly references, these are opaque and unforgable to Wasm:
22/// they cannot be faked and Wasm cannot, for example, cast the integer
23/// `0x12345678` into a reference, pretend it is a valid `anyref`, and trick the
24/// host into dereferencing it and segfaulting or worse.
25///
26/// Note that you can also use `Rooted<AnyRef>` and `OwnedRooted<AnyRef>` as
27/// a type parameter with [`Func::typed`][crate::Func::typed]- and
28/// [`Func::wrap`][crate::Func::wrap]-style APIs.
29///
30/// # Example
31///
32/// ```
33/// # use wasmtime::*;
34/// # fn _foo() -> Result<()> {
35/// let mut config = Config::new();
36/// config.wasm_gc(true);
37///
38/// let engine = Engine::new(&config)?;
39///
40/// // Define a module which does stuff with `anyref`s.
41/// let module = Module::new(&engine, r#"
42///     (module
43///         (func (export "increment-if-i31") (param (ref null any)) (result (ref null any))
44///             block
45///                 ;; Try to cast the arg to an `i31`, otherwise branch out
46///                 ;; of this `block`.
47///                 local.get 0
48///                 br_on_cast_fail (ref null any) (ref i31) 0
49///                 ;; Get the `i31`'s inner value and add one to it.
50///                 i31.get_u
51///                 i32.const 1
52///                 i32.add
53///                 ;; Wrap the incremented value back into an `i31` reference and
54///                 ;; return it.
55///                 ref.i31
56///                 return
57///             end
58///
59///             ;; If the `anyref` we were given is not an `i31`, just return it
60///             ;; as-is.
61///             local.get 0
62///         )
63///     )
64/// "#)?;
65///
66/// // Instantiate the module.
67/// let mut store = Store::new(&engine, ());
68/// let instance = Instance::new(&mut store, &module, &[])?;
69///
70/// // Extract the function.
71/// let increment_if_i31 = instance
72///     .get_typed_func::<Option<Rooted<AnyRef>>, Option<Rooted<AnyRef>>>(
73///         &mut store,
74///         "increment-if-i31",
75///     )?;
76///
77/// {
78///     // Create a new scope for the `Rooted` arguments and returns.
79///     let mut scope = RootScope::new(&mut store);
80///
81///     // Call the function with an `i31`.
82///     let arg = AnyRef::from_i31(&mut scope, I31::wrapping_u32(419));
83///     let result = increment_if_i31.call(&mut scope, Some(arg))?;
84///     assert_eq!(result.unwrap().as_i31(&scope)?, Some(I31::wrapping_u32(420)));
85///
86///     // Call the function with something that isn't an `i31`.
87///     let result = increment_if_i31.call(&mut scope, None)?;
88///     assert!(result.is_none());
89/// }
90/// # Ok(())
91/// # }
92/// ```
93#[derive(Debug)]
94#[repr(transparent)]
95pub struct AnyRef {
96    pub(super) inner: GcRootIndex,
97}
98
99impl From<Rooted<EqRef>> for Rooted<AnyRef> {
100    #[inline]
101    fn from(e: Rooted<EqRef>) -> Self {
102        e.to_anyref()
103    }
104}
105
106impl From<OwnedRooted<EqRef>> for OwnedRooted<AnyRef> {
107    #[inline]
108    fn from(e: OwnedRooted<EqRef>) -> Self {
109        e.to_anyref()
110    }
111}
112
113impl From<Rooted<StructRef>> for Rooted<AnyRef> {
114    #[inline]
115    fn from(s: Rooted<StructRef>) -> Self {
116        s.to_anyref()
117    }
118}
119
120impl From<OwnedRooted<StructRef>> for OwnedRooted<AnyRef> {
121    #[inline]
122    fn from(s: OwnedRooted<StructRef>) -> Self {
123        s.to_anyref()
124    }
125}
126
127impl From<Rooted<ArrayRef>> for Rooted<AnyRef> {
128    #[inline]
129    fn from(s: Rooted<ArrayRef>) -> Self {
130        s.to_anyref()
131    }
132}
133
134impl From<OwnedRooted<ArrayRef>> for OwnedRooted<AnyRef> {
135    #[inline]
136    fn from(s: OwnedRooted<ArrayRef>) -> Self {
137        s.to_anyref()
138    }
139}
140
141unsafe impl GcRefImpl for AnyRef {
142    fn transmute_ref(index: &GcRootIndex) -> &Self {
143        // Safety: `AnyRef` is a newtype of a `GcRootIndex`.
144        let me: &Self = unsafe { mem::transmute(index) };
145
146        // Assert we really are just a newtype of a `GcRootIndex`.
147        assert!(matches!(
148            me,
149            Self {
150                inner: GcRootIndex { .. },
151            }
152        ));
153
154        me
155    }
156}
157
158impl AnyRef {
159    /// Construct an `anyref` from an `i31`.
160    ///
161    /// # Example
162    ///
163    /// ```
164    /// # use wasmtime::*;
165    /// # fn _foo() -> Result<()> {
166    /// let mut store = Store::<()>::default();
167    ///
168    /// // Create an `i31`.
169    /// let i31 = I31::wrapping_u32(999);
170    ///
171    /// // Convert it into an `anyref`.
172    /// let anyref = AnyRef::from_i31(&mut store, i31);
173    /// # Ok(())
174    /// # }
175    /// ```
176    pub fn from_i31(mut store: impl AsContextMut, value: I31) -> Rooted<Self> {
177        let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
178        Self::_from_i31(&mut store, value)
179    }
180
181    pub(crate) fn _from_i31(store: &mut AutoAssertNoGc<'_>, value: I31) -> Rooted<Self> {
182        let gc_ref = VMGcRef::from_i31(value.runtime_i31());
183        Rooted::new(store, gc_ref)
184    }
185
186    /// Convert an `externref` into an `anyref`.
187    ///
188    /// This is equivalent to the `any.convert_extern` instruction in Wasm.
189    ///
190    /// You can recover the underlying `externref` again via the
191    /// [`ExternRef::convert_any`] method or the `extern.convert_any` Wasm
192    /// instruction.
193    ///
194    /// Returns an error if the `externref` GC reference has been unrooted (eg
195    /// if you attempt to use a `Rooted<ExternRef>` after exiting the scope it
196    /// was rooted within). See the documentation for
197    /// [`Rooted<T>`][crate::Rooted] for more details.
198    ///
199    /// # Example
200    ///
201    /// ```
202    /// use wasmtime::*;
203    /// # fn foo() -> Result<()> {
204    /// let engine = Engine::default();
205    /// let mut store = Store::new(&engine, ());
206    ///
207    /// // Create an `externref`.
208    /// let externref = ExternRef::new(&mut store, "hello")?;
209    ///
210    /// // Convert that `externref` into an `anyref`.
211    /// let anyref = AnyRef::convert_extern(&mut store, externref)?;
212    ///
213    /// // The converted value is an `anyref` but is not an `eqref`.
214    /// assert_eq!(anyref.matches_ty(&store, &HeapType::Any)?, true);
215    /// assert_eq!(anyref.matches_ty(&store, &HeapType::Eq)?, false);
216    ///
217    /// // We can convert it back to the original `externref` and get its
218    /// // associated host data again.
219    /// let externref = ExternRef::convert_any(&mut store, anyref)?;
220    /// let data = externref
221    ///     .data(&store)?
222    ///     .expect("externref should have host data")
223    ///     .downcast_ref::<&str>()
224    ///     .expect("host data should be a str");
225    /// assert_eq!(*data, "hello");
226    /// # Ok(()) }
227    /// # foo().unwrap();
228    pub fn convert_extern(
229        mut store: impl AsContextMut,
230        externref: Rooted<ExternRef>,
231    ) -> Result<Rooted<Self>> {
232        let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
233        Self::_convert_extern(&mut store, externref)
234    }
235
236    pub(crate) fn _convert_extern(
237        store: &mut AutoAssertNoGc<'_>,
238        externref: Rooted<ExternRef>,
239    ) -> Result<Rooted<Self>> {
240        let gc_ref = externref.try_clone_gc_ref(store)?;
241        Ok(Self::from_cloned_gc_ref(store, gc_ref))
242    }
243
244    /// Creates a new strongly-owned [`AnyRef`] from the raw value provided.
245    ///
246    /// This is intended to be used in conjunction with [`Func::new_unchecked`],
247    /// [`Func::call_unchecked`], and [`ValRaw`] with its `anyref` field.
248    ///
249    /// This function assumes that `raw` is an `anyref` value which is currently
250    /// rooted within the [`Store`].
251    ///
252    /// # Correctness
253    ///
254    /// This function is tricky to get right because `raw` not only must be a
255    /// valid `anyref` value produced prior by [`AnyRef::to_raw`] but it
256    /// must also be correctly rooted within the store. When arguments are
257    /// provided to a callback with [`Func::new_unchecked`], for example, or
258    /// returned via [`Func::call_unchecked`], if a GC is performed within the
259    /// store then floating `anyref` values are not rooted and will be GC'd,
260    /// meaning that this function will no longer be correct to call with the
261    /// values cleaned up. This function must be invoked *before* possible GC
262    /// operations can happen (such as calling Wasm).
263    ///
264    ///
265    /// When in doubt try to not use this. Instead use the Rust APIs of
266    /// [`TypedFunc`] and friends. Note though that this function is not
267    /// `unsafe` as any value can be passed in. Incorrect values can result in
268    /// runtime panics, however, so care must still be taken with this method.
269    ///
270    /// [`Func::call_unchecked`]: crate::Func::call_unchecked
271    /// [`Func::new_unchecked`]: crate::Func::new_unchecked
272    /// [`Store`]: crate::Store
273    /// [`TypedFunc`]: crate::TypedFunc
274    /// [`ValRaw`]: crate::ValRaw
275    pub fn from_raw(mut store: impl AsContextMut, raw: u32) -> Option<Rooted<Self>> {
276        let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
277        Self::_from_raw(&mut store, raw)
278    }
279
280    // (Not actually memory unsafe since we have indexed GC heaps.)
281    pub(crate) fn _from_raw(store: &mut AutoAssertNoGc, raw: u32) -> Option<Rooted<Self>> {
282        let gc_ref = VMGcRef::from_raw_u32(raw)?;
283        let gc_ref = store.clone_gc_ref(&gc_ref);
284        Some(Self::from_cloned_gc_ref(store, gc_ref))
285    }
286
287    /// Create a new `Rooted<AnyRef>` from the given GC reference.
288    ///
289    /// `gc_ref` should point to a valid `anyref` and should belong to the
290    /// store's GC heap. Failure to uphold these invariants is memory safe but
291    /// will lead to general incorrectness such as panics or wrong results.
292    pub(crate) fn from_cloned_gc_ref(
293        store: &mut AutoAssertNoGc<'_>,
294        gc_ref: VMGcRef,
295    ) -> Rooted<Self> {
296        debug_assert!(
297            gc_ref.is_i31()
298                || store
299                    .unwrap_gc_store()
300                    .kind(&gc_ref)
301                    .unwrap()
302                    .matches(VMGcKind::AnyRef)
303                || store
304                    .unwrap_gc_store()
305                    .kind(&gc_ref)
306                    .unwrap()
307                    .matches(VMGcKind::ExternRef)
308        );
309        Rooted::new(store, gc_ref)
310    }
311
312    #[inline]
313    pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
314        self.inner.comes_from_same_store(store)
315    }
316
317    /// Converts this [`AnyRef`] to a raw value suitable to store within a
318    /// [`ValRaw`].
319    ///
320    /// Returns an error if this `anyref` has been unrooted.
321    ///
322    /// # Correctness
323    ///
324    /// Produces a raw value which is only valid to pass into a store if a GC
325    /// doesn't happen between when the value is produce and when it's passed
326    /// into the store.
327    ///
328    /// [`ValRaw`]: crate::ValRaw
329    pub fn to_raw(&self, mut store: impl AsContextMut) -> Result<u32> {
330        let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
331        self._to_raw(&mut store)
332    }
333
334    pub(crate) fn _to_raw(&self, store: &mut AutoAssertNoGc<'_>) -> Result<u32> {
335        self.inner.expose_gc_ref_to_wasm(store).map(|r| r.get())
336    }
337
338    /// Get the type of this reference.
339    ///
340    /// # Errors
341    ///
342    /// Return an error if this reference has been unrooted.
343    ///
344    /// # Panics
345    ///
346    /// Panics if this reference is associated with a different store.
347    pub fn ty(&self, store: impl AsContext) -> Result<HeapType> {
348        self._ty(store.as_context().0)
349    }
350
351    pub(crate) fn _ty(&self, store: &StoreOpaque) -> Result<HeapType> {
352        let gc_ref = self.inner.try_gc_ref(store)?;
353        if gc_ref.is_i31() {
354            return Ok(HeapType::I31);
355        }
356
357        let header = store.require_gc_store()?.header(gc_ref)?;
358        let kind = match header.kind() {
359            Some(kind) => kind,
360            None => bail_bug!("invalid `VMGcKind`"),
361        };
362
363        if kind.matches(VMGcKind::ExternRef) {
364            return Ok(HeapType::Any);
365        }
366
367        debug_assert!(kind.matches(VMGcKind::AnyRef));
368        debug_assert!(kind.matches(VMGcKind::EqRef));
369        let ty = match header.ty() {
370            Some(ty) => ty,
371            None => bail_bug!("ty should be present"),
372        };
373
374        if kind.matches(VMGcKind::StructRef) {
375            return match StructType::from_shared_type_index(store.engine(), ty) {
376                Some(ty) => Ok(HeapType::ConcreteStruct(ty)),
377                None => bail_bug!("invalid struct type index"),
378            };
379        }
380
381        if kind.matches(VMGcKind::ArrayRef) {
382            return match ArrayType::from_shared_type_index(store.engine(), ty) {
383                Some(ty) => Ok(HeapType::ConcreteArray(ty)),
384                None => bail_bug!("invalid array type index"),
385            };
386        }
387
388        bail_bug!("no other kinds of `anyref`s")
389    }
390
391    /// Does this `anyref` match the given type?
392    ///
393    /// That is, is this object's type a subtype of the given type?
394    ///
395    /// # Errors
396    ///
397    /// Return an error if this reference has been unrooted.
398    ///
399    /// # Panics
400    ///
401    /// Panics if this reference is associated with a different store.
402    pub fn matches_ty(&self, store: impl AsContext, ty: &HeapType) -> Result<bool> {
403        self._matches_ty(store.as_context().0, ty)
404    }
405
406    pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &HeapType) -> Result<bool> {
407        assert!(self.comes_from_same_store(store));
408        Ok(self._ty(store)?.matches(ty))
409    }
410
411    pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &HeapType) -> Result<()> {
412        if !self.comes_from_same_store(store) {
413            bail!("function used with wrong store");
414        }
415        if self._matches_ty(store, ty)? {
416            Ok(())
417        } else {
418            let actual_ty = self._ty(store)?;
419            bail!("type mismatch: expected `(ref {ty})`, found `(ref {actual_ty})`")
420        }
421    }
422
423    /// Is this `anyref` an `eqref`?
424    ///
425    /// # Errors
426    ///
427    /// Return an error if this reference has been unrooted.
428    ///
429    /// # Panics
430    ///
431    /// Panics if this reference is associated with a different store.
432    pub fn is_eqref(&self, store: impl AsContext) -> Result<bool> {
433        self._is_eqref(store.as_context().0)
434    }
435
436    pub(crate) fn _is_eqref(&self, store: &StoreOpaque) -> Result<bool> {
437        assert!(self.comes_from_same_store(store));
438        let gc_ref = self.inner.try_gc_ref(store)?;
439        Ok(gc_ref.is_i31()
440            || store
441                .require_gc_store()?
442                .kind(gc_ref)?
443                .matches(VMGcKind::EqRef))
444    }
445
446    /// Downcast this `anyref` to an `eqref`.
447    ///
448    /// If this `anyref` is an `eqref`, then `Some(_)` is returned.
449    ///
450    /// If this `anyref` is not an `eqref`, then `None` is returned.
451    ///
452    /// # Errors
453    ///
454    /// Return an error if this reference has been unrooted.
455    ///
456    /// # Panics
457    ///
458    /// Panics if this reference is associated with a different store.
459    pub fn as_eqref(&self, mut store: impl AsContextMut) -> Result<Option<Rooted<EqRef>>> {
460        self._as_eqref(store.as_context_mut().0)
461    }
462
463    pub(crate) fn _as_eqref(&self, store: &mut StoreOpaque) -> Result<Option<Rooted<EqRef>>> {
464        if self._is_eqref(store)? {
465            let gc_ref = self.inner.try_gc_ref(store)?.unchecked_copy();
466            let gc_ref = store.clone_gc_ref(&gc_ref);
467            let mut store = AutoAssertNoGc::new(store);
468            Ok(Some(Rooted::new(&mut store, gc_ref)))
469        } else {
470            Ok(None)
471        }
472    }
473
474    /// Downcast this `anyref` to an `eqref`, panicking if this `anyref` is not
475    /// an `eqref`.
476    ///
477    /// # Errors
478    ///
479    /// Return an error if this reference has been unrooted.
480    ///
481    /// # Panics
482    ///
483    /// Panics if this reference is associated with a different store, or if
484    /// this `anyref` is not an `eqref`.
485    pub fn unwrap_eqref(&self, mut store: impl AsContextMut) -> Result<Rooted<EqRef>> {
486        self._unwrap_eqref(store.as_context_mut().0)
487    }
488
489    pub(crate) fn _unwrap_eqref(&self, store: &mut StoreOpaque) -> Result<Rooted<EqRef>> {
490        Ok(self
491            ._as_eqref(store)?
492            .expect("AnyRef::unwrap_eqref on non-eqref"))
493    }
494
495    /// Is this `anyref` an `i31`?
496    ///
497    /// # Errors
498    ///
499    /// Return an error if this reference has been unrooted.
500    ///
501    /// # Panics
502    ///
503    /// Panics if this reference is associated with a different store.
504    pub fn is_i31(&self, store: impl AsContext) -> Result<bool> {
505        self._is_i31(store.as_context().0)
506    }
507
508    pub(crate) fn _is_i31(&self, store: &StoreOpaque) -> Result<bool> {
509        assert!(self.comes_from_same_store(store));
510        let gc_ref = self.inner.try_gc_ref(store)?;
511        Ok(gc_ref.is_i31())
512    }
513
514    /// Downcast this `anyref` to an `i31`.
515    ///
516    /// If this `anyref` is an `i31`, then `Some(_)` is returned.
517    ///
518    /// If this `anyref` is not an `i31`, then `None` is returned.
519    ///
520    /// # Errors
521    ///
522    /// Return an error if this reference has been unrooted.
523    ///
524    /// # Panics
525    ///
526    /// Panics if this reference is associated with a different store.
527    pub fn as_i31(&self, store: impl AsContext) -> Result<Option<I31>> {
528        self._as_i31(store.as_context().0)
529    }
530
531    pub(crate) fn _as_i31(&self, store: &StoreOpaque) -> Result<Option<I31>> {
532        assert!(self.comes_from_same_store(store));
533        let gc_ref = self.inner.try_gc_ref(store)?;
534        Ok(gc_ref.as_i31().map(Into::into))
535    }
536
537    /// Downcast this `anyref` to an `i31`, panicking if this `anyref` is not an
538    /// `i31`.
539    ///
540    /// # Errors
541    ///
542    /// Return an error if this reference has been unrooted.
543    ///
544    /// # Panics
545    ///
546    /// Panics if this reference is associated with a different store, or if
547    /// this `anyref` is not an `i31`.
548    pub fn unwrap_i31(&self, store: impl AsContext) -> Result<I31> {
549        Ok(self.as_i31(store)?.expect("AnyRef::unwrap_i31 on non-i31"))
550    }
551
552    /// Is this `anyref` a `structref`?
553    ///
554    /// # Errors
555    ///
556    /// Return an error if this reference has been unrooted.
557    ///
558    /// # Panics
559    ///
560    /// Panics if this reference is associated with a different store.
561    pub fn is_struct(&self, store: impl AsContext) -> Result<bool> {
562        self._is_struct(store.as_context().0)
563    }
564
565    pub(crate) fn _is_struct(&self, store: &StoreOpaque) -> Result<bool> {
566        let gc_ref = self.inner.try_gc_ref(store)?;
567        Ok(!gc_ref.is_i31()
568            && store
569                .require_gc_store()?
570                .kind(gc_ref)?
571                .matches(VMGcKind::StructRef))
572    }
573
574    /// Downcast this `anyref` to a `structref`.
575    ///
576    /// If this `anyref` is a `structref`, then `Some(_)` is returned.
577    ///
578    /// If this `anyref` is not a `structref`, then `None` is returned.
579    ///
580    /// # Errors
581    ///
582    /// Return an error if this reference has been unrooted.
583    ///
584    /// # Panics
585    ///
586    /// Panics if this reference is associated with a different store.
587    pub fn as_struct(&self, store: impl AsContext) -> Result<Option<Rooted<StructRef>>> {
588        self._as_struct(store.as_context().0)
589    }
590
591    pub(crate) fn _as_struct(&self, store: &StoreOpaque) -> Result<Option<Rooted<StructRef>>> {
592        if self._is_struct(store)? {
593            Ok(Some(Rooted::from_gc_root_index(self.inner)))
594        } else {
595            Ok(None)
596        }
597    }
598
599    /// Downcast this `anyref` to a `structref`, panicking if this `anyref` is
600    /// not a `structref`.
601    ///
602    /// # Errors
603    ///
604    /// Return an error if this reference has been unrooted.
605    ///
606    /// # Panics
607    ///
608    /// Panics if this reference is associated with a different store, or if
609    /// this `anyref` is not a `struct`.
610    pub fn unwrap_struct(&self, store: impl AsContext) -> Result<Rooted<StructRef>> {
611        self._unwrap_struct(store.as_context().0)
612    }
613
614    pub(crate) fn _unwrap_struct(&self, store: &StoreOpaque) -> Result<Rooted<StructRef>> {
615        Ok(self
616            ._as_struct(store)?
617            .expect("AnyRef::unwrap_struct on non-structref"))
618    }
619
620    /// Is this `anyref` an `arrayref`?
621    ///
622    /// # Errors
623    ///
624    /// Return an error if this reference has been unrooted.
625    ///
626    /// # Panics
627    ///
628    /// Panics if this reference is associated with a different store.
629    pub fn is_array(&self, store: impl AsContext) -> Result<bool> {
630        self._is_array(store.as_context().0)
631    }
632
633    pub(crate) fn _is_array(&self, store: &StoreOpaque) -> Result<bool> {
634        let gc_ref = self.inner.try_gc_ref(store)?;
635        Ok(!gc_ref.is_i31()
636            && store
637                .require_gc_store()?
638                .kind(gc_ref)?
639                .matches(VMGcKind::ArrayRef))
640    }
641
642    /// Downcast this `anyref` to an `arrayref`.
643    ///
644    /// If this `anyref` is an `arrayref`, then `Some(_)` is returned.
645    ///
646    /// If this `anyref` is not an `arrayref`, then `None` is returned.
647    ///
648    /// # Errors
649    ///
650    /// Return an error if this reference has been unrooted.
651    ///
652    /// # Panics
653    ///
654    /// Panics if this reference is associated with a different store.
655    pub fn as_array(&self, store: impl AsContext) -> Result<Option<Rooted<ArrayRef>>> {
656        self._as_array(store.as_context().0)
657    }
658
659    pub(crate) fn _as_array(&self, store: &StoreOpaque) -> Result<Option<Rooted<ArrayRef>>> {
660        if self._is_array(store)? {
661            Ok(Some(Rooted::from_gc_root_index(self.inner)))
662        } else {
663            Ok(None)
664        }
665    }
666
667    /// Downcast this `anyref` to an `arrayref`, panicking if this `anyref` is
668    /// not an `arrayref`.
669    ///
670    /// # Errors
671    ///
672    /// Return an error if this reference has been unrooted.
673    ///
674    /// # Panics
675    ///
676    /// Panics if this reference is associated with a different store, or if
677    /// this `anyref` is not an `array`.
678    pub fn unwrap_array(&self, store: impl AsContext) -> Result<Rooted<ArrayRef>> {
679        self._unwrap_array(store.as_context().0)
680    }
681
682    pub(crate) fn _unwrap_array(&self, store: &StoreOpaque) -> Result<Rooted<ArrayRef>> {
683        Ok(self
684            ._as_array(store)?
685            .expect("AnyRef::unwrap_array on non-arrayref"))
686    }
687}
688
689unsafe impl WasmTy for Rooted<AnyRef> {
690    #[inline]
691    fn valtype() -> ValType {
692        ValType::Ref(RefType::new(false, HeapType::Any))
693    }
694
695    #[inline]
696    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
697        self.comes_from_same_store(store)
698    }
699
700    #[inline]
701    fn dynamic_concrete_type_check(
702        &self,
703        store: &StoreOpaque,
704        _nullable: bool,
705        ty: &HeapType,
706    ) -> Result<()> {
707        self.ensure_matches_ty(store, ty)
708    }
709
710    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
711        self.wasm_ty_store(store, ptr, ValRaw::anyref)
712    }
713
714    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
715        Self::wasm_ty_load(store, ptr.get_anyref(), AnyRef::from_cloned_gc_ref)
716    }
717}
718
719unsafe impl WasmTy for Option<Rooted<AnyRef>> {
720    #[inline]
721    fn valtype() -> ValType {
722        ValType::ANYREF
723    }
724
725    #[inline]
726    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
727        self.map_or(true, |x| x.comes_from_same_store(store))
728    }
729
730    #[inline]
731    fn dynamic_concrete_type_check(
732        &self,
733        store: &StoreOpaque,
734        nullable: bool,
735        ty: &HeapType,
736    ) -> Result<()> {
737        match self {
738            Some(a) => a.ensure_matches_ty(store, ty),
739            None => {
740                ensure!(
741                    nullable,
742                    "expected a non-null reference, but found a null reference"
743                );
744                Ok(())
745            }
746        }
747    }
748
749    #[inline]
750    fn is_vmgcref_and_points_to_object(&self) -> bool {
751        self.is_some()
752    }
753
754    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
755        <Rooted<AnyRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
756    }
757
758    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
759        <Rooted<AnyRef>>::wasm_ty_option_load(store, ptr.get_anyref(), AnyRef::from_cloned_gc_ref)
760    }
761}
762
763unsafe impl WasmTy for OwnedRooted<AnyRef> {
764    #[inline]
765    fn valtype() -> ValType {
766        ValType::Ref(RefType::new(false, HeapType::Any))
767    }
768
769    #[inline]
770    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
771        self.comes_from_same_store(store)
772    }
773
774    #[inline]
775    fn dynamic_concrete_type_check(
776        &self,
777        store: &StoreOpaque,
778        _nullable: bool,
779        ty: &HeapType,
780    ) -> Result<()> {
781        self.ensure_matches_ty(store, ty)
782    }
783
784    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
785        self.wasm_ty_store(store, ptr, ValRaw::anyref)
786    }
787
788    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
789        Self::wasm_ty_load(store, ptr.get_anyref(), AnyRef::from_cloned_gc_ref)
790    }
791}
792
793unsafe impl WasmTy for Option<OwnedRooted<AnyRef>> {
794    #[inline]
795    fn valtype() -> ValType {
796        ValType::ANYREF
797    }
798
799    #[inline]
800    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
801        self.as_ref()
802            .map_or(true, |x| x.comes_from_same_store(store))
803    }
804
805    #[inline]
806    fn dynamic_concrete_type_check(
807        &self,
808        store: &StoreOpaque,
809        nullable: bool,
810        ty: &HeapType,
811    ) -> Result<()> {
812        match self {
813            Some(a) => a.ensure_matches_ty(store, ty),
814            None => {
815                ensure!(
816                    nullable,
817                    "expected a non-null reference, but found a null reference"
818                );
819                Ok(())
820            }
821        }
822    }
823
824    #[inline]
825    fn is_vmgcref_and_points_to_object(&self) -> bool {
826        self.is_some()
827    }
828
829    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
830        <OwnedRooted<AnyRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
831    }
832
833    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
834        <OwnedRooted<AnyRef>>::wasm_ty_option_load(
835            store,
836            ptr.get_anyref(),
837            AnyRef::from_cloned_gc_ref,
838        )
839    }
840}