dusk_wasmtime/runtime/gc/enabled/externref.rs
1//! Implementation of `externref` in Wasmtime.
2
3use crate::{
4 store::{AutoAssertNoGc, StoreOpaque},
5 AsContextMut, FuncType, GcHeapOutOfMemory, GcRefImpl, GcRootIndex, HeapType, ManuallyRooted,
6 RefType, Result, RootSet, Rooted, StoreContext, StoreContextMut, ValRaw, ValType, WasmTy,
7};
8use anyhow::Context;
9use std::any::Any;
10use std::num::NonZeroU64;
11use wasmtime_runtime::VMGcRef;
12
13/// An opaque, GC-managed reference to some host data that can be passed to
14/// WebAssembly.
15///
16/// The `ExternRef` type represents WebAssembly `externref` values. Wasm can't
17/// do anything with the `externref`s other than put them in tables, globals,
18/// and locals or pass them to other functions (such as imported functions from
19/// the host). Unlike `anyref`s, Wasm guests cannot directly allocate new
20/// `externref`s; only the host can.
21///
22/// You can use `ExternRef` to give access to host objects and control the
23/// operations that Wasm can perform on them via what functions you allow Wasm
24/// to import.
25///
26/// Like all WebAssembly references, these are opaque and unforgable to Wasm:
27/// they cannot be faked and Wasm cannot, for example, cast the integer
28/// `0x12345678` into a reference, pretend it is a valid `externref`, and trick
29/// the host into dereferencing it and segfaulting or worse.
30///
31/// Note that you can also use `Rooted<ExternRef>` and
32/// `ManuallyRooted<ExternRef>` as a type parameter with
33/// [`Func::typed`][crate::Func::typed]- and
34/// [`Func::wrap`][crate::Func::wrap]-style APIs.
35///
36/// # Example
37///
38/// ```
39/// # use wasmtime::*;
40/// # use std::borrow::Cow;
41/// # fn _foo() -> Result<()> {
42/// let engine = Engine::default();
43/// let mut store = Store::new(&engine, ());
44///
45/// // Define some APIs for working with host strings from Wasm via `externref`.
46/// let mut linker = Linker::new(&engine);
47/// linker.func_wrap(
48/// "host-string",
49/// "new",
50/// |caller: Caller<'_, ()>| -> Result<Rooted<ExternRef>> {
51/// ExternRef::new(caller, Cow::from(""))
52/// },
53/// )?;
54/// linker.func_wrap(
55/// "host-string",
56/// "concat",
57/// |mut caller: Caller<'_, ()>, a: Rooted<ExternRef>, b: Rooted<ExternRef>| -> Result<Rooted<ExternRef>> {
58/// let mut s = a
59/// .data(&caller)?
60/// .downcast_ref::<Cow<str>>()
61/// .ok_or_else(|| Error::msg("externref was not a string"))?
62/// .clone()
63/// .into_owned();
64/// let b = b
65/// .data(&caller)?
66/// .downcast_ref::<Cow<str>>()
67/// .ok_or_else(|| Error::msg("externref was not a string"))?;
68/// s.push_str(&b);
69/// ExternRef::new(&mut caller, s)
70/// },
71/// )?;
72///
73/// // Here is a Wasm module that uses those APIs.
74/// let module = Module::new(
75/// &engine,
76/// r#"
77/// (module
78/// (import "host-string" "concat" (func $concat (param externref externref)
79/// (result externref)))
80/// (func (export "run") (param externref externref) (result externref)
81/// local.get 0
82/// local.get 1
83/// call $concat
84/// )
85/// )
86/// "#,
87/// )?;
88///
89/// // Create a couple `externref`s wrapping `Cow<str>`s.
90/// let hello = ExternRef::new(&mut store, Cow::from("Hello, "))?;
91/// let world = ExternRef::new(&mut store, Cow::from("World!"))?;
92///
93/// // Instantiate the module and pass the `externref`s into it.
94/// let instance = linker.instantiate(&mut store, &module)?;
95/// let result = instance
96/// .get_typed_func::<(Rooted<ExternRef>, Rooted<ExternRef>), Rooted<ExternRef>>(&mut store, "run")?
97/// .call(&mut store, (hello, world))?;
98///
99/// // The module should have concatenated the strings together!
100/// assert_eq!(
101/// result.data(&store)?.downcast_ref::<Cow<str>>().unwrap(),
102/// "Hello, World!"
103/// );
104/// # Ok(())
105/// # }
106/// ```
107#[derive(Debug, Clone)]
108#[repr(transparent)]
109pub struct ExternRef {
110 inner: GcRootIndex,
111}
112
113unsafe impl GcRefImpl for ExternRef {
114 #[allow(private_interfaces)]
115 fn transmute_ref(index: &GcRootIndex) -> &Self {
116 // Safety: `ExternRef` is a newtype of a `GcRootIndex`.
117 let me: &Self = unsafe { std::mem::transmute(index) };
118
119 // Assert we really are just a newtype of a `GcRootIndex`.
120 assert!(matches!(
121 me,
122 Self {
123 inner: GcRootIndex { .. },
124 }
125 ));
126
127 me
128 }
129}
130
131impl ExternRef {
132 /// Creates a new instance of `ExternRef` wrapping the given value.
133 ///
134 /// The resulting value is automatically unrooted when the given `context`'s
135 /// scope is exited. See [`Rooted<T>`][crate::Rooted]'s documentation for
136 /// more details.
137 ///
138 /// This method will *not* automatically trigger a GC to free up space in
139 /// the GC heap; instead it will return an error. This gives you more
140 /// precise control over when collections happen and allows you to choose
141 /// between performing synchronous and asynchronous collections.
142 ///
143 /// # Errors
144 ///
145 /// If the allocation cannot be satisfied because the GC heap is currently
146 /// out of memory, but performing a garbage collection might free up space
147 /// such that retrying the allocation afterwards might succeed, then a
148 /// `GcHeapOutOfMemory<T>` error is returned.
149 ///
150 /// The `GcHeapOutOfMemory<T>` error contains the host value that the
151 /// `externref` would have wrapped. You can extract that value from this
152 /// error and reuse it when attempting to allocate an `externref` again
153 /// after GC or otherwise do with it whatever you see fit.
154 ///
155 /// # Example
156 ///
157 /// ```
158 /// # use wasmtime::*;
159 /// # fn _foo() -> Result<()> {
160 /// let mut store = Store::<()>::default();
161 ///
162 /// {
163 /// let mut scope = RootScope::new(&mut store);
164 ///
165 /// // Create an `externref` wrapping a `str`.
166 /// let externref = match ExternRef::new(&mut scope, "hello!") {
167 /// Ok(x) => x,
168 /// // If the heap is out of memory, then do a GC and try again.
169 /// Err(e) if e.is::<GcHeapOutOfMemory<&'static str>>() => {
170 /// // Do a GC! Note: in an async context, you'd want to do
171 /// // `scope.as_context_mut().gc_async().await`.
172 /// scope.as_context_mut().gc();
173 ///
174 /// // Extract the original host value from the error.
175 /// let host_value = e
176 /// .downcast::<GcHeapOutOfMemory<&'static str>>()
177 /// .unwrap()
178 /// .into_inner();
179 ///
180 /// // Try to allocate the `externref` again, now that the GC
181 /// // has hopefully freed up some space.
182 /// ExternRef::new(&mut scope, host_value)?
183 /// }
184 /// Err(e) => return Err(e),
185 /// };
186 ///
187 /// // Use the `externref`, pass it to Wasm, etc...
188 /// }
189 ///
190 /// // The `externref` is automatically unrooted when we exit the scope.
191 /// # Ok(())
192 /// # }
193 /// ```
194 pub fn new<T>(mut context: impl AsContextMut, value: T) -> Result<Rooted<ExternRef>>
195 where
196 T: 'static + Any + Send + Sync,
197 {
198 let ctx = context.as_context_mut().0;
199
200 let value: Box<dyn Any + Send + Sync> = Box::new(value);
201 let gc_ref = ctx
202 .gc_store_mut()?
203 .alloc_externref(value)
204 .context("unrecoverable error when allocating new `externref`")?
205 .map_err(|x| GcHeapOutOfMemory::<T>::new(*x.downcast().unwrap()))
206 .context("failed to allocate `externref`")?;
207
208 let mut ctx = AutoAssertNoGc::new(ctx);
209 Ok(Self::from_cloned_gc_ref(&mut ctx, gc_ref.into()))
210 }
211
212 /// Creates a new, manually-rooted instance of `ExternRef` wrapping the
213 /// given value.
214 ///
215 /// The resulting value must be manually unrooted, or else it will leak for
216 /// the entire duration of the store's lifetime. See
217 /// [`ManuallyRooted<T>`][crate::ManuallyRooted]'s documentation for more
218 /// details.
219 ///
220 /// # Errors
221 ///
222 /// This function returns the same errors in the same scenarios as
223 /// [`ExternRef::new`][crate::ExternRef::new].
224 ///
225 /// # Example
226 ///
227 /// ```
228 /// # use wasmtime::*;
229 /// # fn _foo() -> Result<()> {
230 /// let mut store = Store::<()>::default();
231 ///
232 /// // Create a manually-rooted `externref` wrapping a `str`.
233 /// let externref = ExternRef::new_manually_rooted(&mut store, "hello!")?;
234 ///
235 /// // Use `externref` a bunch, pass it to Wasm, etc...
236 ///
237 /// // Don't forget to explicitly unroot the `externref` when you're done
238 /// // using it!
239 /// externref.unroot(&mut store);
240 /// # Ok(())
241 /// # }
242 /// ```
243 pub fn new_manually_rooted<T>(
244 mut store: impl AsContextMut,
245 value: T,
246 ) -> Result<ManuallyRooted<ExternRef>>
247 where
248 T: 'static + Any + Send + Sync,
249 {
250 let ctx = store.as_context_mut().0;
251
252 let value: Box<dyn Any + Send + Sync> = Box::new(value);
253 let gc_ref = ctx
254 .gc_store_mut()?
255 .alloc_externref(value)
256 .context("unrecoverable error when allocating new `externref`")?
257 .map_err(|x| GcHeapOutOfMemory::<T>::new(*x.downcast().unwrap()))
258 .context("failed to allocate `externref`")?;
259
260 let mut ctx = AutoAssertNoGc::new(ctx);
261 Ok(ManuallyRooted::new(&mut ctx, gc_ref.into()))
262 }
263
264 /// Create a new `Rooted<ExternRef>` from the given GC reference.
265 ///
266 /// Does not invoke the `GcRuntime`'s clone hook; callers should ensure it
267 /// has been called.
268 ///
269 /// `gc_ref` should be a GC reference pointing to an instance of `externref`
270 /// that is in this store's GC heap. Failure to uphold this invariant is
271 /// memory safe but will result in general incorrectness such as panics and
272 /// wrong results.
273 pub(crate) fn from_cloned_gc_ref(
274 store: &mut AutoAssertNoGc<'_>,
275 gc_ref: VMGcRef,
276 ) -> Rooted<Self> {
277 assert!(
278 gc_ref.is_extern_ref(),
279 "GC reference {gc_ref:#p} is not an externref"
280 );
281 Rooted::new(store, gc_ref)
282 }
283
284 /// Get a shared borrow of the underlying data for this `ExternRef`.
285 ///
286 /// Returns an error if this `externref` GC reference has been unrooted (eg
287 /// if you attempt to use a `Rooted<ExternRef>` after exiting the scope it
288 /// was rooted within). See the documentation for
289 /// [`Rooted<T>`][crate::Rooted] for more details.
290 ///
291 /// # Example
292 ///
293 /// ```
294 /// # use wasmtime::*;
295 /// # fn _foo() -> Result<()> {
296 /// let mut store = Store::<()>::default();
297 ///
298 /// let externref = ExternRef::new(&mut store, "hello")?;
299 ///
300 /// // Access the `externref`'s host data.
301 /// let data = externref.data(&store)?;
302 /// // Dowcast it to a `&str`.
303 /// let data = data.downcast_ref::<&str>().ok_or_else(|| Error::msg("not a str"))?;
304 /// // We should have got the data we created the `externref` with!
305 /// assert_eq!(*data, "hello");
306 /// # Ok(())
307 /// # }
308 /// ```
309 pub fn data<'a, T>(
310 &self,
311 store: impl Into<StoreContext<'a, T>>,
312 ) -> Result<&'a (dyn Any + Send + Sync)>
313 where
314 T: 'a,
315 {
316 let store = store.into().0;
317 let gc_ref = self.inner.unchecked_try_gc_ref(&store)?;
318 let externref = gc_ref.as_externref_unchecked();
319 Ok(store.gc_store()?.externref_host_data(externref))
320 }
321
322 /// Get an exclusive borrow of the underlying data for this `ExternRef`.
323 ///
324 /// Returns an error if this `externref` GC reference has been unrooted (eg
325 /// if you attempt to use a `Rooted<ExternRef>` after exiting the scope it
326 /// was rooted within). See the documentation for
327 /// [`Rooted<T>`][crate::Rooted] for more details.
328 ///
329 /// # Example
330 ///
331 /// ```
332 /// # use wasmtime::*;
333 /// # fn _foo() -> Result<()> {
334 /// let mut store = Store::<()>::default();
335 ///
336 /// let externref = ExternRef::new::<usize>(&mut store, 0)?;
337 ///
338 /// // Access the `externref`'s host data.
339 /// let data = externref.data_mut(&mut store)?;
340 /// // Dowcast it to a `usize`.
341 /// let data = data.downcast_mut::<usize>().ok_or_else(|| Error::msg("not a usize"))?;
342 /// // We initialized to zero.
343 /// assert_eq!(*data, 0);
344 /// // And we can mutate the value!
345 /// *data += 10;
346 /// # Ok(())
347 /// # }
348 /// ```
349 pub fn data_mut<'a, T>(
350 &self,
351 store: impl Into<StoreContextMut<'a, T>>,
352 ) -> Result<&'a mut (dyn Any + Send + Sync)>
353 where
354 T: 'a,
355 {
356 let store = store.into().0;
357 let gc_ref = self.inner.unchecked_try_gc_ref(store)?.unchecked_copy();
358 let externref = gc_ref.as_externref_unchecked();
359 Ok(store.gc_store_mut()?.externref_host_data_mut(externref))
360 }
361
362 /// Creates a new strongly-owned [`ExternRef`] from the raw value provided.
363 ///
364 /// This is intended to be used in conjunction with [`Func::new_unchecked`],
365 /// [`Func::call_unchecked`], and [`ValRaw`] with its `externref` field.
366 ///
367 /// This function assumes that `raw` is an externref value which is
368 /// currently rooted within the [`Store`].
369 ///
370 /// # Unsafety
371 ///
372 /// This function is particularly `unsafe` because `raw` not only must be a
373 /// valid externref value produced prior by `to_raw` but it must also be
374 /// correctly rooted within the store. When arguments are provided to a
375 /// callback with [`Func::new_unchecked`], for example, or returned via
376 /// [`Func::call_unchecked`], if a GC is performed within the store then
377 /// floating externref values are not rooted and will be GC'd, meaning that
378 /// this function will no longer be safe to call with the values cleaned up.
379 /// This function must be invoked *before* possible GC operations can happen
380 /// (such as calling wasm).
381 ///
382 /// When in doubt try to not use this. Instead use the safe Rust APIs of
383 /// [`TypedFunc`] and friends.
384 ///
385 /// [`Func::call_unchecked`]: crate::Func::call_unchecked
386 /// [`Func::new_unchecked`]: crate::Func::new_unchecked
387 /// [`Store`]: crate::Store
388 /// [`TypedFunc`]: crate::TypedFunc
389 /// [`ValRaw`]: crate::ValRaw
390 pub unsafe fn from_raw(mut store: impl AsContextMut, raw: u32) -> Option<Rooted<ExternRef>> {
391 let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
392 let gc_ref = VMGcRef::from_raw_u32(raw)?;
393 let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref);
394 Some(Self::from_cloned_gc_ref(&mut store, gc_ref))
395 }
396
397 /// Converts this [`ExternRef`] to a raw value suitable to store within a
398 /// [`ValRaw`].
399 ///
400 /// Returns an error if this `externref` has been unrooted.
401 ///
402 /// # Unsafety
403 ///
404 /// Produces a raw value which is only safe to pass into a store if a GC
405 /// doesn't happen between when the value is produce and when it's passed
406 /// into the store.
407 ///
408 /// [`ValRaw`]: crate::ValRaw
409 pub unsafe fn to_raw(&self, mut store: impl AsContextMut) -> Result<u32> {
410 let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
411 let gc_ref = self.inner.try_clone_gc_ref(&mut store)?;
412 let raw = gc_ref.as_raw_u32();
413 store.unwrap_gc_store_mut().expose_gc_ref_to_wasm(gc_ref);
414 Ok(raw)
415 }
416}
417
418unsafe impl WasmTy for Rooted<ExternRef> {
419 // TODO: this should be `VMGcRef` but Cranelift currently doesn't support
420 // using r32 types when targeting 64-bit platforms.
421 type Abi = NonZeroU64;
422
423 #[inline]
424 fn valtype() -> ValType {
425 ValType::Ref(RefType::new(false, HeapType::Extern))
426 }
427
428 #[inline]
429 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
430 self.comes_from_same_store(store)
431 }
432
433 #[inline]
434 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &FuncType) -> Result<()> {
435 unreachable!()
436 }
437
438 #[inline]
439 fn is_non_i31_gc_ref(&self) -> bool {
440 true
441 }
442
443 #[inline]
444 unsafe fn abi_from_raw(raw: *mut ValRaw) -> Self::Abi {
445 let raw = (*raw).get_externref();
446 debug_assert_ne!(raw, 0);
447 NonZeroU64::new_unchecked(u64::from(raw))
448 }
449
450 #[inline]
451 unsafe fn abi_into_raw(abi: Self::Abi, raw: *mut ValRaw) {
452 let externref = u32::try_from(abi.get()).unwrap();
453 *raw = ValRaw::externref(externref);
454 }
455
456 #[inline]
457 fn into_abi(self, store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi> {
458 let gc_ref = self.inner.try_clone_gc_ref(store)?;
459 let r64 = gc_ref.as_r64();
460 store.gc_store_mut()?.expose_gc_ref_to_wasm(gc_ref);
461 debug_assert_ne!(r64, 0);
462 Ok(unsafe { NonZeroU64::new_unchecked(r64) })
463 }
464
465 #[inline]
466 unsafe fn from_abi(abi: Self::Abi, store: &mut AutoAssertNoGc<'_>) -> Self {
467 let gc_ref = VMGcRef::from_r64(abi.get())
468 .expect("valid r64")
469 .expect("non-null");
470 let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref);
471 ExternRef::from_cloned_gc_ref(store, gc_ref)
472 }
473}
474
475unsafe impl WasmTy for Option<Rooted<ExternRef>> {
476 type Abi = u64;
477
478 #[inline]
479 fn valtype() -> ValType {
480 ValType::EXTERNREF
481 }
482
483 #[inline]
484 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
485 self.map_or(true, |x| x.comes_from_same_store(store))
486 }
487
488 #[inline]
489 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &FuncType) -> Result<()> {
490 unreachable!()
491 }
492
493 #[inline]
494 fn is_non_i31_gc_ref(&self) -> bool {
495 true
496 }
497
498 #[inline]
499 unsafe fn abi_from_raw(raw: *mut ValRaw) -> Self::Abi {
500 let externref = (*raw).get_externref();
501 u64::from(externref)
502 }
503
504 #[inline]
505 unsafe fn abi_into_raw(abi: Self::Abi, raw: *mut ValRaw) {
506 let externref = u32::try_from(abi).unwrap();
507 *raw = ValRaw::externref(externref);
508 }
509
510 #[inline]
511 fn into_abi(self, store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi> {
512 Ok(if let Some(x) = self {
513 <Rooted<ExternRef> as WasmTy>::into_abi(x, store)?.get()
514 } else {
515 0
516 })
517 }
518
519 #[inline]
520 unsafe fn from_abi(abi: Self::Abi, store: &mut AutoAssertNoGc<'_>) -> Self {
521 let gc_ref = VMGcRef::from_r64(abi).expect("valid r64")?;
522 let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref);
523 Some(ExternRef::from_cloned_gc_ref(store, gc_ref))
524 }
525}
526
527unsafe impl WasmTy for ManuallyRooted<ExternRef> {
528 type Abi = NonZeroU64;
529
530 #[inline]
531 fn valtype() -> ValType {
532 ValType::Ref(RefType::new(false, HeapType::Extern))
533 }
534
535 #[inline]
536 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
537 self.comes_from_same_store(store)
538 }
539
540 #[inline]
541 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &FuncType) -> Result<()> {
542 unreachable!()
543 }
544
545 #[inline]
546 fn is_non_i31_gc_ref(&self) -> bool {
547 true
548 }
549
550 #[inline]
551 unsafe fn abi_from_raw(raw: *mut ValRaw) -> Self::Abi {
552 let externref = (*raw).get_externref();
553 debug_assert_ne!(externref, 0);
554 NonZeroU64::new_unchecked(u64::from(externref))
555 }
556
557 #[inline]
558 unsafe fn abi_into_raw(abi: Self::Abi, raw: *mut ValRaw) {
559 let externref = u32::try_from(abi.get()).unwrap();
560 *raw = ValRaw::externref(externref);
561 }
562
563 #[inline]
564 fn into_abi(self, store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi> {
565 let gc_ref = self.inner.try_clone_gc_ref(store)?;
566 let r64 = gc_ref.as_r64();
567 store.gc_store_mut()?.expose_gc_ref_to_wasm(gc_ref);
568 Ok(unsafe { NonZeroU64::new_unchecked(r64) })
569 }
570
571 #[inline]
572 unsafe fn from_abi(abi: Self::Abi, store: &mut AutoAssertNoGc<'_>) -> Self {
573 let gc_ref = VMGcRef::from_r64(abi.get())
574 .expect("valid r64")
575 .expect("non-null");
576 let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref);
577 RootSet::with_lifo_scope(store, |store| {
578 let rooted = ExternRef::from_cloned_gc_ref(store, gc_ref);
579 rooted
580 ._to_manually_rooted(store)
581 .expect("rooted is in scope")
582 })
583 }
584}
585
586unsafe impl WasmTy for Option<ManuallyRooted<ExternRef>> {
587 type Abi = u64;
588
589 #[inline]
590 fn valtype() -> ValType {
591 ValType::EXTERNREF
592 }
593
594 #[inline]
595 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
596 self.as_ref()
597 .map_or(true, |x| x.comes_from_same_store(store))
598 }
599
600 #[inline]
601 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &FuncType) -> Result<()> {
602 unreachable!()
603 }
604
605 #[inline]
606 fn is_non_i31_gc_ref(&self) -> bool {
607 true
608 }
609
610 #[inline]
611 unsafe fn abi_from_raw(raw: *mut ValRaw) -> Self::Abi {
612 let externref = (*raw).get_externref();
613 u64::from(externref)
614 }
615
616 #[inline]
617 unsafe fn abi_into_raw(abi: Self::Abi, raw: *mut ValRaw) {
618 let externref = u32::try_from(abi).unwrap();
619 *raw = ValRaw::externref(externref);
620 }
621
622 #[inline]
623 fn into_abi(self, store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi> {
624 Ok(if let Some(x) = self {
625 <ManuallyRooted<ExternRef> as WasmTy>::into_abi(x, store)?.get()
626 } else {
627 0
628 })
629 }
630
631 #[inline]
632 unsafe fn from_abi(abi: Self::Abi, store: &mut AutoAssertNoGc<'_>) -> Self {
633 let gc_ref = VMGcRef::from_r64(abi).expect("valid r64")?;
634 let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref);
635 RootSet::with_lifo_scope(store, |store| {
636 let rooted = ExternRef::from_cloned_gc_ref(store, gc_ref);
637 Some(
638 rooted
639 ._to_manually_rooted(store)
640 .expect("rooted is in scope"),
641 )
642 })
643 }
644}