1#[cfg(feature = "component-model-async")]
4use crate::component::concurrent;
5#[cfg(feature = "component-model-async")]
6use crate::component::concurrent::{Accessor, Status};
7use crate::component::func::{LiftContext, LowerContext};
8use crate::component::matching::InstanceType;
9use crate::component::storage::{slice_to_storage, slice_to_storage_mut};
10use crate::component::types::ComponentFunc;
11use crate::component::{ComponentNamedList, Instance, Lift, Lower, Val};
12use crate::prelude::*;
13use crate::runtime::vm::component::{
14 ComponentInstance, VMComponentContext, VMLowering, VMLoweringCallee,
15};
16use crate::runtime::vm::{VMOpaqueContext, VMStore};
17use crate::store::Asyncness;
18use crate::{AsContextMut, CallHook, StoreContextMut, ValRaw};
19use alloc::sync::Arc;
20use core::any::Any;
21use core::mem::{self, MaybeUninit};
22#[cfg(feature = "async")]
23use core::pin::Pin;
24use core::ptr::NonNull;
25use wasmtime_environ::component::{
26 CanonicalAbiInfo, InterfaceType, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, OptionsIndex, TypeFuncIndex,
27};
28
29pub struct HostFunc {
35 entrypoint: VMLoweringCallee,
37
38 typecheck: fn(TypeFuncIndex, &InstanceType<'_>) -> Result<()>,
45
46 func: Box<dyn Any + Send + Sync>,
52
53 asyncness: Asyncness,
56}
57
58impl core::fmt::Debug for HostFunc {
59 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
60 f.debug_struct("HostFunc").finish_non_exhaustive()
61 }
62}
63
64enum HostResult<T> {
65 Done(Result<T>),
66 #[cfg(feature = "component-model-async")]
67 Future(Pin<Box<dyn Future<Output = Result<T>> + Send>>),
68}
69
70impl HostFunc {
71 fn new<T, F, P, R>(asyncness: Asyncness, func: F) -> Arc<HostFunc>
80 where
81 T: 'static,
82 R: Send + Sync + 'static,
83 F: HostFn<T, P, R> + Send + Sync + 'static,
84 {
85 Arc::new(HostFunc {
86 entrypoint: F::cabi_entrypoint,
87 typecheck: F::typecheck,
88 func: Box::new(func),
89 asyncness,
90 })
91 }
92
93 pub(crate) fn func_wrap<T, F, P, R>(func: F) -> Arc<HostFunc>
95 where
96 T: 'static,
97 F: Fn(StoreContextMut<T>, P) -> Result<R> + Send + Sync + 'static,
98 P: ComponentNamedList + Lift + 'static,
99 R: ComponentNamedList + Lower + 'static,
100 {
101 Self::new(
102 Asyncness::No,
103 StaticHostFn::<_, false>::new(move |store, params| {
104 HostResult::Done(func(store, params))
105 }),
106 )
107 }
108
109 #[cfg(feature = "async")]
111 pub(crate) fn func_wrap_async<T, F, P, R>(func: F) -> Arc<HostFunc>
112 where
113 T: 'static,
114 F: Fn(StoreContextMut<'_, T>, P) -> Box<dyn Future<Output = Result<R>> + Send + '_>
115 + Send
116 + Sync
117 + 'static,
118 P: ComponentNamedList + Lift + 'static,
119 R: ComponentNamedList + Lower + 'static,
120 {
121 Self::new(
122 Asyncness::Yes,
123 StaticHostFn::<_, false>::new(move |store, params| {
124 HostResult::Done(
125 store
126 .block_on(|store| Pin::from(func(store, params)))
127 .and_then(|r| r),
128 )
129 }),
130 )
131 }
132
133 #[cfg(feature = "component-model-async")]
135 pub(crate) fn func_wrap_concurrent<T, F, P, R>(func: F) -> Arc<HostFunc>
136 where
137 T: 'static,
138 F: Fn(&Accessor<T>, P) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>>
139 + Send
140 + Sync
141 + 'static,
142 P: ComponentNamedList + Lift + 'static,
143 R: ComponentNamedList + Lower + 'static,
144 {
145 let func = Arc::new(func);
146 Self::new(
147 Asyncness::Yes,
148 StaticHostFn::<_, true>::new(move |store, params| {
149 let func = func.clone();
150 HostResult::Future(Box::pin(
151 store.wrap_call(move |accessor| func(accessor, params)),
152 ))
153 }),
154 )
155 }
156
157 pub(crate) fn func_new<T, F>(func: F) -> Arc<HostFunc>
159 where
160 T: 'static,
161 F: Fn(StoreContextMut<'_, T>, ComponentFunc, &[Val], &mut [Val]) -> Result<()>
162 + Send
163 + Sync
164 + 'static,
165 {
166 Self::new(
167 Asyncness::No,
168 DynamicHostFn::<_, false>::new(
169 move |store, ty, mut params_and_results, result_start| {
170 let (params, results) = params_and_results.split_at_mut(result_start);
171 let result = func(store, ty, params, results).map(move |()| params_and_results);
172 HostResult::Done(result)
173 },
174 ),
175 )
176 }
177
178 #[cfg(feature = "async")]
180 pub(crate) fn func_new_async<T, F>(func: F) -> Arc<HostFunc>
181 where
182 T: 'static,
183 F: for<'a> Fn(
184 StoreContextMut<'a, T>,
185 ComponentFunc,
186 &'a [Val],
187 &'a mut [Val],
188 ) -> Box<dyn Future<Output = Result<()>> + Send + 'a>
189 + Send
190 + Sync
191 + 'static,
192 {
193 Self::new(
194 Asyncness::Yes,
195 DynamicHostFn::<_, false>::new(
196 move |store, ty, mut params_and_results, result_start| {
197 let (params, results) = params_and_results.split_at_mut(result_start);
198 let result = store
199 .with_blocking(|store, cx| {
200 cx.block_on(Pin::from(func(store, ty, params, results)))
201 })
202 .and_then(|r| r);
203 let result = result.map(move |()| params_and_results);
204 HostResult::Done(result)
205 },
206 ),
207 )
208 }
209
210 #[cfg(feature = "component-model-async")]
212 pub(crate) fn func_new_concurrent<T, F>(func: F) -> Arc<HostFunc>
213 where
214 T: 'static,
215 F: for<'a> Fn(
216 &'a Accessor<T>,
217 ComponentFunc,
218 &'a [Val],
219 &'a mut [Val],
220 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
221 + Send
222 + Sync
223 + 'static,
224 {
225 let func = Arc::new(func);
226 Self::new(
227 Asyncness::Yes,
228 DynamicHostFn::<_, true>::new(
229 move |store, ty, mut params_and_results, result_start| {
230 let func = func.clone();
231 HostResult::Future(Box::pin(store.wrap_call(move |accessor| {
232 Box::pin(async move {
233 let (params, results) = params_and_results.split_at_mut(result_start);
234 func(accessor, ty, params, results).await?;
235 Ok(params_and_results)
236 })
237 })))
238 },
239 ),
240 )
241 }
242
243 pub fn typecheck(&self, ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()> {
244 (self.typecheck)(ty, types)
245 }
246
247 pub fn lowering(&self) -> VMLowering {
248 let data = NonNull::from(&*self.func).cast();
249 VMLowering {
250 callee: NonNull::new(self.entrypoint as *mut _).unwrap().into(),
251 data: data.into(),
252 }
253 }
254
255 pub fn asyncness(&self) -> Asyncness {
256 self.asyncness
257 }
258}
259
260enum Source<'a> {
262 Flat(&'a [ValRaw]),
264 Memory(usize),
267}
268
269enum Destination<'a> {
271 Flat(&'a mut [MaybeUninit<ValRaw>]),
273 Memory(usize),
276}
277
278trait HostFn<T, P, R>
284where
285 T: 'static,
286 R: Send + Sync + 'static,
287{
288 const ASYNC: bool;
291
292 fn typecheck(ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()>;
295
296 fn run(&self, store: StoreContextMut<'_, T>, params: P) -> HostResult<R>;
298
299 fn lift_params(cx: &mut LiftContext<'_>, ty: TypeFuncIndex, source: Source<'_>) -> Result<P>;
302
303 fn lower_result(
306 cx: &mut LowerContext<'_, T>,
307 ty: TypeFuncIndex,
308 result: R,
309 dst: Destination<'_>,
310 ) -> Result<()>;
311
312 unsafe extern "C" fn cabi_entrypoint(
332 cx: NonNull<VMOpaqueContext>,
333 data: NonNull<u8>,
334 ty: u32,
335 options: u32,
336 storage: NonNull<MaybeUninit<ValRaw>>,
337 storage_len: usize,
338 ) -> bool
339 where
340 Self: Sized,
341 {
342 let cx = unsafe { VMComponentContext::from_opaque(cx) };
343 unsafe {
344 ComponentInstance::enter_host_from_wasm(cx, |store, instance| {
345 let mut store = store.unchecked_context_mut();
346 let ty = TypeFuncIndex::from_u32(ty);
347 let options = OptionsIndex::from_u32(options);
348 let storage = NonNull::slice_from_raw_parts(storage, storage_len).as_mut();
349 let data = data.cast::<Self>().as_ref();
350
351 store.0.call_hook(CallHook::CallingHost)?;
352 let res = data.entrypoint(store.as_context_mut(), instance, ty, options, storage);
353 store.0.call_hook(CallHook::ReturningFromHost)?;
354
355 res
356 })
357 }
358 }
359
360 fn entrypoint(
363 &self,
364 mut store: StoreContextMut<'_, T>,
365 instance: Instance,
366 ty: TypeFuncIndex,
367 options: OptionsIndex,
368 storage: &mut [MaybeUninit<ValRaw>],
369 ) -> Result<()> {
370 let vminstance = instance.id().get(store.0);
371 let async_ = vminstance.component().env_component().options[options].async_;
372
373 if !async_ && Self::ASYNC {
377 store.0.check_blocking()?;
378 }
379
380 let host_task = store.0.host_task_create()?;
382
383 let host_task_complete = if async_ {
384 #[cfg(feature = "component-model-async")]
385 {
386 self.call_async_lower(store.as_context_mut(), instance, ty, options, storage)?
387 }
388 #[cfg(not(feature = "component-model-async"))]
389 unreachable!(
390 "async-lowered imports should have failed validation \
391 when `component-model-async` feature disabled"
392 );
393 } else {
394 self.call_sync_lower(store.as_context_mut(), instance, ty, options, storage)?;
395 true
396 };
397
398 if host_task_complete {
404 store.0.host_task_delete(host_task)?;
405 }
406
407 Ok(())
408 }
409
410 fn call_sync_lower(
418 &self,
419 mut store: StoreContextMut<'_, T>,
420 instance: Instance,
421 ty: TypeFuncIndex,
422 options: OptionsIndex,
423 storage: &mut [MaybeUninit<ValRaw>],
424 ) -> Result<()> {
425 let mut lift = LiftContext::new(store.0.store_opaque_mut(), options, instance);
426 let (params, rest) = self.load_params(&mut lift, ty, MAX_FLAT_PARAMS, storage)?;
427
428 let ret = match self.run(store.as_context_mut(), params) {
429 HostResult::Done(result) => result?,
430 #[cfg(feature = "component-model-async")]
431 HostResult::Future(future) => concurrent::poll_and_block(store.0, future)?,
432 };
433
434 let mut lower = LowerContext::new(store, options, instance);
435 let fty = &lower.types[ty];
436 let result_tys = &lower.types[fty.results];
437 let dst = if let Some(cnt) = result_tys.abi.flat_count(MAX_FLAT_RESULTS) {
438 Destination::Flat(&mut storage[..cnt])
439 } else {
440 let ptr = unsafe { rest[0].assume_init_ref() };
444 Destination::Memory(validate_inbounds_dynamic(
445 &result_tys.abi,
446 lower.as_slice_mut(),
447 ptr,
448 )?)
449 };
450 Self::lower_result_and_exit_call(&mut lower, ty, Some(ret), dst)
451 }
452
453 #[cfg(feature = "component-model-async")]
459 fn call_async_lower(
460 &self,
461 store: StoreContextMut<'_, T>,
462 instance: Instance,
463 ty: TypeFuncIndex,
464 options: OptionsIndex,
465 storage: &mut [MaybeUninit<ValRaw>],
466 ) -> Result<bool> {
467 use wasmtime_environ::component::MAX_FLAT_ASYNC_PARAMS;
468
469 let (component, store) = instance.component_and_store_mut(store.0);
470 let mut store = StoreContextMut(store);
471 let types = component.types();
472 let fty = &types[ty];
473
474 let mut lift = LiftContext::new(store.0.store_opaque_mut(), options, instance);
477 let (params, rest) = self.load_params(&mut lift, ty, MAX_FLAT_ASYNC_PARAMS, storage)?;
478
479 let retptr = if !lift.types[fty.results].types.is_empty() {
481 let mut lower = LowerContext::new(store.as_context_mut(), options, instance);
482 let ptr = unsafe { rest[0].assume_init_ref() };
485 let result_tys = &lower.types[fty.results];
486 validate_inbounds_dynamic(&result_tys.abi, lower.as_slice_mut(), ptr)?
487 } else {
488 0
493 };
494
495 let host_result = self.run(store.as_context_mut(), params);
496
497 let task = match host_result {
498 HostResult::Done(result) => {
499 Self::lower_result_and_exit_call(
500 &mut LowerContext::new(store, options, instance),
501 ty,
502 Some(result?),
503 Destination::Memory(retptr),
504 )?;
505 None
506 }
507 #[cfg(feature = "component-model-async")]
508 HostResult::Future(future) => {
509 instance.first_poll(store, future, move |store, ret| {
510 Self::lower_result_and_exit_call(
511 &mut LowerContext::new(store, options, instance),
512 ty,
513 ret,
514 Destination::Memory(retptr),
515 )
516 })?
517 }
518 };
519
520 storage[0].write(ValRaw::u32(if let Some(task) = task {
521 Status::Started.pack(Some(task))
522 } else {
523 Status::Returned.pack(None)
524 }));
525
526 Ok(task.is_none())
527 }
528
529 fn load_params<'a>(
534 &self,
535 lift: &mut LiftContext<'_>,
536 ty: TypeFuncIndex,
537 max_flat_params: usize,
538 storage: &'a [MaybeUninit<ValRaw>],
539 ) -> Result<(P, &'a [MaybeUninit<ValRaw>])> {
540 let fty = &lift.types[ty];
541 let param_tys = &lift.types[fty.params];
542 let param_flat_count = param_tys.abi.flat_count(max_flat_params);
543 let src = match param_flat_count {
544 Some(cnt) => {
545 let params = &storage[..cnt];
546 Source::Flat(unsafe { mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(params) })
550 }
551 None => {
552 let ptr = unsafe { storage[0].assume_init_ref() };
556 Source::Memory(validate_inbounds_dynamic(
557 ¶m_tys.abi,
558 lift.memory(),
559 ptr,
560 )?)
561 }
562 };
563 let params = Self::lift_params(lift, ty, src)?;
564 Ok((params, &storage[param_flat_count.unwrap_or(1)..]))
565 }
566
567 fn lower_result_and_exit_call(
569 lower: &mut LowerContext<'_, T>,
570 ty: TypeFuncIndex,
571 ret: Option<R>,
572 dst: Destination<'_>,
573 ) -> Result<()> {
574 lower.validate_scope_exit()?;
577
578 lower.store.0.host_task_reenter_caller()?;
583
584 if let Some(ret) = ret {
585 let caller_instance = lower.options().instance;
586 let mut flags = lower.instance_mut().instance_flags(caller_instance);
587 unsafe {
588 flags.set_may_leave(false);
589 }
590 Self::lower_result(lower, ty, ret, dst)?;
591 unsafe {
592 flags.set_may_leave(true);
593 }
594 }
595 Ok(())
596 }
597}
598
599#[repr(transparent)]
602struct StaticHostFn<F, const ASYNC: bool>(F);
603
604impl<F, const ASYNC: bool> StaticHostFn<F, ASYNC> {
605 fn new<T, P, R>(func: F) -> Self
606 where
607 T: 'static,
608 P: ComponentNamedList + Lift + 'static,
609 R: ComponentNamedList + Lower + 'static,
610 F: Fn(StoreContextMut<'_, T>, P) -> HostResult<R>,
611 {
612 Self(func)
613 }
614}
615
616impl<T, F, P, R, const ASYNC: bool> HostFn<T, P, R> for StaticHostFn<F, ASYNC>
617where
618 T: 'static,
619 F: Fn(StoreContextMut<'_, T>, P) -> HostResult<R>,
620 P: ComponentNamedList + Lift + 'static,
621 R: ComponentNamedList + Lower + 'static,
622{
623 const ASYNC: bool = ASYNC;
624
625 fn typecheck(ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()> {
626 let ty = &types.types[ty];
627 if ASYNC != ty.async_ {
628 bail!("type mismatch with async");
629 }
630 P::typecheck(&InterfaceType::Tuple(ty.params), types)
631 .context("type mismatch with parameters")?;
632 R::typecheck(&InterfaceType::Tuple(ty.results), types)
633 .context("type mismatch with results")?;
634 Ok(())
635 }
636
637 fn run(&self, store: StoreContextMut<'_, T>, params: P) -> HostResult<R> {
638 (self.0)(store, params)
639 }
640
641 fn lift_params(cx: &mut LiftContext<'_>, ty: TypeFuncIndex, src: Source<'_>) -> Result<P> {
642 let ty = InterfaceType::Tuple(cx.types[ty].params);
643 match src {
644 Source::Flat(storage) => {
645 let storage: &P::Lower = unsafe { slice_to_storage(storage) };
650 P::linear_lift_from_flat(cx, ty, storage)
651 }
652 Source::Memory(offset) => {
653 P::linear_lift_from_memory(cx, ty, &cx.memory()[offset..][..P::SIZE32])
654 }
655 }
656 }
657
658 fn lower_result(
659 cx: &mut LowerContext<'_, T>,
660 ty: TypeFuncIndex,
661 ret: R,
662 dst: Destination<'_>,
663 ) -> Result<()> {
664 let fty = &cx.types[ty];
665 let ty = InterfaceType::Tuple(fty.results);
666 match dst {
667 Destination::Flat(storage) => {
668 let storage: &mut MaybeUninit<R::Lower> = unsafe { slice_to_storage_mut(storage) };
672 ret.linear_lower_to_flat(cx, ty, storage)
673 }
674 Destination::Memory(ptr) => ret.linear_lower_to_memory(cx, ty, ptr),
675 }
676 }
677}
678
679struct DynamicHostFn<F, const ASYNC: bool>(F);
686
687impl<F, const ASYNC: bool> DynamicHostFn<F, ASYNC> {
688 fn new<T>(func: F) -> Self
689 where
690 T: 'static,
691 F: Fn(StoreContextMut<'_, T>, ComponentFunc, Vec<Val>, usize) -> HostResult<Vec<Val>>,
692 {
693 Self(func)
694 }
695}
696
697impl<T, F, const ASYNC: bool> HostFn<T, (ComponentFunc, Vec<Val>), Vec<Val>>
698 for DynamicHostFn<F, ASYNC>
699where
700 T: 'static,
701 F: Fn(StoreContextMut<'_, T>, ComponentFunc, Vec<Val>, usize) -> HostResult<Vec<Val>>,
702{
703 const ASYNC: bool = ASYNC;
704
705 fn typecheck(ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()> {
709 let ty = &types.types[ty];
710 if ASYNC != ty.async_ {
711 bail!("type mismatch with async");
712 }
713
714 Ok(())
715 }
716
717 fn run(
718 &self,
719 store: StoreContextMut<'_, T>,
720 (ty, mut params): (ComponentFunc, Vec<Val>),
721 ) -> HostResult<Vec<Val>> {
722 let offset = params.len();
723 for _ in 0..ty.results().len() {
724 params.push(Val::Bool(false));
725 }
726 (self.0)(store, ty, params, offset)
727 }
728
729 fn lift_params(
730 cx: &mut LiftContext<'_>,
731 ty: TypeFuncIndex,
732 src: Source<'_>,
733 ) -> Result<(ComponentFunc, Vec<Val>)> {
734 let param_tys = &cx.types[cx.types[ty].params];
735 let mut params = Vec::new();
736 match src {
737 Source::Flat(storage) => {
738 let mut iter = storage.iter();
739 for ty in param_tys.types.iter() {
740 params.push(Val::lift(cx, *ty, &mut iter)?);
741 }
742 assert!(iter.next().is_none());
743 }
744 Source::Memory(mut offset) => {
745 for ty in param_tys.types.iter() {
746 let abi = cx.types.canonical_abi(ty);
747 let size = usize::try_from(abi.size32).unwrap();
748 let memory = &cx.memory()[abi.next_field32_size(&mut offset)..][..size];
749 params.push(Val::load(cx, *ty, memory)?);
750 }
751 }
752 }
753
754 Ok((ComponentFunc::from(ty, &cx.instance_type()), params))
755 }
756
757 fn lower_result(
758 cx: &mut LowerContext<'_, T>,
759 ty: TypeFuncIndex,
760 result_vals: Vec<Val>,
761 dst: Destination<'_>,
762 ) -> Result<()> {
763 let fty = &cx.types[ty];
764 let param_tys = &cx.types[fty.params];
765 let result_tys = &cx.types[fty.results];
766 let result_vals = &result_vals[param_tys.types.len()..];
767 match dst {
768 Destination::Flat(storage) => {
769 let mut dst = storage.iter_mut();
770 for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
771 val.lower(cx, *ty, &mut dst)?;
772 }
773 assert!(dst.next().is_none());
774 }
775 Destination::Memory(mut ptr) => {
776 for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
777 let offset = cx.types.canonical_abi(ty).next_field32_size(&mut ptr);
778 val.store(cx, *ty, offset)?;
779 }
780 }
781 }
782 Ok(())
783 }
784}
785
786pub(crate) fn validate_inbounds_dynamic(
787 abi: &CanonicalAbiInfo,
788 memory: &[u8],
789 ptr: &ValRaw,
790) -> Result<usize> {
791 let ptr = usize::try_from(ptr.get_u32())?;
793 if ptr % usize::try_from(abi.align32)? != 0 {
794 bail!("pointer not aligned");
795 }
796 let end = match ptr.checked_add(usize::try_from(abi.size32).unwrap()) {
797 Some(n) => n,
798 None => bail!("pointer size overflow"),
799 };
800 if end > memory.len() {
801 bail!("pointer out of bounds")
802 }
803 Ok(ptr)
804}