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