From: Alex Lam S.L Date: Sat, 6 May 2017 15:18:55 +0000 (+0800) Subject: fix `unsafe` on `evaluate` of `reduce_vars` (#1870) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=2c7ee956fd829624cacfdbde43d74ee8d3bb5e82;p=UglifyJS.git fix `unsafe` on `evaluate` of `reduce_vars` (#1870) Determine if variables with non-constant values can escape and be modified. fixes #1865 --- diff --git a/lib/compress.js b/lib/compress.js index 63eaacad..919ee20f 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -283,6 +283,14 @@ merge(Compressor.prototype, { if (d.fixed === undefined || !safe_to_read(d) || is_modified(node, 0, is_immutable(node.fixed_value()))) { d.fixed = false; + } else { + var parent = tw.parent(); + if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right + || parent instanceof AST_Call && node !== parent.expression + || parent instanceof AST_Return && node === parent.value && node.scope !== d.scope + || parent instanceof AST_VarDef && node === parent.value) { + d.escaped = true; + } } } if (node instanceof AST_SymbolCatch) { @@ -482,6 +490,7 @@ merge(Compressor.prototype, { } function reset_def(def) { + def.escaped = false; if (!def.global || def.orig[0] instanceof AST_SymbolConst || compressor.toplevel(def)) { def.fixed = undefined; } else { @@ -1595,23 +1604,20 @@ merge(Compressor.prototype, { : ev(this.alternative, compressor); }); def(AST_SymbolRef, function(compressor){ - if (this._evaluating) throw def; + if (!compressor.option("reduce_vars") || this._evaluating) throw def; this._evaluating = true; try { var fixed = this.fixed_value(); - if (compressor.option("reduce_vars") && fixed) { - if (compressor.option("unsafe")) { - if (!HOP(fixed, "_evaluated")) { - fixed._evaluated = ev(fixed, compressor); - } - return fixed._evaluated; - } - return ev(fixed, compressor); - } + if (!fixed) throw def; + var value = ev(fixed, compressor); + if (!HOP(fixed, "_eval")) fixed._eval = function() { + return value; + }; + if (value && typeof value == "object" && this.definition().escaped) throw def; + return value; } finally { this._evaluating = false; } - throw def; }); def(AST_PropAccess, function(compressor){ if (compressor.option("unsafe")) { diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index f7bdcb36..22c1a874 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -297,7 +297,7 @@ unsafe_evaluate_array: { } } -unsafe_evaluate_equality: { +unsafe_evaluate_equality_1: { options = { evaluate : true, reduce_vars : true, @@ -305,47 +305,62 @@ unsafe_evaluate_equality: { unused : true } input: { - function f0(){ + function f0() { var a = {}; - console.log(a === a); + return a === a; } - - function f1(){ + function f1() { var a = []; - console.log(a === a); + return a === a; + } + console.log(f0(), f1()); + } + expect: { + function f0() { + return true; + } + function f1() { + return true; } + console.log(f0(), f1()); + } + expect_stdout: true +} - function f2(){ +unsafe_evaluate_equality_2: { + options = { + collapse_vars: true, + evaluate : true, + passes : 2, + reduce_vars : true, + unsafe : true, + unused : true + } + input: { + function f2() { var a = {a:1, b:2}; var b = a; var c = a; - console.log(b === c); + return b === c; } - - function f3(){ + function f3() { var a = [1, 2, 3]; var b = a; var c = a; - console.log(b === c); + return b === c; } + console.log(f2(), f3()); } expect: { - function f0(){ - console.log(true); - } - - function f1(){ - console.log(true); - } - - function f2(){ - console.log(true); + function f2() { + return true; } - - function f3(){ - console.log(true); + function f3() { + return true; } + console.log(f2(), f3()); } + expect_stdout: true } passes: { @@ -2417,3 +2432,32 @@ issue_1850_4: { } expect_stdout: true } + +issue_1865: { + options = { + evaluate: true, + reduce_vars: true, + unsafe: true, + } + input: { + function f(a) { + a.b = false; + } + console.log(function() { + var some = { thing: true }; + f(some); + return some.thing; + }()); + } + expect: { + function f(a) { + a.b = false; + } + console.log(function() { + var some = { thing: true }; + f(some); + return some.thing; + }()); + } + expect_stdout: true +}