Skip to content

Commit

Permalink
refactor(codegen): accept SymbolTable instead of Mangler
Browse files Browse the repository at this point in the history
  • Loading branch information
danbulant committed Feb 1, 2025
1 parent 30eec26 commit 61f422b
Show file tree
Hide file tree
Showing 15 changed files with 87 additions and 50 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

6 changes: 3 additions & 3 deletions crates/oxc/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,21 +286,21 @@ pub trait CompilerInterface {
Compressor::new(allocator, options).build(program);
}

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

fn codegen(
&self,
program: &Program<'_>,
source_path: &Path,
mangler: Option<Mangler>,
symbol_table: Option<SymbolTable>,
options: CodegenOptions,
) -> CodegenReturn {
let mut options = options;
if self.enable_sourcemap() {
options.source_map_path = Some(source_path.to_path_buf());
}
CodeGenerator::new().with_options(options).with_mangler(mangler).build(program)
CodeGenerator::new().with_options(options).with_symbol_table(symbol_table).build(program)
}
}
1 change: 1 addition & 0 deletions crates/oxc_codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ oxc_mangler = { workspace = true }
oxc_sourcemap = { workspace = true }
oxc_span = { workspace = true }
oxc_syntax = { workspace = true }
oxc_semantic = { workspace = true }

