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