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