Skip to content

Commit ffbc5c2

Browse files
committed
remove canonicalization of equality order
1 parent 4a00ba9 commit ffbc5c2

File tree

3 files changed

+103
-77
lines changed

3 files changed

+103
-77
lines changed

internal/bundler/snapshots/snapshots_dce.txt

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -358,22 +358,14 @@ TestDCETypeOfEqualsStringGuardCondition
358358
var keep_1 = typeof x != "object" && x;
359359
var keep_1 = typeof x === "object" || x;
360360
var keep_1 = typeof x == "object" || x;
361-
var keep_1 = typeof x !== "object" ? x : null;
362-
var keep_1 = typeof x != "object" ? x : null;
363-
var keep_1 = typeof x === "object" ? null : x;
364-
var keep_1 = typeof x == "object" ? null : x;
365-
var keep_1 = typeof x !== "object" && x;
366-
var keep_1 = typeof x != "object" && x;
367-
var keep_1 = typeof x === "object" || x;
368-
var keep_1 = typeof x == "object" || x;
369-
var keep_2 = typeof x !== "undefined" ? y : null;
370-
var keep_2 = typeof x != "undefined" ? y : null;
371-
var keep_2 = typeof x === "undefined" ? null : y;
372-
var keep_2 = typeof x == "undefined" ? null : y;
373-
var keep_2 = typeof x !== "undefined" && y;
374-
var keep_2 = typeof x != "undefined" && y;
375-
var keep_2 = typeof x === "undefined" || y;
376-
var keep_2 = typeof x == "undefined" || y;
361+
var keep_1 = "object" !== typeof x ? x : null;
362+
var keep_1 = "object" != typeof x ? x : null;
363+
var keep_1 = "object" === typeof x ? null : x;
364+
var keep_1 = "object" == typeof x ? null : x;
365+
var keep_1 = "object" !== typeof x && x;
366+
var keep_1 = "object" != typeof x && x;
367+
var keep_1 = "object" === typeof x || x;
368+
var keep_1 = "object" == typeof x || x;
377369
var keep_2 = typeof x !== "undefined" ? y : null;
378370
var keep_2 = typeof x != "undefined" ? y : null;
379371
var keep_2 = typeof x === "undefined" ? null : y;
@@ -382,6 +374,14 @@ TestDCETypeOfEqualsStringGuardCondition
382374
var keep_2 = typeof x != "undefined" && y;
383375
var keep_2 = typeof x === "undefined" || y;
384376
var keep_2 = typeof x == "undefined" || y;
377+
var keep_2 = "undefined" !== typeof x ? y : null;
378+
var keep_2 = "undefined" != typeof x ? y : null;
379+
var keep_2 = "undefined" === typeof x ? null : y;
380+
var keep_2 = "undefined" == typeof x ? null : y;
381+
var keep_2 = "undefined" !== typeof x && y;
382+
var keep_2 = "undefined" != typeof x && y;
383+
var keep_2 = "undefined" === typeof x || y;
384+
var keep_2 = "undefined" == typeof x || y;
385385
var keep_3 = typeof x !== "undefined" ? null : x;
386386
var keep_3 = typeof x != "undefined" ? null : x;
387387
var keep_3 = typeof x === "undefined" ? x : null;
@@ -390,14 +390,14 @@ TestDCETypeOfEqualsStringGuardCondition
390390
var keep_3 = typeof x != "undefined" || x;
391391
var keep_3 = typeof x === "undefined" && x;
392392
var keep_3 = typeof x == "undefined" && x;
393-
var keep_3 = typeof x !== "undefined" ? null : x;
394-
var keep_3 = typeof x != "undefined" ? null : x;
395-
var keep_3 = typeof x === "undefined" ? x : null;
396-
var keep_3 = typeof x == "undefined" ? x : null;
397-
var keep_3 = typeof x !== "undefined" || x;
398-
var keep_3 = typeof x != "undefined" || x;
399-
var keep_3 = typeof x === "undefined" && x;
400-
var keep_3 = typeof x == "undefined" && x;
393+
var keep_3 = "undefined" !== typeof x ? null : x;
394+
var keep_3 = "undefined" != typeof x ? null : x;
395+
var keep_3 = "undefined" === typeof x ? x : null;
396+
var keep_3 = "undefined" == typeof x ? x : null;
397+
var keep_3 = "undefined" !== typeof x || x;
398+
var keep_3 = "undefined" != typeof x || x;
399+
var keep_3 = "undefined" === typeof x && x;
400+
var keep_3 = "undefined" == typeof x && x;
401401
})();
402402

