Skip to main content

wasmtime/runtime/gc/enabled/
externref.rs

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