perf(codegen)!: borrow sourcemaps from codegen (#23422)

Summary:
- Upgrade oxc_sourcemap to 8.0.1.
- Return borrowed SourceMap from CodegenReturn.
- Support no-sourcemap feature builds used by benchmarks.

AI-assisted: yes
This commit is contained in:
Boshen authored and GitHub committed 2026-06-15 19:07:21 +08:00
1 parent d241add8d8
commit 7a24911846
10 files changed
+77 -39

No files matched your search

Generated
+2 -2
View File
@@ -2628,9 +2628,9 @@ dependencies = [
[[package]]
name = "oxc_sourcemap"
version = "7.0.0"
version = "8.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3866f9075027840e0711b52e15d2e1ba076fc5f5f01c96eeedd26375410641"
checksum = "174d2498c2390ccfab0a9caaf47ef5accda1d509aaa34a5be8f45e00e5299f04"
dependencies = [
"base64-simd",
"json-escape-simd",
+1 -1
View File
@@ -175,7 +175,7 @@ json-strip-comments = "3.1.0" # Strip JSON comments
oxc-browserslist = "3.0.0" # Browser compatibility queries
oxc_index = { version = "5.0.0", features = ["nonmax", "serde"] } # Type-safe indices
oxc_resolver = "11.16.3" # Module resolution
oxc_sourcemap = "7.0.0" # Source map generation
oxc_sourcemap = "8.0.1" # Source map generation
#
allocator-api2 = "=0.2.21" # Allocator trait
+6 -6
View File
@@ -27,7 +27,7 @@ impl CompilerInterface for Compiler {
self.errors.extend(errors);
}
fn after_codegen(&mut self, ret: CodegenReturn) {
fn after_codegen(&mut self, ret: CodegenReturn<'_>) {
self.printed = ret.code;
}
}
@@ -112,7 +112,7 @@ pub trait CompilerInterface {
ControlFlow::Continue(())
}
fn after_isolated_declarations(&mut self, _ret: CodegenReturn) {}
fn after_isolated_declarations(&mut self, _ret: CodegenReturn<'_>) {}
fn after_transform(
&mut self,
@@ -122,7 +122,7 @@ pub trait CompilerInterface {
ControlFlow::Continue(())
}
fn after_codegen(&mut self, _ret: CodegenReturn) {}
fn after_codegen(&mut self, _ret: CodegenReturn<'_>) {}
fn compile(&mut self, source_text: &str, source_type: SourceType, source_path: &Path) {
let allocator = Allocator::default();
@@ -308,13 +308,13 @@ pub trait CompilerInterface {
Mangler::new().with_options(options).build(program)
}
fn codegen(
fn codegen<'a>(
&self,
program: &Program<'_>,
program: &Program<'a>,
source_path: &Path,
mangler_return: Option<ManglerReturn>,
options: CodegenOptions,
) -> CodegenReturn {
) -> CodegenReturn<'a> {
let mut options = options;
if self.enable_sourcemap() {
options.source_map_path = Some(source_path.to_path_buf());
+10 -3
View File
@@ -3,6 +3,8 @@
//! Code adapted from
//! * [esbuild](https://github.com/evanw/esbuild/blob/v0.24.0/internal/js_printer/js_printer.go)
#[cfg(not(feature = "sourcemap"))]
use std::marker::PhantomData;
use std::{borrow::Cow, cmp, slice};
use cow_utils::CowUtils;
@@ -48,7 +50,7 @@ pub use oxc_data_structures::code_buffer::IndentChar;
/// Output from [`Codegen::build`]
#[non_exhaustive]
pub struct CodegenReturn {
pub struct CodegenReturn<'a> {
/// The generated source code.
pub code: String,
@@ -56,7 +58,10 @@ pub struct CodegenReturn {
///
/// You must set [`CodegenOptions::source_map_path`] for this to be [`Some`].
#[cfg(feature = "sourcemap")]
pub map: Option<oxc_sourcemap::OwnedSourceMap>,
pub map: Option<oxc_sourcemap::SourceMap<'a>>,
#[cfg(not(feature = "sourcemap"))]
_source_map_lifetime: PhantomData<&'a ()>,
/// All the legal comments returned from [LegalComment::Linked] or [LegalComment::External].
pub legal_comments: Vec<Comment>,
@@ -246,7 +251,7 @@ impl<'a> Codegen<'a> {
///
/// A source map will be generated if [`CodegenOptions::source_map_path`] is set.
#[must_use]
pub fn build(mut self, program: &Program<'a>) -> CodegenReturn {
pub fn build(mut self, program: &Program<'a>) -> CodegenReturn<'a> {
self.quote = if self.options.single_quote { Quote::Single } else { Quote::Double };
self.source_text = Some(program.source_text);
self.indent = self.options.initial_indent;
@@ -265,6 +270,8 @@ impl<'a> Codegen<'a> {
code,
#[cfg(feature = "sourcemap")]
map,
#[cfg(not(feature = "sourcemap"))]
_source_map_lifetime: PhantomData,
legal_comments,
}
}
+40 -16
View File
@@ -1,8 +1,9 @@
use std::path::Path;
use std::{borrow::Cow, path::Path};
use oxc_index::{IndexVec, define_nonmax_u32_index_type};
use oxc_span::Span;
use oxc_syntax::line_terminator::{LS, LS_LAST_2_BYTES, LS_OR_PS_FIRST_BYTE, PS, PS_LAST_2_BYTES};
use rustc_hash::FxHashMap;
/// Number of lines to check with linear search when translating byte position to line index
const LINE_SEARCH_LINEAR_ITERATIONS: usize = 16;
@@ -45,14 +46,15 @@ pub struct ColumnOffsets {
columns: Box<[u32]>,
}
#[expect(clippy::struct_field_names)]
pub struct SourcemapBuilder<'a> {
source_id: u32,
source_name: String,
original_source: &'a str,
names: Vec<&'a str>,
names_map: FxHashMap<&'a str, u32>,
tokens: Vec<oxc_sourcemap::Token>,
last_generated_update: usize,
last_position: Option<u32>,
line_offset_tables: LineOffsetTables,
sourcemap_builder: oxc_sourcemap::SourceMapBuilder,
generated_line: u32,
generated_column: u32,
/// Tracks the last accessed line index to optimize sequential lookups in `search_original_line_and_column`.
@@ -63,25 +65,37 @@ pub struct SourcemapBuilder<'a> {
impl<'a> SourcemapBuilder<'a> {
pub fn new(path: &Path, source_text: &'a str) -> Self {
let mut sourcemap_builder = oxc_sourcemap::SourceMapBuilder::default();
let line_offset_tables = Self::generate_line_offset_tables(source_text);
let source_id =
sourcemap_builder.set_source_and_content(path.to_string_lossy().as_ref(), source_text);
Self {
source_id,
source_name: path.to_string_lossy().into_owned(),
original_source: source_text,
names: Vec::new(),
names_map: FxHashMap::default(),
tokens: Vec::new(),
last_generated_update: 0,
last_position: None,
line_offset_tables,
sourcemap_builder,
generated_line: 0,
generated_column: 0,
last_line_lookup: 0,
}
}
pub fn into_sourcemap(self) -> oxc_sourcemap::OwnedSourceMap {
self.sourcemap_builder.into_owned_sourcemap()
pub fn into_sourcemap(mut self) -> oxc_sourcemap::SourceMap<'a> {
self.names.shrink_to_fit();
self.tokens.shrink_to_fit();
oxc_sourcemap::SourceMap::from_parts(oxc_sourcemap::SourceMapParts {
file: None,
names: self.names.into_iter().map(Cow::Borrowed).collect(),
source_root: None,
sources: vec![Cow::Owned(self.source_name)],
source_contents: vec![Some(Cow::Borrowed(self.original_source))],
tokens: self.tokens.into_boxed_slice(),
token_chunks: None,
x_google_ignore_list: None,
debug_id: None,
})
}
pub fn add_source_mapping_for_name(&mut self, output: &[u8], span: Span, name: &str) {
@@ -99,24 +113,34 @@ impl<'a> SourcemapBuilder<'a> {
self.add_source_mapping(output, span.start, token_name);
}
pub fn add_source_mapping(&mut self, output: &[u8], position: u32, name: Option<&str>) {
pub fn add_source_mapping(&mut self, output: &[u8], position: u32, name: Option<&'a str>) {
if self.last_position == Some(position) {
return;
}
let (original_line, original_column) = self.search_original_line_and_column(position);
self.update_generated_line_and_column(output);
let name_id = name.map(|s| self.sourcemap_builder.add_name(s));
self.sourcemap_builder.add_token(
let name_id = name.map(|s| self.add_name(s));
self.tokens.push(oxc_sourcemap::Token::new(
self.generated_line,
self.generated_column,
original_line,
original_column,
Some(self.source_id),
Some(0),
name_id,
);
));
self.last_position = Some(position);
}
fn add_name(&mut self, name: &'a str) -> u32 {
if let Some(&id) = self.names_map.get(name) {
return id;
}
let id = u32::try_from(self.names.len()).expect("sourcemap names length should fit in u32");
self.names_map.insert(name, id);
self.names.push(name);
id
}
#[expect(clippy::cast_possible_truncation)]
fn search_original_line_and_column(&mut self, position: u32) -> (u32, u32) {
let original_line = self.search_original_line(position);
+10 -3
View File
@@ -1,5 +1,6 @@
use oxc_allocator::Allocator;
use oxc_codegen::{Codegen, CodegenOptions, CodegenReturn};
use oxc_ast::ast::Comment;
use oxc_codegen::{Codegen, CodegenOptions};
use oxc_parser::{ParseOptions, Parser};
use oxc_span::SourceType;
@@ -95,14 +96,20 @@ pub fn codegen(source_text: &str) -> String {
}
#[track_caller]
pub fn codegen_options(source_text: &str, options: &CodegenOptions) -> CodegenReturn {
pub fn codegen_options(source_text: &str, options: &CodegenOptions) -> CodegenTestReturn {
let allocator = Allocator::default();
let source_type = SourceType::ts();
let ret = Parser::new(&allocator, source_text, source_type).parse();
assert!(ret.diagnostics.is_empty(), "Parse errors: {:?}", ret.diagnostics);
let mut options = options.clone();
options.single_quote = true;
Codegen::new().with_options(options).build(&ret.program)
let ret = Codegen::new().with_options(options).build(&ret.program);
CodegenTestReturn { code: ret.code, legal_comments: ret.legal_comments }
}
pub struct CodegenTestReturn {
pub code: String,
pub legal_comments: Vec<Comment>,
}
#[track_caller]
+4 -4
View File
@@ -81,15 +81,15 @@ fn main() -> std::io::Result<()> {
Ok(())
}
fn minify(
allocator: &Allocator,
source_text: &str,
fn minify<'a>(
allocator: &'a Allocator,
source_text: &'a str,
source_type: SourceType,
source_map_path: Option<PathBuf>,
mangle: bool,
nospace: bool,
max_iterations: Option<u8>,
) -> CodegenReturn {
) -> CodegenReturn<'a> {
let ret = Parser::new(allocator, source_text, source_type).parse();
assert!(ret.diagnostics.is_empty());
let mut program = ret.program;
+2 -2
View File
@@ -871,12 +871,12 @@ impl CompilerInterface for Compiler {
self.inject.clone()
}
fn after_codegen(&mut self, ret: CodegenReturn) {
fn after_codegen(&mut self, ret: CodegenReturn<'_>) {
self.printed = ret.code;
self.printed_sourcemap = ret.map.map(SourceMap::from);
}
fn after_isolated_declarations(&mut self, ret: CodegenReturn) {
fn after_isolated_declarations(&mut self, ret: CodegenReturn<'_>) {
self.declaration.replace(ret.code);
self.declaration_map = ret.map.map(SourceMap::from);
}
+1 -1
View File
@@ -119,7 +119,7 @@ impl CompilerInterface for Driver {
ControlFlow::Continue(())
}
fn after_codegen(&mut self, ret: CodegenReturn) {
fn after_codegen(&mut self, ret: CodegenReturn<'_>) {
self.printed = ret.code;
}
}
+1 -1
View File
@@ -52,7 +52,7 @@ impl CompilerInterface for Driver {
self.errors.extend(errors);
}
fn after_codegen(&mut self, ret: CodegenReturn) {
fn after_codegen(&mut self, ret: CodegenReturn<'_>) {
self.printed = ret.code;
}