mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
fix(mangler,codegen): do not mangle top level symbols (#5965)
This commit is contained in:
parent
fd1c46ca9e
commit
362c427b94
4 changed files with 64 additions and 37 deletions
|
|
@ -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),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<SymbolId, Slot>,
|
||||
) -> Vec<SlotFrequency> {
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -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| {
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue