From d6d13dd296fd365d4229e02d72a6658266b914c7 Mon Sep 17 00:00:00 2001 From: Boshen <1430279+Boshen@users.noreply.github.com> Date: Mon, 3 Feb 2025 13:28:58 +0000 Subject: [PATCH] feat(minifier): minimize `!!(boolean_expr)` -> `boolean_expr` (#8849) --- .../src/constant_evaluation/value_type.rs | 32 ++++++++++++++++++- .../peephole/substitute_alternate_syntax.rs | 18 +++-------- crates/oxc_minifier/tests/peephole/esbuild.rs | 10 +++--- tasks/minsize/minsize.snap | 4 +-- 4 files changed, 42 insertions(+), 22 deletions(-) diff --git a/crates/oxc_ecmascript/src/constant_evaluation/value_type.rs b/crates/oxc_ecmascript/src/constant_evaluation/value_type.rs index 24f60d0038808..473be0b32a9f4 100644 --- a/crates/oxc_ecmascript/src/constant_evaluation/value_type.rs +++ b/crates/oxc_ecmascript/src/constant_evaluation/value_type.rs @@ -1,4 +1,4 @@ -use oxc_ast::ast::{BinaryExpression, Expression}; +use oxc_ast::ast::{BinaryExpression, ConditionalExpression, Expression, LogicalExpression}; use oxc_syntax::operator::{BinaryOperator, UnaryOperator}; /// JavaScript Language Type @@ -94,6 +94,8 @@ impl<'a> From<&Expression<'a>> for ValueType { e.expressions.last().map_or(ValueType::Undetermined, Self::from) } Expression::AssignmentExpression(e) => Self::from(&e.right), + Expression::ConditionalExpression(e) => Self::from(&**e), + Expression::LogicalExpression(e) => Self::from(&**e), _ => Self::Undetermined, } } @@ -145,3 +147,31 @@ impl<'a> From<&BinaryExpression<'a>> for ValueType { } } } + +impl<'a> From<&ConditionalExpression<'a>> for ValueType { + fn from(e: &ConditionalExpression<'a>) -> Self { + let left = Self::from(&e.consequent); + if left.is_undetermined() { + return Self::Undetermined; + } + let right = Self::from(&e.alternate); + if left == right { + return left; + } + Self::Undetermined + } +} + +impl<'a> From<&LogicalExpression<'a>> for ValueType { + fn from(e: &LogicalExpression<'a>) -> Self { + let left = Self::from(&e.left); + if !left.is_boolean() { + return Self::Undetermined; + } + let right = Self::from(&e.right); + if left == right { + return left; + } + Self::Undetermined + } +} diff --git a/crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs b/crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs index 8dbd44e6aa3dd..b79646f6148dc 100644 --- a/crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs +++ b/crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs @@ -503,20 +503,10 @@ impl<'a> PeepholeOptimizations { "Boolean" => match arg { None => Some(ctx.ast.expression_boolean_literal(span, false)), Some(arg) => { - if let Expression::UnaryExpression(unary_expr) = arg { - if unary_expr.operator == UnaryOperator::LogicalNot { - return Some(ctx.ast.move_expression(arg)); - } - } - Some(ctx.ast.expression_unary( - span, - UnaryOperator::LogicalNot, - ctx.ast.expression_unary( - span, - UnaryOperator::LogicalNot, - ctx.ast.move_expression(arg), - ), - )) + let mut arg = ctx.ast.move_expression(arg); + Self::try_fold_expr_in_boolean_context(&mut arg, ctx); + let arg = ctx.ast.expression_unary(span, UnaryOperator::LogicalNot, arg); + Some(Self::minimize_not(span, arg, ctx)) } }, "String" => { diff --git a/crates/oxc_minifier/tests/peephole/esbuild.rs b/crates/oxc_minifier/tests/peephole/esbuild.rs index d45f9cd7f93fd..0564591bfa1b4 100644 --- a/crates/oxc_minifier/tests/peephole/esbuild.rs +++ b/crates/oxc_minifier/tests/peephole/esbuild.rs @@ -403,8 +403,8 @@ fn js_parser_test() { test("a = !!(b && !c)", "a = !!(b && !c);"); test("a = !!(b || !c)", "a = !!(b || !c);"); test("a = !!(b ?? !c)", "a = !!(b ?? !c);"); - // test("a = !!(!b && !c)", "a = !b && !c;"); - // test("a = !!(!b || !c)", "a = !b || !c;"); + test("a = !!(!b && !c)", "a = !b && !c;"); + test("a = !!(!b || !c)", "a = !b || !c;"); test("a = !!(!b ?? !c)", "a = !b;"); test("a = !!(b, c)", "a = (b, !!c);"); test("a = Boolean(b); var Boolean", "a = Boolean(b);var Boolean;"); @@ -414,9 +414,9 @@ fn js_parser_test() { test("a = Boolean(!!b)", "a = !!b;"); test("a = Boolean(b ? true : false)", "a = !!b;"); test("a = Boolean(b ? false : true)", "a = !b;"); - // test("a = Boolean(b ? c > 0 : c < 0)", "a = b ? c > 0 : c < 0;"); - // test("a = Boolean((b | c) !== 0)", "a = !!(b | c);"); - // test("a = Boolean(b ? (c | d) !== 0 : (d | e) !== 0)", "a = !!(b ? c | d : d | e);"); + test("a = Boolean(b ? c > 0 : c < 0)", "a = b ? c > 0 : c < 0;"); + test("a = Boolean((b | c) !== 0)", "a = !!(b | c);"); + test("a = Boolean(b ? (c | d) !== 0 : (d | e) !== 0)", "a = !!(b ? c | d : d | e);"); test("a = Number(x)", "a = Number(x);"); test("a = Number(0n)", "a = Number(0n);"); test("a = Number(false); var Number", "a = Number(!1);var Number;"); diff --git a/tasks/minsize/minsize.snap b/tasks/minsize/minsize.snap index 8bcf48d9e8bf4..08cf3d3d5e58d 100644 --- a/tasks/minsize/minsize.snap +++ b/tasks/minsize/minsize.snap @@ -17,9 +17,9 @@ Original | minified | minified | gzip | gzip | Fixture 1.25 MB | 650.33 kB | 646.76 kB | 160.96 kB | 163.73 kB | three.js -2.14 MB | 717.07 kB | 724.14 kB | 162.06 kB | 181.07 kB | victory.js +2.14 MB | 717.07 kB | 724.14 kB | 162.05 kB | 181.07 kB | victory.js -3.20 MB | 1.01 MB | 1.01 MB | 324.41 kB | 331.56 kB | echarts.js +3.20 MB | 1.01 MB | 1.01 MB | 324.40 kB | 331.56 kB | echarts.js 6.69 MB | 2.28 MB | 2.31 MB | 467.77 kB | 488.28 kB | antd.js