Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/c-api/src/func.rs
1692 views
1
use crate::{WasmtimeCaller, WasmtimeStoreData, wasm_trap_t};
2
use crate::{
3
WasmtimeStoreContext, WasmtimeStoreContextMut, wasm_extern_t, wasm_functype_t, wasm_store_t,
4
wasm_val_t, wasm_val_vec_t, wasmtime_error_t, wasmtime_extern_t, wasmtime_val_t,
5
wasmtime_val_union,
6
};
7
use anyhow::{Error, Result};
8
use std::any::Any;
9
use std::ffi::c_void;
10
use std::mem::{self, MaybeUninit};
11
use std::panic::{self, AssertUnwindSafe};
12
use std::ptr;
13
use std::str;
14
use wasmtime::{
15
AsContext, AsContextMut, Extern, Func, RootScope, StoreContext, StoreContextMut, Trap, Val,
16
ValRaw,
17
};
18
19
#[derive(Clone)]
20
#[repr(transparent)]
21
pub struct wasm_func_t {
22
ext: wasm_extern_t,
23
}
24
25
wasmtime_c_api_macros::declare_ref!(wasm_func_t);
26
27
pub type wasm_func_callback_t = extern "C" fn(
28
args: *const wasm_val_vec_t,
29
results: *mut wasm_val_vec_t,
30
) -> Option<Box<wasm_trap_t>>;
31
32
pub type wasm_func_callback_with_env_t = extern "C" fn(
33
env: *mut std::ffi::c_void,
34
args: *const wasm_val_vec_t,
35
results: *mut wasm_val_vec_t,
36
) -> Option<Box<wasm_trap_t>>;
37
38
impl wasm_func_t {
39
pub(crate) fn try_from(e: &wasm_extern_t) -> Option<&wasm_func_t> {
40
match &e.which {
41
Extern::Func(_) => Some(unsafe { &*(e as *const _ as *const _) }),
42
_ => None,
43
}
44
}
45
46
pub(crate) fn func(&self) -> Func {
47
match self.ext.which {
48
Extern::Func(f) => f,
49
_ => unsafe { std::hint::unreachable_unchecked() },
50
}
51
}
52
}
53
54
unsafe fn create_function(
55
store: &mut wasm_store_t,
56
ty: &wasm_functype_t,
57
func: impl Fn(*const wasm_val_vec_t, *mut wasm_val_vec_t) -> Option<Box<wasm_trap_t>>
58
+ Send
59
+ Sync
60
+ 'static,
61
) -> Box<wasm_func_t> {
62
let ty = ty.ty().ty(store.store.context().engine());
63
let func = Func::new(
64
store.store.context_mut(),
65
ty,
66
move |_caller, params, results| {
67
let params: wasm_val_vec_t = params
68
.iter()
69
.cloned()
70
.map(|p| wasm_val_t::from_val(p))
71
.collect::<Vec<_>>()
72
.into();
73
let mut out_results: wasm_val_vec_t = vec![wasm_val_t::default(); results.len()].into();
74
let out = func(&params, &mut out_results);
75
if let Some(trap) = out {
76
return Err(trap.error);
77
}
78
79
let out_results = out_results.as_slice();
80
for i in 0..results.len() {
81
results[i] = out_results[i].val();
82
}
83
Ok(())
84
},
85
);
86
Box::new(wasm_func_t {
87
ext: wasm_extern_t {
88
store: store.store.clone(),
89
which: func.into(),
90
},
91
})
92
}
93
94
#[unsafe(no_mangle)]
95
pub unsafe extern "C" fn wasm_func_new(
96
store: &mut wasm_store_t,
97
ty: &wasm_functype_t,
98
callback: wasm_func_callback_t,
99
) -> Box<wasm_func_t> {
100
create_function(store, ty, move |params, results| callback(params, results))
101
}
102
103
#[unsafe(no_mangle)]
104
pub unsafe extern "C" fn wasm_func_new_with_env(
105
store: &mut wasm_store_t,
106
ty: &wasm_functype_t,
107
callback: wasm_func_callback_with_env_t,
108
data: *mut c_void,
109
finalizer: Option<extern "C" fn(arg1: *mut std::ffi::c_void)>,
110
) -> Box<wasm_func_t> {
111
let finalizer = crate::ForeignData { data, finalizer };
112
create_function(store, ty, move |params, results| {
113
let _ = &finalizer; // move entire finalizer into this closure
114
callback(finalizer.data, params, results)
115
})
116
}
117
118
/// Places the `args` into `dst` and additionally reserves space in `dst` for `results_size`
119
/// returns. The params/results slices are then returned separately.
120
pub(crate) fn translate_args<'a>(
121
dst: &'a mut Vec<Val>,
122
args: impl ExactSizeIterator<Item = Val>,
123
results_size: usize,
124
) -> (&'a [Val], &'a mut [Val]) {
125
debug_assert!(dst.is_empty());
126
let num_args = args.len();
127
dst.reserve(args.len() + results_size);
128
dst.extend(args);
129
dst.extend((0..results_size).map(|_| Val::null_func_ref()));
130
let (a, b) = dst.split_at_mut(num_args);
131
(a, b)
132
}
133
134
#[unsafe(no_mangle)]
135
pub unsafe extern "C" fn wasm_func_call(
136
func: &mut wasm_func_t,
137
args: *const wasm_val_vec_t,
138
results: *mut wasm_val_vec_t,
139
) -> *mut wasm_trap_t {
140
let f = func.func();
141
let results = (*results).as_uninit_slice();
142
let args = (*args).as_slice();
143
let mut dst = Vec::new();
144
let (wt_params, wt_results) =
145
translate_args(&mut dst, args.iter().map(|i| i.val()), results.len());
146
147
// We're calling arbitrary code here most of the time, and we in general
148
// want to try to insulate callers against bugs in wasmtime/wasi/etc if we
149
// can. As a result we catch panics here and transform them to traps to
150
// allow the caller to have any insulation possible against Rust panics.
151
let result = panic::catch_unwind(AssertUnwindSafe(|| {
152
f.call(func.ext.store.context_mut(), wt_params, wt_results)
153
}));
154
match result {
155
Ok(Ok(())) => {
156
for (slot, val) in results.iter_mut().zip(wt_results.iter().cloned()) {
157
crate::initialize(slot, wasm_val_t::from_val(val));
158
}
159
ptr::null_mut()
160
}
161
Ok(Err(err)) => Box::into_raw(Box::new(wasm_trap_t::new(err))),
162
Err(panic) => {
163
let err = error_from_panic(panic);
164
let trap = Box::new(wasm_trap_t::new(err));
165
Box::into_raw(trap)
166
}
167
}
168
}
169
170
fn error_from_panic(panic: Box<dyn Any + Send>) -> Error {
171
if let Some(msg) = panic.downcast_ref::<String>() {
172
Error::msg(msg.clone())
173
} else if let Some(msg) = panic.downcast_ref::<&'static str>() {
174
Error::msg(*msg)
175
} else {
176
Error::msg("rust panic happened")
177
}
178
}
179
180
#[unsafe(no_mangle)]
181
pub unsafe extern "C" fn wasm_func_type(f: &wasm_func_t) -> Box<wasm_functype_t> {
182
Box::new(wasm_functype_t::new(f.func().ty(f.ext.store.context())))
183
}
184
185
#[unsafe(no_mangle)]
186
pub unsafe extern "C" fn wasm_func_param_arity(f: &wasm_func_t) -> usize {
187
f.func().ty(f.ext.store.context()).params().len()
188
}
189
190
#[unsafe(no_mangle)]
191
pub unsafe extern "C" fn wasm_func_result_arity(f: &wasm_func_t) -> usize {
192
f.func().ty(f.ext.store.context()).results().len()
193
}
194
195
#[unsafe(no_mangle)]
196
pub extern "C" fn wasm_func_as_extern(f: &mut wasm_func_t) -> &mut wasm_extern_t {
197
&mut (*f).ext
198
}
199
200
#[unsafe(no_mangle)]
201
pub extern "C" fn wasm_func_as_extern_const(f: &wasm_func_t) -> &wasm_extern_t {
202
&(*f).ext
203
}
204
205
#[repr(C)]
206
pub struct wasmtime_caller_t<'a> {
207
pub(crate) caller: WasmtimeCaller<'a>,
208
}
209
210
impl AsContext for wasmtime_caller_t<'_> {
211
type Data = WasmtimeStoreData;
212
213
fn as_context(&self) -> StoreContext<'_, WasmtimeStoreData> {
214
self.caller.as_context()
215
}
216
}
217
218
impl AsContextMut for wasmtime_caller_t<'_> {
219
fn as_context_mut(&mut self) -> StoreContextMut<'_, WasmtimeStoreData> {
220
self.caller.as_context_mut()
221
}
222
}
223
224
pub type wasmtime_func_callback_t = extern "C" fn(
225
*mut c_void,
226
*mut wasmtime_caller_t,
227
*const wasmtime_val_t,
228
usize,
229
*mut wasmtime_val_t,
230
usize,
231
) -> Option<Box<wasm_trap_t>>;
232
233
pub type wasmtime_func_unchecked_callback_t = extern "C" fn(
234
*mut c_void,
235
*mut wasmtime_caller_t,
236
*mut ValRaw,
237
usize,
238
) -> Option<Box<wasm_trap_t>>;
239
240
#[unsafe(no_mangle)]
241
pub unsafe extern "C" fn wasmtime_func_new(
242
store: WasmtimeStoreContextMut<'_>,
243
ty: &wasm_functype_t,
244
callback: wasmtime_func_callback_t,
245
data: *mut c_void,
246
finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>,
247
func: &mut Func,
248
) {
249
let ty = ty.ty().ty(store.engine());
250
let cb = c_callback_to_rust_fn(callback, data, finalizer);
251
let f = Func::new(store, ty, cb);
252
*func = f;
253
}
254
255
pub(crate) unsafe fn c_callback_to_rust_fn(
256
callback: wasmtime_func_callback_t,
257
data: *mut c_void,
258
finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>,
259
) -> impl Fn(WasmtimeCaller<'_>, &[Val], &mut [Val]) -> Result<()> {
260
let foreign = crate::ForeignData { data, finalizer };
261
move |mut caller, params, results| {
262
let _ = &foreign; // move entire foreign into this closure
263
264
// Convert `params/results` to `wasmtime_val_t`. Use the previous
265
// storage in `hostcall_val_storage` to help avoid allocations all the
266
// time.
267
let mut vals = mem::take(&mut caller.data_mut().hostcall_val_storage);
268
debug_assert!(vals.is_empty());
269
vals.reserve(params.len() + results.len());
270
vals.extend(
271
params
272
.iter()
273
.cloned()
274
.map(|p| wasmtime_val_t::from_val_unscoped(&mut caller, p)),
275
);
276
vals.extend((0..results.len()).map(|_| wasmtime_val_t {
277
kind: crate::WASMTIME_I32,
278
of: wasmtime_val_union { i32: 0 },
279
}));
280
let (params, out_results) = vals.split_at_mut(params.len());
281
282
// Invoke the C function pointer, getting the results.
283
let mut caller = wasmtime_caller_t { caller };
284
let out = callback(
285
foreign.data,
286
&mut caller,
287
params.as_ptr(),
288
params.len(),
289
out_results.as_mut_ptr(),
290
out_results.len(),
291
);
292
if let Some(trap) = out {
293
return Err(trap.error);
294
}
295
296
// Translate the `wasmtime_val_t` results into the `results` space
297
for (i, result) in out_results.iter().enumerate() {
298
results[i] = result.to_val_unscoped(&mut caller);
299
}
300
301
// Move our `vals` storage back into the store now that we no longer
302
// need it. This'll get picked up by the next hostcall and reuse our
303
// same storage.
304
vals.truncate(0);
305
caller.caller.data_mut().hostcall_val_storage = vals;
306
Ok(())
307
}
308
}
309
310
#[unsafe(no_mangle)]
311
pub unsafe extern "C" fn wasmtime_func_new_unchecked(
312
store: WasmtimeStoreContextMut<'_>,
313
ty: &wasm_functype_t,
314
callback: wasmtime_func_unchecked_callback_t,
315
data: *mut c_void,
316
finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>,
317
func: &mut Func,
318
) {
319
let ty = ty.ty().ty(store.engine());
320
let cb = c_unchecked_callback_to_rust_fn(callback, data, finalizer);
321
*func = Func::new_unchecked(store, ty, cb);
322
}
323
324
pub(crate) unsafe fn c_unchecked_callback_to_rust_fn(
325
callback: wasmtime_func_unchecked_callback_t,
326
data: *mut c_void,
327
finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>,
328
) -> impl Fn(WasmtimeCaller<'_>, &mut [ValRaw]) -> Result<()> {
329
let foreign = crate::ForeignData { data, finalizer };
330
move |caller, values| {
331
let _ = &foreign; // move entire foreign into this closure
332
let mut caller = wasmtime_caller_t { caller };
333
match callback(foreign.data, &mut caller, values.as_mut_ptr(), values.len()) {
334
None => Ok(()),
335
Some(trap) => Err(trap.error),
336
}
337
}
338
}
339
340
#[unsafe(no_mangle)]
341
pub unsafe extern "C" fn wasmtime_func_call(
342
mut store: WasmtimeStoreContextMut<'_>,
343
func: &Func,
344
args: *const wasmtime_val_t,
345
nargs: usize,
346
results: *mut MaybeUninit<wasmtime_val_t>,
347
nresults: usize,
348
trap_ret: &mut *mut wasm_trap_t,
349
) -> Option<Box<wasmtime_error_t>> {
350
let mut scope = RootScope::new(&mut store);
351
let mut params = mem::take(&mut scope.as_context_mut().data_mut().wasm_val_storage);
352
let (wt_params, wt_results) = translate_args(
353
&mut params,
354
crate::slice_from_raw_parts(args, nargs)
355
.iter()
356
.map(|i| i.to_val(&mut scope)),
357
nresults,
358
);
359
360
// We're calling arbitrary code here most of the time, and we in general
361
// want to try to insulate callers against bugs in wasmtime/wasi/etc if we
362
// can. As a result we catch panics here and transform them to traps to
363
// allow the caller to have any insulation possible against Rust panics.
364
let result = panic::catch_unwind(AssertUnwindSafe(|| {
365
func.call(&mut scope, wt_params, wt_results)
366
}));
367
match result {
368
Ok(Ok(())) => {
369
let results = crate::slice_from_raw_parts_mut(results, nresults);
370
for (slot, val) in results.iter_mut().zip(wt_results.iter()) {
371
crate::initialize(slot, wasmtime_val_t::from_val(&mut scope, *val));
372
}
373
params.truncate(0);
374
scope.as_context_mut().data_mut().wasm_val_storage = params;
375
None
376
}
377
Ok(Err(trap)) => store_err(trap, trap_ret),
378
Err(panic) => {
379
let err = error_from_panic(panic);
380
*trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(err)));
381
None
382
}
383
}
384
}
385
386
#[unsafe(no_mangle)]
387
pub unsafe extern "C" fn wasmtime_func_call_unchecked(
388
store: WasmtimeStoreContextMut<'_>,
389
func: &Func,
390
args_and_results: *mut ValRaw,
391
args_and_results_len: usize,
392
trap_ret: &mut *mut wasm_trap_t,
393
) -> Option<Box<wasmtime_error_t>> {
394
let slice = std::ptr::slice_from_raw_parts_mut(args_and_results, args_and_results_len);
395
match func.call_unchecked(store, slice) {
396
Ok(()) => None,
397
Err(trap) => store_err(trap, trap_ret),
398
}
399
}
400
401
fn store_err(err: Error, trap_ret: &mut *mut wasm_trap_t) -> Option<Box<wasmtime_error_t>> {
402
if err.is::<Trap>() {
403
*trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(err)));
404
None
405
} else {
406
Some(Box::new(wasmtime_error_t::from(err)))
407
}
408
}
409
410
#[unsafe(no_mangle)]
411
pub extern "C" fn wasmtime_func_type(
412
store: WasmtimeStoreContext<'_>,
413
func: &Func,
414
) -> Box<wasm_functype_t> {
415
Box::new(wasm_functype_t::new(func.ty(store)))
416
}
417
418
#[unsafe(no_mangle)]
419
pub extern "C" fn wasmtime_caller_context<'a>(
420
caller: &'a mut wasmtime_caller_t,
421
) -> WasmtimeStoreContextMut<'a> {
422
caller.caller.as_context_mut()
423
}
424
425
#[unsafe(no_mangle)]
426
pub unsafe extern "C" fn wasmtime_caller_export_get(
427
caller: &mut wasmtime_caller_t,
428
name: *const u8,
429
name_len: usize,
430
item: &mut MaybeUninit<wasmtime_extern_t>,
431
) -> bool {
432
let name = match str::from_utf8(crate::slice_from_raw_parts(name, name_len)) {
433
Ok(name) => name,
434
Err(_) => return false,
435
};
436
let which = match caller.caller.get_export(name) {
437
Some(item) => item,
438
None => return false,
439
};
440
crate::initialize(item, which.into());
441
true
442
}
443
444
#[unsafe(no_mangle)]
445
pub unsafe extern "C" fn wasmtime_func_from_raw(
446
store: WasmtimeStoreContextMut<'_>,
447
raw: *mut c_void,
448
func: &mut Func,
449
) {
450
*func = Func::from_raw(store, raw).unwrap();
451
}
452
453
#[unsafe(no_mangle)]
454
pub unsafe extern "C" fn wasmtime_func_to_raw(
455
store: WasmtimeStoreContextMut<'_>,
456
func: &Func,
457
) -> *mut c_void {
458
func.to_raw(store)
459
}
460
461