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