From 362c427b94b5d3cf0d659ed5e0466fd541864fc6 Mon Sep 17 00:00:00 2001 From: Boshen <1430279+Boshen@users.noreply.github.com> Date: Sun, 22 Sep 2024 13:45:54 +0000 Subject: [PATCH] fix(mangler,codegen): do not mangle top level symbols (#5965) --- crates/oxc_codegen/src/gen.rs | 43 ++++++++++--------- crates/oxc_mangler/src/lib.rs | 35 ++++++++++----- crates/oxc_minifier/tests/mangler/mod.rs | 2 + .../tests/mangler/snapshots/mangler.snap | 21 ++++++--- 4 files changed, 64 insertions(+), 37 deletions(-) diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index 7be8f0833b2b6..f72ba077e06f2 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -762,24 +762,10 @@ impl<'a> Gen for ImportDeclaration<'a> { p.print_str("type "); } - let imported_name = match &spec.imported { - ModuleExportName::IdentifierName(identifier) => { - identifier.print(p, ctx); - identifier.name.as_str() - } - ModuleExportName::IdentifierReference(identifier) => { - identifier.print(p, ctx); - identifier.name.as_str() - } - ModuleExportName::StringLiteral(literal) => { - literal.print(p, ctx); - literal.value.as_str() - } - }; - - let local_name = spec.local.name.as_str(); - - if imported_name != local_name { + spec.imported.print(p, ctx); + let local_name = p.get_binding_identifier_name(&spec.local); + let imported_name = get_module_export_name(&spec.imported, p); + if imported_name.is_none() || imported_name != Some(local_name) { p.print_str(" as "); spec.local.print(p, ctx); } @@ -919,13 +905,28 @@ impl<'a> Gen for TSNamespaceExportDeclaration<'a> { } } +fn get_module_export_name<'a>( + module_export_name: &ModuleExportName<'a>, + p: &Codegen<'a>, +) -> Option<&'a str> { + match module_export_name { + ModuleExportName::IdentifierName(ident) => Some(ident.name.as_str()), + ModuleExportName::IdentifierReference(ident) => { + Some(p.get_identifier_reference_name(ident)) + } + ModuleExportName::StringLiteral(_) => None, + } +} + impl<'a> Gen for ExportSpecifier<'a> { fn gen(&self, p: &mut Codegen, ctx: Context) { if self.export_kind.is_type() { p.print_str("type "); } self.local.print(p, ctx); - if self.local.name() != self.exported.name() { + let local_name = get_module_export_name(&self.local, p); + let exported_name = get_module_export_name(&self.exported, p); + if exported_name.is_none() || local_name != exported_name { p.print_str(" as "); self.exported.print(p, ctx); } @@ -935,8 +936,8 @@ impl<'a> Gen for ExportSpecifier<'a> { impl<'a> Gen for ModuleExportName<'a> { fn gen(&self, p: &mut Codegen, ctx: Context) { match self { - Self::IdentifierName(identifier) => p.print_str(identifier.name.as_str()), - Self::IdentifierReference(identifier) => identifier.print(p, ctx), + Self::IdentifierName(ident) => ident.print(p, ctx), + Self::IdentifierReference(ident) => ident.print(p, ctx), Self::StringLiteral(literal) => literal.print(p, ctx), }; } diff --git a/crates/oxc_mangler/src/lib.rs b/crates/oxc_mangler/src/lib.rs index d61ecc936c6c0..22034deffa8fb 100644 --- a/crates/oxc_mangler/src/lib.rs +++ b/crates/oxc_mangler/src/lib.rs @@ -1,7 +1,7 @@ use itertools::Itertools; use oxc_ast::ast::Program; use oxc_index::{index_vec, Idx, IndexVec}; -use oxc_semantic::{ReferenceId, SemanticBuilder, SymbolId, SymbolTable}; +use oxc_semantic::{ReferenceId, ScopeTree, SemanticBuilder, SymbolId, SymbolTable}; use oxc_span::CompactStr; type Slot = usize; @@ -124,9 +124,12 @@ impl Mangler { } let frequencies = - Self::tally_slot_frequencies(&symbol_table, total_number_of_slots, &slots); + Self::tally_slot_frequencies(&symbol_table, &scope_tree, total_number_of_slots, &slots); - let mut names = Vec::with_capacity(total_number_of_slots); + let root_unresolved_references = scope_tree.root_unresolved_references(); + let root_bindings = scope_tree.get_bindings(scope_tree.root_scope_id()); + + let mut reserved_names = Vec::with_capacity(total_number_of_slots); let generate_name = if self.options.debug { debug_name } else { base54 }; let mut count = 0; @@ -135,13 +138,16 @@ impl Mangler { let name = generate_name(count); count += 1; // Do not mangle keywords and unresolved references - if !is_keyword(&name) - && !scope_tree.root_unresolved_references().contains_key(name.as_str()) + let n = name.as_str(); + if !is_keyword(n) + && !is_special_name(n) + && !root_unresolved_references.contains_key(n) + && !root_bindings.contains_key(n) { break name; } }; - names.push(name); + reserved_names.push(name); } // Group similar symbols for smaller gzipped file @@ -160,7 +166,9 @@ impl Mangler { let mut freq_iter = frequencies.iter(); // 2. "N number of vars are going to be assigned names of the same length" - for (_, slice_of_same_len_strings_group) in &names.into_iter().chunk_by(CompactStr::len) { + for (_, slice_of_same_len_strings_group) in + &reserved_names.into_iter().chunk_by(CompactStr::len) + { // 1. "The most frequent vars get the shorter names" // (freq_iter is sorted by frequency from highest to lowest, // so taking means take the N most frequent symbols remaining) @@ -194,14 +202,17 @@ impl Mangler { fn tally_slot_frequencies( symbol_table: &SymbolTable, + scope_tree: &ScopeTree, total_number_of_slots: usize, slots: &IndexVec, ) -> Vec { + let root_scope_id = scope_tree.root_scope_id(); let mut frequencies = vec![SlotFrequency::default(); total_number_of_slots]; for (symbol_id, slot) in slots.iter_enumerated() { - let symbol_flags = symbol_table.get_flags(symbol_id); - // omit renaming `export { x }` - if !symbol_flags.is_variable() || symbol_flags.is_export() { + if symbol_table.get_scope_id(symbol_id) == root_scope_id { + continue; + } + if is_special_name(symbol_table.get_name(symbol_id)) { continue; } let index = *slot; @@ -215,6 +226,10 @@ impl Mangler { } } +fn is_special_name(name: &str) -> bool { + matches!(name, "exports" | "arguments") +} + #[derive(Debug, Default, Clone)] struct SlotFrequency { pub slot: Slot, diff --git a/crates/oxc_minifier/tests/mangler/mod.rs b/crates/oxc_minifier/tests/mangler/mod.rs index 83459ef608f19..8ce4ef7913dfd 100644 --- a/crates/oxc_minifier/tests/mangler/mod.rs +++ b/crates/oxc_minifier/tests/mangler/mod.rs @@ -22,6 +22,8 @@ fn mangler() { "function foo(a) { let _ = { x } }", "function foo(a) { let { x } = y }", "var x; function foo(a) { ({ x } = y) }", + "import { x } from 's'; export { x }", + "function _ (exports) { Object.defineProperty(exports, '__esModule', { value: true }) }", ]; let snapshot = cases.into_iter().fold(String::new(), |mut w, case| { diff --git a/crates/oxc_minifier/tests/mangler/snapshots/mangler.snap b/crates/oxc_minifier/tests/mangler/snapshots/mangler.snap index 551e9687ec7cc..6e28a492caec8 100644 --- a/crates/oxc_minifier/tests/mangler/snapshots/mangler.snap +++ b/crates/oxc_minifier/tests/mangler/snapshots/mangler.snap @@ -2,22 +2,31 @@ source: crates/oxc_minifier/tests/mangler/mod.rs --- function foo(a) {a} -function a(b) { +function foo(b) { b; } function foo(a) { let _ = { x } } -function a(b) { +function foo(b) { let c = { x }; } function foo(a) { let { x } = y } -function a(b) { +function foo(b) { let { x: c } = y; } var x; function foo(a) { ({ x } = y) } -var a; -function b(c) { - ({x: a} = y); +var x; +function foo(c) { + ({x} = y); +} + +import { x } from 's'; export { x } +import { x } from "s"; +export { x }; + +function _ (exports) { Object.defineProperty(exports, '__esModule', { value: true }) } +function _(exports) { + Object.defineProperty(exports, "__esModule", { value: true }); }