Skip to content

Commit

Permalink
feat(minifier): dce ExpressionStatements with no side effect (#6457)
Browse files Browse the repository at this point in the history
Not sure about the performance. Just have a try.
  • Loading branch information
7086cmd committed Oct 11, 2024
1 parent e811812 commit 3677ef8
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 2 deletions.
54 changes: 54 additions & 0 deletions crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use oxc_ast::{ast::*, Visit};
use oxc_span::SPAN;
use oxc_traverse::{Ancestor, Traverse, TraverseCtx};

use crate::node_util::IsLiteralValue;
use crate::{keep_var::KeepVar, node_util::NodeUtil, tri::Tri, CompressorPass};

/// Remove Dead Code from the AST.
Expand Down Expand Up @@ -31,6 +32,9 @@ impl<'a> Traverse<'a> for PeepholeRemoveDeadCode {
if let Some(new_stmt) = match stmt {
Statement::IfStatement(if_stmt) => self.try_fold_if(if_stmt, ctx),
Statement::ForStatement(for_stmt) => self.try_fold_for(for_stmt, ctx),
Statement::ExpressionStatement(expr_stmt) => {
Self::try_fold_expression_stmt(expr_stmt, ctx)
}
_ => None,
} {
*stmt = new_stmt;
Expand Down Expand Up @@ -192,6 +196,27 @@ impl<'a> PeepholeRemoveDeadCode {
}
}

fn try_fold_expression_stmt(
stmt: &mut ExpressionStatement<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Option<Statement<'a>> {
// We need to check if it is in arrow function with `expression: true`.
// This is the only scenario where we can't remove it even if `ExpressionStatement`.
// TODO find a better way to handle this.

stmt.expression
.is_literal_value(false)
.then(|| {
if let Ancestor::ArrowFunctionExpressionBody(body) = ctx.ancestry.ancestor(1) {
if *body.expression() {
return None;
}
}
Some(ctx.ast.statement_empty(SPAN))
})
.unwrap_or(None)
}

/// Try folding conditional expression (?:) if the condition results of the condition is known.
fn try_fold_conditional_expression(
expr: &mut ConditionalExpression<'a>,
Expand Down Expand Up @@ -290,4 +315,33 @@ mod test {
fold("for(;undefined;) foo()", "");
fold("for(;'';) foo()", "");
}

#[test]
fn test_object_literal() {
fold("({})", "");
fold("({a:1})", "");
// fold("({a:foo()})", "foo()");
// fold("({'a':foo()})", "foo()");
// Object-spread may trigger getters.
fold_same("({...a})");
fold_same("({...foo()})");
}

#[test]
fn test_array_literal() {
fold("([])", "");
fold("([1])", "");
// fold("([a])", "a");
// fold("([foo()])", "foo()");
}

#[test]
#[ignore]
fn test_array_literal_containing_spread() {
fold_same("([...c])");
fold("([4, ...c, a])", "([...c])");
fold("([foo(), ...c, bar()])", "(foo(), [...c], bar())");
fold("([...a, b, ...c])", "([...a], [...c])");
fold_same("([...b, ...c])"); // It would also be fine if the spreads were split apart.
}
}
3 changes: 2 additions & 1 deletion crates/oxc_minifier/src/node_util/is_literal_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ impl<'a, 'b> IsLiteralValue<'a, 'b> for ObjectProperty<'a> {
impl<'a, 'b> IsLiteralValue<'a, 'b> for PropertyKey<'a> {
fn is_literal_value(&self, include_functions: bool) -> bool {
match self {
Self::StaticIdentifier(_) | Self::PrivateIdentifier(_) => false,
Self::StaticIdentifier(_) => true,
Self::PrivateIdentifier(_) => false,
match_expression!(Self) => self.to_expression().is_literal_value(include_functions),
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ fn dce_conditional_expression() {

#[test]
fn dce_logical_expression() {
test("false && bar()", "false");
test("false && bar()", "");
test("true && bar()", "bar()");

test("const foo = false && bar()", "const foo = false");
Expand Down

0 comments on commit 3677ef8

Please sign in to comment.