wasmtime/runtime/gc/enabled/
externref.rs

1//! Implementation of `externref` in Wasmtime.
2
3use super::{AnyRef, RootedGcRefImpl};
4use crate::prelude::*;
5use crate::runtime::vm::VMGcRef;
6use crate::{
7    store::{AutoAssertNoGc, StoreOpaque},
8    AsContextMut, GcHeapOutOfMemory, GcRefImpl, GcRootIndex, HeapType, ManuallyRooted, RefType,
9    Result, Rooted, StoreContext, StoreContextMut, ValRaw, ValType, WasmTy,
10};
11use core::any::Any;
12use core::mem;
13use core::mem::MaybeUninit;
14
15/// An opaque, GC-managed reference to some host data that can be passed to
16/// WebAssembly.
17///
18/// The `ExternRef` type represents WebAssembly `externref` values. Wasm can't
19/// do anything with the `externref`s other than put them in tables, globals,
20/// and locals or pass them to other functions (such as imported functions from
21/// the host). Unlike `anyref`s, Wasm guests cannot directly allocate new
22/// `externref`s; only the host can.
23///
24/// You can use `ExternRef` to give access to host objects and control the
25/// operations that Wasm can perform on them via what functions you allow Wasm
26/// to import.
27///
28/// Like all WebAssembly references, these are opaque and unforgeable to Wasm:
29/// they cannot be faked and Wasm cannot, for example, cast the integer
30/// `0x12345678` into a reference, pretend it is a valid `externref`, and trick
31/// the host into dereferencing it and segfaulting or worse.
32///
33/// Note that you can also use `Rooted<ExternRef>` and
34/// `ManuallyRooted<ExternRef>` as a type parameter with
35/// [`Func::typed`][crate::Func::typed]- and
36/// [`Func::wrap`][crate::Func::wrap]-style APIs.
37///
38/// # Example
39///
40/// ```
41/// # use wasmtime::*;
42/// # use std::borrow::Cow;
43/// # fn _foo() -> Result<()> {
44/// let engine = Engine::default();
45/// let mut store = Store::new(&engine, ());
46///
47/// // Define some APIs for working with host strings from Wasm via `externref`.
48/// let mut linker = Linker::new(&engine);
49/// linker.func_wrap(
50///     "host-string",
51///     "new",
52///     |caller: Caller<'_, ()>| -> Result<Rooted<ExternRef>> {
53///         ExternRef::new(caller, Cow::from(""))
54///     },
55/// )?;
56/// linker.func_wrap(
57///     "host-string",
58///     "concat",
59///     |mut caller: Caller<'_, ()>, a: Rooted<ExternRef>, b: Rooted<ExternRef>| -> Result<Rooted<ExternRef>> {
60///         let mut s = a
61///             .data(&caller)?
62///             .ok_or_else(|| Error::msg("externref has no host data"))?
63///             .downcast_ref::<Cow<str>>()
64///             .ok_or_else(|| Error::msg("externref was not a string"))?
65///             .clone()
66///             .into_owned();
67///         let b = b
68///             .data(&caller)?
69///             .ok_or_else(|| Error::msg("externref has no host data"))?
70///             .downcast_ref::<Cow<str>>()
71///             .ok_or_else(|| Error::msg("externref was not a string"))?;
72///         s.push_str(&b);
73///         ExternRef::new(&mut caller, s)
74///     },
75/// )?;
76///
77/// // Here is a Wasm module that uses those APIs.
78/// let module = Module::new(
79///     &engine,
80///     r#"
81///         (module
82///             (import "host-string" "concat" (func $concat (param externref externref)
83///                                                          (result externref)))
84///             (func (export "run") (param externref externref) (result externref)
85///                 local.get 0
86///                 local.get 1
87///                 call $concat
88///             )
89///         )
90///     "#,
91/// )?;
92///
93/// // Create a couple `externref`s wrapping `Cow<str>`s.
94/// let hello = ExternRef::new(&mut store, Cow::from("Hello, "))?;
95/// let world = ExternRef::new(&mut store, Cow::from("World!"))?;
96///
97/// // Instantiate the module and pass the `externref`s into it.
98/// let instance = linker.instantiate(&mut store, &module)?;
99/// let result = instance
100///     .get_typed_func::<(Rooted<ExternRef>, Rooted<ExternRef>), Rooted<ExternRef>>(&mut store, "run")?
101///     .call(&mut store, (hello, world))?;
102///
103/// // The module should have concatenated the strings together!
104/// assert_eq!(
105///     result
106///         .data(&store)?
107///         .expect("externref should have host data")
108///         .downcast_ref::<Cow<str>>()
109///         .expect("host data should be a `Cow<str>`"),
110///     "Hello, World!"
111/// );
112/// # Ok(())
113/// # }
114/// ```
115#[derive(Debug, Clone)]
116#[repr(transparent)]
117pub struct ExternRef {
118    pub(crate) inner: GcRootIndex,
119}
120
121unsafe impl GcRefImpl for ExternRef {
122    #[allow(private_interfaces)]
123    fn transmute_ref(index: &GcRootIndex) -> &Self {
124        // Safety: `ExternRef` is a newtype of a `GcRootIndex`.
125        let me: &Self = unsafe { mem::transmute(index) };
126
127        // Assert we really are just a newtype of a `GcRootIndex`.
128        assert!(matches!(
129            me,
130            Self {
131                inner: GcRootIndex { .. },
132            }
133        ));
134
135        me
136    }
137}
138
139impl ExternRef {
140    /// Creates a new instance of `ExternRef` wrapping the given value.
141    ///
142    /// The resulting value is automatically unrooted when the given `context`'s
143    /// scope is exited. See [`Rooted<T>`][crate::Rooted]'s documentation for
144    /// more details.
145    ///
146    /// This method will *not* automatically trigger a GC to free up space in
147    /// the GC heap; instead it will return an error. This gives you more
148    /// precise control over when collections happen and allows you to choose
149    /// between performing synchronous and asynchronous collections.
150    ///
151    /// # Errors
152    ///
153    /// If the allocation cannot be satisfied because the GC heap is currently
154    /// out of memory, but performing a garbage collection might free up space
155    /// such that retrying the allocation afterwards might succeed, then a
156    /// `GcHeapOutOfMemory<T>` error is returned.
157    ///
158    /// The `GcHeapOutOfMemory<T>` error contains the host value that the
159    /// `externref` would have wrapped. You can extract that value from this
160    /// error and reuse it when attempting to allocate an `externref` again
161    /// after GC or otherwise do with it whatever you see fit.
162    ///
163    /// # Example
164    ///
165    /// ```
166    /// # use wasmtime::*;
167    /// # fn _foo() -> Result<()> {
168    /// let mut store = Store::<()>::default();
169    ///
170    /// {
171    ///     let mut scope = RootScope::new(&mut store);
172    ///
173    ///     // Create an `externref` wrapping a `str`.
174    ///     let externref = match ExternRef::new(&mut scope, "hello!") {
175    ///         Ok(x) => x,
176    ///         // If the heap is out of memory, then do a GC and try again.
177    ///         Err(e) if e.is::<GcHeapOutOfMemory<&'static str>>() => {
178    ///             // Do a GC! Note: in an async context, you'd want to do
179    ///             // `scope.as_context_mut().gc_async().await`.
180    ///             scope.as_context_mut().gc();
181    ///
182    ///             // Extract the original host value from the error.
183    ///             let host_value = e
184    ///                 .downcast::<GcHeapOutOfMemory<&'static str>>()
185    ///                 .unwrap()
186    ///                 .into_inner();
187    ///
188    ///             // Try to allocate the `externref` again, now that the GC
189    ///             // has hopefully freed up some space.
190    ///             ExternRef::new(&mut scope, host_value)?
191    ///         }
192    ///         Err(e) => return Err(e),
193    ///     };
194    ///
195    ///     // Use the `externref`, pass it to Wasm, etc...
196    /// }
197    ///
198    /// // The `externref` is automatically unrooted when we exit the scope.
199    /// # Ok(())
200    /// # }
201    /// ```
202    pub fn new<T>(mut context: impl AsContextMut, value: T) -> Result<Rooted<ExternRef>>
203    where
204        T: 'static + Any + Send + Sync,
205    {
206        let ctx = context.as_context_mut().0;
207
208        let value: Box<dyn Any + Send + Sync> = Box::new(value);
209        let gc_ref = ctx
210            .gc_store_mut()?
211            .alloc_externref(value)
212            .context("unrecoverable error when allocating new `externref`")?
213            .map_err(|x| GcHeapOutOfMemory::<T>::new(*x.downcast().unwrap()))
214            .context("failed to allocate `externref`")?;
215
216        let mut ctx = AutoAssertNoGc::new(ctx);
217        Ok(Self::from_cloned_gc_ref(&mut ctx, gc_ref.into()))
218    }
219
220    /// Convert an `anyref` into an `externref`.
221    ///
222    /// This is equivalent to the `extern.convert_any` instruction in Wasm.
223    ///
224    /// You can get the underlying `anyref` again via the
225    /// [`AnyRef::convert_extern`] method or the `any.convert_extern` Wasm
226    /// instruction.
227    ///
228    /// The resulting `ExternRef` will not have any host data associated with
229    /// it, so [`ExternRef::data`] and [`ExternRef::data_mut`] will both return
230    /// `None`.
231    ///
232    /// Returns an error if the `anyref` GC reference has been unrooted (eg if
233    /// you attempt to use a `Rooted<AnyRef>` after exiting the scope it was
234    /// rooted within). See the documentation for [`Rooted<T>`][crate::Rooted]
235    /// for more details.
236    ///
237    /// # Example
238    ///
239    /// ```
240    /// use wasmtime::*;
241    /// # fn foo() -> Result<()> {
242    /// let engine = Engine::default();
243    /// let mut store = Store::new(&engine, ());
244    ///
245    /// // Create an `anyref`.
246    /// let i31 = I31::wrapping_u32(0x1234);
247    /// let anyref = AnyRef::from_i31(&mut store, i31);
248    ///
249    /// // Convert that `anyref` into an `externref`.
250    /// let externref = ExternRef::convert_any(&mut store, anyref)?;
251    ///
252    /// // This `externref` doesn't have any associated host data.
253    /// assert!(externref.data(&store)?.is_none());
254    ///
255    /// // We can convert it back to an `anyref` and get its underlying `i31`
256    /// // data.
257    /// let anyref = AnyRef::convert_extern(&mut store, externref)?;
258    /// assert_eq!(anyref.unwrap_i31(&store)?.get_u32(), 0x1234);
259    /// # Ok(()) }
260    /// # foo().unwrap();
261    pub fn convert_any(
262        mut context: impl AsContextMut,
263        anyref: Rooted<AnyRef>,
264    ) -> Result<Rooted<ExternRef>> {
265        let mut store = AutoAssertNoGc::new(context.as_context_mut().0);
266        Self::_convert_any(&mut store, anyref)
267    }
268
269    pub(crate) fn _convert_any(
270        store: &mut AutoAssertNoGc<'_>,
271        anyref: Rooted<AnyRef>,
272    ) -> Result<Rooted<ExternRef>> {
273        let gc_ref = anyref.try_clone_gc_ref(store)?;
274        Ok(Self::from_cloned_gc_ref(store, gc_ref))
275    }
276
277    /// Creates a new, manually-rooted instance of `ExternRef` wrapping the
278    /// given value.
279    ///
280    /// The resulting value must be manually unrooted, or else it will leak for
281    /// the entire duration of the store's lifetime. See
282    /// [`ManuallyRooted<T>`][crate::ManuallyRooted]'s documentation for more
283    /// details.
284    ///
285    /// # Errors
286    ///
287    /// This function returns the same errors in the same scenarios as
288    /// [`ExternRef::new`][crate::ExternRef::new].
289    ///
290    /// # Example
291    ///
292    /// ```
293    /// # use wasmtime::*;
294    /// # fn _foo() -> Result<()> {
295    /// let mut store = Store::<()>::default();
296    ///
297    /// // Create a manually-rooted `externref` wrapping a `str`.
298    /// let externref = ExternRef::new_manually_rooted(&mut store, "hello!")?;
299    ///
300    /// // Use `externref` a bunch, pass it to Wasm, etc...
301    ///
302    /// // Don't forget to explicitly unroot the `externref` when you're done
303    /// // using it!
304    /// externref.unroot(&mut store);
305    /// # Ok(())
306    /// # }
307    /// ```
308    pub fn new_manually_rooted<T>(
309        mut store: impl AsContextMut,
310        value: T,
311    ) -> Result<ManuallyRooted<ExternRef>>
312    where
313        T: 'static + Any + Send + Sync,
314    {
315        let ctx = store.as_context_mut().0;
316
317        let value: Box<dyn Any + Send + Sync> = Box::new(value);
318        let gc_ref = ctx
319            .gc_store_mut()?
320            .alloc_externref(value)
321            .context("unrecoverable error when allocating new `externref`")?
322            .map_err(|x| GcHeapOutOfMemory::<T>::new(*x.downcast().unwrap()))
323            .context("failed to allocate `externref`")?;
324
325        let mut ctx = AutoAssertNoGc::new(ctx);
326        Ok(ManuallyRooted::new(&mut ctx, gc_ref.into()))
327    }
328
329    /// Create a new `Rooted<ExternRef>` from the given GC reference.
330    ///
331    /// Does not invoke the `GcRuntime`'s clone hook; callers should ensure it
332    /// has been called.
333    ///
334    /// `gc_ref` should be a GC reference pointing to an instance of `externref`
335    /// that is in this store's GC heap. Failure to uphold this invariant is
336    /// memory safe but will result in general incorrectness such as panics and
337    /// wrong results.
338    pub(crate) fn from_cloned_gc_ref(
339        store: &mut AutoAssertNoGc<'_>,
340        gc_ref: VMGcRef,
341    ) -> Rooted<Self> {
342        assert!(
343            gc_ref.is_extern_ref(&*store.unwrap_gc_store().gc_heap)
344                || gc_ref.is_any_ref(&*store.unwrap_gc_store().gc_heap),
345            "GC reference {gc_ref:#p} should be an externref or anyref"
346        );
347        Rooted::new(store, gc_ref)
348    }
349
350    /// Get a shared borrow of the underlying data for this `ExternRef`.
351    ///
352    /// Returns `None` if this is an `externref` wrapper of an `anyref` created
353    /// by the `extern.convert_any` instruction or the
354    /// [`ExternRef::convert_any`] method.
355    ///
356    /// Returns an error if this `externref` GC reference has been unrooted (eg
357    /// if you attempt to use a `Rooted<ExternRef>` after exiting the scope it
358    /// was rooted within). See the documentation for
359    /// [`Rooted<T>`][crate::Rooted] for more details.
360    ///
361    /// # Example
362    ///
363    /// ```
364    /// # use wasmtime::*;
365    /// # fn _foo() -> Result<()> {
366    /// let mut store = Store::<()>::default();
367    ///
368    /// let externref = ExternRef::new(&mut store, "hello")?;
369    ///
370    /// // Access the `externref`'s host data.
371    /// let data = externref.data(&store)?.ok_or_else(|| Error::msg("no host data"))?;
372    /// // Dowcast it to a `&str`.
373    /// let data = data.downcast_ref::<&str>().ok_or_else(|| Error::msg("not a str"))?;
374    /// // We should have got the data we created the `externref` with!
375    /// assert_eq!(*data, "hello");
376    /// # Ok(())
377    /// # }
378    /// ```
379    pub fn data<'a, T>(
380        &self,
381        store: impl Into<StoreContext<'a, T>>,
382    ) -> Result<Option<&'a (dyn Any + Send + Sync)>>
383    where
384        T: 'a,
385    {
386        let store = store.into().0;
387        let gc_ref = self.inner.try_gc_ref(&store)?;
388        let gc_store = store.gc_store()?;
389        if let Some(externref) = gc_ref.as_externref(&*gc_store.gc_heap) {
390            Ok(Some(gc_store.externref_host_data(externref)))
391        } else {
392            Ok(None)
393        }
394    }
395
396    /// Get an exclusive borrow of the underlying data for this `ExternRef`.
397    ///
398    /// Returns `None` if this is an `externref` wrapper of an `anyref` created
399    /// by the `extern.convert_any` instruction or the
400    /// [`ExternRef::convert_any`] constructor.
401    ///
402    /// Returns an error if this `externref` GC reference has been unrooted (eg
403    /// if you attempt to use a `Rooted<ExternRef>` after exiting the scope it
404    /// was rooted within). See the documentation for
405    /// [`Rooted<T>`][crate::Rooted] for more details.
406    ///
407    /// # Example
408    ///
409    /// ```
410    /// # use wasmtime::*;
411    /// # fn _foo() -> Result<()> {
412    /// let mut store = Store::<()>::default();
413    ///
414    /// let externref = ExternRef::new::<usize>(&mut store, 0)?;
415    ///
416    /// // Access the `externref`'s host data.
417    /// let data = externref.data_mut(&mut store)?.ok_or_else(|| Error::msg("no host data"))?;
418    /// // Dowcast it to a `usize`.
419    /// let data = data.downcast_mut::<usize>().ok_or_else(|| Error::msg("not a usize"))?;
420    /// // We initialized to zero.
421    /// assert_eq!(*data, 0);
422    /// // And we can mutate the value!
423    /// *data += 10;
424    /// # Ok(())
425    /// # }
426    /// ```
427    pub fn data_mut<'a, T>(
428        &self,
429        store: impl Into<StoreContextMut<'a, T>>,
430    ) -> Result<Option<&'a mut (dyn Any + Send + Sync)>>
431    where
432        T: 'a,
433    {
434        let store = store.into().0;
435        // NB: need to do an unchecked copy to release the borrow on the store
436        // so that we can get the store's GC store. But importantly we cannot
437        // trigger a GC while we are working with `gc_ref` here.
438        let gc_ref = self.inner.try_gc_ref(store)?.unchecked_copy();
439        let gc_store = store.gc_store_mut()?;
440        if let Some(externref) = gc_ref.as_externref(&*gc_store.gc_heap) {
441            Ok(Some(gc_store.externref_host_data_mut(externref)))
442        } else {
443            Ok(None)
444        }
445    }
446
447    /// Creates a new strongly-owned [`ExternRef`] from the raw value provided.
448    ///
449    /// This is intended to be used in conjunction with [`Func::new_unchecked`],
450    /// [`Func::call_unchecked`], and [`ValRaw`] with its `externref` field.
451    ///
452    /// This function assumes that `raw` is an externref value which is
453    /// currently rooted within the [`Store`].
454    ///
455    /// # Unsafety
456    ///
457    /// This function is particularly `unsafe` because `raw` not only must be a
458    /// valid externref value produced prior by `to_raw` but it must also be
459    /// correctly rooted within the store. When arguments are provided to a
460    /// callback with [`Func::new_unchecked`], for example, or returned via
461    /// [`Func::call_unchecked`], if a GC is performed within the store then
462    /// floating externref values are not rooted and will be GC'd, meaning that
463    /// this function will no longer be safe to call with the values cleaned up.
464    /// This function must be invoked *before* possible GC operations can happen
465    /// (such as calling wasm).
466    ///
467    /// When in doubt try to not use this. Instead use the safe Rust APIs of
468    /// [`TypedFunc`] and friends.
469    ///
470    /// [`Func::call_unchecked`]: crate::Func::call_unchecked
471    /// [`Func::new_unchecked`]: crate::Func::new_unchecked
472    /// [`Store`]: crate::Store
473    /// [`TypedFunc`]: crate::TypedFunc
474    /// [`ValRaw`]: crate::ValRaw
475    pub unsafe fn from_raw(mut store: impl AsContextMut, raw: u32) -> Option<Rooted<ExternRef>> {
476        let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
477        Self::_from_raw(&mut store, raw)
478    }
479
480    // (Not actually memory unsafe since we have indexed GC heaps.)
481    pub(crate) fn _from_raw(store: &mut AutoAssertNoGc, raw: u32) -> Option<Rooted<ExternRef>> {
482        let gc_ref = VMGcRef::from_raw_u32(raw)?;
483        let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref);
484        Some(Self::from_cloned_gc_ref(store, gc_ref))
485    }
486
487    /// Converts this [`ExternRef`] to a raw value suitable to store within a
488    /// [`ValRaw`].
489    ///
490    /// Returns an error if this `externref` has been unrooted.
491    ///
492    /// # Unsafety
493    ///
494    /// Produces a raw value which is only safe to pass into a store if a GC
495    /// doesn't happen between when the value is produce and when it's passed
496    /// into the store.
497    ///
498    /// [`ValRaw`]: crate::ValRaw
499    pub unsafe fn to_raw(&self, mut store: impl AsContextMut) -> Result<u32> {
500        let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
501        self._to_raw(&mut store)
502    }
503
504    pub(crate) fn _to_raw(&self, store: &mut AutoAssertNoGc) -> Result<u32> {
505        let gc_ref = self.inner.try_clone_gc_ref(store)?;
506        let raw = gc_ref.as_raw_u32();
507        store.unwrap_gc_store_mut().expose_gc_ref_to_wasm(gc_ref);
508        Ok(raw)
509    }
510}
511
512unsafe impl WasmTy for Rooted<ExternRef> {
513    #[inline]
514    fn valtype() -> ValType {
515        ValType::Ref(RefType::new(false, HeapType::Extern))
516    }
517
518    #[inline]
519    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
520        self.comes_from_same_store(store)
521    }
522
523    #[inline]
524    fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
525        unreachable!()
526    }
527
528    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
529        self.wasm_ty_store(store, ptr, ValRaw::externref)
530    }
531
532    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
533        Self::wasm_ty_load(store, ptr.get_externref(), ExternRef::from_cloned_gc_ref)
534    }
535}
536
537unsafe impl WasmTy for Option<Rooted<ExternRef>> {
538    #[inline]
539    fn valtype() -> ValType {
540        ValType::EXTERNREF
541    }
542
543    #[inline]
544    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
545        self.map_or(true, |x| x.comes_from_same_store(store))
546    }
547
548    #[inline]
549    fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
550        unreachable!()
551    }
552
553    #[inline]
554    fn is_vmgcref_and_points_to_object(&self) -> bool {
555        self.is_some()
556    }
557
558    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
559        <Rooted<ExternRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::externref)
560    }
561
562    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
563        <Rooted<ExternRef>>::wasm_ty_option_load(
564            store,
565            ptr.get_externref(),
566            ExternRef::from_cloned_gc_ref,
567        )
568    }
569}
570
571unsafe impl WasmTy for ManuallyRooted<ExternRef> {
572    #[inline]
573    fn valtype() -> ValType {
574        ValType::Ref(RefType::new(false, HeapType::Extern))
575    }
576
577    #[inline]
578    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
579        self.comes_from_same_store(store)
580    }
581
582    #[inline]
583    fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
584        unreachable!()
585    }
586
587    #[inline]
588    fn is_vmgcref_and_points_to_object(&self) -> bool {
589        true
590    }
591
592    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
593        self.wasm_ty_store(store, ptr, ValRaw::externref)
594    }
595
596    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
597        Self::wasm_ty_load(store, ptr.get_externref(), ExternRef::from_cloned_gc_ref)
598    }
599}
600
601unsafe impl WasmTy for Option<ManuallyRooted<ExternRef>> {
602    #[inline]
603    fn valtype() -> ValType {
604        ValType::EXTERNREF
605    }
606
607    #[inline]
608    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
609        self.as_ref()
610            .map_or(true, |x| x.comes_from_same_store(store))
611    }
612
613    #[inline]
614    fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
615        unreachable!()
616    }
617
618    #[inline]
619    fn is_vmgcref_and_points_to_object(&self) -> bool {
620        self.is_some()
621    }
622
623    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
624        <ManuallyRooted<ExternRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::externref)
625    }
626
627    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
628        <ManuallyRooted<ExternRef>>::wasm_ty_option_load(
629            store,
630            ptr.get_externref(),
631            ExternRef::from_cloned_gc_ref,
632        )
633    }
634}