use alloc::{
collections::BTreeMap,
string::{String, ToString},
sync::Arc,
vec::Vec,
};
use miden_debug_types::{FileLineCol, Location};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use super::{AsmOpId, Decorator, DecoratorId, MastForestError, MastNodeId};
use crate::{
LexicographicWord, Word,
mast::serialization::{
StringTable,
asm_op::{AsmOpDataBuilder, AsmOpInfo},
decorator::{DecoratorDataBuilder, DecoratorInfo},
},
operations::{AssemblyOp, DebugVarInfo},
serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
utils::{Idx, IndexVec},
};
mod asm_op_storage;
pub use asm_op_storage::{AsmOpIndexError, OpToAsmOpId};
mod decorator_storage;
pub use decorator_storage::{
DecoratedLinks, DecoratedLinksIter, DecoratorIndexError, OpToDecoratorIds,
};
mod debug_var_storage;
pub use debug_var_storage::{DebugVarId, OpToDebugVarIds};
mod node_decorator_storage;
pub use node_decorator_storage::NodeToDecoratorIds;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DebugInfo {
decorators: IndexVec<DecoratorId, Decorator>,
op_decorator_storage: OpToDecoratorIds,
node_decorator_storage: NodeToDecoratorIds,
asm_ops: IndexVec<AsmOpId, AssemblyOp>,
asm_op_storage: OpToAsmOpId,
debug_vars: IndexVec<DebugVarId, DebugVarInfo>,
op_debug_var_storage: OpToDebugVarIds,
error_codes: BTreeMap<u64, Arc<str>>,
#[cfg_attr(feature = "serde", serde(skip))]
procedure_names: BTreeMap<LexicographicWord, Arc<str>>,
}
impl DebugInfo {
pub fn new() -> Self {
Self {
decorators: IndexVec::new(),
op_decorator_storage: OpToDecoratorIds::new(),
node_decorator_storage: NodeToDecoratorIds::new(),
asm_ops: IndexVec::new(),
asm_op_storage: OpToAsmOpId::new(),
debug_vars: IndexVec::new(),
op_debug_var_storage: OpToDebugVarIds::new(),
error_codes: BTreeMap::new(),
procedure_names: BTreeMap::new(),
}
}
pub fn with_capacity(
decorators_capacity: usize,
nodes_capacity: usize,
operations_capacity: usize,
decorator_ids_capacity: usize,
) -> Self {
Self {
decorators: IndexVec::with_capacity(decorators_capacity),
op_decorator_storage: OpToDecoratorIds::with_capacity(
nodes_capacity,
operations_capacity,
decorator_ids_capacity,
),
node_decorator_storage: NodeToDecoratorIds::with_capacity(nodes_capacity, 0, 0),
asm_ops: IndexVec::new(),
asm_op_storage: OpToAsmOpId::new(),
debug_vars: IndexVec::new(),
op_debug_var_storage: OpToDebugVarIds::new(),
error_codes: BTreeMap::new(),
procedure_names: BTreeMap::new(),
}
}
pub fn empty_for_nodes(num_nodes: usize) -> Self {
let node_indptr_for_op_idx = IndexVec::try_from(vec![0; num_nodes + 1])
.expect("num_nodes should not exceed u32::MAX");
let op_decorator_storage =
OpToDecoratorIds::from_components(Vec::new(), Vec::new(), node_indptr_for_op_idx)
.expect("Empty CSR structure should be valid");
Self {
decorators: IndexVec::new(),
op_decorator_storage,
node_decorator_storage: NodeToDecoratorIds::new(),
asm_ops: IndexVec::new(),
asm_op_storage: OpToAsmOpId::new(),
debug_vars: IndexVec::new(),
op_debug_var_storage: OpToDebugVarIds::new(),
error_codes: BTreeMap::new(),
procedure_names: BTreeMap::new(),
}
}
pub fn is_empty(&self) -> bool {
self.decorators.is_empty()
&& self.asm_ops.is_empty()
&& self.debug_vars.is_empty()
&& self.error_codes.is_empty()
&& self.procedure_names.is_empty()
}
pub fn clear(&mut self) {
self.clear_mappings();
self.decorators = IndexVec::new();
self.asm_ops = IndexVec::new();
self.asm_op_storage = OpToAsmOpId::new();
self.debug_vars = IndexVec::new();
self.op_debug_var_storage.clear();
self.error_codes.clear();
self.procedure_names.clear();
}
pub fn num_decorators(&self) -> usize {
self.decorators.len()
}
pub fn decorators(&self) -> &[Decorator] {
self.decorators.as_slice()
}
pub fn decorator(&self, decorator_id: DecoratorId) -> Option<&Decorator> {
self.decorators.get(decorator_id)
}
pub fn before_enter_decorators(&self, node_id: MastNodeId) -> &[DecoratorId] {
self.node_decorator_storage.get_before_decorators(node_id)
}
pub fn after_exit_decorators(&self, node_id: MastNodeId) -> &[DecoratorId] {
self.node_decorator_storage.get_after_decorators(node_id)
}
pub fn decorators_for_operation(
&self,
node_id: MastNodeId,
local_op_idx: usize,
) -> &[DecoratorId] {
self.op_decorator_storage
.decorator_ids_for_operation(node_id, local_op_idx)
.unwrap_or(&[])
}
pub(super) fn decorator_links_for_node(
&self,
node_id: MastNodeId,
) -> Result<DecoratedLinks<'_>, DecoratorIndexError> {
self.op_decorator_storage.decorator_links_for_node(node_id)
}
pub fn num_debug_vars(&self) -> usize {
self.debug_vars.len()
}
pub fn debug_vars(&self) -> &[DebugVarInfo] {
self.debug_vars.as_slice()
}
pub fn debug_var(&self, debug_var_id: DebugVarId) -> Option<&DebugVarInfo> {
self.debug_vars.get(debug_var_id)
}
pub fn debug_vars_for_node(&self, node_id: MastNodeId) -> Vec<(usize, DebugVarId)> {
self.op_debug_var_storage.debug_vars_for_node(node_id)
}
pub fn debug_vars_for_operation(
&self,
node_id: MastNodeId,
local_op_idx: usize,
) -> &[DebugVarId] {
self.op_debug_var_storage
.debug_var_ids_for_operation(node_id, local_op_idx)
.unwrap_or(&[])
}
pub fn add_decorator(&mut self, decorator: Decorator) -> Result<DecoratorId, MastForestError> {
self.decorators.push(decorator).map_err(|_| MastForestError::TooManyDecorators)
}
pub(super) fn decorator_mut(&mut self, decorator_id: DecoratorId) -> Option<&mut Decorator> {
if decorator_id.to_usize() < self.decorators.len() {
Some(&mut self.decorators[decorator_id])
} else {
None
}
}
pub(super) fn register_node_decorators(
&mut self,
node_id: MastNodeId,
before_enter: &[DecoratorId],
after_exit: &[DecoratorId],
) {
self.node_decorator_storage
.add_node_decorators(node_id, before_enter, after_exit);
}
pub(crate) fn register_op_indexed_decorators(
&mut self,
node_id: MastNodeId,
decorators_info: Vec<(usize, DecoratorId)>,
) -> Result<(), crate::mast::debuginfo::decorator_storage::DecoratorIndexError> {
self.op_decorator_storage.add_decorator_info_for_node(node_id, decorators_info)
}
pub fn clear_mappings(&mut self) {
self.op_decorator_storage = OpToDecoratorIds::new();
self.node_decorator_storage.clear();
}
pub fn num_asm_ops(&self) -> usize {
self.asm_ops.len()
}
pub fn asm_ops(&self) -> &[AssemblyOp] {
self.asm_ops.as_slice()
}
pub fn asm_op(&self, asm_op_id: AsmOpId) -> Option<&AssemblyOp> {
self.asm_ops.get(asm_op_id)
}
pub fn asm_op_for_operation(&self, node_id: MastNodeId, op_idx: usize) -> Option<&AssemblyOp> {
let asm_op_id = self.asm_op_storage.asm_op_id_for_operation(node_id, op_idx)?;
self.asm_ops.get(asm_op_id)
}
pub fn first_asm_op_for_node(&self, node_id: MastNodeId) -> Option<&AssemblyOp> {
let asm_op_id = self.asm_op_storage.first_asm_op_for_node(node_id)?;
self.asm_ops.get(asm_op_id)
}
pub fn asm_ops_for_node(&self, node_id: MastNodeId) -> Vec<(usize, AsmOpId)> {
self.asm_op_storage.asm_ops_for_node(node_id)
}
pub fn add_asm_op(&mut self, asm_op: AssemblyOp) -> Result<AsmOpId, MastForestError> {
self.asm_ops.push(asm_op).map_err(|_| MastForestError::TooManyDecorators)
}
pub fn rewrite_source_locations(
&mut self,
mut rewrite_location: impl FnMut(Location) -> Location,
mut rewrite_file_line_col: impl FnMut(FileLineCol) -> FileLineCol,
) {
for asm_op in self.asm_ops.iter_mut() {
if let Some(location) = asm_op.location().cloned() {
asm_op.set_location(rewrite_location(location));
}
}
for debug_var in self.debug_vars.iter_mut() {
if let Some(location) = debug_var.location().cloned() {
debug_var.set_location(rewrite_file_line_col(location));
}
}
}
pub fn register_asm_ops(
&mut self,
node_id: MastNodeId,
num_operations: usize,
asm_ops: Vec<(usize, AsmOpId)>,
) -> Result<(), AsmOpIndexError> {
self.asm_op_storage.add_asm_ops_for_node(node_id, num_operations, asm_ops)
}
pub(super) fn remap_asm_op_storage(&mut self, remapping: &BTreeMap<MastNodeId, MastNodeId>) {
self.asm_op_storage = self.asm_op_storage.remap_nodes(remapping);
}
pub(super) fn remap_debug_var_storage(&mut self, remapping: &BTreeMap<MastNodeId, MastNodeId>) {
self.op_debug_var_storage = self.op_debug_var_storage.remap_nodes(remapping);
}
pub fn add_debug_var(
&mut self,
debug_var: DebugVarInfo,
) -> Result<DebugVarId, MastForestError> {
self.debug_vars.push(debug_var).map_err(|_| MastForestError::TooManyDecorators)
}
pub fn register_op_indexed_debug_vars(
&mut self,
node_id: MastNodeId,
debug_vars_info: Vec<(usize, DebugVarId)>,
) -> Result<(), crate::mast::debuginfo::decorator_storage::DecoratorIndexError> {
self.op_debug_var_storage.add_debug_var_info_for_node(node_id, debug_vars_info)
}
pub fn error_message(&self, code: u64) -> Option<Arc<str>> {
self.error_codes.get(&code).cloned()
}
pub fn error_codes(&self) -> impl Iterator<Item = (&u64, &Arc<str>)> {
self.error_codes.iter()
}
pub fn insert_error_code(&mut self, code: u64, msg: Arc<str>) {
self.error_codes.insert(code, msg);
}
pub fn extend_error_codes<I>(&mut self, error_codes: I)
where
I: IntoIterator<Item = (u64, Arc<str>)>,
{
self.error_codes.extend(error_codes);
}
pub fn clear_error_codes(&mut self) {
self.error_codes.clear();
}
pub fn procedure_name(&self, digest: &Word) -> Option<&str> {
self.procedure_names.get(&LexicographicWord::from(*digest)).map(|s| s.as_ref())
}
pub fn procedure_names(&self) -> impl Iterator<Item = (Word, &Arc<str>)> {
self.procedure_names.iter().map(|(key, name)| (key.into_inner(), name))
}
pub fn num_procedure_names(&self) -> usize {
self.procedure_names.len()
}
pub fn insert_procedure_name(&mut self, digest: Word, name: Arc<str>) {
self.procedure_names.insert(LexicographicWord::from(digest), name);
}
pub fn extend_procedure_names<I>(&mut self, names: I)
where
I: IntoIterator<Item = (Word, Arc<str>)>,
{
self.procedure_names
.extend(names.into_iter().map(|(d, n)| (LexicographicWord::from(d), n)));
}
pub fn clear_procedure_names(&mut self) {
self.procedure_names.clear();
}
pub(super) fn validate(&self) -> Result<(), String> {
let decorator_count = self.decorators.len();
let asm_op_count = self.asm_ops.len();
self.op_decorator_storage.validate_csr(decorator_count)?;
self.node_decorator_storage.validate_csr(decorator_count)?;
self.asm_op_storage.validate_csr(asm_op_count)?;
let debug_var_count = self.debug_vars.len();
self.op_debug_var_storage.validate_csr(debug_var_count)?;
Ok(())
}
#[cfg(test)]
pub(crate) fn op_decorator_storage(&self) -> &OpToDecoratorIds {
&self.op_decorator_storage
}
}
impl Serializable for DebugInfo {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
let mut decorator_data_builder = DecoratorDataBuilder::new();
for decorator in self.decorators.iter() {
decorator_data_builder.add_decorator(decorator);
}
let (decorator_data, decorator_infos, string_table) = decorator_data_builder.finalize();
decorator_data.write_into(target);
string_table.write_into(target);
decorator_infos.write_into(target);
let error_codes: BTreeMap<u64, String> =
self.error_codes.iter().map(|(k, v)| (*k, v.to_string())).collect();
error_codes.write_into(target);
self.op_decorator_storage.write_into(target);
self.node_decorator_storage.write_into(target);
let procedure_names: BTreeMap<Word, String> =
self.procedure_names().map(|(k, v)| (k, v.to_string())).collect();
procedure_names.write_into(target);
let mut asm_op_data_builder = AsmOpDataBuilder::new();
for asm_op in self.asm_ops.iter() {
asm_op_data_builder.add_asm_op(asm_op);
}
let (asm_op_data, asm_op_infos, asm_op_string_table) = asm_op_data_builder.finalize();
asm_op_data.write_into(target);
asm_op_string_table.write_into(target);
asm_op_infos.write_into(target);
self.asm_op_storage.write_into(target);
self.debug_vars.write_into(target);
self.op_debug_var_storage.write_into(target);
}
}
impl Deserializable for DebugInfo {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let decorator_data: Vec<u8> = Deserializable::read_from(source)?;
let string_table: StringTable = Deserializable::read_from(source)?;
let decorator_infos: Vec<DecoratorInfo> = Deserializable::read_from(source)?;
let mut decorators = IndexVec::new();
for decorator_info in decorator_infos {
let decorator = decorator_info.try_into_decorator(&string_table, &decorator_data)?;
decorators.push(decorator).map_err(|_| {
DeserializationError::InvalidValue(
"Failed to add decorator to IndexVec".to_string(),
)
})?;
}
let error_codes_raw: BTreeMap<u64, String> = Deserializable::read_from(source)?;
let error_codes: BTreeMap<u64, Arc<str>> =
error_codes_raw.into_iter().map(|(k, v)| (k, Arc::from(v.as_str()))).collect();
let op_decorator_storage = OpToDecoratorIds::read_from(source, decorators.len())?;
let node_decorator_storage = NodeToDecoratorIds::read_from(source, decorators.len())?;
let procedure_names_raw: BTreeMap<Word, String> = Deserializable::read_from(source)?;
let procedure_names: BTreeMap<LexicographicWord, Arc<str>> = procedure_names_raw
.into_iter()
.map(|(k, v)| (LexicographicWord::from(k), Arc::from(v.as_str())))
.collect();
let asm_op_data: Vec<u8> = Deserializable::read_from(source)?;
let asm_op_string_table: StringTable = Deserializable::read_from(source)?;
let asm_op_infos: Vec<AsmOpInfo> = Deserializable::read_from(source)?;
let mut asm_ops = IndexVec::new();
for asm_op_info in asm_op_infos {
let asm_op = asm_op_info.try_into_asm_op(&asm_op_string_table, &asm_op_data)?;
asm_ops.push(asm_op).map_err(|_| {
DeserializationError::InvalidValue(
"Failed to add AssemblyOp to IndexVec".to_string(),
)
})?;
}
let asm_op_storage = OpToAsmOpId::read_from(source, asm_ops.len())?;
let debug_vars: IndexVec<DebugVarId, DebugVarInfo> = Deserializable::read_from(source)?;
let op_debug_var_storage = OpToDebugVarIds::read_from(source, debug_vars.len())?;
let debug_info = DebugInfo {
decorators,
op_decorator_storage,
node_decorator_storage,
asm_ops,
asm_op_storage,
debug_vars,
op_debug_var_storage,
error_codes,
procedure_names,
};
debug_info.validate().map_err(|e| {
DeserializationError::InvalidValue(format!("DebugInfo validation failed: {}", e))
})?;
Ok(debug_info)
}
}
impl Default for DebugInfo {
fn default() -> Self {
Self::new()
}
}