use std::{
fmt::Display,
path::{Path, PathBuf},
rc::Rc,
};
use boa_ast::Position;
mod builder;
use boa_gc::{Finalize, Trace};
use boa_string::JsString;
pub(crate) use builder::SourceMapBuilder;
use crate::SpannedSourceText;
#[cfg(test)]
mod tests;
#[derive(Debug, Default, Clone, Finalize, Trace)]
#[boa_gc(unsafe_empty_trace)]
pub(crate) struct SourceInfo {
inner: Rc<Inner>,
}
impl SourceInfo {
pub(crate) fn new(
source_map: SourceMap,
function_name: JsString,
source_text_spanned: SpannedSourceText,
) -> Self {
Self {
inner: Rc::new(Inner {
map: source_map,
function_name,
text_spanned: source_text_spanned,
}),
}
}
pub(crate) fn map(&self) -> &SourceMap {
&self.inner.map
}
pub(crate) fn function_name(&self) -> &JsString {
&self.inner.function_name
}
pub(crate) fn text_spanned(&self) -> &SpannedSourceText {
&self.inner.text_spanned
}
}
#[derive(Debug, Default, Clone)]
struct Inner {
map: SourceMap,
function_name: JsString,
text_spanned: SpannedSourceText,
}
#[derive(Debug, Default, Clone)]
pub(crate) struct SourceMap {
entries: Box<[Entry]>,
path: SourcePath,
}
impl SourceMap {
pub(crate) fn new(entries: Box<[Entry]>, path: SourcePath) -> Self {
Self { entries, path }
}
pub(crate) fn entries(&self) -> &[Entry] {
&self.entries
}
pub(crate) fn find(&self, pc: u32) -> Option<Position> {
find_entry(self.entries(), pc)
}
pub(crate) fn path(&self) -> &SourcePath {
&self.path
}
}
fn find_entry(entries: &[Entry], pc: u32) -> Option<Position> {
let first = entries.first()?;
if pc < first.pc() {
return None;
}
let mut low = 0;
let mut high = entries.len() - 1;
while low <= high {
let mid = low.midpoint(high);
let entry = &entries[mid];
let start = entry.pc;
let end = entries.get(mid + 1).map_or(u32::MAX, |entry| entry.pc);
if pc < start {
high = mid;
} else if pc >= end {
low = mid + 1;
} else {
return entry.position();
}
}
entries.last().and_then(Entry::position)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct Entry {
pub(crate) pc: u32,
pub(crate) position: Option<Position>,
}
impl Entry {
pub(crate) const fn pc(&self) -> u32 {
self.pc
}
pub(crate) const fn position(&self) -> Option<Position> {
self.position
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub enum SourcePath {
#[default]
None,
Eval,
Json,
Path(Rc<Path>),
}
impl From<Option<PathBuf>> for SourcePath {
fn from(value: Option<PathBuf>) -> Self {
match value {
None => Self::None,
Some(path) => Self::Path(path.into()),
}
}
}
impl From<Option<Rc<Path>>> for SourcePath {
fn from(value: Option<Rc<Path>>) -> Self {
match value {
None => Self::None,
Some(path) => Self::Path(path),
}
}
}
impl Display for SourcePath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SourcePath::None => f.write_str("unknown at "),
SourcePath::Eval => f.write_str("eval at "),
SourcePath::Json => f.write_str("json at "),
SourcePath::Path(path) => write!(f, "{}", path.display()),
}
}
}
impl SourcePath {
pub(crate) fn is_none(&self) -> bool {
matches!(self, Self::None)
}
pub(crate) fn is_some(&self) -> bool {
!self.is_none()
}
}
#[derive(Debug, Clone, Copy)]
pub struct NativeSourceInfo {
#[cfg(feature = "native-backtrace")]
inner: &'static std::panic::Location<'static>,
#[cfg(not(feature = "native-backtrace"))]
inner: std::marker::PhantomData<()>,
}
impl NativeSourceInfo {
#[inline]
#[must_use]
#[cfg_attr(feature = "native-backtrace", track_caller)]
pub const fn caller() -> Self {
Self {
#[cfg(feature = "native-backtrace")]
inner: std::panic::Location::caller(),
#[cfg(not(feature = "native-backtrace"))]
inner: std::marker::PhantomData,
}
}
#[inline]
#[must_use]
pub const fn as_location(self) -> Option<&'static std::panic::Location<'static>> {
#[cfg(feature = "native-backtrace")]
return Some(self.inner);
#[cfg(not(feature = "native-backtrace"))]
return None;
}
}