Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] feat: Add native SourceMap support #819

Open
wants to merge 31 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
3d8c9c8
feat: Add native SourceMap support
bartlomieju Jul 11, 2024
2905e0d
update comment
bartlomieju Jul 11, 2024
23cdef9
prototype source mapping url being non-base64 url
bartlomieju Jul 12, 2024
ec8e4f1
start moving source map loading to ModuleLoader
bartlomieju Jul 12, 2024
333c579
Merge branch 'main' into source_map_getter
bartlomieju Jul 12, 2024
72ffd9d
Merge branch 'main' into source_map_getter
bartlomieju Jul 15, 2024
588dc8a
Merge branch 'main' into source_map_getter
bartlomieju Jul 15, 2024
a78caa2
Merge branch 'main' into source_map_getter
bartlomieju Jul 16, 2024
a46cd55
fixes after merge
bartlomieju Jul 16, 2024
0a2821c
rename
bartlomieju Jul 16, 2024
6fc5d1d
lint
bartlomieju Jul 16, 2024
0166243
use ModuleCode static for ext_source_maps
bartlomieju Jul 16, 2024
6bfb77b
more cleanup
bartlomieju Jul 17, 2024
224bcf2
remove a TODO comment
bartlomieju Jul 17, 2024
01bf21c
Merge branch 'source_map_perf' into source_map_getter
bartlomieju Jul 17, 2024
7ff9928
remove setting up `ModuleMap` for `SourceMapper`
bartlomieju Jul 17, 2024
40aff1a
simplify SourceMapper::get_source_line
bartlomieju Jul 17, 2024
84d6c3f
lint
bartlomieju Jul 17, 2024
7a380af
Merge branch 'main' into source_map_getter
bartlomieju Jul 17, 2024
85a9634
fix after merge
bartlomieju Jul 17, 2024
b0bfb5c
simplify error
bartlomieju Jul 17, 2024
a6c6f76
self-review
bartlomieju Jul 17, 2024
126eb6b
add a TODO
bartlomieju Jul 17, 2024
4e4036d
remove base64
bartlomieju Jul 17, 2024
b253371
wip - dcore, can't print source_line in JsError
bartlomieju Jul 17, 2024
a503d80
add transpiler api
bartlomieju Jul 18, 2024
cc573b4
wip upgrade
bartlomieju Jul 18, 2024
f01fbe8
Merge branch 'main' into source_map_getter
bartlomieju Jul 18, 2024
2d4fdb9
Merge remote-tracking branch 'upstream/main' into source_map_getter
bartlomieju Jul 26, 2024
1ccf6c6
Merge branch 'main' into source_map_getter
bartlomieju Jul 29, 2024
0795341
Merge remote-tracking branch 'upstream/main' into source_map_getter
bartlomieju Aug 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions a.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Empty file added a.js.map
Empty file.
6 changes: 6 additions & 0 deletions bar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"use strict";

throw new Error("Hello world!");
//# sourceMappingURL=bar.js.map

asd;
11 changes: 11 additions & 0 deletions bar.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

