fix(minifier): Preserve init variable declarations when removing for statements during DCE (#6551)

- Closes: #6547

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
magic-akari 2024-10-15 16:51:30 +08:00 committed by GitHub
parent 2e4c793a78
commit a71e8a00bf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 58 additions and 17 deletions

View file

@ -112,6 +112,20 @@ impl<'a> AstBuilder<'a> {
mem::replace(decl, empty_decl)
}
#[inline]
pub fn move_variable_declaration(
self,
decl: &mut VariableDeclaration<'a>,
) -> VariableDeclaration<'a> {
let empty_decl = self.variable_declaration(
Span::default(),
VariableDeclarationKind::Var,
self.vec(),
false,
);
mem::replace(decl, empty_decl)
}
#[inline]
pub fn move_vec<T>(self, vec: &mut Vec<'a, T>) -> Vec<'a, T> {
mem::replace(vec, self.vec())

View file

@ -203,11 +203,26 @@ impl<'a, 'b> PeepholeRemoveDeadCode {
// Check vars in statement
let mut keep_var = KeepVar::new(ctx.ast);
keep_var.visit_statement(&for_stmt.body);
Some(
keep_var
.get_variable_declaration_statement()
.unwrap_or_else(|| ctx.ast.statement_empty(SPAN)),
)
let mut var_decl = keep_var.get_variable_declaration();
if let Some(ForStatementInit::VariableDeclaration(var_init)) = &mut for_stmt.init {
if var_init.kind.is_var() {
if let Some(var_decl) = &mut var_decl {
var_decl
.declarations
.splice(0..0, ctx.ast.move_vec(&mut var_init.declarations));
} else {
var_decl = Some(ctx.ast.move_variable_declaration(var_init));
}
}
}
var_decl
.map(|var_decl| {
ctx.ast.statement_declaration(ctx.ast.declaration_from_variable(var_decl))
})
.or_else(|| Some(ctx.ast.statement_empty(SPAN)))
}
Some(true) => {
// Remove the test expression.
@ -350,6 +365,10 @@ mod test {
// Make sure it plays nice with minimizing
fold("for(;false;) { foo(); continue }", "");
fold("for (var { c, x: [d] } = {}; 0;);", "var { c, x: [d] } = {};");
fold("for (var se = [1, 2]; false;);", "var se = [1, 2];");
fold("for (var se = [1, 2]; false;) { var a = 0; }", "var se = [1, 2], a;");
// fold("l1:for(;false;) { }", "");
}

View file

@ -34,19 +34,21 @@ impl<'a> Visit<'a> for KeepVar<'a> {
// match_module_declaration!(Statement) => {
// visitor.visit_module_declaration(it.to_module_declaration())
// }
Statement::VariableDeclaration(decl) => {
if decl.kind.is_var() {
decl.bound_names(&mut |ident| {
self.vars.push((ident.name.clone(), ident.span));
});
if decl.has_init() {
self.all_hoisted = false;
}
}
}
Statement::VariableDeclaration(decl) => self.visit_variable_declaration(decl),
_ => {}
}
}
fn visit_variable_declaration(&mut self, it: &VariableDeclaration<'a>) {
if it.kind.is_var() {
it.bound_names(&mut |ident| {
self.vars.push((ident.name.clone(), ident.span));
});
if it.has_init() {
self.all_hoisted = false;
}
}
}
}
impl<'a> KeepVar<'a> {
@ -58,7 +60,7 @@ impl<'a> KeepVar<'a> {
self.all_hoisted
}
pub fn get_variable_declaration_statement(self) -> Option<Statement<'a>> {
pub fn get_variable_declaration(self) -> Option<VariableDeclaration<'a>> {
if self.vars.is_empty() {
return None;
}
@ -71,7 +73,13 @@ impl<'a> KeepVar<'a> {
}));
let decl = self.ast.variable_declaration(SPAN, kind, decls, false);
let stmt = self.ast.statement_declaration(self.ast.declaration_from_variable(decl));
Some(decl)
}
pub fn get_variable_declaration_statement(self) -> Option<Statement<'a>> {
let stmt = self.ast.statement_declaration(
self.ast.declaration_from_variable(self.get_variable_declaration()?),
);
Some(stmt)
}
}