use crate::component::matching::InstanceType;
use crate::component::resources::{HostResourceData, HostResourceIndex, HostResourceTables};
use crate::component::{Instance, 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 core::pin::Pin;
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)?,
u32::try_from(old_size)?,
old_align,
u32::try_from(new_size)?,
);
type ReallocFunc = crate::TypedFunc<(u32, u32, u32, u32), u32>;
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)?;
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.as_ptr(), 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.as_ptr(), 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: 'static> {
pub store: StoreContextMut<'a, T>,
pub options: &'a Options,
pub types: &'a ComponentTypes,
instance: Instance,
}
#[doc(hidden)]
impl<'a, T: 'static> LowerContext<'a, T> {
pub fn new(
store: StoreContextMut<'a, T>,
options: &'a Options,
types: &'a ComponentTypes,
instance: Instance,
) -> LowerContext<'a, T> {
LowerContext {
store,
options,
types,
instance,
}
}
pub fn instance(&self) -> &ComponentInstance {
self.instance.id().get(self.store.0)
}
pub fn instance_mut(&mut self) -> Pin<&mut ComponentInstance> {
self.instance.id().get_mut(self.store.0)
}
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(self.instance().component().realloc_func_ty());
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] {
self.as_slice_mut()[offset..].first_chunk_mut().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 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(self.instance())
}
fn resource_tables(&mut self) -> HostResourceTables<'_> {
let (calls, host_table, host_resource_data, instance) = self
.store
.0
.component_resource_state_with_instance(self.instance);
HostResourceTables::from_parts(
ResourceTables {
host_table: Some(host_table),
calls,
guest: Some(instance.guest_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: Pin<&'a mut ComponentInstance>,
instance_handle: Instance,
host_table: &'a mut ResourceTable,
host_resource_data: &'a mut HostResourceData,
calls: &'a mut CallContexts,
}
#[doc(hidden)]
impl<'a> LiftContext<'a> {
#[inline]
pub fn new(
store: &'a mut StoreOpaque,
options: &'a Options,
types: &'a Arc<ComponentTypes>,
instance_handle: Instance,
) -> LiftContext<'a> {
let memory = options
.memory
.map(|_| options.memory(unsafe { &*(store as *const StoreOpaque) }));
let (calls, host_table, host_resource_data, instance) =
store.component_resource_state_with_instance(instance_handle);
LiftContext {
memory,
options,
types,
instance,
instance_handle,
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_mut(&mut self) -> Pin<&mut ComponentInstance> {
self.instance.as_mut()
}
pub fn instance_handle(&self) -> Instance {
self.instance_handle
}
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) = 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(&self.instance)
}
fn resource_tables(&mut self) -> HostResourceTables<'_> {
HostResourceTables::from_parts(
ResourceTables {
host_table: Some(self.host_table),
calls: self.calls,
guest: Some(self.instance.as_mut().guest_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()
}
}