sphinx/debug/
traceback.rs

1use core::fmt;
2use crate::source::ModuleSource;
3use crate::runtime::gc::{Gc, GcTrace};
4use crate::runtime::module::{Module, Chunk};
5
6
7/// Traceback information
8#[derive(Debug, Clone)]
9pub enum TraceSite {
10    Chunk {
11        offset: usize,
12        module: Gc<Module>,
13        chunk_id: Chunk,
14    },
15    Native,  // TODO reference native function?
16}
17
18unsafe impl GcTrace for TraceSite {
19    fn trace(&self) {
20        if let Self::Chunk { module, .. } = self {
21            module.mark_trace();
22        }
23    }
24}
25
26
27pub struct FrameSummary<'a> {
28    trace: &'a TraceSite,
29}
30
31impl fmt::Display for FrameSummary<'_> {
32    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
33        match self.trace {
34            TraceSite::Chunk { offset, module, chunk_id } => {
35                let module_desc = module_desc(module);
36                let loc_desc = format!("<@{:#X}>", offset);
37                let chunk_desc = chunk_desc(module, chunk_id);
38                write!(fmt, "{}, {} in {}", module_desc, loc_desc, chunk_desc)
39            },
40            
41            TraceSite::Native => {
42                write!(fmt, "<native code>")
43            },
44        }
45    }
46}
47
48fn module_desc(module: &Module) -> String {
49    if let Some(ModuleSource::File(path)) = module.source() {
50        format!("File \"{}\"", path.display())
51    } else {
52        "<anonymous module>".to_string()
53    }
54}
55
56fn chunk_desc(module: &Module, chunk_id: &Chunk) -> String {
57    match chunk_id {
58        Chunk::Main => "<module>".to_string(),
59        
60        Chunk::Function(fun_id) => {
61            let function = module.data().get_function(*fun_id);
62            format!("{}", function.signature())
63        },
64    }
65}
66
67
68pub struct Traceback<'a> {
69    frames: Vec<FrameSummary<'a>>,
70}
71
72
73impl<'a> Traceback<'a> {
74    pub fn build(trace: impl Iterator<Item=&'a TraceSite>) -> Self {
75        Self {
76            frames: trace.map(|trace| FrameSummary { trace }).collect(),
77        }
78    }
79}
80
81impl fmt::Display for Traceback<'_> {
82    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
83        fmt.write_str("Stack trace (most recent call last):\n")?;
84        
85        for (idx, frame) in self.frames.iter().enumerate().rev() {
86            writeln!(fmt, "#{} {}", idx + 1, frame)?;
87        }
88        
89        Ok(())
90    }
91}