From 2b7be08af4ca983a3e5ae53d928b7a31ae357688 Mon Sep 17 00:00:00 2001 From: Boshen <1430279+Boshen@users.noreply.github.com> Date: Fri, 11 Oct 2024 04:13:41 +0000 Subject: [PATCH] feat(ast)! add `source_text` to `Program` (#6444) --- crates/oxc/README.md | 2 +- crates/oxc/src/compiler.rs | 43 ++++++------------- crates/oxc_ast/src/ast/js.rs | 2 + crates/oxc_ast/src/ast_impl/js.rs | 14 +----- .../oxc_ast/src/generated/assert_layouts.rs | 22 +++++----- crates/oxc_ast/src/generated/ast_builder.rs | 33 +++++++++++--- .../oxc_ast/src/generated/derive_clone_in.rs | 1 + .../src/generated/derive_content_eq.rs | 1 + .../src/generated/derive_content_hash.rs | 1 + crates/oxc_isolated_declarations/src/lib.rs | 3 +- crates/oxc_language_server/src/linter.rs | 2 +- crates/oxc_linter/examples/linter.rs | 2 +- crates/oxc_linter/src/service/runtime.rs | 2 +- crates/oxc_linter/src/utils/jest.rs | 2 +- crates/oxc_mangler/src/lib.rs | 2 +- crates/oxc_minifier/src/compressor.rs | 2 +- crates/oxc_minifier/src/tester.rs | 2 +- crates/oxc_parser/src/lib.rs | 10 ++++- crates/oxc_semantic/examples/cfg.rs | 2 +- crates/oxc_semantic/examples/simple.rs | 2 +- crates/oxc_semantic/src/builder.rs | 17 ++++++-- crates/oxc_semantic/src/jsdoc/builder.rs | 3 +- crates/oxc_semantic/src/jsdoc/parser/jsdoc.rs | 2 +- .../src/jsdoc/parser/jsdoc_tag.rs | 2 +- crates/oxc_semantic/src/lib.rs | 2 +- crates/oxc_semantic/src/module_record/mod.rs | 5 +-- .../tests/integration/util/mod.rs | 2 +- crates/oxc_semantic/tests/main.rs | 6 +-- .../oxc_transformer/examples/transformer.rs | 4 +- crates/oxc_transformer/src/context.rs | 9 +--- crates/oxc_transformer/src/lib.rs | 4 +- .../tests/plugins/inject_global_variables.rs | 6 +-- .../tests/plugins/replace_global_defines.rs | 6 +-- crates/oxc_traverse/src/generated/ancestor.rs | 16 +++++++ crates/oxc_wasm/src/lib.rs | 12 +++--- tasks/benchmark/benches/linter.rs | 2 +- tasks/benchmark/benches/minifier.rs | 2 +- tasks/benchmark/benches/semantic.rs | 2 +- tasks/benchmark/benches/transformer.rs | 13 ++---- tasks/transform_checker/src/lib.rs | 2 +- tasks/transform_conformance/src/driver.rs | 5 +-- 41 files changed, 143 insertions(+), 129 deletions(-) diff --git a/crates/oxc/README.md b/crates/oxc/README.md index e5fb1399df72b..20d07dfaf5dfa 100644 --- a/crates/oxc/README.md +++ b/crates/oxc/README.md @@ -99,7 +99,7 @@ if panicked { let SemanticBuilderReturn { semantic, errors: semantic_errors, -} = SemanticBuilder::new(source_text) +} = SemanticBuilder::new() .with_check_syntax_error(true) // Enable extra syntax error checking .with_trivias(trivias) // Pass comments for JSDoc parsing .with_build_jsdoc(true) // Enable JSDoc parsing diff --git a/crates/oxc/src/compiler.rs b/crates/oxc/src/compiler.rs index 6b72e597c43df..30027347a5bea 100644 --- a/crates/oxc/src/compiler.rs +++ b/crates/oxc/src/compiler.rs @@ -139,19 +139,12 @@ pub trait CompilerInterface { /* Isolated Declarations */ if let Some(options) = self.isolated_declaration_options() { - self.isolated_declaration( - options, - &allocator, - &program, - source_text, - source_path, - &trivias, - ); + self.isolated_declaration(options, &allocator, &program, source_path, &trivias); } /* Semantic */ - let mut semantic_return = self.semantic(&program, source_text, source_path); + let mut semantic_return = self.semantic(&program, source_path); if !semantic_return.errors.is_empty() { self.handle_errors(semantic_return.errors); return; @@ -170,7 +163,6 @@ pub trait CompilerInterface { &allocator, &mut program, source_path, - source_text, &trivias, symbols, scopes, @@ -218,7 +210,7 @@ pub trait CompilerInterface { /* Codegen */ if let Some(options) = self.codegen_options() { - let ret = self.codegen(&program, source_text, source_path, &trivias, mangler, options); + let ret = self.codegen(&program, source_path, &trivias, mangler, options); self.after_codegen(ret); } } @@ -232,13 +224,8 @@ pub trait CompilerInterface { Parser::new(allocator, source_text, source_type).with_options(self.parse_options()).parse() } - fn semantic<'a>( - &self, - program: &Program<'a>, - source_text: &'a str, - source_path: &Path, - ) -> SemanticBuilderReturn<'a> { - let mut builder = SemanticBuilder::new(source_text); + fn semantic<'a>(&self, program: &Program<'a>, source_path: &Path) -> SemanticBuilderReturn<'a> { + let mut builder = SemanticBuilder::new(); if self.transform_options().is_some() { // Estimate transformer will triple scopes, symbols, references @@ -257,16 +244,14 @@ pub trait CompilerInterface { options: IsolatedDeclarationsOptions, allocator: &'a Allocator, program: &Program<'a>, - source_text: &'a str, source_path: &Path, trivias: &Trivias, ) { - let ret = - IsolatedDeclarations::new(allocator, source_text, trivias, options).build(program); + let ret = IsolatedDeclarations::new(allocator, program.source_text, trivias, options) + .build(program); self.handle_errors(ret.errors); let ret = self.codegen( &ret.program, - source_text, source_path, trivias, None, @@ -282,12 +267,11 @@ pub trait CompilerInterface { allocator: &'a Allocator, program: &mut Program<'a>, source_path: &Path, - source_text: &'a str, trivias: &Trivias, symbols: SymbolTable, scopes: ScopeTree, ) -> TransformerReturn { - Transformer::new(allocator, source_path, source_text, trivias.clone(), options) + Transformer::new(allocator, source_path, trivias.clone(), options) .build_with_symbols_and_scopes(symbols, scopes, program) } @@ -304,10 +288,9 @@ pub trait CompilerInterface { Mangler::new().with_options(options).build(program) } - fn codegen<'a>( + fn codegen( &self, - program: &Program<'a>, - source_text: &'a str, + program: &Program<'_>, source_path: &Path, trivias: &Trivias, mangler: Option, @@ -317,10 +300,10 @@ pub trait CompilerInterface { let mut codegen = CodeGenerator::new() .with_options(options) .with_mangler(mangler) - .enable_comment(source_text, trivias.clone(), comment_options); + .enable_comment(program.source_text, trivias.clone(), comment_options); if self.enable_sourcemap() { - codegen = - codegen.enable_source_map(source_path.to_string_lossy().as_ref(), source_text); + codegen = codegen + .enable_source_map(source_path.to_string_lossy().as_ref(), program.source_text); } codegen.build(program) } diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index e06d43eb38e4a..a68e3911082a0 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -39,6 +39,8 @@ pub struct Program<'a> { #[serde(flatten)] pub span: Span, pub source_type: SourceType, + #[serde(skip)] + pub source_text: &'a str, pub hashbang: Option>, pub directives: Vec<'a, Directive<'a>>, pub body: Vec<'a, Statement<'a>>, diff --git a/crates/oxc_ast/src/ast_impl/js.rs b/crates/oxc_ast/src/ast_impl/js.rs index c11746f21b938..9aaacb8227760 100644 --- a/crates/oxc_ast/src/ast_impl/js.rs +++ b/crates/oxc_ast/src/ast_impl/js.rs @@ -1,7 +1,7 @@ use std::{borrow::Cow, cell::Cell, fmt}; use oxc_allocator::{Box, FromIn, Vec}; -use oxc_span::{Atom, GetSpan, SourceType, Span}; +use oxc_span::{Atom, GetSpan, Span}; use oxc_syntax::{ operator::UnaryOperator, reference::ReferenceId, @@ -28,18 +28,6 @@ export interface FormalParameterRest extends Span { } "#; -impl<'a> Program<'a> { - pub fn new( - span: Span, - source_type: SourceType, - directives: Vec<'a, Directive<'a>>, - hashbang: Option>, - body: Vec<'a, Statement<'a>>, - ) -> Self { - Self { span, source_type, directives, hashbang, body, scope_id: Cell::default() } - } -} - impl<'a> Program<'a> { pub fn is_empty(&self) -> bool { self.body.is_empty() && self.directives.is_empty() diff --git a/crates/oxc_ast/src/generated/assert_layouts.rs b/crates/oxc_ast/src/generated/assert_layouts.rs index 7969d6dfe8585..ed4f099ca52b0 100644 --- a/crates/oxc_ast/src/generated/assert_layouts.rs +++ b/crates/oxc_ast/src/generated/assert_layouts.rs @@ -55,14 +55,15 @@ const _: () = { assert!(offset_of!(StringLiteral, span) == 0usize); assert!(offset_of!(StringLiteral, value) == 8usize); - assert!(size_of::() == 112usize); + assert!(size_of::() == 128usize); assert!(align_of::() == 8usize); assert!(offset_of!(Program, span) == 0usize); assert!(offset_of!(Program, source_type) == 8usize); - assert!(offset_of!(Program, hashbang) == 16usize); - assert!(offset_of!(Program, directives) == 40usize); - assert!(offset_of!(Program, body) == 72usize); - assert!(offset_of!(Program, scope_id) == 104usize); + assert!(offset_of!(Program, source_text) == 16usize); + assert!(offset_of!(Program, hashbang) == 32usize); + assert!(offset_of!(Program, directives) == 56usize); + assert!(offset_of!(Program, body) == 88usize); + assert!(offset_of!(Program, scope_id) == 120usize); assert!(size_of::() == 16usize); assert!(align_of::() == 8usize); @@ -1597,14 +1598,15 @@ const _: () = { assert!(offset_of!(StringLiteral, span) == 0usize); assert!(offset_of!(StringLiteral, value) == 8usize); - assert!(size_of::() == 64usize); + assert!(size_of::() == 72usize); assert!(align_of::() == 4usize); assert!(offset_of!(Program, span) == 0usize); assert!(offset_of!(Program, source_type) == 8usize); - assert!(offset_of!(Program, hashbang) == 12usize); - assert!(offset_of!(Program, directives) == 28usize); - assert!(offset_of!(Program, body) == 44usize); - assert!(offset_of!(Program, scope_id) == 60usize); + assert!(offset_of!(Program, source_text) == 12usize); + assert!(offset_of!(Program, hashbang) == 20usize); + assert!(offset_of!(Program, directives) == 36usize); + assert!(offset_of!(Program, body) == 52usize); + assert!(offset_of!(Program, scope_id) == 68usize); assert!(size_of::() == 8usize); assert!(align_of::() == 4usize); diff --git a/crates/oxc_ast/src/generated/ast_builder.rs b/crates/oxc_ast/src/generated/ast_builder.rs index e0fe52d16d83b..000ed9f5234f6 100644 --- a/crates/oxc_ast/src/generated/ast_builder.rs +++ b/crates/oxc_ast/src/generated/ast_builder.rs @@ -221,19 +221,32 @@ impl<'a> AstBuilder<'a> { /// ## Parameters /// - span: The [`Span`] covering this node /// - source_type + /// - source_text /// - hashbang /// - directives /// - body #[inline] - pub fn program( + pub fn program( self, span: Span, source_type: SourceType, + source_text: S, hashbang: Option>, directives: Vec<'a, Directive<'a>>, body: Vec<'a, Statement<'a>>, - ) -> Program<'a> { - Program { span, source_type, hashbang, directives, body, scope_id: Default::default() } + ) -> Program<'a> + where + S: IntoIn<'a, &'a str>, + { + Program { + span, + source_type, + source_text: source_text.into_in(self.allocator), + hashbang, + directives, + body, + scope_id: Default::default(), + } } /// Builds a [`Program`] and stores it in the memory arena. @@ -243,19 +256,27 @@ impl<'a> AstBuilder<'a> { /// ## Parameters /// - span: The [`Span`] covering this node /// - source_type + /// - source_text /// - hashbang /// - directives /// - body #[inline] - pub fn alloc_program( + pub fn alloc_program( self, span: Span, source_type: SourceType, + source_text: S, hashbang: Option>, directives: Vec<'a, Directive<'a>>, body: Vec<'a, Statement<'a>>, - ) -> Box<'a, Program<'a>> { - Box::new_in(self.program(span, source_type, hashbang, directives, body), self.allocator) + ) -> Box<'a, Program<'a>> + where + S: IntoIn<'a, &'a str>, + { + Box::new_in( + self.program(span, source_type, source_text, hashbang, directives, body), + self.allocator, + ) } /// Build a [`Expression::BooleanLiteral`] diff --git a/crates/oxc_ast/src/generated/derive_clone_in.rs b/crates/oxc_ast/src/generated/derive_clone_in.rs index 798007b74b4ff..6f48349f6af21 100644 --- a/crates/oxc_ast/src/generated/derive_clone_in.rs +++ b/crates/oxc_ast/src/generated/derive_clone_in.rs @@ -112,6 +112,7 @@ impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for Program<'old_alloc> { Program { span: CloneIn::clone_in(&self.span, allocator), source_type: CloneIn::clone_in(&self.source_type, allocator), + source_text: CloneIn::clone_in(&self.source_text, allocator), hashbang: CloneIn::clone_in(&self.hashbang, allocator), directives: CloneIn::clone_in(&self.directives, allocator), body: CloneIn::clone_in(&self.body, allocator), diff --git a/crates/oxc_ast/src/generated/derive_content_eq.rs b/crates/oxc_ast/src/generated/derive_content_eq.rs index 783c23b7c7e79..d85fe2fb7e3f7 100644 --- a/crates/oxc_ast/src/generated/derive_content_eq.rs +++ b/crates/oxc_ast/src/generated/derive_content_eq.rs @@ -92,6 +92,7 @@ impl<'a> ContentEq for StringLiteral<'a> { impl<'a> ContentEq for Program<'a> { fn content_eq(&self, other: &Self) -> bool { ContentEq::content_eq(&self.source_type, &other.source_type) + && ContentEq::content_eq(&self.source_text, &other.source_text) && ContentEq::content_eq(&self.hashbang, &other.hashbang) && ContentEq::content_eq(&self.directives, &other.directives) && ContentEq::content_eq(&self.body, &other.body) diff --git a/crates/oxc_ast/src/generated/derive_content_hash.rs b/crates/oxc_ast/src/generated/derive_content_hash.rs index dfe5280b67d5f..82f89e9bac5d9 100644 --- a/crates/oxc_ast/src/generated/derive_content_hash.rs +++ b/crates/oxc_ast/src/generated/derive_content_hash.rs @@ -70,6 +70,7 @@ impl<'a> ContentHash for StringLiteral<'a> { impl<'a> ContentHash for Program<'a> { fn content_hash(&self, state: &mut H) { ContentHash::content_hash(&self.source_type, state); + ContentHash::content_hash(&self.source_text, state); ContentHash::content_hash(&self.hashbang, state); ContentHash::content_hash(&self.directives, state); ContentHash::content_hash(&self.body, state); diff --git a/crates/oxc_isolated_declarations/src/lib.rs b/crates/oxc_isolated_declarations/src/lib.rs index a057a516a5d85..acb5f0be76fe6 100644 --- a/crates/oxc_isolated_declarations/src/lib.rs +++ b/crates/oxc_isolated_declarations/src/lib.rs @@ -92,7 +92,8 @@ impl<'a> IsolatedDeclarations<'a> { let source_type = SourceType::d_ts(); let directives = self.ast.vec(); let stmts = self.transform_program(program); - let program = self.ast.program(SPAN, source_type, None, directives, stmts); + let program = + self.ast.program(SPAN, source_type, program.source_text, None, directives, stmts); IsolatedDeclarationsReturn { program, errors: self.take_errors() } } diff --git a/crates/oxc_language_server/src/linter.rs b/crates/oxc_language_server/src/linter.rs index 8cf40c4e3cac8..6bc98d6b1c3f1 100644 --- a/crates/oxc_language_server/src/linter.rs +++ b/crates/oxc_language_server/src/linter.rs @@ -260,7 +260,7 @@ impl IsolatedLintHandler { }; let program = allocator.alloc(ret.program); - let semantic_ret = SemanticBuilder::new(javascript_source_text) + let semantic_ret = SemanticBuilder::new() .with_cfg(true) .with_trivias(ret.trivias) .with_check_syntax_error(true) diff --git a/crates/oxc_linter/examples/linter.rs b/crates/oxc_linter/examples/linter.rs index be0ae5575bd2b..c93dbef31ac13 100644 --- a/crates/oxc_linter/examples/linter.rs +++ b/crates/oxc_linter/examples/linter.rs @@ -30,7 +30,7 @@ fn main() -> std::io::Result<()> { } let program = allocator.alloc(ret.program); - let semantic_ret = SemanticBuilder::new(&source_text).with_trivias(ret.trivias).build(program); + let semantic_ret = SemanticBuilder::new().with_trivias(ret.trivias).build(program); let mut errors: Vec = vec![]; diff --git a/crates/oxc_linter/src/service/runtime.rs b/crates/oxc_linter/src/service/runtime.rs index ab9d4542e372a..1820efb090099 100644 --- a/crates/oxc_linter/src/service/runtime.rs +++ b/crates/oxc_linter/src/service/runtime.rs @@ -208,7 +208,7 @@ impl Runtime { // Build the module record to unblock other threads from waiting for too long. // The semantic model is not built at this stage. - let semantic_builder = SemanticBuilder::new(source_text) + let semantic_builder = SemanticBuilder::new() .with_cfg(true) .with_trivias(trivias) .with_build_jsdoc(true) diff --git a/crates/oxc_linter/src/utils/jest.rs b/crates/oxc_linter/src/utils/jest.rs index 2479167e50988..27a61b9f02c3a 100644 --- a/crates/oxc_linter/src/utils/jest.rs +++ b/crates/oxc_linter/src/utils/jest.rs @@ -318,7 +318,7 @@ mod test { let source_type = SourceType::default(); let parser_ret = Parser::new(&allocator, "", source_type).parse(); let program = allocator.alloc(parser_ret.program); - let semantic_ret = SemanticBuilder::new("").with_cfg(true).build(program).semantic; + let semantic_ret = SemanticBuilder::new().with_cfg(true).build(program).semantic; let semantic_ret = Rc::new(semantic_ret); let build_ctx = |path: &'static str| { diff --git a/crates/oxc_mangler/src/lib.rs b/crates/oxc_mangler/src/lib.rs index 22034deffa8fb..94202715d3bc3 100644 --- a/crates/oxc_mangler/src/lib.rs +++ b/crates/oxc_mangler/src/lib.rs @@ -82,7 +82,7 @@ impl Mangler { #[must_use] pub fn build<'a>(mut self, program: &'a Program<'a>) -> Mangler { - let semantic = SemanticBuilder::new("").build(program).semantic; + let semantic = SemanticBuilder::new().build(program).semantic; // Mangle the symbol table by computing slots from the scope tree. // A slot is the occurrence index of a binding identifier inside a scope. diff --git a/crates/oxc_minifier/src/compressor.rs b/crates/oxc_minifier/src/compressor.rs index 6f7188a55b634..f722a2266a420 100644 --- a/crates/oxc_minifier/src/compressor.rs +++ b/crates/oxc_minifier/src/compressor.rs @@ -24,7 +24,7 @@ impl<'a> Compressor<'a> { pub fn build(self, program: &mut Program<'a>) { let (symbols, scopes) = - SemanticBuilder::new("").build(program).semantic.into_symbol_table_and_scope_tree(); + SemanticBuilder::new().build(program).semantic.into_symbol_table_and_scope_tree(); self.build_with_symbols_and_scopes(symbols, scopes, program); } diff --git a/crates/oxc_minifier/src/tester.rs b/crates/oxc_minifier/src/tester.rs index e159d0036f0b9..6daeec5369eb2 100644 --- a/crates/oxc_minifier/src/tester.rs +++ b/crates/oxc_minifier/src/tester.rs @@ -28,7 +28,7 @@ fn run<'a, P: CompressorPass<'a>>( if let Some(pass) = pass { let (symbols, scopes) = - SemanticBuilder::new("").build(&program).semantic.into_symbol_table_and_scope_tree(); + SemanticBuilder::new().build(&program).semantic.into_symbol_table_and_scope_tree(); let mut ctx = TraverseCtx::new(scopes, symbols, allocator); RemoveSyntax::new(CompressOptions::all_false()).build(&mut program, &mut ctx); pass.build(&mut program, &mut ctx); diff --git a/crates/oxc_parser/src/lib.rs b/crates/oxc_parser/src/lib.rs index 264771157cf76..6f115f8945160 100644 --- a/crates/oxc_parser/src/lib.rs +++ b/crates/oxc_parser/src/lib.rs @@ -404,6 +404,7 @@ impl<'a> ParserImpl<'a> { let program = self.ast.program( Span::default(), self.source_type, + self.source_text, None, self.ast.vec(), self.ast.vec(), @@ -450,7 +451,14 @@ impl<'a> ParserImpl<'a> { self.set_source_type_to_script_if_unambiguous(); let span = Span::new(0, self.source_text.len() as u32); - Ok(self.ast.program(span, self.source_type, hashbang, directives, statements)) + Ok(self.ast.program( + span, + self.source_type, + self.source_text, + hashbang, + directives, + statements, + )) } fn default_context(source_type: SourceType, options: ParseOptions) -> Context { diff --git a/crates/oxc_semantic/examples/cfg.rs b/crates/oxc_semantic/examples/cfg.rs index 02e4a3abb7a6c..e3a94b4ca3451 100644 --- a/crates/oxc_semantic/examples/cfg.rs +++ b/crates/oxc_semantic/examples/cfg.rs @@ -55,7 +55,7 @@ fn main() -> std::io::Result<()> { std::fs::write(ast_file_path, format!("{:#?}", &program))?; println!("Wrote AST to: {}", &ast_file_name); - let semantic = SemanticBuilder::new(&source_text) + let semantic = SemanticBuilder::new() .with_check_syntax_error(true) .with_trivias(parser_ret.trivias) .with_cfg(true) diff --git a/crates/oxc_semantic/examples/simple.rs b/crates/oxc_semantic/examples/simple.rs index 890b5abb8ba55..29609e2a079bd 100644 --- a/crates/oxc_semantic/examples/simple.rs +++ b/crates/oxc_semantic/examples/simple.rs @@ -34,7 +34,7 @@ fn main() -> std::io::Result<()> { let program = allocator.alloc(parser_ret.program); - let semantic = SemanticBuilder::new(&source_text) + let semantic = SemanticBuilder::new() .build_module_record(path, program) // Enable additional syntax checks not performed by the parser .with_check_syntax_error(true) diff --git a/crates/oxc_semantic/src/builder.rs b/crates/oxc_semantic/src/builder.rs index b8ec6c94ebd81..45aa581730d40 100644 --- a/crates/oxc_semantic/src/builder.rs +++ b/crates/oxc_semantic/src/builder.rs @@ -119,14 +119,20 @@ pub struct SemanticBuilderReturn<'a> { pub errors: Vec, } +impl<'a> Default for SemanticBuilder<'a> { + fn default() -> Self { + Self::new() + } +} + impl<'a> SemanticBuilder<'a> { - pub fn new(source_text: &'a str) -> Self { + pub fn new() -> Self { let scope = ScopeTree::default(); let current_scope_id = scope.root_scope_id(); let trivias = Trivias::default(); Self { - source_text, + source_text: "", source_type: SourceType::default(), trivias: trivias.clone(), errors: RefCell::new(vec![]), @@ -145,7 +151,7 @@ impl<'a> SemanticBuilder<'a> { module_record: Arc::new(ModuleRecord::default()), unused_labels: UnusedLabels::default(), build_jsdoc: false, - jsdoc: JSDocBuilder::new(source_text, &trivias), + jsdoc: JSDocBuilder::default(), stats: None, excess_capacity: 0.0, check_syntax_error: false, @@ -178,7 +184,6 @@ impl<'a> SemanticBuilder<'a> { /// `with_trivias` must be called prior to this call. #[must_use] pub fn with_build_jsdoc(mut self, yes: bool) -> Self { - self.jsdoc = JSDocBuilder::new(self.source_text, &self.trivias); self.build_jsdoc = yes; self } @@ -252,7 +257,11 @@ impl<'a> SemanticBuilder<'a> { /// /// # Panics pub fn build(mut self, program: &Program<'a>) -> SemanticBuilderReturn<'a> { + self.source_text = program.source_text; self.source_type = program.source_type; + if self.build_jsdoc { + self.jsdoc = JSDocBuilder::new(self.source_text, &self.trivias); + } if self.source_type.is_typescript_definition() { let scope_id = self.scope.add_scope(None, NodeId::DUMMY, ScopeFlags::Top); program.scope_id.set(Some(scope_id)); diff --git a/crates/oxc_semantic/src/jsdoc/builder.rs b/crates/oxc_semantic/src/jsdoc/builder.rs index 62a8fe2872364..9ae9fb384c108 100644 --- a/crates/oxc_semantic/src/jsdoc/builder.rs +++ b/crates/oxc_semantic/src/jsdoc/builder.rs @@ -7,6 +7,7 @@ use crate::jsdoc::JSDocFinder; use super::parser::JSDoc; +#[derive(Default)] pub struct JSDocBuilder<'a> { not_attached_docs: FxHashMap>>, attached_docs: FxHashMap>>, @@ -207,7 +208,7 @@ mod test { let source_type = source_type.unwrap_or_default(); let ret = Parser::new(allocator, source_text, source_type).parse(); let program = allocator.alloc(ret.program); - let semantic = SemanticBuilder::new(source_text) + let semantic = SemanticBuilder::new() .with_trivias(ret.trivias) .with_build_jsdoc(true) .build(program) diff --git a/crates/oxc_semantic/src/jsdoc/parser/jsdoc.rs b/crates/oxc_semantic/src/jsdoc/parser/jsdoc.rs index 8a892968008e1..ddd8d3c757e55 100644 --- a/crates/oxc_semantic/src/jsdoc/parser/jsdoc.rs +++ b/crates/oxc_semantic/src/jsdoc/parser/jsdoc.rs @@ -46,7 +46,7 @@ mod test { let source_type = SourceType::default(); let ret = Parser::new(allocator, source_text, source_type).parse(); let program = allocator.alloc(ret.program); - let semantic = SemanticBuilder::new(source_text) + let semantic = SemanticBuilder::new() .with_trivias(ret.trivias) .with_build_jsdoc(true) .build(program) diff --git a/crates/oxc_semantic/src/jsdoc/parser/jsdoc_tag.rs b/crates/oxc_semantic/src/jsdoc/parser/jsdoc_tag.rs index fbee06e55273b..de79328e0a4ca 100644 --- a/crates/oxc_semantic/src/jsdoc/parser/jsdoc_tag.rs +++ b/crates/oxc_semantic/src/jsdoc/parser/jsdoc_tag.rs @@ -195,7 +195,7 @@ mod test { let source_type = SourceType::default(); let ret = Parser::new(allocator, source_text, source_type).parse(); let program = allocator.alloc(ret.program); - let semantic = SemanticBuilder::new(source_text) + let semantic = SemanticBuilder::new() .with_trivias(ret.trivias) .with_build_jsdoc(true) .build(program) diff --git a/crates/oxc_semantic/src/lib.rs b/crates/oxc_semantic/src/lib.rs index f863183136fe6..65db5557fb02b 100644 --- a/crates/oxc_semantic/src/lib.rs +++ b/crates/oxc_semantic/src/lib.rs @@ -230,7 +230,7 @@ mod tests { let parse = oxc_parser::Parser::new(allocator, source, source_type).parse(); assert!(parse.errors.is_empty()); let program = allocator.alloc(parse.program); - let semantic = SemanticBuilder::new(source).build(program); + let semantic = SemanticBuilder::new().build(program); assert!(semantic.errors.is_empty(), "Parse error: {}", semantic.errors[0]); semantic.semantic } diff --git a/crates/oxc_semantic/src/module_record/mod.rs b/crates/oxc_semantic/src/module_record/mod.rs index 28a67df331d93..879aaeae2856a 100644 --- a/crates/oxc_semantic/src/module_record/mod.rs +++ b/crates/oxc_semantic/src/module_record/mod.rs @@ -19,9 +19,8 @@ mod module_record_tests { let allocator = Allocator::default(); let ret = Parser::new(&allocator, source_text, source_type).parse(); let program = allocator.alloc(ret.program); - let semantic_ret = SemanticBuilder::new(source_text) - .build_module_record(Path::new(""), program) - .build(program); + let semantic_ret = + SemanticBuilder::new().build_module_record(Path::new(""), program).build(program); Arc::clone(&semantic_ret.semantic.module_record) } diff --git a/crates/oxc_semantic/tests/integration/util/mod.rs b/crates/oxc_semantic/tests/integration/util/mod.rs index 1ba05e62bf243..505739d46f3f2 100644 --- a/crates/oxc_semantic/tests/integration/util/mod.rs +++ b/crates/oxc_semantic/tests/integration/util/mod.rs @@ -167,7 +167,7 @@ impl<'a> SemanticTester<'a> { ); let program = self.allocator.alloc(parse.program); - SemanticBuilder::new(self.source_text) + SemanticBuilder::new() .with_check_syntax_error(true) .with_trivias(parse.trivias) .with_cfg(self.cfg) diff --git a/crates/oxc_semantic/tests/main.rs b/crates/oxc_semantic/tests/main.rs index 000d394a08d59..ad755bfb85500 100644 --- a/crates/oxc_semantic/tests/main.rs +++ b/crates/oxc_semantic/tests/main.rs @@ -123,10 +123,8 @@ fn analyze(path: &Path, source_text: &str, conformance_suite: &SemanticConforman let source_type = SourceType::from_path(path).unwrap(); let ret = Parser::new(&allocator, source_text, source_type).parse(); - let semantic = SemanticBuilder::new(source_text) - .with_check_syntax_error(true) - .build(&ret.program) - .semantic; + let semantic = + SemanticBuilder::new().with_check_syntax_error(true).build(&ret.program).semantic; let ctx = TestContext { path, semantic }; diff --git a/crates/oxc_transformer/examples/transformer.rs b/crates/oxc_transformer/examples/transformer.rs index 410d0feb41ba4..a99222b468304 100644 --- a/crates/oxc_transformer/examples/transformer.rs +++ b/crates/oxc_transformer/examples/transformer.rs @@ -40,7 +40,7 @@ fn main() { let mut program = ret.program; let trivias = ret.trivias; - let ret = SemanticBuilder::new(&source_text) + let ret = SemanticBuilder::new() // Estimate transformer will triple scopes, symbols, references .with_excess_capacity(2.0) .build(&program); @@ -65,7 +65,7 @@ fn main() { TransformOptions::enable_all() }; - let ret = Transformer::new(&allocator, path, &source_text, trivias.clone(), transform_options) + let ret = Transformer::new(&allocator, path, trivias.clone(), transform_options) .build_with_symbols_and_scopes(symbols, scopes, &mut program); if !ret.errors.is_empty() { diff --git a/crates/oxc_transformer/src/context.rs b/crates/oxc_transformer/src/context.rs index 3f0b166a011b6..aba1ec3c57b13 100644 --- a/crates/oxc_transformer/src/context.rs +++ b/crates/oxc_transformer/src/context.rs @@ -41,12 +41,7 @@ pub struct TransformCtx<'a> { } impl<'a> TransformCtx<'a> { - pub fn new( - source_path: &Path, - source_text: &'a str, - trivias: Trivias, - options: &TransformOptions, - ) -> Self { + pub fn new(source_path: &Path, trivias: Trivias, options: &TransformOptions) -> Self { let filename = source_path .file_stem() // omit file extension .map_or_else(|| String::from("unknown"), |name| name.to_string_lossy().to_string()); @@ -60,7 +55,7 @@ impl<'a> TransformCtx<'a> { filename, source_path, source_type: SourceType::default(), - source_text, + source_text: "", trivias, module_imports: ModuleImportsStore::new(), var_declarations: VarDeclarationsStore::new(), diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index a9f502ebb47af..ea92aa0d89628 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -72,11 +72,10 @@ impl<'a> Transformer<'a> { pub fn new( allocator: &'a Allocator, source_path: &Path, - source_text: &'a str, trivias: Trivias, options: TransformOptions, ) -> Self { - let ctx = TransformCtx::new(source_path, source_text, trivias, &options); + let ctx = TransformCtx::new(source_path, trivias, &options); Self { ctx, options, allocator } } @@ -90,6 +89,7 @@ impl<'a> Transformer<'a> { let ast_builder = AstBuilder::new(allocator); self.ctx.source_type = program.source_type; + self.ctx.source_text = program.source_text; react::update_options_with_comments(&mut self.options, &self.ctx); let mut transformer = TransformerImpl { diff --git a/crates/oxc_transformer/tests/plugins/inject_global_variables.rs b/crates/oxc_transformer/tests/plugins/inject_global_variables.rs index b34ef5aea1482..015664ecc0149 100644 --- a/crates/oxc_transformer/tests/plugins/inject_global_variables.rs +++ b/crates/oxc_transformer/tests/plugins/inject_global_variables.rs @@ -16,10 +16,8 @@ pub(crate) fn test(source_text: &str, expected: &str, config: InjectGlobalVariab let allocator = Allocator::default(); let ret = Parser::new(&allocator, source_text, source_type).parse(); let program = allocator.alloc(ret.program); - let (symbols, scopes) = SemanticBuilder::new(source_text) - .build(program) - .semantic - .into_symbol_table_and_scope_tree(); + let (symbols, scopes) = + SemanticBuilder::new().build(program).semantic.into_symbol_table_and_scope_tree(); let _ = InjectGlobalVariables::new(&allocator, config).build(symbols, scopes, program); let result = CodeGenerator::new() .with_options(CodegenOptions { single_quote: true, ..CodegenOptions::default() }) diff --git a/crates/oxc_transformer/tests/plugins/replace_global_defines.rs b/crates/oxc_transformer/tests/plugins/replace_global_defines.rs index 9fa7aec2293df..b5e15b333e4ac 100644 --- a/crates/oxc_transformer/tests/plugins/replace_global_defines.rs +++ b/crates/oxc_transformer/tests/plugins/replace_global_defines.rs @@ -12,10 +12,8 @@ pub(crate) fn test(source_text: &str, expected: &str, config: ReplaceGlobalDefin let allocator = Allocator::default(); let ret = Parser::new(&allocator, source_text, source_type).parse(); let program = allocator.alloc(ret.program); - let (symbols, scopes) = SemanticBuilder::new(source_text) - .build(program) - .semantic - .into_symbol_table_and_scope_tree(); + let (symbols, scopes) = + SemanticBuilder::new().build(program).semantic.into_symbol_table_and_scope_tree(); let _ = ReplaceGlobalDefines::new(&allocator, config).build(symbols, scopes, program); let result = CodeGenerator::new() .with_options(CodegenOptions { single_quote: true, ..CodegenOptions::default() }) diff --git a/crates/oxc_traverse/src/generated/ancestor.rs b/crates/oxc_traverse/src/generated/ancestor.rs index 0dbe693eb8e5e..44327c49900b6 100644 --- a/crates/oxc_traverse/src/generated/ancestor.rs +++ b/crates/oxc_traverse/src/generated/ancestor.rs @@ -2201,6 +2201,7 @@ impl<'a, 't> Ancestor<'a, 't> { pub(crate) const OFFSET_PROGRAM_SPAN: usize = offset_of!(Program, span); pub(crate) const OFFSET_PROGRAM_SOURCE_TYPE: usize = offset_of!(Program, source_type); +pub(crate) const OFFSET_PROGRAM_SOURCE_TEXT: usize = offset_of!(Program, source_text); pub(crate) const OFFSET_PROGRAM_HASHBANG: usize = offset_of!(Program, hashbang); pub(crate) const OFFSET_PROGRAM_DIRECTIVES: usize = offset_of!(Program, directives); pub(crate) const OFFSET_PROGRAM_BODY: usize = offset_of!(Program, body); @@ -2224,6 +2225,11 @@ impl<'a, 't> ProgramWithoutHashbang<'a, 't> { unsafe { &*((self.0 as *const u8).add(OFFSET_PROGRAM_SOURCE_TYPE) as *const SourceType) } } + #[inline] + pub fn source_text(self) -> &'t &'a str { + unsafe { &*((self.0 as *const u8).add(OFFSET_PROGRAM_SOURCE_TEXT) as *const &'a str) } + } + #[inline] pub fn directives(self) -> &'t Vec<'a, Directive<'a>> { unsafe { @@ -2265,6 +2271,11 @@ impl<'a, 't> ProgramWithoutDirectives<'a, 't> { unsafe { &*((self.0 as *const u8).add(OFFSET_PROGRAM_SOURCE_TYPE) as *const SourceType) } } + #[inline] + pub fn source_text(self) -> &'t &'a str { + unsafe { &*((self.0 as *const u8).add(OFFSET_PROGRAM_SOURCE_TEXT) as *const &'a str) } + } + #[inline] pub fn hashbang(self) -> &'t Option> { unsafe { @@ -2305,6 +2316,11 @@ impl<'a, 't> ProgramWithoutBody<'a, 't> { unsafe { &*((self.0 as *const u8).add(OFFSET_PROGRAM_SOURCE_TYPE) as *const SourceType) } } + #[inline] + pub fn source_text(self) -> &'t &'a str { + unsafe { &*((self.0 as *const u8).add(OFFSET_PROGRAM_SOURCE_TEXT) as *const &'a str) } + } + #[inline] pub fn hashbang(self) -> &'t Option> { unsafe { diff --git a/crates/oxc_wasm/src/lib.rs b/crates/oxc_wasm/src/lib.rs index ef245fd626502..65ebccc4c63f7 100644 --- a/crates/oxc_wasm/src/lib.rs +++ b/crates/oxc_wasm/src/lib.rs @@ -197,7 +197,7 @@ impl Oxc { self.ir = format!("{:#?}", program.body); self.ast = program.serialize(&self.serializer)?; - let mut semantic_builder = SemanticBuilder::new(source_text); + let mut semantic_builder = SemanticBuilder::new(); if run_options.transform.unwrap_or_default() { // Estimate transformer will triple scopes, symbols, references semantic_builder = semantic_builder.with_excess_capacity(2.0); @@ -221,7 +221,7 @@ impl Oxc { ); } - self.run_linter(&run_options, source_text, &path, &trivias, &program); + self.run_linter(&run_options, &path, &trivias, &program); self.run_prettier(&run_options, source_text, source_type); @@ -241,9 +241,8 @@ impl Oxc { targets: Targets::from_query("chrome 51"), ..EnvOptions::default() }) { - let result = - Transformer::new(&allocator, &path, source_text, trivias.clone(), options) - .build_with_symbols_and_scopes(symbols, scopes, &mut program); + let result = Transformer::new(&allocator, &path, trivias.clone(), options) + .build_with_symbols_and_scopes(symbols, scopes, &mut program); if !result.errors.is_empty() { self.save_diagnostics( result.errors.into_iter().map(Error::from).collect::>(), @@ -293,14 +292,13 @@ impl Oxc { fn run_linter( &mut self, run_options: &OxcRunOptions, - source_text: &str, path: &Path, trivias: &Trivias, program: &Program, ) { // Only lint if there are no syntax errors if run_options.lint.unwrap_or_default() && self.diagnostics.borrow().is_empty() { - let semantic_ret = SemanticBuilder::new(source_text) + let semantic_ret = SemanticBuilder::new() .with_cfg(true) .with_trivias(trivias.clone()) .build_module_record(path, program) diff --git a/tasks/benchmark/benches/linter.rs b/tasks/benchmark/benches/linter.rs index 19089074e7aa1..d3f25a9b97e6c 100644 --- a/tasks/benchmark/benches/linter.rs +++ b/tasks/benchmark/benches/linter.rs @@ -28,7 +28,7 @@ fn bench_linter(criterion: &mut Criterion) { let allocator = Allocator::default(); let ret = Parser::new(&allocator, source_text, source_type).parse(); let program = allocator.alloc(ret.program); - let semantic_ret = SemanticBuilder::new(source_text) + let semantic_ret = SemanticBuilder::new() .with_trivias(ret.trivias) .with_build_jsdoc(true) .with_cfg(true) diff --git a/tasks/benchmark/benches/minifier.rs b/tasks/benchmark/benches/minifier.rs index 2d600f85626ec..cffac57cf136b 100644 --- a/tasks/benchmark/benches/minifier.rs +++ b/tasks/benchmark/benches/minifier.rs @@ -26,7 +26,7 @@ fn bench_minifier(criterion: &mut Criterion) { // Create fresh AST + semantic data for each iteration let program = Parser::new(&allocator, source_text, source_type).parse().program; let program = allocator.alloc(program); - let (symbols, scopes) = SemanticBuilder::new(source_text) + let (symbols, scopes) = SemanticBuilder::new() .build(program) .semantic .into_symbol_table_and_scope_tree(); diff --git a/tasks/benchmark/benches/semantic.rs b/tasks/benchmark/benches/semantic.rs index ca7a58d601632..bc8823e1d8556 100644 --- a/tasks/benchmark/benches/semantic.rs +++ b/tasks/benchmark/benches/semantic.rs @@ -23,7 +23,7 @@ fn bench_semantic(criterion: &mut Criterion) { // We return `error`s to be dropped outside of the measured section, as usually // code would have no errors. One of our benchmarks `cal.com.tsx` has a lot of errors, // but that's atypical, so don't want to include it in benchmark time. - let ret = SemanticBuilder::new(source_text) + let ret = SemanticBuilder::new() .with_trivias(ret.trivias.clone()) .with_build_jsdoc(true) .build_module_record(Path::new(""), program) diff --git a/tasks/benchmark/benches/transformer.rs b/tasks/benchmark/benches/transformer.rs index ea764b3f55719..b83698f667c0c 100644 --- a/tasks/benchmark/benches/transformer.rs +++ b/tasks/benchmark/benches/transformer.rs @@ -29,7 +29,7 @@ fn bench_transformer(criterion: &mut Criterion) { let ParserReturn { trivias, program, .. } = Parser::new(&allocator, source_text, source_type).parse(); let program = allocator.alloc(program); - let (symbols, scopes) = SemanticBuilder::new(source_text) + let (symbols, scopes) = SemanticBuilder::new() // Estimate transformer will triple scopes, symbols, references .with_excess_capacity(2.0) .build(program) @@ -47,14 +47,9 @@ fn bench_transformer(criterion: &mut Criterion) { options.es2015.arrow_function = Some(ArrowFunctionsOptions { spec: true }); runner.run(|| { - let ret = Transformer::new( - &allocator, - Path::new(&file.file_name), - source_text, - trivias, - options, - ) - .build_with_symbols_and_scopes(symbols, scopes, program); + let ret = + Transformer::new(&allocator, Path::new(&file.file_name), trivias, options) + .build_with_symbols_and_scopes(symbols, scopes, program); // Return the `TransformerReturn`, so it's dropped outside of the measured section. // `TransformerReturn` contains `ScopeTree` and `SymbolTable` which are costly to drop. diff --git a/tasks/transform_checker/src/lib.rs b/tasks/transform_checker/src/lib.rs index 3d29bf3f83abc..329235e295a91 100644 --- a/tasks/transform_checker/src/lib.rs +++ b/tasks/transform_checker/src/lib.rs @@ -137,7 +137,7 @@ pub fn check_semantic_after_transform( // so the cloned AST will be "clean" of all semantic data, as if it had come fresh from the parser. let allocator = Allocator::default(); let program = program.clone_in(&allocator); - let (symbols_rebuilt, scopes_rebuilt) = SemanticBuilder::new("") + let (symbols_rebuilt, scopes_rebuilt) = SemanticBuilder::new() .with_scope_tree_child_ids(scopes_after_transform.has_child_ids()) .build(&program) .semantic diff --git a/tasks/transform_conformance/src/driver.rs b/tasks/transform_conformance/src/driver.rs index 701caca8f1a55..56778147f253d 100644 --- a/tasks/transform_conformance/src/driver.rs +++ b/tasks/transform_conformance/src/driver.rs @@ -57,10 +57,9 @@ impl CompilerInterface for Driver { } // Disable comments - fn codegen<'a>( + fn codegen( &self, - program: &Program<'a>, - _source_text: &'a str, + program: &Program<'_>, _source_path: &Path, _trivias: &Trivias, mangler: Option,