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}