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 ¶m_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