wasmtime/runtime/gc/enabled/externref.rs
1//! Implementation of `externref` in Wasmtime.
2
3use super::{AnyRef, RootedGcRefImpl};
4use crate::prelude::*;
5use crate::runtime::vm::{self, VMGcRef};
6use crate::store::Asyncness;
7#[cfg(feature = "async")]
8use crate::vm::VMStore;
9use crate::{
10 AsContextMut, GcHeapOutOfMemory, GcRefImpl, GcRootIndex, HeapType, OwnedRooted, RefType,
11 Result, Rooted, StoreContext, StoreContextMut, ValRaw, ValType, WasmTy,
12 store::{AutoAssertNoGc, StoreOpaque, StoreResourceLimiter},
13};
14use core::any::Any;
15use core::mem;
16use 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)]
120pub struct ExternRef {
121 pub(crate) inner: GcRootIndex,
122}
123
124unsafe impl GcRefImpl for ExternRef {
125 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
141impl 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 /// ```
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")]
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
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();
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
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.
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 /// ```
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 /// ```
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
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.)
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
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
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
601unsafe impl WasmTy for Rooted<ExternRef> {
602 #[inline]
603 fn valtype() -> ValType {
604 ValType::Ref(RefType::new(false, HeapType::Extern))
605 }
606
607 #[inline]
608 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
609 self.comes_from_same_store(store)
610 }
611
612 #[inline]
613 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
614 unreachable!()
615 }
616
617 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
618 self.wasm_ty_store(store, ptr, ValRaw::externref)
619 }
620
621 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
626unsafe impl WasmTy for Option<Rooted<ExternRef>> {
627 #[inline]
628 fn valtype() -> ValType {
629 ValType::EXTERNREF
630 }
631
632 #[inline]
633 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
634 self.map_or(true, |x| x.comes_from_same_store(store))
635 }
636
637 #[inline]
638 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
639 unreachable!()
640 }
641
642 #[inline]
643 fn is_vmgcref_and_points_to_object(&self) -> bool {
644 self.is_some()
645 }
646
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
651 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
660unsafe impl WasmTy for OwnedRooted<ExternRef> {
661 #[inline]
662 fn valtype() -> ValType {
663 ValType::Ref(RefType::new(false, HeapType::Extern))
664 }
665
666 #[inline]
667 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
668 self.comes_from_same_store(store)
669 }
670
671 #[inline]
672 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
673 unreachable!()
674 }
675
676 #[inline]
677 fn is_vmgcref_and_points_to_object(&self) -> bool {
678 true
679 }
680
681 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
682 self.wasm_ty_store(store, ptr, ValRaw::externref)
683 }
684
685 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
690unsafe impl WasmTy for Option<OwnedRooted<ExternRef>> {
691 #[inline]
692 fn valtype() -> ValType {
693 ValType::EXTERNREF
694 }
695
696 #[inline]
697 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]
703 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
704 unreachable!()
705 }
706
707 #[inline]
708 fn is_vmgcref_and_points_to_object(&self) -> bool {
709 self.is_some()
710 }
711
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
716 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}