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