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