feat(minifier): add constant folding to remove dead code (#4058)

This commit is contained in:
Boshen 2024-07-05 15:44:01 +08:00 committed by GitHub
parent edb557c02b
commit 0da9dfbf09
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 38 additions and 11 deletions

2
Cargo.lock generated
View file

@ -1551,6 +1551,7 @@ dependencies = [
name = "oxc_minifier" name = "oxc_minifier"
version = "0.16.3" version = "0.16.3"
dependencies = [ dependencies = [
"insta",
"itertools 0.13.0", "itertools 0.13.0",
"num-bigint", "num-bigint",
"num-traits", "num-traits",
@ -1564,6 +1565,7 @@ dependencies = [
"oxc_span", "oxc_span",
"oxc_syntax", "oxc_syntax",
"pico-args", "pico-args",
"walkdir",
] ]
[[package]] [[package]]

View file

@ -38,6 +38,6 @@ num-traits = { workspace = true }
oxc_parser = { workspace = true } oxc_parser = { workspace = true }
oxc_codegen = { workspace = true } oxc_codegen = { workspace = true }
# insta = { workspace = true } insta = { workspace = true }
# walkdir = { workspace = true } walkdir = { workspace = true }
pico-args = { workspace = true } pico-args = { workspace = true }

View file

@ -2,25 +2,34 @@ use oxc_allocator::Allocator;
use oxc_ast::{ast::*, visit::walk_mut, AstBuilder, VisitMut}; use oxc_ast::{ast::*, visit::walk_mut, AstBuilder, VisitMut};
use oxc_span::SPAN; use oxc_span::SPAN;
use crate::{compressor::ast_util::get_boolean_value, folder::Folder};
/// Remove Dead Code from the AST. /// Remove Dead Code from the AST.
/// ///
/// Terser option: `dead_code: true`. /// Terser option: `dead_code: true`.
pub struct RemoveDeadCode<'a> { pub struct RemoveDeadCode<'a> {
ast: AstBuilder<'a>, ast: AstBuilder<'a>,
folder: Folder<'a>,
} }
impl<'a> RemoveDeadCode<'a> { impl<'a> RemoveDeadCode<'a> {
pub fn new(allocator: &'a Allocator) -> Self { pub fn new(allocator: &'a Allocator) -> Self {
Self { ast: AstBuilder::new(allocator) } let ast = AstBuilder::new(allocator);
Self { ast, folder: Folder::new(ast) }
} }
pub fn build(&mut self, program: &mut Program<'a>) { pub fn build(&mut self, program: &mut Program<'a>) {
self.visit_program(program); self.visit_program(program);
} }
fn test_expression(&mut self, expr: &mut Expression<'a>) -> Option<bool> {
self.folder.fold_expression(expr);
get_boolean_value(expr)
}
pub fn remove_if(&mut self, stmt: &mut Statement<'a>) { pub fn remove_if(&mut self, stmt: &mut Statement<'a>) {
let Statement::IfStatement(if_stmt) = stmt else { return }; let Statement::IfStatement(if_stmt) = stmt else { return };
match if_stmt.test.get_boolean_value() { match self.test_expression(&mut if_stmt.test) {
Some(true) => { Some(true) => {
*stmt = self.ast.move_statement(&mut if_stmt.consequent); *stmt = self.ast.move_statement(&mut if_stmt.consequent);
} }
@ -41,7 +50,7 @@ impl<'a> RemoveDeadCode<'a> {
else { else {
return; return;
}; };
match conditional_expr.test.get_boolean_value() { match self.test_expression(&mut conditional_expr.test) {
Some(true) => { Some(true) => {
expression_stmt.expression = expression_stmt.expression =
self.ast.move_expression(&mut conditional_expr.consequent); self.ast.move_expression(&mut conditional_expr.consequent);

View file

@ -40,7 +40,7 @@ impl<'a> Folder<'a> {
self self
} }
pub(crate) fn fold_expression<'b>(&mut self, expr: &'b mut Expression<'a>) { pub fn fold_expression<'b>(&mut self, expr: &'b mut Expression<'a>) {
let folded_expr = match expr { let folded_expr = match expr {
Expression::BinaryExpression(binary_expr) => match binary_expr.operator { Expression::BinaryExpression(binary_expr) => match binary_expr.operator {
BinaryOperator::Equality BinaryOperator::Equality

View file

@ -1,8 +1,9 @@
mod closure; #![allow(unused)]
mod esbuild; // mod closure;
// mod esbuild;
mod oxc; mod oxc;
mod tdewolff; // mod tdewolff;
mod terser; // mod terser;
use oxc_allocator::Allocator; use oxc_allocator::Allocator;
use oxc_codegen::WhitespaceRemover; use oxc_codegen::WhitespaceRemover;

View file

@ -1,5 +1,5 @@
mod code_removal; mod code_removal;
mod folding; mod folding;
mod precedence; // mod precedence;
mod remove_dead_code; mod remove_dead_code;
mod replace_global_defines; mod replace_global_defines;

View file

@ -24,6 +24,21 @@ fn remove_dead_code() {
test("if (true) { foo } else { bar }", "{foo}"); test("if (true) { foo } else { bar }", "{foo}");
test("if (false) { foo } else { bar }", "{bar}"); test("if (false) { foo } else { bar }", "{bar}");
test("if (!false) { foo }", "{foo}");
test("if (!true) { foo } else { bar }", "{bar}");
test("if ('production' == 'production') { foo } else { bar }", "{foo}");
test("if ('development' == 'production') { foo } else { bar }", "{bar}");
test("if ('production' === 'production') { foo } else { bar }", "{foo}");
test("if ('development' === 'production') { foo } else { bar }", "{bar}");
test("false ? foo : bar;", "bar"); test("false ? foo : bar;", "bar");
test("true ? foo : bar;", "foo"); test("true ? foo : bar;", "foo");
test("!true ? foo : bar;", "bar");
test("!false ? foo : bar;", "foo");
test("!!false ? foo : bar;", "bar");
test("!!true ? foo : bar;", "foo");
} }