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