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