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