mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(minifier): dce ExpressionStatements with no side effect (#6457)
Not sure about the performance. Just have a try.
This commit is contained in:
parent
e81181261d
commit
3677ef81e9
3 changed files with 57 additions and 2 deletions
|
|
@ -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.
|
||||
|
|
@ -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;
|
||||
|
|
@ -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>,
|
||||
|
|
@ -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.
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
Loading…
Reference in a new issue