From cf72fe552f5a51ccfe40c32e0fb86d549e0ca848 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sat, 8 Apr 2017 14:25:28 +0800 Subject: [PATCH] fix `delete` corner cases (#1799) - assignment - boolean - conditional - sequence --- lib/compress.js | 62 +++++++++------ test/compress/conditionals.js | 53 +++++++++++++ test/compress/drop-unused.js | 55 +++++++++++++ test/compress/evaluate.js | 79 ++++++++++++++++++- test/compress/sequences.js | 144 ++++++++++++++++++++++++++++++++++ 5 files changed, 369 insertions(+), 24 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 03b1fefb..de0ff387 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -481,15 +481,15 @@ merge(Compressor.prototype, { // func(something) because that changes the meaning of // the func (becomes lexical instead of global). function maintain_this_binding(parent, orig, val) { - if (parent instanceof AST_Call && parent.expression === orig) { - if (val instanceof AST_PropAccess || val instanceof AST_SymbolRef && val.name === "eval") { - return make_node(AST_Seq, orig, { - car: make_node(AST_Number, orig, { - value: 0 - }), - cdr: val - }); - } + if (parent instanceof AST_UnaryPrefix && parent.operator == "delete" + || parent instanceof AST_Call && parent.expression === orig + && (val instanceof AST_PropAccess || val instanceof AST_SymbolRef && val.name == "eval")) { + return make_node(AST_Seq, orig, { + car: make_node(AST_Number, orig, { + value: 0 + }), + cdr: val + }); } return val; } @@ -3103,11 +3103,27 @@ merge(Compressor.prototype, { }); OPT(AST_UnaryPrefix, function(self, compressor){ + var e = self.expression; + if (self.operator == "delete" + && !(e instanceof AST_SymbolRef + || e instanceof AST_PropAccess + || e instanceof AST_NaN + || e instanceof AST_Infinity + || e instanceof AST_Undefined)) { + if (e instanceof AST_Seq) { + e = e.to_array(); + e.push(make_node(AST_True, self)); + return AST_Seq.from_array(e).optimize(compressor); + } + return make_node(AST_Seq, self, { + car: e, + cdr: make_node(AST_True, self) + }).optimize(compressor); + } var seq = self.lift_sequences(compressor); if (seq !== self) { return seq; } - var e = self.expression; if (compressor.option("side_effects") && self.operator == "void") { e = e.drop_side_effect_free(compressor); if (e) { @@ -3606,6 +3622,14 @@ merge(Compressor.prototype, { return self; }); + function in_delete(parent) { + return parent instanceof AST_UnaryPrefix && parent.operator == "delete"; + } + + function is_atomic(parent, self) { + return parent.expression instanceof AST_SymbolRef || parent.expression.TYPE === self.TYPE; + } + OPT(AST_Undefined, function(self, compressor){ if (compressor.option("unsafe")) { var undef = find_variable(compressor, "undefined"); @@ -3620,10 +3644,7 @@ merge(Compressor.prototype, { } } var parent = compressor.parent(); - if (parent instanceof AST_UnaryPrefix - && parent.operator == "delete" - && (parent.expression instanceof AST_SymbolRef - || parent.expression.TYPE === self.TYPE)) return self; + if (in_delete(parent) && is_atomic(parent, self)) return self; return make_node(AST_UnaryPrefix, self, { operator: "void", expression: make_node(AST_Number, self, { @@ -3634,12 +3655,10 @@ merge(Compressor.prototype, { OPT(AST_Infinity, function(self, compressor){ var parent = compressor.parent(); - if (parent instanceof AST_UnaryPrefix - && parent.operator == "delete" - && (parent.expression instanceof AST_SymbolRef - || parent.expression.TYPE === self.TYPE)) - return self; + var del = in_delete(parent); + if (del && is_atomic(parent, self)) return self; if (compressor.option("keep_infinity") + && !(del && !is_atomic(parent, self)) && !find_variable(compressor, "Infinity")) return self; return make_node(AST_Binary, self, { @@ -3655,10 +3674,7 @@ merge(Compressor.prototype, { OPT(AST_NaN, function(self, compressor){ var parent = compressor.parent(); - if (parent instanceof AST_UnaryPrefix - && parent.operator == "delete" - && !(parent.expression instanceof AST_SymbolRef - || parent.expression.TYPE === self.TYPE) + if (in_delete(parent) && !is_atomic(parent, self) || find_variable(compressor, "NaN")) { return make_node(AST_Binary, self, { operator: "/", diff --git a/test/compress/conditionals.js b/test/compress/conditionals.js index e7ea2bb2..200b487f 100644 --- a/test/compress/conditionals.js +++ b/test/compress/conditionals.js @@ -962,3 +962,56 @@ condition_symbol_matches_consequent: { } expect_stdout: "3 7 true 4" } + +delete_conditional_1: { + options = { + booleans: true, + conditionals: true, + evaluate: true, + side_effects: true, + } + input: { + console.log(delete (1 ? undefined : x)); + console.log(delete (1 ? void 0 : x)); + console.log(delete (1 ? Infinity : x)); + console.log(delete (1 ? 1 / 0 : x)); + console.log(delete (1 ? NaN : x)); + console.log(delete (1 ? 0 / 0 : x)); + } + expect: { + console.log((void 0, !0)); + console.log((void 0, !0)); + console.log((1 / 0, !0)); + console.log((1 / 0, !0)); + console.log((NaN, !0)); + console.log((NaN, !0)); + } + expect_stdout: true +} + +delete_conditional_2: { + options = { + booleans: true, + conditionals: true, + evaluate: true, + keep_infinity: true, + side_effects: true, + } + input: { + console.log(delete (0 ? x : undefined)); + console.log(delete (0 ? x : void 0)); + console.log(delete (0 ? x : Infinity)); + console.log(delete (0 ? x : 1 / 0)); + console.log(delete (0 ? x : NaN)); + console.log(delete (0 ? x : 0 / 0)); + } + expect: { + console.log((void 0, !0)); + console.log((void 0, !0)); + console.log((Infinity, !0)); + console.log((1 / 0, !0)); + console.log((NaN, !0)); + console.log((NaN, !0)); + } + expect_stdout: true +} diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index 28118fc4..99d9cace 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -974,3 +974,58 @@ issue_1715_4: { } expect_stdout: "1" } + +delete_assign_1: { + options = { + booleans: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a; + console.log(delete (a = undefined)); + console.log(delete (a = void 0)); + console.log(delete (a = Infinity)); + console.log(delete (a = 1 / 0)); + console.log(delete (a = NaN)); + console.log(delete (a = 0 / 0)); + } + expect: { + console.log((void 0, !0)); + console.log((void 0, !0)); + console.log((1 / 0, !0)); + console.log((1 / 0, !0)); + console.log((NaN, !0)); + console.log((0 / 0, !0)); + } + expect_stdout: true +} + +delete_assign_2: { + options = { + booleans: true, + keep_infinity: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a; + console.log(delete (a = undefined)); + console.log(delete (a = void 0)); + console.log(delete (a = Infinity)); + console.log(delete (a = 1 / 0)); + console.log(delete (a = NaN)); + console.log(delete (a = 0 / 0)); + } + expect: { + console.log((void 0, !0)); + console.log((void 0, !0)); + console.log((Infinity, !0)); + console.log((1 / 0, !0)); + console.log((NaN, !0)); + console.log((0 / 0, !0)); + } + expect_stdout: true +} diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js index e660071d..3c16e201 100644 --- a/test/compress/evaluate.js +++ b/test/compress/evaluate.js @@ -858,8 +858,9 @@ issue_1760_2: { expect_stdout: "Infinity" } -delete_expr: { +delete_expr_1: { options = { + booleans: true, evaluate: true, } input: { @@ -871,6 +872,23 @@ delete_expr: { console.log(delete (0 / 0)); } expect: { + console.log(delete undefined); + console.log((void 0, !0)); + console.log(delete Infinity); + console.log((1 / 0, !0)); + console.log(delete NaN); + console.log((0 / 0, !0)); + } + expect_stdout: true +} + +delete_expr_2: { + options = { + booleans: true, + evaluate: true, + keep_infinity: true, + } + input: { console.log(delete undefined); console.log(delete void 0); console.log(delete Infinity); @@ -878,5 +896,64 @@ delete_expr: { console.log(delete NaN); console.log(delete (0 / 0)); } + expect: { + console.log(delete undefined); + console.log((void 0, !0)); + console.log(delete Infinity); + console.log((1 / 0, !0)); + console.log(delete NaN); + console.log((0 / 0, !0)); + } + expect_stdout: true +} + +delete_binary_1: { + options = { + booleans: true, + evaluate: true, + side_effects: true, + } + input: { + console.log(delete (true && undefined)); + console.log(delete (true && void 0)); + console.log(delete (true && Infinity)); + console.log(delete (true && (1 / 0))); + console.log(delete (true && NaN)); + console.log(delete (true && (0 / 0))); + } + expect: { + console.log((void 0, !0)); + console.log((void 0, !0)); + console.log((1 / 0, !0)); + console.log((1 / 0, !0)); + console.log((NaN, !0)); + console.log((NaN, !0)); + } + expect_stdout: true +} + +delete_binary_2: { + options = { + booleans: true, + evaluate: true, + keep_infinity: true, + side_effects: true, + } + input: { + console.log(delete (false || undefined)); + console.log(delete (false || void 0)); + console.log(delete (false || Infinity)); + console.log(delete (false || (1 / 0))); + console.log(delete (false || NaN)); + console.log(delete (false || (0 / 0))); + } + expect: { + console.log((void 0, !0)); + console.log((void 0, !0)); + console.log((Infinity, !0)); + console.log((1 / 0, !0)); + console.log((NaN, !0)); + console.log((NaN, !0)); + } expect_stdout: true } diff --git a/test/compress/sequences.js b/test/compress/sequences.js index b3c54635..699341c0 100644 --- a/test/compress/sequences.js +++ b/test/compress/sequences.js @@ -466,3 +466,147 @@ issue_1758: { } expect_stdout: "undefined" } + +delete_seq_1: { + options = { + booleans: true, + side_effects: true, + } + input: { + console.log(delete (1, undefined)); + console.log(delete (1, void 0)); + console.log(delete (1, Infinity)); + console.log(delete (1, 1 / 0)); + console.log(delete (1, NaN)); + console.log(delete (1, 0 / 0)); + } + expect: { + console.log((void 0, !0)); + console.log((void 0, !0)); + console.log((1 / 0, !0)); + console.log((1 / 0, !0)); + console.log((NaN, !0)); + console.log((0 / 0, !0)); + } + expect_stdout: true +} + +delete_seq_2: { + options = { + booleans: true, + side_effects: true, + } + input: { + console.log(delete (1, 2, undefined)); + console.log(delete (1, 2, void 0)); + console.log(delete (1, 2, Infinity)); + console.log(delete (1, 2, 1 / 0)); + console.log(delete (1, 2, NaN)); + console.log(delete (1, 2, 0 / 0)); + } + expect: { + console.log((void 0, !0)); + console.log((void 0, !0)); + console.log((1 / 0, !0)); + console.log((1 / 0, !0)); + console.log((NaN, !0)); + console.log((0 / 0, !0)); + } + expect_stdout: true +} + +delete_seq_3: { + options = { + booleans: true, + keep_infinity: true, + side_effects: true, + } + input: { + console.log(delete (1, 2, undefined)); + console.log(delete (1, 2, void 0)); + console.log(delete (1, 2, Infinity)); + console.log(delete (1, 2, 1 / 0)); + console.log(delete (1, 2, NaN)); + console.log(delete (1, 2, 0 / 0)); + } + expect: { + console.log((void 0, !0)); + console.log((void 0, !0)); + console.log((Infinity, !0)); + console.log((1 / 0, !0)); + console.log((NaN, !0)); + console.log((0 / 0, !0)); + } + expect_stdout: true +} + +delete_seq_4: { + options = { + booleans: true, + sequences: true, + side_effects: true, + } + input: { + function f() {} + console.log(delete (f(), undefined)); + console.log(delete (f(), void 0)); + console.log(delete (f(), Infinity)); + console.log(delete (f(), 1 / 0)); + console.log(delete (f(), NaN)); + console.log(delete (f(), 0 / 0)); + } + expect: { + function f() {} + console.log((f(), !0)), + console.log((f(), !0)), + console.log((f(), !0)), + console.log((f(), !0)), + console.log((f(), !0)), + console.log((f(), !0)); + } + expect_stdout: true +} + +delete_seq_5: { + options = { + booleans: true, + keep_infinity: true, + sequences: true, + side_effects: true, + } + input: { + function f() {} + console.log(delete (f(), undefined)); + console.log(delete (f(), void 0)); + console.log(delete (f(), Infinity)); + console.log(delete (f(), 1 / 0)); + console.log(delete (f(), NaN)); + console.log(delete (f(), 0 / 0)); + } + expect: { + function f() {} + console.log((f(), !0)), + console.log((f(), !0)), + console.log((f(), !0)), + console.log((f(), !0)), + console.log((f(), !0)), + console.log((f(), !0)); + } + expect_stdout: true +} + +delete_seq_6: { + options = { + booleans: true, + side_effects: true, + } + input: { + var a; + console.log(delete (1, a)); + } + expect: { + var a; + console.log((a, !0)); + } + expect_stdout: true +} -- 2.34.1