From 14e712ee802da2a6199653268f8519187ece2c51 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Thu, 10 May 2018 06:16:35 +0800 Subject: [PATCH] fix corner case in call binding (#3128) fixes #3127 --- lib/compress.js | 26 +++++++----- test/compress/issue-782.js | 28 ++++++++++++- test/compress/issue-973.js | 85 ++++++++++++++++++++++++++++++++------ 3 files changed, 115 insertions(+), 24 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 8c565c21..d3c24529 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -919,15 +919,21 @@ merge(Compressor.prototype, { type: typeof val })); } - }; + } + + function needs_unbinding(compressor, val) { + return val instanceof AST_PropAccess + || compressor.has_directive("use strict") + && is_undeclared_ref(val) + && val.name == "eval"; + } // we shouldn't compress (1,func)(something) to // func(something) because that changes the meaning of // the func (becomes lexical instead of global). - function maintain_this_binding(parent, orig, val) { + function maintain_this_binding(compressor, parent, orig, val) { if (parent instanceof AST_UnaryPrefix && parent.operator == "delete" - || parent.TYPE == "Call" && parent.expression === orig - && (val instanceof AST_PropAccess || val instanceof AST_SymbolRef && val.name == "eval")) { + || parent.TYPE == "Call" && parent.expression === orig && needs_unbinding(compressor, val)) { return make_sequence(orig, [ make_node(AST_Number, orig, { value: 0 }), val ]); } return val; @@ -1098,7 +1104,7 @@ merge(Compressor.prototype, { var def = candidate.name.definition(); if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) { def.replaced++; - return maintain_this_binding(parent, node, candidate.value); + return maintain_this_binding(compressor, parent, node, candidate.value); } return make_node(AST_Assign, candidate, { operator: "=", @@ -3346,7 +3352,7 @@ merge(Compressor.prototype, { } if (value) { props.push(value); - return maintain_this_binding(parent, node, make_sequence(node, props.map(function(prop) { + return maintain_this_binding(compressor, parent, node, make_sequence(node, props.map(function(prop) { return prop.transform(tt); }))); } @@ -4477,7 +4483,7 @@ merge(Compressor.prototype, { var exp = this.expression; if (!(exp instanceof AST_Sequence)) return this; var tail = exp.tail_node(); - if (tail instanceof AST_PropAccess && !(this instanceof AST_New)) return this; + if (needs_unbinding(compressor, tail) && !(this instanceof AST_New)) return this; var expressions = exp.expressions.slice(0, -1); var node = this.clone(); node.expression = tail; @@ -5028,7 +5034,7 @@ merge(Compressor.prototype, { var end = expressions.length - 1; trim_right_for_undefined(); if (end == 0) { - self = maintain_this_binding(compressor.parent(), compressor.self(), expressions[0]); + self = maintain_this_binding(compressor, compressor.parent(), compressor.self(), expressions[0]); if (!(self instanceof AST_Sequence)) self = self.optimize(compressor); return self; } @@ -5347,7 +5353,7 @@ merge(Compressor.prototype, { var ll = fuzzy_eval(self.left); if (!ll) { compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start); - return maintain_this_binding(compressor.parent(), compressor.self(), self.left).optimize(compressor); + return maintain_this_binding(compressor, compressor.parent(), compressor.self(), self.left).optimize(compressor); } else if (!(ll instanceof AST_Node)) { compressor.warn("Condition left of && always true [{file}:{line},{col}]", self.start); return make_sequence(self, [ self.left, self.right ]).optimize(compressor); @@ -5386,7 +5392,7 @@ merge(Compressor.prototype, { return make_sequence(self, [ self.left, self.right ]).optimize(compressor); } else if (!(ll instanceof AST_Node)) { compressor.warn("Condition left of || always true [{file}:{line},{col}]", self.start); - return maintain_this_binding(compressor.parent(), compressor.self(), self.left).optimize(compressor); + return maintain_this_binding(compressor, compressor.parent(), compressor.self(), self.left).optimize(compressor); } var rr = self.right.evaluate(compressor); if (!rr) { diff --git a/test/compress/issue-782.js b/test/compress/issue-782.js index 2f72d1ab..6e1c92f5 100644 --- a/test/compress/issue-782.js +++ b/test/compress/issue-782.js @@ -1,11 +1,31 @@ +remove_sequence: { + options = { + side_effects: true, + } + input: { + (0, 1, eval)(); + (0, 1, logThis)(); + (0, 1, _decorators.logThis)(); + } + expect: { + eval(); + logThis(); + (0, _decorators.logThis)(); + } +} + remove_redundant_sequence_items: { - options = { side_effects: true }; + options = { + side_effects: true, + } input: { + "use strict"; (0, 1, eval)(); (0, 1, logThis)(); (0, 1, _decorators.logThis)(); } expect: { + "use strict"; (0, eval)(); logThis(); (0, _decorators.logThis)(); @@ -13,13 +33,17 @@ remove_redundant_sequence_items: { } dont_remove_this_binding_sequence: { - options = { side_effects: true }; + options = { + side_effects: true, + } input: { + "use strict"; (0, eval)(); (0, logThis)(); (0, _decorators.logThis)(); } expect: { + "use strict"; (0, eval)(); logThis(); (0, _decorators.logThis)(); diff --git a/test/compress/issue-973.js b/test/compress/issue-973.js index a9fcd84f..3eb25fac 100644 --- a/test/compress/issue-973.js +++ b/test/compress/issue-973.js @@ -3,8 +3,9 @@ this_binding_conditionals: { conditionals: true, evaluate: true, side_effects: true, - }; + } input: { + "use strict"; (1 && a)(); (0 || a)(); (0 || 1 && a)(); @@ -26,6 +27,7 @@ this_binding_conditionals: { (1 ? eval : 0)(); } expect: { + "use strict"; a(); a(); a(); @@ -53,13 +55,15 @@ this_binding_collapse_vars: { collapse_vars: true, toplevel: true, unused: true, - }; + } input: { + "use strict"; var c = a; c(); var d = a.b; d(); var e = eval; e(); } expect: { + "use strict"; a(); (0, a.b)(); (0, eval)(); @@ -69,31 +73,88 @@ this_binding_collapse_vars: { this_binding_side_effects: { options = { side_effects : true - }; + } input: { - (function (foo) { + (function(foo) { + (0, foo)(); + (0, foo.bar)(); + (0, eval)("console.log(foo);"); + }()); + (function(foo) { + "use strict"; (0, foo)(); (0, foo.bar)(); - (0, eval)('console.log(foo);'); + (0, eval)("console.log(foo);"); }()); - (function (foo) { + (function(foo) { var eval = console; (0, foo)(); (0, foo.bar)(); - (0, eval)('console.log(foo);'); + (0, eval)("console.log(foo);"); }()); } expect: { - (function (foo) { + (function(foo) { foo(); (0, foo.bar)(); - (0, eval)('console.log(foo);'); + eval("console.log(foo);"); }()); - (function (foo) { + (function(foo) { + "use strict"; + foo(); + (0, foo.bar)(); + (0, eval)("console.log(foo);"); + }()); + (function(foo) { var eval = console; foo(); (0, foo.bar)(); - (0, eval)('console.log(foo);'); + eval("console.log(foo);"); + }()); + } +} + +this_binding_sequences: { + options = { + sequences: true, + side_effects: true, + } + input: { + console.log(typeof function() { + return eval("this"); + }()); + console.log(typeof function() { + "use strict"; + return eval("this"); + }()); + console.log(typeof function() { + return (0, eval)("this"); + }()); + console.log(typeof function() { + "use strict"; + return (0, eval)("this"); + }()); + } + expect: { + console.log(typeof function() { + return eval("this"); + }()), + console.log(typeof function() { + "use strict"; + return eval("this"); + }()), + console.log(typeof function() { + return eval("this"); + }()), + console.log(typeof function() { + "use strict"; + return (0, eval)("this"); }()); } -} \ No newline at end of file + expect_stdout: [ + "object", + "undefined", + "object", + "object", + ] +} -- 2.34.1