use crate::component::func::{LiftContext, LowerContext, bad_type_info, desc};
use crate::component::matching::InstanceType;
use crate::component::resources::host::{HostResource, HostResourceType};
use crate::component::resources::{HostResourceIndex, HostResourceTables};
use crate::component::{ComponentType, Lift, Lower, Resource, ResourceDynamic, ResourceType};
use crate::prelude::*;
use crate::runtime::vm::ValRaw;
use crate::{AsContextMut, StoreContextMut, Trap};
use core::mem::MaybeUninit;
use core::ptr::NonNull;
use wasmtime_environ::component::{CanonicalAbiInfo, InterfaceType};
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct ResourceAny {
idx: HostResourceIndex,
ty: ResourceType,
owned: bool,
}
impl ResourceAny {
pub(crate) fn new(idx: HostResourceIndex, ty: ResourceType, owned: bool) -> ResourceAny {
ResourceAny { idx, ty, owned }
}
pub fn try_from_resource<T: 'static>(
resource: Resource<T>,
store: impl AsContextMut,
) -> Result<Self> {
resource.try_into_resource_any(store)
}
pub fn try_into_resource<T: 'static>(self, store: impl AsContextMut) -> Result<Resource<T>> {
Resource::try_from_resource_any(self, store)
}
pub fn try_into_resource_dynamic(self, store: impl AsContextMut) -> Result<ResourceDynamic> {
ResourceDynamic::try_from_resource_any(self, store)
}
pub(crate) fn try_into_host_resource<T, D>(
self,
mut store: impl AsContextMut,
) -> Result<HostResource<T, D>>
where
T: HostResourceType<D>,
D: PartialEq + Send + Sync + Copy + 'static,
{
let store = store.as_context_mut();
let mut tables = HostResourceTables::new_host(store.0);
let ResourceAny { idx, ty, owned } = self;
let ty = T::typecheck(ty).ok_or_else(|| crate::format_err!("resource type mismatch"))?;
if owned {
let rep = tables.host_resource_lift_own(idx)?;
Ok(HostResource::new_own(rep, ty))
} else {
let rep = tables.host_resource_lift_borrow(idx)?;
let res = tables.host_resource_drop(idx)?;
assert!(res.is_none());
Ok(HostResource::new_borrow(rep, ty))
}
}
pub fn ty(&self) -> ResourceType {
self.ty
}
pub fn owned(&self) -> bool {
self.owned
}
pub fn resource_drop(self, mut store: impl AsContextMut) -> Result<()> {
let mut store = store.as_context_mut();
store.0.validate_sync_call()?;
self.resource_drop_impl(&mut store)
}
#[cfg(feature = "async")]
pub async fn resource_drop_async(self, mut store: impl AsContextMut<Data: Send>) -> Result<()> {
let mut store = store.as_context_mut();
store
.on_fiber(|store| self.resource_drop_impl(store))
.await?
}
fn resource_drop_impl<T: 'static>(self, store: &mut StoreContextMut<'_, T>) -> Result<()> {
let pair = HostResourceTables::new_host(store.0).host_resource_drop(self.idx)?;
let (rep, slot) = match (pair, self.owned) {
(Some(pair), true) => pair,
(None, false) => return Ok(()),
_ => unreachable!(),
};
if let Some(instance) = slot.instance {
if !store.0.may_enter(instance) {
bail!(Trap::CannotEnterComponent);
}
}
let dtor = match slot.dtor {
Some(dtor) => dtor.as_non_null(),
None => return Ok(()),
};
let mut args = [ValRaw::u32(rep)];
let exit = if let Some(instance) = slot.instance
&& store.0.concurrency_support()
{
store.0.enter_sync_call(None, false, instance)?;
true
} else {
false
};
unsafe {
crate::Func::call_unchecked_raw(store, dtor, NonNull::from(&mut args))?;
}
if exit {
store.0.exit_sync_call(false)?;
}
Ok(())
}
fn lower_to_index<U>(&self, cx: &mut LowerContext<'_, U>, ty: InterfaceType) -> Result<u32> {
match ty {
InterfaceType::Own(t) => {
if cx.resource_type(t) != self.ty {
bail!("mismatched resource types");
}
let rep = cx.host_resource_lift_own(self.idx)?;
cx.guest_resource_lower_own(t, rep)
}
InterfaceType::Borrow(t) => {
if cx.resource_type(t) != self.ty {
bail!("mismatched resource types");
}
let rep = cx.host_resource_lift_borrow(self.idx)?;
cx.guest_resource_lower_borrow(t, rep)
}
_ => bad_type_info(),
}
}
fn lift_from_index(cx: &mut LiftContext<'_>, ty: InterfaceType, index: u32) -> Result<Self> {
match ty {
InterfaceType::Own(t) => {
let ty = cx.resource_type(t);
let (rep, dtor, flags) = cx.guest_resource_lift_own(t, index)?;
let idx = cx.host_resource_lower_own(rep, dtor, flags)?;
Ok(ResourceAny {
idx,
ty,
owned: true,
})
}
InterfaceType::Borrow(t) => {
let ty = cx.resource_type(t);
let rep = cx.guest_resource_lift_borrow(t, index)?;
let idx = cx.host_resource_lower_borrow(rep)?;
Ok(ResourceAny {
idx,
ty,
owned: false,
})
}
_ => bad_type_info(),
}
}
}
unsafe impl ComponentType for ResourceAny {
const ABI: CanonicalAbiInfo = CanonicalAbiInfo::SCALAR4;
type Lower = <u32 as ComponentType>::Lower;
fn typecheck(ty: &InterfaceType, _types: &InstanceType<'_>) -> Result<()> {
match ty {
InterfaceType::Own(_) | InterfaceType::Borrow(_) => Ok(()),
other => bail!("expected `own` or `borrow`, found `{}`", desc(other)),
}
}
}
unsafe impl Lower for ResourceAny {
fn linear_lower_to_flat<T>(
&self,
cx: &mut LowerContext<'_, T>,
ty: InterfaceType,
dst: &mut MaybeUninit<Self::Lower>,
) -> Result<()> {
self.lower_to_index(cx, ty)?
.linear_lower_to_flat(cx, InterfaceType::U32, dst)
}
fn linear_lower_to_memory<T>(
&self,
cx: &mut LowerContext<'_, T>,
ty: InterfaceType,
offset: usize,
) -> Result<()> {
self.lower_to_index(cx, ty)?
.linear_lower_to_memory(cx, InterfaceType::U32, offset)
}
}
unsafe impl Lift for ResourceAny {
fn linear_lift_from_flat(
cx: &mut LiftContext<'_>,
ty: InterfaceType,
src: &Self::Lower,
) -> Result<Self> {
let index = u32::linear_lift_from_flat(cx, InterfaceType::U32, src)?;
ResourceAny::lift_from_index(cx, ty, index)
}
fn linear_lift_from_memory(
cx: &mut LiftContext<'_>,
ty: InterfaceType,
bytes: &[u8],
) -> Result<Self> {
let index = u32::linear_lift_from_memory(cx, InterfaceType::U32, bytes)?;
ResourceAny::lift_from_index(cx, ty, index)
}
}