Skip to content

Commit

Permalink
feat(minifier): minify typeof in binary expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
camc314 committed Jan 6, 2025
1 parent afb9b8b commit bb5e30c
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 5 deletions.
63 changes: 59 additions & 4 deletions crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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");
}
}
2 changes: 1 addition & 1 deletion tasks/minsize/minsize.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down

0 comments on commit bb5e30c

Please sign in to comment.