1 use crate::component::concurrent; 2 #[cfg(feature = "component-model-async")] 3 use crate::component::concurrent::{Accessor, Status}; 4 use crate::component::func::{LiftContext, LowerContext, Options}; 5 use crate::component::matching::InstanceType; 6 use crate::component::storage::slice_to_storage_mut; 7 use crate::component::types::ComponentFunc; 8 use crate::component::{ComponentNamedList, ComponentType, Instance, Lift, Lower, Val}; 9 use crate::prelude::*; 10 use crate::runtime::vm::component::{ 11 ComponentInstance, VMComponentContext, VMLowering, VMLoweringCallee, 12 }; 13 use crate::runtime::vm::{SendSyncPtr, VMOpaqueContext, VMStore}; 14 use crate::{AsContextMut, CallHook, StoreContextMut, ValRaw}; 15 use alloc::sync::Arc; 16 use core::any::Any; 17 use core::future::Future; 18 use core::mem::{self, MaybeUninit}; 19 use core::pin::Pin; 20 use core::ptr::NonNull; 21 use wasmtime_environ::component::{ 22 CanonicalAbiInfo, ComponentTypes, InterfaceType, MAX_FLAT_ASYNC_PARAMS, MAX_FLAT_PARAMS, 23 MAX_FLAT_RESULTS, OptionsIndex, TypeFuncIndex, TypeTuple, 24 }; 25 26 pub struct HostFunc { 27 entrypoint: VMLoweringCallee, 28 typecheck: Box<dyn (Fn(TypeFuncIndex, &InstanceType<'_>) -> Result<()>) + Send + Sync>, 29 func: Box<dyn Any + Send + Sync>, 30 } 31 32 impl core::fmt::Debug for HostFunc { 33 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 34 f.debug_struct("HostFunc").finish_non_exhaustive() 35 } 36 } 37 38 enum HostResult<T> { 39 Done(Result<T>), 40 #[cfg(feature = "component-model-async")] 41 Future(Pin<Box<dyn Future<Output = Result<T>> + Send>>), 42 } 43 44 impl HostFunc { 45 fn from_canonical<T, F, P, R>(func: F) -> Arc<HostFunc> 46 where 47 F: Fn(StoreContextMut<'_, T>, P) -> HostResult<R> + Send + Sync + 'static, 48 P: ComponentNamedList + Lift + 'static, 49 R: ComponentNamedList + Lower + 'static, 50 T: 'static, 51 { 52 let entrypoint = Self::entrypoint::<T, F, P, R>; 53 Arc::new(HostFunc { 54 entrypoint, 55 typecheck: Box::new(typecheck::<P, R>), 56 func: Box::new(func), 57 }) 58 } 59 60 pub(crate) fn from_closure<T, F, P, R>(func: F) -> Arc<HostFunc> 61 where 62 T: 'static, 63 F: Fn(StoreContextMut<T>, P) -> Result<R> + Send + Sync + 'static, 64 P: ComponentNamedList + Lift + 'static, 65 R: ComponentNamedList + Lower + 'static, 66 { 67 Self::from_canonical::<T, _, _, _>(move |store, params| { 68 HostResult::Done(func(store, params)) 69 }) 70 } 71 72 #[cfg(feature = "component-model-async")] 73 pub(crate) fn from_concurrent<T, F, P, R>(func: F) -> Arc<HostFunc> 74 where 75 T: 'static, 76 F: Fn(&Accessor<T>, P) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>> 77 + Send 78 + Sync 79 + 'static, 80 P: ComponentNamedList + Lift + 'static, 81 R: ComponentNamedList + Lower + 'static, 82 { 83 let func = Arc::new(func); 84 Self::from_canonical::<T, _, _, _>(move |store, params| { 85 let func = func.clone(); 86 HostResult::Future(Box::pin( 87 store.wrap_call(move |accessor| func(accessor, params)), 88 )) 89 }) 90 } 91 92 extern "C" fn entrypoint<T, F, P, R>( 93 cx: NonNull<VMOpaqueContext>, 94 data: NonNull<u8>, 95 ty: u32, 96 options: u32, 97 storage: NonNull<MaybeUninit<ValRaw>>, 98 storage_len: usize, 99 ) -> bool 100 where 101 F: Fn(StoreContextMut<'_, T>, P) -> HostResult<R> + Send + Sync + 'static, 102 P: ComponentNamedList + Lift, 103 R: ComponentNamedList + Lower + 'static, 104 T: 'static, 105 { 106 let data = SendSyncPtr::new(NonNull::new(data.as_ptr() as *mut F).unwrap()); 107 unsafe { 108 call_host_and_handle_result::<T>(cx, |store, instance| { 109 call_host( 110 store, 111 instance, 112 TypeFuncIndex::from_u32(ty), 113 OptionsIndex::from_u32(options), 114 NonNull::slice_from_raw_parts(storage, storage_len).as_mut(), 115 move |store, args| (*data.as_ptr())(store, args), 116 ) 117 }) 118 } 119 } 120 121 fn new_dynamic_canonical<T, F>(func: F) -> Arc<HostFunc> 122 where 123 F: Fn( 124 StoreContextMut<'_, T>, 125 ComponentFunc, 126 Vec<Val>, 127 usize, 128 ) -> Pin<Box<dyn Future<Output = Result<Vec<Val>>> + Send + 'static>> 129 + Send 130 + Sync 131 + 'static, 132 T: 'static, 133 { 134 Arc::new(HostFunc { 135 entrypoint: dynamic_entrypoint::<T, F>, 136 // This function performs dynamic type checks and subsequently does 137 // not need to perform up-front type checks. Instead everything is 138 // dynamically managed at runtime. 139 typecheck: Box::new(move |_expected_index, _expected_types| Ok(())), 140 func: Box::new(func), 141 }) 142 } 143 144 pub(crate) fn new_dynamic<T: 'static, F>(func: F) -> Arc<HostFunc> 145 where 146 F: Fn(StoreContextMut<'_, T>, ComponentFunc, &[Val], &mut [Val]) -> Result<()> 147 + Send 148 + Sync 149 + 'static, 150 { 151 Self::new_dynamic_canonical::<T, _>( 152 move |store, ty, mut params_and_results, result_start| { 153 let (params, results) = params_and_results.split_at_mut(result_start); 154 let result = func(store, ty, params, results).map(move |()| params_and_results); 155 Box::pin(async move { result }) 156 }, 157 ) 158 } 159 160 #[cfg(feature = "component-model-async")] 161 pub(crate) fn new_dynamic_concurrent<T, F>(func: F) -> Arc<HostFunc> 162 where 163 T: 'static, 164 F: for<'a> Fn( 165 &'a Accessor<T>, 166 ComponentFunc, 167 &'a [Val], 168 &'a mut [Val], 169 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>> 170 + Send 171 + Sync 172 + 'static, 173 { 174 let func = Arc::new(func); 175 Self::new_dynamic_canonical::<T, _>( 176 move |store, ty, mut params_and_results, result_start| { 177 let func = func.clone(); 178 Box::pin(store.wrap_call(move |accessor| { 179 Box::pin(async move { 180 let (params, results) = params_and_results.split_at_mut(result_start); 181 func(accessor, ty, params, results).await?; 182 Ok(params_and_results) 183 }) 184 })) 185 }, 186 ) 187 } 188 189 pub fn typecheck(&self, ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()> { 190 (self.typecheck)(ty, types) 191 } 192 193 pub fn lowering(&self) -> VMLowering { 194 let data = NonNull::from(&*self.func).cast(); 195 VMLowering { 196 callee: NonNull::new(self.entrypoint as *mut _).unwrap().into(), 197 data: data.into(), 198 } 199 } 200 } 201 202 fn typecheck<P, R>(ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()> 203 where 204 P: ComponentNamedList + Lift, 205 R: ComponentNamedList + Lower, 206 { 207 let ty = &types.types[ty]; 208 P::typecheck(&InterfaceType::Tuple(ty.params), types) 209 .context("type mismatch with parameters")?; 210 R::typecheck(&InterfaceType::Tuple(ty.results), types).context("type mismatch with results")?; 211 Ok(()) 212 } 213 214 /// The "meat" of calling a host function from wasm. 215 /// 216 /// This function is delegated to from implementations of 217 /// `HostFunc::from_closure`. Most of the arguments from the `entrypoint` are 218 /// forwarded here except for the `data` pointer which is encapsulated in the 219 /// `closure` argument here. 220 /// 221 /// This function is parameterized over: 222 /// 223 /// * `T` - the type of store this function works with (an unsafe assertion) 224 /// * `Params` - the parameters to the host function, viewed as a tuple 225 /// * `Return` - the result of the host function 226 /// * `F` - the `closure` to actually receive the `Params` and return the 227 /// `Return` 228 /// 229 /// It's expected that `F` will "un-tuple" the arguments to pass to a host 230 /// closure. 231 /// 232 /// This function is in general `unsafe` as the validity of all the parameters 233 /// must be upheld. Generally that's done by ensuring this is only called from 234 /// the select few places it's intended to be called from. 235 unsafe fn call_host<T, Params, Return, F>( 236 mut store: StoreContextMut<'_, T>, 237 instance: Instance, 238 ty: TypeFuncIndex, 239 options_idx: OptionsIndex, 240 storage: &mut [MaybeUninit<ValRaw>], 241 closure: F, 242 ) -> Result<()> 243 where 244 F: Fn(StoreContextMut<'_, T>, Params) -> HostResult<Return> + Send + Sync + 'static, 245 Params: Lift, 246 Return: Lower + 'static, 247 { 248 let options = Options::new_index(store.0, instance, options_idx); 249 let vminstance = instance.id().get(store.0); 250 let opts = &vminstance.component().env_component().options[options_idx]; 251 let async_ = opts.async_; 252 let caller_instance = opts.instance; 253 let mut flags = vminstance.instance_flags(caller_instance); 254 255 // Perform a dynamic check that this instance can indeed be left. Exiting 256 // the component is disallowed, for example, when the `realloc` function 257 // calls a canonical import. 258 if unsafe { !flags.may_leave() } { 259 return Err(anyhow!(crate::Trap::CannotLeaveComponent)); 260 } 261 262 let types = vminstance.component().types().clone(); 263 let ty = &types[ty]; 264 let param_tys = InterfaceType::Tuple(ty.params); 265 let result_tys = InterfaceType::Tuple(ty.results); 266 267 if async_ { 268 #[cfg(feature = "component-model-async")] 269 { 270 let mut storage = unsafe { Storage::<'_, Params, u32>::new_async::<Return>(storage) }; 271 272 // Lift the parameters, either from flat storage or from linear 273 // memory. 274 let lift = &mut LiftContext::new(store.0.store_opaque_mut(), &options, instance); 275 lift.enter_call(); 276 let params = storage.lift_params(lift, param_tys)?; 277 278 // Load the return pointer, if present. 279 let retptr = match storage.async_retptr() { 280 Some(ptr) => { 281 let mut lower = 282 LowerContext::new(store.as_context_mut(), &options, &types, instance); 283 validate_inbounds::<Return>(lower.as_slice_mut(), ptr)? 284 } 285 // If there's no return pointer then `Return` should have an 286 // empty flat representation. In this situation pretend the 287 // return pointer was 0 so we have something to shepherd along 288 // into the closure below. 289 None => { 290 assert_eq!(Return::flatten_count(), 0); 291 0 292 } 293 }; 294 295 let host_result = closure(store.as_context_mut(), params); 296 297 let mut lower_result = { 298 let types = types.clone(); 299 move |store: StoreContextMut<T>, ret: Return| { 300 unsafe { 301 flags.set_may_leave(false); 302 } 303 let mut lower = LowerContext::new(store, &options, &types, instance); 304 ret.linear_lower_to_memory(&mut lower, result_tys, retptr)?; 305 unsafe { 306 flags.set_may_leave(true); 307 } 308 lower.exit_call()?; 309 Ok(()) 310 } 311 }; 312 let task = match host_result { 313 HostResult::Done(result) => { 314 lower_result(store.as_context_mut(), result?)?; 315 None 316 } 317 #[cfg(feature = "component-model-async")] 318 HostResult::Future(future) => instance.first_poll( 319 store.as_context_mut(), 320 future, 321 caller_instance, 322 lower_result, 323 )?, 324 }; 325 326 let status = if let Some(task) = task { 327 Status::Started.pack(Some(task)) 328 } else { 329 Status::Returned.pack(None) 330 }; 331 332 let mut lower = LowerContext::new(store, &options, &types, instance); 333 storage.lower_results(&mut lower, InterfaceType::U32, status)?; 334 } 335 #[cfg(not(feature = "component-model-async"))] 336 { 337 let _ = caller_instance; 338 unreachable!( 339 "async-lowered imports should have failed validation \ 340 when `component-model-async` feature disabled" 341 ); 342 } 343 } else { 344 let mut storage = unsafe { Storage::<'_, Params, Return>::new_sync(storage) }; 345 let mut lift = LiftContext::new(store.0.store_opaque_mut(), &options, instance); 346 lift.enter_call(); 347 let params = storage.lift_params(&mut lift, param_tys)?; 348 349 let ret = match closure(store.as_context_mut(), params) { 350 HostResult::Done(result) => result?, 351 #[cfg(feature = "component-model-async")] 352 HostResult::Future(future) => { 353 concurrent::poll_and_block(store.0, future, caller_instance)? 354 } 355 }; 356 357 unsafe { 358 flags.set_may_leave(false); 359 } 360 let mut lower = LowerContext::new(store, &options, &types, instance); 361 storage.lower_results(&mut lower, result_tys, ret)?; 362 unsafe { 363 flags.set_may_leave(true); 364 } 365 lower.exit_call()?; 366 } 367 368 return Ok(()); 369 370 /// Type-level representation of the matrix of possibilities of how 371 /// WebAssembly parameters and results are handled in the canonical ABI. 372 /// 373 /// Wasmtime's ABI here always works with `&mut [MaybeUninit<ValRaw>]` as the 374 /// base representation of params/results. Parameters are passed 375 /// sequentially and results are returned by overwriting the parameters. 376 /// That means both params/results start from index 0. 377 /// 378 /// The type-level representation here involves working with the typed 379 /// `P::Lower` and `R::Lower` values which is a type-level representation of 380 /// a lowered value. All lowered values are in essence a sequence of 381 /// `ValRaw` values one after the other to fit within this original array 382 /// that is the basis of Wasmtime's ABI. 383 /// 384 /// The various combinations here are cryptic, but only used in this file. 385 /// This in theory cuts down on the verbosity below, but an explanation of 386 /// the various acronyms here are: 387 /// 388 /// * Pd - params direct - means that parameters are passed directly in 389 /// their flat representation via `P::Lower`. 390 /// 391 /// * Pi - params indirect - means that parameters are passed indirectly in 392 /// linear memory and the argument here is `ValRaw` to store the pointer. 393 /// 394 /// * Rd - results direct - means that results are returned directly in 395 /// their flat representation via `R::Lower`. Note that this is always 396 /// represented as `MaybeUninit<R::Lower>` as well because the return 397 /// values may point to uninitialized memory if there were no parameters 398 /// for example. 399 /// 400 /// * Ri - results indirect - means that results are returned indirectly in 401 /// linear memory through the pointer specified. Note that this is 402 /// specified as a `ValRaw` to represent the argument that's being given 403 /// to the host from WebAssembly. 404 /// 405 /// * Ar - async results - means that the parameters to this call 406 /// additionally include an async result pointer. Async results are always 407 /// transmitted via a pointer so this is always a `ValRaw`. 408 /// 409 /// Internally this type makes liberal use of `Union` and `Pair` helpers 410 /// below which are simple `#[repr(C)]` wrappers around a pair of types that 411 /// are a union or a pair. 412 /// 413 /// Note that for any combination of `P` and `R` this `enum` is actually 414 /// pointless as a single variant will be used. In theory we should be able 415 /// to monomorphize based on `P` and `R` to a specific type. This 416 /// monomorphization depends on conditionals like `flatten_count() <= N`, 417 /// however, and I don't know how to encode that in Rust easily. In lieu of 418 /// that we assume LLVM will figure things out and boil away the actual enum 419 /// and runtime dispatch. 420 enum Storage<'a, P: ComponentType, R: ComponentType> { 421 /// Params: direct, Results: direct 422 /// 423 /// The lowered representation of params/results are overlaid on top of 424 /// each other. 425 PdRd(&'a mut Union<P::Lower, MaybeUninit<R::Lower>>), 426 427 /// Params: direct, Results: indirect 428 /// 429 /// The return pointer comes after the params so this is sequentially 430 /// laid out with one after the other. 431 PdRi(&'a Pair<P::Lower, ValRaw>), 432 433 /// Params: indirect, Results: direct 434 /// 435 /// Here the return values are overlaid on top of the pointer parameter. 436 PiRd(&'a mut Union<ValRaw, MaybeUninit<R::Lower>>), 437 438 /// Params: indirect, Results: indirect 439 /// 440 /// Here the two parameters are laid out sequentially one after the 441 /// other. 442 PiRi(&'a Pair<ValRaw, ValRaw>), 443 444 /// Params: direct + async result, Results: direct 445 /// 446 /// This is like `PdRd` except that the parameters additionally include 447 /// a pointer for where to store the result. 448 #[cfg(feature = "component-model-async")] 449 PdArRd(&'a mut Union<Pair<P::Lower, ValRaw>, MaybeUninit<R::Lower>>), 450 451 /// Params: indirect + async result, Results: direct 452 /// 453 /// This is like `PiRd` except that the parameters additionally include 454 /// a pointer for where to store the result. 455 #[cfg(feature = "component-model-async")] 456 PiArRd(&'a mut Union<Pair<ValRaw, ValRaw>, MaybeUninit<R::Lower>>), 457 } 458 459 // Helper structure used above in `Storage` to represent two consecutive 460 // values. 461 #[repr(C)] 462 #[derive(Copy, Clone)] 463 struct Pair<T, U> { 464 a: T, 465 b: U, 466 } 467 468 // Helper structure used above in `Storage` to represent two values overlaid 469 // on each other. 470 #[repr(C)] 471 union Union<T: Copy, U: Copy> { 472 a: T, 473 b: U, 474 } 475 476 /// Representation of where parameters are lifted from. 477 enum Src<'a, T> { 478 /// Parameters are directly lifted from `T`, which is under the hood a 479 /// sequence of `ValRaw`. This is `P::Lower` for example. 480 Direct(&'a T), 481 482 /// Parameters are loaded from linear memory, and this is the wasm 483 /// parameter representing the pointer into linear memory to load from. 484 Indirect(&'a ValRaw), 485 } 486 487 /// Dual of [`Src`], where to store results. 488 enum Dst<'a, T> { 489 /// Results are stored directly in this pointer. 490 /// 491 /// Note that this is a mutable pointer but it's specifically 492 /// `MaybeUninit` as trampolines do not initialize it. The `T` here will 493 /// be `R::Lower` for example. 494 Direct(&'a mut MaybeUninit<T>), 495 496 /// Results are stored in linear memory, and this value is the wasm 497 /// parameter given which represents the pointer into linear memory. 498 /// 499 /// Note that this is not mutable as the parameter is not mutated, but 500 /// memory will be mutated. 501 Indirect(&'a ValRaw), 502 } 503 504 impl<P, R> Storage<'_, P, R> 505 where 506 P: ComponentType + Lift, 507 R: ComponentType + Lower, 508 { 509 /// Classifies a new `Storage` suitable for use with sync functions. 510 /// 511 /// There's a 2x2 matrix of whether parameters and results are stored on the 512 /// stack or on the heap. Each of the 4 branches here have a different 513 /// representation of the storage of arguments/returns. 514 /// 515 /// Also note that while four branches are listed here only one is taken for 516 /// any particular `Params` and `Return` combination. This should be 517 /// trivially DCE'd by LLVM. Perhaps one day with enough const programming in 518 /// Rust we can make monomorphizations of this function codegen only one 519 /// branch, but today is not that day. 520 /// 521 /// # Safety 522 /// 523 /// Requires that the `storage` provided does indeed match an wasm 524 /// function with the signature of `P` and `R` as params/results. 525 unsafe fn new_sync(storage: &mut [MaybeUninit<ValRaw>]) -> Storage<'_, P, R> { 526 // SAFETY: this `unsafe` is due to the `slice_to_storage_*` helpers 527 // used which view the slice provided as a different type. This 528 // safety should be upheld by the contract of the `ComponentType` 529 // trait and its `Lower` type parameter meaning they're valid to 530 // view as a sequence of `ValRaw` types. Additionally the 531 // `ComponentType` trait ensures that the matching of the runtime 532 // length of `storage` should match the actual size of `P::Lower` 533 // and `R::Lower` or such as needed. 534 unsafe { 535 if P::flatten_count() <= MAX_FLAT_PARAMS { 536 if R::flatten_count() <= MAX_FLAT_RESULTS { 537 Storage::PdRd(slice_to_storage_mut(storage).assume_init_mut()) 538 } else { 539 Storage::PdRi(slice_to_storage_mut(storage).assume_init_ref()) 540 } 541 } else { 542 if R::flatten_count() <= MAX_FLAT_RESULTS { 543 Storage::PiRd(slice_to_storage_mut(storage).assume_init_mut()) 544 } else { 545 Storage::PiRi(slice_to_storage_mut(storage).assume_init_ref()) 546 } 547 } 548 } 549 } 550 551 fn lift_params(&self, cx: &mut LiftContext<'_>, ty: InterfaceType) -> Result<P> { 552 match self.lift_src() { 553 Src::Direct(storage) => P::linear_lift_from_flat(cx, ty, storage), 554 Src::Indirect(ptr) => { 555 let ptr = validate_inbounds::<P>(cx.memory(), ptr)?; 556 P::linear_lift_from_memory(cx, ty, &cx.memory()[ptr..][..P::SIZE32]) 557 } 558 } 559 } 560 561 fn lift_src(&self) -> Src<'_, P::Lower> { 562 match self { 563 // SAFETY: these `unsafe` blocks are due to accessing union 564 // fields. The safety here relies on the contract of the 565 // `ComponentType` trait which should ensure that the types 566 // projected onto a list of wasm parameters are indeed correct. 567 // That means that the projections here, if the types are 568 // correct, all line up to initialized memory that's well-typed 569 // to access. 570 Storage::PdRd(storage) => unsafe { Src::Direct(&storage.a) }, 571 Storage::PdRi(storage) => Src::Direct(&storage.a), 572 #[cfg(feature = "component-model-async")] 573 Storage::PdArRd(storage) => unsafe { Src::Direct(&storage.a.a) }, 574 Storage::PiRd(storage) => unsafe { Src::Indirect(&storage.a) }, 575 Storage::PiRi(storage) => Src::Indirect(&storage.a), 576 #[cfg(feature = "component-model-async")] 577 Storage::PiArRd(storage) => unsafe { Src::Indirect(&storage.a.a) }, 578 } 579 } 580 581 fn lower_results<T>( 582 &mut self, 583 cx: &mut LowerContext<'_, T>, 584 ty: InterfaceType, 585 ret: R, 586 ) -> Result<()> { 587 match self.lower_dst() { 588 Dst::Direct(storage) => ret.linear_lower_to_flat(cx, ty, storage), 589 Dst::Indirect(ptr) => { 590 let ptr = validate_inbounds::<R>(cx.as_slice_mut(), ptr)?; 591 ret.linear_lower_to_memory(cx, ty, ptr) 592 } 593 } 594 } 595 596 fn lower_dst(&mut self) -> Dst<'_, R::Lower> { 597 match self { 598 // SAFETY: these unsafe blocks are due to accessing fields of a 599 // `union` which is not safe in Rust. The returned value is 600 // `MaybeUninit<R::Lower>` in all cases, however, which should 601 // safely model how `union` memory is possibly uninitialized. 602 // Additionally `R::Lower` has the `unsafe` contract that all 603 // its bit patterns must be sound, which additionally should 604 // help make this safe. 605 Storage::PdRd(storage) => unsafe { Dst::Direct(&mut storage.b) }, 606 Storage::PiRd(storage) => unsafe { Dst::Direct(&mut storage.b) }, 607 #[cfg(feature = "component-model-async")] 608 Storage::PdArRd(storage) => unsafe { Dst::Direct(&mut storage.b) }, 609 #[cfg(feature = "component-model-async")] 610 Storage::PiArRd(storage) => unsafe { Dst::Direct(&mut storage.b) }, 611 Storage::PdRi(storage) => Dst::Indirect(&storage.b), 612 Storage::PiRi(storage) => Dst::Indirect(&storage.b), 613 } 614 } 615 616 #[cfg(feature = "component-model-async")] 617 fn async_retptr(&self) -> Option<&ValRaw> { 618 match self { 619 // SAFETY: like above these are `unsafe` due to accessing a 620 // `union` field. This should be safe via the construction of 621 // `Storage` which should correctly determine whether or not an 622 // async return pointer is provided and classify the args/rets 623 // appropriately. 624 Storage::PdArRd(storage) => unsafe { Some(&storage.a.b) }, 625 Storage::PiArRd(storage) => unsafe { Some(&storage.a.b) }, 626 Storage::PdRd(_) | Storage::PiRd(_) | Storage::PdRi(_) | Storage::PiRi(_) => None, 627 } 628 } 629 } 630 631 #[cfg(feature = "component-model-async")] 632 impl<P> Storage<'_, P, u32> 633 where 634 P: ComponentType + Lift, 635 { 636 /// Classifies a new `Storage` suitable for use with async functions. 637 /// 638 /// # Safety 639 /// 640 /// Requires that the `storage` provided does indeed match an `async` 641 /// wasm function with the signature of `P` and `R` as params/results. 642 unsafe fn new_async<R>(storage: &mut [MaybeUninit<ValRaw>]) -> Storage<'_, P, u32> 643 where 644 R: ComponentType + Lower, 645 { 646 // SAFETY: see `Storage::new` for discussion on why this should be 647 // safe given the unsafe contract of the `ComponentType` trait. 648 unsafe { 649 if P::flatten_count() <= wasmtime_environ::component::MAX_FLAT_ASYNC_PARAMS { 650 if R::flatten_count() == 0 { 651 Storage::PdRd(slice_to_storage_mut(storage).assume_init_mut()) 652 } else { 653 Storage::PdArRd(slice_to_storage_mut(storage).assume_init_mut()) 654 } 655 } else { 656 if R::flatten_count() == 0 { 657 Storage::PiRd(slice_to_storage_mut(storage).assume_init_mut()) 658 } else { 659 Storage::PiArRd(slice_to_storage_mut(storage).assume_init_mut()) 660 } 661 } 662 } 663 } 664 } 665 } 666 667 pub(crate) fn validate_inbounds<T: ComponentType>(memory: &[u8], ptr: &ValRaw) -> Result<usize> { 668 // FIXME(#4311): needs memory64 support 669 let ptr = usize::try_from(ptr.get_u32())?; 670 if ptr % usize::try_from(T::ALIGN32)? != 0 { 671 bail!("pointer not aligned"); 672 } 673 let end = match ptr.checked_add(T::SIZE32) { 674 Some(n) => n, 675 None => bail!("pointer size overflow"), 676 }; 677 if end > memory.len() { 678 bail!("pointer out of bounds") 679 } 680 Ok(ptr) 681 } 682 683 unsafe fn call_host_and_handle_result<T>( 684 cx: NonNull<VMOpaqueContext>, 685 func: impl FnOnce(StoreContextMut<'_, T>, Instance) -> Result<()>, 686 ) -> bool 687 where 688 T: 'static, 689 { 690 let cx = unsafe { VMComponentContext::from_opaque(cx) }; 691 unsafe { 692 ComponentInstance::enter_host_from_wasm(cx, |store, instance| { 693 let mut store = store.unchecked_context_mut(); 694 store.0.call_hook(CallHook::CallingHost)?; 695 let res = func(store.as_context_mut(), instance); 696 store.0.call_hook(CallHook::ReturningFromHost)?; 697 res 698 }) 699 } 700 } 701 702 unsafe fn call_host_dynamic<T, F>( 703 mut store: StoreContextMut<'_, T>, 704 instance: Instance, 705 ty: TypeFuncIndex, 706 options_idx: OptionsIndex, 707 storage: &mut [MaybeUninit<ValRaw>], 708 closure: F, 709 ) -> Result<()> 710 where 711 F: Fn( 712 StoreContextMut<'_, T>, 713 ComponentFunc, 714 Vec<Val>, 715 usize, 716 ) -> Pin<Box<dyn Future<Output = Result<Vec<Val>>> + Send + 'static>> 717 + Send 718 + Sync 719 + 'static, 720 T: 'static, 721 { 722 let options = Options::new_index(store.0, instance, options_idx); 723 let vminstance = instance.id().get(store.0); 724 let opts = &vminstance.component().env_component().options[options_idx]; 725 let async_ = opts.async_; 726 let caller_instance = opts.instance; 727 let mut flags = vminstance.instance_flags(caller_instance); 728 729 // Perform a dynamic check that this instance can indeed be left. Exiting 730 // the component is disallowed, for example, when the `realloc` function 731 // calls a canonical import. 732 if unsafe { !flags.may_leave() } { 733 return Err(anyhow!(crate::Trap::CannotLeaveComponent)); 734 } 735 736 let types = instance.id().get(store.0).component().types().clone(); 737 let func_ty = &types[ty]; 738 let param_tys = &types[func_ty.params]; 739 let result_tys = &types[func_ty.results]; 740 741 let mut params_and_results = Vec::new(); 742 let mut lift = &mut LiftContext::new(store.0.store_opaque_mut(), &options, instance); 743 lift.enter_call(); 744 let max_flat = if async_ { 745 MAX_FLAT_ASYNC_PARAMS 746 } else { 747 MAX_FLAT_PARAMS 748 }; 749 let ty = ComponentFunc::from(ty, &lift.instance_type()); 750 751 let ret_index = unsafe { 752 dynamic_params_load( 753 &mut lift, 754 &types, 755 storage, 756 param_tys, 757 &mut params_and_results, 758 max_flat, 759 )? 760 }; 761 let result_start = params_and_results.len(); 762 for _ in 0..result_tys.types.len() { 763 params_and_results.push(Val::Bool(false)); 764 } 765 766 if async_ { 767 #[cfg(feature = "component-model-async")] 768 { 769 let retptr = if result_tys.types.len() == 0 { 770 0 771 } else { 772 let retptr = unsafe { storage[ret_index].assume_init() }; 773 let mut lower = 774 LowerContext::new(store.as_context_mut(), &options, &types, instance); 775 validate_inbounds_dynamic(&result_tys.abi, lower.as_slice_mut(), &retptr)? 776 }; 777 778 let future = closure(store.as_context_mut(), ty, params_and_results, result_start); 779 780 let task = instance.first_poll(store, future, caller_instance, { 781 let types = types.clone(); 782 let result_tys = func_ty.results; 783 move |store: StoreContextMut<T>, result_vals: Vec<Val>| { 784 let result_tys = &types[result_tys]; 785 let result_vals = &result_vals[result_start..]; 786 assert_eq!(result_vals.len(), result_tys.types.len()); 787 788 unsafe { 789 flags.set_may_leave(false); 790 } 791 792 let mut lower = LowerContext::new(store, &options, &types, instance); 793 let mut ptr = retptr; 794 for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) { 795 let offset = types.canonical_abi(ty).next_field32_size(&mut ptr); 796 val.store(&mut lower, *ty, offset)?; 797 } 798 799 unsafe { 800 flags.set_may_leave(true); 801 } 802 803 lower.exit_call()?; 804 805 Ok(()) 806 } 807 })?; 808 809 let status = if let Some(task) = task { 810 Status::Started.pack(Some(task)) 811 } else { 812 Status::Returned.pack(None) 813 }; 814 815 storage[0] = MaybeUninit::new(ValRaw::i32(status as i32)); 816 } 817 #[cfg(not(feature = "component-model-async"))] 818 { 819 unreachable!( 820 "async-lowered imports should have failed validation \ 821 when `component-model-async` feature disabled" 822 ); 823 } 824 } else { 825 let future = closure(store.as_context_mut(), ty, params_and_results, result_start); 826 let result_vals = concurrent::poll_and_block(store.0, future, caller_instance)?; 827 let result_vals = &result_vals[result_start..]; 828 829 unsafe { 830 flags.set_may_leave(false); 831 } 832 833 let mut cx = LowerContext::new(store, &options, &types, instance); 834 if let Some(cnt) = result_tys.abi.flat_count(MAX_FLAT_RESULTS) { 835 let mut dst = storage[..cnt].iter_mut(); 836 for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) { 837 val.lower(&mut cx, *ty, &mut dst)?; 838 } 839 assert!(dst.next().is_none()); 840 } else { 841 let ret_ptr = unsafe { storage[ret_index].assume_init_ref() }; 842 let mut ptr = validate_inbounds_dynamic(&result_tys.abi, cx.as_slice_mut(), ret_ptr)?; 843 for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) { 844 let offset = types.canonical_abi(ty).next_field32_size(&mut ptr); 845 val.store(&mut cx, *ty, offset)?; 846 } 847 } 848 849 unsafe { 850 flags.set_may_leave(true); 851 } 852 853 cx.exit_call()?; 854 } 855 856 Ok(()) 857 } 858 859 /// Loads the parameters for a dynamic host function call into `params` 860 /// 861 /// Returns the number of flat `storage` values consumed. 862 /// 863 /// # Safety 864 /// 865 /// Requires that `param_tys` matches the type signature of the `storage` that 866 /// was passed in. 867 unsafe fn dynamic_params_load( 868 cx: &mut LiftContext<'_>, 869 types: &ComponentTypes, 870 storage: &[MaybeUninit<ValRaw>], 871 param_tys: &TypeTuple, 872 params: &mut Vec<Val>, 873 max_flat_params: usize, 874 ) -> Result<usize> { 875 if let Some(param_count) = param_tys.abi.flat_count(max_flat_params) { 876 // NB: can use `MaybeUninit::slice_assume_init_ref` when that's stable 877 let storage = 878 unsafe { mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(&storage[..param_count]) }; 879 let mut iter = storage.iter(); 880 for ty in param_tys.types.iter() { 881 params.push(Val::lift(cx, *ty, &mut iter)?); 882 } 883 assert!(iter.next().is_none()); 884 Ok(param_count) 885 } else { 886 let mut offset = validate_inbounds_dynamic(¶m_tys.abi, cx.memory(), unsafe { 887 storage[0].assume_init_ref() 888 })?; 889 for ty in param_tys.types.iter() { 890 let abi = types.canonical_abi(ty); 891 let size = usize::try_from(abi.size32).unwrap(); 892 let memory = &cx.memory()[abi.next_field32_size(&mut offset)..][..size]; 893 params.push(Val::load(cx, *ty, memory)?); 894 } 895 Ok(1) 896 } 897 } 898 899 pub(crate) fn validate_inbounds_dynamic( 900 abi: &CanonicalAbiInfo, 901 memory: &[u8], 902 ptr: &ValRaw, 903 ) -> Result<usize> { 904 // FIXME(#4311): needs memory64 support 905 let ptr = usize::try_from(ptr.get_u32())?; 906 if ptr % usize::try_from(abi.align32)? != 0 { 907 bail!("pointer not aligned"); 908 } 909 let end = match ptr.checked_add(usize::try_from(abi.size32).unwrap()) { 910 Some(n) => n, 911 None => bail!("pointer size overflow"), 912 }; 913 if end > memory.len() { 914 bail!("pointer out of bounds") 915 } 916 Ok(ptr) 917 } 918 919 extern "C" fn dynamic_entrypoint<T, F>( 920 cx: NonNull<VMOpaqueContext>, 921 data: NonNull<u8>, 922 ty: u32, 923 options: u32, 924 storage: NonNull<MaybeUninit<ValRaw>>, 925 storage_len: usize, 926 ) -> bool 927 where 928 F: Fn( 929 StoreContextMut<'_, T>, 930 ComponentFunc, 931 Vec<Val>, 932 usize, 933 ) -> Pin<Box<dyn Future<Output = Result<Vec<Val>>> + Send + 'static>> 934 + Send 935 + Sync 936 + 'static, 937 T: 'static, 938 { 939 let data = SendSyncPtr::new(NonNull::new(data.as_ptr() as *mut F).unwrap()); 940 unsafe { 941 call_host_and_handle_result(cx, |store, instance| { 942 call_host_dynamic::<T, _>( 943 store, 944 instance, 945 TypeFuncIndex::from_u32(ty), 946 OptionsIndex::from_u32(options), 947 NonNull::slice_from_raw_parts(storage, storage_len).as_mut(), 948 &*data.as_ptr(), 949 ) 950 }) 951 } 952 } 953