Skip to main content

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