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