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