boa_engine/vm/source_info/
mod.rs1use std::{
2 fmt::Display,
3 path::{Path, PathBuf},
4 rc::Rc,
5};
6
7use boa_ast::Position;
8
9mod builder;
10
11use boa_gc::{Finalize, Trace};
12use boa_string::JsString;
13pub(crate) use builder::SourceMapBuilder;
14
15use crate::SpannedSourceText;
16
17#[cfg(test)]
18mod tests;
19
20#[derive(Debug, Default, Clone, Finalize, Trace)]
22#[boa_gc(unsafe_empty_trace)]
24pub(crate) struct SourceInfo {
25 inner: Rc<Inner>,
26}
27
28impl SourceInfo {
29 pub(crate) fn new(
30 source_map: SourceMap,
31 function_name: JsString,
32 source_text_spanned: SpannedSourceText,
33 ) -> Self {
34 Self {
35 inner: Rc::new(Inner {
36 map: source_map,
37 function_name,
38 text_spanned: source_text_spanned,
39 }),
40 }
41 }
42
43 pub(crate) fn map(&self) -> &SourceMap {
44 &self.inner.map
45 }
46
47 pub(crate) fn function_name(&self) -> &JsString {
48 &self.inner.function_name
49 }
50
51 pub(crate) fn text_spanned(&self) -> &SpannedSourceText {
52 &self.inner.text_spanned
53 }
54}
55
56#[derive(Debug, Default, Clone)]
57struct Inner {
58 map: SourceMap,
59 function_name: JsString,
60
61 text_spanned: SpannedSourceText,
62}
63
64#[derive(Debug, Default, Clone)]
66pub(crate) struct SourceMap {
67 entries: Box<[Entry]>,
68 path: SourcePath,
69}
70
71impl SourceMap {
72 pub(crate) fn new(entries: Box<[Entry]>, path: SourcePath) -> Self {
73 Self { entries, path }
74 }
75
76 pub(crate) fn entries(&self) -> &[Entry] {
77 &self.entries
78 }
79
80 pub(crate) fn find(&self, pc: u32) -> Option<Position> {
81 find_entry(self.entries(), pc)
82 }
83
84 pub(crate) fn path(&self) -> &SourcePath {
85 &self.path
86 }
87}
88
89fn find_entry(entries: &[Entry], pc: u32) -> Option<Position> {
90 let first = entries.first()?;
91
92 if pc < first.pc() {
93 return None;
94 }
95
96 let mut low = 0;
97 let mut high = entries.len() - 1;
98
99 while low <= high {
100 let mid = low.midpoint(high);
101 let entry = &entries[mid];
102 let start = entry.pc;
103
104 let end = entries.get(mid + 1).map_or(u32::MAX, |entry| entry.pc);
105
106 if pc < start {
107 high = mid;
108 } else if pc >= end {
109 low = mid + 1;
110 } else {
111 return entry.position();
112 }
113 }
114
115 entries.last().and_then(Entry::position)
118}
119
120#[derive(Debug, Clone, Copy, PartialEq, Eq)]
123pub(crate) struct Entry {
124 pub(crate) pc: u32,
129
130 pub(crate) position: Option<Position>,
132}
133
134impl Entry {
135 pub(crate) const fn pc(&self) -> u32 {
136 self.pc
137 }
138
139 pub(crate) const fn position(&self) -> Option<Position> {
140 self.position
141 }
142}
143
144#[derive(Debug, Default, Clone, PartialEq, Eq)]
146pub enum SourcePath {
147 #[default]
149 None,
150 Eval,
153 Json,
156 Path(Rc<Path>),
159}
160
161impl From<Option<PathBuf>> for SourcePath {
162 fn from(value: Option<PathBuf>) -> Self {
163 match value {
164 None => Self::None,
165 Some(path) => Self::Path(path.into()),
166 }
167 }
168}
169
170impl From<Option<Rc<Path>>> for SourcePath {
171 fn from(value: Option<Rc<Path>>) -> Self {
172 match value {
173 None => Self::None,
174 Some(path) => Self::Path(path),
175 }
176 }
177}
178
179impl Display for SourcePath {
180 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
181 match self {
182 SourcePath::None => f.write_str("unknown at "),
183 SourcePath::Eval => f.write_str("eval at "),
184 SourcePath::Json => f.write_str("json at "),
185 SourcePath::Path(path) => write!(f, "{}", path.display()),
186 }
187 }
188}
189
190impl SourcePath {
191 pub(crate) fn is_none(&self) -> bool {
192 matches!(self, Self::None)
193 }
194
195 pub(crate) fn is_some(&self) -> bool {
196 !self.is_none()
197 }
198}
199
200#[derive(Debug, Clone, Copy)]
208pub struct NativeSourceInfo {
209 #[cfg(feature = "native-backtrace")]
210 inner: &'static std::panic::Location<'static>,
211
212 #[cfg(not(feature = "native-backtrace"))]
213 inner: std::marker::PhantomData<()>,
214}
215
216impl NativeSourceInfo {
217 #[inline]
223 #[must_use]
224 #[cfg_attr(feature = "native-backtrace", track_caller)]
225 pub const fn caller() -> Self {
226 Self {
227 #[cfg(feature = "native-backtrace")]
228 inner: std::panic::Location::caller(),
229
230 #[cfg(not(feature = "native-backtrace"))]
231 inner: std::marker::PhantomData,
232 }
233 }
234
235 #[inline]
241 #[must_use]
242 pub const fn as_location(self) -> Option<&'static std::panic::Location<'static>> {
243 #[cfg(feature = "native-backtrace")]
244 return Some(self.inner);
245
246 #[cfg(not(feature = "native-backtrace"))]
247 return None;
248 }
249}