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