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