403403
================================================================================

internal/js_parser/js_parser.go

Lines changed: 61 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -9666,7 +9666,7 @@ func (p *parser) visitAndAppendStmt(stmts []js_ast.Stmt, stmt js_ast.Stmt) []js_
96669666
if c.ValueOrNil.Data != nil {
96679667
c.ValueOrNil = p.visitExpr(c.ValueOrNil)
96689668
p.warnAboutEqualityCheck("case", c.ValueOrNil, c.ValueOrNil.Loc)
9669-
p.warnAboutTypeofAndString(s.Test, c.ValueOrNil)
9669+
p.warnAboutTypeofAndString(s.Test, c.ValueOrNil, onlyCheckOriginalOrder)
96709670
}
96719671
c.Body = p.visitStmts(c.Body, stmtsNormal)
96729672

@@ -10820,7 +10820,20 @@ func (p *parser) checkForUnrepresentableIdentifier(loc logger.Loc, name string)
1082010820
}
1082110821
}
1082210822

10823-
func (p *parser) warnAboutTypeofAndString(a js_ast.Expr, b js_ast.Expr) {
10823+
type typeofStringOrder uint8
10824+
10825+
const (
10826+
onlyCheckOriginalOrder typeofStringOrder = iota
10827+
checkBothOrders
10828+
)
10829+
10830+
func (p *parser) warnAboutTypeofAndString(a js_ast.Expr, b js_ast.Expr, order typeofStringOrder) {
10831+
if order == checkBothOrders {
10832+
if _, ok := a.Data.(*js_ast.EString); ok {
10833+
a, b = b, a
10834+
}
10835+
}
10836+
1082410837
if typeof, ok := a.Data.(*js_ast.EUnary); ok && typeof.Op == js_ast.UnOpTypeof {
1082510838
if str, ok := b.Data.(*js_ast.EString); ok {
1082610839
value := helpers.UTF16ToString(str.Value)
@@ -10856,15 +10869,22 @@ func canChangeStrictToLoose(a js_ast.Expr, b js_ast.Expr) bool {
1085610869
}
1085710870

1085810871
func (p *parser) maybeSimplifyEqualityComparison(loc logger.Loc, e *js_ast.EBinary) (js_ast.Expr, bool) {
10872+
value, primitive := e.Left, e.Right
10873+
10874+
// Detect when the primitive comes first and flip the order of our checks
10875+
if isPrimitiveLiteral(value.Data) {
10876+
value, primitive = primitive, value
10877+
}
10878+
1085910879
// "!x === true" => "!x"
1086010880
// "!x === false" => "!!x"
1086110881
// "!x !== true" => "!!x"
1086210882
// "!x !== false" => "!x"
10863-
if boolean, ok := e.Right.Data.(*js_ast.EBoolean); ok && js_ast.KnownPrimitiveType(e.Left) == js_ast.PrimitiveBoolean {
10883+
if boolean, ok := primitive.Data.(*js_ast.EBoolean); ok && js_ast.KnownPrimitiveType(value) == js_ast.PrimitiveBoolean {
1086410884
if boolean.Value == (e.Op == js_ast.BinOpLooseNe || e.Op == js_ast.BinOpStrictNe) {
10865-
return js_ast.Not(e.Left), true
10885+
return js_ast.Not(value), true
1086610886
} else {
10867-
return e.Left, true
10887+
return value, true
1086810888
}
1086910889
}
1087010890

@@ -10875,17 +10895,18 @@ func (p *parser) maybeSimplifyEqualityComparison(loc logger.Loc, e *js_ast.EBina
1087510895
// return something random. The only case of this happening was Internet
1087610896
// Explorer returning "unknown" for some objects, which messes with this
1087710897
// optimization. So we don't do this when targeting Internet Explorer.
10878-
if typeof, ok := e.Left.Data.(*js_ast.EUnary); ok && typeof.Op == js_ast.UnOpTypeof {
10879-
if str, ok := e.Right.Data.(*js_ast.EString); ok && helpers.UTF16EqualsString(str.Value, "undefined") {
10898+
if typeof, ok := value.Data.(*js_ast.EUnary); ok && typeof.Op == js_ast.UnOpTypeof {
10899+
if str, ok := primitive.Data.(*js_ast.EString); ok && helpers.UTF16EqualsString(str.Value, "undefined") {
10900+
flip := value == e.Right
1088010901
op := js_ast.BinOpLt
10881-
if e.Op == js_ast.BinOpLooseEq || e.Op == js_ast.BinOpStrictEq {
10902+
if (e.Op == js_ast.BinOpLooseEq || e.Op == js_ast.BinOpStrictEq) != flip {
1088210903
op = js_ast.BinOpGt
1088310904
}
10884-
return js_ast.Expr{Loc: loc, Data: &js_ast.EBinary{
10885-
Op: op,
10886-
Left: e.Left,
10887-
Right: js_ast.Expr{Loc: e.Right.Loc, Data: &js_ast.EString{Value: []uint16{'u'}}},
10888-
}}, true
10905+
primitive.Data = &js_ast.EString{Value: []uint16{'u'}}
10906+
if flip {
10907+
value, primitive = primitive, value
10908+
}
10909+
return js_ast.Expr{Loc: loc, Data: &js_ast.EBinary{Op: op, Left: value, Right: primitive}}, true
1088910910
}
1089010911
}
1089110912
}
@@ -11477,18 +11498,29 @@ func stringToEquivalentNumberValue(value []uint16) (float64, bool) {
1147711498
func isBinaryNullAndUndefined(left js_ast.Expr, right js_ast.Expr, op js_ast.OpCode) (js_ast.Expr, js_ast.Expr, bool) {
1147811499
if a, ok := left.Data.(*js_ast.EBinary); ok && a.Op == op {
1147911500
if b, ok := right.Data.(*js_ast.EBinary); ok && b.Op == op {
11480-
if idA, ok := a.Left.Data.(*js_ast.EIdentifier); ok {
11481-
if idB, ok := b.Left.Data.(*js_ast.EIdentifier); ok && idA.Ref == idB.Ref {
11501+
idA, eqA := a.Left, a.Right
11502+
idB, eqB := b.Left, b.Right
11503+
11504+
// Detect when the identifier comes second and flip the order of our checks
11505+
if _, ok := eqA.Data.(*js_ast.EIdentifier); ok {
11506+
idA, eqA = eqA, idA
11507+
}
11508+
if _, ok := eqB.Data.(*js_ast.EIdentifier); ok {
11509+
idB, eqB = eqB, idB
11510+
}
11511+
11512+
if idA, ok := idA.Data.(*js_ast.EIdentifier); ok {
11513+
if idB, ok := idB.Data.(*js_ast.EIdentifier); ok && idA.Ref == idB.Ref {
1148211514
// "a === null || a === void 0"
11483-
if _, ok := a.Right.Data.(*js_ast.ENull); ok {
11484-
if _, ok := b.Right.Data.(*js_ast.EUndefined); ok {
11515+
if _, ok := eqA.Data.(*js_ast.ENull); ok {
11516+
if _, ok := eqB.Data.(*js_ast.EUndefined); ok {
1148511517
return a.Left, a.Right, true
1148611518
}
1148711519
}
1148811520

1148911521
// "a === void 0 || a === null"
11490-
if _, ok := a.Right.Data.(*js_ast.EUndefined); ok {
11491-
if _, ok := b.Right.Data.(*js_ast.ENull); ok {
11522+
if _, ok := eqA.Data.(*js_ast.EUndefined); ok {
11523+
if _, ok := eqB.Data.(*js_ast.ENull); ok {
1149211524
return b.Left, b.Right, true
1149311525
}
1149411526
}
@@ -12164,16 +12196,6 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO
1216412196
}
1216512197
p.fnOnlyDataVisit.silenceWarningAboutThisBeingUndefined = oldSilenceWarningAboutThisBeingUndefined
1216612198

12167-
// Always put constants on the right for equality comparisons to help
12168-
// reduce the number of cases we have to check during pattern matching. We
12169-
// can only reorder expressions that do not have any side effects.
12170-
switch e.Op {
12171-
case js_ast.BinOpLooseEq, js_ast.BinOpLooseNe, js_ast.BinOpStrictEq, js_ast.BinOpStrictNe:
12172-
if isPrimitiveLiteral(e.Left.Data) && !isPrimitiveLiteral(e.Right.Data) {
12173-
e.Left, e.Right = e.Right, e.Left
12174-
}
12175-
}
12176-
1217712199
// Post-process the binary expression
1217812200
switch e.Op {
1217912201
case js_ast.BinOpComma:
@@ -12200,11 +12222,13 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO
1220012222
if !p.warnAboutEqualityCheck("==", e.Left, afterOpLoc) {
1220112223
p.warnAboutEqualityCheck("==", e.Right, afterOpLoc)
1220212224
}
12203-
p.warnAboutTypeofAndString(e.Left, e.Right)
12225+
p.warnAboutTypeofAndString(e.Left, e.Right, checkBothOrders)
1220412226

1220512227
if p.options.minifySyntax {
1220612228
// "x == void 0" => "x == null"
12207-
if _, ok := e.Right.Data.(*js_ast.EUndefined); ok {
12229+
if _, ok := e.Left.Data.(*js_ast.EUndefined); ok {
12230+
e.Left.Data = js_ast.ENullShared
12231+
} else if _, ok := e.Right.Data.(*js_ast.EUndefined); ok {
1220812232
e.Right.Data = js_ast.ENullShared
1220912233
}
1221012234

@@ -12221,7 +12245,7 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO
1222112245
if !p.warnAboutEqualityCheck("===", e.Left, afterOpLoc) {
1222212246
p.warnAboutEqualityCheck("===", e.Right, afterOpLoc)
1222312247
}
12224-
p.warnAboutTypeofAndString(e.Left, e.Right)
12248+
p.warnAboutTypeofAndString(e.Left, e.Right, checkBothOrders)
1222512249

1222612250
if p.options.minifySyntax {
1222712251
// "typeof x === 'undefined'" => "typeof x == 'undefined'"
@@ -12242,11 +12266,13 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO
1224212266
if !p.warnAboutEqualityCheck("!=", e.Left, afterOpLoc) {
1224312267
p.warnAboutEqualityCheck("!=", e.Right, afterOpLoc)
1224412268
}
12245-
p.warnAboutTypeofAndString(e.Left, e.Right)
12269+
p.warnAboutTypeofAndString(e.Left, e.Right, checkBothOrders)
1224612270

1224712271
if p.options.minifySyntax {
1224812272
// "x != void 0" => "x != null"
12249-
if _, ok := e.Right.Data.(*js_ast.EUndefined); ok {
12273+
if _, ok := e.Left.Data.(*js_ast.EUndefined); ok {
12274+
e.Left.Data = js_ast.ENullShared
12275+
} else if _, ok := e.Right.Data.(*js_ast.EUndefined); ok {
1225012276
e.Right.Data = js_ast.ENullShared
1225112277
}
1225212278

@@ -12263,7 +12289,7 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO
1226312289
if !p.warnAboutEqualityCheck("!==", e.Left, afterOpLoc) {
1226412290
p.warnAboutEqualityCheck("!==", e.Right, afterOpLoc)
1226512291
}
12266-
p.warnAboutTypeofAndString(e.Left, e.Right)
12292+
p.warnAboutTypeofAndString(e.Left, e.Right, checkBothOrders)
1226712293

1226812294
if p.options.minifySyntax {
1226912295
// "typeof x !== 'undefined'" => "typeof x != 'undefined'"

internal/js_parser/js_parser_test.go

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3172,20 +3172,20 @@ func TestMangleIf(t *testing.T) {
31723172

31733173
expectPrintedMangle(t, "let b; a = null == b ? c : b", "let b;\na = b ?? c;\n")
31743174
expectPrintedMangle(t, "let b; a = null != b ? b : c", "let b;\na = b ?? c;\n")
3175-
expectPrintedMangle(t, "let b; a = null == b ? b : c", "let b;\na = b == null ? b : c;\n")
3176-
expectPrintedMangle(t, "let b; a = null != b ? c : b", "let b;\na = b != null ? c : b;\n")
3175+
expectPrintedMangle(t, "let b; a = null == b ? b : c", "let b;\na = null == b ? b : c;\n")
3176+
expectPrintedMangle(t, "let b; a = null != b ? c : b", "let b;\na = null != b ? c : b;\n")
31773177

31783178
// Don't do this if the condition has side effects
31793179
expectPrintedMangle(t, "let b; a = b.x == null ? c : b.x", "let b;\na = b.x == null ? c : b.x;\n")
31803180
expectPrintedMangle(t, "let b; a = b.x != null ? b.x : c", "let b;\na = b.x != null ? b.x : c;\n")
3181-
expectPrintedMangle(t, "let b; a = null == b.x ? c : b.x", "let b;\na = b.x == null ? c : b.x;\n")
3182-
expectPrintedMangle(t, "let b; a = null != b.x ? b.x : c", "let b;\na = b.x != null ? b.x : c;\n")
3181+
expectPrintedMangle(t, "let b; a = null == b.x ? c : b.x", "let b;\na = null == b.x ? c : b.x;\n")
3182+
expectPrintedMangle(t, "let b; a = null != b.x ? b.x : c", "let b;\na = null != b.x ? b.x : c;\n")
31833183

31843184
// Don't do this for strict equality comparisons
31853185
expectPrintedMangle(t, "let b; a = b === null ? c : b", "let b;\na = b === null ? c : b;\n")
31863186
expectPrintedMangle(t, "let b; a = b !== null ? b : c", "let b;\na = b !== null ? b : c;\n")
3187-
expectPrintedMangle(t, "let b; a = null === b ? c : b", "let b;\na = b === null ? c : b;\n")
3188-
expectPrintedMangle(t, "let b; a = null !== b ? b : c", "let b;\na = b !== null ? b : c;\n")
3187+
expectPrintedMangle(t, "let b; a = null === b ? c : b", "let b;\na = null === b ? c : b;\n")
3188+
expectPrintedMangle(t, "let b; a = null !== b ? b : c", "let b;\na = null !== b ? b : c;\n")
31893189

31903190
expectPrintedMangle(t, "let b; a = null === b || b === undefined ? c : b", "let b;\na = b ?? c;\n")
31913191
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) {
33173317

33183318
expectPrintedMangle(t, "a == null && a.b()", "a == null && a.b();\n")
33193319
expectPrintedMangle(t, "a != null || a.b()", "a != null || a.b();\n")
3320-
expectPrintedMangle(t, "null == a && a.b()", "a == null && a.b();\n")
3321-
expectPrintedMangle(t, "null != a || a.b()", "a != null || a.b();\n")
3320+
expectPrintedMangle(t, "null == a && a.b()", "null == a && a.b();\n")
3321+
expectPrintedMangle(t, "null != a || a.b()", "null != a || a.b();\n")
33223322

33233323
expectPrintedMangle(t, "x = a != null && a.b()", "x = a != null && a.b();\n")
33243324
expectPrintedMangle(t, "x = a == null || a.b()", "x = a == null || a.b();\n")
@@ -3746,13 +3746,13 @@ func TestMangleTypeofIdentifier(t *testing.T) {
37463746
func TestMangleTypeofEqualsUndefined(t *testing.T) {
37473747
expectPrintedMangle(t, "return typeof x !== 'undefined'", "return typeof x < \"u\";\n")
37483748
expectPrintedMangle(t, "return typeof x != 'undefined'", "return typeof x < \"u\";\n")
3749-
expectPrintedMangle(t, "return 'undefined' !== typeof x", "return typeof x < \"u\";\n")
3750-
expectPrintedMangle(t, "return 'undefined' != typeof x", "return typeof x < \"u\";\n")
3749+
expectPrintedMangle(t, "return 'undefined' !== typeof x", "return \"u\" > typeof x;\n")
3750+
expectPrintedMangle(t, "return 'undefined' != typeof x", "return \"u\" > typeof x;\n")
37513751

37523752
expectPrintedMangle(t, "return typeof x === 'undefined'", "return typeof x > \"u\";\n")
37533753
expectPrintedMangle(t, "return typeof x == 'undefined'", "return typeof x > \"u\";\n")
3754-
expectPrintedMangle(t, "return 'undefined' === typeof x", "return typeof x > \"u\";\n")
3755-
expectPrintedMangle(t, "return 'undefined' == typeof x", "return typeof x > \"u\";\n")
3754+
expectPrintedMangle(t, "return 'undefined' === typeof x", "return \"u\" < typeof x;\n")
3755+
expectPrintedMangle(t, "return 'undefined' == typeof x", "return \"u\" < typeof x;\n")
37563756
}
37573757

37583758
func TestMangleEquals(t *testing.T) {
@@ -3763,8 +3763,8 @@ func TestMangleEquals(t *testing.T) {
37633763

37643764
expectPrintedMangle(t, "return typeof x === 'string'", "return typeof x == \"string\";\n")
37653765
expectPrintedMangle(t, "return typeof x !== 'string'", "return typeof x != \"string\";\n")
3766-
expectPrintedMangle(t, "return 'string' === typeof x", "return typeof x == \"string\";\n")
3767-
expectPrintedMangle(t, "return 'string' !== typeof x", "return typeof x != \"string\";\n")
3766+
expectPrintedMangle(t, "return 'string' === typeof x", "return \"string\" == typeof x;\n")
3767+
expectPrintedMangle(t, "return 'string' !== typeof x", "return \"string\" != typeof x;\n")
37683768

37693769
expectPrintedMangle(t, "return a === 0", "return a === 0;\n")
37703770
expectPrintedMangle(t, "return a !== 0", "return a !== 0;\n")
@@ -3890,13 +3890,13 @@ func TestMangleNestedLogical(t *testing.T) {
38903890
func TestMangleEqualsUndefined(t *testing.T) {
38913891
expectPrintedMangle(t, "return a === void 0", "return a === void 0;\n")
38923892
expectPrintedMangle(t, "return a !== void 0", "return a !== void 0;\n")
3893-
expectPrintedMangle(t, "return void 0 === a", "return a === void 0;\n")
3894-
expectPrintedMangle(t, "return void 0 !== a", "return a !== void 0;\n")
3893+
expectPrintedMangle(t, "return void 0 === a", "return void 0 === a;\n")
3894+
expectPrintedMangle(t, "return void 0 !== a", "return void 0 !== a;\n")
38953895

38963896
expectPrintedMangle(t, "return a == void 0", "return a == null;\n")
38973897
expectPrintedMangle(t, "return a != void 0", "return a != null;\n")
3898-
expectPrintedMangle(t, "return void 0 == a", "return a == null;\n")
3899-
expectPrintedMangle(t, "return void 0 != a", "return a != null;\n")
3898+
expectPrintedMangle(t, "return void 0 == a", "return null == a;\n")
3899+
expectPrintedMangle(t, "return void 0 != a", "return null != a;\n")
39003900

39013901
expectPrintedMangle(t, "return a === null || a === undefined", "return a == null;\n")
39023902
expectPrintedMangle(t, "return a === null || a !== undefined", "return a === null || a !== void 0;\n")

0 commit comments

Comments
 (0)