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