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