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