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, Options};
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
44impl 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
202fn typecheck<P, R>(ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()>
203where
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.
235unsafe 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<()>
243where
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
667pub(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
683unsafe fn call_host_and_handle_result<T>(
684    cx: NonNull<VMOpaqueContext>,
685    func: impl FnOnce(StoreContextMut<'_, T>, Instance) -> Result<()>,
686) -> bool
687where
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
702unsafe 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<()>
710where
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.
867unsafe 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
899pub(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
919extern "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
927where
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}