1use super::{invoke_wasm_and_catch_traps, HostAbi};
2use crate::store::{AutoAssertNoGc, StoreOpaque};
3use crate::{AsContextMut, ExternRef, Func, FuncType, StoreContextMut, ValRaw, ValType};
4use anyhow::{bail, Result};
5use std::marker;
6use std::mem::{self, MaybeUninit};
7use std::ptr;
8use wasmtime_runtime::{
9 VMCallerCheckedFuncRef, VMContext, VMFunctionBody, VMOpaqueContext, VMSharedSignatureIndex,
10};
11
12#[repr(transparent)] pub struct TypedFunc<Params, Results> {
22 _a: marker::PhantomData<fn(Params) -> Results>,
23 func: Func,
24}
25
26impl<Params, Results> Copy for TypedFunc<Params, Results> {}
27
28impl<Params, Results> Clone for TypedFunc<Params, Results> {
29 fn clone(&self) -> TypedFunc<Params, Results> {
30 *self
31 }
32}
33
34impl<Params, Results> TypedFunc<Params, Results>
35where
36 Params: WasmParams,
37 Results: WasmResults,
38{
39 pub unsafe fn new_unchecked(func: Func) -> TypedFunc<Params, Results> {
52 TypedFunc {
53 _a: marker::PhantomData,
54 func,
55 }
56 }
57
58 pub fn func(&self) -> &Func {
61 &self.func
62 }
63
64 pub fn call(&self, mut store: impl AsContextMut, params: Params) -> Result<Results> {
82 let mut store = store.as_context_mut();
83 assert!(
84 !store.0.async_support(),
85 "must use `call_async` with async stores"
86 );
87 let func = self.func.caller_checked_anyfunc(store.0);
88 unsafe { Self::call_raw(&mut store, func, params) }
89 }
90
91 #[cfg(feature = "async")]
109 #[cfg_attr(nightlydoc, doc(cfg(feature = "async")))]
110 pub async fn call_async<T>(
111 &self,
112 mut store: impl AsContextMut<Data = T>,
113 params: Params,
114 ) -> Result<Results>
115 where
116 T: Send,
117 {
118 let mut store = store.as_context_mut();
119 assert!(
120 store.0.async_support(),
121 "must use `call` with non-async stores"
122 );
123 store
124 .on_fiber(|store| {
125 let func = self.func.caller_checked_anyfunc(store.0);
126 unsafe { Self::call_raw(store, func, params) }
127 })
128 .await?
129 }
130
131 pub(crate) unsafe fn call_raw<T>(
132 store: &mut StoreContextMut<'_, T>,
133 func: ptr::NonNull<VMCallerCheckedFuncRef>,
134 params: Params,
135 ) -> Result<Results> {
136 if cfg!(debug_assertions) {
139 Self::debug_typecheck(store.0, func.as_ref().type_index);
140 }
141
142 if params.externrefs_count()
144 > store
145 .0
146 .externref_activations_table()
147 .bump_capacity_remaining()
148 {
149 store.gc();
150 }
151
152 let params = {
157 let mut store = AutoAssertNoGc::new(&mut **store.as_context_mut().0);
161
162 match params.into_abi(&mut store) {
163 Some(abi) => abi,
164 None => {
165 bail!("attempt to pass cross-`Store` value to Wasm as function argument")
166 }
167 }
168 };
169
170 let mut captures = (func, MaybeUninit::uninit(), params, false);
176
177 let result = invoke_wasm_and_catch_traps(store, |caller| {
178 let (anyfunc, ret, params, returned) = &mut captures;
179 let anyfunc = anyfunc.as_ref();
180 let result = Params::invoke::<Results>(
181 anyfunc.func_ptr.as_ptr(),
182 anyfunc.vmctx,
183 caller,
184 *params,
185 );
186 ptr::write(ret.as_mut_ptr(), result);
187 *returned = true
188 });
189 let (_, ret, _, returned) = captures;
190 debug_assert_eq!(result.is_ok(), returned);
191 result?;
192 Ok(Results::from_abi(store.0, ret.assume_init()))
193 }
194
195 fn debug_typecheck(store: &StoreOpaque, func: VMSharedSignatureIndex) {
197 let ty = FuncType::from_wasm_func_type(
198 store
199 .engine()
200 .signatures()
201 .lookup_type(func)
202 .expect("signature should be registered"),
203 );
204 Params::typecheck(ty.params()).expect("params should match");
205 Results::typecheck(ty.results()).expect("results should match");
206 }
207}
208
209pub unsafe trait WasmTy: Send {
218 #[doc(hidden)]
219 type Abi: Copy;
220 #[doc(hidden)]
221 #[inline]
222 fn typecheck(ty: crate::ValType) -> Result<()> {
223 if ty == Self::valtype() {
224 Ok(())
225 } else {
226 bail!("expected {} found {}", Self::valtype(), ty)
227 }
228 }
229 #[doc(hidden)]
230 fn valtype() -> ValType;
231 #[doc(hidden)]
232 fn compatible_with_store(&self, store: &StoreOpaque) -> bool;
233 #[doc(hidden)]
234 fn is_externref(&self) -> bool;
235 #[doc(hidden)]
236 unsafe fn abi_from_raw(raw: *mut ValRaw) -> Self::Abi;
237 #[doc(hidden)]
238 unsafe fn abi_into_raw(abi: Self::Abi, raw: *mut ValRaw);
239 #[doc(hidden)]
240 fn into_abi(self, store: &mut StoreOpaque) -> Self::Abi;
241 #[doc(hidden)]
242 unsafe fn from_abi(abi: Self::Abi, store: &mut StoreOpaque) -> Self;
243}
244
245macro_rules! integers {
246 ($($primitive:ident/$get_primitive:ident => $ty:ident in $raw:ident)*) => ($(
247 unsafe impl WasmTy for $primitive {
248 type Abi = $primitive;
249 #[inline]
250 fn valtype() -> ValType {
251 ValType::$ty
252 }
253 #[inline]
254 fn compatible_with_store(&self, _: &StoreOpaque) -> bool {
255 true
256 }
257 #[inline]
258 fn is_externref(&self) -> bool {
259 false
260 }
261 #[inline]
262 unsafe fn abi_from_raw(raw: *mut ValRaw) -> $primitive {
263 (*raw).$get_primitive()
264 }
265 #[inline]
266 unsafe fn abi_into_raw(abi: $primitive, raw: *mut ValRaw) {
267 *raw = ValRaw::$primitive(abi);
268 }
269 #[inline]
270 fn into_abi(self, _store: &mut StoreOpaque) -> Self::Abi {
271 self
272 }
273 #[inline]
274 unsafe fn from_abi(abi: Self::Abi, _store: &mut StoreOpaque) -> Self {
275 abi
276 }
277 }
278 )*)
279}
280
281integers! {
282 i32/get_i32 => I32 in i32
283 i64/get_i64 => I64 in i64
284 u32/get_u32 => I32 in i32
285 u64/get_u64 => I64 in i64
286}
287
288macro_rules! floats {
289 ($($float:ident/$int:ident/$get_float:ident => $ty:ident)*) => ($(
290 unsafe impl WasmTy for $float {
291 type Abi = $float;
292 #[inline]
293 fn valtype() -> ValType {
294 ValType::$ty
295 }
296 #[inline]
297 fn compatible_with_store(&self, _: &StoreOpaque) -> bool {
298 true
299 }
300 #[inline]
301 fn is_externref(&self) -> bool {
302 false
303 }
304 #[inline]
305 unsafe fn abi_from_raw(raw: *mut ValRaw) -> $float {
306 $float::from_bits((*raw).$get_float())
307 }
308 #[inline]
309 unsafe fn abi_into_raw(abi: $float, raw: *mut ValRaw) {
310 *raw = ValRaw::$float(abi.to_bits());
311 }
312 #[inline]
313 fn into_abi(self, _store: &mut StoreOpaque) -> Self::Abi {
314 self
315 }
316 #[inline]
317 unsafe fn from_abi(abi: Self::Abi, _store: &mut StoreOpaque) -> Self {
318 abi
319 }
320 }
321 )*)
322}
323
324floats! {
325 f32/u32/get_f32 => F32
326 f64/u64/get_f64 => F64
327}
328
329unsafe impl WasmTy for Option<ExternRef> {
330 type Abi = *mut u8;
331
332 #[inline]
333 fn valtype() -> ValType {
334 ValType::ExternRef
335 }
336
337 #[inline]
338 fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
339 true
340 }
341
342 #[inline]
343 fn is_externref(&self) -> bool {
344 true
345 }
346
347 #[inline]
348 unsafe fn abi_from_raw(raw: *mut ValRaw) -> *mut u8 {
349 (*raw).get_externref() as *mut u8
350 }
351
352 #[inline]
353 unsafe fn abi_into_raw(abi: *mut u8, raw: *mut ValRaw) {
354 *raw = ValRaw::externref(abi as usize);
355 }
356
357 #[inline]
358 fn into_abi(self, store: &mut StoreOpaque) -> Self::Abi {
359 if let Some(x) = self {
360 let abi = x.inner.as_raw();
361 unsafe {
362 let mut store = AutoAssertNoGc::new(store);
391 store.insert_vmexternref_without_gc(x.inner);
392 }
393 abi
394 } else {
395 ptr::null_mut()
396 }
397 }
398
399 #[inline]
400 unsafe fn from_abi(abi: Self::Abi, _store: &mut StoreOpaque) -> Self {
401 if abi.is_null() {
402 None
403 } else {
404 Some(ExternRef {
405 inner: wasmtime_runtime::VMExternRef::clone_from_raw(abi),
406 })
407 }
408 }
409}
410
411unsafe impl WasmTy for Option<Func> {
412 type Abi = *mut wasmtime_runtime::VMCallerCheckedFuncRef;
413
414 #[inline]
415 fn valtype() -> ValType {
416 ValType::FuncRef
417 }
418
419 #[inline]
420 fn compatible_with_store<'a>(&self, store: &StoreOpaque) -> bool {
421 if let Some(f) = self {
422 store.store_data().contains(f.0)
423 } else {
424 true
425 }
426 }
427
428 #[inline]
429 fn is_externref(&self) -> bool {
430 false
431 }
432
433 #[inline]
434 unsafe fn abi_from_raw(raw: *mut ValRaw) -> Self::Abi {
435 (*raw).get_funcref() as Self::Abi
436 }
437
438 #[inline]
439 unsafe fn abi_into_raw(abi: Self::Abi, raw: *mut ValRaw) {
440 *raw = ValRaw::funcref(abi as usize);
441 }
442
443 #[inline]
444 fn into_abi(self, store: &mut StoreOpaque) -> Self::Abi {
445 if let Some(f) = self {
446 f.caller_checked_anyfunc(store).as_ptr()
447 } else {
448 ptr::null_mut()
449 }
450 }
451
452 #[inline]
453 unsafe fn from_abi(abi: Self::Abi, store: &mut StoreOpaque) -> Self {
454 Func::from_caller_checked_anyfunc(store, abi)
455 }
456}
457
458pub unsafe trait WasmParams: Send {
464 #[doc(hidden)]
465 type Abi: Copy;
466
467 #[doc(hidden)]
468 fn typecheck(params: impl ExactSizeIterator<Item = crate::ValType>) -> Result<()>;
469
470 #[doc(hidden)]
471 fn externrefs_count(&self) -> usize;
472
473 #[doc(hidden)]
474 fn into_abi(self, store: &mut StoreOpaque) -> Option<Self::Abi>;
475
476 #[doc(hidden)]
477 unsafe fn invoke<R: WasmResults>(
478 func: *const VMFunctionBody,
479 vmctx1: *mut VMOpaqueContext,
480 vmctx2: *mut VMContext,
481 abi: Self::Abi,
482 ) -> R::ResultAbi;
483}
484
485unsafe impl<T> WasmParams for T
488where
489 T: WasmTy,
490{
491 type Abi = <(T,) as WasmParams>::Abi;
492
493 fn typecheck(params: impl ExactSizeIterator<Item = crate::ValType>) -> Result<()> {
494 <(T,) as WasmParams>::typecheck(params)
495 }
496
497 #[inline]
498 fn externrefs_count(&self) -> usize {
499 T::is_externref(self) as usize
500 }
501
502 #[inline]
503 fn into_abi(self, store: &mut StoreOpaque) -> Option<Self::Abi> {
504 <(T,) as WasmParams>::into_abi((self,), store)
505 }
506
507 unsafe fn invoke<R: WasmResults>(
508 func: *const VMFunctionBody,
509 vmctx1: *mut VMOpaqueContext,
510 vmctx2: *mut VMContext,
511 abi: Self::Abi,
512 ) -> R::ResultAbi {
513 <(T,) as WasmParams>::invoke::<R>(func, vmctx1, vmctx2, abi)
514 }
515}
516
517macro_rules! impl_wasm_params {
518 ($n:tt $($t:ident)*) => {
519 #[allow(non_snake_case)]
520 unsafe impl<$($t: WasmTy,)*> WasmParams for ($($t,)*) {
521 type Abi = ($($t::Abi,)*);
522
523 fn typecheck(mut params: impl ExactSizeIterator<Item = crate::ValType>) -> Result<()> {
524 let mut _n = 0;
525
526 $(
527 match params.next() {
528 Some(t) => {
529 _n += 1;
530 $t::typecheck(t)?
531 },
532 None => bail!("expected {} types, found {}", $n, params.len() + _n),
533 }
534 )*
535
536 match params.next() {
537 None => Ok(()),
538 Some(_) => {
539 _n += 1;
540 bail!("expected {} types, found {}", $n, params.len() + _n)
541 },
542 }
543 }
544
545 #[inline]
546 fn externrefs_count(&self) -> usize {
547 let ($(ref $t,)*) = self;
548 0 $(
549 + $t.is_externref() as usize
550 )*
551 }
552
553
554 #[inline]
555 fn into_abi(self, _store: &mut StoreOpaque) -> Option<Self::Abi> {
556 let ($($t,)*) = self;
557 $(
558 let $t = if $t.compatible_with_store(_store) {
559 $t.into_abi(_store)
560 } else {
561 return None;
562 };
563 )*
564 Some(($($t,)*))
565 }
566
567 unsafe fn invoke<R: WasmResults>(
568 func: *const VMFunctionBody,
569 vmctx1: *mut VMOpaqueContext,
570 vmctx2: *mut VMContext,
571 abi: Self::Abi,
572 ) -> R::ResultAbi {
573 let fnptr = mem::transmute::<
574 *const VMFunctionBody,
575 unsafe extern "C" fn(
576 *mut VMOpaqueContext,
577 *mut VMContext,
578 $($t::Abi,)*
579 <R::ResultAbi as HostAbi>::Retptr,
580 ) -> <R::ResultAbi as HostAbi>::Abi,
581 >(func);
582 let ($($t,)*) = abi;
583 <R::ResultAbi as HostAbi>::call(|retptr| {
591 let fnptr = wasmtime_runtime::prepare_host_to_wasm_trampoline(vmctx2, fnptr);
592 fnptr(vmctx1, vmctx2, $($t,)* retptr)
593 })
594 }
595 }
596 };
597}
598
599for_each_function_signature!(impl_wasm_params);
600
601pub unsafe trait WasmResults: WasmParams {
608 #[doc(hidden)]
609 type ResultAbi: HostAbi;
610 #[doc(hidden)]
611 unsafe fn from_abi(store: &mut StoreOpaque, abi: Self::ResultAbi) -> Self;
612}
613
614unsafe impl<T: WasmTy> WasmResults for T
616where
617 (T::Abi,): HostAbi,
618{
619 type ResultAbi = <(T,) as WasmResults>::ResultAbi;
620
621 unsafe fn from_abi(store: &mut StoreOpaque, abi: Self::ResultAbi) -> Self {
622 <(T,) as WasmResults>::from_abi(store, abi).0
623 }
624}
625
626macro_rules! impl_wasm_results {
627 ($n:tt $($t:ident)*) => {
628 #[allow(non_snake_case, unused_variables)]
629 unsafe impl<$($t: WasmTy,)*> WasmResults for ($($t,)*)
630 where ($($t::Abi,)*): HostAbi
631 {
632 type ResultAbi = ($($t::Abi,)*);
633
634 #[inline]
635 unsafe fn from_abi(store: &mut StoreOpaque, abi: Self::ResultAbi) -> Self {
636 let ($($t,)*) = abi;
637 ($($t::from_abi($t, store),)*)
638 }
639 }
640 };
641}
642
643for_each_function_signature!(impl_wasm_results);