use crate::source::LineColumn;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum DebugInfoMode {
#[default]
Full,
StripSource,
StripDebug,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Pc2LineEntry {
pub pc: u32,
pub position: LineColumn,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Pc2LineTable {
pub definition: LineColumn,
pub entries: Box<[Pc2LineEntry]>,
}
impl Pc2LineTable {
#[must_use]
pub fn new(definition: LineColumn, entries: impl Into<Box<[Pc2LineEntry]>>) -> Self {
Self {
definition,
entries: entries.into(),
}
}
#[cfg(test)]
#[must_use]
pub fn lookup(&self, pc: Option<u32>) -> LineColumn {
let Some(pc) = pc else {
return self.definition;
};
self.entries
.iter()
.take_while(|entry| entry.pc <= pc)
.last()
.map_or(self.definition, |entry| entry.position)
}
}
#[cfg(test)]
mod tests {
use super::{LineColumn, Pc2LineEntry, Pc2LineTable};
use crate::source::DebugMetadataError;
use crate::source::{QuickJsSourceLocator, SourceOffset};
#[test]
fn quickjs_locator_only_treats_lf_as_a_newline() {
let source = "a\rb\r\nc\u{2028}d\u{2029}e\nf";
let locator = QuickJsSourceLocator::new(source);
assert_eq!(locator.locate_byte_offset(0), Ok(LineColumn::new(0, 0)));
assert_eq!(
locator.locate_byte_offset(source.find('b').unwrap()),
Ok(LineColumn::new(0, 2))
);
assert_eq!(
locator.locate_byte_offset(source.find('c').unwrap()),
Ok(LineColumn::new(1, 0))
);
assert_eq!(
locator.locate_byte_offset(source.find('d').unwrap()),
Ok(LineColumn::new(1, 2))
);
assert_eq!(
locator.locate_byte_offset(source.find('e').unwrap()),
Ok(LineColumn::new(1, 4))
);
assert_eq!(
locator.locate_byte_offset(source.find('f').unwrap()),
Ok(LineColumn::new(2, 0))
);
}
#[test]
fn quickjs_locator_counts_utf8_lead_bytes_not_raw_bytes() {
let source = "é中x";
let locator = QuickJsSourceLocator::new(source);
assert_eq!(
locator.locate_byte_offset(source.find('x').unwrap()),
Ok(LineColumn::new(0, 2))
);
assert_eq!(
locator.locate_byte_offset(1),
Err(DebugMetadataError::OffsetNotUtf8Boundary)
);
}
#[test]
fn quickjs_raw_locator_preserves_cesu8_and_malformed_byte_columns() {
let canonical = QuickJsSourceLocator::from_bytes(b"/*\xf0\x9f\x98\x80*/@");
let cesu8 = QuickJsSourceLocator::from_bytes(b"/*\xed\xa0\xbd\xed\xb8\x80*/@");
let continuation = QuickJsSourceLocator::from_bytes(b"/*\x80*/@");
let invalid_lead = QuickJsSourceLocator::from_bytes(b"/*\xff*/@");
assert_eq!(canonical.locate_byte_offset(8), Ok(LineColumn::new(0, 5)));
assert_eq!(cesu8.locate_byte_offset(10), Ok(LineColumn::new(0, 6)));
assert_eq!(
continuation.locate_byte_offset(5),
Ok(LineColumn::new(0, 4))
);
assert_eq!(
invalid_lead.locate_byte_offset(5),
Ok(LineColumn::new(0, 5))
);
}
#[test]
fn pc_lookup_uses_the_last_entry_at_or_before_the_pc() {
let table = Pc2LineTable::new(
LineColumn::new(1, 2),
vec![
Pc2LineEntry {
pc: 1,
position: LineColumn::new(3, 4),
},
Pc2LineEntry {
pc: 5,
position: LineColumn::new(6, 7),
},
],
);
assert_eq!(table.lookup(None), LineColumn::new(1, 2));
assert_eq!(table.lookup(Some(0)), LineColumn::new(1, 2));
assert_eq!(table.lookup(Some(4)), LineColumn::new(3, 4));
assert_eq!(table.lookup(Some(5)), LineColumn::new(6, 7));
assert_eq!(SourceOffset::try_from_usize(7).unwrap().get(), 7);
}
}