fix(mangler,codegen): do not mangle top level symbols (#5965)

This commit is contained in:
Boshen 2024-09-22 13:45:54 +00:00
parent fd1c46ca9e
commit 362c427b94
4 changed files with 64 additions and 37 deletions

View file

@ -762,24 +762,10 @@ impl<'a> Gen for ImportDeclaration<'a> {
p.print_str("type "); p.print_str("type ");
} }
let imported_name = match &spec.imported { spec.imported.print(p, ctx);
ModuleExportName::IdentifierName(identifier) => { let local_name = p.get_binding_identifier_name(&spec.local);
identifier.print(p, ctx); let imported_name = get_module_export_name(&spec.imported, p);
identifier.name.as_str() if imported_name.is_none() || imported_name != Some(local_name) {
}
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 {
p.print_str(" as "); p.print_str(" as ");
spec.local.print(p, ctx); 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> { impl<'a> Gen for ExportSpecifier<'a> {
fn gen(&self, p: &mut Codegen, ctx: Context) { fn gen(&self, p: &mut Codegen, ctx: Context) {
if self.export_kind.is_type() { if self.export_kind.is_type() {
p.print_str("type "); p.print_str("type ");
} }
self.local.print(p, ctx); 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 "); p.print_str(" as ");
self.exported.print(p, ctx); self.exported.print(p, ctx);
} }
@ -935,8 +936,8 @@ impl<'a> Gen for ExportSpecifier<'a> {
impl<'a> Gen for ModuleExportName<'a> { impl<'a> Gen for ModuleExportName<'a> {
fn gen(&self, p: &mut Codegen, ctx: Context) { fn gen(&self, p: &mut Codegen, ctx: Context) {
match self { match self {
Self::IdentifierName(identifier) => p.print_str(identifier.name.as_str()), Self::IdentifierName(ident) => ident.print(p, ctx),
Self::IdentifierReference(identifier) => identifier.print(p, ctx), Self::IdentifierReference(ident) => ident.print(p, ctx),
Self::StringLiteral(literal) => literal.print(p, ctx), Self::StringLiteral(literal) => literal.print(p, ctx),
}; };
} }

View file

@ -1,7 +1,7 @@
use itertools::Itertools; use itertools::Itertools;
use oxc_ast::ast::Program; use oxc_ast::ast::Program;
use oxc_index::{index_vec, Idx, IndexVec}; 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; use oxc_span::CompactStr;
type Slot = usize; type Slot = usize;
@ -124,9 +124,12 @@ impl Mangler {
} }
let frequencies = 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 generate_name = if self.options.debug { debug_name } else { base54 };
let mut count = 0; let mut count = 0;
@ -135,13 +138,16 @@ impl Mangler {
let name = generate_name(count); let name = generate_name(count);
count += 1; count += 1;
// Do not mangle keywords and unresolved references // Do not mangle keywords and unresolved references
if !is_keyword(&name) let n = name.as_str();
&& !scope_tree.root_unresolved_references().contains_key(name.as_str()) if !is_keyword(n)
&& !is_special_name(n)
&& !root_unresolved_references.contains_key(n)
&& !root_bindings.contains_key(n)
{ {
break name; break name;
} }
}; };
names.push(name); reserved_names.push(name);
} }
// Group similar symbols for smaller gzipped file // Group similar symbols for smaller gzipped file
@ -160,7 +166,9 @@ impl Mangler {
let mut freq_iter = frequencies.iter(); let mut freq_iter = frequencies.iter();
// 2. "N number of vars are going to be assigned names of the same length" // 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" // 1. "The most frequent vars get the shorter names"
// (freq_iter is sorted by frequency from highest to lowest, // (freq_iter is sorted by frequency from highest to lowest,
// so taking means take the N most frequent symbols remaining) // so taking means take the N most frequent symbols remaining)
@ -194,14 +202,17 @@ impl Mangler {
fn tally_slot_frequencies( fn tally_slot_frequencies(
symbol_table: &SymbolTable, symbol_table: &SymbolTable,
scope_tree: &ScopeTree,
total_number_of_slots: usize, total_number_of_slots: usize,
slots: &IndexVec<SymbolId, Slot>, slots: &IndexVec<SymbolId, Slot>,
) -> Vec<SlotFrequency> { ) -> Vec<SlotFrequency> {
let root_scope_id = scope_tree.root_scope_id();
let mut frequencies = vec![SlotFrequency::default(); total_number_of_slots]; let mut frequencies = vec![SlotFrequency::default(); total_number_of_slots];
for (symbol_id, slot) in slots.iter_enumerated() { for (symbol_id, slot) in slots.iter_enumerated() {
let symbol_flags = symbol_table.get_flags(symbol_id); if symbol_table.get_scope_id(symbol_id) == root_scope_id {
// omit renaming `export { x }` continue;
if !symbol_flags.is_variable() || symbol_flags.is_export() { }
if is_special_name(symbol_table.get_name(symbol_id)) {
continue; continue;
} }
let index = *slot; let index = *slot;
@ -215,6 +226,10 @@ impl Mangler {
} }
} }
fn is_special_name(name: &str) -> bool {
matches!(name, "exports" | "arguments")
}
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
struct SlotFrequency { struct SlotFrequency {
pub slot: Slot, pub slot: Slot,

View file

@ -22,6 +22,8 @@ fn mangler() {
"function foo(a) { let _ = { x } }", "function foo(a) { let _ = { x } }",
"function foo(a) { let { x } = y }", "function foo(a) { let { x } = y }",
"var x; function foo(a) { ({ 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| { let snapshot = cases.into_iter().fold(String::new(), |mut w, case| {

View file

@ -2,22 +2,31 @@
source: crates/oxc_minifier/tests/mangler/mod.rs source: crates/oxc_minifier/tests/mangler/mod.rs
--- ---
function foo(a) {a} function foo(a) {a}
function a(b) { function foo(b) {
b; b;
} }
function foo(a) { let _ = { x } } function foo(a) { let _ = { x } }
function a(b) { function foo(b) {
let c = { x }; let c = { x };
} }
function foo(a) { let { x } = y } function foo(a) { let { x } = y }
function a(b) { function foo(b) {
let { x: c } = y; let { x: c } = y;
} }
var x; function foo(a) { ({ x } = y) } var x; function foo(a) { ({ x } = y) }
var a; var x;
function b(c) { function foo(c) {
({x: a} = y); ({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 });
} }