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<VMComponentContext>);
}
macro_rules! define_builtins {
(
$(
$( #[$attr:meta] )*
$name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?;
)*
) => {
#[repr(C)]
pub struct VMComponentBuiltins {
$(
$name: unsafe extern "C" fn(
$(signature!(@ty $param),)*
) $( -> signature!(@ty $result))?,
)*
}
unsafe impl VmSafe for VMComponentBuiltins {}
impl VMComponentBuiltins {
pub const INIT: VMComponentBuiltins = VMComponentBuiltins {
$($name: trampolines::$name,)*
};
pub fn expose_provenance(&self) -> NonNull<Self>{
$(
(self.$name as *mut u8).expose_provenance();
)*
NonNull::from(self)
}
}
};
}
wasmtime_environ::foreach_builtin_component_function!(define_builtins);
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<VMComponentContext>
$(,$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!();
}
)?
}
)*
);
(@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) => ({
assert!(($arg as usize) % 2 == 0, "unaligned 16-bit pointer");
});
(@validate_param $arg:ident $ty:ident) => ();
(@invoke $m:ident ($($args:tt)*)) => (super::$m($($args)*));
(@invoke $m:ident ($($args:tt)*) ret2 $($rest:tt)*) => (
shims!(@invoke $m ($($args)*) $($rest)*)
);
(@invoke $m:ident ($($args:tt)*) $param:ident $($rest:tt)*) => (
shims!(@invoke $m ($($args)* $param,) $($rest)*)
);
}
wasmtime_environ::foreach_builtin_component_function!(shims);
}
fn assert_no_overlap<T, U>(a: &[T], b: &[U]) {
let a_start = a.as_ptr() as usize;
let a_end = a_start + (a.len() * core::mem::size_of::<T>());
let b_start = b.as_ptr() as usize;
let b_end = b_start + (b.len() * core::mem::size_of::<U>());
if a_start < b_start {
assert!(a_end <= b_start);
} else {
assert!(b_end <= a_start);
}
}
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(())
}
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(())
}
fn run_utf16_to_utf16(src: &[u16], mut dst: &mut [u16]) -> Result<bool> {
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)
}
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(())
}
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
}
}
unsafe fn utf8_to_utf16(
_: &mut dyn VMStore,
_: Instance,
src: *mut u8,
len: usize,
dst: *mut u16,
) -> Result<CopySizeReturn> {
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<usize> {
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)
}
}
unsafe fn utf16_to_utf8(
_: &mut dyn VMStore,
_: Instance,
src: *mut u16,
src_len: usize,
dst: *mut u8,
dst_len: usize,
) -> Result<SizePair> {
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);
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 dst.len() < 4 && dst.len() < ch.len_utf8() {
break;
}
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,
})
}
unsafe fn latin1_to_utf8(
_: &mut dyn VMStore,
_: Instance,
src: *mut u8,
src_len: usize,
dst: *mut u8,
dst_len: usize,
) -> Result<SizePair> {
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,
})
}
unsafe fn utf16_to_compact_probably_utf16(
_: &mut dyn VMStore,
_: Instance,
src: *mut u16,
len: usize,
dst: *mut u16,
) -> Result<CopySizeReturn> {
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::<u8>() };
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))
}
}
unsafe fn utf8_to_latin1(
_: &mut dyn VMStore,
_: Instance,
src: *mut u8,
len: usize,
dst: *mut u8,
) -> Result<SizePair> {
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,
})
}
unsafe fn utf16_to_latin1(
_: &mut dyn VMStore,
_: Instance,
src: *mut u16,
len: usize,
dst: *mut u8,
) -> Result<SizePair> {
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,
})
}
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<CopySizeReturn> {
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))
}
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<CopySizeReturn> {
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))
}
fn inflate_latin1_bytes(dst: &mut [u16], latin1_bytes_so_far: usize) -> &mut [u16] {
let (to_inflate, rest) = dst.split_at_mut(latin1_bytes_so_far);
let (left, mid, right) = unsafe { to_inflate.align_to_mut::<u8>() };
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<u32> {
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<u32> {
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<ResourceDropRet> {
let resource = TypeResourceTableIndex::from_u32(resource);
Ok(ResourceDropRet(
instance.resource_drop(store, resource, idx)?,
))
}
struct ResourceDropRet(Option<u32>);
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<u32> {
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<u32> {
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<u32> {
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<u32> {
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<u32> {
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<bool> {
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<u32> {
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::<crate::vm::VMMemoryDefinition>(),
NonNull::new(start).unwrap().cast::<crate::vm::VMFuncRef>(),
NonNull::new(return_)
.unwrap()
.cast::<crate::vm::VMFuncRef>(),
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::<crate::ValRaw>(),
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::<crate::vm::VMFuncRef>(),
NonNull::new(callee).unwrap().cast::<crate::vm::VMFuncRef>(),
param_count,
storage.cast::<std::mem::MaybeUninit<crate::ValRaw>>(),
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<u32> {
unsafe {
store.component_async_store().async_start(
instance,
callback.cast::<crate::vm::VMFuncRef>(),
post_return.cast::<crate::vm::VMFuncRef>(),
NonNull::new(callee).unwrap().cast::<crate::vm::VMFuncRef>(),
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<u32> {
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<u32> {
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<u32> {
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<ResourcePair> {
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<u32> {
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<u32> {
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<u32> {
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<u32> {
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<ResourcePair> {
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<u32> {
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<u32> {
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<u32> {
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<u32> {
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<u32> {
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<u32> {
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<u32> {
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<u32> {
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<u32> {
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<u32> {
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<bool> {
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<bool> {
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<bool> {
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<bool> {
instance
.suspension_intrinsic(
store,
RuntimeComponentInstanceIndex::from_u32(caller_instance),
cancellable != 0,
true,
SuspensionTarget::SomeSuspended(thread_idx),
)
.map(|r| r == WaitResult::Cancelled)
}