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
// the func (becomes lexical instead of global).
function maintain_this_binding(compressor, parent, orig, val) {
if (parent instanceof AST_UnaryPrefix && parent.operator == "delete"
|| parent.TYPE == "Call" && parent.expression === orig && needs_unbinding(compressor, val)) {
return make_sequence(orig, [ make_node(AST_Number, orig, { value: 0 }), val ]);
var wrap = false;
if (parent.TYPE == "Call") {
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) {
@@ -6905,72 +6908,70 @@ merge(Compressor.prototype, {
var SIGN_OPS = makePredicate("+ -");
var MULTIPLICATIVE_OPS = makePredicate("* / %");
OPT(AST_UnaryPrefix, function(self, compressor) {
var e = self.expression;
var op = self.operator;
var exp = self.expression;
if (compressor.option("evaluate")
&& self.operator == "delete"
&& !(e instanceof AST_SymbolRef
|| e instanceof AST_PropAccess
|| is_identifier_atom(e))) {
if (e instanceof AST_Sequence) {
e = e.expressions.slice();
e.push(make_node(AST_True, self));
return make_sequence(self, e).optimize(compressor);
&& op == "delete"
&& !(exp instanceof AST_SymbolRef
|| exp instanceof AST_PropAccess
|| is_identifier_atom(exp))) {
if (exp instanceof AST_Sequence) {
exp = exp.expressions.slice();
exp.push(make_node(AST_True, self));
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);
if (seq !== self) return seq.optimize(compressor);
}
if (compressor.option("side_effects") && self.operator == "void") {
e = e.drop_side_effect_free(compressor);
if (e) {
self.expression = e;
if (compressor.option("side_effects") && op == "void") {
exp = exp.drop_side_effect_free(compressor);
if (!exp) return make_node(AST_Undefined, self).optimize(compressor);
self.expression = exp;
return self;
} else {
return make_node(AST_Undefined, self).optimize(compressor);
}
}
if (compressor.option("booleans")) {
if (self.operator == "!" && e.is_truthy()) {
return make_sequence(self, [ e, make_node(AST_False, self) ]).optimize(compressor);
} else if (compressor.in_boolean_context()) switch (self.operator) {
if (op == "!" && exp.is_truthy()) {
return make_sequence(self, [ exp, make_node(AST_False, self) ]).optimize(compressor);
} else if (compressor.in_boolean_context()) switch (op) {
case "!":
if (e instanceof AST_UnaryPrefix && e.operator == "!") {
if (exp instanceof AST_UnaryPrefix && exp.operator == "!") {
// !!foo => foo, if we're in boolean context
return e.expression;
return exp.expression;
}
if (e instanceof AST_Binary) {
self = best_of(compressor, self, e.negate(compressor, first_in_statement(compressor)));
if (exp instanceof AST_Binary) {
self = best_of(compressor, self, exp.negate(compressor, first_in_statement(compressor)));
}
break;
case "typeof":
// typeof always returns a non-empty string, thus it's
// always true in booleans
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, [
e,
return (exp instanceof AST_SymbolRef ? make_node(AST_True, self) : make_sequence(self, [
exp,
make_node(AST_True, self)
])).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")
&& e instanceof AST_Binary
&& SIGN_OPS[self.operator]
&& MULTIPLICATIVE_OPS[e.operator]
&& (e.left.is_constant() || !e.right.has_side_effects(compressor))) {
&& exp instanceof AST_Binary
&& SIGN_OPS[op]
&& MULTIPLICATIVE_OPS[exp.operator]
&& (exp.left.is_constant() || !exp.right.has_side_effects(compressor))) {
return make_node(AST_Binary, self, {
operator: e.operator,
left: make_node(AST_UnaryPrefix, e.left, {
operator: self.operator,
expression: e.left
operator: exp.operator,
left: make_node(AST_UnaryPrefix, exp.left, {
operator: op,
expression: exp.left
}),
right: e.right
right: exp.right
});
}
// 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);
});

View File

@@ -1155,3 +1155,25 @@ issue_3703: {
}
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"
}