Skip to main content

wasmtime/runtime/component/func/
host.rs

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