wasmer_c_api/wasm_c_api/
value.rs

1use super::store::StoreRef;
2use super::types::{wasm_ref_t, wasm_valkind_enum};
3use std::convert::{TryFrom, TryInto};
4use wasmer_api::Value;
5
6/// Represents the kind of values. The variants of this C enum is
7/// defined in `wasm.h` to list the following:
8///
9/// * `WASM_I32`, a 32-bit integer,
10/// * `WASM_I64`, a 64-bit integer,
11/// * `WASM_F32`, a 32-bit float,
12/// * `WASM_F64`, a 64-bit float,
13/// * `WASM_EXTERNREF`, a WebAssembly reference,
14/// * `WASM_FUNCREF`, a WebAssembly reference.
15#[allow(non_camel_case_types)]
16pub type wasm_valkind_t = u8;
17
18/// A Rust union, compatible with C, that holds a value of kind
19/// [`wasm_valkind_t`] (see [`wasm_val_t`] to get the complete
20/// picture). Members of the union are:
21///
22/// * `int32_t` if the value is a 32-bit integer,
23/// * `int64_t` if the value is a 64-bit integer,
24/// * `float32_t` if the value is a 32-bit float,
25/// * `float64_t` if the value is a 64-bit float,
26/// * `wref` (`wasm_ref_t`) if the value is a WebAssembly reference.
27#[allow(non_camel_case_types)]
28#[derive(Clone, Copy)]
29pub union wasm_val_inner {
30    pub(crate) int32_t: i32,
31    pub(crate) int64_t: i64,
32    pub(crate) float32_t: f32,
33    pub(crate) float64_t: f64,
34    pub(crate) wref: *mut wasm_ref_t,
35}
36
37/// A WebAssembly value composed of its type and its value.
38///
39/// Note that `wasm.h` defines macros to create Wasm values more
40/// easily: `WASM_I32_VAL`, `WASM_I64_VAL`, `WASM_F32_VAL`,
41/// `WASM_F64_VAL`, and `WASM_REF_VAL`.
42///
43/// # Example
44///
45/// ```rust
46/// # use inline_c::assert_c;
47/// # fn main() {
48/// #    (assert_c! {
49/// # #include "tests/wasmer.h"
50/// #
51/// int main() {
52///     // Create a 32-bit integer Wasm value.
53///     wasm_val_t value1 = {
54///         .kind = WASM_I32,
55///         .of = { .i32 = 7 },
56///     };
57///
58///     // Create the same value with the `wasm.h` macro.
59///     wasm_val_t value2 = WASM_I32_VAL(7);
60///
61///     assert(value2.kind == WASM_I32);
62///     assert(value1.of.i32 == value2.of.i32);
63///
64///     return 0;
65/// }
66/// #    })
67/// #    .success();
68/// # }
69/// ```
70#[allow(non_camel_case_types)]
71#[repr(C)]
72pub struct wasm_val_t {
73    /// The kind of the value.
74    pub kind: wasm_valkind_t,
75
76    /// The real value.
77    pub of: wasm_val_inner,
78}
79
80impl std::fmt::Debug for wasm_val_t {
81    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
82        let mut ds = f.debug_struct("wasm_val_t");
83        ds.field("kind", &self.kind);
84
85        match self.kind.try_into() {
86            Ok(wasm_valkind_enum::WASM_I32) => {
87                ds.field("i32", &unsafe { self.of.int32_t });
88            }
89            Ok(wasm_valkind_enum::WASM_I64) => {
90                ds.field("i64", &unsafe { self.of.int64_t });
91            }
92            Ok(wasm_valkind_enum::WASM_F32) => {
93                ds.field("f32", &unsafe { self.of.float32_t });
94            }
95            Ok(wasm_valkind_enum::WASM_F64) => {
96                ds.field("f64", &unsafe { self.of.float64_t });
97            }
98            Ok(wasm_valkind_enum::WASM_EXTERNREF) => {
99                ds.field("anyref", &unsafe { self.of.wref });
100            }
101            Ok(wasm_valkind_enum::WASM_FUNCREF) => {
102                ds.field("funcref", &unsafe { self.of.wref });
103            }
104            Ok(wasm_valkind_enum::WASM_EXNREF) => {
105                ds.field("exnref", &unsafe { self.of.wref });
106            }
107            Err(_) => {
108                ds.field("value", &"Invalid value type");
109            }
110        }
111        ds.finish()
112    }
113}
114
115wasm_declare_vec!(val);
116
117impl Clone for wasm_val_t {
118    fn clone(&self) -> Self {
119        // Reference values own their boxed `wasm_ref_t`, so a shallow copy of
120        // the pointer would double-free on drop. Deep-copy the box instead.
121        // Kept in sync with `Drop`, which only frees EXTERNREF/FUNCREF (EXNREF
122        // is never boxed, so it stays a plain, non-owning copy).
123        match self.kind.try_into() {
124            Ok(wasm_valkind_enum::WASM_EXTERNREF) | Ok(wasm_valkind_enum::WASM_FUNCREF) => {
125                let wref = unsafe { self.of.wref };
126                let cloned = if wref.is_null() {
127                    std::ptr::null_mut()
128                } else {
129                    Box::into_raw(Box::new(unsafe { &*wref }.clone()))
130                };
131                wasm_val_t {
132                    kind: self.kind,
133                    of: wasm_val_inner { wref: cloned },
134                }
135            }
136            _ => wasm_val_t {
137                kind: self.kind,
138                of: self.of,
139            },
140        }
141    }
142}
143
144impl Default for wasm_val_t {
145    fn default() -> Self {
146        Self {
147            kind: wasm_valkind_enum::WASM_I64 as _,
148            of: wasm_val_inner { int64_t: 0 },
149        }
150    }
151}
152
153#[unsafe(no_mangle)]
154pub unsafe extern "C" fn wasm_val_copy(
155    // own
156    out: &mut wasm_val_t,
157    val: &wasm_val_t,
158) {
159    // `out` is an owned (uninitialized) out-parameter, so write into it without
160    // running `Drop` on its prior (stale) contents. `Clone` deep-copies refs.
161    unsafe { std::ptr::write(out, val.clone()) };
162}
163
164impl Drop for wasm_val_t {
165    fn drop(&mut self) {
166        let kind: Result<wasm_valkind_enum, _> = self.kind.try_into();
167        match kind {
168            Ok(wasm_valkind_enum::WASM_EXTERNREF) | Ok(wasm_valkind_enum::WASM_FUNCREF) => unsafe {
169                if !self.of.wref.is_null() {
170                    drop(Box::from_raw(self.of.wref));
171                }
172            },
173            _ => {}
174        }
175    }
176}
177
178#[unsafe(no_mangle)]
179pub unsafe extern "C" fn wasm_val_delete(val: *mut wasm_val_t) {
180    if !val.is_null() {
181        unsafe {
182            std::ptr::drop_in_place(val);
183        }
184    }
185}
186
187impl TryFrom<wasm_valkind_t> for wasm_valkind_enum {
188    type Error = &'static str;
189
190    fn try_from(item: wasm_valkind_t) -> Result<Self, Self::Error> {
191        Ok(match item {
192            0 => wasm_valkind_enum::WASM_I32,
193            1 => wasm_valkind_enum::WASM_I64,
194            2 => wasm_valkind_enum::WASM_F32,
195            3 => wasm_valkind_enum::WASM_F64,
196            128 => wasm_valkind_enum::WASM_EXTERNREF,
197            129 => wasm_valkind_enum::WASM_FUNCREF,
198            130 => wasm_valkind_enum::WASM_EXNREF,
199            _ => return Err("valkind value out of bounds"),
200        })
201    }
202}
203
204impl TryFrom<wasm_val_t> for Value {
205    type Error = &'static str;
206
207    fn try_from(item: wasm_val_t) -> Result<Self, Self::Error> {
208        (&item).try_into()
209    }
210}
211
212impl TryFrom<&wasm_val_t> for Value {
213    type Error = &'static str;
214
215    fn try_from(item: &wasm_val_t) -> Result<Self, Self::Error> {
216        Ok(match item.kind.try_into()? {
217            wasm_valkind_enum::WASM_I32 => Value::I32(unsafe { item.of.int32_t }),
218            wasm_valkind_enum::WASM_I64 => Value::I64(unsafe { item.of.int64_t }),
219            wasm_valkind_enum::WASM_F32 => Value::F32(unsafe { item.of.float32_t }),
220            wasm_valkind_enum::WASM_F64 => Value::F64(unsafe { item.of.float64_t }),
221            wasm_valkind_enum::WASM_EXTERNREF => {
222                let wref = unsafe { item.of.wref };
223                if wref.is_null() {
224                    Value::ExternRef(None)
225                } else {
226                    // The boxed `wasm_ref_t` carries the authoritative value.
227                    unsafe { &*wref }.inner.clone()
228                }
229            }
230            wasm_valkind_enum::WASM_FUNCREF => {
231                let wref = unsafe { item.of.wref };
232                if wref.is_null() {
233                    Value::FuncRef(None)
234                } else {
235                    unsafe { &*wref }.inner.clone()
236                }
237            }
238            wasm_valkind_enum::WASM_EXNREF => return Err("EXNREF not supported at this time"),
239        })
240    }
241}
242
243impl wasm_val_t {
244    /// Convert a [`Value`] into a [`wasm_val_t`], boxing reference values into a
245    /// [`wasm_ref_t`] rooted in `store`. Null references become a null pointer.
246    pub(crate) fn from_value(value: &Value, store: &StoreRef) -> Result<wasm_val_t, &'static str> {
247        Ok(match value {
248            Value::ExternRef(None) => wasm_val_t {
249                kind: wasm_valkind_enum::WASM_EXTERNREF as _,
250                of: wasm_val_inner {
251                    wref: std::ptr::null_mut(),
252                },
253            },
254            Value::FuncRef(None) => wasm_val_t {
255                kind: wasm_valkind_enum::WASM_FUNCREF as _,
256                of: wasm_val_inner {
257                    wref: std::ptr::null_mut(),
258                },
259            },
260            Value::ExternRef(Some(_)) | Value::FuncRef(Some(_)) => {
261                let kind = if matches!(value, Value::ExternRef(_)) {
262                    wasm_valkind_enum::WASM_EXTERNREF
263                } else {
264                    wasm_valkind_enum::WASM_FUNCREF
265                };
266                // `wasm_ref_t::new` returns `Some` for the `Some(_)` variants.
267                let boxed = wasm_ref_t::new(store.clone(), value.clone())
268                    .ok_or("failed to box reference value")?;
269                wasm_val_t {
270                    kind: kind as _,
271                    of: wasm_val_inner {
272                        wref: Box::into_raw(boxed),
273                    },
274                }
275            }
276            other => wasm_val_t::try_from(other)?,
277        })
278    }
279}
280
281impl TryFrom<Value> for wasm_val_t {
282    type Error = &'static str;
283
284    fn try_from(item: Value) -> Result<Self, Self::Error> {
285        wasm_val_t::try_from(&item)
286    }
287}
288
289impl TryFrom<&Value> for wasm_val_t {
290    type Error = &'static str;
291
292    fn try_from(item: &Value) -> Result<Self, Self::Error> {
293        Ok(match *item {
294            Value::I32(v) => wasm_val_t {
295                of: wasm_val_inner { int32_t: v },
296                kind: wasm_valkind_enum::WASM_I32 as _,
297            },
298            Value::I64(v) => wasm_val_t {
299                of: wasm_val_inner { int64_t: v },
300                kind: wasm_valkind_enum::WASM_I64 as _,
301            },
302            Value::F32(v) => wasm_val_t {
303                of: wasm_val_inner { float32_t: v },
304                kind: wasm_valkind_enum::WASM_F32 as _,
305            },
306            Value::F64(v) => wasm_val_t {
307                of: wasm_val_inner { float64_t: v },
308                kind: wasm_valkind_enum::WASM_F64 as _,
309            },
310            Value::V128(_) => return Err("128bit SIMD types not yet supported in Wasm C API"),
311            // Reference values need a store to box into a `wasm_ref_t`; callers
312            // must use `wasm_val_t::from_value` instead.
313            Value::ExternRef(_) | Value::FuncRef(_) | Value::ExceptionRef(_) => {
314                return Err("reference values require a store; use wasm_val_t::from_value");
315            }
316        })
317    }
318}