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