From 5ed439bcaff16a9023ba39afefa19e0fa5818835 Mon Sep 17 00:00:00 2001 From: camc314 <18101008+camc314@users.noreply.github.com> Date: Mon, 6 Jan 2025 23:26:19 +0000 Subject: [PATCH] feat(minifier): minify typeof in binary expressions (#8302) --- .../src/ast_passes/peephole_fold_constants.rs | 63 +++++++++++++++++-- tasks/minsize/minsize.snap | 2 +- 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs index 081d4826981f3..1827cb3cdeeb6 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs @@ -31,7 +31,8 @@ impl<'a> Traverse<'a> for PeepholeFoldConstants { fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { let ctx = Ctx(ctx); if let Some(folded_expr) = match expr { - Expression::BinaryExpression(e) => Self::try_fold_binary_expr(e, ctx), + Expression::BinaryExpression(e) => Self::try_fold_binary_expr(e, ctx) + .or_else(|| Self::try_fold_binary_typeof_comparison(e, ctx)), Expression::UnaryExpression(e) => Self::try_fold_unary_expr(e, ctx), Expression::StaticMemberExpression(e) => Self::try_fold_static_member_expr(e, ctx), Expression::LogicalExpression(e) => Self::try_fold_logical_expr(e, ctx), @@ -624,6 +625,55 @@ impl<'a, 'b> PeepholeFoldConstants { .to_js_string() .map(|value| ctx.ast.expression_string_literal(object.span(), value, None)) } + + // `typeof a === typeof b` -> `typeof a == typeof b`, `typeof a != typeof b` -> `typeof a != typeof b`, + // `typeof a == typeof a` -> `true`, `typeof a != typeof a` -> `false` + fn try_fold_binary_typeof_comparison( + bin_expr: &mut BinaryExpression<'a>, + ctx: Ctx<'a, 'b>, + ) -> Option<Expression<'a>> { + if bin_expr.operator.is_equality() { + if let (Expression::UnaryExpression(left), Expression::UnaryExpression(right)) = + (&bin_expr.left, &bin_expr.right) + { + if left.operator.is_typeof() && right.operator.is_typeof() { + if let ( + Expression::Identifier(left_ident), + Expression::Identifier(right_ident), + ) = (&left.argument, &right.argument) + { + if left_ident.name == right_ident.name { + return Some(ctx.ast.expression_boolean_literal( + bin_expr.span, + matches!( + bin_expr.operator, + BinaryOperator::StrictEquality | BinaryOperator::Equality + ), + )); + } + } + + if matches!( + bin_expr.operator, + BinaryOperator::StrictEquality | BinaryOperator::StrictInequality + ) { + return Some(ctx.ast.expression_binary( + bin_expr.span, + ctx.ast.move_expression(&mut bin_expr.left), + if bin_expr.operator == BinaryOperator::StrictEquality { + BinaryOperator::Equality + } else { + BinaryOperator::Inequality + }, + ctx.ast.move_expression(&mut bin_expr.right), + )); + } + } + } + } + + None + } } /// <https://github.com/google/closure-compiler/blob/v20240609/test/com/google/javascript/jscomp/PeepholeFoldConstantsTest.java> @@ -931,11 +981,8 @@ mod test { test("'a' == 'a'", "true"); test("'b' != 'a'", "true"); test_same("typeof a != 'number'"); - test_same("typeof a == typeof a"); test("'a' === 'a'", "true"); test("'b' !== 'a'", "true"); - test_same("typeof a === typeof a"); - test_same("typeof a !== typeof a"); test_same("'' + x <= '' + y"); test_same("'' + x != '' + y"); test_same("'' + x === '' + y"); @@ -1797,4 +1844,12 @@ mod test { test("typeof foo + ''", "typeof foo"); test_same("typeof foo - ''"); } + + #[test] + fn test_fold_same_typeof() { + test("typeof foo === typeof bar", "typeof foo == typeof bar"); + test("typeof foo !== typeof bar", "typeof foo != typeof bar"); + test("typeof foo.bar === typeof foo.bar", "typeof foo.bar == typeof foo.bar"); + test("typeof foo.bar !== typeof foo.bar", "typeof foo.bar != typeof foo.bar"); + } } diff --git a/tasks/minsize/minsize.snap b/tasks/minsize/minsize.snap index f78847f3d6fca..084a421f0306e 100644 --- a/tasks/minsize/minsize.snap +++ b/tasks/minsize/minsize.snap @@ -13,7 +13,7 @@ Original | minified | minified | gzip | gzip | Fixture 555.77 kB | 273.15 kB | 270.13 kB | 90.95 kB | 90.80 kB | d3.js -1.01 MB | 460.32 kB | 458.89 kB | 126.84 kB | 126.71 kB | bundle.min.js +1.01 MB | 460.31 kB | 458.89 kB | 126.84 kB | 126.71 kB | bundle.min.js 1.25 MB | 652.68 kB | 646.76 kB | 163.53 kB | 163.73 kB | three.js