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, Options};
5 use crate::component::matching::InstanceType;
6 use crate::component::storage::slice_to_storage_mut;
7 use crate::component::{ComponentNamedList, ComponentType, Instance, Lift, Lower, Val};
8 use crate::prelude::*;
9 use crate::runtime::vm::component::{
10     ComponentInstance, VMComponentContext, VMLowering, VMLoweringCallee,
11 };
12 use crate::runtime::vm::{SendSyncPtr, VMOpaqueContext, VMStore};
13 use crate::{AsContextMut, CallHook, StoreContextMut, ValRaw};
14 use alloc::sync::Arc;
15 use core::any::Any;
16 use core::future::Future;
17 use core::mem::{self, MaybeUninit};
18 use core::pin::Pin;
19 use core::ptr::NonNull;
20 use wasmtime_environ::component::{
21     CanonicalAbiInfo, ComponentTypes, InterfaceType, MAX_FLAT_ASYNC_PARAMS, MAX_FLAT_PARAMS,
22     MAX_FLAT_RESULTS, OptionsIndex, TypeFuncIndex, TypeTuple,
23 };
24 
25 pub struct HostFunc {
26     entrypoint: VMLoweringCallee,
27     typecheck: Box<dyn (Fn(TypeFuncIndex, &InstanceType<'_>) -> Result<()>) + Send + Sync>,
28     func: Box<dyn Any + Send + Sync>,
29 }
30 
31 impl core::fmt::Debug for HostFunc {
32     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
33         f.debug_struct("HostFunc").finish_non_exhaustive()
34     }
35 }
36 
37 enum HostResult<T> {
38     Done(Result<T>),
39     #[cfg(feature = "component-model-async")]
40     Future(Pin<Box<dyn Future<Output = Result<T>> + Send>>),
41 }
42 
43 impl HostFunc {
44     fn from_canonical<T, F, P, R>(func: F) -> Arc<HostFunc>
45     where
46         F: Fn(StoreContextMut<'_, T>, P) -> HostResult<R> + Send + Sync + 'static,
47         P: ComponentNamedList + Lift + 'static,
48         R: ComponentNamedList + Lower + 'static,
49         T: 'static,
50     {
51         let entrypoint = Self::entrypoint::<T, F, P, R>;
52         Arc::new(HostFunc {
53             entrypoint,
54             typecheck: Box::new(typecheck::<P, R>),
55             func: Box::new(func),
56         })
57     }
58 
59     pub(crate) fn from_closure<T, F, P, R>(func: F) -> Arc<HostFunc>
60     where
61         T: 'static,
62         F: Fn(StoreContextMut<T>, P) -> Result<R> + Send + Sync + 'static,
63         P: ComponentNamedList + Lift + 'static,
64         R: ComponentNamedList + Lower + 'static,
65     {
66         Self::from_canonical::<T, _, _, _>(move |store, params| {
67             HostResult::Done(func(store, params))
68         })
69     }
70 
71     #[cfg(feature = "component-model-async")]
72     pub(crate) fn from_concurrent<T, F, P, R>(func: F) -> Arc<HostFunc>
73     where
74         T: 'static,
75         F: Fn(&Accessor<T>, P) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>>
76             + Send
77             + Sync
78             + 'static,
79         P: ComponentNamedList + Lift + 'static,
80         R: ComponentNamedList + Lower + 'static,
81     {
82         let func = Arc::new(func);
83         Self::from_canonical::<T, _, _, _>(move |store, params| {
84             let func = func.clone();
85             HostResult::Future(Box::pin(
86                 store.wrap_call(move |accessor| func(accessor, params)),
87             ))
88         })
89     }
90 
91     extern "C" fn entrypoint<T, F, P, R>(
92         cx: NonNull<VMOpaqueContext>,
93         data: NonNull<u8>,
94         ty: u32,
95         options: u32,
96         storage: NonNull<MaybeUninit<ValRaw>>,
97         storage_len: usize,
98     ) -> bool
99     where
100         F: Fn(StoreContextMut<'_, T>, P) -> HostResult<R> + Send + Sync + 'static,
101         P: ComponentNamedList + Lift,
102         R: ComponentNamedList + Lower + 'static,
103         T: 'static,
104     {
105         let data = SendSyncPtr::new(NonNull::new(data.as_ptr() as *mut F).unwrap());
106         unsafe {
107             call_host_and_handle_result::<T>(cx, |store, instance| {
108                 call_host(
109                     store,
110                     instance,
111                     TypeFuncIndex::from_u32(ty),
112                     OptionsIndex::from_u32(options),
113                     NonNull::slice_from_raw_parts(storage, storage_len).as_mut(),
114                     move |store, args| (*data.as_ptr())(store, args),
115                 )
116             })
117         }
118     }
119 
120     fn new_dynamic_canonical<T, F>(func: F) -> Arc<HostFunc>
121     where
122         F: Fn(
123                 StoreContextMut<'_, T>,
124                 Vec<Val>,
125                 usize,
126             ) -> Pin<Box<dyn Future<Output = Result<Vec<Val>>> + Send + 'static>>
127             + Send
128             + Sync
129             + 'static,
130         T: 'static,
131     {
132         Arc::new(HostFunc {
133             entrypoint: dynamic_entrypoint::<T, F>,
134             // This function performs dynamic type checks and subsequently does
135             // not need to perform up-front type checks. Instead everything is
136             // dynamically managed at runtime.
137             typecheck: Box::new(move |_expected_index, _expected_types| Ok(())),
138             func: Box::new(func),
139         })
140     }
141 
142     pub(crate) fn new_dynamic<T: 'static, F>(func: F) -> Arc<HostFunc>
143     where
144         F: Fn(StoreContextMut<'_, T>, &[Val], &mut [Val]) -> Result<()> + Send + Sync + 'static,
145     {
146         Self::new_dynamic_canonical::<T, _>(move |store, mut params_and_results, result_start| {
147             let (params, results) = params_and_results.split_at_mut(result_start);
148             let result = func(store, params, results).map(move |()| params_and_results);
149             Box::pin(async move { result })
150         })
151     }
152 
153     #[cfg(feature = "component-model-async")]
154     pub(crate) fn new_dynamic_concurrent<T, F>(func: F) -> Arc<HostFunc>
155     where
156         T: 'static,
157         F: for<'a> Fn(
158                 &'a Accessor<T>,
159                 &'a [Val],
160                 &'a mut [Val],
161             ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
162             + Send
163             + Sync
164             + 'static,
165     {
166         let func = Arc::new(func);
167         Self::new_dynamic_canonical::<T, _>(move |store, mut params_and_results, result_start| {
168             let func = func.clone();
169             Box::pin(store.wrap_call(move |accessor| {
170                 Box::pin(async move {
171                     let (params, results) = params_and_results.split_at_mut(result_start);
172                     func(accessor, params, results).await?;
173                     Ok(params_and_results)
174                 })
175             }))
176         })
177     }
178 
179     pub fn typecheck(&self, ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()> {
180         (self.typecheck)(ty, types)
181     }
182 
183     pub fn lowering(&self) -> VMLowering {
184         let data = NonNull::from(&*self.func).cast();
185         VMLowering {
186             callee: NonNull::new(self.entrypoint as *mut _).unwrap().into(),
187             data: data.into(),
188         }
189     }
190 }
191 
192 fn typecheck<P, R>(ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()>
193 where
194     P: ComponentNamedList + Lift,
195     R: ComponentNamedList + Lower,
196 {
197     let ty = &types.types[ty];
198     P::typecheck(&InterfaceType::Tuple(ty.params), types)
199         .context("type mismatch with parameters")?;
200     R::typecheck(&InterfaceType::Tuple(ty.results), types).context("type mismatch with results")?;
201     Ok(())
202 }
203 
204 /// The "meat" of calling a host function from wasm.
205 ///
206 /// This function is delegated to from implementations of
207 /// `HostFunc::from_closure`. Most of the arguments from the `entrypoint` are
208 /// forwarded here except for the `data` pointer which is encapsulated in the
209 /// `closure` argument here.
210 ///
211 /// This function is parameterized over:
212 ///
213 /// * `T` - the type of store this function works with (an unsafe assertion)
214 /// * `Params` - the parameters to the host function, viewed as a tuple
215 /// * `Return` - the result of the host function
216 /// * `F` - the `closure` to actually receive the `Params` and return the
217 ///   `Return`
218 ///
219 /// It's expected that `F` will "un-tuple" the arguments to pass to a host
220 /// closure.
221 ///
222 /// This function is in general `unsafe` as the validity of all the parameters
223 /// must be upheld. Generally that's done by ensuring this is only called from
224 /// the select few places it's intended to be called from.
225 unsafe fn call_host<T, Params, Return, F>(
226     mut store: StoreContextMut<'_, T>,
227     instance: Instance,
228     ty: TypeFuncIndex,
229     options_idx: OptionsIndex,
230     storage: &mut [MaybeUninit<ValRaw>],
231     closure: F,
232 ) -> Result<()>
233 where
234     F: Fn(StoreContextMut<'_, T>, Params) -> HostResult<Return> + Send + Sync + 'static,
235     Params: Lift,
236     Return: Lower + 'static,
237 {
238     let options = Options::new_index(store.0, instance, options_idx);
239     let vminstance = instance.id().get(store.0);
240     let opts = &vminstance.component().env_component().options[options_idx];
241     let async_ = opts.async_;
242     let caller_instance = opts.instance;
243     let mut flags = vminstance.instance_flags(caller_instance);
244 
245     // Perform a dynamic check that this instance can indeed be left. Exiting
246     // the component is disallowed, for example, when the `realloc` function
247     // calls a canonical import.
248     if unsafe { !flags.may_leave() } {
249         return Err(anyhow!(crate::Trap::CannotLeaveComponent));
250     }
251 
252     let types = vminstance.component().types().clone();
253     let ty = &types[ty];
254     let param_tys = InterfaceType::Tuple(ty.params);
255     let result_tys = InterfaceType::Tuple(ty.results);
256 
257     if async_ {
258         #[cfg(feature = "component-model-async")]
259         {
260             let mut storage = unsafe { Storage::<'_, Params, u32>::new_async::<Return>(storage) };
261 
262             // Lift the parameters, either from flat storage or from linear
263             // memory.
264             let lift = &mut LiftContext::new(store.0.store_opaque_mut(), &options, instance);
265             lift.enter_call();
266             let params = storage.lift_params(lift, param_tys)?;
267 
268             // Load the return pointer, if present.
269             let retptr = match storage.async_retptr() {
270                 Some(ptr) => {
271                     let mut lower =
272                         LowerContext::new(store.as_context_mut(), &options, &types, instance);
273                     validate_inbounds::<Return>(lower.as_slice_mut(), ptr)?
274                 }
275                 // If there's no return pointer then `Return` should have an
276                 // empty flat representation. In this situation pretend the
277                 // return pointer was 0 so we have something to shepherd along
278                 // into the closure below.
279                 None => {
280                     assert_eq!(Return::flatten_count(), 0);
281                     0
282                 }
283             };
284 
285             let host_result = closure(store.as_context_mut(), params);
286 
287             let mut lower_result = {
288                 let types = types.clone();
289                 move |store: StoreContextMut<T>, ret: Return| {
290                     unsafe {
291                         flags.set_may_leave(false);
292                     }
293                     let mut lower = LowerContext::new(store, &options, &types, instance);
294                     ret.linear_lower_to_memory(&mut lower, result_tys, retptr)?;
295                     unsafe {
296                         flags.set_may_leave(true);
297                     }
298                     lower.exit_call()?;
299                     Ok(())
300                 }
301             };
302             let task = match host_result {
303                 HostResult::Done(result) => {
304                     lower_result(store.as_context_mut(), result?)?;
305                     None
306                 }
307                 #[cfg(feature = "component-model-async")]
308                 HostResult::Future(future) => instance.first_poll(
309                     store.as_context_mut(),
310                     future,
311                     caller_instance,
312                     lower_result,
313                 )?,
314             };
315 
316             let status = if let Some(task) = task {
317                 Status::Started.pack(Some(task))
318             } else {
319                 Status::Returned.pack(None)
320             };
321 
322             let mut lower = LowerContext::new(store, &options, &types, instance);
323             storage.lower_results(&mut lower, InterfaceType::U32, status)?;
324         }
325         #[cfg(not(feature = "component-model-async"))]
326         {
327             let _ = caller_instance;
328             unreachable!(
329                 "async-lowered imports should have failed validation \
330                  when `component-model-async` feature disabled"
331             );
332         }
333     } else {
334         let mut storage = unsafe { Storage::<'_, Params, Return>::new_sync(storage) };
335         let mut lift = LiftContext::new(store.0.store_opaque_mut(), &options, instance);
336         lift.enter_call();
337         let params = storage.lift_params(&mut lift, param_tys)?;
338 
339         let ret = match closure(store.as_context_mut(), params) {
340             HostResult::Done(result) => result?,
341             #[cfg(feature = "component-model-async")]
342             HostResult::Future(future) => {
343                 concurrent::poll_and_block(store.0, future, caller_instance)?
344             }
345         };
346 
347         unsafe {
348             flags.set_may_leave(false);
349         }
350         let mut lower = LowerContext::new(store, &options, &types, instance);
351         storage.lower_results(&mut lower, result_tys, ret)?;
352         unsafe {
353             flags.set_may_leave(true);
354         }
355         lower.exit_call()?;
356     }
357 
358     return Ok(());
359 
360     /// Type-level representation of the matrix of possibilities of how
361     /// WebAssembly parameters and results are handled in the canonical ABI.
362     ///
363     /// Wasmtime's ABI here always works with `&mut [MaybeUninit<ValRaw>]` as the
364     /// base representation of params/results. Parameters are passed
365     /// sequentially and results are returned by overwriting the parameters.
366     /// That means both params/results start from index 0.
367     ///
368     /// The type-level representation here involves working with the typed
369     /// `P::Lower` and `R::Lower` values which is a type-level representation of
370     /// a lowered value. All lowered values are in essence a sequence of
371     /// `ValRaw` values one after the other to fit within this original array
372     /// that is the basis of Wasmtime's ABI.
373     ///
374     /// The various combinations here are cryptic, but only used in this file.
375     /// This in theory cuts down on the verbosity below, but an explanation of
376     /// the various acronyms here are:
377     ///
378     /// * Pd - params direct - means that parameters are passed directly in
379     ///   their flat representation via `P::Lower`.
380     ///
381     /// * Pi - params indirect - means that parameters are passed indirectly in
382     ///   linear memory and the argument here is `ValRaw` to store the pointer.
383     ///
384     /// * Rd - results direct - means that results are returned directly in
385     ///   their flat representation via `R::Lower`. Note that this is always
386     ///   represented as `MaybeUninit<R::Lower>` as well because the return
387     ///   values may point to uninitialized memory if there were no parameters
388     ///   for example.
389     ///
390     /// * Ri - results indirect - means that results are returned indirectly in
391     ///   linear memory through the pointer specified. Note that this is
392     ///   specified as a `ValRaw` to represent the argument that's being given
393     ///   to the host from WebAssembly.
394     ///
395     /// * Ar - async results - means that the parameters to this call
396     ///   additionally include an async result pointer. Async results are always
397     ///   transmitted via a pointer so this is always a `ValRaw`.
398     ///
399     /// Internally this type makes liberal use of `Union` and `Pair` helpers
400     /// below which are simple `#[repr(C)]` wrappers around a pair of types that
401     /// are a union or a pair.
402     ///
403     /// Note that for any combination of `P` and `R` this `enum` is actually
404     /// pointless as a single variant will be used. In theory we should be able
405     /// to monomorphize based on `P` and `R` to a specific type. This
406     /// monomorphization depends on conditionals like `flatten_count() <= N`,
407     /// however, and I don't know how to encode that in Rust easily. In lieu of
408     /// that we assume LLVM will figure things out and boil away the actual enum
409     /// and runtime dispatch.
410     enum Storage<'a, P: ComponentType, R: ComponentType> {
411         /// Params: direct, Results: direct
412         ///
413         /// The lowered representation of params/results are overlaid on top of
414         /// each other.
415         PdRd(&'a mut Union<P::Lower, MaybeUninit<R::Lower>>),
416 
417         /// Params: direct, Results: indirect
418         ///
419         /// The return pointer comes after the params so this is sequentially
420         /// laid out with one after the other.
421         PdRi(&'a Pair<P::Lower, ValRaw>),
422 
423         /// Params: indirect, Results: direct
424         ///
425         /// Here the return values are overlaid on top of the pointer parameter.
426         PiRd(&'a mut Union<ValRaw, MaybeUninit<R::Lower>>),
427 
428         /// Params: indirect, Results: indirect
429         ///
430         /// Here the two parameters are laid out sequentially one after the
431         /// other.
432         PiRi(&'a Pair<ValRaw, ValRaw>),
433 
434         /// Params: direct + async result, Results: direct
435         ///
436         /// This is like `PdRd` except that the parameters additionally include
437         /// a pointer for where to store the result.
438         #[cfg(feature = "component-model-async")]
439         PdArRd(&'a mut Union<Pair<P::Lower, ValRaw>, MaybeUninit<R::Lower>>),
440 
441         /// Params: indirect + async result, Results: direct
442         ///
443         /// This is like `PiRd` except that the parameters additionally include
444         /// a pointer for where to store the result.
445         #[cfg(feature = "component-model-async")]
446         PiArRd(&'a mut Union<Pair<ValRaw, ValRaw>, MaybeUninit<R::Lower>>),
447     }
448 
449     // Helper structure used above in `Storage` to represent two consecutive
450     // values.
451     #[repr(C)]
452     #[derive(Copy, Clone)]
453     struct Pair<T, U> {
454         a: T,
455         b: U,
456     }
457 
458     // Helper structure used above in `Storage` to represent two values overlaid
459     // on each other.
460     #[repr(C)]
461     union Union<T: Copy, U: Copy> {
462         a: T,
463         b: U,
464     }
465 
466     /// Representation of where parameters are lifted from.
467     enum Src<'a, T> {
468         /// Parameters are directly lifted from `T`, which is under the hood a
469         /// sequence of `ValRaw`. This is `P::Lower` for example.
470         Direct(&'a T),
471 
472         /// Parameters are loaded from linear memory, and this is the wasm
473         /// parameter representing the pointer into linear memory to load from.
474         Indirect(&'a ValRaw),
475     }
476 
477     /// Dual of [`Src`], where to store results.
478     enum Dst<'a, T> {
479         /// Results are stored directly in this pointer.
480         ///
481         /// Note that this is a mutable pointer but it's specifically
482         /// `MaybeUninit` as trampolines do not initialize it. The `T` here will
483         /// be `R::Lower` for example.
484         Direct(&'a mut MaybeUninit<T>),
485 
486         /// Results are stored in linear memory, and this value is the wasm
487         /// parameter given which represents the pointer into linear memory.
488         ///
489         /// Note that this is not mutable as the parameter is not mutated, but
490         /// memory will be mutated.
491         Indirect(&'a ValRaw),
492     }
493 
494     impl<P, R> Storage<'_, P, R>
495     where
496         P: ComponentType + Lift,
497         R: ComponentType + Lower,
498     {
499         /// Classifies a new `Storage` suitable for use with sync functions.
500         ///
501         /// There's a 2x2 matrix of whether parameters and results are stored on the
502         /// stack or on the heap. Each of the 4 branches here have a different
503         /// representation of the storage of arguments/returns.
504         ///
505         /// Also note that while four branches are listed here only one is taken for
506         /// any particular `Params` and `Return` combination. This should be
507         /// trivially DCE'd by LLVM. Perhaps one day with enough const programming in
508         /// Rust we can make monomorphizations of this function codegen only one
509         /// branch, but today is not that day.
510         ///
511         /// # Safety
512         ///
513         /// Requires that the `storage` provided does indeed match an wasm
514         /// function with the signature of `P` and `R` as params/results.
515         unsafe fn new_sync(storage: &mut [MaybeUninit<ValRaw>]) -> Storage<'_, P, R> {
516             // SAFETY: this `unsafe` is due to the `slice_to_storage_*` helpers
517             // used which view the slice provided as a different type. This
518             // safety should be upheld by the contract of the `ComponentType`
519             // trait and its `Lower` type parameter meaning they're valid to
520             // view as a sequence of `ValRaw` types. Additionally the
521             // `ComponentType` trait ensures that the matching of the runtime
522             // length of `storage` should match the actual size of `P::Lower`
523             // and `R::Lower` or such as needed.
524             unsafe {
525                 if P::flatten_count() <= MAX_FLAT_PARAMS {
526                     if R::flatten_count() <= MAX_FLAT_RESULTS {
527                         Storage::PdRd(slice_to_storage_mut(storage).assume_init_mut())
528                     } else {
529                         Storage::PdRi(slice_to_storage_mut(storage).assume_init_ref())
530                     }
531                 } else {
532                     if R::flatten_count() <= MAX_FLAT_RESULTS {
533                         Storage::PiRd(slice_to_storage_mut(storage).assume_init_mut())
534                     } else {
535                         Storage::PiRi(slice_to_storage_mut(storage).assume_init_ref())
536                     }
537                 }
538             }
539         }
540 
541         fn lift_params(&self, cx: &mut LiftContext<'_>, ty: InterfaceType) -> Result<P> {
542             match self.lift_src() {
543                 Src::Direct(storage) => P::linear_lift_from_flat(cx, ty, storage),
544                 Src::Indirect(ptr) => {
545                     let ptr = validate_inbounds::<P>(cx.memory(), ptr)?;
546                     P::linear_lift_from_memory(cx, ty, &cx.memory()[ptr..][..P::SIZE32])
547                 }
548             }
549         }
550 
551         fn lift_src(&self) -> Src<'_, P::Lower> {
552             match self {
553                 // SAFETY: these `unsafe` blocks are due to accessing union
554                 // fields. The safety here relies on the contract of the
555                 // `ComponentType` trait which should ensure that the types
556                 // projected onto a list of wasm parameters are indeed correct.
557                 // That means that the projections here, if the types are
558                 // correct, all line up to initialized memory that's well-typed
559                 // to access.
560                 Storage::PdRd(storage) => unsafe { Src::Direct(&storage.a) },
561                 Storage::PdRi(storage) => Src::Direct(&storage.a),
562                 #[cfg(feature = "component-model-async")]
563                 Storage::PdArRd(storage) => unsafe { Src::Direct(&storage.a.a) },
564                 Storage::PiRd(storage) => unsafe { Src::Indirect(&storage.a) },
565                 Storage::PiRi(storage) => Src::Indirect(&storage.a),
566                 #[cfg(feature = "component-model-async")]
567                 Storage::PiArRd(storage) => unsafe { Src::Indirect(&storage.a.a) },
568             }
569         }
570 
571         fn lower_results<T>(
572             &mut self,
573             cx: &mut LowerContext<'_, T>,
574             ty: InterfaceType,
575             ret: R,
576         ) -> Result<()> {
577             match self.lower_dst() {
578                 Dst::Direct(storage) => ret.linear_lower_to_flat(cx, ty, storage),
579                 Dst::Indirect(ptr) => {
580                     let ptr = validate_inbounds::<R>(cx.as_slice_mut(), ptr)?;
581                     ret.linear_lower_to_memory(cx, ty, ptr)
582                 }
583             }
584         }
585 
586         fn lower_dst(&mut self) -> Dst<'_, R::Lower> {
587             match self {
588                 // SAFETY: these unsafe blocks are due to accessing fields of a
589                 // `union` which is not safe in Rust. The returned value is
590                 // `MaybeUninit<R::Lower>` in all cases, however, which should
591                 // safely model how `union` memory is possibly uninitialized.
592                 // Additionally `R::Lower` has the `unsafe` contract that all
593                 // its bit patterns must be sound, which additionally should
594                 // help make this safe.
595                 Storage::PdRd(storage) => unsafe { Dst::Direct(&mut storage.b) },
596                 Storage::PiRd(storage) => unsafe { Dst::Direct(&mut storage.b) },
597                 #[cfg(feature = "component-model-async")]
598                 Storage::PdArRd(storage) => unsafe { Dst::Direct(&mut storage.b) },
599                 #[cfg(feature = "component-model-async")]
600                 Storage::PiArRd(storage) => unsafe { Dst::Direct(&mut storage.b) },
601                 Storage::PdRi(storage) => Dst::Indirect(&storage.b),
602                 Storage::PiRi(storage) => Dst::Indirect(&storage.b),
603             }
604         }
605 
606         #[cfg(feature = "component-model-async")]
607         fn async_retptr(&self) -> Option<&ValRaw> {
608             match self {
609                 // SAFETY: like above these are `unsafe` due to accessing a
610                 // `union` field. This should be safe via the construction of
611                 // `Storage` which should correctly determine whether or not an
612                 // async return pointer is provided and classify the args/rets
613                 // appropriately.
614                 Storage::PdArRd(storage) => unsafe { Some(&storage.a.b) },
615                 Storage::PiArRd(storage) => unsafe { Some(&storage.a.b) },
616                 Storage::PdRd(_) | Storage::PiRd(_) | Storage::PdRi(_) | Storage::PiRi(_) => None,
617             }
618         }
619     }
620 
621     #[cfg(feature = "component-model-async")]
622     impl<P> Storage<'_, P, u32>
623     where
624         P: ComponentType + Lift,
625     {
626         /// Classifies a new `Storage` suitable for use with async functions.
627         ///
628         /// # Safety
629         ///
630         /// Requires that the `storage` provided does indeed match an `async`
631         /// wasm function with the signature of `P` and `R` as params/results.
632         unsafe fn new_async<R>(storage: &mut [MaybeUninit<ValRaw>]) -> Storage<'_, P, u32>
633         where
634             R: ComponentType + Lower,
635         {
636             // SAFETY: see `Storage::new` for discussion on why this should be
637             // safe given the unsafe contract of the `ComponentType` trait.
638             unsafe {
639                 if P::flatten_count() <= wasmtime_environ::component::MAX_FLAT_ASYNC_PARAMS {
640                     if R::flatten_count() == 0 {
641                         Storage::PdRd(slice_to_storage_mut(storage).assume_init_mut())
642                     } else {
643                         Storage::PdArRd(slice_to_storage_mut(storage).assume_init_mut())
644                     }
645                 } else {
646                     if R::flatten_count() == 0 {
647                         Storage::PiRd(slice_to_storage_mut(storage).assume_init_mut())
648                     } else {
649                         Storage::PiArRd(slice_to_storage_mut(storage).assume_init_mut())
650                     }
651                 }
652             }
653         }
654     }
655 }
656 
657 pub(crate) fn validate_inbounds<T: ComponentType>(memory: &[u8], ptr: &ValRaw) -> Result<usize> {
658     // FIXME(#4311): needs memory64 support
659     let ptr = usize::try_from(ptr.get_u32())?;
660     if ptr % usize::try_from(T::ALIGN32)? != 0 {
661         bail!("pointer not aligned");
662     }
663     let end = match ptr.checked_add(T::SIZE32) {
664         Some(n) => n,
665         None => bail!("pointer size overflow"),
666     };
667     if end > memory.len() {
668         bail!("pointer out of bounds")
669     }
670     Ok(ptr)
671 }
672 
673 unsafe fn call_host_and_handle_result<T>(
674     cx: NonNull<VMOpaqueContext>,
675     func: impl FnOnce(StoreContextMut<'_, T>, Instance) -> Result<()>,
676 ) -> bool
677 where
678     T: 'static,
679 {
680     let cx = unsafe { VMComponentContext::from_opaque(cx) };
681     unsafe {
682         ComponentInstance::enter_host_from_wasm(cx, |store, instance| {
683             let mut store = store.unchecked_context_mut();
684             store.0.call_hook(CallHook::CallingHost)?;
685             let res = func(store.as_context_mut(), instance);
686             store.0.call_hook(CallHook::ReturningFromHost)?;
687             res
688         })
689     }
690 }
691 
692 unsafe fn call_host_dynamic<T, F>(
693     mut store: StoreContextMut<'_, T>,
694     instance: Instance,
695     ty: TypeFuncIndex,
696     options_idx: OptionsIndex,
697     storage: &mut [MaybeUninit<ValRaw>],
698     closure: F,
699 ) -> Result<()>
700 where
701     F: Fn(
702             StoreContextMut<'_, T>,
703             Vec<Val>,
704             usize,
705         ) -> Pin<Box<dyn Future<Output = Result<Vec<Val>>> + Send + 'static>>
706         + Send
707         + Sync
708         + 'static,
709     T: 'static,
710 {
711     let options = Options::new_index(store.0, instance, options_idx);
712     let vminstance = instance.id().get(store.0);
713     let opts = &vminstance.component().env_component().options[options_idx];
714     let async_ = opts.async_;
715     let caller_instance = opts.instance;
716     let mut flags = vminstance.instance_flags(caller_instance);
717 
718     // Perform a dynamic check that this instance can indeed be left. Exiting
719     // the component is disallowed, for example, when the `realloc` function
720     // calls a canonical import.
721     if unsafe { !flags.may_leave() } {
722         return Err(anyhow!(crate::Trap::CannotLeaveComponent));
723     }
724 
725     let types = instance.id().get(store.0).component().types().clone();
726     let func_ty = &types[ty];
727     let param_tys = &types[func_ty.params];
728     let result_tys = &types[func_ty.results];
729 
730     let mut params_and_results = Vec::new();
731     let mut lift = &mut LiftContext::new(store.0.store_opaque_mut(), &options, instance);
732     lift.enter_call();
733     let max_flat = if async_ {
734         MAX_FLAT_ASYNC_PARAMS
735     } else {
736         MAX_FLAT_PARAMS
737     };
738 
739     let ret_index = unsafe {
740         dynamic_params_load(
741             &mut lift,
742             &types,
743             storage,
744             param_tys,
745             &mut params_and_results,
746             max_flat,
747         )?
748     };
749     let result_start = params_and_results.len();
750     for _ in 0..result_tys.types.len() {
751         params_and_results.push(Val::Bool(false));
752     }
753 
754     if async_ {
755         #[cfg(feature = "component-model-async")]
756         {
757             let retptr = if result_tys.types.len() == 0 {
758                 0
759             } else {
760                 let retptr = unsafe { storage[ret_index].assume_init() };
761                 let mut lower =
762                     LowerContext::new(store.as_context_mut(), &options, &types, instance);
763                 validate_inbounds_dynamic(&result_tys.abi, lower.as_slice_mut(), &retptr)?
764             };
765 
766             let future = closure(store.as_context_mut(), params_and_results, result_start);
767 
768             let task = instance.first_poll(store, future, caller_instance, {
769                 let types = types.clone();
770                 let result_tys = func_ty.results;
771                 move |store: StoreContextMut<T>, result_vals: Vec<Val>| {
772                     let result_tys = &types[result_tys];
773                     let result_vals = &result_vals[result_start..];
774                     assert_eq!(result_vals.len(), result_tys.types.len());
775 
776                     unsafe {
777                         flags.set_may_leave(false);
778                     }
779 
780                     let mut lower = LowerContext::new(store, &options, &types, instance);
781                     let mut ptr = retptr;
782                     for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
783                         let offset = types.canonical_abi(ty).next_field32_size(&mut ptr);
784                         val.store(&mut lower, *ty, offset)?;
785                     }
786 
787                     unsafe {
788                         flags.set_may_leave(true);
789                     }
790 
791                     lower.exit_call()?;
792 
793                     Ok(())
794                 }
795             })?;
796 
797             let status = if let Some(task) = task {
798                 Status::Started.pack(Some(task))
799             } else {
800                 Status::Returned.pack(None)
801             };
802 
803             storage[0] = MaybeUninit::new(ValRaw::i32(status as i32));
804         }
805         #[cfg(not(feature = "component-model-async"))]
806         {
807             unreachable!(
808                 "async-lowered imports should have failed validation \
809                  when `component-model-async` feature disabled"
810             );
811         }
812     } else {
813         let future = closure(store.as_context_mut(), params_and_results, result_start);
814         let result_vals = concurrent::poll_and_block(store.0, future, caller_instance)?;
815         let result_vals = &result_vals[result_start..];
816 
817         unsafe {
818             flags.set_may_leave(false);
819         }
820 
821         let mut cx = LowerContext::new(store, &options, &types, instance);
822         if let Some(cnt) = result_tys.abi.flat_count(MAX_FLAT_RESULTS) {
823             let mut dst = storage[..cnt].iter_mut();
824             for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
825                 val.lower(&mut cx, *ty, &mut dst)?;
826             }
827             assert!(dst.next().is_none());
828         } else {
829             let ret_ptr = unsafe { storage[ret_index].assume_init_ref() };
830             let mut ptr = validate_inbounds_dynamic(&result_tys.abi, cx.as_slice_mut(), ret_ptr)?;
831             for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
832                 let offset = types.canonical_abi(ty).next_field32_size(&mut ptr);
833                 val.store(&mut cx, *ty, offset)?;
834             }
835         }
836 
837         unsafe {
838             flags.set_may_leave(true);
839         }
840 
841         cx.exit_call()?;
842     }
843 
844     Ok(())
845 }
846 
847 /// Loads the parameters for a dynamic host function call into `params`
848 ///
849 /// Returns the number of flat `storage` values consumed.
850 ///
851 /// # Safety
852 ///
853 /// Requires that `param_tys` matches the type signature of the `storage` that
854 /// was passed in.
855 unsafe fn dynamic_params_load(
856     cx: &mut LiftContext<'_>,
857     types: &ComponentTypes,
858     storage: &[MaybeUninit<ValRaw>],
859     param_tys: &TypeTuple,
860     params: &mut Vec<Val>,
861     max_flat_params: usize,
862 ) -> Result<usize> {
863     if let Some(param_count) = param_tys.abi.flat_count(max_flat_params) {
864         // NB: can use `MaybeUninit::slice_assume_init_ref` when that's stable
865         let storage =
866             unsafe { mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(&storage[..param_count]) };
867         let mut iter = storage.iter();
868         for ty in param_tys.types.iter() {
869             params.push(Val::lift(cx, *ty, &mut iter)?);
870         }
871         assert!(iter.next().is_none());
872         Ok(param_count)
873     } else {
874         let mut offset = validate_inbounds_dynamic(&param_tys.abi, cx.memory(), unsafe {
875             storage[0].assume_init_ref()
876         })?;
877         for ty in param_tys.types.iter() {
878             let abi = types.canonical_abi(ty);
879             let size = usize::try_from(abi.size32).unwrap();
880             let memory = &cx.memory()[abi.next_field32_size(&mut offset)..][..size];
881             params.push(Val::load(cx, *ty, memory)?);
882         }
883         Ok(1)
884     }
885 }
886 
887 pub(crate) fn validate_inbounds_dynamic(
888     abi: &CanonicalAbiInfo,
889     memory: &[u8],
890     ptr: &ValRaw,
891 ) -> Result<usize> {
892     // FIXME(#4311): needs memory64 support
893     let ptr = usize::try_from(ptr.get_u32())?;
894     if ptr % usize::try_from(abi.align32)? != 0 {
895         bail!("pointer not aligned");
896     }
897     let end = match ptr.checked_add(usize::try_from(abi.size32).unwrap()) {
898         Some(n) => n,
899         None => bail!("pointer size overflow"),
900     };
901     if end > memory.len() {
902         bail!("pointer out of bounds")
903     }
904     Ok(ptr)
905 }
906 
907 extern "C" fn dynamic_entrypoint<T, F>(
908     cx: NonNull<VMOpaqueContext>,
909     data: NonNull<u8>,
910     ty: u32,
911     options: u32,
912     storage: NonNull<MaybeUninit<ValRaw>>,
913     storage_len: usize,
914 ) -> bool
915 where
916     F: Fn(
917             StoreContextMut<'_, T>,
918             Vec<Val>,
919             usize,
920         ) -> Pin<Box<dyn Future<Output = Result<Vec<Val>>> + Send + 'static>>
921         + Send
922         + Sync
923         + 'static,
924     T: 'static,
925 {
926     let data = SendSyncPtr::new(NonNull::new(data.as_ptr() as *mut F).unwrap());
927     unsafe {
928         call_host_and_handle_result(cx, |store, instance| {
929             call_host_dynamic::<T, _>(
930                 store,
931                 instance,
932                 TypeFuncIndex::from_u32(ty),
933                 OptionsIndex::from_u32(options),
934                 NonNull::slice_from_raw_parts(storage, storage_len).as_mut(),
935                 move |store, params, results| (*data.as_ptr())(store, params, results),
936             )
937         })
938     }
939 }
940