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