1use alloc::{
21 boxed::Box,
22 collections::{BTreeMap, BTreeSet},
23 string::String,
24 vec::Vec,
25};
26use pliron::{
27 basic_block::BasicBlock,
28 combine::stream::position::SourcePosition,
29 context::{Context, Ptr},
30 irbuild::listener::InsertionListener,
31 location::{Location, Source},
32 operation::Operation,
33 std_deps::path::PathBuf,
34};
35
36use crate::ContextExt;
37
38#[derive(Debug, Default)]
43pub struct DebugState {
44 enabled: bool,
45 frames: Vec<Frame>,
46 sources: BTreeMap<String, &'static str>,
49 files: BTreeSet<String>,
51}
52
53#[derive(Debug)]
55struct Frame {
56 name: &'static str,
57 file: Source,
58 pos: SourcePosition,
60}
61
62impl DebugState {
63 #[must_use]
65 pub fn new(enabled: bool) -> Self {
66 Self {
67 enabled,
68 ..Self::default()
69 }
70 }
71
72 #[must_use]
74 pub fn is_enabled(&self) -> bool {
75 self.enabled
76 }
77
78 pub fn enter_fn(&mut self, name: &'static str, file: Source, line: u32, column: u32) {
80 self.frames.push(Frame {
81 name,
82 file,
83 pos: position(line, column),
84 });
85 }
86
87 pub fn exit_fn(&mut self) {
89 self.frames.pop();
90 }
91
92 pub fn set_pos(&mut self, line: u32, column: u32) {
94 if let Some(frame) = self.frames.last_mut() {
95 frame.pos = position(line, column);
96 }
97 }
98
99 pub fn add_source(&mut self, path: &str, text: &'static str) {
102 if !self.files.contains(path) {
103 self.files.insert(path.into());
104 }
105 if !text.is_empty() && !self.sources.contains_key(path) {
106 self.sources.insert(path.into(), text);
107 }
108 }
109
110 #[must_use]
113 pub fn sources(&self) -> &BTreeMap<String, &'static str> {
114 &self.sources
115 }
116
117 #[must_use]
120 pub fn files(&self) -> &BTreeSet<String> {
121 &self.files
122 }
123
124 pub fn finish(&mut self) {
127 self.enabled = false;
128 self.frames.clear();
129 }
130
131 fn location(&self) -> Option<Location> {
133 let (kernel, callees) = self.frames.split_first()?;
134 let location =
135 callees
136 .iter()
137 .fold(kernel.location(), |caller, callee| Location::CallSite {
138 callee: Box::new(callee.location()),
139 caller: Box::new(caller),
140 });
141 Some(location)
142 }
143}
144
145impl Frame {
146 fn location(&self) -> Location {
147 Location::Named {
148 name: self.name.into(),
149 child_loc: Box::new(Location::SrcPos {
150 src: self.file,
151 pos: self.pos,
152 }),
153 }
154 }
155}
156
157fn position(line: u32, column: u32) -> SourcePosition {
160 SourcePosition {
161 line: i32::try_from(line).unwrap_or(i32::MAX),
162 column: i32::try_from(column).unwrap_or(i32::MAX),
163 }
164}
165
166#[derive(Default)]
168pub struct LocationListener;
169
170impl InsertionListener for LocationListener {
171 fn notify_operation_inserted(&mut self, ctx: &Context, operation: Ptr<Operation>) {
172 let Some(debug) = ctx.try_aux_ty::<DebugState>() else {
173 return;
174 };
175 if let Some(loc) = debug.location()
176 && operation.deref(ctx).loc().is_unknown()
177 {
178 operation.deref_mut(ctx).set_loc(loc);
179 }
180 }
181
182 fn notify_block_inserted(&mut self, _ctx: &Context, _block: Ptr<BasicBlock>) {}
183}
184
185pub fn leaf_line<'c>(ctx: &'c Context, loc: &Location) -> Option<(&'c PathBuf, u32)> {
189 match loc {
190 Location::CallSite { callee, .. } => leaf_line(ctx, callee),
191 Location::Named { child_loc, .. } => leaf_line(ctx, child_loc),
192 Location::SrcPos {
193 src: Source::File(key),
194 pos,
195 } => Some((
196 pliron::uniqued_any::get(ctx, *key),
197 u32::try_from(pos.line).unwrap_or(0),
198 )),
199 Location::Fused { locations, .. } => locations.iter().find_map(|loc| leaf_line(ctx, loc)),
200 Location::SrcPos {
201 src: Source::InMemory,
202 ..
203 }
204 | Location::Unknown => None,
205 }
206}