use crate::component::matching::InstanceType;
use crate::component::resources::{HostResourceData, HostResourceIndex, HostResourceTables};
use crate::component::ResourceType;
use crate::prelude::*;
use crate::runtime::vm::component::{
CallContexts, ComponentInstance, InstanceFlags, ResourceTable, ResourceTables,
};
use crate::runtime::vm::{VMFuncRef, VMMemoryDefinition};
use crate::store::{StoreId, StoreOpaque};
use crate::{FuncType, StoreContextMut};
use alloc::sync::Arc;
use anyhow::{bail, Result};
use core::ptr::NonNull;
use wasmtime_environ::component::{ComponentTypes, StringEncoding, TypeResourceTableIndex};
#[derive(Copy, Clone)]
pub struct Options {
store_id: StoreId,
memory: Option<NonNull<VMMemoryDefinition>>,
realloc: Option<NonNull<VMFuncRef>>,
string_encoding: StringEncoding,
}
unsafe impl Send for Options {}
unsafe impl Sync for Options {}
impl Options {
pub unsafe fn new(
store_id: StoreId,
memory: Option<NonNull<VMMemoryDefinition>>,
realloc: Option<NonNull<VMFuncRef>>,
string_encoding: StringEncoding,
) -> Options {
Options {
store_id,
memory,
realloc,
string_encoding,
}
}
fn realloc<'a, T>(
&self,
store: &'a mut StoreContextMut<'_, T>,
realloc_ty: &FuncType,
old: usize,
old_size: usize,
old_align: u32,
new_size: usize,
) -> Result<(&'a mut [u8], usize)> {
self.store_id.assert_belongs_to(store.0.id());
let realloc = self.realloc.unwrap();
let params = (
u32::try_from(old).err2anyhow()?,
u32::try_from(old_size).err2anyhow()?,
old_align,
u32::try_from(new_size).err2anyhow()?,
);
type ReallocFunc = crate::TypedFunc<(u32, u32, u32, u32), u32>;
debug_assert!(!ReallocFunc::need_gc_before_call_raw(store.0, ¶ms));
let result = unsafe { ReallocFunc::call_raw(store, realloc_ty, realloc, params)? };
if result % old_align != 0 {
bail!("realloc return: result not aligned");
}
let result = usize::try_from(result).err2anyhow()?;
let memory = self.memory_mut(store.0);
let result_slice = match memory.get_mut(result..).and_then(|s| s.get_mut(..new_size)) {
Some(end) => end,
None => bail!("realloc return: beyond end of memory"),
};
Ok((result_slice, result))
}
pub fn memory<'a>(&self, store: &'a StoreOpaque) -> &'a [u8] {
self.store_id.assert_belongs_to(store.id());
unsafe {
let memory = self.memory.unwrap().as_ref();
core::slice::from_raw_parts(memory.base, memory.current_length())
}
}
pub fn memory_mut<'a>(&self, store: &'a mut StoreOpaque) -> &'a mut [u8] {
self.store_id.assert_belongs_to(store.id());
unsafe {
let memory = self.memory.unwrap().as_ref();
core::slice::from_raw_parts_mut(memory.base, memory.current_length())
}
}
pub fn string_encoding(&self) -> StringEncoding {
self.string_encoding
}
pub fn store_id(&self) -> StoreId {
self.store_id
}
}
#[doc(hidden)]
pub struct LowerContext<'a, T> {
pub store: StoreContextMut<'a, T>,
pub options: &'a Options,
pub types: &'a ComponentTypes,
instance: *mut ComponentInstance,
}
#[doc(hidden)]
impl<'a, T> LowerContext<'a, T> {
pub unsafe fn new(
store: StoreContextMut<'a, T>,
options: &'a Options,
types: &'a ComponentTypes,
instance: *mut ComponentInstance,
) -> LowerContext<'a, T> {
LowerContext {
store,
options,
types,
instance,
}
}
pub fn as_slice_mut(&mut self) -> &mut [u8] {
self.options.memory_mut(self.store.0)
}
pub fn realloc(
&mut self,
old: usize,
old_size: usize,
old_align: u32,
new_size: usize,
) -> Result<usize> {
let realloc_func_ty = Arc::clone(unsafe { (*self.instance).realloc_func_ty() });
let realloc_func_ty = realloc_func_ty.downcast_ref::<FuncType>().unwrap();
self.options
.realloc(
&mut self.store,
&realloc_func_ty,
old,
old_size,
old_align,
new_size,
)
.map(|(_, ptr)| ptr)
}
pub fn get<const N: usize>(&mut self, offset: usize) -> &mut [u8; N] {
(&mut self.as_slice_mut()[offset..][..N])
.try_into()
.unwrap()
}
pub fn guest_resource_lower_own(
&mut self,
ty: TypeResourceTableIndex,
rep: u32,
) -> Result<u32> {
self.resource_tables().guest_resource_lower_own(rep, ty)
}
pub fn guest_resource_lower_borrow(
&mut self,
ty: TypeResourceTableIndex,
rep: u32,
) -> Result<u32> {
if unsafe { (*self.instance).resource_owned_by_own_instance(ty) } {
return Ok(rep);
}
self.resource_tables().guest_resource_lower_borrow(rep, ty)
}
pub fn host_resource_lift_own(&mut self, idx: HostResourceIndex) -> Result<u32> {
self.resource_tables().host_resource_lift_own(idx)
}
pub fn host_resource_lift_borrow(&mut self, idx: HostResourceIndex) -> Result<u32> {
self.resource_tables().host_resource_lift_borrow(idx)
}
pub fn host_resource_lower_own(
&mut self,
rep: u32,
dtor: Option<NonNull<VMFuncRef>>,
flags: Option<InstanceFlags>,
) -> Result<HostResourceIndex> {
self.resource_tables()
.host_resource_lower_own(rep, dtor, flags)
}
pub fn resource_type(&self, ty: TypeResourceTableIndex) -> ResourceType {
self.instance_type().resource_type(ty)
}
pub fn instance_type(&self) -> InstanceType<'_> {
InstanceType::new(unsafe { &*self.instance })
}
fn resource_tables(&mut self) -> HostResourceTables<'_> {
let (calls, host_table, host_resource_data) = self.store.0.component_resource_state();
HostResourceTables::from_parts(
ResourceTables {
host_table: Some(host_table),
calls,
tables: Some(unsafe { (*self.instance).component_resource_tables() }),
},
host_resource_data,
)
}
#[inline]
pub fn enter_call(&mut self) {
self.resource_tables().enter_call()
}
#[inline]
pub fn exit_call(&mut self) -> Result<()> {
self.resource_tables().exit_call()
}
}
#[doc(hidden)]
pub struct LiftContext<'a> {
pub options: &'a Options,
pub types: &'a Arc<ComponentTypes>,
memory: Option<&'a [u8]>,
instance: *mut ComponentInstance,
host_table: &'a mut ResourceTable,
host_resource_data: &'a mut HostResourceData,
calls: &'a mut CallContexts,
}
#[doc(hidden)]
impl<'a> LiftContext<'a> {
#[inline]
pub unsafe fn new(
store: &'a mut StoreOpaque,
options: &'a Options,
types: &'a Arc<ComponentTypes>,
instance: *mut ComponentInstance,
) -> LiftContext<'a> {
let (calls, host_table, host_resource_data) =
(&mut *(store as *mut StoreOpaque)).component_resource_state();
let memory = options.memory.map(|_| options.memory(store));
LiftContext {
memory,
options,
types,
instance,
calls,
host_table,
host_resource_data,
}
}
pub fn memory(&self) -> &'a [u8] {
self.memory.unwrap()
}
pub fn store_id(&self) -> StoreId {
self.options.store_id
}
pub fn instance_ptr(&self) -> *mut ComponentInstance {
self.instance
}
pub fn guest_resource_lift_own(
&mut self,
ty: TypeResourceTableIndex,
idx: u32,
) -> Result<(u32, Option<NonNull<VMFuncRef>>, Option<InstanceFlags>)> {
let idx = self.resource_tables().guest_resource_lift_own(idx, ty)?;
let (dtor, flags) = unsafe { (*self.instance).dtor_and_flags(ty) };
Ok((idx, dtor, flags))
}
pub fn guest_resource_lift_borrow(
&mut self,
ty: TypeResourceTableIndex,
idx: u32,
) -> Result<u32> {
self.resource_tables().guest_resource_lift_borrow(idx, ty)
}
pub fn host_resource_lower_own(
&mut self,
rep: u32,
dtor: Option<NonNull<VMFuncRef>>,
flags: Option<InstanceFlags>,
) -> Result<HostResourceIndex> {
self.resource_tables()
.host_resource_lower_own(rep, dtor, flags)
}
pub fn host_resource_lower_borrow(&mut self, rep: u32) -> Result<HostResourceIndex> {
self.resource_tables().host_resource_lower_borrow(rep)
}
pub fn resource_type(&self, ty: TypeResourceTableIndex) -> ResourceType {
self.instance_type().resource_type(ty)
}
pub fn instance_type(&self) -> InstanceType<'_> {
InstanceType::new(unsafe { &*self.instance })
}
fn resource_tables(&mut self) -> HostResourceTables<'_> {
HostResourceTables::from_parts(
ResourceTables {
host_table: Some(self.host_table),
calls: self.calls,
tables: Some(unsafe { (*self.instance).component_resource_tables() }),
},
self.host_resource_data,
)
}
#[inline]
pub fn enter_call(&mut self) {
self.resource_tables().enter_call()
}
#[inline]
pub fn exit_call(&mut self) -> Result<()> {
self.resource_tables().exit_call()
}
}