use crate::ir::entities::{ExceptionTag, SigRef};
use crate::ir::instructions::ValueListPool;
use crate::ir::{BlockCall, Value};
use alloc::vec::Vec;
use core::fmt::{self, Display, Formatter};
#[cfg(feature = "enable-serde")]
use serde_derive::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct ExceptionTableData {
targets: Vec<BlockCall>,
items: Vec<InternalExceptionTableItem>,
sig: SigRef,
}
#[derive(Clone, Debug)]
pub enum ExceptionTableItem {
Tag(ExceptionTag, BlockCall),
Default(BlockCall),
Context(Value),
}
#[derive(Clone, Debug, PartialEq, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
enum InternalExceptionTableItem {
Tag(ExceptionTag, u32),
Default(u32),
Context(Value),
}
impl ExceptionTableData {
pub fn new(
sig: SigRef,
normal_return: BlockCall,
matches: impl IntoIterator<Item = ExceptionTableItem>,
) -> Self {
let mut targets = vec![];
let mut items = vec![];
for item in matches {
let target_idx = u32::try_from(targets.len()).unwrap();
match item {
ExceptionTableItem::Tag(tag, target) => {
items.push(InternalExceptionTableItem::Tag(tag, target_idx));
targets.push(target);
}
ExceptionTableItem::Default(target) => {
items.push(InternalExceptionTableItem::Default(target_idx));
targets.push(target);
}
ExceptionTableItem::Context(ctx) => {
items.push(InternalExceptionTableItem::Context(ctx));
}
}
}
targets.push(normal_return);
ExceptionTableData {
targets,
items,
sig,
}
}
pub fn display<'a>(&'a self, pool: &'a ValueListPool) -> DisplayExceptionTable<'a> {
DisplayExceptionTable { table: self, pool }
}
pub fn deep_clone(&self, pool: &mut ValueListPool) -> Self {
Self {
targets: self.targets.iter().map(|b| b.deep_clone(pool)).collect(),
items: self.items.clone(),
sig: self.sig,
}
}
pub fn normal_return(&self) -> &BlockCall {
self.targets.last().unwrap()
}
pub fn normal_return_mut(&mut self) -> &mut BlockCall {
self.targets.last_mut().unwrap()
}
pub fn items(&self) -> impl Iterator<Item = ExceptionTableItem> + '_ {
self.items.iter().map(|item| match item {
InternalExceptionTableItem::Tag(tag, target_idx) => {
ExceptionTableItem::Tag(*tag, self.targets[usize::try_from(*target_idx).unwrap()])
}
InternalExceptionTableItem::Default(target_idx) => {
ExceptionTableItem::Default(self.targets[usize::try_from(*target_idx).unwrap()])
}
InternalExceptionTableItem::Context(ctx) => ExceptionTableItem::Context(*ctx),
})
}
pub fn all_branches(&self) -> &[BlockCall] {
&self.targets[..]
}
pub fn all_branches_mut(&mut self) -> &mut [BlockCall] {
&mut self.targets[..]
}
pub fn signature(&self) -> SigRef {
self.sig
}
pub(crate) fn signature_mut(&mut self) -> &mut SigRef {
&mut self.sig
}
pub(crate) fn contexts(&self) -> impl DoubleEndedIterator<Item = Value> {
self.items.iter().filter_map(|item| match item {
InternalExceptionTableItem::Context(ctx) => Some(*ctx),
_ => None,
})
}
pub(crate) fn contexts_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut Value> {
self.items.iter_mut().filter_map(|item| match item {
InternalExceptionTableItem::Context(ctx) => Some(ctx),
_ => None,
})
}
pub fn clear(&mut self) {
self.items.clear();
self.targets.clear();
}
}
pub struct DisplayExceptionTable<'a> {
table: &'a ExceptionTableData,
pool: &'a ValueListPool,
}
impl<'a> Display for DisplayExceptionTable<'a> {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
write!(
fmt,
"{}, {}, [",
self.table.sig,
self.table.normal_return().display(self.pool)
)?;
let mut first = true;
for item in self.table.items() {
if first {
write!(fmt, " ")?;
first = false;
} else {
write!(fmt, ", ")?;
}
match item {
ExceptionTableItem::Tag(tag, block_call) => {
write!(fmt, "{}: {}", tag, block_call.display(self.pool))?;
}
ExceptionTableItem::Default(block_call) => {
write!(fmt, "default: {}", block_call.display(self.pool))?;
}
ExceptionTableItem::Context(ctx) => {
write!(fmt, "context {ctx}")?;
}
}
}
let space = if first { "" } else { " " };
write!(fmt, "{space}]")?;
Ok(())
}
}