use super::{HandleCounter, HandleTable, InstanceState, RemovedResource};
use crate::bail_bug;
use crate::component::store::ComponentTaskState;
use crate::prelude::*;
use core::error::Error;
use core::fmt;
use core::mem;
use wasmtime_environ::component::{
ComponentTypes, RuntimeComponentInstanceIndex, TypeResourceTableIndex,
};
use wasmtime_environ::prelude::TryPrimaryMap;
pub struct ResourceTables<'a> {
pub guest: Option<(
&'a mut TryPrimaryMap<RuntimeComponentInstanceIndex, InstanceState>,
&'a ComponentTypes,
)>,
pub host_table: &'a mut HandleTable,
pub handle_counter: &'a mut HandleCounter,
pub task_state: &'a mut ComponentTaskState,
pub current_scope: Option<CurrentScope>,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum CurrentScope {
Id(Scope),
#[cfg_attr(not(feature = "component-model-async"), allow(dead_code))]
DeferredHost,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Scope {
Id(u32),
HostId(u32),
}
#[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 CallContext {
#[cfg_attr(not(feature = "component-model-async"), allow(dead_code))]
pub(crate) fn is_empty(&self) -> bool {
self.lenders.is_empty() && self.borrow_count == 0
}
}
impl ResourceTables<'_> {
fn table_for_resource(
&mut self,
resource: &TypedResource,
) -> (&mut HandleTable, &mut HandleCounter) {
let table = match resource {
TypedResource::Host(_) => &mut *self.host_table,
TypedResource::Component { ty, .. } => {
let (states, types) = self.guest.as_mut().unwrap();
states[types[*ty].unwrap_concrete_instance()].handle_table()
}
};
(table, &mut *self.handle_counter)
}
fn table_for_index(
&mut self,
index: &TypedResourceIndex,
) -> (&mut HandleTable, &mut HandleCounter) {
let table = match index {
TypedResourceIndex::Host(_) => &mut *self.host_table,
TypedResourceIndex::Component { ty, .. } => {
let (states, types) = self.guest.as_mut().unwrap();
states[types[*ty].unwrap_concrete_instance()].handle_table()
}
};
(table, &mut *self.handle_counter)
}
pub fn resource_new(&mut self, resource: TypedResource) -> Result<u32> {
let (table, counter) = self.table_for_resource(&resource);
table.resource_own_insert(counter, resource)
}
pub fn resource_rep(&mut self, index: TypedResourceIndex) -> Result<u32> {
self.table_for_index(&index).0.resource_rep(index)
}
pub fn resource_drop(&mut self, index: TypedResourceIndex) -> Result<Option<u32>> {
let (table, counter) = self.table_for_index(&index);
match table.remove_resource(counter, 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> {
let (table, counter) = self.table_for_resource(&resource);
table.resource_own_insert(counter, resource)
}
pub fn resource_lift_own(&mut self, index: TypedResourceIndex) -> Result<u32> {
let (table, counter) = self.table_for_index(&index);
match table.remove_resource(counter, index)? {
RemovedResource::Own { rep } => Ok(rep),
RemovedResource::Borrow { .. } => bail!("cannot lift own resource from a borrow"),
}
}
fn materialize_current_scope(&mut self) -> Result<Scope> {
let id = match self.current_scope {
Some(CurrentScope::Id(id)) => return Ok(id),
Some(CurrentScope::DeferredHost) => self.task_state.materialize_current_scope()?,
None => bail_bug!("no current scope"),
};
self.current_scope = Some(CurrentScope::Id(id));
Ok(id)
}
fn current_scope(&mut self) -> Result<&mut CallContext> {
match self.current_scope {
Some(CurrentScope::Id(id)) => self.task_state.call_context(id),
Some(CurrentScope::DeferredHost) => {
match self.task_state.deferred_host_call_context() {
Some(cx) => Ok(cx),
None => bail_bug!("deferred host scope has no call context"),
}
}
None => bail_bug!("no current scope"),
}
}
pub fn resource_lift_borrow(&mut self, index: TypedResourceIndex) -> Result<u32> {
let (rep, is_own) = self.table_for_index(&index).0.resource_lend(index)?;
if is_own {
self.current_scope()?.lenders.push(index);
}
Ok(rep)
}
pub fn resource_lower_borrow(&mut self, resource: TypedResource) -> Result<u32> {
let scope = self.materialize_current_scope()?;
let cx = self.task_state.call_context(scope)?;
cx.borrow_count = cx.borrow_count.checked_add(1).unwrap();
let (table, counter) = self.table_for_resource(&resource);
table.resource_borrow_insert(counter, resource, scope)
}
#[inline]
pub fn validate_scope_exit(&mut self) -> Result<()> {
let cx = self.current_scope()?;
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)
.0
.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 {}