From: Alex Lam S.L Date: Tue, 29 Oct 2019 11:51:55 +0000 (+0800) Subject: enhance `evaluate` (#3549) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=1d5c2becbd4e0266a702db93649dbe025056b885;p=UglifyJS.git enhance `evaluate` (#3549) --- diff --git a/lib/compress.js b/lib/compress.js index d37108d1..ad47621a 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -3019,7 +3019,26 @@ merge(Compressor.prototype, { }); def(AST_Call, function(compressor, cached, depth) { var exp = this.expression; - if (compressor.option("unsafe") && exp instanceof AST_PropAccess) { + if (exp instanceof AST_SymbolRef) { + var fn = exp.fixed_value(); + if (!(fn instanceof AST_Lambda)) return this; + if (fn.name && fn.name.definition().recursive_refs > 0) return this; + if (fn.body.length != 1 || !fn.is_constant_expression()) return this; + var stat = fn.body[0]; + if (!(stat instanceof AST_Return)) return this; + var args = eval_args(this.args); + if (!args) return this; + fn.argnames.forEach(function(sym, i) { + var value = args[i]; + sym.definition().references.forEach(function(node) { + node._eval = function() { + return value; + }; + cached.push(node); + }); + }); + return stat.value ? stat.value._eval(compressor, cached, depth) : undefined; + } else if (compressor.option("unsafe") && exp instanceof AST_PropAccess) { var key = exp.property; if (key instanceof AST_Node) { key = key._eval(compressor, cached, depth); @@ -3037,13 +3056,8 @@ merge(Compressor.prototype, { var native_fn = native_fns[val.constructor.name]; if (!native_fn || !native_fn[key]) return this; } - var args = []; - for (var i = 0; i < this.args.length; i++) { - var arg = this.args[i]; - var value = arg._eval(compressor, cached, depth); - if (arg === value) return this; - args.push(value); - } + var args = eval_args(this.args); + if (!args) return this; if (key == "replace" && typeof args[1] == "function") return this; try { return val[key].apply(val, args); @@ -3057,6 +3071,17 @@ merge(Compressor.prototype, { } } return this; + + function eval_args(args) { + var values = []; + for (var i = 0; i < args.length; i++) { + var arg = args[i]; + var value = arg._eval(compressor, cached, depth); + if (arg === value) return; + values.push(value); + } + return values; + } }); def(AST_New, return_this); })(function(node, func) { @@ -3406,19 +3431,19 @@ merge(Compressor.prototype, { def(AST_Lambda, function(scope) { var self = this; var result = true; - var inner_scopes = []; + var scopes = []; self.walk(new TreeWalker(function(node, descend) { if (!result) return true; if (node instanceof AST_Catch) { - inner_scopes.push(node.argname.scope); + scopes.push(node.argname.scope); descend(); - inner_scopes.pop(); + scopes.pop(); return true; } - if (node instanceof AST_Scope && node !== self) { - inner_scopes.push(node); + if (node instanceof AST_Scope) { + scopes.push(node); descend(); - inner_scopes.pop(); + scopes.pop(); return true; } if (node instanceof AST_SymbolRef) { @@ -3427,16 +3452,15 @@ merge(Compressor.prototype, { return true; } var def = node.definition(); - if (!self.variables.has(def.name) && !member(def.scope, inner_scopes)) { - if (scope) { - var scope_def = scope.find_variable(node); - if (def.undeclared ? !scope_def : scope_def === def) { - result = "f"; - return true; - } + if (member(def.scope, scopes)) return true; + if (scope) { + var scope_def = scope.find_variable(node); + if (def.undeclared ? !scope_def : scope_def === def) { + result = "f"; + return true; } - result = false; } + result = false; return true; } })); diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js index 0ac117fe..771bd4f6 100644 --- a/test/compress/evaluate.js +++ b/test/compress/evaluate.js @@ -1757,3 +1757,84 @@ issue_3387_2: { } expect_stdout: "NaN" } + +simple_function_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function sum(a, b) { + return a + b; + } + console.log(sum(1, 2) * sum(3, 4)); + } + expect: { + console.log(21); + } + expect_stdout: "21" +} + +simple_function_2: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var sum = function(a, b) { + return a + b; + } + console.log(sum(1, 2) * sum(3, 4)); + } + expect: { + console.log(21); + } + expect_stdout: "21" +} + +recursive_function_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function factorial(a) { + return a > 0 ? a * factorial(a - 1) : 1; + } + console.log(factorial(5)); + } + expect: { + console.log(function factorial(a) { + return a > 0 ? a * factorial(a - 1) : 1; + }(5)); + } + expect_stdout: "120" +} + +recursive_function_2: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var factorial = function(a) { + return a > 0 ? a * factorial(a - 1) : 1; + } + console.log(factorial(5)); + } + expect: { + var factorial = function(a) { + return a > 0 ? a * factorial(a - 1) : 1; + } + console.log(factorial(5)); + } + expect_stdout: "120" +}