fix corner case in call binding (#3128)

fixes #3127
This commit is contained in:
Alex Lam S.L
2018-05-10 06:16:35 +08:00
committed by GitHub
parent f83adcc995
commit 14e712ee80
3 changed files with 116 additions and 25 deletions

View File

@@ -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) {

View File

@@ -1,11 +1,31 @@
remove_redundant_sequence_items: {
options = { side_effects: true };
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,
}
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)();

View File

@@ -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);');
(0, eval)("console.log(foo);");
}());
(function (foo) {
(function(foo) {
"use strict";
(0, foo)();
(0, foo.bar)();
(0, eval)("console.log(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");
}());
}
expect_stdout: [
"object",
"undefined",
"object",
"object",
]
}