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