Skip to content

Commit

Permalink
fix(mangler,codegen): do not mangle top level symbols (#5965)
Browse files Browse the repository at this point in the history
  • Loading branch information
Boshen committed Sep 22, 2024
1 parent fd1c46c commit 362c427
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 37 deletions.
43 changes: 22 additions & 21 deletions crates/oxc_codegen/src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand All @@ -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),
};
}
Expand Down
35 changes: 25 additions & 10 deletions crates/oxc_mangler/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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;
Expand All @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions crates/oxc_minifier/tests/mangler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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| {
Expand Down
21 changes: 15 additions & 6 deletions crates/oxc_minifier/tests/mangler/snapshots/mangler.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
}

0 comments on commit 362c427

Please sign in to comment.