quickjs_oxide/engine/code/
debug.rs1use crate::source::LineColumn;
10
11#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
17pub enum DebugInfoMode {
18 #[default]
20 Full,
21 StripSource,
23 StripDebug,
25}
26
27#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
29pub struct Pc2LineEntry {
30 pub pc: u32,
31 pub position: LineColumn,
32}
33
34#[derive(Clone, Debug, PartialEq, Eq)]
36pub struct Pc2LineTable {
37 pub definition: LineColumn,
38 pub entries: Box<[Pc2LineEntry]>,
39}
40
41impl Pc2LineTable {
42 #[must_use]
43 pub fn new(definition: LineColumn, entries: impl Into<Box<[Pc2LineEntry]>>) -> Self {
44 Self {
45 definition,
46 entries: entries.into(),
47 }
48 }
49
50 #[cfg(test)]
53 #[must_use]
54 pub fn lookup(&self, pc: Option<u32>) -> LineColumn {
55 let Some(pc) = pc else {
56 return self.definition;
57 };
58 self.entries
59 .iter()
60 .take_while(|entry| entry.pc <= pc)
61 .last()
62 .map_or(self.definition, |entry| entry.position)
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::{LineColumn, Pc2LineEntry, Pc2LineTable};
69 use crate::source::DebugMetadataError;
70 use crate::source::{QuickJsSourceLocator, SourceOffset};
71
72 #[test]
73 fn quickjs_locator_only_treats_lf_as_a_newline() {
74 let source = "a\rb\r\nc\u{2028}d\u{2029}e\nf";
75 let locator = QuickJsSourceLocator::new(source);
76
77 assert_eq!(locator.locate_byte_offset(0), Ok(LineColumn::new(0, 0)));
78 assert_eq!(
79 locator.locate_byte_offset(source.find('b').unwrap()),
80 Ok(LineColumn::new(0, 2))
81 );
82 assert_eq!(
83 locator.locate_byte_offset(source.find('c').unwrap()),
84 Ok(LineColumn::new(1, 0))
85 );
86 assert_eq!(
87 locator.locate_byte_offset(source.find('d').unwrap()),
88 Ok(LineColumn::new(1, 2))
89 );
90 assert_eq!(
91 locator.locate_byte_offset(source.find('e').unwrap()),
92 Ok(LineColumn::new(1, 4))
93 );
94 assert_eq!(
95 locator.locate_byte_offset(source.find('f').unwrap()),
96 Ok(LineColumn::new(2, 0))
97 );
98 }
99
100 #[test]
101 fn quickjs_locator_counts_utf8_lead_bytes_not_raw_bytes() {
102 let source = "é中x";
103 let locator = QuickJsSourceLocator::new(source);
104 assert_eq!(
105 locator.locate_byte_offset(source.find('x').unwrap()),
106 Ok(LineColumn::new(0, 2))
107 );
108 assert_eq!(
109 locator.locate_byte_offset(1),
110 Err(DebugMetadataError::OffsetNotUtf8Boundary)
111 );
112 }
113
114 #[test]
115 fn quickjs_raw_locator_preserves_cesu8_and_malformed_byte_columns() {
116 let canonical = QuickJsSourceLocator::from_bytes(b"/*\xf0\x9f\x98\x80*/@");
117 let cesu8 = QuickJsSourceLocator::from_bytes(b"/*\xed\xa0\xbd\xed\xb8\x80*/@");
118 let continuation = QuickJsSourceLocator::from_bytes(b"/*\x80*/@");
119 let invalid_lead = QuickJsSourceLocator::from_bytes(b"/*\xff*/@");
120
121 assert_eq!(canonical.locate_byte_offset(8), Ok(LineColumn::new(0, 5)));
122 assert_eq!(cesu8.locate_byte_offset(10), Ok(LineColumn::new(0, 6)));
123 assert_eq!(
124 continuation.locate_byte_offset(5),
125 Ok(LineColumn::new(0, 4))
126 );
127 assert_eq!(
128 invalid_lead.locate_byte_offset(5),
129 Ok(LineColumn::new(0, 5))
130 );
131 }
132
133 #[test]
134 fn pc_lookup_uses_the_last_entry_at_or_before_the_pc() {
135 let table = Pc2LineTable::new(
136 LineColumn::new(1, 2),
137 vec![
138 Pc2LineEntry {
139 pc: 1,
140 position: LineColumn::new(3, 4),
141 },
142 Pc2LineEntry {
143 pc: 5,
144 position: LineColumn::new(6, 7),
145 },
146 ],
147 );
148 assert_eq!(table.lookup(None), LineColumn::new(1, 2));
149 assert_eq!(table.lookup(Some(0)), LineColumn::new(1, 2));
150 assert_eq!(table.lookup(Some(4)), LineColumn::new(3, 4));
151 assert_eq!(table.lookup(Some(5)), LineColumn::new(6, 7));
152 assert_eq!(SourceOffset::try_from_usize(7).unwrap().get(), 7);
153 }
154}