use alloc::{
boxed::Box,
collections::{BTreeMap, BTreeSet},
string::String,
vec::Vec,
};
use pliron::{
basic_block::BasicBlock,
combine::stream::position::SourcePosition,
context::{Context, Ptr},
irbuild::listener::InsertionListener,
location::{Location, Source},
operation::Operation,
std_deps::path::PathBuf,
};
use crate::ContextExt;
#[derive(Debug, Default)]
pub struct DebugState {
enabled: bool,
frames: Vec<Frame>,
sources: BTreeMap<String, &'static str>,
files: BTreeSet<String>,
}
#[derive(Debug)]
struct Frame {
name: &'static str,
file: Source,
pos: SourcePosition,
}
impl DebugState {
#[must_use]
pub fn new(enabled: bool) -> Self {
Self {
enabled,
..Self::default()
}
}
#[must_use]
pub fn is_enabled(&self) -> bool {
self.enabled
}
pub fn enter_fn(&mut self, name: &'static str, file: Source, line: u32, column: u32) {
self.frames.push(Frame {
name,
file,
pos: position(line, column),
});
}
pub fn exit_fn(&mut self) {
self.frames.pop();
}
pub fn set_pos(&mut self, line: u32, column: u32) {
if let Some(frame) = self.frames.last_mut() {
frame.pos = position(line, column);
}
}
pub fn add_source(&mut self, path: &str, text: &'static str) {
if !self.files.contains(path) {
self.files.insert(path.into());
}
if !text.is_empty() && !self.sources.contains_key(path) {
self.sources.insert(path.into(), text);
}
}
#[must_use]
pub fn sources(&self) -> &BTreeMap<String, &'static str> {
&self.sources
}
#[must_use]
pub fn files(&self) -> &BTreeSet<String> {
&self.files
}
pub fn finish(&mut self) {
self.enabled = false;
self.frames.clear();
}
fn location(&self) -> Option<Location> {
let (kernel, callees) = self.frames.split_first()?;
let location =
callees
.iter()
.fold(kernel.location(), |caller, callee| Location::CallSite {
callee: Box::new(callee.location()),
caller: Box::new(caller),
});
Some(location)
}
}
impl Frame {
fn location(&self) -> Location {
Location::Named {
name: self.name.into(),
child_loc: Box::new(Location::SrcPos {
src: self.file,
pos: self.pos,
}),
}
}
}
fn position(line: u32, column: u32) -> SourcePosition {
SourcePosition {
line: i32::try_from(line).unwrap_or(i32::MAX),
column: i32::try_from(column).unwrap_or(i32::MAX),
}
}
#[derive(Default)]
pub struct LocationListener;
impl InsertionListener for LocationListener {
fn notify_operation_inserted(&mut self, ctx: &Context, operation: Ptr<Operation>) {
let Some(debug) = ctx.try_aux_ty::<DebugState>() else {
return;
};
if let Some(loc) = debug.location()
&& operation.deref(ctx).loc().is_unknown()
{
operation.deref_mut(ctx).set_loc(loc);
}
}
fn notify_block_inserted(&mut self, _ctx: &Context, _block: Ptr<BasicBlock>) {}
}
pub fn leaf_line<'c>(ctx: &'c Context, loc: &Location) -> Option<(&'c PathBuf, u32)> {
match loc {
Location::CallSite { callee, .. } => leaf_line(ctx, callee),
Location::Named { child_loc, .. } => leaf_line(ctx, child_loc),
Location::SrcPos {
src: Source::File(key),
pos,
} => Some((
pliron::uniqued_any::get(ctx, *key),
u32::try_from(pos.line).unwrap_or(0),
)),
Location::Fused { locations, .. } => locations.iter().find_map(|loc| leaf_line(ctx, loc)),
Location::SrcPos {
src: Source::InMemory,
..
}
| Location::Unknown => None,
}
}