1pub(crate) mod env;
4pub(crate) mod typed;
5
6#[cfg(feature = "experimental-async")]
7use crate::{
8 AsStoreAsync, AsyncFunctionEnvMut, BackendAsyncFunctionEnvMut, StoreAsync,
9 entities::function::async_host::{AsyncFunctionEnv, AsyncHostFunction},
10 sys::{
11 async_runtime::{AsyncRuntimeError, block_on_host_future, call_function_async},
12 function::env::AsyncFunctionEnvMutStore,
13 },
14};
15use crate::{
16 BackendFunction, FunctionEnv, FunctionEnvMut, FunctionType, HostFunction, RuntimeError,
17 StoreContext, StoreInner, Value, WithEnv, WithoutEnv,
18 backend::sys::{engine::NativeEngineExt, vm::VMFunctionCallback},
19 entities::store::{AsStoreMut, AsStoreRef, StoreMut},
20 utils::{FromToNativeWasmType, IntoResult, NativeWasmTypeInto, WasmTypeList},
21 vm::{VMExtern, VMExternFunction},
22};
23use std::panic::{self, AssertUnwindSafe};
24use std::{
25 cell::UnsafeCell, cmp::max, error::Error, ffi::c_void, future::Future, marker::PhantomData,
26 pin::Pin, sync::Arc,
27};
28use wasmer_types::{NativeWasmType, RawValue, StoreId};
29#[cfg(feature = "experimental-host-interrupt")]
30use wasmer_vm::interrupt_registry;
31use wasmer_vm::{
32 MaybeInstanceOwned, StoreHandle, Trap, TrapCode, VMCallerCheckedAnyfunc, VMContext,
33 VMDynamicFunctionContext, VMExceptionRef, VMFuncRef, VMFunction, VMFunctionBody,
34 VMFunctionContext, VMFunctionKind, VMTrampoline, on_host_stack, raise_lib_trap,
35 raise_user_trap, resume_panic, wasmer_call_trampoline,
36};
37
38#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub struct Function {
42 pub(crate) handle: StoreHandle<VMFunction>,
43}
44
45impl From<StoreHandle<VMFunction>> for Function {
46 fn from(handle: StoreHandle<VMFunction>) -> Self {
47 Self { handle }
48 }
49}
50
51impl Function {
52 pub(crate) fn new_with_env<FT, F, T: Send + 'static>(
53 store: &mut impl AsStoreMut,
54 env: &FunctionEnv<T>,
55 ty: FT,
56 func: F,
57 ) -> Self
58 where
59 FT: Into<FunctionType>,
60 F: Fn(FunctionEnvMut<T>, &[Value]) -> Result<Vec<Value>, RuntimeError>
61 + 'static
62 + Send
63 + Sync,
64 {
65 let function_type = ty.into();
66 let func_ty = function_type.clone();
67 let func_env = env.clone().into_sys();
68 let store_id = store.objects_mut().id();
69 let wrapper = move |values_vec: *mut RawValue| -> HostCallOutcome {
70 unsafe {
71 let mut store_wrapper = unsafe { StoreContext::get_current(store_id) };
72 let mut store_mut = store_wrapper.as_mut();
73 let mut args = Vec::with_capacity(func_ty.params().len());
74
75 for (i, ty) in func_ty.params().iter().enumerate() {
76 args.push(Value::from_raw(
77 &mut store_mut,
78 *ty,
79 values_vec.add(i).read_unaligned(),
80 ));
81 }
82 let env = env::FunctionEnvMut {
83 store_mut,
84 func_env: func_env.clone(),
85 }
86 .into();
87 let sig = func_ty.clone();
88 let result = func(env, &args);
89 HostCallOutcome::Ready {
90 func_ty: sig,
91 result,
92 }
93 }
94 };
95 let mut host_data = Box::new(VMDynamicFunctionContext {
96 address: std::ptr::null(),
97 ctx: DynamicFunction {
98 func: wrapper,
99 store_id,
100 },
101 });
102 host_data.address = host_data.ctx.func_body_ptr();
103
104 let func_ptr = std::ptr::null() as VMFunctionCallback;
108 let type_signature_hash = store
109 .as_store_ref()
110 .engine()
111 .as_sys()
112 .register_signature(&function_type);
113 let vmctx = VMFunctionContext {
114 host_env: host_data.as_ref() as *const _ as *mut c_void,
115 };
116 let call_trampoline = host_data.ctx.call_trampoline_address();
117 let anyfunc = VMCallerCheckedAnyfunc {
118 func_ptr,
119 type_signature_hash,
120 vmctx,
121 call_trampoline,
122 };
123
124 let vm_function = VMFunction {
125 anyfunc: MaybeInstanceOwned::Host(Box::new(UnsafeCell::new(anyfunc))),
126 kind: VMFunctionKind::Dynamic,
127 signature: function_type,
128 host_data,
129 };
130 Self {
131 handle: StoreHandle::new(store.objects_mut().as_sys_mut(), vm_function),
132 }
133 }
134
135 #[cfg(feature = "experimental-async")]
136 pub(crate) fn new_async<FT, F, Fut>(store: &mut impl AsStoreMut, ty: FT, func: F) -> Self
137 where
138 FT: Into<FunctionType>,
139 F: Fn(&[Value]) -> Fut + 'static,
140 Fut: Future<Output = Result<Vec<Value>, RuntimeError>> + 'static,
141 {
142 let env = FunctionEnv::new(store, ());
143 let wrapped = move |_env: AsyncFunctionEnvMut<()>, values: &[Value]| func(values);
144 Self::new_with_env_async(store, &env, ty, wrapped)
145 }
146
147 #[cfg(feature = "experimental-async")]
148 pub(crate) fn new_with_env_async<FT, F, Fut, T: 'static>(
149 store: &mut impl AsStoreMut,
150 env: &FunctionEnv<T>,
151 ty: FT,
152 func: F,
153 ) -> Self
154 where
155 FT: Into<FunctionType>,
156 F: Fn(AsyncFunctionEnvMut<T>, &[Value]) -> Fut + 'static,
157 Fut: Future<Output = Result<Vec<Value>, RuntimeError>> + 'static,
158 {
159 let function_type = ty.into();
160 let func_ty = function_type.clone();
161 let func_env = env.clone().into_sys();
162 let store_id = store.objects_mut().id();
163 let wrapper = move |values_vec: *mut RawValue| -> HostCallOutcome {
164 unsafe {
165 let mut context = StoreContext::try_get_current_async(store_id);
166 let mut store_mut = match &mut context {
167 crate::GetStoreAsyncGuardResult::Ok(wrapper) => StoreMut {
168 inner: wrapper.guard.as_mut().unwrap(),
169 },
170 crate::GetStoreAsyncGuardResult::NotAsync(ptr) => ptr.as_mut(),
171 crate::GetStoreAsyncGuardResult::NotInstalled => {
172 panic!("No store context installed on this thread")
173 }
174 };
175 let id = store_mut.as_store_ref().objects().id();
176 let mut args = Vec::with_capacity(func_ty.params().len());
177
178 for (i, ty) in func_ty.params().iter().enumerate() {
179 args.push(Value::from_raw(
180 &mut store_mut,
181 *ty,
182 values_vec.add(i).read_unaligned(),
183 ));
184 }
185 let store_async = match context {
186 crate::GetStoreAsyncGuardResult::Ok(wrapper) => {
187 AsyncFunctionEnvMutStore::Async(StoreAsync {
188 id,
189 inner: crate::LocalRwLockWriteGuard::lock_handle(
190 wrapper.guard.as_mut().unwrap(),
191 ),
192 })
193 }
194 crate::GetStoreAsyncGuardResult::NotAsync(ptr) => {
195 AsyncFunctionEnvMutStore::Sync(ptr)
196 }
197 crate::GetStoreAsyncGuardResult::NotInstalled => unreachable!(),
198 };
199 let env = crate::AsyncFunctionEnvMut(crate::BackendAsyncFunctionEnvMut::Sys(
200 env::AsyncFunctionEnvMut {
201 store: store_async,
202 func_env: func_env.clone(),
203 },
204 ));
205 let sig = func_ty.clone();
206 let future = func(env, &args);
207 HostCallOutcome::Future {
208 func_ty: sig,
209 future: Box::pin(future),
210 }
211 }
212 };
213 let mut host_data = Box::new(VMDynamicFunctionContext {
214 address: std::ptr::null(),
215 ctx: DynamicFunction {
216 func: wrapper,
217 store_id,
218 },
219 });
220 host_data.address = host_data.ctx.func_body_ptr();
221
222 let func_ptr = std::ptr::null() as VMFunctionCallback;
223 let type_signature_hash = store
224 .as_store_ref()
225 .engine()
226 .as_sys()
227 .register_signature(&function_type);
228 let vmctx = VMFunctionContext {
229 host_env: host_data.as_ref() as *const _ as *mut c_void,
230 };
231 let call_trampoline = host_data.ctx.call_trampoline_address();
232 let anyfunc = VMCallerCheckedAnyfunc {
233 func_ptr,
234 type_signature_hash,
235 vmctx,
236 call_trampoline,
237 };
238
239 let vm_function = VMFunction {
240 anyfunc: MaybeInstanceOwned::Host(Box::new(UnsafeCell::new(anyfunc))),
241 kind: VMFunctionKind::Dynamic,
242 signature: function_type,
243 host_data,
244 };
245 Self {
246 handle: StoreHandle::new(store.objects_mut().as_sys_mut(), vm_function),
247 }
248 }
249
250 pub(crate) fn new_typed<F, Args, Rets>(store: &mut impl AsStoreMut, func: F) -> Self
252 where
253 F: HostFunction<(), Args, Rets, WithoutEnv> + 'static + Send + Sync,
254 Args: WasmTypeList,
255 Rets: WasmTypeList,
256 {
257 let env = FunctionEnv::new(store, ());
258 let func_ptr = func.function_callback_sys().unwrap_sys();
259 let host_data = Box::new(StaticFunction {
260 store_id: store.objects_mut().id(),
261 env,
262 func,
263 });
264 let function_type = FunctionType::new(Args::wasm_types(), Rets::wasm_types());
265
266 let type_signature_hash = store
267 .as_store_ref()
268 .engine()
269 .as_sys()
270 .register_signature(&function_type);
271 let vmctx = VMFunctionContext {
272 host_env: host_data.as_ref() as *const _ as *mut c_void,
273 };
274 let call_trampoline =
275 <F as HostFunction<(), Args, Rets, WithoutEnv>>::call_trampoline_address().unwrap_sys();
276 let anyfunc = VMCallerCheckedAnyfunc {
277 func_ptr,
278 type_signature_hash,
279 vmctx,
280 call_trampoline,
281 };
282
283 let vm_function = VMFunction {
284 anyfunc: MaybeInstanceOwned::Host(Box::new(UnsafeCell::new(anyfunc))),
285 kind: VMFunctionKind::Static,
286 signature: function_type,
287 host_data,
288 };
289 Self {
290 handle: StoreHandle::new(store.objects_mut().as_sys_mut(), vm_function),
291 }
292 }
293
294 #[cfg(feature = "experimental-async")]
295 pub(crate) fn new_typed_async<F, Args, Rets>(store: &mut impl AsStoreMut, func: F) -> Self
296 where
297 Args: WasmTypeList + 'static,
298 Rets: WasmTypeList + 'static,
299 F: AsyncHostFunction<(), Args, Rets, WithoutEnv> + 'static,
300 {
301 let env = FunctionEnv::new(store, ());
302 let signature = FunctionType::new(Args::wasm_types(), Rets::wasm_types());
303 let args_sig = Arc::new(signature.clone());
304 let results_sig = Arc::new(signature.clone());
305 let func = Arc::new(func);
306 Self::new_with_env_async(
307 store,
308 &env,
309 signature,
310 move |mut env_mut,
311 values|
312 -> Pin<Box<dyn Future<Output = Result<Vec<Value>, RuntimeError>>>> {
313 let sys_env = match env_mut.0 {
314 BackendAsyncFunctionEnvMut::Sys(ref mut sys_env) => sys_env,
315 _ => panic!("Not a sys backend"),
316 };
317 let mut store_mut_wrapper =
318 unsafe { StoreContext::get_current(sys_env.store_id()) };
319 let mut store_mut = store_mut_wrapper.as_mut();
320 let args_sig = args_sig.clone();
321 let results_sig = results_sig.clone();
322 let func = func.clone();
323 let args =
324 match typed_args_from_values::<Args>(&mut store_mut, args_sig.as_ref(), values)
325 {
326 Ok(args) => args,
327 Err(err) => return Box::pin(async { Err(err) }),
328 };
329 drop(store_mut_wrapper);
330 let future = func.as_ref().call_async(AsyncFunctionEnv::new(), args);
331 Box::pin(async move {
332 let typed_result = future.await?;
333 let mut store_mut = env_mut.write().await;
334 typed_results_to_values::<Rets>(
335 &mut store_mut.as_store_mut(),
336 results_sig.as_ref(),
337 typed_result,
338 )
339 })
340 },
341 )
342 }
343
344 #[cfg(feature = "experimental-async")]
345 pub(crate) fn new_typed_with_env_async<T, F, Args, Rets>(
346 store: &mut impl AsStoreMut,
347 env: &FunctionEnv<T>,
348 func: F,
349 ) -> Self
350 where
351 T: 'static,
352 F: AsyncHostFunction<T, Args, Rets, WithEnv> + 'static,
353 Args: WasmTypeList + 'static,
354 Rets: WasmTypeList + 'static,
355 {
356 let signature = FunctionType::new(Args::wasm_types(), Rets::wasm_types());
357 let args_sig = Arc::new(signature.clone());
358 let results_sig = Arc::new(signature.clone());
359 let func = Arc::new(func);
360 Self::new_with_env_async(
361 store,
362 env,
363 signature,
364 move |mut env_mut,
365 values|
366 -> Pin<Box<dyn Future<Output = Result<Vec<Value>, RuntimeError>>>> {
367 let sys_env = match env_mut.0 {
368 BackendAsyncFunctionEnvMut::Sys(ref mut sys_env) => sys_env,
369 _ => panic!("Not a sys backend"),
370 };
371 let mut store_mut_wrapper =
372 unsafe { StoreContext::get_current(sys_env.store_id()) };
373 let mut store_mut = store_mut_wrapper.as_mut();
374 let args_sig = args_sig.clone();
375 let results_sig = results_sig.clone();
376 let func = func.clone();
377 let args =
378 match typed_args_from_values::<Args>(&mut store_mut, args_sig.as_ref(), values)
379 {
380 Ok(args) => args,
381 Err(err) => return Box::pin(async { Err(err) }),
382 };
383 drop(store_mut_wrapper);
384 let env_mut_clone = env_mut.as_mut();
385 let future = func
386 .as_ref()
387 .call_async(AsyncFunctionEnv::with_env(env_mut), args);
388 Box::pin(async move {
389 let typed_result = future.await?;
390 let mut store_mut = env_mut_clone.write().await;
391 typed_results_to_values::<Rets>(
392 &mut store_mut.as_store_mut(),
393 results_sig.as_ref(),
394 typed_result,
395 )
396 })
397 },
398 )
399 }
400
401 pub(crate) fn new_typed_with_env<T: Send + 'static, F, Args, Rets>(
402 store: &mut impl AsStoreMut,
403 env: &FunctionEnv<T>,
404 func: F,
405 ) -> Self
406 where
407 F: HostFunction<T, Args, Rets, WithEnv> + 'static + Send + Sync,
408 Args: WasmTypeList,
409 Rets: WasmTypeList,
410 {
411 let func_ptr = func.function_callback_sys().unwrap_sys();
412 let host_data = Box::new(StaticFunction {
413 store_id: store.objects_mut().id(),
414 env: env.as_sys().clone().into(),
415 func,
416 });
417 let function_type = FunctionType::new(Args::wasm_types(), Rets::wasm_types());
418
419 let type_signature_hash = store
420 .as_store_ref()
421 .engine()
422 .as_sys()
423 .register_signature(&function_type);
424 let vmctx = VMFunctionContext {
425 host_env: host_data.as_ref() as *const _ as *mut c_void,
426 };
427 let call_trampoline =
428 <F as HostFunction<T, Args, Rets, WithEnv>>::call_trampoline_address().unwrap_sys();
429 let anyfunc = VMCallerCheckedAnyfunc {
430 func_ptr,
431 type_signature_hash,
432 vmctx,
433 call_trampoline,
434 };
435
436 let vm_function = VMFunction {
437 anyfunc: MaybeInstanceOwned::Host(Box::new(UnsafeCell::new(anyfunc))),
438 kind: VMFunctionKind::Static,
439 signature: function_type,
440 host_data,
441 };
442 Self {
443 handle: StoreHandle::new(store.objects_mut().as_sys_mut(), vm_function),
444 }
445 }
446
447 pub(crate) fn ty(&self, store: &impl AsStoreRef) -> FunctionType {
448 self.handle
449 .get(store.as_store_ref().objects().as_sys())
450 .signature
451 .clone()
452 }
453
454 fn call_wasm(
455 &self,
456 store: &mut impl AsStoreMut,
457 trampoline: VMTrampoline,
458 params: &[Value],
459 results: &mut [Value],
460 ) -> Result<(), RuntimeError> {
461 let format_types_for_error_message = |items: &[Value]| {
462 items
463 .iter()
464 .map(|param| param.ty().to_string())
465 .collect::<Vec<String>>()
466 .join(", ")
467 };
468 let signature = self.ty(store);
470 if signature.params().len() != params.len() {
471 return Err(RuntimeError::new(format!(
472 "Parameters of type [{}] did not match signature {}",
473 format_types_for_error_message(params),
474 signature
475 )));
476 }
477 if signature.results().len() != results.len() {
478 return Err(RuntimeError::new(format!(
479 "Results of type [{}] did not match signature {}",
480 format_types_for_error_message(results),
481 signature,
482 )));
483 }
484
485 let mut values_vec = vec![RawValue { i32: 0 }; max(params.len(), results.len())];
486
487 let param_tys = signature.params().iter();
489 for ((arg, slot), ty) in params.iter().zip(&mut values_vec).zip(param_tys) {
490 if arg.ty() != *ty {
491 let param_types = format_types_for_error_message(params);
492 return Err(RuntimeError::new(format!(
493 "Parameters of type [{param_types}] did not match signature {signature}",
494 )));
495 }
496 if !arg.is_from_store(store) {
497 return Err(RuntimeError::new("cross-`Store` values are not supported"));
498 }
499 *slot = arg.as_raw(store);
500 }
501
502 self.call_wasm_raw(store, trampoline, values_vec, results)?;
504 Ok(())
505 }
506
507 fn call_wasm_raw(
508 &self,
509 store: &mut impl AsStoreMut,
510 trampoline: VMTrampoline,
511 mut params: Vec<RawValue>,
512 results: &mut [Value],
513 ) -> Result<(), RuntimeError> {
514 let result = {
516 let store_id = store.objects_mut().id();
517
518 #[cfg(feature = "experimental-host-interrupt")]
519 let interrupt_guard = match interrupt_registry::install(store_id) {
520 Ok(x) => x,
521 Err(interrupt_registry::InstallError::AlreadyInterrupted) => {
522 return Err(Trap::lib(TrapCode::HostInterrupt).into());
523 }
524 };
525
526 let store_install_guard =
529 unsafe { StoreContext::ensure_installed(store.as_store_mut().inner as *mut _) };
530
531 let mut r;
532 loop {
534 let storeref = store.as_store_ref();
535 let vm_function = self.handle.get(storeref.objects().as_sys());
536 let config = storeref.engine().tunables().vmconfig();
537 let signal_handler = storeref.signal_handler();
538 r = unsafe {
539 let pause_guard = StoreContext::pause(store_id);
542 wasmer_call_trampoline(
543 signal_handler,
544 config,
545 vm_function.anyfunc.as_ptr().as_ref().vmctx,
546 trampoline,
547 vm_function.anyfunc.as_ptr().as_ref().func_ptr,
548 params.as_mut_ptr() as *mut u8,
549 )
550 };
551 let store_mut = store.as_store_mut();
552 if let Some(callback) = store_mut.inner.on_called.take() {
553 match callback(store_mut) {
554 Ok(wasmer_types::OnCalledAction::InvokeAgain) => {
555 continue;
556 }
557 Ok(wasmer_types::OnCalledAction::Finish) => {
558 break;
559 }
560 Ok(wasmer_types::OnCalledAction::Trap(trap)) => {
561 return Err(RuntimeError::user(trap));
562 }
563 Err(trap) => return Err(RuntimeError::user(trap)),
564 }
565 }
566 break;
567 }
568
569 drop(store_install_guard);
570 #[cfg(feature = "experimental-host-interrupt")]
571 drop(interrupt_guard);
572
573 r
574 };
575
576 if let Err(error) = result {
577 return Err(error.into());
578 }
579
580 let signature = self.ty(store);
582 for (index, &value_type) in signature.results().iter().enumerate() {
583 unsafe {
584 results[index] = Value::from_raw(store, value_type, params[index]);
585 }
586 }
587
588 Ok(())
589 }
590
591 pub(crate) fn result_arity(&self, store: &impl AsStoreRef) -> usize {
592 self.ty(store).results().len()
593 }
594
595 pub(crate) fn call(
596 &self,
597 store: &mut impl AsStoreMut,
598 params: &[Value],
599 ) -> Result<Box<[Value]>, RuntimeError> {
600 let trampoline = unsafe {
601 self.handle
602 .get(store.objects_mut().as_sys())
603 .anyfunc
604 .as_ptr()
605 .as_ref()
606 .call_trampoline
607 };
608 let mut results = vec![Value::null(); self.result_arity(store)];
609 self.call_wasm(store, trampoline, params, &mut results)?;
610 Ok(results.into_boxed_slice())
611 }
612
613 #[cfg(feature = "experimental-async")]
614 #[allow(clippy::type_complexity)]
615 pub(crate) fn call_async(
616 &self,
617 store: &impl AsStoreAsync,
618 params: Vec<Value>,
619 ) -> Pin<Box<dyn Future<Output = Result<Box<[Value]>, RuntimeError>> + 'static>> {
620 let function = self.clone();
621 let store = store.store();
622 Box::pin(call_function_async(function, store, params))
623 }
624
625 #[doc(hidden)]
626 #[allow(missing_docs)]
627 pub(crate) fn call_raw(
628 &self,
629 store: &mut impl AsStoreMut,
630 params: Vec<RawValue>,
631 ) -> Result<Box<[Value]>, RuntimeError> {
632 let trampoline = unsafe {
633 self.handle
634 .get(store.objects_mut().as_sys())
635 .anyfunc
636 .as_ptr()
637 .as_ref()
638 .call_trampoline
639 };
640 let mut results = vec![Value::null(); self.result_arity(store)];
641 self.call_wasm_raw(store, trampoline, params, &mut results)?;
642 Ok(results.into_boxed_slice())
643 }
644
645 pub(crate) fn vm_funcref(&self, store: &impl AsStoreRef) -> VMFuncRef {
646 let vm_function = self.handle.get(store.as_store_ref().objects().as_sys());
647 if vm_function.kind == VMFunctionKind::Dynamic {
648 panic!("dynamic functions cannot be used in tables or as funcrefs");
649 }
650 VMFuncRef(vm_function.anyfunc.as_ptr())
651 }
652
653 pub(crate) unsafe fn from_vm_funcref(store: &mut impl AsStoreMut, funcref: VMFuncRef) -> Self {
654 let signature = {
655 let anyfunc = unsafe { funcref.0.as_ref() };
656 store
657 .as_store_mut()
658 .engine()
659 .as_sys()
660 .lookup_signature(anyfunc.type_signature_hash)
661 .expect("Signature not found in store")
662 };
663 let vm_function = VMFunction {
664 anyfunc: MaybeInstanceOwned::Instance(funcref.0),
665 signature,
666 kind: wasmer_vm::VMFunctionKind::Static,
669 host_data: Box::new(()),
670 };
671 Self {
672 handle: StoreHandle::new(store.objects_mut().as_sys_mut(), vm_function),
673 }
674 }
675
676 pub(crate) fn from_vm_extern(store: &mut impl AsStoreMut, vm_extern: VMExternFunction) -> Self {
677 Self {
678 handle: unsafe {
679 StoreHandle::from_internal(store.objects_mut().id(), vm_extern.unwrap_sys())
680 },
681 }
682 }
683
684 pub(crate) fn is_from_store(&self, store: &impl AsStoreRef) -> bool {
686 self.handle.store_id() == store.as_store_ref().objects().id()
687 }
688
689 pub(crate) fn to_vm_extern(&self) -> VMExtern {
690 VMExtern::Sys(wasmer_vm::VMExtern::Function(self.handle.internal_handle()))
691 }
692}
693
694enum InvocationResult<T, E> {
700 Success(T),
701 Exception(crate::Exception),
702 Trap(Box<E>),
703 YieldOutsideAsyncContext,
704}
705
706fn to_invocation_result<T, E>(result: Result<T, E>) -> InvocationResult<T, E>
707where
708 E: Error + 'static,
709{
710 match result {
711 Ok(value) => InvocationResult::Success(value),
712 Err(trap) => {
713 let dyn_err_ref = &trap as &dyn Error;
714 if let Some(runtime_error) = dyn_err_ref.downcast_ref::<RuntimeError>()
715 && let Some(exception) = runtime_error.to_exception()
716 {
717 return InvocationResult::Exception(exception);
718 }
719 InvocationResult::Trap(Box::new(trap))
720 }
721 }
722}
723
724fn write_dynamic_results(
725 store_id: StoreId,
726 func_ty: &FunctionType,
727 returns: Vec<Value>,
728 values_vec: *mut RawValue,
729) -> Result<(), RuntimeError> {
730 let mut store_wrapper = unsafe { StoreContext::get_current(store_id) };
731 let mut store = store_wrapper.as_mut();
732 let return_types = returns.iter().map(|ret| ret.ty());
733 if return_types.ne(func_ty.results().iter().copied()) {
734 return Err(RuntimeError::new(format!(
735 "Dynamic function returned wrong signature. Expected {:?} but got {:?}",
736 func_ty.results(),
737 returns.iter().map(|ret| ret.ty())
738 )));
739 }
740 for (i, ret) in returns.iter().enumerate() {
741 unsafe {
742 values_vec.add(i).write_unaligned(ret.as_raw(&store));
743 }
744 }
745 Ok(())
746}
747
748fn finalize_dynamic_call(
749 store_id: StoreId,
750 func_ty: FunctionType,
751 values_vec: *mut RawValue,
752 result: Result<Vec<Value>, RuntimeError>,
753) -> Result<(), RuntimeError> {
754 match result {
755 Ok(values) => write_dynamic_results(store_id, &func_ty, values, values_vec),
756 Err(err) => Err(err),
757 }
758}
759
760fn typed_args_from_values<Args>(
761 store: &mut StoreMut,
762 func_ty: &FunctionType,
763 values: &[Value],
764) -> Result<Args, RuntimeError>
765where
766 Args: WasmTypeList,
767{
768 if values.len() != func_ty.params().len() {
769 return Err(RuntimeError::new(
770 "typed host function received wrong number of parameters",
771 ));
772 }
773 let mut raw_array = Args::empty_array();
774 for ((slot, value), expected_ty) in raw_array
775 .as_mut()
776 .iter_mut()
777 .zip(values.iter())
778 .zip(func_ty.params().iter())
779 {
780 debug_assert_eq!(
781 value.ty(),
782 *expected_ty,
783 "wasm should only call host functions with matching signatures"
784 );
785 *slot = value.as_raw(store);
786 }
787 unsafe { Ok(Args::from_array(store, raw_array)) }
788}
789
790fn typed_results_to_values<Rets>(
791 store: &mut StoreMut,
792 func_ty: &FunctionType,
793 rets: Rets,
794) -> Result<Vec<Value>, RuntimeError>
795where
796 Rets: WasmTypeList,
797{
798 let mut raw_array = unsafe { rets.into_array(store) };
799 let mut values = Vec::with_capacity(func_ty.results().len());
800 for (raw, ty) in raw_array.as_mut().iter().zip(func_ty.results().iter()) {
801 unsafe {
802 values.push(Value::from_raw(store, *ty, *raw));
803 }
804 }
805 Ok(values)
806}
807
808pub(crate) enum HostCallOutcome {
809 Ready {
810 func_ty: FunctionType,
811 result: Result<Vec<Value>, RuntimeError>,
812 },
813 #[cfg(feature = "experimental-async")]
814 Future {
815 func_ty: FunctionType,
816 future: Pin<Box<dyn Future<Output = Result<Vec<Value>, RuntimeError>>>>,
817 },
818}
819
820pub(crate) struct DynamicFunction<F> {
822 func: F,
823 store_id: StoreId,
824}
825
826impl<F> DynamicFunction<F>
827where
828 F: Fn(*mut RawValue) -> HostCallOutcome + 'static,
829{
830 unsafe extern "C-unwind" fn func_wrapper(
833 this: &mut VMDynamicFunctionContext<Self>,
834 values_vec: *mut RawValue,
835 ) {
836 let result = on_host_stack(|| {
837 panic::catch_unwind(AssertUnwindSafe(|| match (this.ctx.func)(values_vec) {
838 HostCallOutcome::Ready { func_ty, result } => to_invocation_result(
839 finalize_dynamic_call(this.ctx.store_id, func_ty, values_vec, result),
840 ),
841 #[cfg(feature = "experimental-async")]
842 HostCallOutcome::Future { func_ty, future } => {
843 let awaited = block_on_host_future(future);
844 let result = match awaited {
845 Ok(value) => Ok(value),
846 Err(AsyncRuntimeError::RuntimeError(e)) => Err(e),
847 Err(AsyncRuntimeError::YieldOutsideAsyncContext) => {
848 return InvocationResult::YieldOutsideAsyncContext;
849 }
850 };
851 to_invocation_result(finalize_dynamic_call(
852 this.ctx.store_id,
853 func_ty,
854 values_vec,
855 result,
856 ))
857 }
858 }))
859 });
860
861 match result {
865 Ok(InvocationResult::Success(())) => unsafe {
866 let mut store_wrapper = StoreContext::get_current_transient(this.ctx.store_id);
871 let mut store = store_wrapper.as_mut().unwrap();
872 #[cfg(feature = "experimental-host-interrupt")]
873 if interrupt_registry::is_interrupted(store.objects.id()) {
874 raise_lib_trap(Trap::lib(TrapCode::HostInterrupt))
875 }
876 },
877 Ok(InvocationResult::Exception(exception)) => unsafe {
878 let mut store_wrapper = StoreContext::get_current_transient(this.ctx.store_id);
883 let mut store = store_wrapper.as_mut().unwrap();
884 #[cfg(feature = "experimental-host-interrupt")]
885 if interrupt_registry::is_interrupted(store.objects.id()) {
886 raise_lib_trap(Trap::lib(TrapCode::HostInterrupt))
887 }
888 wasmer_vm::libcalls::throw(
889 store.objects.as_sys(),
890 exception.vm_exceptionref().unwrap_sys_ref().to_u32_exnref(),
891 )
892 },
893 Ok(InvocationResult::Trap(trap)) => unsafe { raise_user_trap(trap) },
894 Ok(InvocationResult::YieldOutsideAsyncContext) => unsafe {
895 raise_lib_trap(Trap::lib(TrapCode::YieldOutsideAsyncContext))
896 },
897 Err(panic) => unsafe { resume_panic(panic) },
898 }
899 }
900
901 fn func_body_ptr(&self) -> VMFunctionCallback {
902 Self::func_wrapper as VMFunctionCallback
903 }
904
905 fn call_trampoline_address(&self) -> VMTrampoline {
906 Self::call_trampoline
907 }
908
909 unsafe extern "C" fn call_trampoline(
910 vmctx: *mut VMContext,
911 _body: VMFunctionCallback,
912 args: *mut RawValue,
913 ) {
914 unsafe {
917 let dynamic_function = &mut *(vmctx as *mut VMDynamicFunctionContext<Self>);
918 Self::func_wrapper(dynamic_function, args);
919 }
920 }
921}
922
923pub(crate) struct StaticFunction<F, T> {
927 pub(crate) store_id: StoreId,
928 pub(crate) env: FunctionEnv<T>,
929 pub(crate) func: F,
930}
931
932impl crate::Function {
933 pub fn into_sys(self) -> crate::backend::sys::function::Function {
935 match self.0 {
936 BackendFunction::Sys(s) => s,
937 _ => panic!("Not a `sys` function!"),
938 }
939 }
940
941 pub fn as_sys(&self) -> &crate::backend::sys::function::Function {
943 match self.0 {
944 BackendFunction::Sys(ref s) => s,
945 _ => panic!("Not a `sys` function!"),
946 }
947 }
948
949 pub fn as_sys_mut(&mut self) -> &mut crate::backend::sys::function::Function {
951 match self.0 {
952 BackendFunction::Sys(ref mut s) => s,
953 _ => panic!("Not a `sys` function!"),
954 }
955 }
956}
957
958macro_rules! impl_host_function {
959 ([$c_struct_representation:ident] $c_struct_name:ident, $( $x:ident ),* ) => {
960 paste::paste! {
961 #[allow(non_snake_case)]
962 pub(crate) fn [<gen_fn_callback_ $c_struct_name:lower _no_env>]
963 <$( $x: FromToNativeWasmType, )* Rets: WasmTypeList, RetsAsResult: IntoResult<Rets>, Func: Fn($( $x , )*) -> RetsAsResult + 'static>
964 (this: &Func) -> crate::backend::sys::vm::VMFunctionCallback {
965 unsafe extern "C-unwind" fn func_wrapper<$( $x, )* Rets, RetsAsResult, Func>( env: &StaticFunction<Func, ()>, $( $x: <$x::Native as NativeWasmType>::Abi, )* ) -> Rets::CStruct
969 where
970 $( $x: FromToNativeWasmType, )*
971 Rets: WasmTypeList,
972 RetsAsResult: IntoResult<Rets>,
973 Func: Fn($( $x , )*) -> RetsAsResult + 'static,
974 {
975 let result = on_host_stack(|| {
976 panic::catch_unwind(AssertUnwindSafe(|| {
977 let mut store_wrapper = unsafe { StoreContext::get_current(env.store_id) };
978 let mut store = store_wrapper.as_mut();
979 $(
980 let $x = unsafe {
981 FromToNativeWasmType::from_native(NativeWasmTypeInto::from_abi(&mut store, $x))
982 };
983 )*
984 to_invocation_result((env.func)($($x),* ).into_result())
985 }))
986 });
987
988 match result {
992 Ok(InvocationResult::Success(result)) => unsafe {
993 let mut store_wrapper = StoreContext::get_current_transient(env.store_id);
998 let mut store = store_wrapper.as_mut().unwrap();
999 #[cfg(feature = "experimental-host-interrupt")]
1000 if interrupt_registry::is_interrupted(store.objects.id()) {
1001 raise_lib_trap(Trap::lib(TrapCode::HostInterrupt))
1002 }
1003 return result.into_c_struct(store);
1004 },
1005 Ok(InvocationResult::Exception(exception)) => unsafe {
1006 let mut store_wrapper = StoreContext::get_current_transient(env.store_id);
1011 let mut store = store_wrapper.as_mut().unwrap();
1012 #[cfg(feature = "experimental-host-interrupt")]
1013 if interrupt_registry::is_interrupted(store.objects.id()) {
1014 raise_lib_trap(Trap::lib(TrapCode::HostInterrupt))
1015 }
1016 wasmer_vm::libcalls::throw(
1017 store.objects.as_sys(),
1018 exception.vm_exceptionref().unwrap_sys_ref().to_u32_exnref(),
1019 )
1020 }
1021 Ok(InvocationResult::Trap(trap)) => unsafe { raise_user_trap(trap) },
1022 Ok(InvocationResult::YieldOutsideAsyncContext) => unsafe {
1023 raise_lib_trap(Trap::lib(TrapCode::YieldOutsideAsyncContext))
1024 },
1025 Err(panic) => unsafe { resume_panic(panic) },
1026 }
1027 }
1028
1029 func_wrapper::< $( $x, )* Rets, RetsAsResult, Func > as _
1030
1031 }
1032
1033 #[allow(non_snake_case)]
1034 pub(crate) fn [<gen_call_trampoline_address_ $c_struct_name:lower _no_env>]
1035 <$( $x: FromToNativeWasmType, )* Rets: WasmTypeList>
1036 () -> crate::backend::sys::vm::VMTrampoline {
1037
1038 unsafe extern "C" fn call_trampoline<$( $x: FromToNativeWasmType, )* Rets: WasmTypeList>
1039 (
1040 vmctx: *mut crate::backend::sys::vm::VMContext,
1041 body: crate::backend::sys::vm::VMFunctionCallback,
1042 args: *mut RawValue,
1043 ) {
1044 let mut _n = 0;
1045
1046 unsafe {
1047 let body: unsafe extern "C" fn(vmctx: *mut crate::backend::sys::vm::VMContext, $( $x: <$x::Native as NativeWasmType>::Abi, )*) -> Rets::CStruct = std::mem::transmute(body);
1048 $(
1049 let $x = *args.add(_n).cast();
1050 _n += 1;
1051 )*
1052 let results = body(vmctx, $( $x ),*);
1053 Rets::write_c_struct_to_ptr(results, args);
1054 }
1055 }
1056
1057 call_trampoline::<$( $x, )* Rets> as _
1058
1059 }
1060
1061 #[allow(non_snake_case)]
1062 pub(crate) fn [<gen_fn_callback_ $c_struct_name:lower>]
1063 <$( $x: FromToNativeWasmType, )* Rets: WasmTypeList, RetsAsResult: IntoResult<Rets>, T: Send + 'static, Func: Fn(FunctionEnvMut<T>, $( $x , )*) -> RetsAsResult + 'static>
1064 (this: &Func) -> crate::backend::sys::vm::VMFunctionCallback {
1065 unsafe extern "C-unwind" fn func_wrapper<T: Send + 'static, $( $x, )* Rets, RetsAsResult, Func>( env: &StaticFunction<Func, T>, $( $x: <$x::Native as NativeWasmType>::Abi, )* ) -> Rets::CStruct
1069 where
1070 $( $x: FromToNativeWasmType, )*
1071 Rets: WasmTypeList,
1072 RetsAsResult: IntoResult<Rets>,
1073 Func: Fn(FunctionEnvMut<T>, $( $x , )*) -> RetsAsResult + 'static,
1074 {
1075 let result = wasmer_vm::on_host_stack(|| {
1076 panic::catch_unwind(AssertUnwindSafe(|| {
1077 let mut store_wrapper = unsafe { StoreContext::get_current(env.store_id) };
1078 let mut store = store_wrapper.as_mut();
1079 $(
1080 let $x = unsafe {
1081 FromToNativeWasmType::from_native(NativeWasmTypeInto::from_abi(&mut store, $x))
1082 };
1083 )*
1084 let f_env = crate::backend::sys::function::env::FunctionEnvMut {
1085 store_mut: store,
1086 func_env: env.env.as_sys().clone(),
1087 }.into();
1088 to_invocation_result((env.func)(f_env, $($x),* ).into_result())
1089 }))
1090 });
1091
1092 match result {
1096 Ok(InvocationResult::Success(result)) => unsafe {
1097 let mut store_wrapper = StoreContext::get_current_transient(env.store_id);
1102 let mut store = store_wrapper.as_mut().unwrap();
1103 #[cfg(feature = "experimental-host-interrupt")]
1104 if interrupt_registry::is_interrupted(store.objects.id()) {
1105 raise_lib_trap(Trap::lib(TrapCode::HostInterrupt))
1106 }
1107 return result.into_c_struct(store);
1108 },
1109 Ok(InvocationResult::Exception(exception)) => unsafe {
1110 let mut store_wrapper = StoreContext::get_current_transient(env.store_id);
1115 let mut store = store_wrapper.as_mut().unwrap();
1116 #[cfg(feature = "experimental-host-interrupt")]
1117 if interrupt_registry::is_interrupted(store.objects.id()) {
1118 raise_lib_trap(Trap::lib(TrapCode::HostInterrupt))
1119 }
1120 wasmer_vm::libcalls::throw(
1121 store.objects.as_sys(),
1122 exception.vm_exceptionref().unwrap_sys_ref().to_u32_exnref(),
1123 )
1124 }
1125 Ok(InvocationResult::Trap(trap)) => unsafe { raise_user_trap(trap) },
1126 Ok(InvocationResult::YieldOutsideAsyncContext) => unsafe {
1127 raise_lib_trap(Trap::lib(TrapCode::YieldOutsideAsyncContext))
1128 },
1129 Err(panic) => unsafe { resume_panic(panic) },
1130 }
1131 }
1132 func_wrapper::< T, $( $x, )* Rets, RetsAsResult, Func > as _
1133 }
1134
1135 #[allow(non_snake_case)]
1136 pub(crate) fn [<gen_call_trampoline_address_ $c_struct_name:lower>]
1137 <$( $x: FromToNativeWasmType, )* Rets: WasmTypeList>
1138 () -> crate::backend::sys::vm::VMTrampoline {
1139
1140 unsafe extern "C" fn call_trampoline<$( $x: FromToNativeWasmType, )* Rets: WasmTypeList>(
1141 vmctx: *mut crate::backend::sys::vm::VMContext,
1142 body: crate::backend::sys::vm::VMFunctionCallback,
1143 args: *mut RawValue,
1144 ) {
1145 unsafe {
1146 let body: unsafe extern "C" fn(vmctx: *mut crate::backend::sys::vm::VMContext, $( $x: <$x::Native as NativeWasmType>::Abi, )*) -> Rets::CStruct = std::mem::transmute(body);
1147 let mut _n = 0;
1148 $(
1149 let $x = *args.add(_n).cast();
1150 _n += 1;
1151 )*
1152
1153 let results = body(vmctx, $( $x ),*);
1154
1155 Rets::write_c_struct_to_ptr(results, args);
1156 }
1157 }
1158
1159 call_trampoline::<$( $x, )* Rets> as _
1160 }
1161 }};
1162}
1163
1164impl_host_function!([C] S0,);
1167impl_host_function!([transparent] S1, A1);
1168impl_host_function!([C] S2, A1, A2);
1169impl_host_function!([C] S3, A1, A2, A3);
1170impl_host_function!([C] S4, A1, A2, A3, A4);
1171impl_host_function!([C] S5, A1, A2, A3, A4, A5);
1172impl_host_function!([C] S6, A1, A2, A3, A4, A5, A6);
1173impl_host_function!([C] S7, A1, A2, A3, A4, A5, A6, A7);
1174impl_host_function!([C] S8, A1, A2, A3, A4, A5, A6, A7, A8);
1175impl_host_function!([C] S9, A1, A2, A3, A4, A5, A6, A7, A8, A9);
1176impl_host_function!([C] S10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
1177impl_host_function!([C] S11, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11);
1178impl_host_function!([C] S12, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12);
1179impl_host_function!([C] S13, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13);
1180impl_host_function!([C] S14, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14);
1181impl_host_function!([C] S15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15);
1182impl_host_function!([C] S16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16);
1183impl_host_function!([C] S17, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17);
1184impl_host_function!([C] S18, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18);
1185impl_host_function!([C] S19, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19);
1186impl_host_function!([C] S20, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20);
1187impl_host_function!([C] S21, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21);
1188impl_host_function!([C] S22, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22);
1189impl_host_function!([C] S23, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23);
1190impl_host_function!([C] S24, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24);
1191impl_host_function!([C] S25, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25);
1192impl_host_function!([C] S26, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26);