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