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