Skip to main content

wasmtime_c_api/
val.rs

1#[cfg(feature = "gc")]
2use crate::r#ref::ref_to_val;
3#[cfg(feature = "gc")]
4use crate::{
5    WASM_EXTERNREF, WASM_FUNCREF, wasm_ref_t, wasmtime_anyref_t, wasmtime_exnref_t,
6    wasmtime_externref_t,
7};
8use crate::{WASM_F32, WASM_F64, WASM_I32, WASM_I64, wasm_valkind_t};
9use std::mem::{ManuallyDrop, MaybeUninit};
10use std::ptr;
11use wasmtime::{AsContextMut, Func, Val};
12#[cfg(feature = "gc")]
13use wasmtime::{Ref, RootScope};
14
15#[repr(C)]
16pub struct wasm_val_t {
17    pub kind: wasm_valkind_t,
18    pub of: wasm_val_union,
19}
20
21#[repr(C)]
22#[derive(Copy, Clone)]
23pub union wasm_val_union {
24    pub i32: i32,
25    pub i64: i64,
26    pub u32: u32,
27    pub u64: u64,
28    pub f32: f32,
29    pub f64: f64,
30    #[cfg(feature = "gc")]
31    pub ref_: *mut wasm_ref_t,
32}
33
34#[cfg(feature = "gc")]
35impl Drop for wasm_val_t {
36    fn drop(&mut self) {
37        match self.kind {
38            WASM_FUNCREF | WASM_EXTERNREF => unsafe {
39                if !self.of.ref_.is_null() {
40                    drop(Box::from_raw(self.of.ref_));
41                }
42            },
43            _ => {}
44        }
45    }
46}
47
48impl Clone for wasm_val_t {
49    fn clone(&self) -> Self {
50        #[allow(
51            unused_mut,
52            reason = "needed for conditional mutation under cfg(feature = \"gc\")"
53        )]
54        let mut ret = wasm_val_t {
55            kind: self.kind,
56            of: self.of,
57        };
58
59        #[cfg(feature = "gc")]
60        unsafe {
61            match self.kind {
62                WASM_FUNCREF | WASM_EXTERNREF if !self.of.ref_.is_null() => {
63                    ret.of.ref_ = Box::into_raw(Box::new((*self.of.ref_).clone()));
64                }
65                _ => {}
66            }
67        }
68
69        return ret;
70    }
71}
72
73impl Default for wasm_val_t {
74    fn default() -> Self {
75        wasm_val_t {
76            kind: WASM_I32,
77            of: wasm_val_union { i32: 0 },
78        }
79    }
80}
81
82impl wasm_val_t {
83    pub fn from_val(val: Val) -> wasm_val_t {
84        match val {
85            Val::I32(i) => wasm_val_t {
86                kind: WASM_I32,
87                of: wasm_val_union { i32: i },
88            },
89            Val::I64(i) => wasm_val_t {
90                kind: WASM_I64,
91                of: wasm_val_union { i64: i },
92            },
93            Val::F32(f) => wasm_val_t {
94                kind: WASM_F32,
95                of: wasm_val_union { u32: f },
96            },
97            Val::F64(f) => wasm_val_t {
98                kind: WASM_F64,
99                of: wasm_val_union { u64: f },
100            },
101            #[cfg(feature = "gc")]
102            Val::FuncRef(f) => wasm_val_t {
103                kind: WASM_FUNCREF,
104                of: wasm_val_union {
105                    ref_: f.map_or(ptr::null_mut(), |f| {
106                        Box::into_raw(Box::new(wasm_ref_t {
107                            r: Ref::Func(Some(f)),
108                        }))
109                    }),
110                },
111            },
112            #[cfg(not(feature = "gc"))]
113            Val::FuncRef(_) => crate::abort("creating a wasm_val_t from a funcref"),
114            Val::AnyRef(_) => crate::abort("creating a wasm_val_t from an anyref"),
115            Val::ExternRef(_) => crate::abort("creating a wasm_val_t from an externref"),
116            Val::ExnRef(_) => crate::abort("creating a wasm_val_t from  an exnref"),
117            Val::V128(_) => crate::abort("creating a wasm_val_t from a v128"),
118            Val::ContRef(_) => crate::abort("creating a wasm_val_t from a contref"),
119        }
120    }
121
122    pub fn val(&self) -> Val {
123        match self.kind {
124            WASM_I32 => Val::from(unsafe { self.of.i32 }),
125            WASM_I64 => Val::from(unsafe { self.of.i64 }),
126            WASM_F32 => Val::from(unsafe { self.of.f32 }),
127            WASM_F64 => Val::from(unsafe { self.of.f64 }),
128            #[cfg(feature = "gc")]
129            WASM_FUNCREF => unsafe {
130                if self.of.ref_.is_null() {
131                    Val::FuncRef(None)
132                } else {
133                    ref_to_val(&*self.of.ref_)
134                }
135            },
136            _ => crate::abort("unsupported val discriminant"),
137        }
138    }
139}
140
141#[unsafe(no_mangle)]
142pub unsafe extern "C" fn wasm_val_copy(out: &mut MaybeUninit<wasm_val_t>, source: &wasm_val_t) {
143    crate::initialize(out, source.clone());
144}
145
146#[unsafe(no_mangle)]
147pub unsafe extern "C" fn wasm_val_delete(val: *mut wasm_val_t) {
148    ptr::drop_in_place(val);
149}
150
151#[repr(C)]
152pub struct wasmtime_val_t {
153    pub kind: wasmtime_valkind_t,
154    pub of: wasmtime_val_union,
155}
156
157pub type wasmtime_valkind_t = u8;
158pub const WASMTIME_I32: wasmtime_valkind_t = 0;
159pub const WASMTIME_I64: wasmtime_valkind_t = 1;
160pub const WASMTIME_F32: wasmtime_valkind_t = 2;
161pub const WASMTIME_F64: wasmtime_valkind_t = 3;
162pub const WASMTIME_V128: wasmtime_valkind_t = 4;
163pub const WASMTIME_FUNCREF: wasmtime_valkind_t = 5;
164pub const WASMTIME_EXTERNREF: wasmtime_valkind_t = 6;
165pub const WASMTIME_ANYREF: wasmtime_valkind_t = 7;
166pub const WASMTIME_EXNREF: wasmtime_valkind_t = 8;
167
168#[repr(C)]
169pub union wasmtime_val_union {
170    pub i32: i32,
171    pub i64: i64,
172    pub f32: u32,
173    pub f64: u64,
174    #[cfg(feature = "gc")]
175    pub anyref: ManuallyDrop<wasmtime_anyref_t>,
176    #[cfg(feature = "gc")]
177    pub externref: ManuallyDrop<wasmtime_externref_t>,
178    #[cfg(feature = "gc")]
179    pub exnref: ManuallyDrop<wasmtime_exnref_t>,
180    #[cfg(feature = "gc")]
181    pub funcref: wasmtime_func_t,
182    pub v128: [u8; 16],
183}
184
185const _: () = {
186    // This is forced to 24 or 20 bytes by `anyref` and `externref`.
187    assert!(std::mem::size_of::<wasmtime_val_union>() <= 24);
188    assert!(std::mem::align_of::<wasmtime_val_union>() == std::mem::align_of::<u64>());
189};
190
191#[cfg(feature = "gc")]
192impl Drop for wasmtime_val_t {
193    fn drop(&mut self) {
194        unsafe {
195            match self.kind {
196                crate::WASMTIME_ANYREF => {
197                    let _ = ManuallyDrop::take(&mut self.of.anyref);
198                }
199                crate::WASMTIME_EXTERNREF => {
200                    let _ = ManuallyDrop::take(&mut self.of.externref);
201                }
202                crate::WASMTIME_EXNREF => {
203                    let _ = ManuallyDrop::take(&mut self.of.exnref);
204                }
205                _ => {}
206            }
207        }
208    }
209}
210
211// The raw pointers are actually optional boxes.
212#[cfg(feature = "gc")]
213unsafe impl Send for wasmtime_val_union
214where
215    Option<Box<wasmtime_anyref_t>>: Send,
216    Option<Box<wasmtime_externref_t>>: Send,
217    Option<Box<wasmtime_exnref_t>>: Send,
218{
219}
220#[cfg(feature = "gc")]
221unsafe impl Sync for wasmtime_val_union
222where
223    Option<Box<wasmtime_anyref_t>>: Sync,
224    Option<Box<wasmtime_externref_t>>: Sync,
225    Option<Box<wasmtime_exnref_t>>: Sync,
226{
227}
228
229#[repr(C)]
230#[derive(Clone, Copy)]
231pub union wasmtime_func_t {
232    store_id: u64,
233    func: Func,
234}
235
236impl wasmtime_func_t {
237    #[cfg(feature = "gc")]
238    unsafe fn as_wasmtime(&self) -> Option<Func> {
239        if self.store_id == 0 {
240            None
241        } else {
242            Some(self.func)
243        }
244    }
245}
246
247impl From<Option<Func>> for wasmtime_func_t {
248    fn from(func: Option<Func>) -> wasmtime_func_t {
249        match func {
250            Some(func) => wasmtime_func_t { func },
251            None => wasmtime_func_t { store_id: 0 },
252        }
253    }
254}
255
256impl wasmtime_val_t {
257    /// Creates a new `wasmtime_val_t` from a `wasmtime::Val`.
258    ///
259    /// Note that this requires a `RootScope` to be present to serve as proof
260    /// that `val` is not require to be rooted in the store itself which would
261    /// prevent GC. Callers should prefer this API where possible, creating a
262    /// temporary `RootScope` when needed.
263    #[cfg(feature = "gc")]
264    pub fn from_val(cx: &mut RootScope<impl AsContextMut>, val: Val) -> wasmtime_val_t {
265        Self::from_val_unscoped(cx, val)
266    }
267
268    /// Creates a new `wasmtime_val_t` from a `wasmtime::Val` (non-gc version).
269    ///
270    /// When gc feature is disabled, this does not require a `RootScope`.
271    #[cfg(not(feature = "gc"))]
272    pub fn from_val(cx: impl AsContextMut, val: Val) -> wasmtime_val_t {
273        Self::from_val_unscoped(cx, val)
274    }
275
276    /// Equivalent of [`wasmtime_val_t::from_val`] except that a `RootScope`
277    /// is not required.
278    ///
279    /// This method should only be used when a `RootScope` is known to be
280    /// elsewhere on the stack. For example this is used when we call back out
281    /// to the embedder. In such a situation we know we previously entered with
282    /// some other call so the root scope is on the stack there.
283    pub fn from_val_unscoped(cx: impl AsContextMut, val: Val) -> wasmtime_val_t {
284        #[cfg(not(feature = "gc"))]
285        let _ = cx;
286        match val {
287            Val::I32(i) => wasmtime_val_t {
288                kind: crate::WASMTIME_I32,
289                of: wasmtime_val_union { i32: i },
290            },
291            Val::I64(i) => wasmtime_val_t {
292                kind: crate::WASMTIME_I64,
293                of: wasmtime_val_union { i64: i },
294            },
295            Val::F32(i) => wasmtime_val_t {
296                kind: crate::WASMTIME_F32,
297                of: wasmtime_val_union { f32: i },
298            },
299            Val::F64(i) => wasmtime_val_t {
300                kind: crate::WASMTIME_F64,
301                of: wasmtime_val_union { f64: i },
302            },
303            #[cfg(feature = "gc")]
304            Val::AnyRef(a) => wasmtime_val_t {
305                kind: crate::WASMTIME_ANYREF,
306                of: wasmtime_val_union {
307                    anyref: ManuallyDrop::new(a.and_then(|a| a.to_owned_rooted(cx).ok()).into()),
308                },
309            },
310            #[cfg(feature = "gc")]
311            Val::ExternRef(e) => wasmtime_val_t {
312                kind: crate::WASMTIME_EXTERNREF,
313                of: wasmtime_val_union {
314                    externref: ManuallyDrop::new(e.and_then(|e| e.to_owned_rooted(cx).ok()).into()),
315                },
316            },
317            #[cfg(feature = "gc")]
318            Val::FuncRef(func) => wasmtime_val_t {
319                kind: crate::WASMTIME_FUNCREF,
320                of: wasmtime_val_union {
321                    funcref: func.into(),
322                },
323            },
324            #[cfg(feature = "gc")]
325            Val::ExnRef(e) => wasmtime_val_t {
326                kind: crate::WASMTIME_EXNREF,
327                of: wasmtime_val_union {
328                    exnref: ManuallyDrop::new(e.and_then(|e| e.to_owned_rooted(cx).ok()).into()),
329                },
330            },
331            #[cfg(not(feature = "gc"))]
332            Val::AnyRef(_) | Val::ExternRef(_) | Val::FuncRef(_) | Val::ExnRef(_) => {
333                crate::abort("reference types require gc feature")
334            }
335            Val::V128(val) => wasmtime_val_t {
336                kind: crate::WASMTIME_V128,
337                of: wasmtime_val_union {
338                    v128: val.as_u128().to_le_bytes(),
339                },
340            },
341            Val::ContRef(_) => crate::abort("contrefs not yet supported in C API (#10248)"),
342        }
343    }
344
345    /// Convert this `wasmtime_val_t` into a `wasmtime::Val`.
346    ///
347    /// See [`wasmtime_val_t::from_val`] for notes on the `RootScope`
348    /// requirement here. Note that this is particularly meaningful for this
349    /// API as the `Val` returned may contain a `Rooted<T>` which requires a
350    /// `RootScope` if we don't want the value to live for the entire lifetime
351    /// of the `Store`.
352    #[cfg(feature = "gc")]
353    pub unsafe fn to_val(&self, cx: &mut RootScope<impl AsContextMut>) -> Val {
354        self.to_val_unscoped(cx)
355    }
356
357    /// Convert this `wasmtime_val_t` into a `wasmtime::Val` (non-gc version).
358    ///
359    /// When gc feature is disabled, this does not require a `RootScope`.
360    #[cfg(not(feature = "gc"))]
361    pub unsafe fn to_val(&self, cx: impl AsContextMut) -> Val {
362        self.to_val_unscoped(cx)
363    }
364
365    /// Equivalent of `to_val` except doesn't require a `RootScope`.
366    ///
367    /// See notes on [`wasmtime_val_t::from_val_unscoped`] for notes on when to
368    /// use this.
369    pub unsafe fn to_val_unscoped(&self, cx: impl AsContextMut) -> Val {
370        #[cfg(not(feature = "gc"))]
371        let _ = cx;
372        match self.kind {
373            crate::WASMTIME_I32 => Val::I32(self.of.i32),
374            crate::WASMTIME_I64 => Val::I64(self.of.i64),
375            crate::WASMTIME_F32 => Val::F32(self.of.f32),
376            crate::WASMTIME_F64 => Val::F64(self.of.f64),
377            crate::WASMTIME_V128 => Val::V128(u128::from_le_bytes(self.of.v128).into()),
378            #[cfg(feature = "gc")]
379            crate::WASMTIME_ANYREF => {
380                Val::AnyRef(self.of.anyref.as_wasmtime().map(|a| a.to_rooted(cx)))
381            }
382            #[cfg(feature = "gc")]
383            crate::WASMTIME_EXTERNREF => {
384                Val::ExternRef(self.of.externref.as_wasmtime().map(|e| e.to_rooted(cx)))
385            }
386            #[cfg(feature = "gc")]
387            crate::WASMTIME_FUNCREF => Val::FuncRef(self.of.funcref.as_wasmtime()),
388            #[cfg(feature = "gc")]
389            crate::WASMTIME_EXNREF => {
390                Val::ExnRef(self.of.exnref.as_wasmtime().map(|e| e.to_rooted(cx)))
391            }
392            other => panic!("unknown wasmtime_valkind_t: {other}"),
393        }
394    }
395}
396
397#[unsafe(no_mangle)]
398pub unsafe extern "C" fn wasmtime_val_unroot(val: &mut ManuallyDrop<wasmtime_val_t>) {
399    ManuallyDrop::drop(val);
400}
401
402#[unsafe(no_mangle)]
403pub unsafe extern "C" fn wasmtime_val_clone(
404    src: &wasmtime_val_t,
405    dst: &mut MaybeUninit<wasmtime_val_t>,
406) {
407    let of = match src.kind {
408        #[cfg(feature = "gc")]
409        crate::WASMTIME_ANYREF => wasmtime_val_union {
410            anyref: ManuallyDrop::new(src.of.anyref.as_wasmtime().into()),
411        },
412        #[cfg(feature = "gc")]
413        crate::WASMTIME_EXTERNREF => wasmtime_val_union {
414            externref: ManuallyDrop::new(src.of.externref.as_wasmtime().into()),
415        },
416        #[cfg(feature = "gc")]
417        crate::WASMTIME_EXNREF => wasmtime_val_union {
418            exnref: ManuallyDrop::new(src.of.exnref.as_wasmtime().into()),
419        },
420        crate::WASMTIME_I32 => wasmtime_val_union { i32: src.of.i32 },
421        crate::WASMTIME_I64 => wasmtime_val_union { i64: src.of.i64 },
422        crate::WASMTIME_F32 => wasmtime_val_union { f32: src.of.f32 },
423        crate::WASMTIME_F64 => wasmtime_val_union { f64: src.of.f64 },
424        crate::WASMTIME_V128 => wasmtime_val_union { v128: src.of.v128 },
425        #[cfg(feature = "gc")]
426        crate::WASMTIME_FUNCREF => wasmtime_val_union {
427            funcref: src.of.funcref,
428        },
429        _ => unreachable!(),
430    };
431    dst.write(wasmtime_val_t { kind: src.kind, of });
432}