1#[cfg(feature = "component-model-async")]
4use crate::component::concurrent::{self, Accessor, Status};
5use crate::component::func::{LiftContext, LowerContext};
6use crate::component::matching::InstanceType;
7use crate::component::storage::{slice_to_storage, slice_to_storage_mut};
8use crate::component::types::ComponentFunc;
9use crate::component::{ComponentNamedList, Instance, Lift, Lower, Val};
10use crate::prelude::*;
11use crate::runtime::vm::component::{
12 ComponentInstance, VMComponentContext, VMLowering, VMLoweringCallee,
13};
14use crate::runtime::vm::{VMOpaqueContext, VMStore};
15use crate::store::Asyncness;
16use crate::{AsContextMut, StoreContextMut, ValRaw};
17use alloc::sync::Arc;
18use core::any::Any;
19use core::mem::{self, MaybeUninit};
20#[cfg(feature = "async")]
21use core::pin::Pin;
22use core::ptr::NonNull;
23use wasmtime_environ::component::{
24 CanonicalAbiInfo, InterfaceType, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, OptionsIndex, TypeFuncIndex,
25};
26
27pub struct HostFunc {
33 entrypoint: VMLoweringCallee,
35
36 typecheck: fn(TypeFuncIndex, &InstanceType<'_>) -> Result<()>,
43
44 func: Box<dyn Any + Send + Sync>,
50
51 asyncness: Asyncness,
54}
55
56impl core::fmt::Debug for HostFunc {
57 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
58 f.debug_struct("HostFunc").finish_non_exhaustive()
59 }
60}
61
62enum HostResult<T> {
63 Done(Result<T>),
64 #[cfg(feature = "component-model-async")]
65 Future(Pin<Box<dyn Future<Output = Result<T>> + Send>>),
66}
67
68impl HostFunc {
69 fn new<T, F, P, R>(asyncness: Asyncness, func: F) -> Result<Arc<HostFunc>>
78 where
79 T: 'static,
80 R: Send + Sync + 'static,
81 F: HostFn<T, P, R> + Send + Sync + 'static,
82 {
83 Ok(try_new::<Arc<_>>(HostFunc {
84 entrypoint: F::cabi_entrypoint,
85 typecheck: F::typecheck,
86 func: try_new::<Box<_>>(func)?,
87 asyncness,
88 })?)
89 }
90
91 pub(crate) fn func_wrap<T, F, P, R>(func: F) -> Result<Arc<HostFunc>>
93 where
94 T: 'static,
95 F: Fn(StoreContextMut<T>, P) -> Result<R> + Send + Sync + 'static,
96 P: ComponentNamedList + Lift + 'static,
97 R: ComponentNamedList + Lower + 'static,
98 {
99 Self::new(
100 Asyncness::No,
101 StaticHostFn::<_, false>::new(move |store, params| {
102 HostResult::Done(func(store, params))
103 }),
104 )
105 }
106
107 #[cfg(feature = "async")]
109 pub(crate) fn func_wrap_async<T, F, P, R>(func: F) -> Result<Arc<HostFunc>>
110 where
111 T: 'static,
112 F: Fn(StoreContextMut<'_, T>, P) -> Box<dyn Future<Output = Result<R>> + Send + '_>
113 + Send
114 + Sync
115 + 'static,
116 P: ComponentNamedList + Lift + 'static,
117 R: ComponentNamedList + Lower + 'static,
118 {
119 Self::new(
120 Asyncness::Yes,
121 StaticHostFn::<_, false>::new(move |store, params| {
122 HostResult::Done(
123 store
124 .block_on(|store| Pin::from(func(store, params)))
125 .and_then(|r| r),
126 )
127 }),
128 )
129 }
130
131 #[cfg(feature = "component-model-async")]
133 pub(crate) fn func_wrap_concurrent<T, F, P, R>(func: F) -> Result<Arc<HostFunc>>
134 where
135 T: 'static,
136 F: Fn(&Accessor<T>, P) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>>
137 + Send
138 + Sync
139 + 'static,
140 P: ComponentNamedList + Lift + 'static,
141 R: ComponentNamedList + Lower + 'static,
142 {
143 let func = Arc::new(func);
144 Self::new(
145 Asyncness::Yes,
146 StaticHostFn::<_, true>::new(move |store, params| {
147 let func = func.clone();
148 HostResult::Future(Box::pin(
149 store.wrap_call(move |accessor| func(accessor, params)),
150 ))
151 }),
152 )
153 }
154
155 pub(crate) fn func_new<T, F>(func: F) -> Result<Arc<HostFunc>>
157 where
158 T: 'static,
159 F: Fn(StoreContextMut<'_, T>, ComponentFunc, &[Val], &mut [Val]) -> Result<()>
160 + Send
161 + Sync
162 + 'static,
163 {
164 Self::new(
165 Asyncness::No,
166 DynamicHostFn::<_, false>::new(
167 move |store, ty, mut params_and_results, result_start| {
168 let (params, results) = params_and_results.split_at_mut(result_start);
169 let result = func(store, ty, params, results).map(move |()| params_and_results);
170 HostResult::Done(result)
171 },
172 ),
173 )
174 }
175
176 #[cfg(feature = "async")]
178 pub(crate) fn func_new_async<T, F>(func: F) -> Result<Arc<HostFunc>>
179 where
180 T: 'static,
181 F: for<'a> Fn(
182 StoreContextMut<'a, T>,
183 ComponentFunc,
184 &'a [Val],
185 &'a mut [Val],
186 ) -> Box<dyn Future<Output = Result<()>> + Send + 'a>
187 + Send
188 + Sync
189 + 'static,
190 {
191 Self::new(
192 Asyncness::Yes,
193 DynamicHostFn::<_, false>::new(
194 move |store, ty, mut params_and_results, result_start| {
195 let (params, results) = params_and_results.split_at_mut(result_start);
196 let result = store
197 .with_blocking(|store, cx| {
198 cx.block_on(Pin::from(func(store, ty, params, results)))
199 })
200 .and_then(|r| r);
201 let result = result.map(move |()| params_and_results);
202 HostResult::Done(result)
203 },
204 ),
205 )
206 }
207
208 #[cfg(feature = "component-model-async")]
210 pub(crate) fn func_new_concurrent<T, F>(func: F) -> Result<Arc<HostFunc>>
211 where
212 T: 'static,
213 F: for<'a> Fn(
214 &'a Accessor<T>,
215 ComponentFunc,
216 &'a [Val],
217 &'a mut [Val],
218 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
219 + Send
220 + Sync
221 + 'static,
222 {
223 let func = Arc::new(func);
224 Self::new(
225 Asyncness::Yes,
226 DynamicHostFn::<_, true>::new(
227 move |store, ty, mut params_and_results, result_start| {
228 let func = func.clone();
229 HostResult::Future(Box::pin(store.wrap_call(move |accessor| {
230 Box::pin(async move {
231 let (params, results) = params_and_results.split_at_mut(result_start);
232 func(accessor, ty, params, results).await?;
233 Ok(params_and_results)
234 })
235 })))
236 },
237 ),
238 )
239 }
240
241 pub fn typecheck(&self, ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()> {
242 (self.typecheck)(ty, types)
243 }
244
245 pub fn lowering(&self) -> VMLowering {
246 let data = NonNull::from(&*self.func).cast();
247 VMLowering {
248 callee: NonNull::new(self.entrypoint as *mut _).unwrap().into(),
249 data: data.into(),
250 }
251 }
252
253 pub fn asyncness(&self) -> Asyncness {
254 self.asyncness
255 }
256}
257
258enum Source<'a> {
260 Flat(&'a [ValRaw]),
262 Memory(usize),
265}
266
267enum Destination<'a> {
269 Flat(&'a mut [MaybeUninit<ValRaw>]),
271 Memory(usize),
274}
275
276trait HostFn<T, P, R>
282where
283 T: 'static,
284 R: Send + Sync + 'static,
285{
286 const ASYNC: bool;
289
290 fn typecheck(ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()>;
293
294 fn run(&self, store: StoreContextMut<'_, T>, params: P) -> HostResult<R>;
296
297 fn lift_params(cx: &mut LiftContext<'_>, ty: TypeFuncIndex, source: Source<'_>) -> Result<P>;
300
301 fn lower_result(
304 cx: &mut LowerContext<'_, T>,
305 ty: TypeFuncIndex,
306 result: R,
307 dst: Destination<'_>,
308 ) -> Result<()>;
309
310 unsafe extern "C" fn cabi_entrypoint(
330 cx: NonNull<VMOpaqueContext>,
331 data: NonNull<u8>,
332 ty: u32,
333 options: u32,
334 storage: NonNull<MaybeUninit<ValRaw>>,
335 storage_len: usize,
336 ) -> bool
337 where
338 Self: Sized,
339 {
340 let cx = unsafe { VMComponentContext::from_opaque(cx) };
341 unsafe {
342 ComponentInstance::enter_host_from_wasm(cx, |store, instance| {
343 let mut store = store.unchecked_context_mut();
344 let ty = TypeFuncIndex::from_u32(ty);
345 let options = OptionsIndex::from_u32(options);
346 let storage = NonNull::slice_from_raw_parts(storage, storage_len).as_mut();
347 let data = data.cast::<Self>().as_ref();
348 data.entrypoint(store.as_context_mut(), instance, ty, options, storage)
349 })
350 }
351 }
352
353 fn entrypoint(
356 &self,
357 mut store: StoreContextMut<'_, T>,
358 instance: Instance,
359 ty: TypeFuncIndex,
360 options: OptionsIndex,
361 storage: &mut [MaybeUninit<ValRaw>],
362 ) -> Result<()> {
363 let vminstance = instance.id().get(store.0);
364 let async_ = vminstance.component().env_component().options[options].async_;
365
366 if !async_ && Self::ASYNC {
370 store.0.check_blocking()?;
371 }
372
373 if async_ {
374 #[cfg(feature = "component-model-async")]
375 {
376 self.call_async_lower(store.as_context_mut(), instance, ty, options, storage)
377 }
378 #[cfg(not(feature = "component-model-async"))]
379 unreachable!(
380 "async-lowered imports should have failed validation \
381 when `component-model-async` feature disabled"
382 );
383 } else {
384 self.call_sync_lower(store.as_context_mut(), instance, ty, options, storage)
385 }
386 }
387
388 fn call_sync_lower(
396 &self,
397 mut store: StoreContextMut<'_, T>,
398 instance: Instance,
399 ty: TypeFuncIndex,
400 options: OptionsIndex,
401 storage: &mut [MaybeUninit<ValRaw>],
402 ) -> Result<()> {
403 let entered_host_task = store.0.host_task_create()?;
404
405 let mut lift = LiftContext::new(store.0.store_opaque_mut(), options, instance)?;
406 let (params, rest) = self.load_params(&mut lift, ty, MAX_FLAT_PARAMS, storage)?;
407
408 let ret = match self.run(store.as_context_mut(), params) {
409 HostResult::Done(result) => result?,
410 #[cfg(feature = "component-model-async")]
411 HostResult::Future(future) => {
412 concurrent::poll_and_block(store.0, entered_host_task, future)?
413 }
414 };
415
416 let mut lower = LowerContext::new(store, options, instance);
417 let fty = &lower.types[ty];
418 let result_tys = &lower.types[fty.results];
419 let dst = if let Some(cnt) = result_tys.abi.flat_count(MAX_FLAT_RESULTS) {
420 Destination::Flat(&mut storage[..cnt])
421 } else {
422 let ptr = unsafe { rest[0].assume_init_ref() };
426 Destination::Memory(validate_inbounds_dynamic(
427 &result_tys.abi,
428 lower.as_slice_mut(),
429 ptr,
430 )?)
431 };
432 lower.validate_scope_exit()?;
433 lower.store.0.host_task_delete(entered_host_task)?;
434 Self::lower_raw(&mut lower, ty, ret, dst)
435 }
436
437 #[cfg(feature = "component-model-async")]
443 fn call_async_lower(
444 &self,
445 store: StoreContextMut<'_, T>,
446 instance: Instance,
447 ty: TypeFuncIndex,
448 options: OptionsIndex,
449 storage: &mut [MaybeUninit<ValRaw>],
450 ) -> Result<()> {
451 use wasmtime_environ::component::MAX_FLAT_ASYNC_PARAMS;
452
453 let (component, store) = instance.component_and_store_mut(store.0);
454 let mut store = StoreContextMut(store);
455 let types = component.types();
456 let fty = &types[ty];
457 let entered_host_task = store.0.host_task_create()?;
458
459 let mut lift = LiftContext::new(store.0.store_opaque_mut(), options, instance)?;
462 let (params, rest) = self.load_params(&mut lift, ty, MAX_FLAT_ASYNC_PARAMS, storage)?;
463
464 let retptr = if !lift.types[fty.results].types.is_empty() {
466 let mut lower = LowerContext::new(store.as_context_mut(), options, instance);
467 let ptr = unsafe { rest[0].assume_init_ref() };
470 let result_tys = &lower.types[fty.results];
471 validate_inbounds_dynamic(&result_tys.abi, lower.as_slice_mut(), ptr)?
472 } else {
473 0
478 };
479
480 let host_result = self.run(store.as_context_mut(), params);
481
482 let rc = match host_result {
483 HostResult::Done(result) => {
484 let result = result?;
485 let mut lower = LowerContext::new(store, options, instance);
486 lower.validate_scope_exit()?;
487 lower.store.0.host_task_delete(entered_host_task)?;
488 Self::lower_raw(&mut lower, ty, result, Destination::Memory(retptr))?;
489 Status::Returned.pack(None)
490 }
491 HostResult::Future(future) => instance.first_poll(
492 store.as_context_mut(),
493 entered_host_task,
494 future,
495 move |store, ret, immediate| {
496 let mut lower = LowerContext::new(store, options, instance);
497 lower.validate_scope_exit()?;
498 if immediate {
499 lower.store.0.host_task_delete(entered_host_task)?;
500 }
501 if let Some(result) = ret {
507 Self::lower_raw(&mut lower, ty, result, Destination::Memory(retptr))?;
508 }
509 Ok(())
510 },
511 )?,
512 };
513
514 storage[0].write(ValRaw::u32(rc));
515
516 Ok(())
517 }
518
519 fn load_params<'a>(
524 &self,
525 lift: &mut LiftContext<'_>,
526 ty: TypeFuncIndex,
527 max_flat_params: usize,
528 storage: &'a [MaybeUninit<ValRaw>],
529 ) -> Result<(P, &'a [MaybeUninit<ValRaw>])> {
530 let fty = &lift.types[ty];
531 let param_tys = &lift.types[fty.params];
532 let param_flat_count = param_tys.abi.flat_count(max_flat_params);
533 let src = match param_flat_count {
534 Some(cnt) => {
535 let params = &storage[..cnt];
536 Source::Flat(unsafe { mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(params) })
540 }
541 None => {
542 let ptr = unsafe { storage[0].assume_init_ref() };
546 Source::Memory(validate_inbounds_dynamic(
547 ¶m_tys.abi,
548 lift.memory(),
549 ptr,
550 )?)
551 }
552 };
553 let params = Self::lift_params(lift, ty, src)?;
554 Ok((params, &storage[param_flat_count.unwrap_or(1)..]))
555 }
556
557 fn lower_raw(
558 lower: &mut LowerContext<'_, T>,
559 ty: TypeFuncIndex,
560 ret: R,
561 dst: Destination<'_>,
562 ) -> Result<()> {
563 let caller_instance = lower.options().instance;
564 let mut flags = lower.instance_mut().instance_flags(caller_instance);
565 unsafe {
566 flags.set_may_leave(false);
567 }
568 Self::lower_result(lower, ty, ret, dst)?;
569 unsafe {
570 flags.set_may_leave(true);
571 }
572 Ok(())
573 }
574}
575
576fn typecheck_async(host_async: bool, wit_async: bool) -> Result<()> {
589 if host_async == wit_async {
590 return Ok(());
591 }
592 if wit_async {
593 bail!(
594 "type mismatch with async: this import is declared `async func` in WIT, but was \
595 satisfied with a sync-style host function (`func_new`/`func_wrap`, or \
596 `func_new_async`/`func_wrap_async` — despite the name, these implement a \
597 *sync*-WIT-typed function via blocking host code, not an `async func` import); \
598 use `func_new_concurrent`/`func_wrap_concurrent` instead"
599 );
600 } else {
601 bail!(
602 "type mismatch with async: this import's WIT type is a plain (non-`async`) \
603 function, but was satisfied with `func_new_concurrent`/`func_wrap_concurrent`, \
604 which is only for `async func`-typed imports; use `func_new`/`func_wrap` (or \
605 `func_new_async`/`func_wrap_async` for blocking host code) instead"
606 );
607 }
608}
609
610#[repr(transparent)]
613struct StaticHostFn<F, const ASYNC: bool>(F);
614
615impl<F, const ASYNC: bool> StaticHostFn<F, ASYNC> {
616 fn new<T, P, R>(func: F) -> Self
617 where
618 T: 'static,
619 P: ComponentNamedList + Lift + 'static,
620 R: ComponentNamedList + Lower + 'static,
621 F: Fn(StoreContextMut<'_, T>, P) -> HostResult<R>,
622 {
623 Self(func)
624 }
625}
626
627impl<T, F, P, R, const ASYNC: bool> HostFn<T, P, R> for StaticHostFn<F, ASYNC>
628where
629 T: 'static,
630 F: Fn(StoreContextMut<'_, T>, P) -> HostResult<R>,
631 P: ComponentNamedList + Lift + 'static,
632 R: ComponentNamedList + Lower + 'static,
633{
634 const ASYNC: bool = ASYNC;
635
636 fn typecheck(ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()> {
637 let ty = &types.types[ty];
638 typecheck_async(ASYNC, ty.async_)?;
639 P::typecheck(&InterfaceType::Tuple(ty.params), types)
640 .context("type mismatch with parameters")?;
641 R::typecheck(&InterfaceType::Tuple(ty.results), types)
642 .context("type mismatch with results")?;
643 Ok(())
644 }
645
646 fn run(&self, store: StoreContextMut<'_, T>, params: P) -> HostResult<R> {
647 (self.0)(store, params)
648 }
649
650 fn lift_params(cx: &mut LiftContext<'_>, ty: TypeFuncIndex, src: Source<'_>) -> Result<P> {
651 let ty = InterfaceType::Tuple(cx.types[ty].params);
652 match src {
653 Source::Flat(storage) => {
654 let storage: &P::Lower = unsafe { slice_to_storage(storage) };
659 P::linear_lift_from_flat(cx, ty, storage)
660 }
661 Source::Memory(offset) => {
662 P::linear_lift_from_memory(cx, ty, &cx.memory()[offset..][..P::SIZE32])
663 }
664 }
665 }
666
667 fn lower_result(
668 cx: &mut LowerContext<'_, T>,
669 ty: TypeFuncIndex,
670 ret: R,
671 dst: Destination<'_>,
672 ) -> Result<()> {
673 let fty = &cx.types[ty];
674 let ty = InterfaceType::Tuple(fty.results);
675 match dst {
676 Destination::Flat(storage) => {
677 let storage: &mut MaybeUninit<R::Lower> = unsafe { slice_to_storage_mut(storage) };
681 ret.linear_lower_to_flat(cx, ty, storage)
682 }
683 Destination::Memory(ptr) => ret.linear_lower_to_memory(cx, ty, ptr),
684 }
685 }
686}
687
688struct DynamicHostFn<F, const ASYNC: bool>(F);
695
696impl<F, const ASYNC: bool> DynamicHostFn<F, ASYNC> {
697 fn new<T>(func: F) -> Self
698 where
699 T: 'static,
700 F: Fn(StoreContextMut<'_, T>, ComponentFunc, Vec<Val>, usize) -> HostResult<Vec<Val>>,
701 {
702 Self(func)
703 }
704}
705
706impl<T, F, const ASYNC: bool> HostFn<T, (ComponentFunc, Vec<Val>), Vec<Val>>
707 for DynamicHostFn<F, ASYNC>
708where
709 T: 'static,
710 F: Fn(StoreContextMut<'_, T>, ComponentFunc, Vec<Val>, usize) -> HostResult<Vec<Val>>,
711{
712 const ASYNC: bool = ASYNC;
713
714 fn typecheck(ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()> {
718 let ty = &types.types[ty];
719 typecheck_async(ASYNC, ty.async_)
720 }
721
722 fn run(
723 &self,
724 store: StoreContextMut<'_, T>,
725 (ty, mut params): (ComponentFunc, Vec<Val>),
726 ) -> HostResult<Vec<Val>> {
727 let offset = params.len();
728 for _ in 0..ty.results().len() {
729 params.push(Val::Bool(false));
730 }
731 (self.0)(store, ty, params, offset)
732 }
733
734 fn lift_params(
735 cx: &mut LiftContext<'_>,
736 ty: TypeFuncIndex,
737 src: Source<'_>,
738 ) -> Result<(ComponentFunc, Vec<Val>)> {
739 let param_tys = &cx.types[cx.types[ty].params];
740 let mut params = Vec::new();
741 match src {
742 Source::Flat(storage) => {
743 let mut iter = storage.iter();
744 for ty in param_tys.types.iter() {
745 params.push(Val::lift(cx, *ty, &mut iter)?);
746 }
747 assert!(iter.next().is_none());
748 }
749 Source::Memory(mut offset) => {
750 for ty in param_tys.types.iter() {
751 let abi = cx.types.canonical_abi(ty);
752 let size = usize::try_from(abi.size32).unwrap();
753 let memory = &cx.memory()[abi.next_field32_size(&mut offset)..][..size];
754 params.push(Val::load(cx, *ty, memory)?);
755 }
756 }
757 }
758
759 Ok((ComponentFunc::from(ty, &cx.instance_type()), params))
760 }
761
762 fn lower_result(
763 cx: &mut LowerContext<'_, T>,
764 ty: TypeFuncIndex,
765 result_vals: Vec<Val>,
766 dst: Destination<'_>,
767 ) -> Result<()> {
768 let fty = &cx.types[ty];
769 let param_tys = &cx.types[fty.params];
770 let result_tys = &cx.types[fty.results];
771 let result_vals = &result_vals[param_tys.types.len()..];
772 match dst {
773 Destination::Flat(storage) => {
774 let mut dst = storage.iter_mut();
775 for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
776 val.lower(cx, *ty, &mut dst)?;
777 }
778 assert!(dst.next().is_none());
779 }
780 Destination::Memory(mut ptr) => {
781 for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
782 let offset = cx.types.canonical_abi(ty).next_field32_size(&mut ptr);
783 val.store(cx, *ty, offset)?;
784 }
785 }
786 }
787 Ok(())
788 }
789}
790
791pub(crate) fn validate_inbounds_dynamic(
792 abi: &CanonicalAbiInfo,
793 memory: &[u8],
794 ptr: &ValRaw,
795) -> Result<usize> {
796 let ptr = usize::try_from(ptr.get_u32())?;
798 if ptr % usize::try_from(abi.align32)? != 0 {
799 bail!("pointer not aligned");
800 }
801 let end = match ptr.checked_add(usize::try_from(abi.size32).unwrap()) {
802 Some(n) => n,
803 None => bail!("pointer size overflow"),
804 };
805 if end > memory.len() {
806 bail!("pointer out of bounds")
807 }
808 Ok(ptr)
809}