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}