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