Skip to main content

wasmtime_c_api/
anyref.rs

1use crate::{
2    WasmtimeStoreContextMut, wasmtime_arrayref_t, wasmtime_eqref_t, wasmtime_heaptype_t,
3    wasmtime_structref_t,
4};
5use std::mem::MaybeUninit;
6use wasmtime::{AnyRef, ArrayRef, EqRef, I31, OwnedRooted, RootScope, StructRef};
7
8/// C-API representation of `anyref`.
9///
10/// This represented differently in the C API from the header to handle how
11/// this is dispatched internally. Null anyref values are represented with a
12/// `store_id` of zero, and otherwise the `rooted` field is valid.
13///
14/// Note that this relies on the Wasmtime definition of `OwnedRooted` to have
15/// a 64-bit store_id first.
16macro_rules! ref_wrapper {
17    ({
18        wasmtime: $wasmtime:ident,
19        capi: $c:ident,
20        clone: $clone:ident,
21        unroot: $unroot:ident,
22        $(
23            to_raw: $to_raw:ident,
24            from_raw: $from_raw:ident,
25        )?
26   }) => {
27        pub struct $c {
28            store_id: u64,
29            a: u32,
30            b: u32,
31            c: *const (),
32        }
33
34        impl $c {
35            pub unsafe fn as_wasmtime(&self) -> Option<wasmtime::OwnedRooted<$wasmtime>> {
36                let store_id = std::num::NonZeroU64::new(self.store_id)?;
37                Some(wasmtime::OwnedRooted::from_borrowed_raw_parts_for_c_api(
38                    store_id, self.a, self.b, self.c,
39                ))
40            }
41
42            pub unsafe fn into_wasmtime(self) -> Option<wasmtime::OwnedRooted<$wasmtime>> {
43                std::mem::ManuallyDrop::new(self).to_owned()
44            }
45
46            unsafe fn to_owned(&self) -> Option<wasmtime::OwnedRooted<$wasmtime>> {
47                let store_id = std::num::NonZeroU64::new(self.store_id)?;
48                Some(wasmtime::OwnedRooted::from_owned_raw_parts_for_c_api(
49                    store_id, self.a, self.b, self.c,
50                ))
51            }
52        }
53
54        impl Drop for $c {
55            fn drop(&mut self) {
56                unsafe {
57                    let _ = self.to_owned();
58                }
59            }
60        }
61
62        impl From<Option<wasmtime::OwnedRooted<$wasmtime>>> for $c {
63            fn from(rooted: Option<wasmtime::OwnedRooted<$wasmtime>>) -> $c {
64                let mut ret = $c {
65                    store_id: 0,
66                    a: 0,
67                    b: 0,
68                    c: core::ptr::null(),
69                };
70                if let Some(rooted) = rooted {
71                    let (store_id, a, b, c) = rooted.into_parts_for_c_api();
72                    ret.store_id = store_id.get();
73                    ret.a = a;
74                    ret.b = b;
75                    ret.c = c;
76                }
77                ret
78            }
79        }
80
81        impl From<wasmtime::OwnedRooted<$wasmtime>> for $c {
82            fn from(rooted: wasmtime::OwnedRooted<$wasmtime>) -> $c {
83                Self::from(Some(rooted))
84            }
85        }
86
87        // SAFETY: The `*const ()` comes from (and is converted back
88        // into) an `Arc<()>`, and is only accessed as such, so this
89        // type is both Send and Sync. These constraints are necessary
90        // in the async machinery in this crate.
91        unsafe impl Send for $c {}
92        unsafe impl Sync for $c {}
93
94        #[unsafe(no_mangle)]
95        pub unsafe extern "C" fn $clone(anyref: Option<&$c>, out: &mut std::mem::MaybeUninit<$c>) {
96            let anyref = anyref.and_then(|a| a.as_wasmtime());
97            out.write(anyref.into());
98        }
99
100        #[unsafe(no_mangle)]
101        pub unsafe extern "C" fn $unroot(val: Option<&mut std::mem::ManuallyDrop<$c>>) {
102            if let Some(val) = val {
103                unsafe {
104                    std::mem::ManuallyDrop::drop(val);
105                }
106            }
107        }
108
109        $(
110            #[unsafe(no_mangle)]
111            pub unsafe extern "C" fn $to_raw(
112                cx: crate::WasmtimeStoreContextMut<'_>,
113                val: Option<&$c>,
114            ) -> u32 {
115                val.and_then(|v| v.as_wasmtime())
116                    .and_then(|e| e.to_raw(cx).ok())
117                    .unwrap_or_default()
118            }
119
120            #[unsafe(no_mangle)]
121            pub unsafe extern "C" fn $from_raw(
122                cx: crate::WasmtimeStoreContextMut<'_>,
123                raw: u32,
124                val: &mut std::mem::MaybeUninit<$c>,
125            ) {
126                let mut scope = wasmtime::RootScope::new(cx);
127                let anyref = $wasmtime::from_raw(&mut scope, raw)
128                    .map(|a| a.to_owned_rooted(&mut scope).expect("in scope"));
129                crate::initialize(val, anyref.into());
130            }
131        )?
132    };
133}
134pub(crate) use ref_wrapper;
135
136ref_wrapper!({
137    wasmtime: AnyRef,
138    capi: wasmtime_anyref_t,
139    clone: wasmtime_anyref_clone,
140    unroot: wasmtime_anyref_unroot,
141    to_raw: wasmtime_anyref_to_raw,
142    from_raw: wasmtime_anyref_from_raw,
143});
144
145#[unsafe(no_mangle)]
146pub extern "C" fn wasmtime_anyref_from_i31(
147    cx: WasmtimeStoreContextMut<'_>,
148    val: u32,
149    out: &mut MaybeUninit<wasmtime_anyref_t>,
150) {
151    let mut scope = RootScope::new(cx);
152    let anyref = AnyRef::from_i31(&mut scope, I31::wrapping_u32(val));
153    let anyref = anyref.to_owned_rooted(&mut scope).expect("in scope");
154    crate::initialize(out, Some(anyref).into())
155}
156
157#[unsafe(no_mangle)]
158pub unsafe extern "C" fn wasmtime_anyref_is_i31(
159    cx: WasmtimeStoreContextMut<'_>,
160    anyref: Option<&wasmtime_anyref_t>,
161) -> bool {
162    match anyref.and_then(|a| a.as_wasmtime()) {
163        Some(anyref) => anyref.is_i31(&cx).expect("OwnedRooted always in scope"),
164        None => false,
165    }
166}
167
168#[unsafe(no_mangle)]
169pub unsafe extern "C" fn wasmtime_anyref_i31_get_u(
170    cx: WasmtimeStoreContextMut<'_>,
171    anyref: Option<&wasmtime_anyref_t>,
172    dst: &mut MaybeUninit<u32>,
173) -> bool {
174    match anyref.and_then(|a| a.as_wasmtime()) {
175        Some(anyref) if anyref.is_i31(&cx).expect("OwnedRooted always in scope") => {
176            let val = anyref
177                .unwrap_i31(&cx)
178                .expect("OwnedRooted always in scope")
179                .get_u32();
180            crate::initialize(dst, val);
181            true
182        }
183        _ => false,
184    }
185}
186
187#[unsafe(no_mangle)]
188pub unsafe extern "C" fn wasmtime_anyref_i31_get_s(
189    cx: WasmtimeStoreContextMut<'_>,
190    anyref: Option<&wasmtime_anyref_t>,
191    dst: &mut MaybeUninit<i32>,
192) -> bool {
193    match anyref.and_then(|a| a.as_wasmtime()) {
194        Some(anyref) if anyref.is_i31(&cx).expect("OwnedRooted always in scope") => {
195            let val = anyref
196                .unwrap_i31(&cx)
197                .expect("OwnedRooted always in scope")
198                .get_i32();
199            crate::initialize(dst, val);
200            true
201        }
202        _ => false,
203    }
204}
205
206#[unsafe(no_mangle)]
207pub unsafe extern "C" fn wasmtime_anyref_is_eqref(
208    cx: WasmtimeStoreContextMut<'_>,
209    anyref: Option<&wasmtime_anyref_t>,
210) -> bool {
211    match anyref.and_then(|a| a.as_wasmtime()) {
212        Some(anyref) => anyref.is_eqref(&cx).expect("OwnedRooted always in scope"),
213        None => false,
214    }
215}
216
217#[unsafe(no_mangle)]
218pub unsafe extern "C" fn wasmtime_anyref_as_eqref(
219    mut cx: WasmtimeStoreContextMut<'_>,
220    anyref: Option<&wasmtime_anyref_t>,
221    out: &mut MaybeUninit<wasmtime_eqref_t>,
222) -> bool {
223    if let Some(anyref) = anyref.and_then(|a| a.as_wasmtime()) {
224        let mut scope = RootScope::new(&mut cx);
225        let rooted = anyref.to_rooted(&mut scope);
226        if let Ok(Some(eqref)) = rooted.as_eqref(&mut scope) {
227            let owned = eqref.to_owned_rooted(&mut scope).expect("in scope");
228            crate::initialize(out, Some(owned).into());
229            return true;
230        }
231    }
232    crate::initialize(out, None::<OwnedRooted<EqRef>>.into());
233    false
234}
235
236#[unsafe(no_mangle)]
237pub unsafe extern "C" fn wasmtime_anyref_is_struct(
238    cx: WasmtimeStoreContextMut<'_>,
239    anyref: Option<&wasmtime_anyref_t>,
240) -> bool {
241    match anyref.and_then(|a| a.as_wasmtime()) {
242        Some(anyref) => anyref.is_struct(&cx).expect("OwnedRooted always in scope"),
243        None => false,
244    }
245}
246
247#[unsafe(no_mangle)]
248pub unsafe extern "C" fn wasmtime_anyref_as_struct(
249    mut cx: WasmtimeStoreContextMut<'_>,
250    anyref: Option<&wasmtime_anyref_t>,
251    out: &mut MaybeUninit<wasmtime_structref_t>,
252) -> bool {
253    if let Some(anyref) = anyref.and_then(|a| a.as_wasmtime()) {
254        let mut scope = RootScope::new(&mut cx);
255        let rooted = anyref.to_rooted(&mut scope);
256        if let Ok(Some(structref)) = rooted.as_struct(&scope) {
257            let owned = structref.to_owned_rooted(&mut scope).expect("in scope");
258            crate::initialize(out, Some(owned).into());
259            return true;
260        }
261    }
262    crate::initialize(out, None::<OwnedRooted<StructRef>>.into());
263    false
264}
265
266#[unsafe(no_mangle)]
267pub unsafe extern "C" fn wasmtime_anyref_is_array(
268    cx: WasmtimeStoreContextMut<'_>,
269    anyref: Option<&wasmtime_anyref_t>,
270) -> bool {
271    match anyref.and_then(|a| a.as_wasmtime()) {
272        Some(anyref) => anyref.is_array(&cx).expect("OwnedRooted always in scope"),
273        None => false,
274    }
275}
276
277#[unsafe(no_mangle)]
278pub unsafe extern "C" fn wasmtime_anyref_as_array(
279    mut cx: WasmtimeStoreContextMut<'_>,
280    anyref: Option<&wasmtime_anyref_t>,
281    out: &mut MaybeUninit<wasmtime_arrayref_t>,
282) -> bool {
283    if let Some(anyref) = anyref.and_then(|a| a.as_wasmtime()) {
284        let mut scope = RootScope::new(&mut cx);
285        let rooted = anyref.to_rooted(&mut scope);
286        if let Ok(Some(arrayref)) = rooted.as_array(&scope) {
287            let owned = arrayref.to_owned_rooted(&mut scope).expect("in scope");
288            crate::initialize(out, Some(owned).into());
289            return true;
290        }
291    }
292    crate::initialize(out, None::<OwnedRooted<ArrayRef>>.into());
293    false
294}
295
296#[unsafe(no_mangle)]
297pub unsafe extern "C" fn wasmtime_anyref_type(
298    mut cx: WasmtimeStoreContextMut<'_>,
299    anyref: Option<&wasmtime_anyref_t>,
300    out: &mut MaybeUninit<wasmtime_heaptype_t>,
301) -> bool {
302    match anyref.and_then(|a| a.as_wasmtime()) {
303        Some(anyref) => {
304            let ty = anyref.ty(&mut cx).expect("should be rooted");
305            out.write(ty.into());
306            true
307        }
308        None => false,
309    }
310}