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