mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
fix(mangler): keep exported symbols for top_level: true (#7927)
~~I'm not going to work on a fix for a while so feel free to fix it if anyone wants to work on it.~~ Exported symbols are now not mangled.
This commit is contained in:
parent
d15c856b13
commit
5c63414c23
5 changed files with 72 additions and 6 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1833,6 +1833,7 @@ dependencies = [
|
||||||
"oxc_index",
|
"oxc_index",
|
||||||
"oxc_semantic",
|
"oxc_semantic",
|
||||||
"oxc_span",
|
"oxc_span",
|
||||||
|
"rustc-hash",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -26,3 +26,4 @@ oxc_ast = { workspace = true }
|
||||||
oxc_index = { workspace = true }
|
oxc_index = { workspace = true }
|
||||||
oxc_semantic = { workspace = true }
|
oxc_semantic = { workspace = true }
|
||||||
oxc_span = { workspace = true }
|
oxc_span = { workspace = true }
|
||||||
|
rustc-hash = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use oxc_ast::ast::Program;
|
use oxc_ast::ast::{Declaration, Program, Statement};
|
||||||
use oxc_index::{index_vec, Idx, IndexVec};
|
use oxc_index::{index_vec, Idx, IndexVec};
|
||||||
use oxc_semantic::{ReferenceId, ScopeTree, SemanticBuilder, SymbolId, SymbolTable};
|
use oxc_semantic::{ReferenceId, ScopeTree, SemanticBuilder, SymbolId, SymbolTable};
|
||||||
use oxc_span::CompactStr;
|
use oxc_span::CompactStr;
|
||||||
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
type Slot = usize;
|
type Slot = usize;
|
||||||
|
|
||||||
|
|
@ -85,6 +86,12 @@ impl Mangler {
|
||||||
pub fn build<'a>(mut self, program: &'a Program<'a>) -> Mangler {
|
pub fn build<'a>(mut self, program: &'a Program<'a>) -> Mangler {
|
||||||
let semantic = SemanticBuilder::new().build(program).semantic;
|
let semantic = SemanticBuilder::new().build(program).semantic;
|
||||||
|
|
||||||
|
let (exported_names, exported_symbols) = if self.options.top_level {
|
||||||
|
Mangler::collect_exported_symbols(program)
|
||||||
|
} else {
|
||||||
|
Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
// Mangle the symbol table by computing slots from the scope tree.
|
// Mangle the symbol table by computing slots from the scope tree.
|
||||||
// A slot is the occurrence index of a binding identifier inside a scope.
|
// A slot is the occurrence index of a binding identifier inside a scope.
|
||||||
let (mut symbol_table, scope_tree) = semantic.into_symbol_table_and_scope_tree();
|
let (mut symbol_table, scope_tree) = semantic.into_symbol_table_and_scope_tree();
|
||||||
|
|
@ -126,8 +133,13 @@ impl Mangler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let frequencies =
|
let frequencies = self.tally_slot_frequencies(
|
||||||
self.tally_slot_frequencies(&symbol_table, &scope_tree, total_number_of_slots, &slots);
|
&symbol_table,
|
||||||
|
&exported_symbols,
|
||||||
|
&scope_tree,
|
||||||
|
total_number_of_slots,
|
||||||
|
&slots,
|
||||||
|
);
|
||||||
|
|
||||||
let root_unresolved_references = scope_tree.root_unresolved_references();
|
let root_unresolved_references = scope_tree.root_unresolved_references();
|
||||||
let root_bindings = scope_tree.get_bindings(scope_tree.root_scope_id());
|
let root_bindings = scope_tree.get_bindings(scope_tree.root_scope_id());
|
||||||
|
|
@ -145,7 +157,8 @@ impl Mangler {
|
||||||
if !is_keyword(n)
|
if !is_keyword(n)
|
||||||
&& !is_special_name(n)
|
&& !is_special_name(n)
|
||||||
&& !root_unresolved_references.contains_key(n)
|
&& !root_unresolved_references.contains_key(n)
|
||||||
&& (self.options.top_level || !root_bindings.contains_key(n))
|
&& !(root_bindings.contains_key(n)
|
||||||
|
&& (!self.options.top_level || exported_names.contains(n)))
|
||||||
{
|
{
|
||||||
break name;
|
break name;
|
||||||
}
|
}
|
||||||
|
|
@ -206,6 +219,7 @@ impl Mangler {
|
||||||
fn tally_slot_frequencies(
|
fn tally_slot_frequencies(
|
||||||
&self,
|
&self,
|
||||||
symbol_table: &SymbolTable,
|
symbol_table: &SymbolTable,
|
||||||
|
exported_symbols: &FxHashSet<SymbolId>,
|
||||||
scope_tree: &ScopeTree,
|
scope_tree: &ScopeTree,
|
||||||
total_number_of_slots: usize,
|
total_number_of_slots: usize,
|
||||||
slots: &IndexVec<SymbolId, Slot>,
|
slots: &IndexVec<SymbolId, Slot>,
|
||||||
|
|
@ -213,7 +227,9 @@ impl Mangler {
|
||||||
let root_scope_id = scope_tree.root_scope_id();
|
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() {
|
||||||
if !self.options.top_level && symbol_table.get_scope_id(symbol_id) == root_scope_id {
|
if symbol_table.get_scope_id(symbol_id) == root_scope_id
|
||||||
|
&& (!self.options.top_level || exported_symbols.contains(&symbol_id))
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if is_special_name(symbol_table.get_name(symbol_id)) {
|
if is_special_name(symbol_table.get_name(symbol_id)) {
|
||||||
|
|
@ -228,6 +244,29 @@ impl Mangler {
|
||||||
frequencies.sort_unstable_by_key(|x| std::cmp::Reverse(x.frequency));
|
frequencies.sort_unstable_by_key(|x| std::cmp::Reverse(x.frequency));
|
||||||
frequencies
|
frequencies
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn collect_exported_symbols(program: &Program) -> (FxHashSet<CompactStr>, FxHashSet<SymbolId>) {
|
||||||
|
program
|
||||||
|
.body
|
||||||
|
.iter()
|
||||||
|
.filter_map(|statement| {
|
||||||
|
let Statement::ExportNamedDeclaration(v) = statement else { return None };
|
||||||
|
v.declaration.as_ref()
|
||||||
|
})
|
||||||
|
.flat_map(|decl| {
|
||||||
|
if let Declaration::VariableDeclaration(decl) = decl {
|
||||||
|
itertools::Either::Left(
|
||||||
|
decl.declarations
|
||||||
|
.iter()
|
||||||
|
.filter_map(|decl| decl.id.get_binding_identifier()),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
itertools::Either::Right(decl.id().into_iter())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|id| (id.name.to_compact_str(), id.symbol_id()))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_special_name(name: &str) -> bool {
|
fn is_special_name(name: &str) -> bool {
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,13 @@ fn mangler() {
|
||||||
"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 }) }",
|
||||||
];
|
];
|
||||||
let top_level_cases = ["function foo(a) {a}"];
|
let top_level_cases = [
|
||||||
|
"function foo(a) {a}",
|
||||||
|
"export function foo() {}; foo()",
|
||||||
|
"export default function foo() {}; foo()",
|
||||||
|
"export const foo = 1; foo",
|
||||||
|
"const foo = 1; foo; export { foo }",
|
||||||
|
];
|
||||||
|
|
||||||
let mut snapshot = String::new();
|
let mut snapshot = String::new();
|
||||||
cases.into_iter().fold(&mut snapshot, |w, case| {
|
cases.into_iter().fold(&mut snapshot, |w, case| {
|
||||||
|
|
|
||||||
|
|
@ -35,3 +35,22 @@ function foo(a) {a}
|
||||||
function a(b) {
|
function a(b) {
|
||||||
b;
|
b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function foo() {}; foo()
|
||||||
|
export function foo() {}
|
||||||
|
;
|
||||||
|
foo();
|
||||||
|
|
||||||
|
export default function foo() {}; foo()
|
||||||
|
export default function a() {}
|
||||||
|
;
|
||||||
|
a();
|
||||||
|
|
||||||
|
export const foo = 1; foo
|
||||||
|
export const foo = 1;
|
||||||
|
foo;
|
||||||
|
|
||||||
|
const foo = 1; foo; export { foo }
|
||||||
|
const a = 1;
|
||||||
|
a;
|
||||||
|
export { a as foo };
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue