Skip to main content

wasmtime_c_api/
anyref.rs

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