From 580ad40a9dd050b67916cb3453faf4d0407d2e62 Mon Sep 17 00:00:00 2001 From: Evan Wallace Date: Wed, 6 Jul 2022 02:33:54 -0400 Subject: [PATCH] experiment with equality operand orderings (#2364) --- internal/js_parser/js_parser.go | 20 ++++++++++++++++ internal/js_parser/js_parser_test.go | 36 ++++++++++++++-------------- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/internal/js_parser/js_parser.go b/internal/js_parser/js_parser.go index 4a213d691c4..18e6c7e149c 100644 --- a/internal/js_parser/js_parser.go +++ b/internal/js_parser/js_parser.go @@ -12196,6 +12196,26 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO } p.fnOnlyDataVisit.silenceWarningAboutThisBeingUndefined = oldSilenceWarningAboutThisBeingUndefined + // Always put constants consistently on the same side for equality + // comparisons to help improve compression. In theory, dictionary-based + // compression methods may already have a dictionary entry for code that + // is similar to previous code. Note that we can only reorder expressions + // that do not have any side effects. + // + // Constants are currently ordered on the right instead of the left because + // it results in slightly smalller gzip size on our primary benchmark + // (although slightly larger uncompressed size). The size difference is + // less than 0.1% so it really isn't that important an optimization. + if p.options.minifySyntax { + switch e.Op { + case js_ast.BinOpLooseEq, js_ast.BinOpLooseNe, js_ast.BinOpStrictEq, js_ast.BinOpStrictNe: + // "1 === x" => "x === 1" + if isPrimitiveLiteral(e.Left.Data) && !isPrimitiveLiteral(e.Right.Data) { + e.Left, e.Right = e.Right, e.Left + } + } + } + // Post-process the binary expression switch e.Op { case js_ast.BinOpComma: diff --git a/internal/js_parser/js_parser_test.go b/internal/js_parser/js_parser_test.go index ca401326144..975a7a5ee86 100644 --- a/internal/js_parser/js_parser_test.go +++ b/internal/js_parser/js_parser_test.go @@ -3172,20 +3172,20 @@ func TestMangleIf(t *testing.T) { expectPrintedMangle(t, "let b; a = null == b ? c : b", "let b;\na = b ?? c;\n") expectPrintedMangle(t, "let b; a = null != b ? b : c", "let b;\na = b ?? c;\n") - expectPrintedMangle(t, "let b; a = null == b ? b : c", "let b;\na = null == b ? b : c;\n") - expectPrintedMangle(t, "let b; a = null != b ? c : b", "let b;\na = null != b ? c : b;\n") + expectPrintedMangle(t, "let b; a = null == b ? b : c", "let b;\na = b == null ? b : c;\n") + expectPrintedMangle(t, "let b; a = null != b ? c : b", "let b;\na = b != null ? c : b;\n") // Don't do this if the condition has side effects expectPrintedMangle(t, "let b; a = b.x == null ? c : b.x", "let b;\na = b.x == null ? c : b.x;\n") expectPrintedMangle(t, "let b; a = b.x != null ? b.x : c", "let b;\na = b.x != null ? b.x : c;\n") - expectPrintedMangle(t, "let b; a = null == b.x ? c : b.x", "let b;\na = null == b.x ? c : b.x;\n") - expectPrintedMangle(t, "let b; a = null != b.x ? b.x : c", "let b;\na = null != b.x ? b.x : c;\n") + expectPrintedMangle(t, "let b; a = null == b.x ? c : b.x", "let b;\na = b.x == null ? c : b.x;\n") + expectPrintedMangle(t, "let b; a = null != b.x ? b.x : c", "let b;\na = b.x != null ? b.x : c;\n") // Don't do this for strict equality comparisons expectPrintedMangle(t, "let b; a = b === null ? c : b", "let b;\na = b === null ? c : b;\n") expectPrintedMangle(t, "let b; a = b !== null ? b : c", "let b;\na = b !== null ? b : c;\n") - expectPrintedMangle(t, "let b; a = null === b ? c : b", "let b;\na = null === b ? c : b;\n") - expectPrintedMangle(t, "let b; a = null !== b ? b : c", "let b;\na = null !== b ? b : c;\n") + expectPrintedMangle(t, "let b; a = null === b ? c : b", "let b;\na = b === null ? c : b;\n") + expectPrintedMangle(t, "let b; a = null !== b ? b : c", "let b;\na = b !== null ? b : c;\n") expectPrintedMangle(t, "let b; a = null === b || b === undefined ? c : b", "let b;\na = b ?? c;\n") expectPrintedMangle(t, "let b; a = b !== undefined && b !== null ? b : c", "let b;\na = b ?? c;\n") @@ -3317,8 +3317,8 @@ func TestMangleOptionalChain(t *testing.T) { expectPrintedMangle(t, "a == null && a.b()", "a == null && a.b();\n") expectPrintedMangle(t, "a != null || a.b()", "a != null || a.b();\n") - expectPrintedMangle(t, "null == a && a.b()", "null == a && a.b();\n") - expectPrintedMangle(t, "null != a || a.b()", "null != a || a.b();\n") + expectPrintedMangle(t, "null == a && a.b()", "a == null && a.b();\n") + expectPrintedMangle(t, "null != a || a.b()", "a != null || a.b();\n") expectPrintedMangle(t, "x = a != null && a.b()", "x = a != null && a.b();\n") expectPrintedMangle(t, "x = a == null || a.b()", "x = a == null || a.b();\n") @@ -3746,13 +3746,13 @@ func TestMangleTypeofIdentifier(t *testing.T) { func TestMangleTypeofEqualsUndefined(t *testing.T) { expectPrintedMangle(t, "return typeof x !== 'undefined'", "return typeof x < \"u\";\n") expectPrintedMangle(t, "return typeof x != 'undefined'", "return typeof x < \"u\";\n") - expectPrintedMangle(t, "return 'undefined' !== typeof x", "return \"u\" > typeof x;\n") - expectPrintedMangle(t, "return 'undefined' != typeof x", "return \"u\" > typeof x;\n") + expectPrintedMangle(t, "return 'undefined' !== typeof x", "return typeof x < \"u\";\n") + expectPrintedMangle(t, "return 'undefined' != typeof x", "return typeof x < \"u\";\n") expectPrintedMangle(t, "return typeof x === 'undefined'", "return typeof x > \"u\";\n") expectPrintedMangle(t, "return typeof x == 'undefined'", "return typeof x > \"u\";\n") - expectPrintedMangle(t, "return 'undefined' === typeof x", "return \"u\" < typeof x;\n") - expectPrintedMangle(t, "return 'undefined' == typeof x", "return \"u\" < typeof x;\n") + expectPrintedMangle(t, "return 'undefined' === typeof x", "return typeof x > \"u\";\n") + expectPrintedMangle(t, "return 'undefined' == typeof x", "return typeof x > \"u\";\n") } func TestMangleEquals(t *testing.T) { @@ -3763,8 +3763,8 @@ func TestMangleEquals(t *testing.T) { expectPrintedMangle(t, "return typeof x === 'string'", "return typeof x == \"string\";\n") expectPrintedMangle(t, "return typeof x !== 'string'", "return typeof x != \"string\";\n") - expectPrintedMangle(t, "return 'string' === typeof x", "return \"string\" == typeof x;\n") - expectPrintedMangle(t, "return 'string' !== typeof x", "return \"string\" != typeof x;\n") + expectPrintedMangle(t, "return 'string' === typeof x", "return typeof x == \"string\";\n") + expectPrintedMangle(t, "return 'string' !== typeof x", "return typeof x != \"string\";\n") expectPrintedMangle(t, "return a === 0", "return a === 0;\n") expectPrintedMangle(t, "return a !== 0", "return a !== 0;\n") @@ -3890,13 +3890,13 @@ func TestMangleNestedLogical(t *testing.T) { func TestMangleEqualsUndefined(t *testing.T) { expectPrintedMangle(t, "return a === void 0", "return a === void 0;\n") expectPrintedMangle(t, "return a !== void 0", "return a !== void 0;\n") - expectPrintedMangle(t, "return void 0 === a", "return void 0 === a;\n") - expectPrintedMangle(t, "return void 0 !== a", "return void 0 !== a;\n") + expectPrintedMangle(t, "return void 0 === a", "return a === void 0;\n") + expectPrintedMangle(t, "return void 0 !== a", "return a !== void 0;\n") expectPrintedMangle(t, "return a == void 0", "return a == null;\n") expectPrintedMangle(t, "return a != void 0", "return a != null;\n") - expectPrintedMangle(t, "return void 0 == a", "return null == a;\n") - expectPrintedMangle(t, "return void 0 != a", "return null != a;\n") + expectPrintedMangle(t, "return void 0 == a", "return a == null;\n") + expectPrintedMangle(t, "return void 0 != a", "return a != null;\n") expectPrintedMangle(t, "return a === null || a === undefined", "return a == null;\n") expectPrintedMangle(t, "return a === null || a !== undefined", "return a === null || a !== void 0;\n")