Skip to content

Commit

Permalink
fix(mangler): keep exported symbols
Browse files Browse the repository at this point in the history
  • Loading branch information
sapphi-red committed Dec 22, 2024
1 parent 8afd356 commit c2a99eb
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 11 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/oxc_mangler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ oxc_ast = { workspace = true }
oxc_index = { workspace = true }
oxc_semantic = { workspace = true }
oxc_span = { workspace = true }
rustc-hash = { workspace = true }
42 changes: 37 additions & 5 deletions crates/oxc_mangler/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use itertools::Itertools;
use oxc_ast::ast::Program;
use oxc_ast::ast::{Declaration, Program, Statement};
use oxc_index::{index_vec, Idx, IndexVec};
use oxc_semantic::{ReferenceId, ScopeTree, SemanticBuilder, SymbolId, SymbolTable};
use oxc_span::CompactStr;
use rustc_hash::FxHashSet;

type Slot = usize;

Expand Down Expand Up @@ -85,6 +86,28 @@ impl Mangler {
pub fn build<'a>(mut self, program: &'a Program<'a>) -> Mangler {
let semantic = SemanticBuilder::new().build(program).semantic;

let (exported_names, exported_symbols): (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();

// Mangle the symbol table by computing slots from the scope tree.
// 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();
Expand Down Expand Up @@ -126,8 +149,13 @@ impl Mangler {
}
}

let frequencies =
self.tally_slot_frequencies(&symbol_table, &scope_tree, total_number_of_slots, &slots);
let frequencies = self.tally_slot_frequencies(
&symbol_table,
&exported_symbols,
&scope_tree,
total_number_of_slots,
&slots,
);

let root_unresolved_references = scope_tree.root_unresolved_references();
let root_bindings = scope_tree.get_bindings(scope_tree.root_scope_id());
Expand All @@ -145,7 +173,8 @@ impl Mangler {
if !is_keyword(n)
&& !is_special_name(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;
}
Expand Down Expand Up @@ -206,14 +235,17 @@ impl Mangler {
fn tally_slot_frequencies(
&self,
symbol_table: &SymbolTable,
exported_symbols: &FxHashSet<SymbolId>,
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() {
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;
}
if is_special_name(symbol_table.get_name(symbol_id)) {
Expand Down
10 changes: 4 additions & 6 deletions crates/oxc_minifier/tests/mangler/snapshots/mangler.snap
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,18 @@ function a(b) {
}

export function foo() {}; foo()
function a() {}
export function foo() {}
;
a();
export { a as foo };
foo();

export default function foo() {}; foo()
export default function a() {}
;
a();

export const foo = 1; foo
export const a = 1;
a;
export { a as foo };
export const foo = 1;
foo;

const foo = 1; foo; export { foo }
const a = 1;
Expand Down

0 comments on commit c2a99eb

Please sign in to comment.