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
44trait FunctionStyle {
45 const ASYNC: bool;
46}
47
48struct SyncStyle;
49
50impl FunctionStyle for SyncStyle {
51 const ASYNC: bool = false;
52}
53
54#[cfg(feature = "component-model-async")]
55struct AsyncStyle;
56
57#[cfg(feature = "component-model-async")]
58impl FunctionStyle for AsyncStyle {
59 const ASYNC: bool = true;
60}
61
62impl HostFunc {
63 fn from_canonical<T, F, P, R, S>(func: F) -> Arc<HostFunc>
64 where
65 F: Fn(StoreContextMut<'_, T>, P) -> HostResult<R> + Send + Sync + 'static,
66 P: ComponentNamedList + Lift + 'static,
67 R: ComponentNamedList + Lower + 'static,
68 T: 'static,
69 S: FunctionStyle + 'static,
70 {
71 let entrypoint = Self::entrypoint::<T, F, P, R, S>;
72 Arc::new(HostFunc {
73 entrypoint,
74 typecheck: Box::new(typecheck::<P, R, S>),
75 func: Box::new(func),
76 })
77 }
78
79 pub(crate) fn from_closure<T, F, P, R>(func: F) -> Arc<HostFunc>
80 where
81 T: 'static,
82 F: Fn(StoreContextMut<T>, P) -> Result<R> + Send + Sync + 'static,
83 P: ComponentNamedList + Lift + 'static,
84 R: ComponentNamedList + Lower + 'static,
85 {
86 Self::from_canonical::<T, _, _, _, SyncStyle>(move |store, params| {
87 HostResult::Done(func(store, params))
88 })
89 }
90
91 #[cfg(feature = "component-model-async")]
92 pub(crate) fn from_concurrent<T, F, P, R>(func: F) -> Arc<HostFunc>
93 where
94 T: 'static,
95 F: Fn(&Accessor<T>, P) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>>
96 + Send
97 + Sync
98 + 'static,
99 P: ComponentNamedList + Lift + 'static,
100 R: ComponentNamedList + Lower + 'static,
101 {
102 let func = Arc::new(func);
103 Self::from_canonical::<T, _, _, _, AsyncStyle>(move |store, params| {
104 let func = func.clone();
105 HostResult::Future(Box::pin(
106 store.wrap_call(move |accessor| func(accessor, params)),
107 ))
108 })
109 }
110
111 extern "C" fn entrypoint<T, F, P, R, S>(
112 cx: NonNull<VMOpaqueContext>,
113 data: NonNull<u8>,
114 ty: u32,
115 options: u32,
116 storage: NonNull<MaybeUninit<ValRaw>>,
117 storage_len: usize,
118 ) -> bool
119 where
120 F: Fn(StoreContextMut<'_, T>, P) -> HostResult<R> + Send + Sync + 'static,
121 P: ComponentNamedList + Lift,
122 R: ComponentNamedList + Lower + 'static,
123 T: 'static,
124 S: FunctionStyle,
125 {
126 let data = SendSyncPtr::new(NonNull::new(data.as_ptr() as *mut F).unwrap());
127 unsafe {
128 call_host_and_handle_result::<T>(cx, |store, instance| {
129 call_host::<_, _, _, _, S>(
130 store,
131 instance,
132 TypeFuncIndex::from_u32(ty),
133 OptionsIndex::from_u32(options),
134 NonNull::slice_from_raw_parts(storage, storage_len).as_mut(),
135 move |store, args| (*data.as_ptr())(store, args),
136 )
137 })
138 }
139 }
140
141 fn new_dynamic_canonical<T, F, S>(func: F) -> Arc<HostFunc>
142 where
143 F: Fn(
144 StoreContextMut<'_, T>,
145 ComponentFunc,
146 Vec<Val>,
147 usize,
148 ) -> Pin<Box<dyn Future<Output = Result<Vec<Val>>> + Send + 'static>>
149 + Send
150 + Sync
151 + 'static,
152 T: 'static,
153 S: FunctionStyle,
154 {
155 Arc::new(HostFunc {
156 entrypoint: dynamic_entrypoint::<T, F, S>,
157 typecheck: Box::new(move |ty, types| {
161 let ty = &types.types[ty];
162 if S::ASYNC != ty.async_ {
163 bail!("type mismatch with async");
164 }
165
166 Ok(())
167 }),
168 func: Box::new(func),
169 })
170 }
171
172 pub(crate) fn new_dynamic<T: 'static, F>(func: F) -> Arc<HostFunc>
173 where
174 F: Fn(StoreContextMut<'_, T>, ComponentFunc, &[Val], &mut [Val]) -> Result<()>
175 + Send
176 + Sync
177 + 'static,
178 {
179 Self::new_dynamic_canonical::<T, _, SyncStyle>(
180 move |store, ty, mut params_and_results, result_start| {
181 let (params, results) = params_and_results.split_at_mut(result_start);
182 let result = func(store, ty, params, results).map(move |()| params_and_results);
183 Box::pin(async move { result })
184 },
185 )
186 }
187
188 #[cfg(feature = "component-model-async")]
189 pub(crate) fn new_dynamic_concurrent<T, F>(func: F) -> Arc<HostFunc>
190 where
191 T: 'static,
192 F: for<'a> Fn(
193 &'a Accessor<T>,
194 ComponentFunc,
195 &'a [Val],
196 &'a mut [Val],
197 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
198 + Send
199 + Sync
200 + 'static,
201 {
202 let func = Arc::new(func);
203 Self::new_dynamic_canonical::<T, _, AsyncStyle>(
204 move |store, ty, mut params_and_results, result_start| {
205 let func = func.clone();
206 Box::pin(store.wrap_call(move |accessor| {
207 Box::pin(async move {
208 let (params, results) = params_and_results.split_at_mut(result_start);
209 func(accessor, ty, params, results).await?;
210 Ok(params_and_results)
211 })
212 }))
213 },
214 )
215 }
216
217 pub fn typecheck(&self, ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()> {
218 (self.typecheck)(ty, types)
219 }
220
221 pub fn lowering(&self) -> VMLowering {
222 let data = NonNull::from(&*self.func).cast();
223 VMLowering {
224 callee: NonNull::new(self.entrypoint as *mut _).unwrap().into(),
225 data: data.into(),
226 }
227 }
228}
229
230fn typecheck<P, R, S>(ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()>
231where
232 P: ComponentNamedList + Lift,
233 R: ComponentNamedList + Lower,
234 S: FunctionStyle,
235{
236 let ty = &types.types[ty];
237 if S::ASYNC != ty.async_ {
238 bail!("type mismatch with async");
239 }
240 P::typecheck(&InterfaceType::Tuple(ty.params), types)
241 .context("type mismatch with parameters")?;
242 R::typecheck(&InterfaceType::Tuple(ty.results), types).context("type mismatch with results")?;
243 Ok(())
244}
245
246unsafe fn call_host<T, Params, Return, F, S>(
269 store: StoreContextMut<'_, T>,
270 instance: Instance,
271 ty: TypeFuncIndex,
272 options: OptionsIndex,
273 storage: &mut [MaybeUninit<ValRaw>],
274 closure: F,
275) -> Result<()>
276where
277 F: Fn(StoreContextMut<'_, T>, Params) -> HostResult<Return> + Send + Sync + 'static,
278 Params: Lift,
279 Return: Lower + 'static,
280 S: FunctionStyle,
281{
282 let (component, store) = instance.component_and_store_mut(store.0);
283 let mut store = StoreContextMut(store);
284 let vminstance = instance.id().get(store.0);
285 let opts = &vminstance.component().env_component().options[options];
286 let async_lower = opts.async_;
287 let caller_instance = opts.instance;
288 let mut flags = vminstance.instance_flags(caller_instance);
289
290 if unsafe { !flags.may_leave() } {
294 return Err(anyhow!(crate::Trap::CannotLeaveComponent));
295 }
296
297 let types = component.types();
298 let ty = &types[ty];
299 let param_tys = InterfaceType::Tuple(ty.params);
300 let result_tys = InterfaceType::Tuple(ty.results);
301
302 if async_lower {
303 #[cfg(feature = "component-model-async")]
304 {
305 let mut storage = unsafe { Storage::<'_, Params, u32>::new_async::<Return>(storage) };
306
307 let lift = &mut LiftContext::new(store.0.store_opaque_mut(), options, instance);
310 lift.enter_call();
311 let params = storage.lift_params(lift, param_tys)?;
312
313 let retptr = match storage.async_retptr() {
315 Some(ptr) => {
316 let mut lower = LowerContext::new(store.as_context_mut(), options, instance);
317 validate_inbounds::<Return>(lower.as_slice_mut(), ptr)?
318 }
319 None => {
324 assert_eq!(Return::flatten_count(), 0);
325 0
326 }
327 };
328
329 let host_result = closure(store.as_context_mut(), params);
330
331 let mut lower_result = {
332 move |store: StoreContextMut<T>, ret: Return| {
333 unsafe {
334 flags.set_may_leave(false);
335 }
336 let mut lower = LowerContext::new(store, options, instance);
337 ret.linear_lower_to_memory(&mut lower, result_tys, retptr)?;
338 unsafe {
339 flags.set_may_leave(true);
340 }
341 lower.exit_call()?;
342 Ok(())
343 }
344 };
345 let task = match host_result {
346 HostResult::Done(result) => {
347 lower_result(store.as_context_mut(), result?)?;
348 None
349 }
350 #[cfg(feature = "component-model-async")]
351 HostResult::Future(future) => instance.first_poll(
352 store.as_context_mut(),
353 future,
354 caller_instance,
355 lower_result,
356 )?,
357 };
358
359 let status = if let Some(task) = task {
360 Status::Started.pack(Some(task))
361 } else {
362 Status::Returned.pack(None)
363 };
364
365 let mut lower = LowerContext::new(store, options, instance);
366 storage.lower_results(&mut lower, InterfaceType::U32, status)?;
367 }
368 #[cfg(not(feature = "component-model-async"))]
369 {
370 let _ = caller_instance;
371 unreachable!(
372 "async-lowered imports should have failed validation \
373 when `component-model-async` feature disabled"
374 );
375 }
376 } else {
377 if S::ASYNC {
378 concurrent::check_blocking(store.0)?;
382 }
383
384 let mut storage = unsafe { Storage::<'_, Params, Return>::new_sync(storage) };
385 let mut lift = LiftContext::new(store.0.store_opaque_mut(), options, instance);
386 lift.enter_call();
387 let params = storage.lift_params(&mut lift, param_tys)?;
388
389 let ret = match closure(store.as_context_mut(), params) {
390 HostResult::Done(result) => result?,
391 #[cfg(feature = "component-model-async")]
392 HostResult::Future(future) => {
393 concurrent::poll_and_block(store.0, future, caller_instance)?
394 }
395 };
396
397 unsafe {
398 flags.set_may_leave(false);
399 }
400 let mut lower = LowerContext::new(store, options, instance);
401 storage.lower_results(&mut lower, result_tys, ret)?;
402 unsafe {
403 flags.set_may_leave(true);
404 }
405 lower.exit_call()?;
406 }
407
408 return Ok(());
409
410 enum Storage<'a, P: ComponentType, R: ComponentType> {
461 PdRd(&'a mut Union<P::Lower, MaybeUninit<R::Lower>>),
466
467 PdRi(&'a Pair<P::Lower, ValRaw>),
472
473 PiRd(&'a mut Union<ValRaw, MaybeUninit<R::Lower>>),
477
478 PiRi(&'a Pair<ValRaw, ValRaw>),
483
484 #[cfg(feature = "component-model-async")]
489 PdArRd(&'a mut Union<Pair<P::Lower, ValRaw>, MaybeUninit<R::Lower>>),
490
491 #[cfg(feature = "component-model-async")]
496 PiArRd(&'a mut Union<Pair<ValRaw, ValRaw>, MaybeUninit<R::Lower>>),
497 }
498
499 #[repr(C)]
502 #[derive(Copy, Clone)]
503 struct Pair<T, U> {
504 a: T,
505 b: U,
506 }
507
508 #[repr(C)]
511 union Union<T: Copy, U: Copy> {
512 a: T,
513 b: U,
514 }
515
516 enum Src<'a, T> {
518 Direct(&'a T),
521
522 Indirect(&'a ValRaw),
525 }
526
527 enum Dst<'a, T> {
529 Direct(&'a mut MaybeUninit<T>),
535
536 Indirect(&'a ValRaw),
542 }
543
544 impl<P, R> Storage<'_, P, R>
545 where
546 P: ComponentType + Lift,
547 R: ComponentType + Lower,
548 {
549 unsafe fn new_sync(storage: &mut [MaybeUninit<ValRaw>]) -> Storage<'_, P, R> {
566 unsafe {
575 if P::flatten_count() <= MAX_FLAT_PARAMS {
576 if R::flatten_count() <= MAX_FLAT_RESULTS {
577 Storage::PdRd(slice_to_storage_mut(storage).assume_init_mut())
578 } else {
579 Storage::PdRi(slice_to_storage_mut(storage).assume_init_ref())
580 }
581 } else {
582 if R::flatten_count() <= MAX_FLAT_RESULTS {
583 Storage::PiRd(slice_to_storage_mut(storage).assume_init_mut())
584 } else {
585 Storage::PiRi(slice_to_storage_mut(storage).assume_init_ref())
586 }
587 }
588 }
589 }
590
591 fn lift_params(&self, cx: &mut LiftContext<'_>, ty: InterfaceType) -> Result<P> {
592 match self.lift_src() {
593 Src::Direct(storage) => P::linear_lift_from_flat(cx, ty, storage),
594 Src::Indirect(ptr) => {
595 let ptr = validate_inbounds::<P>(cx.memory(), ptr)?;
596 P::linear_lift_from_memory(cx, ty, &cx.memory()[ptr..][..P::SIZE32])
597 }
598 }
599 }
600
601 fn lift_src(&self) -> Src<'_, P::Lower> {
602 match self {
603 Storage::PdRd(storage) => unsafe { Src::Direct(&storage.a) },
611 Storage::PdRi(storage) => Src::Direct(&storage.a),
612 #[cfg(feature = "component-model-async")]
613 Storage::PdArRd(storage) => unsafe { Src::Direct(&storage.a.a) },
614 Storage::PiRd(storage) => unsafe { Src::Indirect(&storage.a) },
615 Storage::PiRi(storage) => Src::Indirect(&storage.a),
616 #[cfg(feature = "component-model-async")]
617 Storage::PiArRd(storage) => unsafe { Src::Indirect(&storage.a.a) },
618 }
619 }
620
621 fn lower_results<T>(
622 &mut self,
623 cx: &mut LowerContext<'_, T>,
624 ty: InterfaceType,
625 ret: R,
626 ) -> Result<()> {
627 match self.lower_dst() {
628 Dst::Direct(storage) => ret.linear_lower_to_flat(cx, ty, storage),
629 Dst::Indirect(ptr) => {
630 let ptr = validate_inbounds::<R>(cx.as_slice_mut(), ptr)?;
631 ret.linear_lower_to_memory(cx, ty, ptr)
632 }
633 }
634 }
635
636 fn lower_dst(&mut self) -> Dst<'_, R::Lower> {
637 match self {
638 Storage::PdRd(storage) => unsafe { Dst::Direct(&mut storage.b) },
646 Storage::PiRd(storage) => unsafe { Dst::Direct(&mut storage.b) },
647 #[cfg(feature = "component-model-async")]
648 Storage::PdArRd(storage) => unsafe { Dst::Direct(&mut storage.b) },
649 #[cfg(feature = "component-model-async")]
650 Storage::PiArRd(storage) => unsafe { Dst::Direct(&mut storage.b) },
651 Storage::PdRi(storage) => Dst::Indirect(&storage.b),
652 Storage::PiRi(storage) => Dst::Indirect(&storage.b),
653 }
654 }
655
656 #[cfg(feature = "component-model-async")]
657 fn async_retptr(&self) -> Option<&ValRaw> {
658 match self {
659 Storage::PdArRd(storage) => unsafe { Some(&storage.a.b) },
665 Storage::PiArRd(storage) => unsafe { Some(&storage.a.b) },
666 Storage::PdRd(_) | Storage::PiRd(_) | Storage::PdRi(_) | Storage::PiRi(_) => None,
667 }
668 }
669 }
670
671 #[cfg(feature = "component-model-async")]
672 impl<P> Storage<'_, P, u32>
673 where
674 P: ComponentType + Lift,
675 {
676 unsafe fn new_async<R>(storage: &mut [MaybeUninit<ValRaw>]) -> Storage<'_, P, u32>
683 where
684 R: ComponentType + Lower,
685 {
686 unsafe {
689 if P::flatten_count() <= wasmtime_environ::component::MAX_FLAT_ASYNC_PARAMS {
690 if R::flatten_count() == 0 {
691 Storage::PdRd(slice_to_storage_mut(storage).assume_init_mut())
692 } else {
693 Storage::PdArRd(slice_to_storage_mut(storage).assume_init_mut())
694 }
695 } else {
696 if R::flatten_count() == 0 {
697 Storage::PiRd(slice_to_storage_mut(storage).assume_init_mut())
698 } else {
699 Storage::PiArRd(slice_to_storage_mut(storage).assume_init_mut())
700 }
701 }
702 }
703 }
704 }
705}
706
707pub(crate) fn validate_inbounds<T: ComponentType>(memory: &[u8], ptr: &ValRaw) -> Result<usize> {
708 let ptr = usize::try_from(ptr.get_u32())?;
710 if ptr % usize::try_from(T::ALIGN32)? != 0 {
711 bail!("pointer not aligned");
712 }
713 let end = match ptr.checked_add(T::SIZE32) {
714 Some(n) => n,
715 None => bail!("pointer size overflow"),
716 };
717 if end > memory.len() {
718 bail!("pointer out of bounds")
719 }
720 Ok(ptr)
721}
722
723unsafe fn call_host_and_handle_result<T>(
724 cx: NonNull<VMOpaqueContext>,
725 func: impl FnOnce(StoreContextMut<'_, T>, Instance) -> Result<()>,
726) -> bool
727where
728 T: 'static,
729{
730 let cx = unsafe { VMComponentContext::from_opaque(cx) };
731 unsafe {
732 ComponentInstance::enter_host_from_wasm(cx, |store, instance| {
733 let mut store = store.unchecked_context_mut();
734 store.0.call_hook(CallHook::CallingHost)?;
735 let res = func(store.as_context_mut(), instance);
736 store.0.call_hook(CallHook::ReturningFromHost)?;
737 res
738 })
739 }
740}
741
742unsafe fn call_host_dynamic<T, F, S>(
743 store: StoreContextMut<'_, T>,
744 instance: Instance,
745 ty: TypeFuncIndex,
746 options: OptionsIndex,
747 storage: &mut [MaybeUninit<ValRaw>],
748 closure: F,
749) -> Result<()>
750where
751 F: Fn(
752 StoreContextMut<'_, T>,
753 ComponentFunc,
754 Vec<Val>,
755 usize,
756 ) -> Pin<Box<dyn Future<Output = Result<Vec<Val>>> + Send + 'static>>
757 + Send
758 + Sync
759 + 'static,
760 T: 'static,
761 S: FunctionStyle,
762{
763 let (component, store) = instance.component_and_store_mut(store.0);
764 let mut store = StoreContextMut(store);
765 let vminstance = instance.id().get(store.0);
766 let opts = &component.env_component().options[options];
767 let async_lower = opts.async_;
768 let caller_instance = opts.instance;
769 let mut flags = vminstance.instance_flags(caller_instance);
770
771 if unsafe { !flags.may_leave() } {
775 return Err(anyhow!(crate::Trap::CannotLeaveComponent));
776 }
777
778 let types = component.types();
779 let func_ty = &types[ty];
780 let param_tys = &types[func_ty.params];
781 let result_tys = &types[func_ty.results];
782
783 let mut params_and_results = Vec::new();
784 let mut lift = &mut LiftContext::new(store.0.store_opaque_mut(), options, instance);
785 lift.enter_call();
786 let max_flat = if async_lower {
787 MAX_FLAT_ASYNC_PARAMS
788 } else {
789 MAX_FLAT_PARAMS
790 };
791 let ty = ComponentFunc::from(ty, &lift.instance_type());
792
793 let ret_index = unsafe {
794 dynamic_params_load(
795 &mut lift,
796 types,
797 storage,
798 param_tys,
799 &mut params_and_results,
800 max_flat,
801 )?
802 };
803 let result_start = params_and_results.len();
804 for _ in 0..result_tys.types.len() {
805 params_and_results.push(Val::Bool(false));
806 }
807
808 if async_lower {
809 #[cfg(feature = "component-model-async")]
810 {
811 let retptr = if result_tys.types.len() == 0 {
812 0
813 } else {
814 let retptr = unsafe { storage[ret_index].assume_init() };
815 let mut lower = LowerContext::new(store.as_context_mut(), options, instance);
816 validate_inbounds_dynamic(&result_tys.abi, lower.as_slice_mut(), &retptr)?
817 };
818
819 let future = closure(store.as_context_mut(), ty, params_and_results, result_start);
820
821 let task = instance.first_poll(store, future, caller_instance, {
822 let result_tys = func_ty.results;
823 move |store: StoreContextMut<T>, result_vals: Vec<Val>| {
824 unsafe {
825 flags.set_may_leave(false);
826 }
827
828 let mut lower = LowerContext::new(store, options, instance);
829 let result_tys = &lower.types[result_tys];
830 let result_vals = &result_vals[result_start..];
831 assert_eq!(result_vals.len(), result_tys.types.len());
832 let mut ptr = retptr;
833 for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
834 let offset = lower.types.canonical_abi(ty).next_field32_size(&mut ptr);
835 val.store(&mut lower, *ty, offset)?;
836 }
837
838 unsafe {
839 flags.set_may_leave(true);
840 }
841
842 lower.exit_call()?;
843
844 Ok(())
845 }
846 })?;
847
848 let status = if let Some(task) = task {
849 Status::Started.pack(Some(task))
850 } else {
851 Status::Returned.pack(None)
852 };
853
854 storage[0] = MaybeUninit::new(ValRaw::i32(status as i32));
855 }
856 #[cfg(not(feature = "component-model-async"))]
857 {
858 unreachable!(
859 "async-lowered imports should have failed validation \
860 when `component-model-async` feature disabled"
861 );
862 }
863 } else {
864 if S::ASYNC {
865 concurrent::check_blocking(store.0)?;
869 }
870
871 let future = closure(store.as_context_mut(), ty, params_and_results, result_start);
872 let result_vals = concurrent::poll_and_block(store.0, future, caller_instance)?;
873 let result_vals = &result_vals[result_start..];
874
875 unsafe {
876 flags.set_may_leave(false);
877 }
878
879 let mut cx = LowerContext::new(store, options, instance);
880 if let Some(cnt) = result_tys.abi.flat_count(MAX_FLAT_RESULTS) {
881 let mut dst = storage[..cnt].iter_mut();
882 for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
883 val.lower(&mut cx, *ty, &mut dst)?;
884 }
885 assert!(dst.next().is_none());
886 } else {
887 let ret_ptr = unsafe { storage[ret_index].assume_init_ref() };
888 let mut ptr = validate_inbounds_dynamic(&result_tys.abi, cx.as_slice_mut(), ret_ptr)?;
889 for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
890 let offset = types.canonical_abi(ty).next_field32_size(&mut ptr);
891 val.store(&mut cx, *ty, offset)?;
892 }
893 }
894
895 unsafe {
896 flags.set_may_leave(true);
897 }
898
899 cx.exit_call()?;
900 }
901
902 Ok(())
903}
904
905unsafe fn dynamic_params_load(
914 cx: &mut LiftContext<'_>,
915 types: &ComponentTypes,
916 storage: &[MaybeUninit<ValRaw>],
917 param_tys: &TypeTuple,
918 params: &mut Vec<Val>,
919 max_flat_params: usize,
920) -> Result<usize> {
921 if let Some(param_count) = param_tys.abi.flat_count(max_flat_params) {
922 let storage =
924 unsafe { mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(&storage[..param_count]) };
925 let mut iter = storage.iter();
926 for ty in param_tys.types.iter() {
927 params.push(Val::lift(cx, *ty, &mut iter)?);
928 }
929 assert!(iter.next().is_none());
930 Ok(param_count)
931 } else {
932 let mut offset = validate_inbounds_dynamic(¶m_tys.abi, cx.memory(), unsafe {
933 storage[0].assume_init_ref()
934 })?;
935 for ty in param_tys.types.iter() {
936 let abi = types.canonical_abi(ty);
937 let size = usize::try_from(abi.size32).unwrap();
938 let memory = &cx.memory()[abi.next_field32_size(&mut offset)..][..size];
939 params.push(Val::load(cx, *ty, memory)?);
940 }
941 Ok(1)
942 }
943}
944
945pub(crate) fn validate_inbounds_dynamic(
946 abi: &CanonicalAbiInfo,
947 memory: &[u8],
948 ptr: &ValRaw,
949) -> Result<usize> {
950 let ptr = usize::try_from(ptr.get_u32())?;
952 if ptr % usize::try_from(abi.align32)? != 0 {
953 bail!("pointer not aligned");
954 }
955 let end = match ptr.checked_add(usize::try_from(abi.size32).unwrap()) {
956 Some(n) => n,
957 None => bail!("pointer size overflow"),
958 };
959 if end > memory.len() {
960 bail!("pointer out of bounds")
961 }
962 Ok(ptr)
963}
964
965extern "C" fn dynamic_entrypoint<T, F, S>(
966 cx: NonNull<VMOpaqueContext>,
967 data: NonNull<u8>,
968 ty: u32,
969 options: u32,
970 storage: NonNull<MaybeUninit<ValRaw>>,
971 storage_len: usize,
972) -> bool
973where
974 F: Fn(
975 StoreContextMut<'_, T>,
976 ComponentFunc,
977 Vec<Val>,
978 usize,
979 ) -> Pin<Box<dyn Future<Output = Result<Vec<Val>>> + Send + 'static>>
980 + Send
981 + Sync
982 + 'static,
983 T: 'static,
984 S: FunctionStyle,
985{
986 let data = SendSyncPtr::new(NonNull::new(data.as_ptr() as *mut F).unwrap());
987 unsafe {
988 call_host_and_handle_result(cx, |store, instance| {
989 call_host_dynamic::<T, _, S>(
990 store,
991 instance,
992 TypeFuncIndex::from_u32(ty),
993 OptionsIndex::from_u32(options),
994 NonNull::slice_from_raw_parts(storage, storage_len).as_mut(),
995 &*data.as_ptr(),
996 )
997 })
998 }
999}