From 183da16896513c22b73d219affebeb155fb8ecdf Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Tue, 10 Apr 2018 02:46:38 +0800 Subject: [PATCH] handle `pure_funcs` under `inline` & `reduce_vars` correctly (#3066) fixes #3065 --- README.md | 3 +- lib/compress.js | 12 ++-- test/compress/pure_funcs.js | 107 ++++++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 35859115d1..9244361884 100644 --- a/README.md +++ b/README.md @@ -685,7 +685,8 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u pass `pure_funcs: [ 'Math.floor' ]` to let it know that this function won't produce any side effect, in which case the whole statement would get discarded. The current implementation adds some - overhead (compression will be slower). + overhead (compression will be slower). Make sure symbols under `pure_funcs` + are also under `mangle.reserved` to avoid mangling. - `pure_getters` (default: `"strict"`) -- If you pass `true` for this, UglifyJS will assume that object property access diff --git a/lib/compress.js b/lib/compress.js index ddba624541..73f4632673 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -4573,7 +4573,8 @@ merge(Compressor.prototype, { } } var stat = is_func && fn.body[0]; - if (compressor.option("inline") && stat instanceof AST_Return) { + var can_inline = compressor.option("inline") && !self.is_expr_pure(compressor); + if (can_inline && stat instanceof AST_Return) { var value = stat.value; if (!value || value.is_constant_expression()) { if (value) { @@ -4587,7 +4588,7 @@ merge(Compressor.prototype, { } if (is_func) { var def, value, scope, in_loop, level = -1; - if (compressor.option("inline") + if (can_inline && !fn.uses_arguments && !fn.uses_eval && !(fn.name && fn instanceof AST_Function) @@ -5460,11 +5461,12 @@ merge(Compressor.prototype, { return make_node(AST_Infinity, self).optimize(compressor); } } - if (compressor.option("reduce_vars") - && is_lhs(self, compressor.parent()) !== self) { + var parent = compressor.parent(); + if (compressor.option("reduce_vars") && is_lhs(self, parent) !== self) { var d = self.definition(); var fixed = self.fixed_value(); - var single_use = d.single_use; + var single_use = d.single_use + && !(parent instanceof AST_Call && parent.is_expr_pure(compressor)); if (single_use && fixed instanceof AST_Lambda) { if (d.scope !== self.scope && (!compressor.option("reduce_funcs") diff --git a/test/compress/pure_funcs.js b/test/compress/pure_funcs.js index 0df51e5fa7..56c36dd77d 100644 --- a/test/compress/pure_funcs.js +++ b/test/compress/pure_funcs.js @@ -535,3 +535,110 @@ issue_2705_6: { "/* */new(/* */a()||b())(c(),d());", ] } + +issue_3065_1: { + options = { + inline: true, + pure_funcs: [ "pureFunc" ], + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function modifyWrapper(a, f, wrapper) { + wrapper.a = a; + wrapper.f = f; + return wrapper; + } + function pureFunc(fun) { + return modifyWrapper(1, fun, function(a) { + return fun(a); + }); + } + var unused = pureFunc(function(x) { + return x; + }); + } + expect: {} +} + +issue_3065_2: { + rename = true + options = { + inline: true, + pure_funcs: [ "pureFunc" ], + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + mangle = { + reserved: [ "pureFunc" ], + toplevel: true, + } + input: { + function modifyWrapper(a, f, wrapper) { + wrapper.a = a; + wrapper.f = f; + return wrapper; + } + function pureFunc(fun) { + return modifyWrapper(1, fun, function(a) { + return fun(a); + }); + } + var unused = pureFunc(function(x) { + return x; + }); + } + expect: {} +} + +issue_3065_3: { + options = { + pure_funcs: [ "debug" ], + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function debug(msg) { + console.log(msg); + } + debug(function() { + console.log("PASS"); + return "FAIL"; + }()); + } + expect: { + (function() { + console.log("PASS"); + })(); + } +} + +issue_3065_4: { + options = { + pure_funcs: [ "debug" ], + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var debug = function(msg) { + console.log(msg); + }; + debug(function() { + console.log("PASS"); + return "FAIL"; + }()); + } + expect: { + (function() { + console.log("PASS"); + })(); + } +}