From: Alex Lam S.L Date: Mon, 11 May 2020 14:46:00 +0000 (+0100) Subject: fix corner cases in `evaluate` & `reduce_vars` (#3879) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=2b24dc25fbe78d25b41b2e48bc38fedc6965d572;p=UglifyJS.git fix corner cases in `evaluate` & `reduce_vars` (#3879) fixes #3878 --- diff --git a/lib/compress.js b/lib/compress.js index 1b5d1a3a..08abde9b 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -858,7 +858,7 @@ merge(Compressor.prototype, { d.assignments++; var fixed = d.fixed; if (!fixed) return; - exp.fixed = d.fixed = function() { + d.fixed = function() { var value = fixed instanceof AST_Node ? fixed : fixed(); return value && make_node(AST_Binary, node, { operator: node.operator.slice(0, -1), @@ -871,6 +871,13 @@ merge(Compressor.prototype, { }) }); }; + exp.fixed = node instanceof AST_UnaryPrefix ? d.fixed : function() { + var value = fixed instanceof AST_Node ? fixed : fixed(); + return value && make_node(AST_UnaryPrefix, node, { + operator: "+", + expression: value + }); + }; if (!safe) return; d.references.push(exp); mark(tw, d, true); @@ -3233,6 +3240,12 @@ merge(Compressor.prototype, { && unaryPrefix[this.operator]; } }); + function modified(sym) { + if (!(sym instanceof AST_SymbolRef)) return; + sym.definition().references.forEach(function(node) { + delete node._eval; + }); + } def(AST_Statement, function() { throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start)); }); @@ -3246,6 +3259,7 @@ merge(Compressor.prototype, { if (this.operator != "=") return this; var node = this.right; var value = node._eval(compressor, ignore_side_effects, cached, depth); + modified(this.left); return value === node ? this : value; }); def(AST_Sequence, function(compressor, ignore_side_effects, cached, depth) { @@ -3317,7 +3331,7 @@ merge(Compressor.prototype, { if (!non_converting_unary[op]) depth++; var v = e._eval(compressor, ignore_side_effects, cached, depth); if (v === e) { - if (ignore_side_effects && op == "void") return void 0; + if (ignore_side_effects && op == "void") return; return this; } switch (op) { @@ -3327,7 +3341,7 @@ merge(Compressor.prototype, { // so cannot evaluate reliably if (v instanceof RegExp) return this; return typeof v; - case "void": return void v; + case "void": return; case "~": return ~v; case "-": return -v; case "+": return +v; @@ -3335,11 +3349,22 @@ merge(Compressor.prototype, { case "--": if (!(e instanceof AST_SymbolRef)) return this; var refs = e.definition().references; - if (refs[refs.length - 1] !== e) return this; - return HOP(e, "_eval") ? +(op[0] + 1) + +v : v; + if (!ignore_side_effects && refs[refs.length - 1] !== e) return this; + if (HOP(e, "_eval")) v = +(op[0] + 1) + +v; + modified(e); + return v; } return this; }); + def(AST_UnaryPostfix, function(compressor, ignore_side_effects, cached, depth) { + var e = this.expression; + if (!(e instanceof AST_SymbolRef)) return this; + var refs = e.definition().references; + if (!ignore_side_effects && refs[refs.length - 1] !== e) return this; + var v = e._eval(compressor, ignore_side_effects, cached, depth + 1); + modified(e); + return v === e ? this : +v; + }); var non_converting_binary = makePredicate("&& || === !=="); def(AST_Binary, function(compressor, ignore_side_effects, cached, depth) { if (!non_converting_binary[this.operator]) depth++; @@ -3514,14 +3539,14 @@ merge(Compressor.prototype, { } if (node instanceof AST_Scope && node !== fn) return true; })); - if (!found) return void 0; + if (!found) return; } return this; } var args = eval_args(this.args); - if (!args) return this; - if (!stat.value) return undefined; - if (!all(fn.argnames, function(sym, i) { + if (!args && !ignore_side_effects) return this; + if (!stat.value) return; + if (args && !all(fn.argnames, function(sym, i) { var value = args[i]; var def = sym.definition(); if (def.orig[def.orig.length - 1] !== sym) return false; @@ -3532,7 +3557,7 @@ merge(Compressor.prototype, { cached.push(node); }); return true; - })) return this; + }) && !ignore_side_effects) return this; fn.evaluating = true; var val = stat.value._eval(compressor, ignore_side_effects, cached, depth); delete fn.evaluating; diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js index a46d38aa..a4bee8cc 100644 --- a/test/compress/evaluate.js +++ b/test/compress/evaluate.js @@ -2326,3 +2326,46 @@ void_returns_recursive: { "undefined", ] } + +issue_3878_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var b = function(a) { + return (a = 0) == (a && this > (a += 0)); + }(); + console.log(b ? "PASS" : "FAIL"); + } + expect: { + var b = function(a) { + return (a = 0) == (a && this > (a += 0)); + }(); + console.log(b ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" +} + +issue_3878_2: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = "foo"; + a++ + a; + a && a; + console.log(a); + } + expect: { + var a = "foo"; + a++ + a; + a; + console.log(a); + } + expect_stdout: "NaN" +}