dusk_wasmtime/runtime/gc/enabled/
anyref.rs

1//! Implementation of `anyref` in Wasmtime.
2
3use crate::{
4    store::{AutoAssertNoGc, StoreOpaque},
5    AsContext, AsContextMut, FuncType, GcRefImpl, GcRootIndex, HeapType, ManuallyRooted, RefType,
6    Result, RootSet, Rooted, ValRaw, ValType, WasmTy, I31,
7};
8use std::num::NonZeroU64;
9use wasmtime_runtime::VMGcRef;
10
11/// An `anyref` GC reference.
12///
13/// The `AnyRef` type represents WebAssembly `anyref` values. These can be
14/// references to `struct`s and `array`s or inline/unboxed 31-bit
15/// integers. Unlike `externref`, Wasm guests can directly allocate `anyref`s.
16///
17/// Like all WebAssembly references, these are opaque and unforgable to Wasm:
18/// they cannot be faked and Wasm cannot, for example, cast the integer
19/// `0x12345678` into a reference, pretend it is a valid `anyref`, and trick the
20/// host into dereferencing it and segfaulting or worse.
21///
22/// Note that you can also use `Rooted<AnyRef>` and `ManuallyRooted<AnyRef>` as
23/// a type parameter with [`Func::typed`][crate::Func::typed]- and
24/// [`Func::wrap`][crate::Func::wrap]-style APIs.
25///
26/// # Example
27///
28/// ```
29/// # use wasmtime::*;
30/// # fn _foo() -> Result<()> {
31/// let mut config = Config::new();
32/// config.wasm_gc(true);
33///
34/// let engine = Engine::new(&config)?;
35///
36/// // Define a module which does stuff with `anyref`s.
37/// let module = Module::new(&engine, r#"
38///     (module
39///         (func (export "increment-if-i31") (param (ref null any)) (result (ref null any))
40///             block
41///                 ;; Try to cast the arg to an `i31`, otherwise branch out
42///                 ;; of this `block`.
43///                 local.get 0
44///                 br_on_cast_fail (ref null any) (ref i31) 0
45///                 ;; Get the `i31`'s inner value and add one to it.
46///                 i31.get_u
47///                 i32.const 1
48///                 i32.add
49///                 ;; Wrap the incremented value back into an `i31` reference and
50///                 ;; return it.
51///                 ref.i31
52///                 return
53///             end
54///
55///             ;; If the `anyref` we were given is not an `i31`, just return it
56///             ;; as-is.
57///             local.get 0
58///         )
59///     )
60/// "#)?;
61///
62/// // Instantiate the module.
63/// let mut store = Store::new(&engine, ());
64/// let instance = Instance::new(&mut store, &module, &[])?;
65///
66/// // Extract the function.
67/// let increment_if_i31 = instance
68///     .get_typed_func::<Option<Rooted<AnyRef>>, Option<Rooted<AnyRef>>>(
69///         &mut store,
70///         "increment-if-i31",
71///     )?;
72///
73/// {
74///     // Create a new scope for the `Rooted` arguments and returns.
75///     let mut scope = RootScope::new(&mut store);
76///
77///     // Call the function with an `i31`.
78///     let arg = AnyRef::from_i31(&mut scope, I31::wrapping_u32(419));
79///     let result = increment_if_i31.call(&mut scope, Some(arg))?;
80///     assert_eq!(result.unwrap().as_i31(&scope)?, Some(I31::wrapping_u32(420)));
81///
82///     // Call the function with something that isn't an `i31`.
83///     let result = increment_if_i31.call(&mut scope, None)?;
84///     assert!(result.is_none());
85/// }
86/// # Ok(())
87/// # }
88/// ```
89#[derive(Debug)]
90#[repr(transparent)]
91pub struct AnyRef {
92    inner: GcRootIndex,
93}
94
95unsafe impl GcRefImpl for AnyRef {
96    #[allow(private_interfaces)]
97    fn transmute_ref(index: &GcRootIndex) -> &Self {
98        // Safety: `AnyRef` is a newtype of a `GcRootIndex`.
99        let me: &Self = unsafe { std::mem::transmute(index) };
100
101        // Assert we really are just a newtype of a `GcRootIndex`.
102        assert!(matches!(
103            me,
104            Self {
105                inner: GcRootIndex { .. },
106            }
107        ));
108
109        me
110    }
111}
112
113impl AnyRef {
114    /// Construct an `anyref` from an `i31`.
115    ///
116    /// # Example
117    ///
118    /// ```
119    /// # use wasmtime::*;
120    /// # fn _foo() -> Result<()> {
121    /// let mut store = Store::<()>::default();
122    ///
123    /// // Create an `i31`.
124    /// let i31 = I31::wrapping_u32(999);
125    ///
126    /// // Convert it into an `anyref`.
127    /// let anyref = AnyRef::from_i31(&mut store, i31);
128    /// # Ok(())
129    /// # }
130    /// ```
131    pub fn from_i31(mut store: impl AsContextMut, value: I31) -> Rooted<Self> {
132        let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
133        Self::_from_i31(&mut store, value)
134    }
135
136    pub(crate) fn _from_i31(store: &mut AutoAssertNoGc<'_>, value: I31) -> Rooted<Self> {
137        let gc_ref = VMGcRef::from_i31(value.runtime_i31());
138        Rooted::new(store, gc_ref)
139    }
140
141    /// Creates a new strongly-owned [`AnyRef`] from the raw value provided.
142    ///
143    /// This is intended to be used in conjunction with [`Func::new_unchecked`],
144    /// [`Func::call_unchecked`], and [`ValRaw`] with its `anyref` field.
145    ///
146    /// This function assumes that `raw` is an `anyref` value which is currently
147    /// rooted within the [`Store`].
148    ///
149    /// # Unsafety
150    ///
151    /// This function is particularly `unsafe` because `raw` not only must be a
152    /// valid `anyref` value produced prior by [`AnyRef::to_raw`] but it must
153    /// also be correctly rooted within the store. When arguments are provided
154    /// to a callback with [`Func::new_unchecked`], for example, or returned via
155    /// [`Func::call_unchecked`], if a GC is performed within the store then
156    /// floating `anyref` values are not rooted and will be GC'd, meaning that
157    /// this function will no longer be safe to call with the values cleaned up.
158    /// This function must be invoked *before* possible GC operations can happen
159    /// (such as calling Wasm).
160    ///
161    /// When in doubt try to not use this. Instead use the safe Rust APIs of
162    /// [`TypedFunc`] and friends.
163    ///
164    /// [`Func::call_unchecked`]: crate::Func::call_unchecked
165    /// [`Func::new_unchecked`]: crate::Func::new_unchecked
166    /// [`Store`]: crate::Store
167    /// [`TypedFunc`]: crate::TypedFunc
168    /// [`ValRaw`]: crate::ValRaw
169    pub unsafe fn from_raw(mut store: impl AsContextMut, raw: u32) -> Option<Rooted<Self>> {
170        let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
171        let gc_ref = VMGcRef::from_raw_u32(raw)?;
172        Some(Self::from_cloned_gc_ref(&mut store, gc_ref))
173    }
174
175    /// Create a new `Rooted<AnyRef>` from the given GC reference.
176    ///
177    /// `gc_ref` should point to a valid `anyref` and should belong to the
178    /// store's GC heap. Failure to uphold these invariants is memory safe but
179    /// will lead to general incorrectness such as panics or wrong results.
180    pub(crate) fn from_cloned_gc_ref(
181        store: &mut AutoAssertNoGc<'_>,
182        gc_ref: VMGcRef,
183    ) -> Rooted<Self> {
184        assert!(gc_ref.is_i31());
185        assert!(VMGcRef::ONLY_EXTERN_REF_AND_I31);
186        Rooted::new(store, gc_ref)
187    }
188
189    /// Converts this [`AnyRef`] to a raw value suitable to store within a
190    /// [`ValRaw`].
191    ///
192    /// Returns an error if this `anyref` has been unrooted.
193    ///
194    /// # Unsafety
195    ///
196    /// Produces a raw value which is only safe to pass into a store if a GC
197    /// doesn't happen between when the value is produce and when it's passed
198    /// into the store.
199    ///
200    /// [`ValRaw`]: crate::ValRaw
201    pub unsafe fn to_raw(&self, mut store: impl AsContextMut) -> Result<u32> {
202        let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
203        let gc_ref = self.inner.try_clone_gc_ref(&mut store)?;
204        let raw = gc_ref.as_raw_u32();
205        store.gc_store_mut()?.expose_gc_ref_to_wasm(gc_ref);
206        Ok(raw)
207    }
208
209    /// Is this `anyref` an `i31`?
210    ///
211    /// Returns an `Err(_)` if this reference has been unrooted.
212    pub fn is_i31(&self, store: impl AsContext) -> Result<bool> {
213        self._is_i31(store.as_context().0)
214    }
215
216    pub(crate) fn _is_i31(&self, store: &StoreOpaque) -> Result<bool> {
217        // NB: Can't use `AutoAssertNoGc` here because we only have a shared
218        // context, not a mutable context.
219        let gc_ref = self.inner.unchecked_try_gc_ref(store)?;
220        Ok(gc_ref.is_i31())
221    }
222
223    /// Downcast this `anyref` to an `i31`.
224    ///
225    /// If this `anyref` is an `i31`, then `Some(_)` is returned.
226    ///
227    /// If this `anyref` is not an `i31`, then `None` is returned.
228    ///
229    /// Returns an `Err(_)` if this reference has been unrooted.
230    pub fn as_i31(&self, store: impl AsContext) -> Result<Option<I31>> {
231        // NB: Can't use `AutoAssertNoGc` here because we only have a shared
232        // context, not a mutable context.
233        let gc_ref = self.inner.unchecked_try_gc_ref(store.as_context().0)?;
234        Ok(gc_ref.as_i31().map(Into::into))
235    }
236
237    /// Downcast this `anyref` to an `i31`, panicking if this `anyref` is not an
238    /// `i31`.
239    ///
240    /// Returns an `Err(_)` if this reference has been unrooted.
241    pub fn unwrap_i31(&self, store: impl AsContext) -> Result<I31> {
242        Ok(self.as_i31(store)?.expect("AnyRef::unwrap_i31 on non-i31"))
243    }
244}
245
246unsafe impl WasmTy for Rooted<AnyRef> {
247    // TODO: this should be `VMGcRef` but Cranelift currently doesn't support
248    // using r32 types when targeting 64-bit platforms.
249    type Abi = NonZeroU64;
250
251    #[inline]
252    fn valtype() -> ValType {
253        ValType::Ref(RefType::new(false, HeapType::Any))
254    }
255
256    #[inline]
257    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
258        self.comes_from_same_store(store)
259    }
260
261    #[inline]
262    fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &FuncType) -> Result<()> {
263        unreachable!()
264    }
265
266    #[inline]
267    fn is_non_i31_gc_ref(&self) -> bool {
268        true
269    }
270
271    #[inline]
272    unsafe fn abi_from_raw(raw: *mut ValRaw) -> Self::Abi {
273        let raw = (*raw).get_externref();
274        debug_assert_ne!(raw, 0);
275        NonZeroU64::new_unchecked(u64::from(raw))
276    }
277
278    #[inline]
279    unsafe fn abi_into_raw(abi: Self::Abi, raw: *mut ValRaw) {
280        let externref = u32::try_from(abi.get()).unwrap();
281        *raw = ValRaw::externref(externref);
282    }
283
284    #[inline]
285    fn into_abi(self, store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi> {
286        let gc_ref = self.inner.try_clone_gc_ref(store)?;
287        let r64 = gc_ref.as_r64();
288        store.gc_store_mut()?.expose_gc_ref_to_wasm(gc_ref);
289        debug_assert_ne!(r64, 0);
290        Ok(unsafe { NonZeroU64::new_unchecked(r64) })
291    }
292
293    #[inline]
294    unsafe fn from_abi(abi: Self::Abi, store: &mut AutoAssertNoGc<'_>) -> Self {
295        let gc_ref = VMGcRef::from_r64(abi.get())
296            .expect("valid r64")
297            .expect("non-null");
298        let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref);
299        AnyRef::from_cloned_gc_ref(store, gc_ref)
300    }
301}
302
303unsafe impl WasmTy for Option<Rooted<AnyRef>> {
304    type Abi = u64;
305
306    #[inline]
307    fn valtype() -> ValType {
308        ValType::ANYREF
309    }
310
311    #[inline]
312    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
313        self.map_or(true, |x| x.comes_from_same_store(store))
314    }
315
316    #[inline]
317    fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &FuncType) -> Result<()> {
318        unreachable!()
319    }
320
321    #[inline]
322    fn is_non_i31_gc_ref(&self) -> bool {
323        true
324    }
325
326    #[inline]
327    unsafe fn abi_from_raw(raw: *mut ValRaw) -> Self::Abi {
328        let externref = (*raw).get_externref();
329        u64::from(externref)
330    }
331
332    #[inline]
333    unsafe fn abi_into_raw(abi: Self::Abi, raw: *mut ValRaw) {
334        let externref = u32::try_from(abi).unwrap();
335        *raw = ValRaw::externref(externref);
336    }
337
338    #[inline]
339    fn into_abi(self, store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi> {
340        Ok(if let Some(x) = self {
341            <Rooted<AnyRef> as WasmTy>::into_abi(x, store)?.get()
342        } else {
343            0
344        })
345    }
346
347    #[inline]
348    unsafe fn from_abi(abi: Self::Abi, store: &mut AutoAssertNoGc<'_>) -> Self {
349        let gc_ref = VMGcRef::from_r64(abi).expect("valid r64")?;
350        let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref);
351        Some(AnyRef::from_cloned_gc_ref(store, gc_ref))
352    }
353}
354
355unsafe impl WasmTy for ManuallyRooted<AnyRef> {
356    type Abi = NonZeroU64;
357
358    #[inline]
359    fn valtype() -> ValType {
360        ValType::Ref(RefType::new(false, HeapType::Any))
361    }
362
363    #[inline]
364    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
365        self.comes_from_same_store(store)
366    }
367
368    #[inline]
369    fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &FuncType) -> Result<()> {
370        unreachable!()
371    }
372
373    #[inline]
374    fn is_non_i31_gc_ref(&self) -> bool {
375        true
376    }
377
378    #[inline]
379    unsafe fn abi_from_raw(raw: *mut ValRaw) -> Self::Abi {
380        let externref = (*raw).get_externref();
381        debug_assert_ne!(externref, 0);
382        NonZeroU64::new_unchecked(u64::from(externref))
383    }
384
385    #[inline]
386    unsafe fn abi_into_raw(abi: Self::Abi, raw: *mut ValRaw) {
387        let externref = u32::try_from(abi.get()).unwrap();
388        *raw = ValRaw::externref(externref);
389    }
390
391    #[inline]
392    fn into_abi(self, store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi> {
393        let gc_ref = self.inner.try_clone_gc_ref(store)?;
394        let r64 = gc_ref.as_r64();
395        store.gc_store_mut()?.expose_gc_ref_to_wasm(gc_ref);
396        Ok(unsafe { NonZeroU64::new_unchecked(r64) })
397    }
398
399    #[inline]
400    unsafe fn from_abi(abi: Self::Abi, store: &mut AutoAssertNoGc<'_>) -> Self {
401        let gc_ref = VMGcRef::from_r64(abi.get())
402            .expect("valid r64")
403            .expect("non-null");
404        let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref);
405        RootSet::with_lifo_scope(store, |store| {
406            let rooted = AnyRef::from_cloned_gc_ref(store, gc_ref);
407            rooted
408                ._to_manually_rooted(store)
409                .expect("rooted is in scope")
410        })
411    }
412}
413
414unsafe impl WasmTy for Option<ManuallyRooted<AnyRef>> {
415    type Abi = u64;
416
417    #[inline]
418    fn valtype() -> ValType {
419        ValType::ANYREF
420    }
421
422    #[inline]
423    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
424        self.as_ref()
425            .map_or(true, |x| x.comes_from_same_store(store))
426    }
427
428    #[inline]
429    fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &FuncType) -> Result<()> {
430        unreachable!()
431    }
432
433    #[inline]
434    fn is_non_i31_gc_ref(&self) -> bool {
435        true
436    }
437
438    #[inline]
439    unsafe fn abi_from_raw(raw: *mut ValRaw) -> Self::Abi {
440        let externref = (*raw).get_externref();
441        u64::from(externref)
442    }
443
444    #[inline]
445    unsafe fn abi_into_raw(abi: Self::Abi, raw: *mut ValRaw) {
446        let externref = u32::try_from(abi).unwrap();
447        *raw = ValRaw::externref(externref);
448    }
449
450    #[inline]
451    fn into_abi(self, store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi> {
452        Ok(if let Some(x) = self {
453            <ManuallyRooted<AnyRef> as WasmTy>::into_abi(x, store)?.get()
454        } else {
455            0
456        })
457    }
458
459    #[inline]
460    unsafe fn from_abi(abi: Self::Abi, store: &mut AutoAssertNoGc<'_>) -> Self {
461        let gc_ref = VMGcRef::from_r64(abi).expect("valid r64")?;
462        let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref);
463        RootSet::with_lifo_scope(store, |store| {
464            let rooted = AnyRef::from_cloned_gc_ref(store, gc_ref);
465            Some(
466                rooted
467                    ._to_manually_rooted(store)
468                    .expect("rooted is in scope"),
469            )
470        })
471    }
472}