use crate::ir::BlockCall;
use crate::ir::entities::{ExceptionTag, SigRef};
use crate::ir::instructions::ValueListPool;
use alloc::vec::Vec;
use core::fmt::{self, Display, Formatter};
use cranelift_entity::packed_option::PackedOption;
#[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>,
tags: Vec<PackedOption<ExceptionTag>>,
sig: SigRef,
}
impl ExceptionTableData {
pub fn new(
sig: SigRef,
normal_return: BlockCall,
tags_and_targets: impl IntoIterator<Item = (Option<ExceptionTag>, BlockCall)>,
) -> Self {
let mut targets = vec![];
let mut tags = vec![];
for (tag, target) in tags_and_targets {
tags.push(tag.into());
targets.push(target);
}
targets.push(normal_return);
ExceptionTableData { targets, tags, sig }
}
pub fn display<'a>(&'a self, pool: &'a ValueListPool) -> DisplayExceptionTable<'a> {
DisplayExceptionTable { table: self, pool }
}
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 catches(&self) -> impl Iterator<Item = (Option<ExceptionTag>, &BlockCall)> + '_ {
self.tags
.iter()
.map(|tag| tag.expand())
.zip(self.targets.iter())
}
pub fn catches_mut(
&mut self,
) -> impl Iterator<Item = (Option<ExceptionTag>, &mut BlockCall)> + '_ {
self.tags
.iter()
.map(|tag| tag.expand())
.zip(self.targets.iter_mut())
}
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 fn clear(&mut self) {
self.tags.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 (tag, block_call) in self.table.catches() {
if first {
write!(fmt, " ")?;
first = false;
} else {
write!(fmt, ", ")?;
}
if let Some(tag) = tag {
write!(fmt, "{}: {}", tag, block_call.display(self.pool))?;
} else {
write!(fmt, "default: {}", block_call.display(self.pool))?;
}
}
let space = if first { "" } else { " " };
write!(fmt, "{space}]")?;
Ok(())
}
}