1 //! Implementation of string transcoding required by the component model. 2 3 use crate::component::Instance; 4 #[cfg(feature = "component-model-async")] 5 use crate::component::concurrent::WaitResult; 6 use crate::prelude::*; 7 use crate::runtime::component::RuntimeInstance; 8 #[cfg(feature = "component-model-async")] 9 use crate::runtime::component::concurrent::{ResourcePair, SuspensionTarget}; 10 use crate::runtime::vm::component::{ComponentInstance, VMComponentContext}; 11 use crate::runtime::vm::{HostResultHasUnwindSentinel, VMStore, VmSafe}; 12 use core::cell::Cell; 13 use core::ptr::NonNull; 14 use core::slice; 15 use wasmtime_environ::component::*; 16 17 const UTF16_TAG: usize = 1 << 31; 18 19 macro_rules! signature { 20 (@ty size) => (usize); 21 (@ty ptr_u8) => (*mut u8); 22 (@ty ptr_u16) => (*mut u16); 23 (@ty ptr_size) => (*mut usize); 24 (@ty u8) => (u8); 25 (@ty u32) => (u32); 26 (@ty u64) => (u64); 27 (@ty bool) => (bool); 28 (@ty vmctx) => (NonNull<VMComponentContext>); 29 } 30 31 /// Defines a `VMComponentBuiltins` structure which contains any builtins such 32 /// as resource-related intrinsics. 33 macro_rules! define_builtins { 34 ( 35 $( 36 $( #[$attr:meta] )* 37 $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?; 38 )* 39 ) => { 40 /// An array that stores addresses of builtin functions. We translate code 41 /// to use indirect calls. This way, we don't have to patch the code. 42 #[repr(C)] 43 pub struct VMComponentBuiltins { 44 $( 45 $name: unsafe extern "C" fn( 46 $(signature!(@ty $param),)* 47 ) $( -> signature!(@ty $result))?, 48 )* 49 } 50 51 // SAFETY: the above structure is repr(C) and only contains `VmSafe` 52 // fields. 53 unsafe impl VmSafe for VMComponentBuiltins {} 54 55 impl VMComponentBuiltins { 56 pub const INIT: VMComponentBuiltins = VMComponentBuiltins { 57 $($name: trampolines::$name,)* 58 }; 59 60 /// Helper to call `expose_provenance()` on all contained pointers. 61 /// 62 /// This is required to be called at least once before entering wasm 63 /// to inform the compiler that these function pointers may all be 64 /// loaded/stored and used on the "other end" to reacquire 65 /// provenance in Pulley. Pulley models hostcalls with a host 66 /// pointer as the first parameter that's a function pointer under 67 /// the hood, and this call ensures that the use of the function 68 /// pointer is considered valid. 69 pub fn expose_provenance(&self) -> NonNull<Self>{ 70 $( 71 (self.$name as *mut u8).expose_provenance(); 72 )* 73 NonNull::from(self) 74 } 75 } 76 }; 77 } 78 79 wasmtime_environ::foreach_builtin_component_function!(define_builtins); 80 81 /// Submodule with macro-generated constants which are the actual libcall 82 /// transcoders that are invoked by Cranelift. These functions have a specific 83 /// ABI defined by the macro itself and will defer to the actual bodies of each 84 /// implementation following this submodule. 85 mod trampolines { 86 use super::{ComponentInstance, VMComponentContext}; 87 use core::ptr::NonNull; 88 89 macro_rules! shims { 90 ( 91 $( 92 $( #[cfg($attr:meta)] )? 93 $name:ident( vmctx: vmctx $(, $pname:ident: $param:ident )* ) $( -> $result:ident )?; 94 )* 95 ) => ( 96 $( 97 #[allow(unused_variables, reason = "macro-generated")] 98 pub unsafe extern "C" fn $name( 99 vmctx: NonNull<VMComponentContext> 100 $(,$pname : signature!(@ty $param))* 101 ) $( -> signature!(@ty $result))? { 102 $(#[cfg($attr)])? 103 { 104 $(shims!(@validate_param $pname $param);)* 105 106 let ret = unsafe { 107 ComponentInstance::enter_host_from_wasm(vmctx, |store, instance| { 108 shims!(@invoke $name(store, instance,) $($pname)*) 109 }) 110 }; 111 shims!(@convert_ret ret $($pname: $param)*) 112 } 113 $( 114 #[cfg(not($attr))] 115 { 116 let _ = vmctx; 117 unreachable!(); 118 } 119 )? 120 } 121 )* 122 ); 123 124 // Helper macro to convert a 2-tuple automatically when the last 125 // parameter is a `ptr_size` argument. 126 (@convert_ret $ret:ident) => ($ret); 127 (@convert_ret $ret:ident $retptr:ident: ptr_size) => ({ 128 let (a, b) = $ret; 129 unsafe { 130 *$retptr = b; 131 } 132 a 133 }); 134 (@convert_ret $ret:ident $name:ident: $ty:ident $($rest:tt)*) => ( 135 shims!(@convert_ret $ret $($rest)*) 136 ); 137 138 (@validate_param $arg:ident ptr_u16) => ({ 139 // This should already be guaranteed by the canonical ABI and our 140 // adapter modules, but double-check here to be extra-sure. If this 141 // is a perf concern it can become a `debug_assert!`. 142 assert!(($arg as usize) % 2 == 0, "unaligned 16-bit pointer"); 143 }); 144 (@validate_param $arg:ident $ty:ident) => (); 145 146 // Helper macro to invoke `$m` with all of the tokens passed except for 147 // any argument named `ret2` 148 (@invoke $m:ident ($($args:tt)*)) => (super::$m($($args)*)); 149 150 // ignore `ret2`-named arguments 151 (@invoke $m:ident ($($args:tt)*) ret2 $($rest:tt)*) => ( 152 shims!(@invoke $m ($($args)*) $($rest)*) 153 ); 154 155 // move all other arguments into the `$args` list 156 (@invoke $m:ident ($($args:tt)*) $param:ident $($rest:tt)*) => ( 157 shims!(@invoke $m ($($args)* $param,) $($rest)*) 158 ); 159 } 160 161 wasmtime_environ::foreach_builtin_component_function!(shims); 162 } 163 164 /// This property should already be guaranteed by construction in the component 165 /// model but assert it here to be extra sure. Nothing below is sound if regions 166 /// can overlap. 167 fn assert_no_overlap<T, U>(a: &[T], b: &[U]) { 168 let a_start = a.as_ptr() as usize; 169 let a_end = a_start + (a.len() * core::mem::size_of::<T>()); 170 let b_start = b.as_ptr() as usize; 171 let b_end = b_start + (b.len() * core::mem::size_of::<U>()); 172 173 if a_start < b_start { 174 assert!(a_end < b_start); 175 } else { 176 assert!(b_end < a_start); 177 } 178 } 179 180 /// Converts a utf8 string to a utf8 string. 181 /// 182 /// The length provided is length of both the source and the destination 183 /// buffers. No value is returned other than whether an invalid string was 184 /// found. 185 unsafe fn utf8_to_utf8( 186 _: &mut dyn VMStore, 187 _: Instance, 188 src: *mut u8, 189 len: usize, 190 dst: *mut u8, 191 ) -> Result<()> { 192 let src = unsafe { slice::from_raw_parts(src, len) }; 193 let dst = unsafe { slice::from_raw_parts_mut(dst, len) }; 194 assert_no_overlap(src, dst); 195 log::trace!("utf8-to-utf8 {len}"); 196 let src = core::str::from_utf8(src).map_err(|_| format_err!("invalid utf8 encoding"))?; 197 dst.copy_from_slice(src.as_bytes()); 198 Ok(()) 199 } 200 201 /// Converts a utf16 string to a utf16 string. 202 /// 203 /// The length provided is length of both the source and the destination 204 /// buffers. No value is returned other than whether an invalid string was 205 /// found. 206 unsafe fn utf16_to_utf16( 207 _: &mut dyn VMStore, 208 _: Instance, 209 src: *mut u16, 210 len: usize, 211 dst: *mut u16, 212 ) -> Result<()> { 213 let src = unsafe { slice::from_raw_parts(src, len) }; 214 let dst = unsafe { slice::from_raw_parts_mut(dst, len) }; 215 assert_no_overlap(src, dst); 216 log::trace!("utf16-to-utf16 {len}"); 217 run_utf16_to_utf16(src, dst)?; 218 Ok(()) 219 } 220 221 /// Transcodes utf16 to itself, returning whether all code points were inside of 222 /// the latin1 space. 223 fn run_utf16_to_utf16(src: &[u16], mut dst: &mut [u16]) -> Result<bool> { 224 let mut all_latin1 = true; 225 for ch in core::char::decode_utf16(src.iter().map(|i| u16::from_le(*i))) { 226 let ch = ch.map_err(|_| format_err!("invalid utf16 encoding"))?; 227 all_latin1 = all_latin1 && u8::try_from(u32::from(ch)).is_ok(); 228 let result = ch.encode_utf16(dst); 229 let size = result.len(); 230 for item in result { 231 *item = item.to_le(); 232 } 233 dst = &mut dst[size..]; 234 } 235 Ok(all_latin1) 236 } 237 238 /// Converts a latin1 string to a latin1 string. 239 /// 240 /// Given that all byte sequences are valid latin1 strings this is simply a 241 /// memory copy. 242 unsafe fn latin1_to_latin1( 243 _: &mut dyn VMStore, 244 _: Instance, 245 src: *mut u8, 246 len: usize, 247 dst: *mut u8, 248 ) -> Result<()> { 249 let src = unsafe { slice::from_raw_parts(src, len) }; 250 let dst = unsafe { slice::from_raw_parts_mut(dst, len) }; 251 assert_no_overlap(src, dst); 252 log::trace!("latin1-to-latin1 {len}"); 253 dst.copy_from_slice(src); 254 Ok(()) 255 } 256 257 /// Converts a latin1 string to a utf16 string. 258 /// 259 /// This simply inflates the latin1 characters to the u16 code points. The 260 /// length provided is the same length of the source and destination buffers. 261 unsafe fn latin1_to_utf16( 262 _: &mut dyn VMStore, 263 _: Instance, 264 src: *mut u8, 265 len: usize, 266 dst: *mut u16, 267 ) -> Result<()> { 268 let src = unsafe { slice::from_raw_parts(src, len) }; 269 let dst = unsafe { slice::from_raw_parts_mut(dst, len) }; 270 assert_no_overlap(src, dst); 271 for (src, dst) in src.iter().zip(dst) { 272 *dst = u16::from(*src).to_le(); 273 } 274 log::trace!("latin1-to-utf16 {len}"); 275 Ok(()) 276 } 277 278 struct CopySizeReturn(usize); 279 280 unsafe impl HostResultHasUnwindSentinel for CopySizeReturn { 281 type Abi = usize; 282 const SENTINEL: usize = usize::MAX; 283 fn into_abi(self) -> usize { 284 self.0 285 } 286 } 287 288 /// Converts utf8 to utf16. 289 /// 290 /// The length provided is the same unit length of both buffers, and the 291 /// returned value from this function is how many u16 units were written. 292 unsafe fn utf8_to_utf16( 293 _: &mut dyn VMStore, 294 _: Instance, 295 src: *mut u8, 296 len: usize, 297 dst: *mut u16, 298 ) -> Result<CopySizeReturn> { 299 let src = unsafe { slice::from_raw_parts(src, len) }; 300 let dst = unsafe { slice::from_raw_parts_mut(dst, len) }; 301 assert_no_overlap(src, dst); 302 303 let result = run_utf8_to_utf16(src, dst)?; 304 log::trace!("utf8-to-utf16 {len} => {result}"); 305 Ok(CopySizeReturn(result)) 306 } 307 308 fn run_utf8_to_utf16(src: &[u8], dst: &mut [u16]) -> Result<usize> { 309 let src = core::str::from_utf8(src).map_err(|_| format_err!("invalid utf8 encoding"))?; 310 let mut amt = 0; 311 for (i, dst) in src.encode_utf16().zip(dst) { 312 *dst = i.to_le(); 313 amt += 1; 314 } 315 Ok(amt) 316 } 317 318 struct SizePair { 319 src_read: usize, 320 dst_written: usize, 321 } 322 323 unsafe impl HostResultHasUnwindSentinel for SizePair { 324 type Abi = (usize, usize); 325 const SENTINEL: (usize, usize) = (usize::MAX, 0); 326 fn into_abi(self) -> (usize, usize) { 327 (self.src_read, self.dst_written) 328 } 329 } 330 331 /// Converts utf16 to utf8. 332 /// 333 /// Each buffer is specified independently here and the returned value is a pair 334 /// of the number of code units read and code units written. This might perform 335 /// a partial transcode if the destination buffer is not large enough to hold 336 /// the entire contents. 337 unsafe fn utf16_to_utf8( 338 _: &mut dyn VMStore, 339 _: Instance, 340 src: *mut u16, 341 src_len: usize, 342 dst: *mut u8, 343 dst_len: usize, 344 ) -> Result<SizePair> { 345 let src = unsafe { slice::from_raw_parts(src, src_len) }; 346 let mut dst = unsafe { slice::from_raw_parts_mut(dst, dst_len) }; 347 assert_no_overlap(src, dst); 348 349 // This iterator will convert to native endianness and additionally count 350 // how many items have been read from the iterator so far. This 351 // count is used to return how many of the source code units were read. 352 let src_iter_read = Cell::new(0); 353 let src_iter = src.iter().map(|i| { 354 src_iter_read.set(src_iter_read.get() + 1); 355 u16::from_le(*i) 356 }); 357 358 let mut src_read = 0; 359 let mut dst_written = 0; 360 361 for ch in core::char::decode_utf16(src_iter) { 362 let ch = ch.map_err(|_| format_err!("invalid utf16 encoding"))?; 363 364 // If the destination doesn't have enough space for this character 365 // then the loop is ended and this function will be called later with a 366 // larger destination buffer. 367 if dst.len() < 4 && dst.len() < ch.len_utf8() { 368 break; 369 } 370 371 // Record that characters were read and then convert the `char` to 372 // utf-8, advancing the destination buffer. 373 src_read = src_iter_read.get(); 374 let len = ch.encode_utf8(dst).len(); 375 dst_written += len; 376 dst = &mut dst[len..]; 377 } 378 379 log::trace!("utf16-to-utf8 {src_len}/{dst_len} => {src_read}/{dst_written}"); 380 Ok(SizePair { 381 src_read, 382 dst_written, 383 }) 384 } 385 386 /// Converts latin1 to utf8. 387 /// 388 /// Receives the independent size of both buffers and returns the number of code 389 /// units read and code units written (both bytes in this case). 390 /// 391 /// This may perform a partial encoding if the destination is not large enough. 392 unsafe fn latin1_to_utf8( 393 _: &mut dyn VMStore, 394 _: Instance, 395 src: *mut u8, 396 src_len: usize, 397 dst: *mut u8, 398 dst_len: usize, 399 ) -> Result<SizePair> { 400 let src = unsafe { slice::from_raw_parts(src, src_len) }; 401 let dst = unsafe { slice::from_raw_parts_mut(dst, dst_len) }; 402 assert_no_overlap(src, dst); 403 let (read, written) = encoding_rs::mem::convert_latin1_to_utf8_partial(src, dst); 404 log::trace!("latin1-to-utf8 {src_len}/{dst_len} => ({read}, {written})"); 405 Ok(SizePair { 406 src_read: read, 407 dst_written: written, 408 }) 409 } 410 411 /// Converts utf16 to "latin1+utf16", probably using a utf16 encoding. 412 /// 413 /// The length specified is the length of both the source and destination 414 /// buffers. If the source string has any characters that don't fit in the 415 /// latin1 code space (0xff and below) then a utf16-tagged length will be 416 /// returned. Otherwise the string is "deflated" from a utf16 string to a latin1 417 /// string and the latin1 length is returned. 418 unsafe fn utf16_to_compact_probably_utf16( 419 _: &mut dyn VMStore, 420 _: Instance, 421 src: *mut u16, 422 len: usize, 423 dst: *mut u16, 424 ) -> Result<CopySizeReturn> { 425 let src = unsafe { slice::from_raw_parts(src, len) }; 426 let dst = unsafe { slice::from_raw_parts_mut(dst, len) }; 427 assert_no_overlap(src, dst); 428 let all_latin1 = run_utf16_to_utf16(src, dst)?; 429 if all_latin1 { 430 let (left, dst, right) = unsafe { dst.align_to_mut::<u8>() }; 431 assert!(left.is_empty()); 432 assert!(right.is_empty()); 433 for i in 0..len { 434 dst[i] = dst[2 * i]; 435 } 436 log::trace!("utf16-to-compact-probably-utf16 {len} => latin1 {len}"); 437 Ok(CopySizeReturn(len)) 438 } else { 439 log::trace!("utf16-to-compact-probably-utf16 {len} => utf16 {len}"); 440 Ok(CopySizeReturn(len | UTF16_TAG)) 441 } 442 } 443 444 /// Converts a utf8 string to latin1. 445 /// 446 /// The length specified is the same length of both the input and the output 447 /// buffers. 448 /// 449 /// Returns the number of code units read from the source and the number of code 450 /// units written to the destination. 451 /// 452 /// Note that this may not convert the entire source into the destination if the 453 /// original utf8 string has usvs not representable in latin1. 454 unsafe fn utf8_to_latin1( 455 _: &mut dyn VMStore, 456 _: Instance, 457 src: *mut u8, 458 len: usize, 459 dst: *mut u8, 460 ) -> Result<SizePair> { 461 let src = unsafe { slice::from_raw_parts(src, len) }; 462 let dst = unsafe { slice::from_raw_parts_mut(dst, len) }; 463 assert_no_overlap(src, dst); 464 let read = encoding_rs::mem::utf8_latin1_up_to(src); 465 let written = encoding_rs::mem::convert_utf8_to_latin1_lossy(&src[..read], dst); 466 log::trace!("utf8-to-latin1 {len} => ({read}, {written})"); 467 Ok(SizePair { 468 src_read: read, 469 dst_written: written, 470 }) 471 } 472 473 /// Converts a utf16 string to latin1 474 /// 475 /// This is the same as `utf8_to_latin1` in terms of parameters/results. 476 unsafe fn utf16_to_latin1( 477 _: &mut dyn VMStore, 478 _: Instance, 479 src: *mut u16, 480 len: usize, 481 dst: *mut u8, 482 ) -> Result<SizePair> { 483 let src = unsafe { slice::from_raw_parts(src, len) }; 484 let dst = unsafe { slice::from_raw_parts_mut(dst, len) }; 485 assert_no_overlap(src, dst); 486 487 let mut size = 0; 488 for (src, dst) in src.iter().zip(dst) { 489 let src = u16::from_le(*src); 490 match u8::try_from(src) { 491 Ok(src) => *dst = src, 492 Err(_) => break, 493 } 494 size += 1; 495 } 496 log::trace!("utf16-to-latin1 {len} => {size}"); 497 Ok(SizePair { 498 src_read: size, 499 dst_written: size, 500 }) 501 } 502 503 /// Converts a utf8 string to a utf16 string which has been partially converted 504 /// as latin1 prior. 505 /// 506 /// The original string has already been partially transcoded with 507 /// `utf8_to_latin1` and that was determined to not be able to transcode the 508 /// entire string. The substring of the source that couldn't be encoded into 509 /// latin1 is passed here via `src` and `src_len`. 510 /// 511 /// The destination buffer is specified by `dst` and `dst_len`. The first 512 /// `latin1_bytes_so_far` bytes (not code units) of the `dst` buffer have 513 /// already been filled in with latin1 characters and need to be inflated 514 /// in-place to their utf16 equivalents. 515 /// 516 /// After the initial latin1 code units have been inflated the entirety of `src` 517 /// is then transcoded into the remaining space within `dst`. 518 unsafe fn utf8_to_compact_utf16( 519 _: &mut dyn VMStore, 520 _: Instance, 521 src: *mut u8, 522 src_len: usize, 523 dst: *mut u16, 524 dst_len: usize, 525 latin1_bytes_so_far: usize, 526 ) -> Result<CopySizeReturn> { 527 let src = unsafe { slice::from_raw_parts(src, src_len) }; 528 let dst = unsafe { slice::from_raw_parts_mut(dst, dst_len) }; 529 assert_no_overlap(src, dst); 530 531 let dst = inflate_latin1_bytes(dst, latin1_bytes_so_far); 532 let result = run_utf8_to_utf16(src, dst)?; 533 log::trace!("utf8-to-compact-utf16 {src_len}/{dst_len}/{latin1_bytes_so_far} => {result}"); 534 Ok(CopySizeReturn(result + latin1_bytes_so_far)) 535 } 536 537 /// Same as `utf8_to_compact_utf16` but for utf16 source strings. 538 unsafe fn utf16_to_compact_utf16( 539 _: &mut dyn VMStore, 540 _: Instance, 541 src: *mut u16, 542 src_len: usize, 543 dst: *mut u16, 544 dst_len: usize, 545 latin1_bytes_so_far: usize, 546 ) -> Result<CopySizeReturn> { 547 let src = unsafe { slice::from_raw_parts(src, src_len) }; 548 let dst = unsafe { slice::from_raw_parts_mut(dst, dst_len) }; 549 assert_no_overlap(src, dst); 550 551 let dst = inflate_latin1_bytes(dst, latin1_bytes_so_far); 552 run_utf16_to_utf16(src, dst)?; 553 let result = src.len(); 554 log::trace!("utf16-to-compact-utf16 {src_len}/{dst_len}/{latin1_bytes_so_far} => {result}"); 555 Ok(CopySizeReturn(result + latin1_bytes_so_far)) 556 } 557 558 /// Inflates the `latin1_bytes_so_far` number of bytes written to the beginning 559 /// of `dst` into u16 codepoints. 560 /// 561 /// Returns the remaining space in the destination that can be transcoded into, 562 /// slicing off the prefix of the string that was inflated from the latin1 563 /// bytes. 564 fn inflate_latin1_bytes(dst: &mut [u16], latin1_bytes_so_far: usize) -> &mut [u16] { 565 // Note that `latin1_bytes_so_far` is a byte measure while `dst` is a region 566 // of u16 units. This `split_at_mut` uses the byte index as an index into 567 // the u16 unit because each of the latin1 bytes will become a whole code 568 // unit in the destination which is 2 bytes large. 569 let (to_inflate, rest) = dst.split_at_mut(latin1_bytes_so_far); 570 571 // Use a byte-oriented view to inflate the original latin1 bytes. 572 let (left, mid, right) = unsafe { to_inflate.align_to_mut::<u8>() }; 573 assert!(left.is_empty()); 574 assert!(right.is_empty()); 575 for i in (0..latin1_bytes_so_far).rev() { 576 mid[2 * i] = mid[i]; 577 mid[2 * i + 1] = 0; 578 } 579 580 return rest; 581 } 582 583 fn resource_new32( 584 store: &mut dyn VMStore, 585 instance: Instance, 586 _caller_instance: u32, 587 resource: u32, 588 rep: u32, 589 ) -> Result<u32> { 590 let resource = TypeResourceTableIndex::from_u32(resource); 591 instance.resource_new32(store, resource, rep) 592 } 593 594 fn resource_rep32( 595 store: &mut dyn VMStore, 596 instance: Instance, 597 _caller_instance: u32, 598 resource: u32, 599 idx: u32, 600 ) -> Result<u32> { 601 let resource = TypeResourceTableIndex::from_u32(resource); 602 instance.resource_rep32(store, resource, idx) 603 } 604 605 fn resource_drop( 606 store: &mut dyn VMStore, 607 instance: Instance, 608 _caller_instance: u32, 609 resource: u32, 610 idx: u32, 611 ) -> Result<ResourceDropRet> { 612 let resource = TypeResourceTableIndex::from_u32(resource); 613 Ok(ResourceDropRet( 614 instance.resource_drop(store, resource, idx)?, 615 )) 616 } 617 618 struct ResourceDropRet(Option<u32>); 619 620 unsafe impl HostResultHasUnwindSentinel for ResourceDropRet { 621 type Abi = u64; 622 const SENTINEL: u64 = u64::MAX; 623 fn into_abi(self) -> u64 { 624 match self.0 { 625 Some(rep) => (u64::from(rep) << 1) | 1, 626 None => 0, 627 } 628 } 629 } 630 631 fn resource_transfer_own( 632 store: &mut dyn VMStore, 633 instance: Instance, 634 src_idx: u32, 635 src_table: u32, 636 dst_table: u32, 637 ) -> Result<u32> { 638 let src_table = TypeResourceTableIndex::from_u32(src_table); 639 let dst_table = TypeResourceTableIndex::from_u32(dst_table); 640 instance.resource_transfer_own(store, src_idx, src_table, dst_table) 641 } 642 643 fn resource_transfer_borrow( 644 store: &mut dyn VMStore, 645 instance: Instance, 646 src_idx: u32, 647 src_table: u32, 648 dst_table: u32, 649 ) -> Result<u32> { 650 let src_table = TypeResourceTableIndex::from_u32(src_table); 651 let dst_table = TypeResourceTableIndex::from_u32(dst_table); 652 instance.resource_transfer_borrow(store, src_idx, src_table, dst_table) 653 } 654 655 fn trap(_store: &mut dyn VMStore, _instance: Instance, code: u32) -> Result<()> { 656 Err(wasmtime_environ::Trap::from_u8(u8::try_from(code).unwrap()) 657 .unwrap() 658 .into()) 659 } 660 661 fn enter_sync_call( 662 store: &mut dyn VMStore, 663 instance: Instance, 664 caller_instance: u32, 665 callee_async: u32, 666 callee_instance: u32, 667 ) -> Result<()> { 668 store.enter_guest_sync_call( 669 Some(RuntimeInstance { 670 instance: instance.id().instance(), 671 index: RuntimeComponentInstanceIndex::from_u32(caller_instance), 672 }), 673 callee_async != 0, 674 RuntimeInstance { 675 instance: instance.id().instance(), 676 index: RuntimeComponentInstanceIndex::from_u32(callee_instance), 677 }, 678 ) 679 } 680 681 fn exit_sync_call(store: &mut dyn VMStore, instance: Instance) -> Result<()> { 682 store 683 .component_resource_tables(Some(instance)) 684 .validate_scope_exit()?; 685 store.exit_guest_sync_call() 686 } 687 688 #[cfg(feature = "component-model-async")] 689 fn backpressure_modify( 690 store: &mut dyn VMStore, 691 instance: Instance, 692 caller_instance: u32, 693 increment: u8, 694 ) -> Result<()> { 695 store.backpressure_modify( 696 RuntimeInstance { 697 instance: instance.id().instance(), 698 index: RuntimeComponentInstanceIndex::from_u32(caller_instance), 699 }, 700 |old| { 701 if increment != 0 { 702 old.checked_add(1) 703 } else { 704 old.checked_sub(1) 705 } 706 }, 707 ) 708 } 709 710 #[cfg(feature = "component-model-async")] 711 unsafe fn task_return( 712 store: &mut dyn VMStore, 713 instance: Instance, 714 _caller_instance: u32, 715 ty: u32, 716 options: u32, 717 storage: *mut u8, 718 storage_len: usize, 719 ) -> Result<()> { 720 instance.task_return( 721 store, 722 TypeTupleIndex::from_u32(ty), 723 OptionsIndex::from_u32(options), 724 unsafe { core::slice::from_raw_parts(storage.cast(), storage_len) }, 725 ) 726 } 727 728 #[cfg(feature = "component-model-async")] 729 fn task_cancel(store: &mut dyn VMStore, instance: Instance, _caller_instance: u32) -> Result<()> { 730 instance.task_cancel(store) 731 } 732 733 #[cfg(feature = "component-model-async")] 734 fn waitable_set_new( 735 store: &mut dyn VMStore, 736 instance: Instance, 737 caller_instance: u32, 738 ) -> Result<u32> { 739 instance.waitable_set_new( 740 store, 741 RuntimeComponentInstanceIndex::from_u32(caller_instance), 742 ) 743 } 744 745 #[cfg(feature = "component-model-async")] 746 fn waitable_set_wait( 747 store: &mut dyn VMStore, 748 instance: Instance, 749 _caller: u32, 750 options: u32, 751 set: u32, 752 payload: u32, 753 ) -> Result<u32> { 754 instance.waitable_set_wait(store, OptionsIndex::from_u32(options), set, payload) 755 } 756 757 #[cfg(feature = "component-model-async")] 758 fn waitable_set_poll( 759 store: &mut dyn VMStore, 760 instance: Instance, 761 _caller: u32, 762 options: u32, 763 set: u32, 764 payload: u32, 765 ) -> Result<u32> { 766 instance.waitable_set_poll(store, OptionsIndex::from_u32(options), set, payload) 767 } 768 769 #[cfg(feature = "component-model-async")] 770 fn waitable_set_drop( 771 store: &mut dyn VMStore, 772 instance: Instance, 773 caller_instance: u32, 774 set: u32, 775 ) -> Result<()> { 776 instance.waitable_set_drop( 777 store, 778 RuntimeComponentInstanceIndex::from_u32(caller_instance), 779 set, 780 ) 781 } 782 783 #[cfg(feature = "component-model-async")] 784 fn waitable_join( 785 store: &mut dyn VMStore, 786 instance: Instance, 787 caller_instance: u32, 788 waitable: u32, 789 set: u32, 790 ) -> Result<()> { 791 instance.waitable_join( 792 store, 793 RuntimeComponentInstanceIndex::from_u32(caller_instance), 794 waitable, 795 set, 796 ) 797 } 798 799 #[cfg(feature = "component-model-async")] 800 fn thread_yield( 801 store: &mut dyn VMStore, 802 instance: Instance, 803 caller_instance: u32, 804 cancellable: u8, 805 ) -> Result<bool> { 806 instance 807 .suspension_intrinsic( 808 store, 809 RuntimeComponentInstanceIndex::from_u32(caller_instance), 810 cancellable != 0, 811 true, 812 SuspensionTarget::None, 813 ) 814 .map(|r| r == WaitResult::Cancelled) 815 } 816 817 #[cfg(feature = "component-model-async")] 818 fn subtask_drop( 819 store: &mut dyn VMStore, 820 instance: Instance, 821 caller_instance: u32, 822 task_id: u32, 823 ) -> Result<()> { 824 instance.subtask_drop( 825 store, 826 RuntimeComponentInstanceIndex::from_u32(caller_instance), 827 task_id, 828 ) 829 } 830 831 #[cfg(feature = "component-model-async")] 832 fn subtask_cancel( 833 store: &mut dyn VMStore, 834 instance: Instance, 835 caller_instance: u32, 836 async_: u8, 837 task_id: u32, 838 ) -> Result<u32> { 839 instance.subtask_cancel( 840 store, 841 RuntimeComponentInstanceIndex::from_u32(caller_instance), 842 async_ != 0, 843 task_id, 844 ) 845 } 846 847 #[cfg(feature = "component-model-async")] 848 unsafe fn prepare_call( 849 store: &mut dyn VMStore, 850 instance: Instance, 851 memory: *mut u8, 852 start: *mut u8, 853 return_: *mut u8, 854 caller_instance: u32, 855 callee_instance: u32, 856 task_return_type: u32, 857 callee_async: u32, 858 string_encoding: u32, 859 result_count_or_max_if_async: u32, 860 storage: *mut u8, 861 storage_len: usize, 862 ) -> Result<()> { 863 unsafe { 864 store.component_async_store().prepare_call( 865 instance, 866 memory.cast::<crate::vm::VMMemoryDefinition>(), 867 NonNull::new(start).unwrap().cast::<crate::vm::VMFuncRef>(), 868 NonNull::new(return_) 869 .unwrap() 870 .cast::<crate::vm::VMFuncRef>(), 871 RuntimeComponentInstanceIndex::from_u32(caller_instance), 872 RuntimeComponentInstanceIndex::from_u32(callee_instance), 873 TypeTupleIndex::from_u32(task_return_type), 874 callee_async != 0, 875 StringEncoding::from_u8(u8::try_from(string_encoding).unwrap()).unwrap(), 876 result_count_or_max_if_async, 877 storage.cast::<crate::ValRaw>(), 878 storage_len, 879 ) 880 } 881 } 882 883 #[cfg(feature = "component-model-async")] 884 unsafe fn sync_start( 885 store: &mut dyn VMStore, 886 instance: Instance, 887 callback: *mut u8, 888 storage: *mut u8, 889 storage_len: usize, 890 callee: *mut u8, 891 param_count: u32, 892 ) -> Result<()> { 893 unsafe { 894 store.component_async_store().sync_start( 895 instance, 896 callback.cast::<crate::vm::VMFuncRef>(), 897 NonNull::new(callee).unwrap().cast::<crate::vm::VMFuncRef>(), 898 param_count, 899 storage.cast::<std::mem::MaybeUninit<crate::ValRaw>>(), 900 storage_len, 901 ) 902 } 903 } 904 905 #[cfg(feature = "component-model-async")] 906 unsafe fn async_start( 907 store: &mut dyn VMStore, 908 instance: Instance, 909 callback: *mut u8, 910 post_return: *mut u8, 911 callee: *mut u8, 912 param_count: u32, 913 result_count: u32, 914 flags: u32, 915 ) -> Result<u32> { 916 unsafe { 917 store.component_async_store().async_start( 918 instance, 919 callback.cast::<crate::vm::VMFuncRef>(), 920 post_return.cast::<crate::vm::VMFuncRef>(), 921 NonNull::new(callee).unwrap().cast::<crate::vm::VMFuncRef>(), 922 param_count, 923 result_count, 924 flags, 925 ) 926 } 927 } 928 929 #[cfg(feature = "component-model-async")] 930 fn future_transfer( 931 store: &mut dyn VMStore, 932 instance: Instance, 933 src_idx: u32, 934 src_table: u32, 935 dst_table: u32, 936 ) -> Result<u32> { 937 instance.future_transfer( 938 store, 939 src_idx, 940 TypeFutureTableIndex::from_u32(src_table), 941 TypeFutureTableIndex::from_u32(dst_table), 942 ) 943 } 944 945 #[cfg(feature = "component-model-async")] 946 fn stream_transfer( 947 store: &mut dyn VMStore, 948 instance: Instance, 949 src_idx: u32, 950 src_table: u32, 951 dst_table: u32, 952 ) -> Result<u32> { 953 instance.stream_transfer( 954 store, 955 src_idx, 956 TypeStreamTableIndex::from_u32(src_table), 957 TypeStreamTableIndex::from_u32(dst_table), 958 ) 959 } 960 961 #[cfg(feature = "component-model-async")] 962 fn error_context_transfer( 963 store: &mut dyn VMStore, 964 instance: Instance, 965 src_idx: u32, 966 src_table: u32, 967 dst_table: u32, 968 ) -> Result<u32> { 969 let src_table = TypeComponentLocalErrorContextTableIndex::from_u32(src_table); 970 let dst_table = TypeComponentLocalErrorContextTableIndex::from_u32(dst_table); 971 instance.error_context_transfer(store, src_idx, src_table, dst_table) 972 } 973 974 #[cfg(feature = "component-model-async")] 975 unsafe impl HostResultHasUnwindSentinel for ResourcePair { 976 type Abi = u64; 977 const SENTINEL: u64 = u64::MAX; 978 979 fn into_abi(self) -> Self::Abi { 980 assert!(self.write & (1 << 31) == 0); 981 (u64::from(self.write) << 32) | u64::from(self.read) 982 } 983 } 984 985 #[cfg(feature = "component-model-async")] 986 fn future_new( 987 store: &mut dyn VMStore, 988 instance: Instance, 989 _caller_instance: u32, 990 ty: u32, 991 ) -> Result<ResourcePair> { 992 instance.future_new(store, TypeFutureTableIndex::from_u32(ty)) 993 } 994 995 #[cfg(feature = "component-model-async")] 996 fn future_write( 997 store: &mut dyn VMStore, 998 instance: Instance, 999 caller_instance: u32, 1000 ty: u32, 1001 options: u32, 1002 future: u32, 1003 address: u32, 1004 ) -> Result<u32> { 1005 store.component_async_store().future_write( 1006 instance, 1007 RuntimeComponentInstanceIndex::from_u32(caller_instance), 1008 TypeFutureTableIndex::from_u32(ty), 1009 OptionsIndex::from_u32(options), 1010 future, 1011 address, 1012 ) 1013 } 1014 1015 #[cfg(feature = "component-model-async")] 1016 fn future_read( 1017 store: &mut dyn VMStore, 1018 instance: Instance, 1019 caller_instance: u32, 1020 ty: u32, 1021 options: u32, 1022 future: u32, 1023 address: u32, 1024 ) -> Result<u32> { 1025 store.component_async_store().future_read( 1026 instance, 1027 RuntimeComponentInstanceIndex::from_u32(caller_instance), 1028 TypeFutureTableIndex::from_u32(ty), 1029 OptionsIndex::from_u32(options), 1030 future, 1031 address, 1032 ) 1033 } 1034 1035 #[cfg(feature = "component-model-async")] 1036 fn future_cancel_write( 1037 store: &mut dyn VMStore, 1038 instance: Instance, 1039 _caller_instance: u32, 1040 ty: u32, 1041 async_: u8, 1042 writer: u32, 1043 ) -> Result<u32> { 1044 instance.future_cancel_write( 1045 store, 1046 TypeFutureTableIndex::from_u32(ty), 1047 async_ != 0, 1048 writer, 1049 ) 1050 } 1051 1052 #[cfg(feature = "component-model-async")] 1053 fn future_cancel_read( 1054 store: &mut dyn VMStore, 1055 instance: Instance, 1056 _caller_instance: u32, 1057 ty: u32, 1058 async_: u8, 1059 reader: u32, 1060 ) -> Result<u32> { 1061 instance.future_cancel_read( 1062 store, 1063 TypeFutureTableIndex::from_u32(ty), 1064 async_ != 0, 1065 reader, 1066 ) 1067 } 1068 1069 #[cfg(feature = "component-model-async")] 1070 fn future_drop_writable( 1071 store: &mut dyn VMStore, 1072 instance: Instance, 1073 _caller_instance: u32, 1074 ty: u32, 1075 writer: u32, 1076 ) -> Result<()> { 1077 store.component_async_store().future_drop_writable( 1078 instance, 1079 TypeFutureTableIndex::from_u32(ty), 1080 writer, 1081 ) 1082 } 1083 1084 #[cfg(feature = "component-model-async")] 1085 fn future_drop_readable( 1086 store: &mut dyn VMStore, 1087 instance: Instance, 1088 _caller_instance: u32, 1089 ty: u32, 1090 reader: u32, 1091 ) -> Result<()> { 1092 instance.future_drop_readable(store, TypeFutureTableIndex::from_u32(ty), reader) 1093 } 1094 1095 #[cfg(feature = "component-model-async")] 1096 fn stream_new( 1097 store: &mut dyn VMStore, 1098 instance: Instance, 1099 _caller_instance: u32, 1100 ty: u32, 1101 ) -> Result<ResourcePair> { 1102 instance.stream_new(store, TypeStreamTableIndex::from_u32(ty)) 1103 } 1104 1105 #[cfg(feature = "component-model-async")] 1106 fn stream_write( 1107 store: &mut dyn VMStore, 1108 instance: Instance, 1109 caller_instance: u32, 1110 ty: u32, 1111 options: u32, 1112 stream: u32, 1113 address: u32, 1114 count: u32, 1115 ) -> Result<u32> { 1116 store.component_async_store().stream_write( 1117 instance, 1118 RuntimeComponentInstanceIndex::from_u32(caller_instance), 1119 TypeStreamTableIndex::from_u32(ty), 1120 OptionsIndex::from_u32(options), 1121 stream, 1122 address, 1123 count, 1124 ) 1125 } 1126 1127 #[cfg(feature = "component-model-async")] 1128 fn stream_read( 1129 store: &mut dyn VMStore, 1130 instance: Instance, 1131 caller_instance: u32, 1132 ty: u32, 1133 options: u32, 1134 stream: u32, 1135 address: u32, 1136 count: u32, 1137 ) -> Result<u32> { 1138 store.component_async_store().stream_read( 1139 instance, 1140 RuntimeComponentInstanceIndex::from_u32(caller_instance), 1141 TypeStreamTableIndex::from_u32(ty), 1142 OptionsIndex::from_u32(options), 1143 stream, 1144 address, 1145 count, 1146 ) 1147 } 1148 1149 #[cfg(feature = "component-model-async")] 1150 fn stream_cancel_write( 1151 store: &mut dyn VMStore, 1152 instance: Instance, 1153 _caller_instance: u32, 1154 ty: u32, 1155 async_: u8, 1156 writer: u32, 1157 ) -> Result<u32> { 1158 instance.stream_cancel_write( 1159 store, 1160 TypeStreamTableIndex::from_u32(ty), 1161 async_ != 0, 1162 writer, 1163 ) 1164 } 1165 1166 #[cfg(feature = "component-model-async")] 1167 fn stream_cancel_read( 1168 store: &mut dyn VMStore, 1169 instance: Instance, 1170 _caller_instance: u32, 1171 ty: u32, 1172 async_: u8, 1173 reader: u32, 1174 ) -> Result<u32> { 1175 instance.stream_cancel_read( 1176 store, 1177 TypeStreamTableIndex::from_u32(ty), 1178 async_ != 0, 1179 reader, 1180 ) 1181 } 1182 1183 #[cfg(feature = "component-model-async")] 1184 fn stream_drop_writable( 1185 store: &mut dyn VMStore, 1186 instance: Instance, 1187 _caller_instance: u32, 1188 ty: u32, 1189 writer: u32, 1190 ) -> Result<()> { 1191 store.component_async_store().stream_drop_writable( 1192 instance, 1193 TypeStreamTableIndex::from_u32(ty), 1194 writer, 1195 ) 1196 } 1197 1198 #[cfg(feature = "component-model-async")] 1199 fn stream_drop_readable( 1200 store: &mut dyn VMStore, 1201 instance: Instance, 1202 _caller_instance: u32, 1203 ty: u32, 1204 reader: u32, 1205 ) -> Result<()> { 1206 instance.stream_drop_readable(store, TypeStreamTableIndex::from_u32(ty), reader) 1207 } 1208 1209 #[cfg(feature = "component-model-async")] 1210 fn flat_stream_write( 1211 store: &mut dyn VMStore, 1212 instance: Instance, 1213 caller_instance: u32, 1214 ty: u32, 1215 options: u32, 1216 payload_size: u32, 1217 payload_align: u32, 1218 stream: u32, 1219 address: u32, 1220 count: u32, 1221 ) -> Result<u32> { 1222 store.component_async_store().flat_stream_write( 1223 instance, 1224 RuntimeComponentInstanceIndex::from_u32(caller_instance), 1225 TypeStreamTableIndex::from_u32(ty), 1226 OptionsIndex::from_u32(options), 1227 payload_size, 1228 payload_align, 1229 stream, 1230 address, 1231 count, 1232 ) 1233 } 1234 1235 #[cfg(feature = "component-model-async")] 1236 fn flat_stream_read( 1237 store: &mut dyn VMStore, 1238 instance: Instance, 1239 caller_instance: u32, 1240 ty: u32, 1241 options: u32, 1242 payload_size: u32, 1243 payload_align: u32, 1244 stream: u32, 1245 address: u32, 1246 count: u32, 1247 ) -> Result<u32> { 1248 store.component_async_store().flat_stream_read( 1249 instance, 1250 RuntimeComponentInstanceIndex::from_u32(caller_instance), 1251 TypeStreamTableIndex::from_u32(ty), 1252 OptionsIndex::from_u32(options), 1253 payload_size, 1254 payload_align, 1255 stream, 1256 address, 1257 count, 1258 ) 1259 } 1260 1261 #[cfg(feature = "component-model-async")] 1262 fn error_context_new( 1263 store: &mut dyn VMStore, 1264 instance: Instance, 1265 _caller_instance: u32, 1266 ty: u32, 1267 options: u32, 1268 debug_msg_address: u32, 1269 debug_msg_len: u32, 1270 ) -> Result<u32> { 1271 instance.error_context_new( 1272 store.store_opaque_mut(), 1273 TypeComponentLocalErrorContextTableIndex::from_u32(ty), 1274 OptionsIndex::from_u32(options), 1275 debug_msg_address, 1276 debug_msg_len, 1277 ) 1278 } 1279 1280 #[cfg(feature = "component-model-async")] 1281 fn error_context_debug_message( 1282 store: &mut dyn VMStore, 1283 instance: Instance, 1284 _caller_instance: u32, 1285 ty: u32, 1286 options: u32, 1287 err_ctx_handle: u32, 1288 debug_msg_address: u32, 1289 ) -> Result<()> { 1290 store.component_async_store().error_context_debug_message( 1291 instance, 1292 TypeComponentLocalErrorContextTableIndex::from_u32(ty), 1293 OptionsIndex::from_u32(options), 1294 err_ctx_handle, 1295 debug_msg_address, 1296 ) 1297 } 1298 1299 #[cfg(feature = "component-model-async")] 1300 fn error_context_drop( 1301 store: &mut dyn VMStore, 1302 instance: Instance, 1303 _caller_instance: u32, 1304 ty: u32, 1305 err_ctx_handle: u32, 1306 ) -> Result<()> { 1307 instance.error_context_drop( 1308 store, 1309 TypeComponentLocalErrorContextTableIndex::from_u32(ty), 1310 err_ctx_handle, 1311 ) 1312 } 1313 1314 #[cfg(feature = "component-model-async")] 1315 fn context_get( 1316 store: &mut dyn VMStore, 1317 instance: Instance, 1318 _caller_instance: u32, 1319 slot: u32, 1320 ) -> Result<u32> { 1321 instance.context_get(store, slot) 1322 } 1323 1324 #[cfg(feature = "component-model-async")] 1325 fn context_set( 1326 store: &mut dyn VMStore, 1327 instance: Instance, 1328 _caller_instance: u32, 1329 slot: u32, 1330 val: u32, 1331 ) -> Result<()> { 1332 instance.context_set(store, slot, val) 1333 } 1334 1335 #[cfg(feature = "component-model-async")] 1336 fn thread_index(store: &mut dyn VMStore, instance: Instance) -> Result<u32> { 1337 instance.thread_index(store) 1338 } 1339 1340 #[cfg(feature = "component-model-async")] 1341 fn thread_new_indirect( 1342 store: &mut dyn VMStore, 1343 instance: Instance, 1344 caller: u32, 1345 func_ty_id: u32, 1346 func_table_idx: u32, 1347 func_idx: u32, 1348 context: u32, 1349 ) -> Result<u32> { 1350 store.component_async_store().thread_new_indirect( 1351 instance, 1352 RuntimeComponentInstanceIndex::from_u32(caller), 1353 TypeFuncIndex::from_u32(func_ty_id), 1354 RuntimeTableIndex::from_u32(func_table_idx), 1355 func_idx, 1356 context as i32, 1357 ) 1358 } 1359 1360 #[cfg(feature = "component-model-async")] 1361 fn thread_suspend_to_suspended( 1362 store: &mut dyn VMStore, 1363 instance: Instance, 1364 caller: u32, 1365 cancellable: u8, 1366 thread_idx: u32, 1367 ) -> Result<bool> { 1368 instance 1369 .suspension_intrinsic( 1370 store, 1371 RuntimeComponentInstanceIndex::from_u32(caller), 1372 cancellable != 0, 1373 false, 1374 SuspensionTarget::SomeSuspended(thread_idx), 1375 ) 1376 .map(|r| r == WaitResult::Cancelled) 1377 } 1378 1379 #[cfg(feature = "component-model-async")] 1380 fn thread_suspend_to( 1381 store: &mut dyn VMStore, 1382 instance: Instance, 1383 caller: u32, 1384 cancellable: u8, 1385 thread_idx: u32, 1386 ) -> Result<bool> { 1387 instance 1388 .suspension_intrinsic( 1389 store, 1390 RuntimeComponentInstanceIndex::from_u32(caller), 1391 cancellable != 0, 1392 false, 1393 SuspensionTarget::Some(thread_idx), 1394 ) 1395 .map(|r| r == WaitResult::Cancelled) 1396 } 1397 1398 #[cfg(feature = "component-model-async")] 1399 fn thread_suspend( 1400 store: &mut dyn VMStore, 1401 instance: Instance, 1402 caller: u32, 1403 cancellable: u8, 1404 ) -> Result<bool> { 1405 instance 1406 .suspension_intrinsic( 1407 store, 1408 RuntimeComponentInstanceIndex::from_u32(caller), 1409 cancellable != 0, 1410 false, 1411 SuspensionTarget::None, 1412 ) 1413 .map(|r| r == WaitResult::Cancelled) 1414 } 1415 1416 #[cfg(feature = "component-model-async")] 1417 fn thread_unsuspend( 1418 store: &mut dyn VMStore, 1419 instance: Instance, 1420 caller_instance: u32, 1421 thread_idx: u32, 1422 ) -> Result<()> { 1423 instance.resume_thread( 1424 store, 1425 RuntimeComponentInstanceIndex::from_u32(caller_instance), 1426 thread_idx, 1427 false, 1428 false, 1429 ) 1430 } 1431 1432 #[cfg(feature = "component-model-async")] 1433 fn thread_yield_to_suspended( 1434 store: &mut dyn VMStore, 1435 instance: Instance, 1436 caller_instance: u32, 1437 cancellable: u8, 1438 thread_idx: u32, 1439 ) -> Result<bool> { 1440 instance 1441 .suspension_intrinsic( 1442 store, 1443 RuntimeComponentInstanceIndex::from_u32(caller_instance), 1444 cancellable != 0, 1445 true, 1446 SuspensionTarget::SomeSuspended(thread_idx), 1447 ) 1448 .map(|r| r == WaitResult::Cancelled) 1449 } 1450