0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-01 09:24:20 -04:00
denoland-deno/core/source_map.rs
Nayeem Rahman 79b42808a4
perf(core): Cache source lookups (#14816)
Keep a cache for source maps and source lines. 

We sort of already had a cache argument for source map lookup 
functions but we just passed an empty map instead of storing it. 

Extended it to cache source line lookups as well and plugged it 
into runtime state.
2022-06-20 14:42:20 +02:00

91 lines
2.8 KiB
Rust

// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
//! This mod provides functions to remap a `JsError` based on a source map.
use crate::resolve_url;
pub use sourcemap::SourceMap;
use std::collections::HashMap;
use std::str;
pub trait SourceMapGetter {
/// Returns the raw source map file.
fn get_source_map(&self, file_name: &str) -> Option<Vec<u8>>;
fn get_source_line(
&self,
file_name: &str,
line_number: usize,
) -> Option<String>;
}
#[derive(Debug, Default)]
pub struct SourceMapCache {
maps: HashMap<String, Option<SourceMap>>,
source_lines: HashMap<(String, i64), Option<String>>,
}
pub fn apply_source_map<G: SourceMapGetter + ?Sized>(
file_name: String,
line_number: i64,
column_number: i64,
cache: &mut SourceMapCache,
getter: &G,
) -> (String, i64, i64) {
// Lookup expects 0-based line and column numbers, but ours are 1-based.
let line_number = line_number - 1;
let column_number = column_number - 1;
let default_pos = (file_name.clone(), line_number, column_number);
let maybe_source_map =
cache.maps.entry(file_name.clone()).or_insert_with(|| {
getter
.get_source_map(&file_name)
.and_then(|raw_source_map| SourceMap::from_slice(&raw_source_map).ok())
});
let (file_name, line_number, column_number) = match maybe_source_map {
None => default_pos,
Some(source_map) => {
match source_map.lookup_token(line_number as u32, column_number as u32) {
None => default_pos,
Some(token) => match token.get_source() {
None => default_pos,
Some(source_file_name) => {
// The `source_file_name` written by tsc in the source map is
// sometimes only the basename of the URL, or has unwanted `<`/`>`
// around it. Use the `file_name` we get from V8 if
// `source_file_name` does not parse as a URL.
let file_name = match resolve_url(source_file_name) {
Ok(m) if m.scheme() == "blob" => file_name,
Ok(m) => m.to_string(),
Err(_) => file_name,
};
(
file_name,
i64::from(token.get_src_line()),
i64::from(token.get_src_col()),
)
}
},
}
}
};
(file_name, line_number + 1, column_number + 1)
}
const MAX_SOURCE_LINE_LENGTH: usize = 150;
pub fn get_source_line<G: SourceMapGetter + ?Sized>(
file_name: &str,
line_number: i64,
cache: &mut SourceMapCache,
getter: &G,
) -> Option<String> {
cache
.source_lines
.entry((file_name.to_string(), line_number))
.or_insert_with(|| {
// Source lookup expects a 0-based line number, ours are 1-based.
let s = getter.get_source_line(file_name, (line_number - 1) as usize);
s.filter(|s| s.len() <= MAX_SOURCE_LINE_LENGTH)
})
.clone()
}