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