wasmtime/runtime/component/func/
host.rs

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