use super::{HandleTable, InstanceState, RemovedResource};
use crate::component::store::ComponentTaskState;
use crate::prelude::*;
use core::error::Error;
use core::fmt;
use core::mem;
use wasmtime_environ::PrimaryMap;
use wasmtime_environ::component::{
ComponentTypes, RuntimeComponentInstanceIndex, TypeResourceTableIndex,
};
pub struct ResourceTables<'a> {
pub guest: Option<(
&'a mut PrimaryMap<RuntimeComponentInstanceIndex, InstanceState>,
&'a ComponentTypes,
)>,
pub host_table: &'a mut HandleTable,
pub task_state: &'a mut ComponentTaskState,
}
#[derive(Debug)]
pub enum TypedResource {
Host(u32),
Component {
rep: u32,
ty: TypeResourceTableIndex,
},
}
impl TypedResource {
pub(super) fn rep(&self, access_ty: &TypedResourceIndex) -> Result<u32> {
match (self, access_ty) {
(Self::Host(rep), TypedResourceIndex::Host(_)) => Ok(*rep),
(Self::Host(_), expected) => bail!(ResourceTypeMismatch {
expected: *expected,
found: "host resource",
}),
(Self::Component { rep, ty }, TypedResourceIndex::Component { ty: expected, .. }) => {
if ty == expected {
Ok(*rep)
} else {
bail!(ResourceTypeMismatch {
expected: *access_ty,
found: "a different guest-defined resource",
})
}
}
(Self::Component { .. }, expected) => bail!(ResourceTypeMismatch {
expected: *expected,
found: "guest-defined resource",
}),
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum TypedResourceIndex {
Host(u32),
Component {
index: u32,
ty: TypeResourceTableIndex,
},
}
impl TypedResourceIndex {
pub(super) fn raw_index(&self) -> u32 {
match self {
Self::Host(index) | Self::Component { index, .. } => *index,
}
}
fn desc(&self) -> &'static str {
match self {
Self::Host(_) => "host resource",
Self::Component { .. } => "guest-defined resource",
}
}
}
#[derive(Default)]
pub struct CallContext {
lenders: Vec<TypedResourceIndex>,
borrow_count: u32,
}
impl ResourceTables<'_> {
fn table_for_resource(&mut self, resource: &TypedResource) -> &mut HandleTable {
match resource {
TypedResource::Host(_) => self.host_table,
TypedResource::Component { ty, .. } => {
let (states, types) = self.guest.as_mut().unwrap();
states[types[*ty].unwrap_concrete_instance()].handle_table()
}
}
}
fn table_for_index(&mut self, index: &TypedResourceIndex) -> &mut HandleTable {
match index {
TypedResourceIndex::Host(_) => self.host_table,
TypedResourceIndex::Component { ty, .. } => {
let (states, types) = self.guest.as_mut().unwrap();
states[types[*ty].unwrap_concrete_instance()].handle_table()
}
}
}
pub fn resource_new(&mut self, resource: TypedResource) -> Result<u32> {
self.table_for_resource(&resource)
.resource_own_insert(resource)
}
pub fn resource_rep(&mut self, index: TypedResourceIndex) -> Result<u32> {
self.table_for_index(&index).resource_rep(index)
}
pub fn resource_drop(&mut self, index: TypedResourceIndex) -> Result<Option<u32>> {
match self.table_for_index(&index).remove_resource(index)? {
RemovedResource::Own { rep } => Ok(Some(rep)),
RemovedResource::Borrow { scope } => {
self.task_state.call_context(scope)?.borrow_count -= 1;
Ok(None)
}
}
}
pub fn resource_lower_own(&mut self, resource: TypedResource) -> Result<u32> {
self.table_for_resource(&resource)
.resource_own_insert(resource)
}
pub fn resource_lift_own(&mut self, index: TypedResourceIndex) -> Result<u32> {
match self.table_for_index(&index).remove_resource(index)? {
RemovedResource::Own { rep } => Ok(rep),
RemovedResource::Borrow { .. } => bail!("cannot lift own resource from a borrow"),
}
}
pub fn resource_lift_borrow(&mut self, index: TypedResourceIndex) -> Result<u32> {
let (rep, is_own) = self.table_for_index(&index).resource_lend(index)?;
if is_own {
let scope = self.task_state.current_call_context_scope_id()?;
self.task_state.call_context(scope)?.lenders.push(index);
}
Ok(rep)
}
pub fn resource_lower_borrow(&mut self, resource: TypedResource) -> Result<u32> {
let scope = self.task_state.current_call_context_scope_id()?;
let cx = self.task_state.call_context(scope)?;
cx.borrow_count = cx.borrow_count.checked_add(1).unwrap();
self.table_for_resource(&resource)
.resource_borrow_insert(resource, scope)
}
#[inline]
pub fn validate_scope_exit(&mut self) -> Result<()> {
let current = self.task_state.current_call_context_scope_id()?;
let cx = self.task_state.call_context(current)?;
if cx.borrow_count > 0 {
bail!("borrow handles still remain at the end of the call")
}
for lender in mem::take(&mut cx.lenders) {
self.table_for_index(&lender)
.resource_undo_lend(lender)
.unwrap();
}
Ok(())
}
}
#[derive(Debug)]
struct ResourceTypeMismatch {
expected: TypedResourceIndex,
found: &'static str,
}
impl fmt::Display for ResourceTypeMismatch {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"handle index {} used with the wrong type, \
expected {} but found {}",
self.expected.raw_index(),
self.expected.desc(),
self.found,
)
}
}
impl Error for ResourceTypeMismatch {}