use crate::trace_data::CasmLevelInfo;
use cairo_lang_sierra::program::StatementIdx;
use cairo_lang_sierra_to_casm::compiler::{CairoProgramDebugInfo, SierraStatementDebugInfo};
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum MappingResult {
SierraStatementIdx(StatementIdx),
Header,
PcOutOfFunctionArea,
}
impl From<MappingResult> for Option<StatementIdx> {
fn from(mapping_result: MappingResult) -> Self {
match mapping_result {
MappingResult::SierraStatementIdx(statement_idx) => Some(statement_idx),
MappingResult::Header | MappingResult::PcOutOfFunctionArea => None,
}
}
}
#[must_use]
pub fn map_pcs_to_sierra_statement_ids(
CairoProgramDebugInfo {
sierra_statement_info,
}: &CairoProgramDebugInfo,
CasmLevelInfo {
run_with_call_header,
vm_trace,
program_offset,
}: &CasmLevelInfo,
) -> Vec<MappingResult> {
if sierra_statement_info.is_empty() {
return Vec::new();
}
let real_minimal_pc = if let Some(offset) = program_offset {
offset + 1
} else {
run_with_call_header
.then(|| vm_trace.last())
.flatten()
.map_or(1, |trace_entry| trace_entry.pc + 1)
};
vm_trace
.iter()
.map(|step| step.pc)
.map(|pc| map_pc_to_sierra_statement_id(sierra_statement_info, pc, real_minimal_pc))
.collect()
}
#[must_use]
#[allow(clippy::missing_panics_doc)]
pub fn map_pc_to_sierra_statement_id(
sierra_statement_info: &[SierraStatementDebugInfo],
pc: usize,
real_minimal_pc: usize,
) -> MappingResult {
if pc < real_minimal_pc {
return MappingResult::Header;
}
let real_pc_code_offset = pc - real_minimal_pc;
let statement_index = StatementIdx(
sierra_statement_info
.partition_point(|statement_debug_info| {
statement_debug_info.start_offset <= real_pc_code_offset
})
.saturating_sub(1),
);
let bytecode_length = sierra_statement_info
.last()
.expect("sierra_statement_info is not empty")
.end_offset;
if real_pc_code_offset >= bytecode_length {
MappingResult::PcOutOfFunctionArea
} else {
MappingResult::SierraStatementIdx(statement_index)
}
}