Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion 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)
}
}
2 changes: 1 addition & 1 deletion crates/oxc_codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ oxc_allocator = { workspace = true }
oxc_ast = { workspace = true }
oxc_data_structures = { workspace = true }
oxc_index = { workspace = true }
oxc_mangler = { workspace = true }
oxc_semantic = { workspace = true }
oxc_sourcemap = { workspace = true }
oxc_span = { workspace = true }
oxc_syntax = { workspace = true }
Expand Down
22 changes: 12 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,12 @@ 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).
#[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 +518,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 +530,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
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
7 changes: 7 additions & 0 deletions crates/oxc_semantic/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,13 @@ 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
7 changes: 5 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,10 @@ 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