Skip to content

Commit 6aa2dde

Browse files
authored
refactor(codegen): accept SymbolTable instead of Mangler (#8829)
It seems to be that the easiest way to rename variables is to use rename_symbol from SymbolTable and to let it be applied during codegen - this is what Mangler does. This PR changes Codegen to accept (any) SymbolTable instead of just Mangler itself, and for Mangler build output to be a new SymbolTable, after consuming itself (which makes it clear at the type level if a Mangler has been built or not). This also moves the few symbol table helper methods from Mangler to SymbolTable itself, and adds an example of Mangler use to it's documentation. Codegen dependencies now include semantic (which was transient by mangler before). It's dependency on Mangler could be removed, though I've left it to allow easy linking of docs (with_symbol_table points users to Mangler, hopefully helps with migration?)
1 parent 7e8568b commit 6aa2dde

File tree

14 files changed

+85
-52
lines changed

14 files changed

+85
-52
lines changed

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/oxc/src/compiler.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -286,21 +286,21 @@ pub trait CompilerInterface {
286286
Compressor::new(allocator, options).build(program);
287287
}
288288

289-
fn mangle(&self, program: &mut Program<'_>, options: MangleOptions) -> Mangler {
289+
fn mangle(&self, program: &mut Program<'_>, options: MangleOptions) -> SymbolTable {
290290
Mangler::new().with_options(options).build(program)
291291
}
292292

293293
fn codegen(
294294
&self,
295295
program: &Program<'_>,
296296
source_path: &Path,
297-
mangler: Option<Mangler>,
297+
symbol_table: Option<SymbolTable>,
298298
options: CodegenOptions,
299299
) -> CodegenReturn {
300300
let mut options = options;
301301
if self.enable_sourcemap() {
302302
options.source_map_path = Some(source_path.to_path_buf());
303303
}
304-
CodeGenerator::new().with_options(options).with_mangler(mangler).build(program)
304+
CodeGenerator::new().with_options(options).with_symbol_table(symbol_table).build(program)
305305
}
306306
}

crates/oxc_codegen/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ oxc_allocator = { workspace = true }
2424
oxc_ast = { workspace = true }
2525
oxc_data_structures = { workspace = true }
2626
oxc_index = { workspace = true }
27-
oxc_mangler = { workspace = true }
27+
oxc_semantic = { workspace = true }
2828
oxc_sourcemap = { workspace = true }
2929
oxc_span = { workspace = true }
3030
oxc_syntax = { workspace = true }

crates/oxc_codegen/src/lib.rs

+12-10
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use oxc_ast::ast::{
1919
BindingIdentifier, BlockStatement, Comment, Expression, IdentifierReference, Program, Statement,
2020
};
2121
use oxc_data_structures::stack::Stack;
22-
use oxc_mangler::Mangler;
22+
use oxc_semantic::SymbolTable;
2323
use oxc_span::{GetSpan, Span, SPAN};
2424
use oxc_syntax::{
2525
identifier::{is_identifier_part, is_identifier_part_ascii, LS, PS},
@@ -79,7 +79,7 @@ pub struct Codegen<'a> {
7979
/// Original source code of the AST
8080
source_text: &'a str,
8181

82-
mangler: Option<Mangler>,
82+
symbol_table: Option<SymbolTable>,
8383

8484
/// Output Code
8585
code: CodeBuffer,
@@ -162,7 +162,7 @@ impl<'a> Codegen<'a> {
162162
Self {
163163
options,
164164
source_text: "",
165-
mangler: None,
165+
symbol_table: None,
166166
code: CodeBuffer::default(),
167167
needs_semicolon: false,
168168
need_space_before_dot: 0,
@@ -194,10 +194,12 @@ impl<'a> Codegen<'a> {
194194
self
195195
}
196196

197-
/// Set the mangler for mangling identifiers.
197+
/// Set the symbol table used for identifier renaming.
198+
///
199+
/// Can be used for easy renaming of variables (based on semantic analysis).
198200
#[must_use]
199-
pub fn with_mangler(mut self, mangler: Option<Mangler>) -> Self {
200-
self.mangler = mangler;
201+
pub fn with_symbol_table(mut self, symbol_table: Option<SymbolTable>) -> Self {
202+
self.symbol_table = symbol_table;
201203
self
202204
}
203205

@@ -516,9 +518,9 @@ impl<'a> Codegen<'a> {
516518
}
517519

518520
fn get_identifier_reference_name(&self, reference: &IdentifierReference<'a>) -> &'a str {
519-
if let Some(mangler) = &self.mangler {
521+
if let Some(symbol_table) = &self.symbol_table {
520522
if let Some(reference_id) = reference.reference_id.get() {
521-
if let Some(name) = mangler.get_reference_name(reference_id) {
523+
if let Some(name) = symbol_table.get_reference_name(reference_id) {
522524
// SAFETY: Hack the lifetime to be part of the allocator.
523525
return unsafe { std::mem::transmute_copy(&name) };
524526
}
@@ -528,9 +530,9 @@ impl<'a> Codegen<'a> {
528530
}
529531

530532
fn get_binding_identifier_name(&self, ident: &BindingIdentifier<'a>) -> &'a str {
531-
if let Some(mangler) = &self.mangler {
533+
if let Some(symbol_table) = &self.symbol_table {
532534
if let Some(symbol_id) = ident.symbol_id.get() {
533-
let name = mangler.get_symbol_name(symbol_id);
535+
let name = symbol_table.get_name(symbol_id);
534536
// SAFETY: Hack the lifetime to be part of the allocator.
535537
return unsafe { std::mem::transmute_copy(&name) };
536538
}

crates/oxc_mangler/src/lib.rs

+40-19
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,50 @@ use rustc_hash::FxHashSet;
77
use oxc_allocator::{Allocator, Vec};
88
use oxc_ast::ast::{Declaration, Program, Statement};
99
use oxc_index::Idx;
10-
use oxc_semantic::{ReferenceId, ScopeTree, Semantic, SemanticBuilder, SymbolId, SymbolTable};
10+
use oxc_semantic::{ScopeTree, Semantic, SemanticBuilder, SymbolId, SymbolTable};
1111
use oxc_span::Atom;
1212

1313
#[derive(Default, Debug, Clone, Copy)]
1414
pub struct MangleOptions {
15+
/// Also mangle exported variables.
1516
pub top_level: bool,
17+
/// Use more readable mangled names
18+
/// (e.g. `slot_0`, `slot_1`, `slot_2`, ...) for debugging.
19+
///
20+
/// Uses base54 if false.
1621
pub debug: bool,
1722
}
1823

1924
type Slot = usize;
2025

2126
/// # Name Mangler / Symbol Minification
2227
///
28+
/// ## Example
29+
///
30+
/// ```rust
31+
/// use oxc_codegen::{Codegen, CodegenOptions};
32+
/// use oxc_ast::ast::Program;
33+
/// use oxc_parser::Parser;
34+
/// use oxc_allocator::Allocator;
35+
/// use oxc_span::SourceType;
36+
/// use oxc_mangler::{MangleOptions, Mangler};
37+
///
38+
/// let allocator = Allocator::default();
39+
/// let source = "const result = 1 + 2;";
40+
/// let parsed = Parser::new(&allocator, source, SourceType::mjs()).parse();
41+
/// assert!(parsed.errors.is_empty());
42+
///
43+
/// let mangled_symbols = Mangler::new()
44+
/// .with_options(MangleOptions { top_level: true, debug: true })
45+
/// .build(&parsed.program);
46+
///
47+
/// let js = Codegen::new().with_symbol_table(mangled_symbols).build(&parsed.program);
48+
/// // this will be `const a = 1 + 2;` if debug = false
49+
/// assert_eq!(js.code, "const slot_0 = 1 + 2;\n");
50+
/// ```
51+
///
52+
/// ## Implementation
53+
///
2354
/// See:
2455
/// * [esbuild](https://github.com/evanw/esbuild/blob/v0.24.0/docs/architecture.md#symbol-minification)
2556
///
@@ -61,7 +92,7 @@ type Slot = usize;
6192
/// }
6293
/// ```
6394
///
64-
/// ## Name Reuse Calculation
95+
/// ### Name Reuse Calculation
6596
///
6697
/// This improvement was inspired by [evanw/esbuild#2614](https://github.com/evanw/esbuild/pull/2614).
6798
///
@@ -112,8 +143,6 @@ type Slot = usize;
112143
/// - slot 3: `bar`
113144
#[derive(Default)]
114145
pub struct Mangler {
115-
symbol_table: SymbolTable,
116-
117146
options: MangleOptions,
118147
}
119148

@@ -129,17 +158,10 @@ impl Mangler {
129158
self
130159
}
131160

132-
pub fn get_symbol_name(&self, symbol_id: SymbolId) -> &str {
133-
self.symbol_table.get_name(symbol_id)
134-
}
135-
136-
pub fn get_reference_name(&self, reference_id: ReferenceId) -> Option<&str> {
137-
let symbol_id = self.symbol_table.get_reference(reference_id).symbol_id()?;
138-
Some(self.symbol_table.get_name(symbol_id))
139-
}
140-
161+
/// Mangles the program. The resulting SymbolTable contains the mangled symbols - `program` is not modified.
162+
/// Pass the symbol table to oxc_codegen to generate the mangled code.
141163
#[must_use]
142-
pub fn build(self, program: &Program<'_>) -> Mangler {
164+
pub fn build(self, program: &Program<'_>) -> SymbolTable {
143165
let semantic =
144166
SemanticBuilder::new().with_scope_tree_child_ids(true).build(program).semantic;
145167
self.build_with_semantic(semantic, program)
@@ -149,7 +171,7 @@ impl Mangler {
149171
///
150172
/// Panics if the child_ids does not exist in scope_tree.
151173
#[must_use]
152-
pub fn build_with_semantic(self, semantic: Semantic<'_>, program: &Program<'_>) -> Mangler {
174+
pub fn build_with_semantic(self, semantic: Semantic<'_>, program: &Program<'_>) -> SymbolTable {
153175
if self.options.debug {
154176
self.build_with_symbols_and_scopes_impl(semantic, program, debug_name)
155177
} else {
@@ -161,11 +183,11 @@ impl Mangler {
161183
const CAPACITY: usize,
162184
G: Fn(usize) -> InlineString<CAPACITY>,
163185
>(
164-
mut self,
186+
self,
165187
semantic: Semantic<'_>,
166188
program: &Program<'_>,
167189
generate_name: G,
168-
) -> Mangler {
190+
) -> SymbolTable {
169191
let (mut symbol_table, scope_tree, ast_nodes) = semantic.into_symbols_scopes_nodes();
170192

171193
assert!(scope_tree.has_child_ids(), "child_id needs to be generated");
@@ -331,8 +353,7 @@ impl Mangler {
331353
}
332354
}
333355

334-
self.symbol_table = symbol_table;
335-
self
356+
symbol_table
336357
}
337358

338359
fn tally_slot_frequencies<'a>(

crates/oxc_minifier/examples/mangler.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ fn main() -> std::io::Result<()> {
3838
fn mangler(source_text: &str, source_type: SourceType, debug: bool) -> String {
3939
let allocator = Allocator::default();
4040
let ret = Parser::new(&allocator, source_text, source_type).parse();
41-
let mangler = Mangler::new()
41+
let symbol_table = Mangler::new()
4242
.with_options(MangleOptions { debug, top_level: source_type.is_module() })
4343
.build(&ret.program);
44-
CodeGenerator::new().with_mangler(Some(mangler)).build(&ret.program).code
44+
CodeGenerator::new().with_symbol_table(Some(symbol_table)).build(&ret.program).code
4545
}

crates/oxc_minifier/examples/minifier.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ fn minify(
5555
let ret = Minifier::new(options).build(allocator, &mut program);
5656
CodeGenerator::new()
5757
.with_options(CodegenOptions { minify: nospace, ..CodegenOptions::default() })
58-
.with_mangler(ret.mangler)
58+
.with_symbol_table(ret.symbol_table)
5959
.build(&program)
6060
.code
6161
}

crates/oxc_minifier/src/lib.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ mod tester;
1212
use oxc_allocator::Allocator;
1313
use oxc_ast::ast::Program;
1414
use oxc_mangler::Mangler;
15-
use oxc_semantic::{SemanticBuilder, Stats};
15+
use oxc_semantic::{SemanticBuilder, Stats, SymbolTable};
1616

1717
pub use oxc_mangler::MangleOptions;
1818

@@ -31,7 +31,7 @@ impl Default for MinifierOptions {
3131
}
3232

3333
pub struct MinifierReturn {
34-
pub mangler: Option<Mangler>,
34+
pub symbol_table: Option<SymbolTable>,
3535
}
3636

3737
pub struct Minifier {
@@ -54,14 +54,14 @@ impl Minifier {
5454
} else {
5555
Stats::default()
5656
};
57-
let mangler = self.options.mangle.map(|options| {
57+
let symbol_table = self.options.mangle.map(|options| {
5858
let semantic = SemanticBuilder::new()
5959
.with_stats(stats)
6060
.with_scope_tree_child_ids(true)
6161
.build(program)
6262
.semantic;
6363
Mangler::default().with_options(options).build_with_semantic(semantic, program)
6464
});
65-
MinifierReturn { mangler }
65+
MinifierReturn { symbol_table }
6666
}
6767
}

crates/oxc_minifier/tests/mangler/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ fn mangle(source_text: &str, top_level: bool) -> String {
1111
let source_type = SourceType::mjs();
1212
let ret = Parser::new(&allocator, source_text, source_type).parse();
1313
let program = ret.program;
14-
let mangler =
14+
let symbol_table =
1515
Mangler::new().with_options(MangleOptions { debug: false, top_level }).build(&program);
16-
CodeGenerator::new().with_mangler(Some(mangler)).build(&program).code
16+
CodeGenerator::new().with_symbol_table(Some(symbol_table)).build(&program).code
1717
}
1818

1919
#[test]

crates/oxc_semantic/src/symbol.rs

+7
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,13 @@ impl SymbolTable {
250250
&mut self.references[reference_id]
251251
}
252252

253+
/// Get the name of the symbol a reference is resolved to. Returns `None` if the reference is
254+
/// not resolved.
255+
#[inline]
256+
pub fn get_reference_name(&self, reference_id: ReferenceId) -> Option<&str> {
257+
self.get_name(self.references[reference_id].symbol_id()?).into()
258+
}
259+
253260
/// Returns `true` if the corresponding [`Reference`] is resolved to a symbol.
254261
///
255262
/// When `false`, this could either be a reference to a global value or an identifier that does

crates/oxc_wasm/src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ impl Oxc {
265265
}
266266
}
267267

268-
let mangler = if minifier_options.compress.unwrap_or_default()
268+
let symbol_table = if minifier_options.compress.unwrap_or_default()
269269
|| minifier_options.mangle.unwrap_or_default()
270270
{
271271
let compress_options = minifier_options.compress_options.unwrap_or_default();
@@ -281,13 +281,13 @@ impl Oxc {
281281
CompressOptions::all_false()
282282
}),
283283
};
284-
Minifier::new(options).build(&allocator, &mut program).mangler
284+
Minifier::new(options).build(&allocator, &mut program).symbol_table
285285
} else {
286286
None
287287
};
288288

289289
self.codegen_text = CodeGenerator::new()
290-
.with_mangler(mangler)
290+
.with_symbol_table(symbol_table)
291291
.with_options(CodegenOptions {
292292
minify: minifier_options.whitespace.unwrap_or_default(),
293293
..CodegenOptions::default()

napi/minify/src/lib.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ pub fn minify(
3939

4040
let mut program = Parser::new(&allocator, &source_text, source_type).parse().program;
4141

42-
let mangler = Minifier::new(minifier_options).build(&allocator, &mut program).mangler;
42+
let symbol_table = Minifier::new(minifier_options).build(&allocator, &mut program).symbol_table;
4343

4444
let mut codegen_options = match &options.codegen {
4545
Some(Either::A(false)) => CodegenOptions { minify: false, ..CodegenOptions::default() },
@@ -53,7 +53,10 @@ pub fn minify(
5353
codegen_options.source_map_path = Some(PathBuf::from(filename));
5454
}
5555

56-
let ret = Codegen::new().with_options(codegen_options).with_mangler(mangler).build(&program);
56+
let ret = Codegen::new()
57+
.with_options(codegen_options)
58+
.with_symbol_table(symbol_table)
59+
.build(&program);
5760

5861
Ok(MinifyResult { code: ret.code, map: ret.map.map(oxc_sourcemap::napi::SourceMap::from) })
5962
}

tasks/coverage/src/runtime/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -208,17 +208,17 @@ impl Test262RuntimeCase {
208208
);
209209
}
210210

211-
let mangler = if minify {
211+
let symbol_table = if minify {
212212
Minifier::new(MinifierOptions { mangle: None, ..MinifierOptions::default() })
213213
.build(&allocator, &mut program)
214-
.mangler
214+
.symbol_table
215215
} else {
216216
None
217217
};
218218

219219
let mut text = CodeGenerator::new()
220220
.with_options(CodegenOptions { minify, ..CodegenOptions::default() })
221-
.with_mangler(mangler)
221+
.with_symbol_table(symbol_table)
222222
.build(&program)
223223
.code;
224224
if is_only_strict {

tasks/minsize/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ fn minify(source_text: &str, source_type: SourceType) -> String {
146146
let ret = Minifier::new(MinifierOptions::default()).build(&allocator, &mut program);
147147
CodeGenerator::new()
148148
.with_options(CodegenOptions { minify: true, comments: false, ..CodegenOptions::default() })
149-
.with_mangler(ret.mangler)
149+
.with_symbol_table(ret.symbol_table)
150150
.build(&program)
151151
.code
152152
}

0 commit comments

Comments
 (0)