use core::any::Any;
use crate::extern_::{Global, Memory, Table, Tag};
use crate::func::Func;
use crate::instance::Instance;
use crate::module::Module;
use crate::value::{FuncType, GlobalType, MemoryType, Ref, TableType, TagType, Val};
use crate::{Error, Result};
use super::reclaim::ReclaimArena;
fn alloc_err() -> Error {
Error::msg("failed to allocate the initial memory or table")
}
const ARENA_ENTRY_OVERHEAD: usize = 64;
pub(crate) enum ExternEntry {
Host(Box<dyn Any + Send + Sync>),
Internal(u32),
}
impl ExternEntry {
pub(crate) fn byte_size(&self) -> usize {
ARENA_ENTRY_OVERHEAD
}
}
pub(crate) fn extern_charge() -> usize {
ARENA_ENTRY_OVERHEAD
}
pub(crate) type ExternRefs = ReclaimArena<ExternEntry>;
pub(crate) fn exn_charge(n_args: usize) -> usize {
ARENA_ENTRY_OVERHEAD.saturating_add(n_args.saturating_mul(core::mem::size_of::<Val>()))
}
pub(crate) const PAGE_SIZE: usize = 64 * 1024;
const MAX_PAGES: u64 = 1 << 16;
#[derive(Debug)]
pub(crate) struct MemoryEntity {
pub bytes: Vec<u8>,
pub ty: MemoryType,
}
#[derive(Debug)]
pub(crate) struct GlobalEntity {
pub value: Val,
pub ty: GlobalType,
}
#[derive(Debug)]
pub(crate) struct TableEntity {
pub elems: Vec<Ref>,
pub ty: TableType,
}
#[derive(Debug)]
pub(crate) struct TagEntity {
pub ty: TagType,
}
#[derive(Debug)]
pub(crate) struct ExnEntity {
pub tag: Tag,
pub args: Vec<Val>,
pub backtrace: Option<crate::backtrace::WasmBacktrace>,
}
impl ExnEntity {
pub(crate) fn byte_size(&self) -> usize {
exn_charge(self.args.len())
}
}
#[derive(Debug)]
pub(crate) enum FuncEntity {
Wasm {
instance: Instance,
func_index: u32,
},
Host {
ty: FuncType,
sig: std::sync::Arc<HostSig>,
host_index: u32,
},
#[cfg(feature = "async")]
HostAsync {
ty: FuncType,
sig: std::sync::Arc<HostSig>,
host_index: u32,
},
}
#[derive(Debug)]
pub(crate) struct InstanceEntity {
pub module: Module,
pub funcs: Vec<Func>,
pub memories: Vec<Memory>,
pub globals: Vec<Global>,
pub tables: Vec<Table>,
pub tags: Vec<Tag>,
pub dropped_data: Vec<bool>,
pub elems: Vec<Vec<Ref>>,
}
impl MemoryEntity {
pub(crate) fn new(ty: MemoryType) -> Result<Self> {
let len = usize::try_from(ty.minimum())
.map_err(|_| alloc_err())?
.checked_mul(PAGE_SIZE)
.ok_or_else(alloc_err)?;
let mut bytes = Vec::new();
bytes.try_reserve_exact(len).map_err(|_| alloc_err())?;
bytes.resize(len, 0);
Ok(MemoryEntity { bytes, ty })
}
pub(crate) fn size_pages(&self) -> u64 {
(self.bytes.len() / PAGE_SIZE) as u64
}
pub(crate) fn grow(&mut self, delta: u64) -> Option<u64> {
let old = self.size_pages();
let new = old.checked_add(delta)?;
let arch_max = if self.ty.is_64() {
1u64 << 48
} else {
MAX_PAGES
};
let max = self.ty.maximum().unwrap_or(arch_max).min(arch_max);
if new > max {
return None;
}
let new_bytes = usize::try_from(new).ok()?.checked_mul(PAGE_SIZE)?;
self.bytes
.try_reserve_exact(new_bytes.saturating_sub(self.bytes.len()))
.ok()?;
self.bytes.resize(new_bytes, 0);
Some(old)
}
}
impl TableEntity {
pub(crate) fn new(ty: TableType, init: Ref) -> Result<Self> {
let len = usize::try_from(ty.minimum()).map_err(|_| alloc_err())?;
let mut elems = Vec::new();
elems.try_reserve_exact(len).map_err(|_| alloc_err())?;
elems.resize(len, init);
Ok(TableEntity { elems, ty })
}
pub(crate) fn size(&self) -> u64 {
self.elems.len() as u64
}
pub(crate) fn get(&self, index: u64) -> Option<Ref> {
self.elems.get(usize::try_from(index).ok()?).cloned()
}
pub(crate) fn set(&mut self, index: u64, val: Ref) -> bool {
let Ok(index) = usize::try_from(index) else {
return false;
};
match self.elems.get_mut(index) {
Some(slot) => {
*slot = val;
true
}
None => false,
}
}
pub(crate) fn grow(&mut self, delta: u64, init: Ref) -> Option<u64> {
let old = self.size();
let new = old.checked_add(delta)?;
let arch_max = if self.ty.is_64() {
u64::MAX
} else {
u64::from(u32::MAX)
};
let max = self.ty.maximum().unwrap_or(arch_max).min(arch_max);
if new > max {
return None;
}
let new = usize::try_from(new).ok()?;
self.elems.try_reserve_exact(new - self.elems.len()).ok()?;
self.elems.resize(new, init);
Some(old)
}
pub(crate) fn fill(&mut self, dst: u64, val: Ref, len: u64) -> bool {
let end = match dst.checked_add(len) {
Some(end) if end <= self.size() => end,
_ => return false,
};
for i in dst..end {
self.elems[i as usize] = val.clone();
}
true
}
}
#[derive(Debug)]
pub(crate) struct HostSig {
pub params: Box<[crate::value::ValType]>,
pub results: Box<[crate::value::ValType]>,
pub result_defaults: Box<[crate::value::Val]>,
}
impl HostSig {
pub(crate) fn new(ty: &FuncType) -> std::sync::Arc<HostSig> {
let results: Box<[_]> = ty.results().collect();
std::sync::Arc::new(HostSig {
params: ty.params().collect(),
result_defaults: results
.iter()
.map(crate::value::Val::default_for_valtype)
.collect(),
results,
})
}
}