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 store.0.enter_host_call()?;
382
383 let task_exited = 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 task_exited {
404 store.0.exit_host_call()?;
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, 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 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: R,
572 dst: Destination<'_>,
573 ) -> Result<()> {
574 let caller_instance = lower.options().instance;
575 let mut flags = lower.instance_mut().instance_flags(caller_instance);
576 unsafe {
577 flags.set_may_leave(false);
578 }
579 Self::lower_result(lower, ty, ret, dst)?;
580 unsafe {
581 flags.set_may_leave(true);
582 }
583 lower.validate_scope_exit()?;
584 Ok(())
585 }
586}
587
588#[repr(transparent)]
591struct StaticHostFn<F, const ASYNC: bool>(F);
592
593impl<F, const ASYNC: bool> StaticHostFn<F, ASYNC> {
594 fn new<T, P, R>(func: F) -> Self
595 where
596 T: 'static,
597 P: ComponentNamedList + Lift + 'static,
598 R: ComponentNamedList + Lower + 'static,
599 F: Fn(StoreContextMut<'_, T>, P) -> HostResult<R>,
600 {
601 Self(func)
602 }
603}
604
605impl<T, F, P, R, const ASYNC: bool> HostFn<T, P, R> for StaticHostFn<F, ASYNC>
606where
607 T: 'static,
608 F: Fn(StoreContextMut<'_, T>, P) -> HostResult<R>,
609 P: ComponentNamedList + Lift + 'static,
610 R: ComponentNamedList + Lower + 'static,
611{
612 const ASYNC: bool = ASYNC;
613
614 fn typecheck(ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()> {
615 let ty = &types.types[ty];
616 if ASYNC != ty.async_ {
617 bail!("type mismatch with async");
618 }
619 P::typecheck(&InterfaceType::Tuple(ty.params), types)
620 .context("type mismatch with parameters")?;
621 R::typecheck(&InterfaceType::Tuple(ty.results), types)
622 .context("type mismatch with results")?;
623 Ok(())
624 }
625
626 fn run(&self, store: StoreContextMut<'_, T>, params: P) -> HostResult<R> {
627 (self.0)(store, params)
628 }
629
630 fn lift_params(cx: &mut LiftContext<'_>, ty: TypeFuncIndex, src: Source<'_>) -> Result<P> {
631 let ty = InterfaceType::Tuple(cx.types[ty].params);
632 match src {
633 Source::Flat(storage) => {
634 let storage: &P::Lower = unsafe { slice_to_storage(storage) };
639 P::linear_lift_from_flat(cx, ty, storage)
640 }
641 Source::Memory(offset) => {
642 P::linear_lift_from_memory(cx, ty, &cx.memory()[offset..][..P::SIZE32])
643 }
644 }
645 }
646
647 fn lower_result(
648 cx: &mut LowerContext<'_, T>,
649 ty: TypeFuncIndex,
650 ret: R,
651 dst: Destination<'_>,
652 ) -> Result<()> {
653 let fty = &cx.types[ty];
654 let ty = InterfaceType::Tuple(fty.results);
655 match dst {
656 Destination::Flat(storage) => {
657 let storage: &mut MaybeUninit<R::Lower> = unsafe { slice_to_storage_mut(storage) };
661 ret.linear_lower_to_flat(cx, ty, storage)
662 }
663 Destination::Memory(ptr) => ret.linear_lower_to_memory(cx, ty, ptr),
664 }
665 }
666}
667
668struct DynamicHostFn<F, const ASYNC: bool>(F);
675
676impl<F, const ASYNC: bool> DynamicHostFn<F, ASYNC> {
677 fn new<T>(func: F) -> Self
678 where
679 T: 'static,
680 F: Fn(StoreContextMut<'_, T>, ComponentFunc, Vec<Val>, usize) -> HostResult<Vec<Val>>,
681 {
682 Self(func)
683 }
684}
685
686impl<T, F, const ASYNC: bool> HostFn<T, (ComponentFunc, Vec<Val>), Vec<Val>>
687 for DynamicHostFn<F, ASYNC>
688where
689 T: 'static,
690 F: Fn(StoreContextMut<'_, T>, ComponentFunc, Vec<Val>, usize) -> HostResult<Vec<Val>>,
691{
692 const ASYNC: bool = ASYNC;
693
694 fn typecheck(ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()> {
698 let ty = &types.types[ty];
699 if ASYNC != ty.async_ {
700 bail!("type mismatch with async");
701 }
702
703 Ok(())
704 }
705
706 fn run(
707 &self,
708 store: StoreContextMut<'_, T>,
709 (ty, mut params): (ComponentFunc, Vec<Val>),
710 ) -> HostResult<Vec<Val>> {
711 let offset = params.len();
712 for _ in 0..ty.results().len() {
713 params.push(Val::Bool(false));
714 }
715 (self.0)(store, ty, params, offset)
716 }
717
718 fn lift_params(
719 cx: &mut LiftContext<'_>,
720 ty: TypeFuncIndex,
721 src: Source<'_>,
722 ) -> Result<(ComponentFunc, Vec<Val>)> {
723 let param_tys = &cx.types[cx.types[ty].params];
724 let mut params = Vec::new();
725 match src {
726 Source::Flat(storage) => {
727 let mut iter = storage.iter();
728 for ty in param_tys.types.iter() {
729 params.push(Val::lift(cx, *ty, &mut iter)?);
730 }
731 assert!(iter.next().is_none());
732 }
733 Source::Memory(mut offset) => {
734 for ty in param_tys.types.iter() {
735 let abi = cx.types.canonical_abi(ty);
736 let size = usize::try_from(abi.size32).unwrap();
737 let memory = &cx.memory()[abi.next_field32_size(&mut offset)..][..size];
738 params.push(Val::load(cx, *ty, memory)?);
739 }
740 }
741 }
742
743 Ok((ComponentFunc::from(ty, &cx.instance_type()), params))
744 }
745
746 fn lower_result(
747 cx: &mut LowerContext<'_, T>,
748 ty: TypeFuncIndex,
749 result_vals: Vec<Val>,
750 dst: Destination<'_>,
751 ) -> Result<()> {
752 let fty = &cx.types[ty];
753 let param_tys = &cx.types[fty.params];
754 let result_tys = &cx.types[fty.results];
755 let result_vals = &result_vals[param_tys.types.len()..];
756 match dst {
757 Destination::Flat(storage) => {
758 let mut dst = storage.iter_mut();
759 for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
760 val.lower(cx, *ty, &mut dst)?;
761 }
762 assert!(dst.next().is_none());
763 }
764 Destination::Memory(mut ptr) => {
765 for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
766 let offset = cx.types.canonical_abi(ty).next_field32_size(&mut ptr);
767 val.store(cx, *ty, offset)?;
768 }
769 }
770 }
771 Ok(())
772 }
773}
774
775pub(crate) fn validate_inbounds_dynamic(
776 abi: &CanonicalAbiInfo,
777 memory: &[u8],
778 ptr: &ValRaw,
779) -> Result<usize> {
780 let ptr = usize::try_from(ptr.get_u32())?;
782 if ptr % usize::try_from(abi.align32)? != 0 {
783 bail!("pointer not aligned");
784 }
785 let end = match ptr.checked_add(usize::try_from(abi.size32).unwrap()) {
786 Some(n) => n,
787 None => bail!("pointer size overflow"),
788 };
789 if end > memory.len() {
790 bail!("pointer out of bounds")
791 }
792 Ok(ptr)
793}