fix corner case in sequences (#4080)

fixes #4079
This commit is contained in:
Alex Lam S.L
2020-08-26 13:41:11 +01:00
committed by GitHub
parent 3aa765e429
commit b1cc15e85b
2 changed files with 66 additions and 43 deletions

View File

@@ -1094,11 +1094,14 @@ merge(Compressor.prototype, {
// func(something) because that changes the meaning of // func(something) because that changes the meaning of
// the func (becomes lexical instead of global). // the func (becomes lexical instead of global).
function maintain_this_binding(compressor, parent, orig, val) { function maintain_this_binding(compressor, parent, orig, val) {
if (parent instanceof AST_UnaryPrefix && parent.operator == "delete" var wrap = false;
|| parent.TYPE == "Call" && parent.expression === orig && needs_unbinding(compressor, val)) { if (parent.TYPE == "Call") {
return make_sequence(orig, [ make_node(AST_Number, orig, { value: 0 }), val ]); wrap = parent.expression === orig && needs_unbinding(compressor, val);
} else if (parent instanceof AST_UnaryPrefix) {
wrap = parent.operator == "delete"
|| parent.operator == "typeof" && is_undeclared_ref(val);
} }
return val; return wrap ? make_sequence(orig, [ make_node(AST_Number, orig, { value: 0 }), val ]) : val;
} }
function merge_sequence(array, node) { function merge_sequence(array, node) {
@@ -6905,72 +6908,70 @@ merge(Compressor.prototype, {
var SIGN_OPS = makePredicate("+ -"); var SIGN_OPS = makePredicate("+ -");
var MULTIPLICATIVE_OPS = makePredicate("* / %"); var MULTIPLICATIVE_OPS = makePredicate("* / %");
OPT(AST_UnaryPrefix, function(self, compressor) { OPT(AST_UnaryPrefix, function(self, compressor) {
var e = self.expression; var op = self.operator;
var exp = self.expression;
if (compressor.option("evaluate") if (compressor.option("evaluate")
&& self.operator == "delete" && op == "delete"
&& !(e instanceof AST_SymbolRef && !(exp instanceof AST_SymbolRef
|| e instanceof AST_PropAccess || exp instanceof AST_PropAccess
|| is_identifier_atom(e))) { || is_identifier_atom(exp))) {
if (e instanceof AST_Sequence) { if (exp instanceof AST_Sequence) {
e = e.expressions.slice(); exp = exp.expressions.slice();
e.push(make_node(AST_True, self)); exp.push(make_node(AST_True, self));
return make_sequence(self, e).optimize(compressor); return make_sequence(self, exp).optimize(compressor);
} }
return make_sequence(self, [ e, make_node(AST_True, self) ]).optimize(compressor); return make_sequence(self, [ exp, make_node(AST_True, self) ]).optimize(compressor);
} }
if (compressor.option("sequences")) { if (compressor.option("sequences") && !(op == "typeof" && is_undeclared_ref(exp.tail_node()))) {
var seq = lift_sequence_in_expression(self, compressor); var seq = lift_sequence_in_expression(self, compressor);
if (seq !== self) return seq.optimize(compressor); if (seq !== self) return seq.optimize(compressor);
} }
if (compressor.option("side_effects") && self.operator == "void") { if (compressor.option("side_effects") && op == "void") {
e = e.drop_side_effect_free(compressor); exp = exp.drop_side_effect_free(compressor);
if (e) { if (!exp) return make_node(AST_Undefined, self).optimize(compressor);
self.expression = e; self.expression = exp;
return self; return self;
} else {
return make_node(AST_Undefined, self).optimize(compressor);
}
} }
if (compressor.option("booleans")) { if (compressor.option("booleans")) {
if (self.operator == "!" && e.is_truthy()) { if (op == "!" && exp.is_truthy()) {
return make_sequence(self, [ e, make_node(AST_False, self) ]).optimize(compressor); return make_sequence(self, [ exp, make_node(AST_False, self) ]).optimize(compressor);
} else if (compressor.in_boolean_context()) switch (self.operator) { } else if (compressor.in_boolean_context()) switch (op) {
case "!": case "!":
if (e instanceof AST_UnaryPrefix && e.operator == "!") { if (exp instanceof AST_UnaryPrefix && exp.operator == "!") {
// !!foo => foo, if we're in boolean context // !!foo => foo, if we're in boolean context
return e.expression; return exp.expression;
} }
if (e instanceof AST_Binary) { if (exp instanceof AST_Binary) {
self = best_of(compressor, self, e.negate(compressor, first_in_statement(compressor))); self = best_of(compressor, self, exp.negate(compressor, first_in_statement(compressor)));
} }
break; break;
case "typeof": case "typeof":
// typeof always returns a non-empty string, thus it's // typeof always returns a non-empty string, thus it's
// always true in booleans // always true in booleans
AST_Node.warn("Boolean expression always true [{file}:{line},{col}]", self.start); AST_Node.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
return (e instanceof AST_SymbolRef ? make_node(AST_True, self) : make_sequence(self, [ return (exp instanceof AST_SymbolRef ? make_node(AST_True, self) : make_sequence(self, [
e, exp,
make_node(AST_True, self) make_node(AST_True, self)
])).optimize(compressor); ])).optimize(compressor);
} }
} }
if (self.operator == "-" && e instanceof AST_Infinity) e = e.transform(compressor); if (op == "-" && exp instanceof AST_Infinity) exp = exp.transform(compressor);
if (compressor.option("evaluate") if (compressor.option("evaluate")
&& e instanceof AST_Binary && exp instanceof AST_Binary
&& SIGN_OPS[self.operator] && SIGN_OPS[op]
&& MULTIPLICATIVE_OPS[e.operator] && MULTIPLICATIVE_OPS[exp.operator]
&& (e.left.is_constant() || !e.right.has_side_effects(compressor))) { && (exp.left.is_constant() || !exp.right.has_side_effects(compressor))) {
return make_node(AST_Binary, self, { return make_node(AST_Binary, self, {
operator: e.operator, operator: exp.operator,
left: make_node(AST_UnaryPrefix, e.left, { left: make_node(AST_UnaryPrefix, exp.left, {
operator: self.operator, operator: op,
expression: e.left expression: exp.left
}), }),
right: e.right right: exp.right
}); });
} }
// avoids infinite recursion of numerals // avoids infinite recursion of numerals
return self.operator == "-" && (e instanceof AST_Number || e instanceof AST_Infinity) return op == "-" && (exp instanceof AST_Number || exp instanceof AST_Infinity)
? self : try_evaluate(compressor, self); ? self : try_evaluate(compressor, self);
}); });

View File

@@ -1155,3 +1155,25 @@ issue_3703: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_4079: {
options = {
sequences: true,
side_effects: true,
}
input: {
try {
typeof (0, A);
} catch (e) {
console.log("PASS");
}
}
expect: {
try {
A;
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
}