From: Alex Lam S.L Date: Wed, 26 Aug 2020 12:41:11 +0000 (+0100) Subject: fix corner case in `sequences` (#4080) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=b1cc15e85bf57042fc390c58f2b513304f8543c6;p=UglifyJS.git fix corner case in `sequences` (#4080) fixes #4079 --- diff --git a/lib/compress.js b/lib/compress.js index cd790db3..90dd0f27 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -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 ]); - } - return 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 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); - } - return make_sequence(self, [ e, make_node(AST_True, self) ]).optimize(compressor); - } - if (compressor.option("sequences")) { + && 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, [ exp, make_node(AST_True, self) ]).optimize(compressor); + } + 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; - return self; - } else { - return make_node(AST_Undefined, self).optimize(compressor); - } + 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; } 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); }); diff --git a/test/compress/sequences.js b/test/compress/sequences.js index 32db63d3..b8190366 100644 --- a/test/compress/sequences.js +++ b/test/compress/sequences.js @@ -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" +}