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(true) 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 start.cast::<crate::vm::VMFuncRef>(), 868 return_.cast::<crate::vm::VMFuncRef>(), 869 RuntimeComponentInstanceIndex::from_u32(caller_instance), 870 RuntimeComponentInstanceIndex::from_u32(callee_instance), 871 TypeTupleIndex::from_u32(task_return_type), 872 callee_async != 0, 873 u8::try_from(string_encoding).unwrap(), 874 result_count_or_max_if_async, 875 storage.cast::<crate::ValRaw>(), 876 storage_len, 877 ) 878 } 879 } 880 881 #[cfg(feature = "component-model-async")] 882 unsafe fn sync_start( 883 store: &mut dyn VMStore, 884 instance: Instance, 885 callback: *mut u8, 886 storage: *mut u8, 887 storage_len: usize, 888 callee: *mut u8, 889 param_count: u32, 890 ) -> Result<()> { 891 unsafe { 892 store.component_async_store().sync_start( 893 instance, 894 callback.cast::<crate::vm::VMFuncRef>(), 895 callee.cast::<crate::vm::VMFuncRef>(), 896 param_count, 897 storage.cast::<std::mem::MaybeUninit<crate::ValRaw>>(), 898 storage_len, 899 ) 900 } 901 } 902 903 #[cfg(feature = "component-model-async")] 904 unsafe fn async_start( 905 store: &mut dyn VMStore, 906 instance: Instance, 907 callback: *mut u8, 908 post_return: *mut u8, 909 callee: *mut u8, 910 param_count: u32, 911 result_count: u32, 912 flags: u32, 913 ) -> Result<u32> { 914 unsafe { 915 store.component_async_store().async_start( 916 instance, 917 callback.cast::<crate::vm::VMFuncRef>(), 918 post_return.cast::<crate::vm::VMFuncRef>(), 919 callee.cast::<crate::vm::VMFuncRef>(), 920 param_count, 921 result_count, 922 flags, 923 ) 924 } 925 } 926 927 #[cfg(feature = "component-model-async")] 928 fn future_transfer( 929 store: &mut dyn VMStore, 930 instance: Instance, 931 src_idx: u32, 932 src_table: u32, 933 dst_table: u32, 934 ) -> Result<u32> { 935 instance.future_transfer( 936 store, 937 src_idx, 938 TypeFutureTableIndex::from_u32(src_table), 939 TypeFutureTableIndex::from_u32(dst_table), 940 ) 941 } 942 943 #[cfg(feature = "component-model-async")] 944 fn stream_transfer( 945 store: &mut dyn VMStore, 946 instance: Instance, 947 src_idx: u32, 948 src_table: u32, 949 dst_table: u32, 950 ) -> Result<u32> { 951 instance.stream_transfer( 952 store, 953 src_idx, 954 TypeStreamTableIndex::from_u32(src_table), 955 TypeStreamTableIndex::from_u32(dst_table), 956 ) 957 } 958 959 #[cfg(feature = "component-model-async")] 960 fn error_context_transfer( 961 store: &mut dyn VMStore, 962 instance: Instance, 963 src_idx: u32, 964 src_table: u32, 965 dst_table: u32, 966 ) -> Result<u32> { 967 let src_table = TypeComponentLocalErrorContextTableIndex::from_u32(src_table); 968 let dst_table = TypeComponentLocalErrorContextTableIndex::from_u32(dst_table); 969 instance.error_context_transfer(store, src_idx, src_table, dst_table) 970 } 971 972 #[cfg(feature = "component-model-async")] 973 unsafe impl HostResultHasUnwindSentinel for ResourcePair { 974 type Abi = u64; 975 const SENTINEL: u64 = u64::MAX; 976 977 fn into_abi(self) -> Self::Abi { 978 assert!(self.write & (1 << 31) == 0); 979 (u64::from(self.write) << 32) | u64::from(self.read) 980 } 981 } 982 983 #[cfg(feature = "component-model-async")] 984 fn future_new( 985 store: &mut dyn VMStore, 986 instance: Instance, 987 _caller_instance: u32, 988 ty: u32, 989 ) -> Result<ResourcePair> { 990 instance.future_new(store, TypeFutureTableIndex::from_u32(ty)) 991 } 992 993 #[cfg(feature = "component-model-async")] 994 fn future_write( 995 store: &mut dyn VMStore, 996 instance: Instance, 997 caller_instance: u32, 998 ty: u32, 999 options: u32, 1000 future: u32, 1001 address: u32, 1002 ) -> Result<u32> { 1003 store.component_async_store().future_write( 1004 instance, 1005 RuntimeComponentInstanceIndex::from_u32(caller_instance), 1006 TypeFutureTableIndex::from_u32(ty), 1007 OptionsIndex::from_u32(options), 1008 future, 1009 address, 1010 ) 1011 } 1012 1013 #[cfg(feature = "component-model-async")] 1014 fn future_read( 1015 store: &mut dyn VMStore, 1016 instance: Instance, 1017 caller_instance: u32, 1018 ty: u32, 1019 options: u32, 1020 future: u32, 1021 address: u32, 1022 ) -> Result<u32> { 1023 store.component_async_store().future_read( 1024 instance, 1025 RuntimeComponentInstanceIndex::from_u32(caller_instance), 1026 TypeFutureTableIndex::from_u32(ty), 1027 OptionsIndex::from_u32(options), 1028 future, 1029 address, 1030 ) 1031 } 1032 1033 #[cfg(feature = "component-model-async")] 1034 fn future_cancel_write( 1035 store: &mut dyn VMStore, 1036 instance: Instance, 1037 _caller_instance: u32, 1038 ty: u32, 1039 async_: u8, 1040 writer: u32, 1041 ) -> Result<u32> { 1042 instance.future_cancel_write( 1043 store, 1044 TypeFutureTableIndex::from_u32(ty), 1045 async_ != 0, 1046 writer, 1047 ) 1048 } 1049 1050 #[cfg(feature = "component-model-async")] 1051 fn future_cancel_read( 1052 store: &mut dyn VMStore, 1053 instance: Instance, 1054 _caller_instance: u32, 1055 ty: u32, 1056 async_: u8, 1057 reader: u32, 1058 ) -> Result<u32> { 1059 instance.future_cancel_read( 1060 store, 1061 TypeFutureTableIndex::from_u32(ty), 1062 async_ != 0, 1063 reader, 1064 ) 1065 } 1066 1067 #[cfg(feature = "component-model-async")] 1068 fn future_drop_writable( 1069 store: &mut dyn VMStore, 1070 instance: Instance, 1071 _caller_instance: u32, 1072 ty: u32, 1073 writer: u32, 1074 ) -> Result<()> { 1075 store.component_async_store().future_drop_writable( 1076 instance, 1077 TypeFutureTableIndex::from_u32(ty), 1078 writer, 1079 ) 1080 } 1081 1082 #[cfg(feature = "component-model-async")] 1083 fn future_drop_readable( 1084 store: &mut dyn VMStore, 1085 instance: Instance, 1086 _caller_instance: u32, 1087 ty: u32, 1088 reader: u32, 1089 ) -> Result<()> { 1090 instance.future_drop_readable(store, TypeFutureTableIndex::from_u32(ty), reader) 1091 } 1092 1093 #[cfg(feature = "component-model-async")] 1094 fn stream_new( 1095 store: &mut dyn VMStore, 1096 instance: Instance, 1097 _caller_instance: u32, 1098 ty: u32, 1099 ) -> Result<ResourcePair> { 1100 instance.stream_new(store, TypeStreamTableIndex::from_u32(ty)) 1101 } 1102 1103 #[cfg(feature = "component-model-async")] 1104 fn stream_write( 1105 store: &mut dyn VMStore, 1106 instance: Instance, 1107 caller_instance: u32, 1108 ty: u32, 1109 options: u32, 1110 stream: u32, 1111 address: u32, 1112 count: u32, 1113 ) -> Result<u32> { 1114 store.component_async_store().stream_write( 1115 instance, 1116 RuntimeComponentInstanceIndex::from_u32(caller_instance), 1117 TypeStreamTableIndex::from_u32(ty), 1118 OptionsIndex::from_u32(options), 1119 stream, 1120 address, 1121 count, 1122 ) 1123 } 1124 1125 #[cfg(feature = "component-model-async")] 1126 fn stream_read( 1127 store: &mut dyn VMStore, 1128 instance: Instance, 1129 caller_instance: u32, 1130 ty: u32, 1131 options: u32, 1132 stream: u32, 1133 address: u32, 1134 count: u32, 1135 ) -> Result<u32> { 1136 store.component_async_store().stream_read( 1137 instance, 1138 RuntimeComponentInstanceIndex::from_u32(caller_instance), 1139 TypeStreamTableIndex::from_u32(ty), 1140 OptionsIndex::from_u32(options), 1141 stream, 1142 address, 1143 count, 1144 ) 1145 } 1146 1147 #[cfg(feature = "component-model-async")] 1148 fn stream_cancel_write( 1149 store: &mut dyn VMStore, 1150 instance: Instance, 1151 _caller_instance: u32, 1152 ty: u32, 1153 async_: u8, 1154 writer: u32, 1155 ) -> Result<u32> { 1156 instance.stream_cancel_write( 1157 store, 1158 TypeStreamTableIndex::from_u32(ty), 1159 async_ != 0, 1160 writer, 1161 ) 1162 } 1163 1164 #[cfg(feature = "component-model-async")] 1165 fn stream_cancel_read( 1166 store: &mut dyn VMStore, 1167 instance: Instance, 1168 _caller_instance: u32, 1169 ty: u32, 1170 async_: u8, 1171 reader: u32, 1172 ) -> Result<u32> { 1173 instance.stream_cancel_read( 1174 store, 1175 TypeStreamTableIndex::from_u32(ty), 1176 async_ != 0, 1177 reader, 1178 ) 1179 } 1180 1181 #[cfg(feature = "component-model-async")] 1182 fn stream_drop_writable( 1183 store: &mut dyn VMStore, 1184 instance: Instance, 1185 _caller_instance: u32, 1186 ty: u32, 1187 writer: u32, 1188 ) -> Result<()> { 1189 store.component_async_store().stream_drop_writable( 1190 instance, 1191 TypeStreamTableIndex::from_u32(ty), 1192 writer, 1193 ) 1194 } 1195 1196 #[cfg(feature = "component-model-async")] 1197 fn stream_drop_readable( 1198 store: &mut dyn VMStore, 1199 instance: Instance, 1200 _caller_instance: u32, 1201 ty: u32, 1202 reader: u32, 1203 ) -> Result<()> { 1204 instance.stream_drop_readable(store, TypeStreamTableIndex::from_u32(ty), reader) 1205 } 1206 1207 #[cfg(feature = "component-model-async")] 1208 fn flat_stream_write( 1209 store: &mut dyn VMStore, 1210 instance: Instance, 1211 caller_instance: u32, 1212 ty: u32, 1213 options: u32, 1214 payload_size: u32, 1215 payload_align: u32, 1216 stream: u32, 1217 address: u32, 1218 count: u32, 1219 ) -> Result<u32> { 1220 store.component_async_store().flat_stream_write( 1221 instance, 1222 RuntimeComponentInstanceIndex::from_u32(caller_instance), 1223 TypeStreamTableIndex::from_u32(ty), 1224 OptionsIndex::from_u32(options), 1225 payload_size, 1226 payload_align, 1227 stream, 1228 address, 1229 count, 1230 ) 1231 } 1232 1233 #[cfg(feature = "component-model-async")] 1234 fn flat_stream_read( 1235 store: &mut dyn VMStore, 1236 instance: Instance, 1237 caller_instance: u32, 1238 ty: u32, 1239 options: u32, 1240 payload_size: u32, 1241 payload_align: u32, 1242 stream: u32, 1243 address: u32, 1244 count: u32, 1245 ) -> Result<u32> { 1246 store.component_async_store().flat_stream_read( 1247 instance, 1248 RuntimeComponentInstanceIndex::from_u32(caller_instance), 1249 TypeStreamTableIndex::from_u32(ty), 1250 OptionsIndex::from_u32(options), 1251 payload_size, 1252 payload_align, 1253 stream, 1254 address, 1255 count, 1256 ) 1257 } 1258 1259 #[cfg(feature = "component-model-async")] 1260 fn error_context_new( 1261 store: &mut dyn VMStore, 1262 instance: Instance, 1263 _caller_instance: u32, 1264 ty: u32, 1265 options: u32, 1266 debug_msg_address: u32, 1267 debug_msg_len: u32, 1268 ) -> Result<u32> { 1269 instance.error_context_new( 1270 store.store_opaque_mut(), 1271 TypeComponentLocalErrorContextTableIndex::from_u32(ty), 1272 OptionsIndex::from_u32(options), 1273 debug_msg_address, 1274 debug_msg_len, 1275 ) 1276 } 1277 1278 #[cfg(feature = "component-model-async")] 1279 fn error_context_debug_message( 1280 store: &mut dyn VMStore, 1281 instance: Instance, 1282 _caller_instance: u32, 1283 ty: u32, 1284 options: u32, 1285 err_ctx_handle: u32, 1286 debug_msg_address: u32, 1287 ) -> Result<()> { 1288 store.component_async_store().error_context_debug_message( 1289 instance, 1290 TypeComponentLocalErrorContextTableIndex::from_u32(ty), 1291 OptionsIndex::from_u32(options), 1292 err_ctx_handle, 1293 debug_msg_address, 1294 ) 1295 } 1296 1297 #[cfg(feature = "component-model-async")] 1298 fn error_context_drop( 1299 store: &mut dyn VMStore, 1300 instance: Instance, 1301 _caller_instance: u32, 1302 ty: u32, 1303 err_ctx_handle: u32, 1304 ) -> Result<()> { 1305 instance.error_context_drop( 1306 store, 1307 TypeComponentLocalErrorContextTableIndex::from_u32(ty), 1308 err_ctx_handle, 1309 ) 1310 } 1311 1312 #[cfg(feature = "component-model-async")] 1313 fn context_get( 1314 store: &mut dyn VMStore, 1315 instance: Instance, 1316 _caller_instance: u32, 1317 slot: u32, 1318 ) -> Result<u32> { 1319 instance.context_get(store, slot) 1320 } 1321 1322 #[cfg(feature = "component-model-async")] 1323 fn context_set( 1324 store: &mut dyn VMStore, 1325 instance: Instance, 1326 _caller_instance: u32, 1327 slot: u32, 1328 val: u32, 1329 ) -> Result<()> { 1330 instance.context_set(store, slot, val) 1331 } 1332 1333 #[cfg(feature = "component-model-async")] 1334 fn thread_index(store: &mut dyn VMStore, instance: Instance) -> Result<u32> { 1335 instance.thread_index(store) 1336 } 1337 1338 #[cfg(feature = "component-model-async")] 1339 fn thread_new_indirect( 1340 store: &mut dyn VMStore, 1341 instance: Instance, 1342 caller: u32, 1343 func_ty_id: u32, 1344 func_table_idx: u32, 1345 func_idx: u32, 1346 context: u32, 1347 ) -> Result<u32> { 1348 store.component_async_store().thread_new_indirect( 1349 instance, 1350 RuntimeComponentInstanceIndex::from_u32(caller), 1351 TypeFuncIndex::from_u32(func_ty_id), 1352 RuntimeTableIndex::from_u32(func_table_idx), 1353 func_idx, 1354 context as i32, 1355 ) 1356 } 1357 1358 #[cfg(feature = "component-model-async")] 1359 fn thread_suspend_to_suspended( 1360 store: &mut dyn VMStore, 1361 instance: Instance, 1362 caller: u32, 1363 cancellable: u8, 1364 thread_idx: u32, 1365 ) -> Result<bool> { 1366 instance 1367 .suspension_intrinsic( 1368 store, 1369 RuntimeComponentInstanceIndex::from_u32(caller), 1370 cancellable != 0, 1371 false, 1372 SuspensionTarget::SomeSuspended(thread_idx), 1373 ) 1374 .map(|r| r == WaitResult::Cancelled) 1375 } 1376 1377 #[cfg(feature = "component-model-async")] 1378 fn thread_suspend_to( 1379 store: &mut dyn VMStore, 1380 instance: Instance, 1381 caller: u32, 1382 cancellable: u8, 1383 thread_idx: u32, 1384 ) -> Result<bool> { 1385 instance 1386 .suspension_intrinsic( 1387 store, 1388 RuntimeComponentInstanceIndex::from_u32(caller), 1389 cancellable != 0, 1390 false, 1391 SuspensionTarget::Some(thread_idx), 1392 ) 1393 .map(|r| r == WaitResult::Cancelled) 1394 } 1395 1396 #[cfg(feature = "component-model-async")] 1397 fn thread_suspend( 1398 store: &mut dyn VMStore, 1399 instance: Instance, 1400 caller: u32, 1401 cancellable: u8, 1402 ) -> Result<bool> { 1403 instance 1404 .suspension_intrinsic( 1405 store, 1406 RuntimeComponentInstanceIndex::from_u32(caller), 1407 cancellable != 0, 1408 false, 1409 SuspensionTarget::None, 1410 ) 1411 .map(|r| r == WaitResult::Cancelled) 1412 } 1413 1414 #[cfg(feature = "component-model-async")] 1415 fn thread_unsuspend( 1416 store: &mut dyn VMStore, 1417 instance: Instance, 1418 caller_instance: u32, 1419 thread_idx: u32, 1420 ) -> Result<()> { 1421 instance.resume_thread( 1422 store, 1423 RuntimeComponentInstanceIndex::from_u32(caller_instance), 1424 thread_idx, 1425 false, 1426 false, 1427 ) 1428 } 1429 1430 #[cfg(feature = "component-model-async")] 1431 fn thread_yield_to_suspended( 1432 store: &mut dyn VMStore, 1433 instance: Instance, 1434 caller_instance: u32, 1435 cancellable: u8, 1436 thread_idx: u32, 1437 ) -> Result<bool> { 1438 instance 1439 .suspension_intrinsic( 1440 store, 1441 RuntimeComponentInstanceIndex::from_u32(caller_instance), 1442 cancellable != 0, 1443 true, 1444 SuspensionTarget::SomeSuspended(thread_idx), 1445 ) 1446 .map(|r| r == WaitResult::Cancelled) 1447 } 1448