87 changes: 53 additions & 34 deletions core/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,12 +214,14 @@ pub struct JsStackFrame {

/// Applies source map to the given location
fn apply_source_map<'a>(
scope: &mut v8::HandleScope,
source_mapper: &mut crate::source_map::SourceMapper,
file_name: Cow<'a, str>,
line_number: i64,
column_number: i64,
) -> (Cow<'a, str>, i64, i64) {
match source_mapper.apply_source_map(
scope,
&file_name,
line_number as u32,
column_number as u32,
Expand Down Expand Up @@ -357,7 +359,7 @@ impl JsStackFrame {
) {
(Some(f), Some(l), Some(c)) => {
let (file_name, line_num, col_num) =
apply_source_map(&mut source_mapper, f.into(), l, c);
apply_source_map(scope, &mut source_mapper, f.into(), l, c);
(Some(file_name.into_owned()), Some(line_num), Some(col_num))
}
(f, l, c) => (f, l, c),
Expand All @@ -370,7 +372,7 @@ impl JsStackFrame {
return Some(o);
};
let (file, line, col) =
apply_source_map(&mut source_mapper, file.into(), line, col);
apply_source_map(scope, &mut source_mapper, file.into(), line, col);
Some(format!("{before}{file}:{line}:{col}{after}"))
});

Expand Down Expand Up @@ -408,7 +410,7 @@ impl JsStackFrame {
let state = JsRuntime::state_from(scope);
let mut source_mapper = state.source_mapper.borrow_mut();
let (file_name, line_num, col_num) =
apply_source_map(&mut source_mapper, f.into(), l, c);
apply_source_map(scope, &mut source_mapper, f.into(), l, c);
Some(JsStackFrame::from_location(
Some(file_name.into_owned()),
Some(line_num),
Expand Down Expand Up @@ -515,7 +517,7 @@ impl JsError {
Self::inner_from_v8_exception(scope, exception, Default::default())
}

pub fn from_v8_message<'a>(
pub(crate) fn from_v8_message<'a>(
scope: &'a mut v8::HandleScope,
msg: v8::Local<'a, v8::Message>,
) -> Self {
Expand All @@ -525,41 +527,36 @@ impl JsError {

let exception_message = msg.get(scope).to_rust_string_lossy(scope);

// Convert them into Vec<JsStackFrame>
let mut frames: Vec<JsStackFrame> = vec![];
let mut source_line = None;
let mut source_line_frame_index = None;

if let Some(stack_frame) = JsStackFrame::from_v8_message(scope, msg) {
frames = vec![stack_frame];
}
{
let state = JsRuntime::state_from(scope);
let mut source_mapper = state.source_mapper.borrow_mut();
for (i, frame) in frames.iter().enumerate() {
if let (Some(file_name), Some(line_number)) =
(&frame.file_name, frame.line_number)
{
if !file_name.trim_start_matches('[').starts_with("ext:") {
source_line = source_mapper.get_source_line(file_name, line_number);
source_line_frame_index = Some(i);
break;
}
Comment on lines -543 to -547
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verify this in a separate PR. We support source maps for ext: modules so most likely this is not needed

}
}
}

Self {
let mut js_error = Self {
name: None,
message: None,
exception_message,
cause: None,
source_line,
source_line_frame_index,
frames,
source_line: None,
source_line_frame_index: None,
frames: vec![],
stack: None,
aggregated: None,
};

let Some(stack_frame) = JsStackFrame::from_v8_message(scope, msg) else {
return js_error;
};

let state = JsRuntime::state_from(scope);
let mut source_mapper = state.source_mapper.borrow_mut();
if let (Some(file_name), Some(line_number)) =
(&stack_frame.file_name, stack_frame.line_number)
{
if !file_name.trim_start_matches('[').starts_with("ext:") {
js_error.source_line =
source_mapper.get_source_line(file_name, line_number);
js_error.source_line_frame_index = Some(0);
}
}

js_error.frames = vec![stack_frame];
js_error
}

fn inner_from_v8_exception<'a>(
Expand Down Expand Up @@ -678,14 +675,14 @@ impl JsError {
{
let state = JsRuntime::state_from(scope);
let mut source_mapper = state.source_mapper.borrow_mut();
for (i, frame) in frames.iter().enumerate() {
for (index, frame) in frames.iter().enumerate() {
if let (Some(file_name), Some(line_number)) =
(&frame.file_name, frame.line_number)
{
if !file_name.trim_start_matches('[').starts_with("ext:") {
source_line =
source_mapper.get_source_line(file_name, line_number);
source_line_frame_index = Some(i);
source_line_frame_index = Some(index);
break;
}
}
Expand Down Expand Up @@ -749,13 +746,35 @@ impl std::error::Error for JsError {}

impl Display for JsError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
// eprintln!("inside js error {}", self.source_line.is_some());
if let Some(stack) = &self.stack {
// eprintln!("inside js error stack");
let stack_lines = stack.lines();
if stack_lines.count() > 1 {
return write!(f, "{stack}");
}
}

write!(f, "{}", self.exception_message)?;
if let Some(source_line) = self.source_line.as_ref() {
writeln!(f)?;
write!(f, " {}", source_line)?;
let column_number = self
.source_line_frame_index
.and_then(|i| self.frames.get(i).unwrap().column_number);
if let Some(column_number) = column_number {
let mut s = String::new();
for _i in 0..(column_number - 1) {
if source_line.chars().nth(_i as usize).unwrap() == '\t' {
s.push('\t');
} else {
s.push(' ');
}
}
s.push('^');
write!(f, " {}", s)?;
}
}
let location = self.frames.first().and_then(|f| f.maybe_format_location());
if let Some(location) = location {
write!(f, "\n at {location}")?;
Expand Down
12 changes: 10 additions & 2 deletions core/examples/ts_module_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,16 @@ impl ModuleLoader for TypescriptModuleLoader {
ModuleLoadResponse::Sync(load(source_maps, module_specifier))
}

fn get_source_map(&self, specifier: &str) -> Option<Vec<u8>> {
self.source_maps.borrow().get(specifier).cloned()
fn get_source_map_for_file(&self, file_name: &str) -> Option<Vec<u8>> {
self.source_maps.borrow().get(file_name).cloned()
}

fn load_source_map_file(
&self,
source_map_file_name: &str,
_file_name: &str,
) -> Option<Vec<u8>> {
std::fs::read(source_map_file_name).ok()
}
}

Expand Down
27 changes: 25 additions & 2 deletions core/modules/loaders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,34 @@ pub trait ModuleLoader {

/// Returns a source map for given `file_name`.
///
/// This function will soon be deprecated or renamed.
fn get_source_map(&self, _file_name: &str) -> Option<Vec<u8>> {
/// Some embedders might opt into stripping out inlined source map from the source
/// code before it's loaded into V8. The source mapping logic will first ask
/// embedder to provide a source map for given file, before falling back
/// to looking for source map inlined in the code.
fn get_source_map_for_file(&self, _file_name: &str) -> Option<Vec<u8>> {
None
}

/// Returns a source map file.
///
/// This is fallback API for [`Self::get_source_map_for_file`] that will only be called
/// if a module contains magic comment like `//# sourceMappingURL=foo.js.map`.
///
/// Embedders might apply additional logic to decide if the source map should be returned.
/// Eg. if a local file tries to link to a remote source map, embedder might opt into
/// refusing to load it.
fn load_source_map_file(
&self,
_source_map_file_name: &str,
_file_name: &str,
) -> Option<Vec<u8>> {
None
}

/// Return an "original" version of the given line for a file.
///
/// In the future this API might be deprecated in favor of handling it internally
/// in `deno_core` using contents of the source map.
fn get_source_mapped_source_line(
&self,
_file_name: &str,
Expand Down
2 changes: 1 addition & 1 deletion core/ops_builtin_v8.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1082,7 +1082,7 @@ pub fn op_current_user_call_site(
let application = js_runtime_state
.source_mapper
.borrow_mut()
.apply_source_map(&file_name, line_number, column_number);
.apply_source_map(scope, &file_name, line_number, column_number);

match application {
SourceMapApplication::Unchanged => {
Expand Down
Loading
Loading