use std::hash::Hash;
use std::marker::PhantomData;
use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
use itertools::Itertools;
use serde::de::{SeqAccess, Visitor};
use serde::{Deserialize, Serialize};
use smol_str::SmolStr;
use crate::ids::{ConcreteLibfuncId, ConcreteTypeId, FunctionId};
use crate::program::{GenericArg, Program, Statement};
#[cfg(test)]
#[path = "debug_info_test.rs"]
mod test;
#[derive(Clone, Debug, Eq, PartialEq, Default, Serialize, Deserialize)]
pub struct DebugInfo {
#[serde(
serialize_with = "serialize_map::<ConcreteTypeId, _>",
deserialize_with = "deserialize_map::<ConcreteTypeId, _>"
)]
pub type_names: OrderedHashMap<ConcreteTypeId, SmolStr>,
#[serde(
serialize_with = "serialize_map::<ConcreteLibfuncId, _>",
deserialize_with = "deserialize_map::<ConcreteLibfuncId, _>"
)]
pub libfunc_names: OrderedHashMap<ConcreteLibfuncId, SmolStr>,
#[serde(
serialize_with = "serialize_map::<FunctionId, _>",
deserialize_with = "deserialize_map::<FunctionId, _>"
)]
pub user_func_names: OrderedHashMap<FunctionId, SmolStr>,
#[serde(default, skip_serializing_if = "Annotations::is_empty")]
pub annotations: Annotations,
#[serde(default, skip_serializing_if = "OrderedHashMap::is_empty")]
pub executables: OrderedHashMap<String, Vec<FunctionId>>,
}
pub type Annotations = OrderedHashMap<String, serde_json::Value>;
impl DebugInfo {
pub fn extract(program: &Program) -> Self {
Self {
type_names: program
.type_declarations
.iter()
.filter_map(|decl| {
decl.id.debug_name.clone().map(|name| (ConcreteTypeId::new(decl.id.id), name))
})
.collect(),
libfunc_names: program
.libfunc_declarations
.iter()
.filter_map(|decl| {
decl.id
.debug_name
.clone()
.map(|name| (ConcreteLibfuncId::new(decl.id.id), name))
})
.collect(),
user_func_names: program
.funcs
.iter()
.filter_map(|func| {
func.id.debug_name.clone().map(|name| (FunctionId::new(func.id.id), name))
})
.collect(),
annotations: Default::default(),
executables: Default::default(),
}
}
pub fn populate(&self, program: &mut Program) {
for decl in &mut program.type_declarations {
self.try_replace_type_id(&mut decl.id);
self.try_replace_generic_arg_ids(&mut decl.long_id.generic_args);
}
for decl in &mut program.libfunc_declarations {
self.try_replace_libfunc_id(&mut decl.id);
self.try_replace_generic_arg_ids(&mut decl.long_id.generic_args);
}
for func in &mut program.funcs {
self.try_replace_function_id(&mut func.id);
for param in &mut func.params {
self.try_replace_type_id(&mut param.ty);
}
for id in &mut func.signature.param_types {
self.try_replace_type_id(id);
}
for id in &mut func.signature.ret_types {
self.try_replace_type_id(id);
}
}
for statement in &mut program.statements {
match statement {
Statement::Invocation(invocation) => {
self.try_replace_libfunc_id(&mut invocation.libfunc_id)
}
Statement::Return(_) => {}
}
}
}
fn try_replace_generic_arg_ids(&self, generic_args: &mut Vec<GenericArg>) {
for generic_arg in generic_args {
match generic_arg {
GenericArg::Type(id) => self.try_replace_type_id(id),
GenericArg::Libfunc(id) => self.try_replace_libfunc_id(id),
GenericArg::UserFunc(id) => self.try_replace_function_id(id),
GenericArg::Value(_) | GenericArg::UserType(_) => {}
}
}
}
fn try_replace_type_id(&self, id: &mut ConcreteTypeId) {
if let Some(name) = self.type_names.get(id).cloned() {
let _ = id.debug_name.insert(name);
}
}
fn try_replace_libfunc_id(&self, id: &mut ConcreteLibfuncId) {
if let Some(name) = self.libfunc_names.get(id).cloned() {
let _ = id.debug_name.insert(name);
}
}
fn try_replace_function_id(&self, id: &mut FunctionId) {
if let Some(name) = self.user_func_names.get(id).cloned() {
let _ = id.debug_name.insert(name);
}
}
}
pub trait IdAsHashKey: Hash + Eq {
fn get(&self) -> u64;
fn new(id: u64) -> Self;
}
impl IdAsHashKey for ConcreteTypeId {
fn get(&self) -> u64 {
self.id
}
fn new(id: u64) -> Self {
Self::new(id)
}
}
impl IdAsHashKey for ConcreteLibfuncId {
fn get(&self) -> u64 {
self.id
}
fn new(id: u64) -> Self {
Self::new(id)
}
}
impl IdAsHashKey for FunctionId {
fn get(&self) -> u64 {
self.id
}
fn new(id: u64) -> Self {
Self::new(id)
}
}
fn serialize_map<Id: IdAsHashKey, S: serde::Serializer>(
m: &OrderedHashMap<Id, SmolStr>,
serializer: S,
) -> Result<S::Ok, S::Error> {
serializer.collect_seq(m.iter().map(|(id, name)| (id.get(), name)).sorted())
}
fn deserialize_map<'de, Id: IdAsHashKey, D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<OrderedHashMap<Id, SmolStr>, D::Error> {
struct OrderedHashMapVisitor<Id> {
marker: PhantomData<Id>,
}
impl<'de, Id> Visitor<'de> for OrderedHashMapVisitor<Id>
where
Id: IdAsHashKey,
{
type Value = OrderedHashMap<Id, SmolStr>;
fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter.write_str("a sequence of pairs")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut values = OrderedHashMap::default();
if let Some(size) = seq.size_hint() {
values.reserve_exact(size);
}
while let Some((k, v)) = seq.next_element()? {
values.insert(Id::new(k), v);
}
Ok(values)
}
}
deserializer.deserialize_seq(OrderedHashMapVisitor { marker: PhantomData })
}