1use crate::component::concurrent;
2#[cfg(feature = "component-model-async")]
3use crate::component::concurrent::{Accessor, Status};
4use crate::component::func::{LiftContext, LowerContext};
5use crate::component::matching::InstanceType;
6use crate::component::storage::slice_to_storage_mut;
7use crate::component::types::ComponentFunc;
8use crate::component::{ComponentNamedList, ComponentType, Instance, Lift, Lower, Val};
9use crate::prelude::*;
10use crate::runtime::vm::component::{
11 ComponentInstance, VMComponentContext, VMLowering, VMLoweringCallee,
12};
13use crate::runtime::vm::{SendSyncPtr, VMOpaqueContext, VMStore};
14use crate::{AsContextMut, CallHook, StoreContextMut, ValRaw};
15use alloc::sync::Arc;
16use core::any::Any;
17use core::future::Future;
18use core::mem::{self, MaybeUninit};
19use core::pin::Pin;
20use core::ptr::NonNull;
21use wasmtime_environ::component::{
22 CanonicalAbiInfo, ComponentTypes, InterfaceType, MAX_FLAT_ASYNC_PARAMS, MAX_FLAT_PARAMS,
23 MAX_FLAT_RESULTS, OptionsIndex, TypeFuncIndex, TypeTuple,
24};
25
26pub struct HostFunc {
27 entrypoint: VMLoweringCallee,
28 typecheck: Box<dyn (Fn(TypeFuncIndex, &InstanceType<'_>) -> Result<()>) + Send + Sync>,
29 func: Box<dyn Any + Send + Sync>,
30}
31
32impl core::fmt::Debug for HostFunc {
33 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
34 f.debug_struct("HostFunc").finish_non_exhaustive()
35 }
36}
37
38enum HostResult<T> {
39 Done(Result<T>),
40 #[cfg(feature = "component-model-async")]
41 Future(Pin<Box<dyn Future<Output = Result<T>> + Send>>),
42}
43
44impl HostFunc {
45 fn from_canonical<T, F, P, R>(func: F) -> Arc<HostFunc>
46 where
47 F: Fn(StoreContextMut<'_, T>, P) -> HostResult<R> + Send + Sync + 'static,
48 P: ComponentNamedList + Lift + 'static,
49 R: ComponentNamedList + Lower + 'static,
50 T: 'static,
51 {
52 let entrypoint = Self::entrypoint::<T, F, P, R>;
53 Arc::new(HostFunc {
54 entrypoint,
55 typecheck: Box::new(typecheck::<P, R>),
56 func: Box::new(func),
57 })
58 }
59
60 pub(crate) fn from_closure<T, F, P, R>(func: F) -> Arc<HostFunc>
61 where
62 T: 'static,
63 F: Fn(StoreContextMut<T>, P) -> Result<R> + Send + Sync + 'static,
64 P: ComponentNamedList + Lift + 'static,
65 R: ComponentNamedList + Lower + 'static,
66 {
67 Self::from_canonical::<T, _, _, _>(move |store, params| {
68 HostResult::Done(func(store, params))
69 })
70 }
71
72 #[cfg(feature = "component-model-async")]
73 pub(crate) fn from_concurrent<T, F, P, R>(func: F) -> Arc<HostFunc>
74 where
75 T: 'static,
76 F: Fn(&Accessor<T>, P) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>>
77 + Send
78 + Sync
79 + 'static,
80 P: ComponentNamedList + Lift + 'static,
81 R: ComponentNamedList + Lower + 'static,
82 {
83 let func = Arc::new(func);
84 Self::from_canonical::<T, _, _, _>(move |store, params| {
85 let func = func.clone();
86 HostResult::Future(Box::pin(
87 store.wrap_call(move |accessor| func(accessor, params)),
88 ))
89 })
90 }
91
92 extern "C" fn entrypoint<T, F, P, R>(
93 cx: NonNull<VMOpaqueContext>,
94 data: NonNull<u8>,
95 ty: u32,
96 options: u32,
97 storage: NonNull<MaybeUninit<ValRaw>>,
98 storage_len: usize,
99 ) -> bool
100 where
101 F: Fn(StoreContextMut<'_, T>, P) -> HostResult<R> + Send + Sync + 'static,
102 P: ComponentNamedList + Lift,
103 R: ComponentNamedList + Lower + 'static,
104 T: 'static,
105 {
106 let data = SendSyncPtr::new(NonNull::new(data.as_ptr() as *mut F).unwrap());
107 unsafe {
108 call_host_and_handle_result::<T>(cx, |store, instance| {
109 call_host(
110 store,
111 instance,
112 TypeFuncIndex::from_u32(ty),
113 OptionsIndex::from_u32(options),
114 NonNull::slice_from_raw_parts(storage, storage_len).as_mut(),
115 move |store, args| (*data.as_ptr())(store, args),
116 )
117 })
118 }
119 }
120
121 fn new_dynamic_canonical<T, F>(func: F) -> Arc<HostFunc>
122 where
123 F: Fn(
124 StoreContextMut<'_, T>,
125 ComponentFunc,
126 Vec<Val>,
127 usize,
128 ) -> Pin<Box<dyn Future<Output = Result<Vec<Val>>> + Send + 'static>>
129 + Send
130 + Sync
131 + 'static,
132 T: 'static,
133 {
134 Arc::new(HostFunc {
135 entrypoint: dynamic_entrypoint::<T, F>,
136 typecheck: Box::new(move |_expected_index, _expected_types| Ok(())),
140 func: Box::new(func),
141 })
142 }
143
144 pub(crate) fn new_dynamic<T: 'static, F>(func: F) -> Arc<HostFunc>
145 where
146 F: Fn(StoreContextMut<'_, T>, ComponentFunc, &[Val], &mut [Val]) -> Result<()>
147 + Send
148 + Sync
149 + 'static,
150 {
151 Self::new_dynamic_canonical::<T, _>(
152 move |store, ty, mut params_and_results, result_start| {
153 let (params, results) = params_and_results.split_at_mut(result_start);
154 let result = func(store, ty, params, results).map(move |()| params_and_results);
155 Box::pin(async move { result })
156 },
157 )
158 }
159
160 #[cfg(feature = "component-model-async")]
161 pub(crate) fn new_dynamic_concurrent<T, F>(func: F) -> Arc<HostFunc>
162 where
163 T: 'static,
164 F: for<'a> Fn(
165 &'a Accessor<T>,
166 ComponentFunc,
167 &'a [Val],
168 &'a mut [Val],
169 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
170 + Send
171 + Sync
172 + 'static,
173 {
174 let func = Arc::new(func);
175 Self::new_dynamic_canonical::<T, _>(
176 move |store, ty, mut params_and_results, result_start| {
177 let func = func.clone();
178 Box::pin(store.wrap_call(move |accessor| {
179 Box::pin(async move {
180 let (params, results) = params_and_results.split_at_mut(result_start);
181 func(accessor, ty, params, results).await?;
182 Ok(params_and_results)
183 })
184 }))
185 },
186 )
187 }
188
189 pub fn typecheck(&self, ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()> {
190 (self.typecheck)(ty, types)
191 }
192
193 pub fn lowering(&self) -> VMLowering {
194 let data = NonNull::from(&*self.func).cast();
195 VMLowering {
196 callee: NonNull::new(self.entrypoint as *mut _).unwrap().into(),
197 data: data.into(),
198 }
199 }
200}
201
202fn typecheck<P, R>(ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()>
203where
204 P: ComponentNamedList + Lift,
205 R: ComponentNamedList + Lower,
206{
207 let ty = &types.types[ty];
208 P::typecheck(&InterfaceType::Tuple(ty.params), types)
209 .context("type mismatch with parameters")?;
210 R::typecheck(&InterfaceType::Tuple(ty.results), types).context("type mismatch with results")?;
211 Ok(())
212}
213
214unsafe fn call_host<T, Params, Return, F>(
236 store: StoreContextMut<'_, T>,
237 instance: Instance,
238 ty: TypeFuncIndex,
239 options: OptionsIndex,
240 storage: &mut [MaybeUninit<ValRaw>],
241 closure: F,
242) -> Result<()>
243where
244 F: Fn(StoreContextMut<'_, T>, Params) -> HostResult<Return> + Send + Sync + 'static,
245 Params: Lift,
246 Return: Lower + 'static,
247{
248 let (component, store) = instance.component_and_store_mut(store.0);
249 let mut store = StoreContextMut(store);
250 let vminstance = instance.id().get(store.0);
251 let opts = &vminstance.component().env_component().options[options];
252 let async_ = opts.async_;
253 let caller_instance = opts.instance;
254 let mut flags = vminstance.instance_flags(caller_instance);
255
256 if unsafe { !flags.may_leave() } {
260 return Err(anyhow!(crate::Trap::CannotLeaveComponent));
261 }
262
263 let types = component.types();
264 let ty = &types[ty];
265 let param_tys = InterfaceType::Tuple(ty.params);
266 let result_tys = InterfaceType::Tuple(ty.results);
267
268 if async_ {
269 #[cfg(feature = "component-model-async")]
270 {
271 let mut storage = unsafe { Storage::<'_, Params, u32>::new_async::<Return>(storage) };
272
273 let lift = &mut LiftContext::new(store.0.store_opaque_mut(), options, instance);
276 lift.enter_call();
277 let params = storage.lift_params(lift, param_tys)?;
278
279 let retptr = match storage.async_retptr() {
281 Some(ptr) => {
282 let mut lower = LowerContext::new(store.as_context_mut(), options, instance);
283 validate_inbounds::<Return>(lower.as_slice_mut(), ptr)?
284 }
285 None => {
290 assert_eq!(Return::flatten_count(), 0);
291 0
292 }
293 };
294
295 let host_result = closure(store.as_context_mut(), params);
296
297 let mut lower_result = {
298 move |store: StoreContextMut<T>, ret: Return| {
299 unsafe {
300 flags.set_may_leave(false);
301 }
302 let mut lower = LowerContext::new(store, options, instance);
303 ret.linear_lower_to_memory(&mut lower, result_tys, retptr)?;
304 unsafe {
305 flags.set_may_leave(true);
306 }
307 lower.exit_call()?;
308 Ok(())
309 }
310 };
311 let task = match host_result {
312 HostResult::Done(result) => {
313 lower_result(store.as_context_mut(), result?)?;
314 None
315 }
316 #[cfg(feature = "component-model-async")]
317 HostResult::Future(future) => instance.first_poll(
318 store.as_context_mut(),
319 future,
320 caller_instance,
321 lower_result,
322 )?,
323 };
324
325 let status = if let Some(task) = task {
326 Status::Started.pack(Some(task))
327 } else {
328 Status::Returned.pack(None)
329 };
330
331 let mut lower = LowerContext::new(store, options, instance);
332 storage.lower_results(&mut lower, InterfaceType::U32, status)?;
333 }
334 #[cfg(not(feature = "component-model-async"))]
335 {
336 let _ = caller_instance;
337 unreachable!(
338 "async-lowered imports should have failed validation \
339 when `component-model-async` feature disabled"
340 );
341 }
342 } else {
343 let mut storage = unsafe { Storage::<'_, Params, Return>::new_sync(storage) };
344 let mut lift = LiftContext::new(store.0.store_opaque_mut(), options, instance);
345 lift.enter_call();
346 let params = storage.lift_params(&mut lift, param_tys)?;
347
348 let ret = match closure(store.as_context_mut(), params) {
349 HostResult::Done(result) => result?,
350 #[cfg(feature = "component-model-async")]
351 HostResult::Future(future) => {
352 concurrent::poll_and_block(store.0, future, caller_instance)?
353 }
354 };
355
356 unsafe {
357 flags.set_may_leave(false);
358 }
359 let mut lower = LowerContext::new(store, options, instance);
360 storage.lower_results(&mut lower, result_tys, ret)?;
361 unsafe {
362 flags.set_may_leave(true);
363 }
364 lower.exit_call()?;
365 }
366
367 return Ok(());
368
369 enum Storage<'a, P: ComponentType, R: ComponentType> {
420 PdRd(&'a mut Union<P::Lower, MaybeUninit<R::Lower>>),
425
426 PdRi(&'a Pair<P::Lower, ValRaw>),
431
432 PiRd(&'a mut Union<ValRaw, MaybeUninit<R::Lower>>),
436
437 PiRi(&'a Pair<ValRaw, ValRaw>),
442
443 #[cfg(feature = "component-model-async")]
448 PdArRd(&'a mut Union<Pair<P::Lower, ValRaw>, MaybeUninit<R::Lower>>),
449
450 #[cfg(feature = "component-model-async")]
455 PiArRd(&'a mut Union<Pair<ValRaw, ValRaw>, MaybeUninit<R::Lower>>),
456 }
457
458 #[repr(C)]
461 #[derive(Copy, Clone)]
462 struct Pair<T, U> {
463 a: T,
464 b: U,
465 }
466
467 #[repr(C)]
470 union Union<T: Copy, U: Copy> {
471 a: T,
472 b: U,
473 }
474
475 enum Src<'a, T> {
477 Direct(&'a T),
480
481 Indirect(&'a ValRaw),
484 }
485
486 enum Dst<'a, T> {
488 Direct(&'a mut MaybeUninit<T>),
494
495 Indirect(&'a ValRaw),
501 }
502
503 impl<P, R> Storage<'_, P, R>
504 where
505 P: ComponentType + Lift,
506 R: ComponentType + Lower,
507 {
508 unsafe fn new_sync(storage: &mut [MaybeUninit<ValRaw>]) -> Storage<'_, P, R> {
525 unsafe {
534 if P::flatten_count() <= MAX_FLAT_PARAMS {
535 if R::flatten_count() <= MAX_FLAT_RESULTS {
536 Storage::PdRd(slice_to_storage_mut(storage).assume_init_mut())
537 } else {
538 Storage::PdRi(slice_to_storage_mut(storage).assume_init_ref())
539 }
540 } else {
541 if R::flatten_count() <= MAX_FLAT_RESULTS {
542 Storage::PiRd(slice_to_storage_mut(storage).assume_init_mut())
543 } else {
544 Storage::PiRi(slice_to_storage_mut(storage).assume_init_ref())
545 }
546 }
547 }
548 }
549
550 fn lift_params(&self, cx: &mut LiftContext<'_>, ty: InterfaceType) -> Result<P> {
551 match self.lift_src() {
552 Src::Direct(storage) => P::linear_lift_from_flat(cx, ty, storage),
553 Src::Indirect(ptr) => {
554 let ptr = validate_inbounds::<P>(cx.memory(), ptr)?;
555 P::linear_lift_from_memory(cx, ty, &cx.memory()[ptr..][..P::SIZE32])
556 }
557 }
558 }
559
560 fn lift_src(&self) -> Src<'_, P::Lower> {
561 match self {
562 Storage::PdRd(storage) => unsafe { Src::Direct(&storage.a) },
570 Storage::PdRi(storage) => Src::Direct(&storage.a),
571 #[cfg(feature = "component-model-async")]
572 Storage::PdArRd(storage) => unsafe { Src::Direct(&storage.a.a) },
573 Storage::PiRd(storage) => unsafe { Src::Indirect(&storage.a) },
574 Storage::PiRi(storage) => Src::Indirect(&storage.a),
575 #[cfg(feature = "component-model-async")]
576 Storage::PiArRd(storage) => unsafe { Src::Indirect(&storage.a.a) },
577 }
578 }
579
580 fn lower_results<T>(
581 &mut self,
582 cx: &mut LowerContext<'_, T>,
583 ty: InterfaceType,
584 ret: R,
585 ) -> Result<()> {
586 match self.lower_dst() {
587 Dst::Direct(storage) => ret.linear_lower_to_flat(cx, ty, storage),
588 Dst::Indirect(ptr) => {
589 let ptr = validate_inbounds::<R>(cx.as_slice_mut(), ptr)?;
590 ret.linear_lower_to_memory(cx, ty, ptr)
591 }
592 }
593 }
594
595 fn lower_dst(&mut self) -> Dst<'_, R::Lower> {
596 match self {
597 Storage::PdRd(storage) => unsafe { Dst::Direct(&mut storage.b) },
605 Storage::PiRd(storage) => unsafe { Dst::Direct(&mut storage.b) },
606 #[cfg(feature = "component-model-async")]
607 Storage::PdArRd(storage) => unsafe { Dst::Direct(&mut storage.b) },
608 #[cfg(feature = "component-model-async")]
609 Storage::PiArRd(storage) => unsafe { Dst::Direct(&mut storage.b) },
610 Storage::PdRi(storage) => Dst::Indirect(&storage.b),
611 Storage::PiRi(storage) => Dst::Indirect(&storage.b),
612 }
613 }
614
615 #[cfg(feature = "component-model-async")]
616 fn async_retptr(&self) -> Option<&ValRaw> {
617 match self {
618 Storage::PdArRd(storage) => unsafe { Some(&storage.a.b) },
624 Storage::PiArRd(storage) => unsafe { Some(&storage.a.b) },
625 Storage::PdRd(_) | Storage::PiRd(_) | Storage::PdRi(_) | Storage::PiRi(_) => None,
626 }
627 }
628 }
629
630 #[cfg(feature = "component-model-async")]
631 impl<P> Storage<'_, P, u32>
632 where
633 P: ComponentType + Lift,
634 {
635 unsafe fn new_async<R>(storage: &mut [MaybeUninit<ValRaw>]) -> Storage<'_, P, u32>
642 where
643 R: ComponentType + Lower,
644 {
645 unsafe {
648 if P::flatten_count() <= wasmtime_environ::component::MAX_FLAT_ASYNC_PARAMS {
649 if R::flatten_count() == 0 {
650 Storage::PdRd(slice_to_storage_mut(storage).assume_init_mut())
651 } else {
652 Storage::PdArRd(slice_to_storage_mut(storage).assume_init_mut())
653 }
654 } else {
655 if R::flatten_count() == 0 {
656 Storage::PiRd(slice_to_storage_mut(storage).assume_init_mut())
657 } else {
658 Storage::PiArRd(slice_to_storage_mut(storage).assume_init_mut())
659 }
660 }
661 }
662 }
663 }
664}
665
666pub(crate) fn validate_inbounds<T: ComponentType>(memory: &[u8], ptr: &ValRaw) -> Result<usize> {
667 let ptr = usize::try_from(ptr.get_u32())?;
669 if ptr % usize::try_from(T::ALIGN32)? != 0 {
670 bail!("pointer not aligned");
671 }
672 let end = match ptr.checked_add(T::SIZE32) {
673 Some(n) => n,
674 None => bail!("pointer size overflow"),
675 };
676 if end > memory.len() {
677 bail!("pointer out of bounds")
678 }
679 Ok(ptr)
680}
681
682unsafe fn call_host_and_handle_result<T>(
683 cx: NonNull<VMOpaqueContext>,
684 func: impl FnOnce(StoreContextMut<'_, T>, Instance) -> Result<()>,
685) -> bool
686where
687 T: 'static,
688{
689 let cx = unsafe { VMComponentContext::from_opaque(cx) };
690 unsafe {
691 ComponentInstance::enter_host_from_wasm(cx, |store, instance| {
692 let mut store = store.unchecked_context_mut();
693 store.0.call_hook(CallHook::CallingHost)?;
694 let res = func(store.as_context_mut(), instance);
695 store.0.call_hook(CallHook::ReturningFromHost)?;
696 res
697 })
698 }
699}
700
701unsafe fn call_host_dynamic<T, F>(
702 store: StoreContextMut<'_, T>,
703 instance: Instance,
704 ty: TypeFuncIndex,
705 options: OptionsIndex,
706 storage: &mut [MaybeUninit<ValRaw>],
707 closure: F,
708) -> Result<()>
709where
710 F: Fn(
711 StoreContextMut<'_, T>,
712 ComponentFunc,
713 Vec<Val>,
714 usize,
715 ) -> Pin<Box<dyn Future<Output = Result<Vec<Val>>> + Send + 'static>>
716 + Send
717 + Sync
718 + 'static,
719 T: 'static,
720{
721 let (component, store) = instance.component_and_store_mut(store.0);
722 let mut store = StoreContextMut(store);
723 let vminstance = instance.id().get(store.0);
724 let opts = &component.env_component().options[options];
725 let async_ = opts.async_;
726 let caller_instance = opts.instance;
727 let mut flags = vminstance.instance_flags(caller_instance);
728
729 if unsafe { !flags.may_leave() } {
733 return Err(anyhow!(crate::Trap::CannotLeaveComponent));
734 }
735
736 let types = component.types();
737 let func_ty = &types[ty];
738 let param_tys = &types[func_ty.params];
739 let result_tys = &types[func_ty.results];
740
741 let mut params_and_results = Vec::new();
742 let mut lift = &mut LiftContext::new(store.0.store_opaque_mut(), options, instance);
743 lift.enter_call();
744 let max_flat = if async_ {
745 MAX_FLAT_ASYNC_PARAMS
746 } else {
747 MAX_FLAT_PARAMS
748 };
749 let ty = ComponentFunc::from(ty, &lift.instance_type());
750
751 let ret_index = unsafe {
752 dynamic_params_load(
753 &mut lift,
754 types,
755 storage,
756 param_tys,
757 &mut params_and_results,
758 max_flat,
759 )?
760 };
761 let result_start = params_and_results.len();
762 for _ in 0..result_tys.types.len() {
763 params_and_results.push(Val::Bool(false));
764 }
765
766 if async_ {
767 #[cfg(feature = "component-model-async")]
768 {
769 let retptr = if result_tys.types.len() == 0 {
770 0
771 } else {
772 let retptr = unsafe { storage[ret_index].assume_init() };
773 let mut lower = LowerContext::new(store.as_context_mut(), options, instance);
774 validate_inbounds_dynamic(&result_tys.abi, lower.as_slice_mut(), &retptr)?
775 };
776
777 let future = closure(store.as_context_mut(), ty, params_and_results, result_start);
778
779 let task = instance.first_poll(store, future, caller_instance, {
780 let result_tys = func_ty.results;
781 move |store: StoreContextMut<T>, result_vals: Vec<Val>| {
782 unsafe {
783 flags.set_may_leave(false);
784 }
785
786 let mut lower = LowerContext::new(store, options, instance);
787 let result_tys = &lower.types[result_tys];
788 let result_vals = &result_vals[result_start..];
789 assert_eq!(result_vals.len(), result_tys.types.len());
790 let mut ptr = retptr;
791 for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
792 let offset = lower.types.canonical_abi(ty).next_field32_size(&mut ptr);
793 val.store(&mut lower, *ty, offset)?;
794 }
795
796 unsafe {
797 flags.set_may_leave(true);
798 }
799
800 lower.exit_call()?;
801
802 Ok(())
803 }
804 })?;
805
806 let status = if let Some(task) = task {
807 Status::Started.pack(Some(task))
808 } else {
809 Status::Returned.pack(None)
810 };
811
812 storage[0] = MaybeUninit::new(ValRaw::i32(status as i32));
813 }
814 #[cfg(not(feature = "component-model-async"))]
815 {
816 unreachable!(
817 "async-lowered imports should have failed validation \
818 when `component-model-async` feature disabled"
819 );
820 }
821 } else {
822 let future = closure(store.as_context_mut(), ty, params_and_results, result_start);
823 let result_vals = concurrent::poll_and_block(store.0, future, caller_instance)?;
824 let result_vals = &result_vals[result_start..];
825
826 unsafe {
827 flags.set_may_leave(false);
828 }
829
830 let mut cx = LowerContext::new(store, options, instance);
831 if let Some(cnt) = result_tys.abi.flat_count(MAX_FLAT_RESULTS) {
832 let mut dst = storage[..cnt].iter_mut();
833 for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
834 val.lower(&mut cx, *ty, &mut dst)?;
835 }
836 assert!(dst.next().is_none());
837 } else {
838 let ret_ptr = unsafe { storage[ret_index].assume_init_ref() };
839 let mut ptr = validate_inbounds_dynamic(&result_tys.abi, cx.as_slice_mut(), ret_ptr)?;
840 for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
841 let offset = types.canonical_abi(ty).next_field32_size(&mut ptr);
842 val.store(&mut cx, *ty, offset)?;
843 }
844 }
845
846 unsafe {
847 flags.set_may_leave(true);
848 }
849
850 cx.exit_call()?;
851 }
852
853 Ok(())
854}
855
856unsafe fn dynamic_params_load(
865 cx: &mut LiftContext<'_>,
866 types: &ComponentTypes,
867 storage: &[MaybeUninit<ValRaw>],
868 param_tys: &TypeTuple,
869 params: &mut Vec<Val>,
870 max_flat_params: usize,
871) -> Result<usize> {
872 if let Some(param_count) = param_tys.abi.flat_count(max_flat_params) {
873 let storage =
875 unsafe { mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(&storage[..param_count]) };
876 let mut iter = storage.iter();
877 for ty in param_tys.types.iter() {
878 params.push(Val::lift(cx, *ty, &mut iter)?);
879 }
880 assert!(iter.next().is_none());
881 Ok(param_count)
882 } else {
883 let mut offset = validate_inbounds_dynamic(¶m_tys.abi, cx.memory(), unsafe {
884 storage[0].assume_init_ref()
885 })?;
886 for ty in param_tys.types.iter() {
887 let abi = types.canonical_abi(ty);
888 let size = usize::try_from(abi.size32).unwrap();
889 let memory = &cx.memory()[abi.next_field32_size(&mut offset)..][..size];
890 params.push(Val::load(cx, *ty, memory)?);
891 }
892 Ok(1)
893 }
894}
895
896pub(crate) fn validate_inbounds_dynamic(
897 abi: &CanonicalAbiInfo,
898 memory: &[u8],
899 ptr: &ValRaw,
900) -> Result<usize> {
901 let ptr = usize::try_from(ptr.get_u32())?;
903 if ptr % usize::try_from(abi.align32)? != 0 {
904 bail!("pointer not aligned");
905 }
906 let end = match ptr.checked_add(usize::try_from(abi.size32).unwrap()) {
907 Some(n) => n,
908 None => bail!("pointer size overflow"),
909 };
910 if end > memory.len() {
911 bail!("pointer out of bounds")
912 }
913 Ok(ptr)
914}
915
916extern "C" fn dynamic_entrypoint<T, F>(
917 cx: NonNull<VMOpaqueContext>,
918 data: NonNull<u8>,
919 ty: u32,
920 options: u32,
921 storage: NonNull<MaybeUninit<ValRaw>>,
922 storage_len: usize,
923) -> bool
924where
925 F: Fn(
926 StoreContextMut<'_, T>,
927 ComponentFunc,
928 Vec<Val>,
929 usize,
930 ) -> Pin<Box<dyn Future<Output = Result<Vec<Val>>> + Send + 'static>>
931 + Send
932 + Sync
933 + 'static,
934 T: 'static,
935{
936 let data = SendSyncPtr::new(NonNull::new(data.as_ptr() as *mut F).unwrap());
937 unsafe {
938 call_host_and_handle_result(cx, |store, instance| {
939 call_host_dynamic::<T, _>(
940 store,
941 instance,
942 TypeFuncIndex::from_u32(ty),
943 OptionsIndex::from_u32(options),
944 NonNull::slice_from_raw_parts(storage, storage_len).as_mut(),
945 &*data.as_ptr(),
946 )
947 })
948 }
949}