1 use crate::component::concurrent;
2 #[cfg(feature = "component-model-async")]
3 use crate::component::concurrent::{Accessor, Status};
4 use crate::component::func::{LiftContext, LowerContext};
5 use crate::component::matching::InstanceType;
6 use crate::component::storage::slice_to_storage_mut;
7 use crate::component::types::ComponentFunc;
8 use crate::component::{ComponentNamedList, ComponentType, Instance, Lift, Lower, Val};
9 use crate::prelude::*;
10 use crate::runtime::vm::component::{
11     ComponentInstance, VMComponentContext, VMLowering, VMLoweringCallee,
12 };
13 use crate::runtime::vm::{SendSyncPtr, VMOpaqueContext, VMStore};
14 use crate::{AsContextMut, CallHook, StoreContextMut, ValRaw};
15 use alloc::sync::Arc;
16 use core::any::Any;
17 use core::future::Future;
18 use core::mem::{self, MaybeUninit};
19 use core::pin::Pin;
20 use core::ptr::NonNull;
21 use wasmtime_environ::component::{
22     CanonicalAbiInfo, ComponentTypes, InterfaceType, MAX_FLAT_ASYNC_PARAMS, MAX_FLAT_PARAMS,
23     MAX_FLAT_RESULTS, OptionsIndex, TypeFuncIndex, TypeTuple,
24 };
25 
26 pub struct HostFunc {
27     entrypoint: VMLoweringCallee,
28     typecheck: Box<dyn (Fn(TypeFuncIndex, &InstanceType<'_>) -> Result<()>) + Send + Sync>,
29     func: Box<dyn Any + Send + Sync>,
30 }
31 
32 impl 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 
38 enum HostResult<T> {
39     Done(Result<T>),
40     #[cfg(feature = "component-model-async")]
41     Future(Pin<Box<dyn Future<Output = Result<T>> + Send>>),
42 }
43 
44 impl 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             // This function performs dynamic type checks and subsequently does
137             // not need to perform up-front type checks. Instead everything is
138             // dynamically managed at runtime.
139             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 
202 fn typecheck<P, R>(ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()>
203 where
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 
214 /// The "meat" of calling a host function from wasm.
215 ///
216 /// This function is delegated to from implementations of
217 /// `HostFunc::from_closure`. Most of the arguments from the `entrypoint` are
218 /// forwarded here except for the `data` pointer which is encapsulated in the
219 /// `closure` argument here.
220 ///
221 /// This function is parameterized over:
222 ///
223 /// * `T` - the type of store this function works with (an unsafe assertion)
224 /// * `Params` - the parameters to the host function, viewed as a tuple
225 /// * `Return` - the result of the host function
226 /// * `F` - the `closure` to actually receive the `Params` and return the
227 ///   `Return`
228 ///
229 /// It's expected that `F` will "un-tuple" the arguments to pass to a host
230 /// closure.
231 ///
232 /// This function is in general `unsafe` as the validity of all the parameters
233 /// must be upheld. Generally that's done by ensuring this is only called from
234 /// the select few places it's intended to be called from.
235 unsafe 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<()>
243 where
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     // Perform a dynamic check that this instance can indeed be left. Exiting
257     // the component is disallowed, for example, when the `realloc` function
258     // calls a canonical import.
259     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             // Lift the parameters, either from flat storage or from linear
274             // memory.
275             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             // Load the return pointer, if present.
280             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                 // If there's no return pointer then `Return` should have an
286                 // empty flat representation. In this situation pretend the
287                 // return pointer was 0 so we have something to shepherd along
288                 // into the closure below.
289                 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     /// Type-level representation of the matrix of possibilities of how
370     /// WebAssembly parameters and results are handled in the canonical ABI.
371     ///
372     /// Wasmtime's ABI here always works with `&mut [MaybeUninit<ValRaw>]` as the
373     /// base representation of params/results. Parameters are passed
374     /// sequentially and results are returned by overwriting the parameters.
375     /// That means both params/results start from index 0.
376     ///
377     /// The type-level representation here involves working with the typed
378     /// `P::Lower` and `R::Lower` values which is a type-level representation of
379     /// a lowered value. All lowered values are in essence a sequence of
380     /// `ValRaw` values one after the other to fit within this original array
381     /// that is the basis of Wasmtime's ABI.
382     ///
383     /// The various combinations here are cryptic, but only used in this file.
384     /// This in theory cuts down on the verbosity below, but an explanation of
385     /// the various acronyms here are:
386     ///
387     /// * Pd - params direct - means that parameters are passed directly in
388     ///   their flat representation via `P::Lower`.
389     ///
390     /// * Pi - params indirect - means that parameters are passed indirectly in
391     ///   linear memory and the argument here is `ValRaw` to store the pointer.
392     ///
393     /// * Rd - results direct - means that results are returned directly in
394     ///   their flat representation via `R::Lower`. Note that this is always
395     ///   represented as `MaybeUninit<R::Lower>` as well because the return
396     ///   values may point to uninitialized memory if there were no parameters
397     ///   for example.
398     ///
399     /// * Ri - results indirect - means that results are returned indirectly in
400     ///   linear memory through the pointer specified. Note that this is
401     ///   specified as a `ValRaw` to represent the argument that's being given
402     ///   to the host from WebAssembly.
403     ///
404     /// * Ar - async results - means that the parameters to this call
405     ///   additionally include an async result pointer. Async results are always
406     ///   transmitted via a pointer so this is always a `ValRaw`.
407     ///
408     /// Internally this type makes liberal use of `Union` and `Pair` helpers
409     /// below which are simple `#[repr(C)]` wrappers around a pair of types that
410     /// are a union or a pair.
411     ///
412     /// Note that for any combination of `P` and `R` this `enum` is actually
413     /// pointless as a single variant will be used. In theory we should be able
414     /// to monomorphize based on `P` and `R` to a specific type. This
415     /// monomorphization depends on conditionals like `flatten_count() <= N`,
416     /// however, and I don't know how to encode that in Rust easily. In lieu of
417     /// that we assume LLVM will figure things out and boil away the actual enum
418     /// and runtime dispatch.
419     enum Storage<'a, P: ComponentType, R: ComponentType> {
420         /// Params: direct, Results: direct
421         ///
422         /// The lowered representation of params/results are overlaid on top of
423         /// each other.
424         PdRd(&'a mut Union<P::Lower, MaybeUninit<R::Lower>>),
425 
426         /// Params: direct, Results: indirect
427         ///
428         /// The return pointer comes after the params so this is sequentially
429         /// laid out with one after the other.
430         PdRi(&'a Pair<P::Lower, ValRaw>),
431 
432         /// Params: indirect, Results: direct
433         ///
434         /// Here the return values are overlaid on top of the pointer parameter.
435         PiRd(&'a mut Union<ValRaw, MaybeUninit<R::Lower>>),
436 
437         /// Params: indirect, Results: indirect
438         ///
439         /// Here the two parameters are laid out sequentially one after the
440         /// other.
441         PiRi(&'a Pair<ValRaw, ValRaw>),
442 
443         /// Params: direct + async result, Results: direct
444         ///
445         /// This is like `PdRd` except that the parameters additionally include
446         /// a pointer for where to store the result.
447         #[cfg(feature = "component-model-async")]
448         PdArRd(&'a mut Union<Pair<P::Lower, ValRaw>, MaybeUninit<R::Lower>>),
449 
450         /// Params: indirect + async result, Results: direct
451         ///
452         /// This is like `PiRd` except that the parameters additionally include
453         /// a pointer for where to store the result.
454         #[cfg(feature = "component-model-async")]
455         PiArRd(&'a mut Union<Pair<ValRaw, ValRaw>, MaybeUninit<R::Lower>>),
456     }
457 
458     // Helper structure used above in `Storage` to represent two consecutive
459     // values.
460     #[repr(C)]
461     #[derive(Copy, Clone)]
462     struct Pair<T, U> {
463         a: T,
464         b: U,
465     }
466 
467     // Helper structure used above in `Storage` to represent two values overlaid
468     // on each other.
469     #[repr(C)]
470     union Union<T: Copy, U: Copy> {
471         a: T,
472         b: U,
473     }
474 
475     /// Representation of where parameters are lifted from.
476     enum Src<'a, T> {
477         /// Parameters are directly lifted from `T`, which is under the hood a
478         /// sequence of `ValRaw`. This is `P::Lower` for example.
479         Direct(&'a T),
480 
481         /// Parameters are loaded from linear memory, and this is the wasm
482         /// parameter representing the pointer into linear memory to load from.
483         Indirect(&'a ValRaw),
484     }
485 
486     /// Dual of [`Src`], where to store results.
487     enum Dst<'a, T> {
488         /// Results are stored directly in this pointer.
489         ///
490         /// Note that this is a mutable pointer but it's specifically
491         /// `MaybeUninit` as trampolines do not initialize it. The `T` here will
492         /// be `R::Lower` for example.
493         Direct(&'a mut MaybeUninit<T>),
494 
495         /// Results are stored in linear memory, and this value is the wasm
496         /// parameter given which represents the pointer into linear memory.
497         ///
498         /// Note that this is not mutable as the parameter is not mutated, but
499         /// memory will be mutated.
500         Indirect(&'a ValRaw),
501     }
502 
503     impl<P, R> Storage<'_, P, R>
504     where
505         P: ComponentType + Lift,
506         R: ComponentType + Lower,
507     {
508         /// Classifies a new `Storage` suitable for use with sync functions.
509         ///
510         /// There's a 2x2 matrix of whether parameters and results are stored on the
511         /// stack or on the heap. Each of the 4 branches here have a different
512         /// representation of the storage of arguments/returns.
513         ///
514         /// Also note that while four branches are listed here only one is taken for
515         /// any particular `Params` and `Return` combination. This should be
516         /// trivially DCE'd by LLVM. Perhaps one day with enough const programming in
517         /// Rust we can make monomorphizations of this function codegen only one
518         /// branch, but today is not that day.
519         ///
520         /// # Safety
521         ///
522         /// Requires that the `storage` provided does indeed match an wasm
523         /// function with the signature of `P` and `R` as params/results.
524         unsafe fn new_sync(storage: &mut [MaybeUninit<ValRaw>]) -> Storage<'_, P, R> {
525             // SAFETY: this `unsafe` is due to the `slice_to_storage_*` helpers
526             // used which view the slice provided as a different type. This
527             // safety should be upheld by the contract of the `ComponentType`
528             // trait and its `Lower` type parameter meaning they're valid to
529             // view as a sequence of `ValRaw` types. Additionally the
530             // `ComponentType` trait ensures that the matching of the runtime
531             // length of `storage` should match the actual size of `P::Lower`
532             // and `R::Lower` or such as needed.
533             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                 // SAFETY: these `unsafe` blocks are due to accessing union
563                 // fields. The safety here relies on the contract of the
564                 // `ComponentType` trait which should ensure that the types
565                 // projected onto a list of wasm parameters are indeed correct.
566                 // That means that the projections here, if the types are
567                 // correct, all line up to initialized memory that's well-typed
568                 // to access.
569                 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                 // SAFETY: these unsafe blocks are due to accessing fields of a
598                 // `union` which is not safe in Rust. The returned value is
599                 // `MaybeUninit<R::Lower>` in all cases, however, which should
600                 // safely model how `union` memory is possibly uninitialized.
601                 // Additionally `R::Lower` has the `unsafe` contract that all
602                 // its bit patterns must be sound, which additionally should
603                 // help make this safe.
604                 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                 // SAFETY: like above these are `unsafe` due to accessing a
619                 // `union` field. This should be safe via the construction of
620                 // `Storage` which should correctly determine whether or not an
621                 // async return pointer is provided and classify the args/rets
622                 // appropriately.
623                 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         /// Classifies a new `Storage` suitable for use with async functions.
636         ///
637         /// # Safety
638         ///
639         /// Requires that the `storage` provided does indeed match an `async`
640         /// wasm function with the signature of `P` and `R` as params/results.
641         unsafe fn new_async<R>(storage: &mut [MaybeUninit<ValRaw>]) -> Storage<'_, P, u32>
642         where
643             R: ComponentType + Lower,
644         {
645             // SAFETY: see `Storage::new` for discussion on why this should be
646             // safe given the unsafe contract of the `ComponentType` trait.
647             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 
666 pub(crate) fn validate_inbounds<T: ComponentType>(memory: &[u8], ptr: &ValRaw) -> Result<usize> {
667     // FIXME(#4311): needs memory64 support
668     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 
682 unsafe fn call_host_and_handle_result<T>(
683     cx: NonNull<VMOpaqueContext>,
684     func: impl FnOnce(StoreContextMut<'_, T>, Instance) -> Result<()>,
685 ) -> bool
686 where
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 
701 unsafe 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<()>
709 where
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     // Perform a dynamic check that this instance can indeed be left. Exiting
730     // the component is disallowed, for example, when the `realloc` function
731     // calls a canonical import.
732     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 
856 /// Loads the parameters for a dynamic host function call into `params`
857 ///
858 /// Returns the number of flat `storage` values consumed.
859 ///
860 /// # Safety
861 ///
862 /// Requires that `param_tys` matches the type signature of the `storage` that
863 /// was passed in.
864 unsafe 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         // NB: can use `MaybeUninit::slice_assume_init_ref` when that's stable
874         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(&param_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 
896 pub(crate) fn validate_inbounds_dynamic(
897     abi: &CanonicalAbiInfo,
898     memory: &[u8],
899     ptr: &ValRaw,
900 ) -> Result<usize> {
901     // FIXME(#4311): needs memory64 support
902     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 
916 extern "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
924 where
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 }
950