Skip to main content

wasmtime_c_api/
val.rs

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