assert-unchecked = { workspace = true }
bitflags = { workspace = true }
Expand Down
23 changes: 13 additions & 10 deletions crates/oxc_codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use oxc_ast::ast::{
BindingIdentifier, BlockStatement, Comment, Expression, IdentifierReference, Program, Statement,
};
use oxc_data_structures::stack::Stack;
use oxc_mangler::Mangler;
use oxc_semantic::SymbolTable;
use oxc_span::{GetSpan, Span, SPAN};
use oxc_syntax::{
identifier::{is_identifier_part, is_identifier_part_ascii, LS, PS},
Expand Down Expand Up @@ -79,7 +79,7 @@ pub struct Codegen<'a> {
/// Original source code of the AST
source_text: &'a str,

mangler: Option<Mangler>,
symbol_table: Option<SymbolTable>,

/// Output Code
code: CodeBuffer,
Expand Down Expand Up @@ -162,7 +162,7 @@ impl<'a> Codegen<'a> {
Self {
options,
source_text: "",
mangler: None,
symbol_table: None,
code: CodeBuffer::default(),
needs_semicolon: false,
need_space_before_dot: 0,
Expand Down Expand Up @@ -194,10 +194,13 @@ impl<'a> Codegen<'a> {
self
}

/// Set the mangler for mangling identifiers.
/// Set the symbol table used for identifier renaming.
///
/// Can be used for easy renaming of variables (based on semantic analysis).
/// This is also the output of [`oxc_mangler::Mangler`] for symbol mangling/minification.
#[must_use]
pub fn with_mangler(mut self, mangler: Option<Mangler>) -> Self {
self.mangler = mangler;
pub fn with_symbol_table(mut self, symbol_table: Option<SymbolTable>) -> Self {
self.symbol_table = symbol_table;
self
}

Expand Down Expand Up @@ -516,9 +519,9 @@ impl<'a> Codegen<'a> {
}

fn get_identifier_reference_name(&self, reference: &IdentifierReference<'a>) -> &'a str {
if let Some(mangler) = &self.mangler {
if let Some(symbol_table) = &self.symbol_table {
if let Some(reference_id) = reference.reference_id.get() {
if let Some(name) = mangler.get_reference_name(reference_id) {
if let Some(name) = symbol_table.get_reference_name(reference_id) {
// SAFETY: Hack the lifetime to be part of the allocator.
return unsafe { std::mem::transmute_copy(&name) };
}
Expand All @@ -528,9 +531,9 @@ impl<'a> Codegen<'a> {
}

fn get_binding_identifier_name(&self, ident: &BindingIdentifier<'a>) -> &'a str {
if let Some(mangler) = &self.mangler {
if let Some(symbol_table) = &self.symbol_table {
if let Some(symbol_id) = ident.symbol_id.get() {
let name = mangler.get_symbol_name(symbol_id);
let name = symbol_table.get_name(symbol_id);
// SAFETY: Hack the lifetime to be part of the allocator.
return unsafe { std::mem::transmute_copy(&name) };
}
Expand Down
2 changes: 2 additions & 0 deletions crates/oxc_codegen/src/options.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::path::PathBuf;

use oxc_semantic::SymbolTable;

/// Legal comment
///
/// <https://esbuild.github.io/api/#legal-comments>
Expand Down
59 changes: 40 additions & 19 deletions crates/oxc_mangler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,50 @@ use rustc_hash::FxHashSet;
use oxc_allocator::{Allocator, Vec};
use oxc_ast::ast::{Declaration, Program, Statement};
use oxc_index::Idx;
use oxc_semantic::{ReferenceId, ScopeTree, Semantic, SemanticBuilder, SymbolId, SymbolTable};
use oxc_semantic::{ScopeTree, Semantic, SemanticBuilder, SymbolId, SymbolTable};
use oxc_span::Atom;

#[derive(Default, Debug, Clone, Copy)]
pub struct MangleOptions {
/// Also mangle exported variables.
pub top_level: bool,
/// Use more readable mangled names
/// (e.g. `slot_0`, `slot_1`, `slot_2`, ...) for debugging.
///
/// Uses base54 if false.
pub debug: bool,
}

type Slot = usize;

/// # Name Mangler / Symbol Minification
///
/// ## Example
///
/// ```rust
/// use oxc_codegen::{Codegen, CodegenOptions};
/// use oxc_ast::ast::Program;
/// use oxc_parser::Parser;
/// use oxc_allocator::Allocator;
/// use oxc_span::SourceType;
/// use oxc_mangler::{MangleOptions, Mangler};
///
/// let allocator = Allocator::default();
/// let source = "const result = 1 + 2;";
/// let parsed = Parser::new(&allocator, source, SourceType::mjs()).parse();
/// assert!(parsed.errors.is_empty());
///
/// let mangled_symbols = Mangler::new()
/// .with_options(MangleOptions { top_level: true, debug: true })
/// .build(&parsed.program);
///
/// let js = Codegen::new().with_symbol_table(mangled_symbols).build(&parsed.program);
/// // this will be `const a = 1 + 2;` if debug = false
/// assert_eq!(js.code, "const slot_0 = 1 + 2;\n");
/// ```
///
/// ## Implementation
///
/// See:
/// * [esbuild](https://github.com/evanw/esbuild/blob/v0.24.0/docs/architecture.md#symbol-minification)
///
Expand Down Expand Up @@ -61,7 +92,7 @@ type Slot = usize;
/// }
/// ```
///
/// ## Name Reuse Calculation
/// ### Name Reuse Calculation
///
/// This improvement was inspired by [evanw/esbuild#2614](https://github.com/evanw/esbuild/pull/2614).
///
Expand Down Expand Up @@ -112,8 +143,6 @@ type Slot = usize;
/// - slot 3: `bar`
#[derive(Default)]
pub struct Mangler {
symbol_table: SymbolTable,

options: MangleOptions,
}

Expand All @@ -129,17 +158,10 @@ impl Mangler {
self
}

pub fn get_symbol_name(&self, symbol_id: SymbolId) -> &str {
self.symbol_table.get_name(symbol_id)
}

pub fn get_reference_name(&self, reference_id: ReferenceId) -> Option<&str> {
let symbol_id = self.symbol_table.get_reference(reference_id).symbol_id()?;
Some(self.symbol_table.get_name(symbol_id))
}

/// Mangles the program. The resulting SymbolTable contains the mangled symbols - `program` is not modified.
/// Pass the symbol table to oxc_codegen to generate the mangled code.
#[must_use]
pub fn build(self, program: &Program<'_>) -> Mangler {
pub fn build(self, program: &Program<'_>) -> SymbolTable {
let semantic =
SemanticBuilder::new().with_scope_tree_child_ids(true).build(program).semantic;
self.build_with_semantic(semantic, program)
Expand All @@ -149,7 +171,7 @@ impl Mangler {
///
/// Panics if the child_ids does not exist in scope_tree.
#[must_use]
pub fn build_with_semantic(self, semantic: Semantic<'_>, program: &Program<'_>) -> Mangler {
pub fn build_with_semantic(self, semantic: Semantic<'_>, program: &Program<'_>) -> SymbolTable {
if self.options.debug {
self.build_with_symbols_and_scopes_impl(semantic, program, debug_name)
} else {
Expand All @@ -161,11 +183,11 @@ impl Mangler {
const CAPACITY: usize,
G: Fn(usize) -> InlineString<CAPACITY>,
>(
mut self,
self,
semantic: Semantic<'_>,
program: &Program<'_>,
generate_name: G,
) -> Mangler {
) -> SymbolTable {
let (mut symbol_table, scope_tree, ast_nodes) = semantic.into_symbols_scopes_nodes();

assert!(scope_tree.has_child_ids(), "child_id needs to be generated");
Expand Down Expand Up @@ -331,8 +353,7 @@ impl Mangler {
}
}

self.symbol_table = symbol_table;
self
symbol_table
}

fn tally_slot_frequencies<'a>(
Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_minifier/examples/mangler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ fn main() -> std::io::Result<()> {
fn mangler(source_text: &str, source_type: SourceType, debug: bool) -> String {
let allocator = Allocator::default();
let ret = Parser::new(&allocator, source_text, source_type).parse();
let mangler = Mangler::new()
let symbol_table = Mangler::new()
.with_options(MangleOptions { debug, top_level: source_type.is_module() })
.build(&ret.program);
CodeGenerator::new().with_mangler(Some(mangler)).build(&ret.program).code
CodeGenerator::new().with_symbol_table(Some(symbol_table)).build(&ret.program).code
}
2 changes: 1 addition & 1 deletion crates/oxc_minifier/examples/minifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ fn minify(
let ret = Minifier::new(options).build(allocator, &mut program);
CodeGenerator::new()
.with_options(CodegenOptions { minify: nospace, ..CodegenOptions::default() })
.with_mangler(ret.mangler)
.with_symbol_table(ret.symbol_table)
.build(&program)
.code
}
8 changes: 4 additions & 4 deletions crates/oxc_minifier/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ mod tester;
use oxc_allocator::Allocator;
use oxc_ast::ast::Program;
use oxc_mangler::Mangler;
use oxc_semantic::{SemanticBuilder, Stats};
use oxc_semantic::{SemanticBuilder, Stats, SymbolTable};

pub use oxc_mangler::MangleOptions;

Expand All @@ -31,7 +31,7 @@ impl Default for MinifierOptions {
}

pub struct MinifierReturn {
pub mangler: Option<Mangler>,
pub symbol_table: Option<SymbolTable>,
}

pub struct Minifier {
Expand All @@ -54,14 +54,14 @@ impl Minifier {
} else {
Stats::default()
};
let mangler = self.options.mangle.map(|options| {
let symbol_table = self.options.mangle.map(|options| {
let semantic = SemanticBuilder::new()
.with_stats(stats)
.with_scope_tree_child_ids(true)
.build(program)
.semantic;
Mangler::default().with_options(options).build_with_semantic(semantic, program)
});
MinifierReturn { mangler }
MinifierReturn { symbol_table }
}
}
4 changes: 2 additions & 2 deletions crates/oxc_minifier/tests/mangler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ fn mangle(source_text: &str, top_level: bool) -> String {
let source_type = SourceType::mjs();
let ret = Parser::new(&allocator, source_text, source_type).parse();
let program = ret.program;
let mangler =
let symbol_table =
Mangler::new().with_options(MangleOptions { debug: false, top_level }).build(&program);
CodeGenerator::new().with_mangler(Some(mangler)).build(&program).code
CodeGenerator::new().with_symbol_table(Some(symbol_table)).build(&program).code
}

#[test]
Expand Down
9 changes: 9 additions & 0 deletions crates/oxc_semantic/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,15 @@ impl SymbolTable {
&mut self.references[reference_id]
}

/// Get the name of the symbol a reference is resolved to. Returns `None` if the reference is
/// not resolved.
#[inline]
pub fn get_reference_name(&self, reference_id: ReferenceId) -> Option<&str> {
self.get_name(
self.references[reference_id].symbol_id()?
).into()
}

/// Returns `true` if the corresponding [`Reference`] is resolved to a symbol.
///
/// When `false`, this could either be a reference to a global value or an identifier that does
Expand Down
6 changes: 3 additions & 3 deletions crates/oxc_wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ impl Oxc {
}
}

let mangler = if minifier_options.compress.unwrap_or_default()
let symbol_table = if minifier_options.compress.unwrap_or_default()
|| minifier_options.mangle.unwrap_or_default()
{
let compress_options = minifier_options.compress_options.unwrap_or_default();
Expand All @@ -281,13 +281,13 @@ impl Oxc {
CompressOptions::all_false()
}),
};
Minifier::new(options).build(&allocator, &mut program).mangler
Minifier::new(options).build(&allocator, &mut program).symbol_table
} else {
None
};

self.codegen_text = CodeGenerator::new()
.with_mangler(mangler)
.with_symbol_table(symbol_table)
.with_options(CodegenOptions {
minify: minifier_options.whitespace.unwrap_or_default(),
..CodegenOptions::default()
Expand Down
4 changes: 2 additions & 2 deletions napi/minify/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub fn minify(

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

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

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

let ret = Codegen::new().with_options(codegen_options).with_mangler(mangler).build(&program);
let ret = Codegen::new().with_options(codegen_options).with_symbol_table(symbol_table).build(&program);

Ok(MinifyResult { code: ret.code, map: ret.map.map(oxc_sourcemap::napi::SourceMap::from) })
}
6 changes: 3 additions & 3 deletions tasks/coverage/src/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,17 +208,17 @@ impl Test262RuntimeCase {
);
}

let mangler = if minify {
let symbol_table = if minify {
Minifier::new(MinifierOptions { mangle: None, ..MinifierOptions::default() })
.build(&allocator, &mut program)
.mangler
.symbol_table
} else {
None
};

let mut text = CodeGenerator::new()
.with_options(CodegenOptions { minify, ..CodegenOptions::default() })
.with_mangler(mangler)
.with_symbol_table(symbol_table)
.build(&program)
.code;
if is_only_strict {
Expand Down
2 changes: 1 addition & 1 deletion tasks/minsize/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ fn minify(source_text: &str, source_type: SourceType) -> String {
let ret = Minifier::new(MinifierOptions::default()).build(&allocator, &mut program);
CodeGenerator::new()
.with_options(CodegenOptions { minify: true, comments: false, ..CodegenOptions::default() })
.with_mangler(ret.mangler)
.with_symbol_table(ret.symbol_table)
.build(&program)
.code
}
Expand Down

0 comments on commit 61f422b

Please sign in to comment.