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