//! Implementation of string transcoding required by the component model. use crate::component::Instance; #[cfg(feature = "component-model-async")] use crate::component::concurrent::WaitResult; use crate::prelude::*; use crate::runtime::component::RuntimeInstance; #[cfg(feature = "component-model-async")] use crate::runtime::component::concurrent::{ResourcePair, SuspensionTarget}; use crate::runtime::vm::component::{ComponentInstance, VMComponentContext}; use crate::runtime::vm::{HostResultHasUnwindSentinel, VMStore, VmSafe}; use core::cell::Cell; use core::ptr::NonNull; use core::slice; use wasmtime_environ::component::*; const UTF16_TAG: usize = 1 << 31; macro_rules! signature { (@ty size) => (usize); (@ty ptr_u8) => (*mut u8); (@ty ptr_u16) => (*mut u16); (@ty ptr_size) => (*mut usize); (@ty u8) => (u8); (@ty u32) => (u32); (@ty u64) => (u64); (@ty bool) => (bool); (@ty vmctx) => (NonNull); } /// Defines a `VMComponentBuiltins` structure which contains any builtins such /// as resource-related intrinsics. macro_rules! define_builtins { ( $( $( #[$attr:meta] )* $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?; )* ) => { /// An array that stores addresses of builtin functions. We translate code /// to use indirect calls. This way, we don't have to patch the code. #[repr(C)] pub struct VMComponentBuiltins { $( $name: unsafe extern "C" fn( $(signature!(@ty $param),)* ) $( -> signature!(@ty $result))?, )* } // SAFETY: the above structure is repr(C) and only contains `VmSafe` // fields. unsafe impl VmSafe for VMComponentBuiltins {} impl VMComponentBuiltins { pub const INIT: VMComponentBuiltins = VMComponentBuiltins { $($name: trampolines::$name,)* }; /// Helper to call `expose_provenance()` on all contained pointers. /// /// This is required to be called at least once before entering wasm /// to inform the compiler that these function pointers may all be /// loaded/stored and used on the "other end" to reacquire /// provenance in Pulley. Pulley models hostcalls with a host /// pointer as the first parameter that's a function pointer under /// the hood, and this call ensures that the use of the function /// pointer is considered valid. pub fn expose_provenance(&self) -> NonNull{ $( (self.$name as *mut u8).expose_provenance(); )* NonNull::from(self) } } }; } wasmtime_environ::foreach_builtin_component_function!(define_builtins); /// Submodule with macro-generated constants which are the actual libcall /// transcoders that are invoked by Cranelift. These functions have a specific /// ABI defined by the macro itself and will defer to the actual bodies of each /// implementation following this submodule. mod trampolines { use super::{ComponentInstance, VMComponentContext}; use core::ptr::NonNull; macro_rules! shims { ( $( $( #[cfg($attr:meta)] )? $name:ident( vmctx: vmctx $(, $pname:ident: $param:ident )* ) $( -> $result:ident )?; )* ) => ( $( #[allow(unused_variables, reason = "macro-generated")] pub unsafe extern "C" fn $name( vmctx: NonNull $(,$pname : signature!(@ty $param))* ) $( -> signature!(@ty $result))? { $(#[cfg($attr)])? { $(shims!(@validate_param $pname $param);)* let ret = unsafe { ComponentInstance::enter_host_from_wasm(vmctx, |store, instance| { shims!(@invoke $name(store, instance,) $($pname)*) }) }; shims!(@convert_ret ret $($pname: $param)*) } $( #[cfg(not($attr))] { let _ = vmctx; unreachable!(); } )? } )* ); // Helper macro to convert a 2-tuple automatically when the last // parameter is a `ptr_size` argument. (@convert_ret $ret:ident) => ($ret); (@convert_ret $ret:ident $retptr:ident: ptr_size) => ({ let (a, b) = $ret; unsafe { *$retptr = b; } a }); (@convert_ret $ret:ident $name:ident: $ty:ident $($rest:tt)*) => ( shims!(@convert_ret $ret $($rest)*) ); (@validate_param $arg:ident ptr_u16) => ({ // This should already be guaranteed by the canonical ABI and our // adapter modules, but double-check here to be extra-sure. If this // is a perf concern it can become a `debug_assert!`. assert!(($arg as usize) % 2 == 0, "unaligned 16-bit pointer"); }); (@validate_param $arg:ident $ty:ident) => (); // Helper macro to invoke `$m` with all of the tokens passed except for // any argument named `ret2` (@invoke $m:ident ($($args:tt)*)) => (super::$m($($args)*)); // ignore `ret2`-named arguments (@invoke $m:ident ($($args:tt)*) ret2 $($rest:tt)*) => ( shims!(@invoke $m ($($args)*) $($rest)*) ); // move all other arguments into the `$args` list (@invoke $m:ident ($($args:tt)*) $param:ident $($rest:tt)*) => ( shims!(@invoke $m ($($args)* $param,) $($rest)*) ); } wasmtime_environ::foreach_builtin_component_function!(shims); } /// This property should already be guaranteed by construction in the component /// model but assert it here to be extra sure. Nothing below is sound if regions /// can overlap. fn assert_no_overlap(a: &[T], b: &[U]) { let a_start = a.as_ptr() as usize; let a_end = a_start + (a.len() * core::mem::size_of::()); let b_start = b.as_ptr() as usize; let b_end = b_start + (b.len() * core::mem::size_of::()); if a_start < b_start { assert!(a_end <= b_start); } else { assert!(b_end <= a_start); } } /// Converts a utf8 string to a utf8 string. /// /// The length provided is length of both the source and the destination /// buffers. No value is returned other than whether an invalid string was /// found. unsafe fn utf8_to_utf8( _: &mut dyn VMStore, _: Instance, src: *mut u8, len: usize, dst: *mut u8, ) -> Result<()> { let src = unsafe { slice::from_raw_parts(src, len) }; let dst = unsafe { slice::from_raw_parts_mut(dst, len) }; assert_no_overlap(src, dst); log::trace!("utf8-to-utf8 {len}"); let src = core::str::from_utf8(src).map_err(|_| format_err!("invalid utf8 encoding"))?; dst.copy_from_slice(src.as_bytes()); Ok(()) } /// Converts a utf16 string to a utf16 string. /// /// The length provided is length of both the source and the destination /// buffers. No value is returned other than whether an invalid string was /// found. unsafe fn utf16_to_utf16( _: &mut dyn VMStore, _: Instance, src: *mut u16, len: usize, dst: *mut u16, ) -> Result<()> { let src = unsafe { slice::from_raw_parts(src, len) }; let dst = unsafe { slice::from_raw_parts_mut(dst, len) }; assert_no_overlap(src, dst); log::trace!("utf16-to-utf16 {len}"); run_utf16_to_utf16(src, dst)?; Ok(()) } /// Transcodes utf16 to itself, returning whether all code points were inside of /// the latin1 space. fn run_utf16_to_utf16(src: &[u16], mut dst: &mut [u16]) -> Result { let mut all_latin1 = true; for ch in core::char::decode_utf16(src.iter().map(|i| u16::from_le(*i))) { let ch = ch.map_err(|_| format_err!("invalid utf16 encoding"))?; all_latin1 = all_latin1 && u8::try_from(u32::from(ch)).is_ok(); let result = ch.encode_utf16(dst); let size = result.len(); for item in result { *item = item.to_le(); } dst = &mut dst[size..]; } Ok(all_latin1) } /// Converts a latin1 string to a latin1 string. /// /// Given that all byte sequences are valid latin1 strings this is simply a /// memory copy. unsafe fn latin1_to_latin1( _: &mut dyn VMStore, _: Instance, src: *mut u8, len: usize, dst: *mut u8, ) -> Result<()> { let src = unsafe { slice::from_raw_parts(src, len) }; let dst = unsafe { slice::from_raw_parts_mut(dst, len) }; assert_no_overlap(src, dst); log::trace!("latin1-to-latin1 {len}"); dst.copy_from_slice(src); Ok(()) } /// Converts a latin1 string to a utf16 string. /// /// This simply inflates the latin1 characters to the u16 code points. The /// length provided is the same length of the source and destination buffers. unsafe fn latin1_to_utf16( _: &mut dyn VMStore, _: Instance, src: *mut u8, len: usize, dst: *mut u16, ) -> Result<()> { let src = unsafe { slice::from_raw_parts(src, len) }; let dst = unsafe { slice::from_raw_parts_mut(dst, len) }; assert_no_overlap(src, dst); for (src, dst) in src.iter().zip(dst) { *dst = u16::from(*src).to_le(); } log::trace!("latin1-to-utf16 {len}"); Ok(()) } struct CopySizeReturn(usize); unsafe impl HostResultHasUnwindSentinel for CopySizeReturn { type Abi = usize; const SENTINEL: usize = usize::MAX; fn into_abi(self) -> usize { self.0 } } /// Converts utf8 to utf16. /// /// The length provided is the same unit length of both buffers, and the /// returned value from this function is how many u16 units were written. unsafe fn utf8_to_utf16( _: &mut dyn VMStore, _: Instance, src: *mut u8, len: usize, dst: *mut u16, ) -> Result { let src = unsafe { slice::from_raw_parts(src, len) }; let dst = unsafe { slice::from_raw_parts_mut(dst, len) }; assert_no_overlap(src, dst); let result = run_utf8_to_utf16(src, dst)?; log::trace!("utf8-to-utf16 {len} => {result}"); Ok(CopySizeReturn(result)) } fn run_utf8_to_utf16(src: &[u8], dst: &mut [u16]) -> Result { let src = core::str::from_utf8(src).map_err(|_| format_err!("invalid utf8 encoding"))?; let mut amt = 0; for (i, dst) in src.encode_utf16().zip(dst) { *dst = i.to_le(); amt += 1; } Ok(amt) } struct SizePair { src_read: usize, dst_written: usize, } unsafe impl HostResultHasUnwindSentinel for SizePair { type Abi = (usize, usize); const SENTINEL: (usize, usize) = (usize::MAX, 0); fn into_abi(self) -> (usize, usize) { (self.src_read, self.dst_written) } } /// Converts utf16 to utf8. /// /// Each buffer is specified independently here and the returned value is a pair /// of the number of code units read and code units written. This might perform /// a partial transcode if the destination buffer is not large enough to hold /// the entire contents. unsafe fn utf16_to_utf8( _: &mut dyn VMStore, _: Instance, src: *mut u16, src_len: usize, dst: *mut u8, dst_len: usize, ) -> Result { let src = unsafe { slice::from_raw_parts(src, src_len) }; let mut dst = unsafe { slice::from_raw_parts_mut(dst, dst_len) }; assert_no_overlap(src, dst); // This iterator will convert to native endianness and additionally count // how many items have been read from the iterator so far. This // count is used to return how many of the source code units were read. let src_iter_read = Cell::new(0); let src_iter = src.iter().map(|i| { src_iter_read.set(src_iter_read.get() + 1); u16::from_le(*i) }); let mut src_read = 0; let mut dst_written = 0; for ch in core::char::decode_utf16(src_iter) { let ch = ch.map_err(|_| format_err!("invalid utf16 encoding"))?; // If the destination doesn't have enough space for this character // then the loop is ended and this function will be called later with a // larger destination buffer. if dst.len() < 4 && dst.len() < ch.len_utf8() { break; } // Record that characters were read and then convert the `char` to // utf-8, advancing the destination buffer. src_read = src_iter_read.get(); let len = ch.encode_utf8(dst).len(); dst_written += len; dst = &mut dst[len..]; } log::trace!("utf16-to-utf8 {src_len}/{dst_len} => {src_read}/{dst_written}"); Ok(SizePair { src_read, dst_written, }) } /// Converts latin1 to utf8. /// /// Receives the independent size of both buffers and returns the number of code /// units read and code units written (both bytes in this case). /// /// This may perform a partial encoding if the destination is not large enough. unsafe fn latin1_to_utf8( _: &mut dyn VMStore, _: Instance, src: *mut u8, src_len: usize, dst: *mut u8, dst_len: usize, ) -> Result { let src = unsafe { slice::from_raw_parts(src, src_len) }; let dst = unsafe { slice::from_raw_parts_mut(dst, dst_len) }; assert_no_overlap(src, dst); let (read, written) = encoding_rs::mem::convert_latin1_to_utf8_partial(src, dst); log::trace!("latin1-to-utf8 {src_len}/{dst_len} => ({read}, {written})"); Ok(SizePair { src_read: read, dst_written: written, }) } /// Converts utf16 to "latin1+utf16", probably using a utf16 encoding. /// /// The length specified is the length of both the source and destination /// buffers. If the source string has any characters that don't fit in the /// latin1 code space (0xff and below) then a utf16-tagged length will be /// returned. Otherwise the string is "deflated" from a utf16 string to a latin1 /// string and the latin1 length is returned. unsafe fn utf16_to_compact_probably_utf16( _: &mut dyn VMStore, _: Instance, src: *mut u16, len: usize, dst: *mut u16, ) -> Result { let src = unsafe { slice::from_raw_parts(src, len) }; let dst = unsafe { slice::from_raw_parts_mut(dst, len) }; assert_no_overlap(src, dst); let all_latin1 = run_utf16_to_utf16(src, dst)?; if all_latin1 { let (left, dst, right) = unsafe { dst.align_to_mut::() }; assert!(left.is_empty()); assert!(right.is_empty()); for i in 0..len { dst[i] = dst[2 * i]; } log::trace!("utf16-to-compact-probably-utf16 {len} => latin1 {len}"); Ok(CopySizeReturn(len)) } else { log::trace!("utf16-to-compact-probably-utf16 {len} => utf16 {len}"); Ok(CopySizeReturn(len | UTF16_TAG)) } } /// Converts a utf8 string to latin1. /// /// The length specified is the same length of both the input and the output /// buffers. /// /// Returns the number of code units read from the source and the number of code /// units written to the destination. /// /// Note that this may not convert the entire source into the destination if the /// original utf8 string has usvs not representable in latin1. unsafe fn utf8_to_latin1( _: &mut dyn VMStore, _: Instance, src: *mut u8, len: usize, dst: *mut u8, ) -> Result { let src = unsafe { slice::from_raw_parts(src, len) }; let dst = unsafe { slice::from_raw_parts_mut(dst, len) }; assert_no_overlap(src, dst); let read = encoding_rs::mem::utf8_latin1_up_to(src); let written = encoding_rs::mem::convert_utf8_to_latin1_lossy(&src[..read], dst); log::trace!("utf8-to-latin1 {len} => ({read}, {written})"); Ok(SizePair { src_read: read, dst_written: written, }) } /// Converts a utf16 string to latin1 /// /// This is the same as `utf8_to_latin1` in terms of parameters/results. unsafe fn utf16_to_latin1( _: &mut dyn VMStore, _: Instance, src: *mut u16, len: usize, dst: *mut u8, ) -> Result { let src = unsafe { slice::from_raw_parts(src, len) }; let dst = unsafe { slice::from_raw_parts_mut(dst, len) }; assert_no_overlap(src, dst); let mut size = 0; for (src, dst) in src.iter().zip(dst) { let src = u16::from_le(*src); match u8::try_from(src) { Ok(src) => *dst = src, Err(_) => break, } size += 1; } log::trace!("utf16-to-latin1 {len} => {size}"); Ok(SizePair { src_read: size, dst_written: size, }) } /// Converts a utf8 string to a utf16 string which has been partially converted /// as latin1 prior. /// /// The original string has already been partially transcoded with /// `utf8_to_latin1` and that was determined to not be able to transcode the /// entire string. The substring of the source that couldn't be encoded into /// latin1 is passed here via `src` and `src_len`. /// /// The destination buffer is specified by `dst` and `dst_len`. The first /// `latin1_bytes_so_far` bytes (not code units) of the `dst` buffer have /// already been filled in with latin1 characters and need to be inflated /// in-place to their utf16 equivalents. /// /// After the initial latin1 code units have been inflated the entirety of `src` /// is then transcoded into the remaining space within `dst`. unsafe fn utf8_to_compact_utf16( _: &mut dyn VMStore, _: Instance, src: *mut u8, src_len: usize, dst: *mut u16, dst_len: usize, latin1_bytes_so_far: usize, ) -> Result { let src = unsafe { slice::from_raw_parts(src, src_len) }; let dst = unsafe { slice::from_raw_parts_mut(dst, dst_len) }; assert_no_overlap(src, dst); let dst = inflate_latin1_bytes(dst, latin1_bytes_so_far); let result = run_utf8_to_utf16(src, dst)?; log::trace!("utf8-to-compact-utf16 {src_len}/{dst_len}/{latin1_bytes_so_far} => {result}"); Ok(CopySizeReturn(result + latin1_bytes_so_far)) } /// Same as `utf8_to_compact_utf16` but for utf16 source strings. unsafe fn utf16_to_compact_utf16( _: &mut dyn VMStore, _: Instance, src: *mut u16, src_len: usize, dst: *mut u16, dst_len: usize, latin1_bytes_so_far: usize, ) -> Result { let src = unsafe { slice::from_raw_parts(src, src_len) }; let dst = unsafe { slice::from_raw_parts_mut(dst, dst_len) }; assert_no_overlap(src, dst); let dst = inflate_latin1_bytes(dst, latin1_bytes_so_far); run_utf16_to_utf16(src, dst)?; let result = src.len(); log::trace!("utf16-to-compact-utf16 {src_len}/{dst_len}/{latin1_bytes_so_far} => {result}"); Ok(CopySizeReturn(result + latin1_bytes_so_far)) } /// Inflates the `latin1_bytes_so_far` number of bytes written to the beginning /// of `dst` into u16 codepoints. /// /// Returns the remaining space in the destination that can be transcoded into, /// slicing off the prefix of the string that was inflated from the latin1 /// bytes. fn inflate_latin1_bytes(dst: &mut [u16], latin1_bytes_so_far: usize) -> &mut [u16] { // Note that `latin1_bytes_so_far` is a byte measure while `dst` is a region // of u16 units. This `split_at_mut` uses the byte index as an index into // the u16 unit because each of the latin1 bytes will become a whole code // unit in the destination which is 2 bytes large. let (to_inflate, rest) = dst.split_at_mut(latin1_bytes_so_far); // Use a byte-oriented view to inflate the original latin1 bytes. let (left, mid, right) = unsafe { to_inflate.align_to_mut::() }; assert!(left.is_empty()); assert!(right.is_empty()); for i in (0..latin1_bytes_so_far).rev() { mid[2 * i] = mid[i]; mid[2 * i + 1] = 0; } return rest; } fn resource_new32( store: &mut dyn VMStore, instance: Instance, _caller_instance: u32, resource: u32, rep: u32, ) -> Result { let resource = TypeResourceTableIndex::from_u32(resource); instance.resource_new32(store, resource, rep) } fn resource_rep32( store: &mut dyn VMStore, instance: Instance, _caller_instance: u32, resource: u32, idx: u32, ) -> Result { let resource = TypeResourceTableIndex::from_u32(resource); instance.resource_rep32(store, resource, idx) } fn resource_drop( store: &mut dyn VMStore, instance: Instance, _caller_instance: u32, resource: u32, idx: u32, ) -> Result { let resource = TypeResourceTableIndex::from_u32(resource); Ok(ResourceDropRet( instance.resource_drop(store, resource, idx)?, )) } struct ResourceDropRet(Option); unsafe impl HostResultHasUnwindSentinel for ResourceDropRet { type Abi = u64; const SENTINEL: u64 = u64::MAX; fn into_abi(self) -> u64 { match self.0 { Some(rep) => (u64::from(rep) << 1) | 1, None => 0, } } } fn resource_transfer_own( store: &mut dyn VMStore, instance: Instance, src_idx: u32, src_table: u32, dst_table: u32, ) -> Result { let src_table = TypeResourceTableIndex::from_u32(src_table); let dst_table = TypeResourceTableIndex::from_u32(dst_table); instance.resource_transfer_own(store, src_idx, src_table, dst_table) } fn resource_transfer_borrow( store: &mut dyn VMStore, instance: Instance, src_idx: u32, src_table: u32, dst_table: u32, ) -> Result { let src_table = TypeResourceTableIndex::from_u32(src_table); let dst_table = TypeResourceTableIndex::from_u32(dst_table); instance.resource_transfer_borrow(store, src_idx, src_table, dst_table) } fn trap(_store: &mut dyn VMStore, _instance: Instance, code: u32) -> Result<()> { Err(wasmtime_environ::Trap::from_u8(u8::try_from(code).unwrap()) .unwrap() .into()) } fn enter_sync_call( store: &mut dyn VMStore, instance: Instance, caller_instance: u32, callee_async: u32, callee_instance: u32, ) -> Result<()> { store.enter_guest_sync_call( Some(RuntimeInstance { instance: instance.id().instance(), index: RuntimeComponentInstanceIndex::from_u32(caller_instance), }), callee_async != 0, RuntimeInstance { instance: instance.id().instance(), index: RuntimeComponentInstanceIndex::from_u32(callee_instance), }, ) } fn exit_sync_call(store: &mut dyn VMStore, instance: Instance) -> Result<()> { store .component_resource_tables(Some(instance)) .validate_scope_exit()?; store.exit_guest_sync_call() } #[cfg(feature = "component-model-async")] fn backpressure_modify( store: &mut dyn VMStore, instance: Instance, caller_instance: u32, increment: u8, ) -> Result<()> { store.backpressure_modify( RuntimeInstance { instance: instance.id().instance(), index: RuntimeComponentInstanceIndex::from_u32(caller_instance), }, |old| { if increment != 0 { old.checked_add(1) } else { old.checked_sub(1) } }, ) } #[cfg(feature = "component-model-async")] unsafe fn task_return( store: &mut dyn VMStore, instance: Instance, _caller_instance: u32, ty: u32, options: u32, storage: *mut u8, storage_len: usize, ) -> Result<()> { instance.task_return( store, TypeTupleIndex::from_u32(ty), OptionsIndex::from_u32(options), unsafe { core::slice::from_raw_parts(storage.cast(), storage_len) }, ) } #[cfg(feature = "component-model-async")] fn task_cancel(store: &mut dyn VMStore, instance: Instance, _caller_instance: u32) -> Result<()> { instance.task_cancel(store) } #[cfg(feature = "component-model-async")] fn waitable_set_new( store: &mut dyn VMStore, instance: Instance, caller_instance: u32, ) -> Result { instance.waitable_set_new( store, RuntimeComponentInstanceIndex::from_u32(caller_instance), ) } #[cfg(feature = "component-model-async")] fn waitable_set_wait( store: &mut dyn VMStore, instance: Instance, _caller: u32, options: u32, set: u32, payload: u32, ) -> Result { instance.waitable_set_wait(store, OptionsIndex::from_u32(options), set, payload) } #[cfg(feature = "component-model-async")] fn waitable_set_poll( store: &mut dyn VMStore, instance: Instance, _caller: u32, options: u32, set: u32, payload: u32, ) -> Result { instance.waitable_set_poll(store, OptionsIndex::from_u32(options), set, payload) } #[cfg(feature = "component-model-async")] fn waitable_set_drop( store: &mut dyn VMStore, instance: Instance, caller_instance: u32, set: u32, ) -> Result<()> { instance.waitable_set_drop( store, RuntimeComponentInstanceIndex::from_u32(caller_instance), set, ) } #[cfg(feature = "component-model-async")] fn waitable_join( store: &mut dyn VMStore, instance: Instance, caller_instance: u32, waitable: u32, set: u32, ) -> Result<()> { instance.waitable_join( store, RuntimeComponentInstanceIndex::from_u32(caller_instance), waitable, set, ) } #[cfg(feature = "component-model-async")] fn thread_yield( store: &mut dyn VMStore, instance: Instance, caller_instance: u32, cancellable: u8, ) -> Result { instance .suspension_intrinsic( store, RuntimeComponentInstanceIndex::from_u32(caller_instance), cancellable != 0, true, SuspensionTarget::None, ) .map(|r| r == WaitResult::Cancelled) } #[cfg(feature = "component-model-async")] fn subtask_drop( store: &mut dyn VMStore, instance: Instance, caller_instance: u32, task_id: u32, ) -> Result<()> { instance.subtask_drop( store, RuntimeComponentInstanceIndex::from_u32(caller_instance), task_id, ) } #[cfg(feature = "component-model-async")] fn subtask_cancel( store: &mut dyn VMStore, instance: Instance, caller_instance: u32, async_: u8, task_id: u32, ) -> Result { instance.subtask_cancel( store, RuntimeComponentInstanceIndex::from_u32(caller_instance), async_ != 0, task_id, ) } #[cfg(feature = "component-model-async")] unsafe fn prepare_call( store: &mut dyn VMStore, instance: Instance, memory: *mut u8, start: *mut u8, return_: *mut u8, caller_instance: u32, callee_instance: u32, task_return_type: u32, callee_async: u32, string_encoding: u32, result_count_or_max_if_async: u32, storage: *mut u8, storage_len: usize, ) -> Result<()> { unsafe { store.component_async_store().prepare_call( instance, memory.cast::(), NonNull::new(start).unwrap().cast::(), NonNull::new(return_) .unwrap() .cast::(), RuntimeComponentInstanceIndex::from_u32(caller_instance), RuntimeComponentInstanceIndex::from_u32(callee_instance), TypeTupleIndex::from_u32(task_return_type), callee_async != 0, StringEncoding::from_u8(u8::try_from(string_encoding).unwrap()).unwrap(), result_count_or_max_if_async, storage.cast::(), storage_len, ) } } #[cfg(feature = "component-model-async")] unsafe fn sync_start( store: &mut dyn VMStore, instance: Instance, callback: *mut u8, storage: *mut u8, storage_len: usize, callee: *mut u8, param_count: u32, ) -> Result<()> { unsafe { store.component_async_store().sync_start( instance, callback.cast::(), NonNull::new(callee).unwrap().cast::(), param_count, storage.cast::>(), storage_len, ) } } #[cfg(feature = "component-model-async")] unsafe fn async_start( store: &mut dyn VMStore, instance: Instance, callback: *mut u8, post_return: *mut u8, callee: *mut u8, param_count: u32, result_count: u32, flags: u32, ) -> Result { unsafe { store.component_async_store().async_start( instance, callback.cast::(), post_return.cast::(), NonNull::new(callee).unwrap().cast::(), param_count, result_count, flags, ) } } #[cfg(feature = "component-model-async")] fn future_transfer( store: &mut dyn VMStore, instance: Instance, src_idx: u32, src_table: u32, dst_table: u32, ) -> Result { instance.future_transfer( store, src_idx, TypeFutureTableIndex::from_u32(src_table), TypeFutureTableIndex::from_u32(dst_table), ) } #[cfg(feature = "component-model-async")] fn stream_transfer( store: &mut dyn VMStore, instance: Instance, src_idx: u32, src_table: u32, dst_table: u32, ) -> Result { instance.stream_transfer( store, src_idx, TypeStreamTableIndex::from_u32(src_table), TypeStreamTableIndex::from_u32(dst_table), ) } #[cfg(feature = "component-model-async")] fn error_context_transfer( store: &mut dyn VMStore, instance: Instance, src_idx: u32, src_table: u32, dst_table: u32, ) -> Result { let src_table = TypeComponentLocalErrorContextTableIndex::from_u32(src_table); let dst_table = TypeComponentLocalErrorContextTableIndex::from_u32(dst_table); instance.error_context_transfer(store, src_idx, src_table, dst_table) } #[cfg(feature = "component-model-async")] unsafe impl HostResultHasUnwindSentinel for ResourcePair { type Abi = u64; const SENTINEL: u64 = u64::MAX; fn into_abi(self) -> Self::Abi { assert!(self.write & (1 << 31) == 0); (u64::from(self.write) << 32) | u64::from(self.read) } } #[cfg(feature = "component-model-async")] fn future_new( store: &mut dyn VMStore, instance: Instance, _caller_instance: u32, ty: u32, ) -> Result { instance.future_new(store, TypeFutureTableIndex::from_u32(ty)) } #[cfg(feature = "component-model-async")] fn future_write( store: &mut dyn VMStore, instance: Instance, caller_instance: u32, ty: u32, options: u32, future: u32, address: u32, ) -> Result { store.component_async_store().future_write( instance, RuntimeComponentInstanceIndex::from_u32(caller_instance), TypeFutureTableIndex::from_u32(ty), OptionsIndex::from_u32(options), future, address, ) } #[cfg(feature = "component-model-async")] fn future_read( store: &mut dyn VMStore, instance: Instance, caller_instance: u32, ty: u32, options: u32, future: u32, address: u32, ) -> Result { store.component_async_store().future_read( instance, RuntimeComponentInstanceIndex::from_u32(caller_instance), TypeFutureTableIndex::from_u32(ty), OptionsIndex::from_u32(options), future, address, ) } #[cfg(feature = "component-model-async")] fn future_cancel_write( store: &mut dyn VMStore, instance: Instance, _caller_instance: u32, ty: u32, async_: u8, writer: u32, ) -> Result { instance.future_cancel_write( store, TypeFutureTableIndex::from_u32(ty), async_ != 0, writer, ) } #[cfg(feature = "component-model-async")] fn future_cancel_read( store: &mut dyn VMStore, instance: Instance, _caller_instance: u32, ty: u32, async_: u8, reader: u32, ) -> Result { instance.future_cancel_read( store, TypeFutureTableIndex::from_u32(ty), async_ != 0, reader, ) } #[cfg(feature = "component-model-async")] fn future_drop_writable( store: &mut dyn VMStore, instance: Instance, _caller_instance: u32, ty: u32, writer: u32, ) -> Result<()> { store.component_async_store().future_drop_writable( instance, TypeFutureTableIndex::from_u32(ty), writer, ) } #[cfg(feature = "component-model-async")] fn future_drop_readable( store: &mut dyn VMStore, instance: Instance, _caller_instance: u32, ty: u32, reader: u32, ) -> Result<()> { instance.future_drop_readable(store, TypeFutureTableIndex::from_u32(ty), reader) } #[cfg(feature = "component-model-async")] fn stream_new( store: &mut dyn VMStore, instance: Instance, _caller_instance: u32, ty: u32, ) -> Result { instance.stream_new(store, TypeStreamTableIndex::from_u32(ty)) } #[cfg(feature = "component-model-async")] fn stream_write( store: &mut dyn VMStore, instance: Instance, caller_instance: u32, ty: u32, options: u32, stream: u32, address: u32, count: u32, ) -> Result { store.component_async_store().stream_write( instance, RuntimeComponentInstanceIndex::from_u32(caller_instance), TypeStreamTableIndex::from_u32(ty), OptionsIndex::from_u32(options), stream, address, count, ) } #[cfg(feature = "component-model-async")] fn stream_read( store: &mut dyn VMStore, instance: Instance, caller_instance: u32, ty: u32, options: u32, stream: u32, address: u32, count: u32, ) -> Result { store.component_async_store().stream_read( instance, RuntimeComponentInstanceIndex::from_u32(caller_instance), TypeStreamTableIndex::from_u32(ty), OptionsIndex::from_u32(options), stream, address, count, ) } #[cfg(feature = "component-model-async")] fn stream_cancel_write( store: &mut dyn VMStore, instance: Instance, _caller_instance: u32, ty: u32, async_: u8, writer: u32, ) -> Result { instance.stream_cancel_write( store, TypeStreamTableIndex::from_u32(ty), async_ != 0, writer, ) } #[cfg(feature = "component-model-async")] fn stream_cancel_read( store: &mut dyn VMStore, instance: Instance, _caller_instance: u32, ty: u32, async_: u8, reader: u32, ) -> Result { instance.stream_cancel_read( store, TypeStreamTableIndex::from_u32(ty), async_ != 0, reader, ) } #[cfg(feature = "component-model-async")] fn stream_drop_writable( store: &mut dyn VMStore, instance: Instance, _caller_instance: u32, ty: u32, writer: u32, ) -> Result<()> { store.component_async_store().stream_drop_writable( instance, TypeStreamTableIndex::from_u32(ty), writer, ) } #[cfg(feature = "component-model-async")] fn stream_drop_readable( store: &mut dyn VMStore, instance: Instance, _caller_instance: u32, ty: u32, reader: u32, ) -> Result<()> { instance.stream_drop_readable(store, TypeStreamTableIndex::from_u32(ty), reader) } #[cfg(feature = "component-model-async")] fn flat_stream_write( store: &mut dyn VMStore, instance: Instance, caller_instance: u32, ty: u32, options: u32, payload_size: u32, payload_align: u32, stream: u32, address: u32, count: u32, ) -> Result { store.component_async_store().flat_stream_write( instance, RuntimeComponentInstanceIndex::from_u32(caller_instance), TypeStreamTableIndex::from_u32(ty), OptionsIndex::from_u32(options), payload_size, payload_align, stream, address, count, ) } #[cfg(feature = "component-model-async")] fn flat_stream_read( store: &mut dyn VMStore, instance: Instance, caller_instance: u32, ty: u32, options: u32, payload_size: u32, payload_align: u32, stream: u32, address: u32, count: u32, ) -> Result { store.component_async_store().flat_stream_read( instance, RuntimeComponentInstanceIndex::from_u32(caller_instance), TypeStreamTableIndex::from_u32(ty), OptionsIndex::from_u32(options), payload_size, payload_align, stream, address, count, ) } #[cfg(feature = "component-model-async")] fn error_context_new( store: &mut dyn VMStore, instance: Instance, _caller_instance: u32, ty: u32, options: u32, debug_msg_address: u32, debug_msg_len: u32, ) -> Result { instance.error_context_new( store.store_opaque_mut(), TypeComponentLocalErrorContextTableIndex::from_u32(ty), OptionsIndex::from_u32(options), debug_msg_address, debug_msg_len, ) } #[cfg(feature = "component-model-async")] fn error_context_debug_message( store: &mut dyn VMStore, instance: Instance, _caller_instance: u32, ty: u32, options: u32, err_ctx_handle: u32, debug_msg_address: u32, ) -> Result<()> { store.component_async_store().error_context_debug_message( instance, TypeComponentLocalErrorContextTableIndex::from_u32(ty), OptionsIndex::from_u32(options), err_ctx_handle, debug_msg_address, ) } #[cfg(feature = "component-model-async")] fn error_context_drop( store: &mut dyn VMStore, instance: Instance, _caller_instance: u32, ty: u32, err_ctx_handle: u32, ) -> Result<()> { instance.error_context_drop( store, TypeComponentLocalErrorContextTableIndex::from_u32(ty), err_ctx_handle, ) } #[cfg(feature = "component-model-async")] fn context_get( store: &mut dyn VMStore, instance: Instance, _caller_instance: u32, slot: u32, ) -> Result { instance.context_get(store, slot) } #[cfg(feature = "component-model-async")] fn context_set( store: &mut dyn VMStore, instance: Instance, _caller_instance: u32, slot: u32, val: u32, ) -> Result<()> { instance.context_set(store, slot, val) } #[cfg(feature = "component-model-async")] fn thread_index(store: &mut dyn VMStore, instance: Instance) -> Result { instance.thread_index(store) } #[cfg(feature = "component-model-async")] fn thread_new_indirect( store: &mut dyn VMStore, instance: Instance, caller: u32, func_ty_id: u32, func_table_idx: u32, func_idx: u32, context: u32, ) -> Result { store.component_async_store().thread_new_indirect( instance, RuntimeComponentInstanceIndex::from_u32(caller), TypeFuncIndex::from_u32(func_ty_id), RuntimeTableIndex::from_u32(func_table_idx), func_idx, context as i32, ) } #[cfg(feature = "component-model-async")] fn thread_suspend_to_suspended( store: &mut dyn VMStore, instance: Instance, caller: u32, cancellable: u8, thread_idx: u32, ) -> Result { instance .suspension_intrinsic( store, RuntimeComponentInstanceIndex::from_u32(caller), cancellable != 0, false, SuspensionTarget::SomeSuspended(thread_idx), ) .map(|r| r == WaitResult::Cancelled) } #[cfg(feature = "component-model-async")] fn thread_suspend_to( store: &mut dyn VMStore, instance: Instance, caller: u32, cancellable: u8, thread_idx: u32, ) -> Result { instance .suspension_intrinsic( store, RuntimeComponentInstanceIndex::from_u32(caller), cancellable != 0, false, SuspensionTarget::Some(thread_idx), ) .map(|r| r == WaitResult::Cancelled) } #[cfg(feature = "component-model-async")] fn thread_suspend( store: &mut dyn VMStore, instance: Instance, caller: u32, cancellable: u8, ) -> Result { instance .suspension_intrinsic( store, RuntimeComponentInstanceIndex::from_u32(caller), cancellable != 0, false, SuspensionTarget::None, ) .map(|r| r == WaitResult::Cancelled) } #[cfg(feature = "component-model-async")] fn thread_unsuspend( store: &mut dyn VMStore, instance: Instance, caller_instance: u32, thread_idx: u32, ) -> Result<()> { instance.resume_thread( store, RuntimeComponentInstanceIndex::from_u32(caller_instance), thread_idx, false, false, ) } #[cfg(feature = "component-model-async")] fn thread_yield_to_suspended( store: &mut dyn VMStore, instance: Instance, caller_instance: u32, cancellable: u8, thread_idx: u32, ) -> Result { instance .suspension_intrinsic( store, RuntimeComponentInstanceIndex::from_u32(caller_instance), cancellable != 0, true, SuspensionTarget::SomeSuspended(thread_idx), ) .map(|r| r == WaitResult::Cancelled) }