1 //! Implementation of `externref` in Wasmtime.
2 
3 use super::{AnyRef, RootedGcRefImpl};
4 use crate::prelude::*;
5 use crate::runtime::vm::{self, VMGcRef, VMStore};
6 use crate::{
7     AsContextMut, GcHeapOutOfMemory, GcRefImpl, GcRootIndex, HeapType, OwnedRooted, RefType,
8     Result, Rooted, StoreContext, StoreContextMut, ValRaw, ValType, WasmTy,
9     store::{AutoAssertNoGc, StoreOpaque, StoreResourceLimiter},
10 };
11 use core::any::Any;
12 use core::mem;
13 use 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 /// `OwnedRooted<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)]
117 pub struct ExternRef {
118     pub(crate) inner: GcRootIndex,
119 }
120 
121 unsafe 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 
138 impl 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 an
144     /// [`OwnedRooted<T>`][crate::OwnedRooted]. See the documentation for
145     /// [`Rooted<T>`][crate::Rooted] and
146     /// [`OwnedRooted<T>`][crate::OwnedRooted] 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 an
227     /// [`OwnedRooted<T>`][crate::OwnedRooted]. See the documentation for
228     /// [`Rooted<T>`][crate::Rooted] and
229     /// [`OwnedRooted<T>`][crate::OwnedRooted] 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 
590 unsafe 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 
615 unsafe 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 
649 unsafe impl WasmTy for OwnedRooted<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 
679 unsafe impl WasmTy for Option<OwnedRooted<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         <OwnedRooted<ExternRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::externref)
703     }
704 
705     unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
706         <OwnedRooted<ExternRef>>::wasm_ty_option_load(
707             store,
708             ptr.get_externref(),
709             ExternRef::from_cloned_gc_ref,
710         )
711     }
712 }
713