wasmtime/runtime/component/func/
host.rs

1//! Implementation of calling Rust-defined functions from components.
2
3#[cfg(feature = "component-model-async")]
4use crate::component::RuntimeInstance;
5#[cfg(feature = "component-model-async")]
6use crate::component::concurrent;
7#[cfg(feature = "component-model-async")]
8use crate::component::concurrent::{Accessor, Status};
9use crate::component::func::{LiftContext, LowerContext};
10use crate::component::matching::InstanceType;
11use crate::component::storage::{slice_to_storage, slice_to_storage_mut};
12use crate::component::types::ComponentFunc;
13use crate::component::{ComponentNamedList, Instance, Lift, Lower, Val};
14use crate::prelude::*;
15use crate::runtime::vm::component::{
16    ComponentInstance, VMComponentContext, VMLowering, VMLoweringCallee,
17};
18use crate::runtime::vm::{VMOpaqueContext, VMStore};
19use crate::store::Asyncness;
20use crate::{AsContextMut, CallHook, StoreContextMut, ValRaw};
21use alloc::sync::Arc;
22use core::any::Any;
23use core::mem::{self, MaybeUninit};
24#[cfg(feature = "async")]
25use core::pin::Pin;
26use core::ptr::NonNull;
27use wasmtime_environ::component::{
28    CanonicalAbiInfo, InterfaceType, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, OptionsIndex, TypeFuncIndex,
29};
30
31/// A host function suitable for passing into a component.
32///
33/// This structure represents a monomorphic host function that can only be used
34/// in the specific context of a particular store. This is generally not too
35/// too safe to use and is only meant for internal use.
36pub struct HostFunc {
37    /// The raw function pointer which Cranelift will invoke.
38    entrypoint: VMLoweringCallee,
39
40    /// The implementation of type-checking to ensure that this function
41    /// ascribes to the provided function type.
42    ///
43    /// This is used, for example, when a component imports a host function and
44    /// this will determine if the host function can be imported with the given
45    /// type.
46    typecheck: fn(TypeFuncIndex, &InstanceType<'_>) -> Result<()>,
47
48    /// The actual host function.
49    ///
50    /// This is frequently an empty allocation in the sense that the underlying
51    /// type is a zero-sized-type. Host functions are allowed, though, to close
52    /// over the environment as well.
53    func: Box<dyn Any + Send + Sync>,
54
55    /// Whether or not this host function was defined in such a way that async
56    /// stack switching is required when calling it.
57    asyncness: Asyncness,
58}
59
60impl core::fmt::Debug for HostFunc {
61    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
62        f.debug_struct("HostFunc").finish_non_exhaustive()
63    }
64}
65
66enum HostResult<T> {
67    Done(Result<T>),
68    #[cfg(feature = "component-model-async")]
69    Future(Pin<Box<dyn Future<Output = Result<T>> + Send>>),
70}
71
72impl HostFunc {
73    /// Creates a new host function based on the implementation of `func`.
74    ///
75    /// The `asyncness` parameter indicates whether the `func` requires
76    /// wasm to be on a fiber. This is used to propagate to the `Store` during
77    /// instantiation to ensure that this guarantee is met.
78    ///
79    /// Note that if `asyncness` is mistaken then that'll result in panics
80    /// in Wasmtime, but not memory unsafety.
81    fn new<T, F, P, R>(asyncness: Asyncness, func: F) -> Arc<HostFunc>
82    where
83        T: 'static,
84        R: Send + Sync + 'static,
85        F: HostFn<T, P, R> + Send + Sync + 'static,
86    {
87        Arc::new(HostFunc {
88            entrypoint: F::cabi_entrypoint,
89            typecheck: F::typecheck,
90            func: Box::new(func),
91            asyncness,
92        })
93    }
94
95    /// Equivalent for `Linker::func_wrap`
96    pub(crate) fn func_wrap<T, F, P, R>(func: F) -> Arc<HostFunc>
97    where
98        T: 'static,
99        F: Fn(StoreContextMut<T>, P) -> Result<R> + Send + Sync + 'static,
100        P: ComponentNamedList + Lift + 'static,
101        R: ComponentNamedList + Lower + 'static,
102    {
103        Self::new(
104            Asyncness::No,
105            StaticHostFn::<_, false>::new(move |store, params| {
106                HostResult::Done(func(store, params))
107            }),
108        )
109    }
110
111    /// Equivalent for `Linker::func_wrap_async`
112    #[cfg(feature = "async")]
113    pub(crate) fn func_wrap_async<T, F, P, R>(func: F) -> Arc<HostFunc>
114    where
115        T: 'static,
116        F: Fn(StoreContextMut<'_, T>, P) -> Box<dyn Future<Output = Result<R>> + Send + '_>
117            + Send
118            + Sync
119            + 'static,
120        P: ComponentNamedList + Lift + 'static,
121        R: ComponentNamedList + Lower + 'static,
122    {
123        Self::new(
124            Asyncness::Yes,
125            StaticHostFn::<_, false>::new(move |store, params| {
126                HostResult::Done(
127                    store
128                        .block_on(|store| Pin::from(func(store, params)))
129                        .and_then(|r| r),
130                )
131            }),
132        )
133    }
134
135    /// Equivalent for `Linker::func_wrap_concurrent`
136    #[cfg(feature = "component-model-async")]
137    pub(crate) fn func_wrap_concurrent<T, F, P, R>(func: F) -> Arc<HostFunc>
138    where
139        T: 'static,
140        F: Fn(&Accessor<T>, P) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>>
141            + Send
142            + Sync
143            + 'static,
144        P: ComponentNamedList + Lift + 'static,
145        R: ComponentNamedList + Lower + 'static,
146    {
147        let func = Arc::new(func);
148        Self::new(
149            Asyncness::Yes,
150            StaticHostFn::<_, true>::new(move |store, params| {
151                let func = func.clone();
152                HostResult::Future(Box::pin(
153                    store.wrap_call(move |accessor| func(accessor, params)),
154                ))
155            }),
156        )
157    }
158
159    /// Equivalent of `Linker::func_new`
160    pub(crate) fn func_new<T, F>(func: F) -> Arc<HostFunc>
161    where
162        T: 'static,
163        F: Fn(StoreContextMut<'_, T>, ComponentFunc, &[Val], &mut [Val]) -> Result<()>
164            + Send
165            + Sync
166            + 'static,
167    {
168        Self::new(
169            Asyncness::No,
170            DynamicHostFn::<_, false>::new(
171                move |store, ty, mut params_and_results, result_start| {
172                    let (params, results) = params_and_results.split_at_mut(result_start);
173                    let result = func(store, ty, params, results).map(move |()| params_and_results);
174                    HostResult::Done(result)
175                },
176            ),
177        )
178    }
179
180    /// Equivalent of `Linker::func_new_async`
181    #[cfg(feature = "async")]
182    pub(crate) fn func_new_async<T, F>(func: F) -> Arc<HostFunc>
183    where
184        T: 'static,
185        F: for<'a> Fn(
186                StoreContextMut<'a, T>,
187                ComponentFunc,
188                &'a [Val],
189                &'a mut [Val],
190            ) -> Box<dyn Future<Output = Result<()>> + Send + 'a>
191            + Send
192            + Sync
193            + 'static,
194    {
195        Self::new(
196            Asyncness::Yes,
197            DynamicHostFn::<_, false>::new(
198                move |store, ty, mut params_and_results, result_start| {
199                    let (params, results) = params_and_results.split_at_mut(result_start);
200                    let result = store
201                        .with_blocking(|store, cx| {
202                            cx.block_on(Pin::from(func(store, ty, params, results)))
203                        })
204                        .and_then(|r| r);
205                    let result = result.map(move |()| params_and_results);
206                    HostResult::Done(result)
207                },
208            ),
209        )
210    }
211
212    /// Equivalent of `Linker::func_new_concurrent`
213    #[cfg(feature = "component-model-async")]
214    pub(crate) fn func_new_concurrent<T, F>(func: F) -> Arc<HostFunc>
215    where
216        T: 'static,
217        F: for<'a> Fn(
218                &'a Accessor<T>,
219                ComponentFunc,
220                &'a [Val],
221                &'a mut [Val],
222            ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
223            + Send
224            + Sync
225            + 'static,
226    {
227        let func = Arc::new(func);
228        Self::new(
229            Asyncness::Yes,
230            DynamicHostFn::<_, true>::new(
231                move |store, ty, mut params_and_results, result_start| {
232                    let func = func.clone();
233                    HostResult::Future(Box::pin(store.wrap_call(move |accessor| {
234                        Box::pin(async move {
235                            let (params, results) = params_and_results.split_at_mut(result_start);
236                            func(accessor, ty, params, results).await?;
237                            Ok(params_and_results)
238                        })
239                    })))
240                },
241            ),
242        )
243    }
244
245    pub fn typecheck(&self, ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()> {
246        (self.typecheck)(ty, types)
247    }
248
249    pub fn lowering(&self) -> VMLowering {
250        let data = NonNull::from(&*self.func).cast();
251        VMLowering {
252            callee: NonNull::new(self.entrypoint as *mut _).unwrap().into(),
253            data: data.into(),
254        }
255    }
256
257    pub fn asyncness(&self) -> Asyncness {
258        self.asyncness
259    }
260}
261
262/// Argument to [`HostFn::lift_params`]
263enum Source<'a> {
264    /// The parameters come from flat wasm arguments which are provided here.
265    Flat(&'a [ValRaw]),
266    /// The parameters come from linear memory at the provided offset, which is
267    /// already validated to be in-bounds.
268    Memory(usize),
269}
270
271/// Argument to [`HostFn::lower_result`]
272enum Destination<'a> {
273    /// The result is stored in flat parameters whose storage is provided here.
274    Flat(&'a mut [MaybeUninit<ValRaw>]),
275    /// The result is stored in linear memory at the provided offset, which is
276    /// already validated to be in-bounds.
277    Memory(usize),
278}
279
280/// Consolidation of functionality of invoking a host function.
281///
282/// This trait primarily serves as a deduplication of the "static" and
283/// "dynamic" host function paths where all default functions here are shared
284/// (source-wise at least) across the two styles of host functions.
285trait HostFn<T, P, R>
286where
287    T: 'static,
288    R: Send + Sync + 'static,
289{
290    /// Whether or not this is `async` function from the perspective of the
291    /// component model.
292    const ASYNC: bool;
293
294    /// Performs a type-check to ensure that this host function can be imported
295    /// with the provided signature that a component is using.
296    fn typecheck(ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()>;
297
298    /// Execute this host function.
299    fn run(&self, store: StoreContextMut<'_, T>, params: P) -> HostResult<R>;
300
301    /// Performs the lifting operation to convert arguments from the canonical
302    /// ABI in wasm memory/arguments into their Rust representation.
303    fn lift_params(cx: &mut LiftContext<'_>, ty: TypeFuncIndex, source: Source<'_>) -> Result<P>;
304
305    /// Performs the lowering operation to convert the result from its Rust
306    /// representation to the canonical ABI representation.
307    fn lower_result(
308        cx: &mut LowerContext<'_, T>,
309        ty: TypeFuncIndex,
310        result: R,
311        dst: Destination<'_>,
312    ) -> Result<()>;
313
314    /// Raw entrypoint invoked by Cranelift.
315    ///
316    /// # Safety
317    ///
318    /// This function is only safe when called from a trusted source which
319    /// upholds at least these invariants:
320    ///
321    /// * `cx` is a valid pointer which comes from calling wasm.
322    /// * `data` is a valid pointer to `Self`
323    /// * `ty` and `options` are valid within the context of `cx`
324    /// * `storage` and `storage_len` are valid pointers and correspond to
325    ///   correctly initialized wasm arguments/results according to the
326    ///   canonical ABI specified by `ty` and `options`.
327    ///
328    /// The code elsewhere in this trait is all downstream of this `unsafe`,
329    /// and upholding this `unsafe` invariant requires Cranelift, function
330    /// translation, the canonical ABI, and Wasmtime to all stay in sync.
331    /// Basically we can't statically rule out this `unsafe`, we just gotta
332    /// not have bugs.
333    unsafe extern "C" fn cabi_entrypoint(
334        cx: NonNull<VMOpaqueContext>,
335        data: NonNull<u8>,
336        ty: u32,
337        options: u32,
338        storage: NonNull<MaybeUninit<ValRaw>>,
339        storage_len: usize,
340    ) -> bool
341    where
342        Self: Sized,
343    {
344        let cx = unsafe { VMComponentContext::from_opaque(cx) };
345        unsafe {
346            ComponentInstance::enter_host_from_wasm(cx, |store, instance| {
347                let mut store = store.unchecked_context_mut();
348                let ty = TypeFuncIndex::from_u32(ty);
349                let options = OptionsIndex::from_u32(options);
350                let storage = NonNull::slice_from_raw_parts(storage, storage_len).as_mut();
351                let data = data.cast::<Self>().as_ref();
352
353                store.0.call_hook(CallHook::CallingHost)?;
354                let res = data.entrypoint(store.as_context_mut(), instance, ty, options, storage);
355                store.0.call_hook(CallHook::ReturningFromHost)?;
356
357                res
358            })
359        }
360    }
361
362    /// "Rust" entrypoint after panic-handling infrastructure is set up and raw
363    /// arguments are translated to Rust types.
364    fn entrypoint(
365        &self,
366        store: StoreContextMut<'_, T>,
367        instance: Instance,
368        ty: TypeFuncIndex,
369        options: OptionsIndex,
370        storage: &mut [MaybeUninit<ValRaw>],
371    ) -> Result<()> {
372        let vminstance = instance.id().get(store.0);
373        let opts = &vminstance.component().env_component().options[options];
374        let caller_instance = opts.instance;
375        let flags = vminstance.instance_flags(caller_instance);
376
377        // Perform a dynamic check that this instance can indeed be left.
378        // Exiting the component is disallowed, for example, when the `realloc`
379        // function calls a canonical import.
380        if unsafe { !flags.may_leave() } {
381            return Err(format_err!(crate::Trap::CannotLeaveComponent));
382        }
383
384        if opts.async_ {
385            #[cfg(feature = "component-model-async")]
386            return self.call_async_lower(store, instance, ty, options, storage);
387            #[cfg(not(feature = "component-model-async"))]
388            unreachable!(
389                "async-lowered imports should have failed validation \
390                 when `component-model-async` feature disabled"
391            );
392        } else {
393            self.call_sync_lower(store, instance, ty, options, storage)
394        }
395    }
396
397    /// Implementation of the "sync" ABI.
398    ///
399    /// This is the implementation of invoking a host function through the
400    /// synchronous ABI of the component model, or when a function doesn't have
401    /// the `async` option when lowered. Note that the host function itself
402    /// can still be async, in which case this will block here waiting for it
403    /// to finish.
404    fn call_sync_lower(
405        &self,
406        mut store: StoreContextMut<'_, T>,
407        instance: Instance,
408        ty: TypeFuncIndex,
409        options: OptionsIndex,
410        storage: &mut [MaybeUninit<ValRaw>],
411    ) -> Result<()> {
412        if Self::ASYNC {
413            // The caller has synchronously lowered an async function, meaning
414            // the caller can only call it from an async task (i.e. a task
415            // created via a call to an async export).  Otherwise, we'll trap.
416            store.0.check_blocking()?;
417        }
418
419        let mut lift = LiftContext::new(store.0.store_opaque_mut(), options, instance);
420        let (params, rest) = self.load_params(&mut lift, ty, MAX_FLAT_PARAMS, storage)?;
421        #[cfg(feature = "component-model-async")]
422        let caller_instance = lift.options().instance;
423
424        let ret = match self.run(store.as_context_mut(), params) {
425            HostResult::Done(result) => result?,
426            #[cfg(feature = "component-model-async")]
427            HostResult::Future(future) => concurrent::poll_and_block(
428                store.0,
429                future,
430                RuntimeInstance {
431                    instance: instance.id().instance(),
432                    index: caller_instance,
433                },
434            )?,
435        };
436
437        let mut lower = LowerContext::new(store, options, instance);
438        let fty = &lower.types[ty];
439        let result_tys = &lower.types[fty.results];
440        let dst = if let Some(cnt) = result_tys.abi.flat_count(MAX_FLAT_RESULTS) {
441            Destination::Flat(&mut storage[..cnt])
442        } else {
443            // SAFETY: due to the contract of `entrypoint` we know that the
444            // return pointer, located after the parameters, is initialized
445            // by wasm and safe to read.
446            let ptr = unsafe { rest[0].assume_init_ref() };
447            Destination::Memory(validate_inbounds_dynamic(
448                &result_tys.abi,
449                lower.as_slice_mut(),
450                ptr,
451            )?)
452        };
453        Self::lower_result_and_exit_call(&mut lower, ty, ret, dst)
454    }
455
456    /// Implementation of the "async" ABI of the component model.
457    ///
458    /// This is invoked when a component has the `async` options specified on
459    /// its `canon lower` for a host function. Note that the host function may
460    /// be either sync or async, and that's handled here too.
461    #[cfg(feature = "component-model-async")]
462    fn call_async_lower(
463        &self,
464        store: StoreContextMut<'_, T>,
465        instance: Instance,
466        ty: TypeFuncIndex,
467        options: OptionsIndex,
468        storage: &mut [MaybeUninit<ValRaw>],
469    ) -> Result<()> {
470        use wasmtime_environ::component::MAX_FLAT_ASYNC_PARAMS;
471
472        let (component, store) = instance.component_and_store_mut(store.0);
473        let mut store = StoreContextMut(store);
474        let types = component.types();
475        let fty = &types[ty];
476
477        // Lift the parameters, either from flat storage or from linear
478        // memory.
479        let mut lift = LiftContext::new(store.0.store_opaque_mut(), options, instance);
480        let caller_instance = lift.options().instance;
481        let (params, rest) = self.load_params(&mut lift, ty, MAX_FLAT_ASYNC_PARAMS, storage)?;
482
483        // Load/validate the return pointer, if present.
484        let retptr = if !lift.types[fty.results].types.is_empty() {
485            let mut lower = LowerContext::new(store.as_context_mut(), options, instance);
486            // SAFETY: see `load_params` below about how the return pointer
487            // should be safe to use.
488            let ptr = unsafe { rest[0].assume_init_ref() };
489            let result_tys = &lower.types[fty.results];
490            validate_inbounds_dynamic(&result_tys.abi, lower.as_slice_mut(), ptr)?
491        } else {
492            // If there's no return pointer then `R` should have an
493            // empty flat representation. In this situation pretend the return
494            // pointer was 0 so we have something to shepherd along into the
495            // closure below.
496            0
497        };
498
499        let host_result = self.run(store.as_context_mut(), params);
500
501        let task = match host_result {
502            HostResult::Done(result) => {
503                Self::lower_result_and_exit_call(
504                    &mut LowerContext::new(store, options, instance),
505                    ty,
506                    result?,
507                    Destination::Memory(retptr),
508                )?;
509                None
510            }
511            #[cfg(feature = "component-model-async")]
512            HostResult::Future(future) => {
513                instance.first_poll(store, future, caller_instance, move |store, ret| {
514                    Self::lower_result_and_exit_call(
515                        &mut LowerContext::new(store, options, instance),
516                        ty,
517                        ret,
518                        Destination::Memory(retptr),
519                    )
520                })?
521            }
522        };
523
524        storage[0].write(ValRaw::u32(if let Some(task) = task {
525            Status::Started.pack(Some(task))
526        } else {
527            Status::Returned.pack(None)
528        }));
529
530        Ok(())
531    }
532
533    /// Loads parameters the wasm arguments `storage`.
534    ///
535    /// This will internally decide the ABI source of the parameters and use
536    /// `storage` appropriately.
537    fn load_params<'a>(
538        &self,
539        lift: &mut LiftContext<'_>,
540        ty: TypeFuncIndex,
541        max_flat_params: usize,
542        storage: &'a [MaybeUninit<ValRaw>],
543    ) -> Result<(P, &'a [MaybeUninit<ValRaw>])> {
544        let fty = &lift.types[ty];
545        let param_tys = &lift.types[fty.params];
546        let param_flat_count = param_tys.abi.flat_count(max_flat_params);
547        lift.enter_call();
548        let src = match param_flat_count {
549            Some(cnt) => {
550                let params = &storage[..cnt];
551                // SAFETY: due to the contract of `entrypoint` we are
552                // guaranteed that all flat parameters are initialized by
553                // compiled wasm.
554                Source::Flat(unsafe { mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(params) })
555            }
556            None => {
557                // SAFETY: due to the contract of `entrypoint` we are
558                // guaranteed that the return pointer is initialized by
559                // compiled wasm.
560                let ptr = unsafe { storage[0].assume_init_ref() };
561                Source::Memory(validate_inbounds_dynamic(
562                    &param_tys.abi,
563                    lift.memory(),
564                    ptr,
565                )?)
566            }
567        };
568        let params = Self::lift_params(lift, ty, src)?;
569        Ok((params, &storage[param_flat_count.unwrap_or(1)..]))
570    }
571
572    /// Stores the result `ret` into `dst` which is calculated per the ABI.
573    fn lower_result_and_exit_call(
574        lower: &mut LowerContext<'_, T>,
575        ty: TypeFuncIndex,
576        ret: R,
577        dst: Destination<'_>,
578    ) -> Result<()> {
579        let caller_instance = lower.options().instance;
580        let mut flags = lower.instance_mut().instance_flags(caller_instance);
581        unsafe {
582            flags.set_may_leave(false);
583        }
584        Self::lower_result(lower, ty, ret, dst)?;
585        unsafe {
586            flags.set_may_leave(true);
587        }
588        lower.exit_call()?;
589        Ok(())
590    }
591}
592
593/// Implementation of a "static" host function where the parameters and results
594/// of a function are known at compile time.
595#[repr(transparent)]
596struct StaticHostFn<F, const ASYNC: bool>(F);
597
598impl<F, const ASYNC: bool> StaticHostFn<F, ASYNC> {
599    fn new<T, P, R>(func: F) -> Self
600    where
601        T: 'static,
602        P: ComponentNamedList + Lift + 'static,
603        R: ComponentNamedList + Lower + 'static,
604        F: Fn(StoreContextMut<'_, T>, P) -> HostResult<R>,
605    {
606        Self(func)
607    }
608}
609
610impl<T, F, P, R, const ASYNC: bool> HostFn<T, P, R> for StaticHostFn<F, ASYNC>
611where
612    T: 'static,
613    F: Fn(StoreContextMut<'_, T>, P) -> HostResult<R>,
614    P: ComponentNamedList + Lift + 'static,
615    R: ComponentNamedList + Lower + 'static,
616{
617    const ASYNC: bool = ASYNC;
618
619    fn typecheck(ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()> {
620        let ty = &types.types[ty];
621        if ASYNC != ty.async_ {
622            bail!("type mismatch with async");
623        }
624        P::typecheck(&InterfaceType::Tuple(ty.params), types)
625            .context("type mismatch with parameters")?;
626        R::typecheck(&InterfaceType::Tuple(ty.results), types)
627            .context("type mismatch with results")?;
628        Ok(())
629    }
630
631    fn run(&self, store: StoreContextMut<'_, T>, params: P) -> HostResult<R> {
632        (self.0)(store, params)
633    }
634
635    fn lift_params(cx: &mut LiftContext<'_>, ty: TypeFuncIndex, src: Source<'_>) -> Result<P> {
636        let ty = InterfaceType::Tuple(cx.types[ty].params);
637        match src {
638            Source::Flat(storage) => {
639                // SAFETY: the contract of `ComponentType` for `P` means that
640                // it's safe to interpret the parameters `storage` as
641                // `P::Lower`. The contract of `entrypoint` is that everything
642                // is initialized correctly internally.
643                let storage: &P::Lower = unsafe { slice_to_storage(storage) };
644                P::linear_lift_from_flat(cx, ty, storage)
645            }
646            Source::Memory(offset) => {
647                P::linear_lift_from_memory(cx, ty, &cx.memory()[offset..][..P::SIZE32])
648            }
649        }
650    }
651
652    fn lower_result(
653        cx: &mut LowerContext<'_, T>,
654        ty: TypeFuncIndex,
655        ret: R,
656        dst: Destination<'_>,
657    ) -> Result<()> {
658        let fty = &cx.types[ty];
659        let ty = InterfaceType::Tuple(fty.results);
660        match dst {
661            Destination::Flat(storage) => {
662                // SAFETY: the contract of `ComponentType` for `R` means that
663                // it's safe to reinterpret `ValRaw` storage to initialize as
664                // `R::Lower`.
665                let storage: &mut MaybeUninit<R::Lower> = unsafe { slice_to_storage_mut(storage) };
666                ret.linear_lower_to_flat(cx, ty, storage)
667            }
668            Destination::Memory(ptr) => ret.linear_lower_to_memory(cx, ty, ptr),
669        }
670    }
671}
672
673/// Implementation of a "dynamic" host function where the number of parameters,
674/// types of parameters, and result type/presence, are all not known at compile
675/// time.
676///
677/// This is intended for more-dynamic use cases than `StaticHostFn` above such
678/// as demos, gluing things together quickly, and `wast` testing.
679struct DynamicHostFn<F, const ASYNC: bool>(F);
680
681impl<F, const ASYNC: bool> DynamicHostFn<F, ASYNC> {
682    fn new<T>(func: F) -> Self
683    where
684        T: 'static,
685        F: Fn(StoreContextMut<'_, T>, ComponentFunc, Vec<Val>, usize) -> HostResult<Vec<Val>>,
686    {
687        Self(func)
688    }
689}
690
691impl<T, F, const ASYNC: bool> HostFn<T, (ComponentFunc, Vec<Val>), Vec<Val>>
692    for DynamicHostFn<F, ASYNC>
693where
694    T: 'static,
695    F: Fn(StoreContextMut<'_, T>, ComponentFunc, Vec<Val>, usize) -> HostResult<Vec<Val>>,
696{
697    const ASYNC: bool = ASYNC;
698
699    /// This function performs dynamic type checks on its parameters and
700    /// results and subsequently does not need to perform up-front type
701    /// checks. However, we _do_ verify async-ness here.
702    fn typecheck(ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()> {
703        let ty = &types.types[ty];
704        if ASYNC != ty.async_ {
705            bail!("type mismatch with async");
706        }
707
708        Ok(())
709    }
710
711    fn run(
712        &self,
713        store: StoreContextMut<'_, T>,
714        (ty, mut params): (ComponentFunc, Vec<Val>),
715    ) -> HostResult<Vec<Val>> {
716        let offset = params.len();
717        for _ in 0..ty.results().len() {
718            params.push(Val::Bool(false));
719        }
720        (self.0)(store, ty, params, offset)
721    }
722
723    fn lift_params(
724        cx: &mut LiftContext<'_>,
725        ty: TypeFuncIndex,
726        src: Source<'_>,
727    ) -> Result<(ComponentFunc, Vec<Val>)> {
728        let param_tys = &cx.types[cx.types[ty].params];
729        let mut params = Vec::new();
730        match src {
731            Source::Flat(storage) => {
732                let mut iter = storage.iter();
733                for ty in param_tys.types.iter() {
734                    params.push(Val::lift(cx, *ty, &mut iter)?);
735                }
736                assert!(iter.next().is_none());
737            }
738            Source::Memory(mut offset) => {
739                for ty in param_tys.types.iter() {
740                    let abi = cx.types.canonical_abi(ty);
741                    let size = usize::try_from(abi.size32).unwrap();
742                    let memory = &cx.memory()[abi.next_field32_size(&mut offset)..][..size];
743                    params.push(Val::load(cx, *ty, memory)?);
744                }
745            }
746        }
747
748        Ok((ComponentFunc::from(ty, &cx.instance_type()), params))
749    }
750
751    fn lower_result(
752        cx: &mut LowerContext<'_, T>,
753        ty: TypeFuncIndex,
754        result_vals: Vec<Val>,
755        dst: Destination<'_>,
756    ) -> Result<()> {
757        let fty = &cx.types[ty];
758        let param_tys = &cx.types[fty.params];
759        let result_tys = &cx.types[fty.results];
760        let result_vals = &result_vals[param_tys.types.len()..];
761        match dst {
762            Destination::Flat(storage) => {
763                let mut dst = storage.iter_mut();
764                for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
765                    val.lower(cx, *ty, &mut dst)?;
766                }
767                assert!(dst.next().is_none());
768            }
769            Destination::Memory(mut ptr) => {
770                for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
771                    let offset = cx.types.canonical_abi(ty).next_field32_size(&mut ptr);
772                    val.store(cx, *ty, offset)?;
773                }
774            }
775        }
776        Ok(())
777    }
778}
779
780pub(crate) fn validate_inbounds_dynamic(
781    abi: &CanonicalAbiInfo,
782    memory: &[u8],
783    ptr: &ValRaw,
784) -> Result<usize> {
785    // FIXME(#4311): needs memory64 support
786    let ptr = usize::try_from(ptr.get_u32())?;
787    if ptr % usize::try_from(abi.align32)? != 0 {
788        bail!("pointer not aligned");
789    }
790    let end = match ptr.checked_add(usize::try_from(abi.size32).unwrap()) {
791        Some(n) => n,
792        None => bail!("pointer size overflow"),
793    };
794    if end > memory.len() {
795        bail!("pointer out of bounds")
796    }
797    Ok(ptr)
798}