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::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 mut store: StoreContextMut<'_, T>,
237 instance: Instance,
238 ty: TypeFuncIndex,
239 options_idx: 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 options = Options::new_index(store.0, instance, options_idx);
249 let vminstance = instance.id().get(store.0);
250 let opts = &vminstance.component().env_component().options[options_idx];
251 let async_ = opts.async_;
252 let caller_instance = opts.instance;
253 let mut flags = vminstance.instance_flags(caller_instance);
254
255 if unsafe { !flags.may_leave() } {
259 return Err(anyhow!(crate::Trap::CannotLeaveComponent));
260 }
261
262 let types = vminstance.component().types().clone();
263 let ty = &types[ty];
264 let param_tys = InterfaceType::Tuple(ty.params);
265 let result_tys = InterfaceType::Tuple(ty.results);
266
267 if async_ {
268 #[cfg(feature = "component-model-async")]
269 {
270 let mut storage = unsafe { Storage::<'_, Params, u32>::new_async::<Return>(storage) };
271
272 let lift = &mut LiftContext::new(store.0.store_opaque_mut(), &options, instance);
275 lift.enter_call();
276 let params = storage.lift_params(lift, param_tys)?;
277
278 let retptr = match storage.async_retptr() {
280 Some(ptr) => {
281 let mut lower =
282 LowerContext::new(store.as_context_mut(), &options, &types, 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 let types = types.clone();
299 move |store: StoreContextMut<T>, ret: Return| {
300 unsafe {
301 flags.set_may_leave(false);
302 }
303 let mut lower = LowerContext::new(store, &options, &types, instance);
304 ret.linear_lower_to_memory(&mut lower, result_tys, retptr)?;
305 unsafe {
306 flags.set_may_leave(true);
307 }
308 lower.exit_call()?;
309 Ok(())
310 }
311 };
312 let task = match host_result {
313 HostResult::Done(result) => {
314 lower_result(store.as_context_mut(), result?)?;
315 None
316 }
317 #[cfg(feature = "component-model-async")]
318 HostResult::Future(future) => instance.first_poll(
319 store.as_context_mut(),
320 future,
321 caller_instance,
322 lower_result,
323 )?,
324 };
325
326 let status = if let Some(task) = task {
327 Status::Started.pack(Some(task))
328 } else {
329 Status::Returned.pack(None)
330 };
331
332 let mut lower = LowerContext::new(store, &options, &types, instance);
333 storage.lower_results(&mut lower, InterfaceType::U32, status)?;
334 }
335 #[cfg(not(feature = "component-model-async"))]
336 {
337 let _ = caller_instance;
338 unreachable!(
339 "async-lowered imports should have failed validation \
340 when `component-model-async` feature disabled"
341 );
342 }
343 } else {
344 let mut storage = unsafe { Storage::<'_, Params, Return>::new_sync(storage) };
345 let mut lift = LiftContext::new(store.0.store_opaque_mut(), &options, instance);
346 lift.enter_call();
347 let params = storage.lift_params(&mut lift, param_tys)?;
348
349 let ret = match closure(store.as_context_mut(), params) {
350 HostResult::Done(result) => result?,
351 #[cfg(feature = "component-model-async")]
352 HostResult::Future(future) => {
353 concurrent::poll_and_block(store.0, future, caller_instance)?
354 }
355 };
356
357 unsafe {
358 flags.set_may_leave(false);
359 }
360 let mut lower = LowerContext::new(store, &options, &types, instance);
361 storage.lower_results(&mut lower, result_tys, ret)?;
362 unsafe {
363 flags.set_may_leave(true);
364 }
365 lower.exit_call()?;
366 }
367
368 return Ok(());
369
370 enum Storage<'a, P: ComponentType, R: ComponentType> {
421 PdRd(&'a mut Union<P::Lower, MaybeUninit<R::Lower>>),
426
427 PdRi(&'a Pair<P::Lower, ValRaw>),
432
433 PiRd(&'a mut Union<ValRaw, MaybeUninit<R::Lower>>),
437
438 PiRi(&'a Pair<ValRaw, ValRaw>),
443
444 #[cfg(feature = "component-model-async")]
449 PdArRd(&'a mut Union<Pair<P::Lower, ValRaw>, MaybeUninit<R::Lower>>),
450
451 #[cfg(feature = "component-model-async")]
456 PiArRd(&'a mut Union<Pair<ValRaw, ValRaw>, MaybeUninit<R::Lower>>),
457 }
458
459 #[repr(C)]
462 #[derive(Copy, Clone)]
463 struct Pair<T, U> {
464 a: T,
465 b: U,
466 }
467
468 #[repr(C)]
471 union Union<T: Copy, U: Copy> {
472 a: T,
473 b: U,
474 }
475
476 enum Src<'a, T> {
478 Direct(&'a T),
481
482 Indirect(&'a ValRaw),
485 }
486
487 enum Dst<'a, T> {
489 Direct(&'a mut MaybeUninit<T>),
495
496 Indirect(&'a ValRaw),
502 }
503
504 impl<P, R> Storage<'_, P, R>
505 where
506 P: ComponentType + Lift,
507 R: ComponentType + Lower,
508 {
509 unsafe fn new_sync(storage: &mut [MaybeUninit<ValRaw>]) -> Storage<'_, P, R> {
526 unsafe {
535 if P::flatten_count() <= MAX_FLAT_PARAMS {
536 if R::flatten_count() <= MAX_FLAT_RESULTS {
537 Storage::PdRd(slice_to_storage_mut(storage).assume_init_mut())
538 } else {
539 Storage::PdRi(slice_to_storage_mut(storage).assume_init_ref())
540 }
541 } else {
542 if R::flatten_count() <= MAX_FLAT_RESULTS {
543 Storage::PiRd(slice_to_storage_mut(storage).assume_init_mut())
544 } else {
545 Storage::PiRi(slice_to_storage_mut(storage).assume_init_ref())
546 }
547 }
548 }
549 }
550
551 fn lift_params(&self, cx: &mut LiftContext<'_>, ty: InterfaceType) -> Result<P> {
552 match self.lift_src() {
553 Src::Direct(storage) => P::linear_lift_from_flat(cx, ty, storage),
554 Src::Indirect(ptr) => {
555 let ptr = validate_inbounds::<P>(cx.memory(), ptr)?;
556 P::linear_lift_from_memory(cx, ty, &cx.memory()[ptr..][..P::SIZE32])
557 }
558 }
559 }
560
561 fn lift_src(&self) -> Src<'_, P::Lower> {
562 match self {
563 Storage::PdRd(storage) => unsafe { Src::Direct(&storage.a) },
571 Storage::PdRi(storage) => Src::Direct(&storage.a),
572 #[cfg(feature = "component-model-async")]
573 Storage::PdArRd(storage) => unsafe { Src::Direct(&storage.a.a) },
574 Storage::PiRd(storage) => unsafe { Src::Indirect(&storage.a) },
575 Storage::PiRi(storage) => Src::Indirect(&storage.a),
576 #[cfg(feature = "component-model-async")]
577 Storage::PiArRd(storage) => unsafe { Src::Indirect(&storage.a.a) },
578 }
579 }
580
581 fn lower_results<T>(
582 &mut self,
583 cx: &mut LowerContext<'_, T>,
584 ty: InterfaceType,
585 ret: R,
586 ) -> Result<()> {
587 match self.lower_dst() {
588 Dst::Direct(storage) => ret.linear_lower_to_flat(cx, ty, storage),
589 Dst::Indirect(ptr) => {
590 let ptr = validate_inbounds::<R>(cx.as_slice_mut(), ptr)?;
591 ret.linear_lower_to_memory(cx, ty, ptr)
592 }
593 }
594 }
595
596 fn lower_dst(&mut self) -> Dst<'_, R::Lower> {
597 match self {
598 Storage::PdRd(storage) => unsafe { Dst::Direct(&mut storage.b) },
606 Storage::PiRd(storage) => unsafe { Dst::Direct(&mut storage.b) },
607 #[cfg(feature = "component-model-async")]
608 Storage::PdArRd(storage) => unsafe { Dst::Direct(&mut storage.b) },
609 #[cfg(feature = "component-model-async")]
610 Storage::PiArRd(storage) => unsafe { Dst::Direct(&mut storage.b) },
611 Storage::PdRi(storage) => Dst::Indirect(&storage.b),
612 Storage::PiRi(storage) => Dst::Indirect(&storage.b),
613 }
614 }
615
616 #[cfg(feature = "component-model-async")]
617 fn async_retptr(&self) -> Option<&ValRaw> {
618 match self {
619 Storage::PdArRd(storage) => unsafe { Some(&storage.a.b) },
625 Storage::PiArRd(storage) => unsafe { Some(&storage.a.b) },
626 Storage::PdRd(_) | Storage::PiRd(_) | Storage::PdRi(_) | Storage::PiRi(_) => None,
627 }
628 }
629 }
630
631 #[cfg(feature = "component-model-async")]
632 impl<P> Storage<'_, P, u32>
633 where
634 P: ComponentType + Lift,
635 {
636 unsafe fn new_async<R>(storage: &mut [MaybeUninit<ValRaw>]) -> Storage<'_, P, u32>
643 where
644 R: ComponentType + Lower,
645 {
646 unsafe {
649 if P::flatten_count() <= wasmtime_environ::component::MAX_FLAT_ASYNC_PARAMS {
650 if R::flatten_count() == 0 {
651 Storage::PdRd(slice_to_storage_mut(storage).assume_init_mut())
652 } else {
653 Storage::PdArRd(slice_to_storage_mut(storage).assume_init_mut())
654 }
655 } else {
656 if R::flatten_count() == 0 {
657 Storage::PiRd(slice_to_storage_mut(storage).assume_init_mut())
658 } else {
659 Storage::PiArRd(slice_to_storage_mut(storage).assume_init_mut())
660 }
661 }
662 }
663 }
664 }
665}
666
667pub(crate) fn validate_inbounds<T: ComponentType>(memory: &[u8], ptr: &ValRaw) -> Result<usize> {
668 let ptr = usize::try_from(ptr.get_u32())?;
670 if ptr % usize::try_from(T::ALIGN32)? != 0 {
671 bail!("pointer not aligned");
672 }
673 let end = match ptr.checked_add(T::SIZE32) {
674 Some(n) => n,
675 None => bail!("pointer size overflow"),
676 };
677 if end > memory.len() {
678 bail!("pointer out of bounds")
679 }
680 Ok(ptr)
681}
682
683unsafe fn call_host_and_handle_result<T>(
684 cx: NonNull<VMOpaqueContext>,
685 func: impl FnOnce(StoreContextMut<'_, T>, Instance) -> Result<()>,
686) -> bool
687where
688 T: 'static,
689{
690 let cx = unsafe { VMComponentContext::from_opaque(cx) };
691 unsafe {
692 ComponentInstance::enter_host_from_wasm(cx, |store, instance| {
693 let mut store = store.unchecked_context_mut();
694 store.0.call_hook(CallHook::CallingHost)?;
695 let res = func(store.as_context_mut(), instance);
696 store.0.call_hook(CallHook::ReturningFromHost)?;
697 res
698 })
699 }
700}
701
702unsafe fn call_host_dynamic<T, F>(
703 mut store: StoreContextMut<'_, T>,
704 instance: Instance,
705 ty: TypeFuncIndex,
706 options_idx: OptionsIndex,
707 storage: &mut [MaybeUninit<ValRaw>],
708 closure: F,
709) -> Result<()>
710where
711 F: Fn(
712 StoreContextMut<'_, T>,
713 ComponentFunc,
714 Vec<Val>,
715 usize,
716 ) -> Pin<Box<dyn Future<Output = Result<Vec<Val>>> + Send + 'static>>
717 + Send
718 + Sync
719 + 'static,
720 T: 'static,
721{
722 let options = Options::new_index(store.0, instance, options_idx);
723 let vminstance = instance.id().get(store.0);
724 let opts = &vminstance.component().env_component().options[options_idx];
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 = instance.id().get(store.0).component().types().clone();
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 =
774 LowerContext::new(store.as_context_mut(), &options, &types, instance);
775 validate_inbounds_dynamic(&result_tys.abi, lower.as_slice_mut(), &retptr)?
776 };
777
778 let future = closure(store.as_context_mut(), ty, params_and_results, result_start);
779
780 let task = instance.first_poll(store, future, caller_instance, {
781 let types = types.clone();
782 let result_tys = func_ty.results;
783 move |store: StoreContextMut<T>, result_vals: Vec<Val>| {
784 let result_tys = &types[result_tys];
785 let result_vals = &result_vals[result_start..];
786 assert_eq!(result_vals.len(), result_tys.types.len());
787
788 unsafe {
789 flags.set_may_leave(false);
790 }
791
792 let mut lower = LowerContext::new(store, &options, &types, instance);
793 let mut ptr = retptr;
794 for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
795 let offset = types.canonical_abi(ty).next_field32_size(&mut ptr);
796 val.store(&mut lower, *ty, offset)?;
797 }
798
799 unsafe {
800 flags.set_may_leave(true);
801 }
802
803 lower.exit_call()?;
804
805 Ok(())
806 }
807 })?;
808
809 let status = if let Some(task) = task {
810 Status::Started.pack(Some(task))
811 } else {
812 Status::Returned.pack(None)
813 };
814
815 storage[0] = MaybeUninit::new(ValRaw::i32(status as i32));
816 }
817 #[cfg(not(feature = "component-model-async"))]
818 {
819 unreachable!(
820 "async-lowered imports should have failed validation \
821 when `component-model-async` feature disabled"
822 );
823 }
824 } else {
825 let future = closure(store.as_context_mut(), ty, params_and_results, result_start);
826 let result_vals = concurrent::poll_and_block(store.0, future, caller_instance)?;
827 let result_vals = &result_vals[result_start..];
828
829 unsafe {
830 flags.set_may_leave(false);
831 }
832
833 let mut cx = LowerContext::new(store, &options, &types, instance);
834 if let Some(cnt) = result_tys.abi.flat_count(MAX_FLAT_RESULTS) {
835 let mut dst = storage[..cnt].iter_mut();
836 for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
837 val.lower(&mut cx, *ty, &mut dst)?;
838 }
839 assert!(dst.next().is_none());
840 } else {
841 let ret_ptr = unsafe { storage[ret_index].assume_init_ref() };
842 let mut ptr = validate_inbounds_dynamic(&result_tys.abi, cx.as_slice_mut(), ret_ptr)?;
843 for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
844 let offset = types.canonical_abi(ty).next_field32_size(&mut ptr);
845 val.store(&mut cx, *ty, offset)?;
846 }
847 }
848
849 unsafe {
850 flags.set_may_leave(true);
851 }
852
853 cx.exit_call()?;
854 }
855
856 Ok(())
857}
858
859unsafe fn dynamic_params_load(
868 cx: &mut LiftContext<'_>,
869 types: &ComponentTypes,
870 storage: &[MaybeUninit<ValRaw>],
871 param_tys: &TypeTuple,
872 params: &mut Vec<Val>,
873 max_flat_params: usize,
874) -> Result<usize> {
875 if let Some(param_count) = param_tys.abi.flat_count(max_flat_params) {
876 let storage =
878 unsafe { mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(&storage[..param_count]) };
879 let mut iter = storage.iter();
880 for ty in param_tys.types.iter() {
881 params.push(Val::lift(cx, *ty, &mut iter)?);
882 }
883 assert!(iter.next().is_none());
884 Ok(param_count)
885 } else {
886 let mut offset = validate_inbounds_dynamic(¶m_tys.abi, cx.memory(), unsafe {
887 storage[0].assume_init_ref()
888 })?;
889 for ty in param_tys.types.iter() {
890 let abi = types.canonical_abi(ty);
891 let size = usize::try_from(abi.size32).unwrap();
892 let memory = &cx.memory()[abi.next_field32_size(&mut offset)..][..size];
893 params.push(Val::load(cx, *ty, memory)?);
894 }
895 Ok(1)
896 }
897}
898
899pub(crate) fn validate_inbounds_dynamic(
900 abi: &CanonicalAbiInfo,
901 memory: &[u8],
902 ptr: &ValRaw,
903) -> Result<usize> {
904 let ptr = usize::try_from(ptr.get_u32())?;
906 if ptr % usize::try_from(abi.align32)? != 0 {
907 bail!("pointer not aligned");
908 }
909 let end = match ptr.checked_add(usize::try_from(abi.size32).unwrap()) {
910 Some(n) => n,
911 None => bail!("pointer size overflow"),
912 };
913 if end > memory.len() {
914 bail!("pointer out of bounds")
915 }
916 Ok(ptr)
917}
918
919extern "C" fn dynamic_entrypoint<T, F>(
920 cx: NonNull<VMOpaqueContext>,
921 data: NonNull<u8>,
922 ty: u32,
923 options: u32,
924 storage: NonNull<MaybeUninit<ValRaw>>,
925 storage_len: usize,
926) -> bool
927where
928 F: Fn(
929 StoreContextMut<'_, T>,
930 ComponentFunc,
931 Vec<Val>,
932 usize,
933 ) -> Pin<Box<dyn Future<Output = Result<Vec<Val>>> + Send + 'static>>
934 + Send
935 + Sync
936 + 'static,
937 T: 'static,
938{
939 let data = SendSyncPtr::new(NonNull::new(data.as_ptr() as *mut F).unwrap());
940 unsafe {
941 call_host_and_handle_result(cx, |store, instance| {
942 call_host_dynamic::<T, _>(
943 store,
944 instance,
945 TypeFuncIndex::from_u32(ty),
946 OptionsIndex::from_u32(options),
947 NonNull::slice_from_raw_parts(storage, storage_len).as_mut(),
948 &*data.as_ptr(),
949 )
950 })
951 }
952}