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