diff options
author | Alex Lam S.L <alexlamsl@gmail.com> | 2021-02-23 21:57:11 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-02-24 05:57:11 +0800 |
commit | c88566034756eb17c4ff563901b3a1c95b63f788 (patch) | |
tree | 24e376db8595b8152b2685708ee08b7f9e194758 /lib | |
parent | d68d155f93a355a9f6f0451150186b7fad8c58b8 (diff) | |
download | tracifyjs-c88566034756eb17c4ff563901b3a1c95b63f788.tar.gz tracifyjs-c88566034756eb17c4ff563901b3a1c95b63f788.zip |
support nullish coalescing operator (#4678)
Diffstat (limited to 'lib')
-rw-r--r-- | lib/compress.js | 46 | ||||
-rw-r--r-- | lib/output.js | 4 | ||||
-rw-r--r-- | lib/parse.js | 4 |
3 files changed, 41 insertions, 13 deletions
diff --git a/lib/compress.js b/lib/compress.js index dcd48fa8..7f9a8dd5 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -2182,6 +2182,7 @@ merge(Compressor.prototype, { var lazy = lazy_op[expr.operator]; if (unused && lazy + && expr.operator != "??" && expr.right instanceof AST_Assign && expr.right.operator == "=" && !(expr.right.left instanceof AST_Destructured)) { @@ -3840,7 +3841,7 @@ merge(Compressor.prototype, { node.DEFMETHOD("is_string", func); }); - var lazy_op = makePredicate("&& ||"); + var lazy_op = makePredicate("&& || ??"); (function(def) { function to_node(value, orig) { @@ -4286,6 +4287,9 @@ merge(Compressor.prototype, { switch (this.operator) { case "&&" : result = left && right; break; case "||" : result = left || right; break; + case "??" : + result = left == null ? right : left; + break; case "|" : result = left | right; break; case "&" : result = left & right; break; case "^" : result = left ^ right; break; @@ -7130,6 +7134,7 @@ merge(Compressor.prototype, { node = this.clone(); node.right = right.drop_side_effect_free(compressor); } + if (this.operator == "??") return node; return (first_in_statement ? best_of_statement : best_of_expression)(node, make_node(AST_Binary, this, { operator: node.operator == "&&" ? "||" : "&&", left: node.left.negate(compressor, first_in_statement), @@ -9159,7 +9164,7 @@ merge(Compressor.prototype, { // (a = b, x || a = c) ---> a = x ? b : c function to_conditional_assignment(compressor, def, value, node) { if (!(node instanceof AST_Binary)) return; - if (!lazy_op[node.operator]) return; + if (!(node.operator == "&&" || node.operator == "||")) return; if (!(node.right instanceof AST_Assign)) return; if (node.right.operator != "=") return; if (!(node.right.left instanceof AST_SymbolRef)) return; @@ -9680,22 +9685,41 @@ merge(Compressor.prototype, { }).optimize(compressor); } break; + case "??": + var nullish = true; case "||": - var ll = fuzzy_eval(self.left); - if (!ll) { - AST_Node.warn("Condition left of || always false [{file}:{line},{col}]", self.start); + var ll = fuzzy_eval(self.left, nullish); + if (nullish ? ll == null : !ll) { + AST_Node.warn("Condition left of {operator} always {value} [{file}:{line},{col}]", { + operator: self.operator, + value: nullish ? "nulish" : "false", + file: self.start.file, + line: self.start.line, + col: self.start.col, + }); return make_sequence(self, [ self.left, self.right ]).optimize(compressor); } else if (!(ll instanceof AST_Node)) { - AST_Node.warn("Condition left of || always true [{file}:{line},{col}]", self.start); + AST_Node.warn("Condition left of {operator} always {value} [{file}:{line},{col}]", { + operator: self.operator, + value: nullish ? "defined" : "true", + file: self.start.file, + line: self.start.line, + col: self.start.col, + }); return maintain_this_binding(compressor, parent, compressor.self(), self.left).optimize(compressor); } var rr = self.right.evaluate(compressor); if (!rr) { if (in_bool || parent.operator == "||" && parent.left === compressor.self()) { - AST_Node.warn("Dropping side-effect-free || [{file}:{line},{col}]", self.start); + AST_Node.warn("Dropping side-effect-free {operator} [{file}:{line},{col}]", { + operator: self.operator, + file: self.start.file, + line: self.start.line, + col: self.start.col, + }); return self.left.optimize(compressor); } - } else if (!(rr instanceof AST_Node)) { + } else if (!nullish && !(rr instanceof AST_Node)) { if (in_bool) { AST_Node.warn("Boolean || always true [{file}:{line},{col}]", self.start); return make_sequence(self, [ @@ -9705,7 +9729,7 @@ merge(Compressor.prototype, { } else self.truthy = true; } // x && true || y ---> x ? true : y - if (self.left.operator == "&&") { + if (!nullish && self.left.operator == "&&") { var lr = self.left.right.is_truthy() || self.left.right.evaluate(compressor, true); if (lr && !(lr instanceof AST_Node)) return make_node(AST_Conditional, self, { condition: self.left.left, @@ -10047,9 +10071,9 @@ merge(Compressor.prototype, { }); } - function fuzzy_eval(node) { + function fuzzy_eval(node, nullish) { if (node.truthy) return true; - if (node.falsy) return false; + if (node.falsy && !nullish) return false; if (node.is_truthy()) return true; return node.evaluate(compressor, true); } diff --git a/lib/output.js b/lib/output.js index 592717b0..49164359 100644 --- a/lib/output.js +++ b/lib/output.js @@ -753,7 +753,9 @@ function OutputStream(options) { if (p instanceof AST_Binary) { var po = p.operator, pp = PRECEDENCE[po]; var so = this.operator, sp = PRECEDENCE[so]; - return pp > sp || (pp == sp && this === p[po == "**" ? "left" : "right"]); + return pp > sp + || po == "??" && (so == "&&" || so == "||") + || (pp == sp && this === p[po == "**" ? "left" : "right"]); } // (foo && bar)() if (p instanceof AST_Call) return p.expression === this; diff --git a/lib/parse.js b/lib/parse.js index 26c618ef..2cb30409 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -107,7 +107,8 @@ var OPERATORS = makePredicate([ "^=", "&=", "&&", - "||" + "||", + "??", ]); var NEWLINE_CHARS = "\n\r\u2028\u2029"; @@ -662,6 +663,7 @@ var PRECEDENCE = function(a, ret) { } return ret; }([ + ["??"], ["||"], ["&&"], ["|"], |