Skip to content

Commit fe5dcbb

Browse files
committed
Script
1 parent 19dfc69 commit fe5dcbb

File tree

1 file changed

+75
-88
lines changed
  • crates/swc_ecma_transforms_optimization/src

1 file changed

+75
-88
lines changed

crates/swc_ecma_transforms_optimization/src/dce.rs

Lines changed: 75 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -586,115 +586,102 @@ impl VisitMut for DeadCodeEliminator {
586586
}
587587
}
588588

589-
// Visit the module to collect references
590-
module.visit_mut_children_with(self);
589+
// Process module items
590+
module.body.visit_mut_with(self);
591+
592+
// Clean up unreferenced import specifiers
593+
for i in 0..module.body.len() {
594+
if let ModuleItem::ModuleDecl(ModuleDecl::Import(import)) = &mut module.body[i] {
595+
import.specifiers.retain(|s| match s {
596+
ImportSpecifier::Named(named) => {
597+
let imported = match &named.imported {
598+
Some(imported) => match imported {
599+
ModuleExportName::Ident(i) => i.sym.clone(),
600+
ModuleExportName::Str(s) => s.value.clone(),
601+
},
602+
None => named.local.sym.clone(),
603+
};
604+
let id = (imported, named.local.ctxt);
605+
self.import_specifiers.get(&id).copied().unwrap_or(true)
606+
}
607+
ImportSpecifier::Default(default) => {
608+
let id = (Atom::from("default"), default.local.ctxt);
609+
self.import_specifiers.get(&id).copied().unwrap_or(true)
610+
}
611+
ImportSpecifier::Namespace(_) => true, // Always keep namespace imports
612+
});
591613

592-
// Mark used import specifiers
593-
for (id, var_info) in &self.vars {
594-
if var_info.referenced && self.import_specifiers.contains_key(id) {
595-
if let Some(used) = self.import_specifiers.get_mut(id) {
596-
*used = true;
614+
// Remove empty imports (but keep side-effect imports)
615+
if import.specifiers.is_empty() && !import.src.value.is_empty() {
616+
self.changed = true;
617+
// Replace with empty statement to maintain AST structure
618+
module.body[i] = ModuleItem::Stmt(Stmt::Empty(EmptyStmt { span: DUMMY_SP }));
597619
}
598620
}
599621
}
600622

601-
// Second pass: remove unused declarations and unused import specifiers
602-
let mut i = 0;
603-
while i < module.body.len() {
604-
let mut removed = false;
623+
// Remove unreachable statements in the module
624+
for i in 0..module.body.len() {
625+
if let ModuleItem::Stmt(stmt) = &mut module.body[i] {
626+
// Try to remove pure statements that have no side effects
627+
if self.try_remove_pure_stmt(stmt) {
628+
self.changed = true;
629+
module.body[i] = ModuleItem::Stmt(Stmt::Empty(EmptyStmt { span: DUMMY_SP }));
630+
}
631+
}
632+
}
605633

606-
match &mut module.body[i] {
607-
ModuleItem::Stmt(Stmt::Decl(Decl::Var(var))) => {
608-
let mut j = 0;
609-
while j < var.decls.len() {
610-
let decl = &var.decls[j];
611-
let ids = find_pat_ids(&decl.name);
634+
// Exit module scope
635+
self.exit_scope(old_scope);
636+
}
612637

613-
// If all identifiers in this declaration are unused and have no side
614-
// effects, we can remove it
615-
let all_unused = ids.iter().all(|id| {
616-
if let Some(var_info) = self.vars.get(id) {
617-
!var_info.referenced
618-
&& !var_info.has_side_effects
619-
&& !var_info.exported
620-
} else {
621-
false
622-
}
623-
});
638+
fn visit_mut_script(&mut self, script: &mut Script) {
639+
// Initialize the top-level script scope
640+
let old_scope = self.enter_scope(None);
641+
if let Some(scope) = self.scopes.last_mut() {
642+
scope.is_module_scope = true;
643+
}
624644

625-
let no_side_effects = decl
645+
// First pass: collect all declarations
646+
for stmt in &script.body {
647+
match stmt {
648+
Stmt::Decl(Decl::Var(var)) => {
649+
for decl in &var.decls {
650+
let ids = find_pat_ids(&decl.name);
651+
let has_effects = decl
626652
.init
627653
.as_ref()
628-
.map_or(true, |init| self.is_pure_expr(init));
629-
630-
if all_unused && no_side_effects {
631-
var.decls.remove(j);
632-
self.changed = true;
633-
removed = var.decls.is_empty();
634-
} else {
635-
j += 1;
654+
.map_or(false, |init| !self.is_pure_expr(init));
655+
for id in ids {
656+
// Mark all top-level declarations in scripts as referenced
657+
self.register_reference(&id);
658+
self.register_declaration(id, has_effects, false);
636659
}
637660
}
638661
}
639-
ModuleItem::Stmt(Stmt::Decl(Decl::Fn(fn_decl))) => {
662+
Stmt::Decl(Decl::Fn(fn_decl)) => {
640663
let id = (fn_decl.ident.sym.clone(), fn_decl.ident.ctxt);
641-
if let Some(var_info) = self.vars.get(&id) {
642-
if !var_info.referenced && !var_info.exported {
643-
module.body.remove(i);
644-
self.changed = true;
645-
removed = true;
646-
}
647-
}
664+
// Mark all top-level declarations in scripts as referenced
665+
self.register_reference(&id);
666+
self.register_declaration(id, false, true);
648667
}
649-
ModuleItem::Stmt(Stmt::Decl(Decl::Class(class_decl))) => {
668+
Stmt::Decl(Decl::Class(class_decl)) => {
650669
let id = (class_decl.ident.sym.clone(), class_decl.ident.ctxt);
651-
if let Some(var_info) = self.vars.get(&id) {
652-
if !var_info.referenced && !var_info.exported {
653-
module.body.remove(i);
654-
self.changed = true; // 클래스 제거 시 명시적으로 changed 설정
655-
removed = true;
656-
}
657-
}
658-
}
659-
ModuleItem::Stmt(stmt) => {
660-
self.try_remove_pure_stmt(stmt);
661-
}
662-
ModuleItem::ModuleDecl(ModuleDecl::Import(import_decl)) => {
663-
// Store original count to check if we've removed all specifiers
664-
let original_count = import_decl.specifiers.len();
665-
666-
// Remove unused import specifiers
667-
let mut removed_any = false;
668-
import_decl.specifiers.retain(|specifier| {
669-
let local = match specifier {
670-
ImportSpecifier::Named(named) => &named.local,
671-
ImportSpecifier::Default(default) => &default.local,
672-
ImportSpecifier::Namespace(namespace) => &namespace.local,
673-
};
674-
675-
let id = (local.sym.clone(), local.ctxt);
676-
// Keep the specifier if it's used or not tracked
677-
let keep = self.import_specifiers.get(&id).map_or(true, |used| *used);
678-
if !keep {
679-
removed_any = true;
680-
}
681-
keep
682-
});
683-
684-
// 정말로 import specifier가 제거되었는지 확인하고 변경 사항 표시
685-
if removed_any && original_count != import_decl.specifiers.len() {
686-
self.changed = true;
687-
}
670+
// Mark all top-level declarations in scripts as referenced
671+
self.register_reference(&id);
672+
self.register_declaration(id, false, false);
688673
}
689674
_ => {}
690675
}
691-
692-
if !removed {
693-
i += 1;
694-
}
695676
}
696677

697-
// Exit module scope
678+
// Visit the script to process nested statements and expressions
679+
script.visit_mut_children_with(self);
680+
681+
// Remove unreachable statements in the script
682+
self.remove_unreachable_stmts(&mut script.body);
683+
684+
// Exit script scope
698685
self.exit_scope(old_scope);
699686
}
700687

0 commit comments

Comments
 (0)