From e9645e017f297e06506cc139922ff012fb763139 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 31 May 2017 03:38:00 +0800 Subject: [PATCH] introduce `unsafe_Func` (#2033) Separate flag for #203 functionality. --- README.md | 2 + bin/uglifyjs | 2 +- lib/compress.js | 115 +++++++++++++++++++------------------ test/compress/functions.js | 22 +++++++ 4 files changed, 83 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index d7e65b27..d9a0162c 100644 --- a/README.md +++ b/README.md @@ -572,6 +572,8 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u comparison are switching. Compression only works if both `comparisons` and `unsafe_comps` are both set to true. +- `unsafe_Func` (default: false) -- compress and mangle `Function(args, code)`. + - `unsafe_math` (default: false) -- optimize numerical expressions like `2 * x * 3` into `6 * x`, which may give imprecise floating point results. diff --git a/bin/uglifyjs b/bin/uglifyjs index ef2020c5..f2aeb084 100755 --- a/bin/uglifyjs +++ b/bin/uglifyjs @@ -225,7 +225,7 @@ function run() { col = line.length; } if (line) { - var limit = 78; + var limit = 70; if (col > limit) { line = line.slice(col - limit); col = limit; diff --git a/lib/compress.js b/lib/compress.js index 374d14d4..32a4d603 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -81,6 +81,7 @@ function Compressor(options, false_by_default) { toplevel : !!(options && options["top_retain"]), unsafe : false, unsafe_comps : false, + unsafe_Func : false, unsafe_math : false, unsafe_proto : false, unsafe_regexp : false, @@ -2973,63 +2974,6 @@ merge(Compressor.prototype, { operator: "!" }).optimize(compressor); break; - case "Function": - // new Function() => function(){} - if (self.args.length == 0) return make_node(AST_Function, self, { - argnames: [], - body: [] - }); - if (all(self.args, function(x){ return x instanceof AST_String })) { - // quite a corner-case, but we can handle it: - // https://github.com/mishoo/UglifyJS2/issues/203 - // if the code argument is a constant, then we can minify it. - try { - var code = "(function(" + self.args.slice(0, -1).map(function(arg){ - return arg.value; - }).join(",") + "){" + self.args[self.args.length - 1].value + "})()"; - var ast = parse(code); - var mangle = { ie8: compressor.option("ie8") }; - ast.figure_out_scope(mangle); - var comp = new Compressor(compressor.options); - ast = ast.transform(comp); - ast.figure_out_scope(mangle); - ast.mangle_names(); - var fun; - try { - ast.walk(new TreeWalker(function(node){ - if (node instanceof AST_Lambda) { - fun = node; - throw ast; - } - })); - } catch(ex) { - if (ex !== ast) throw ex; - }; - if (!fun) return self; - var args = fun.argnames.map(function(arg, i){ - return make_node(AST_String, self.args[i], { - value: arg.print_to_string() - }); - }); - var code = OutputStream(); - AST_BlockStatement.prototype._codegen.call(fun, fun, code); - code = code.toString().replace(/^\{|\}$/g, ""); - args.push(make_node(AST_String, self.args[self.args.length - 1], { - value: code - })); - self.args = args; - return self; - } catch(ex) { - if (ex instanceof JS_Parse_Error) { - compressor.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start); - compressor.warn(ex.toString()); - } else { - console.log(ex); - throw ex; - } - } - } - break; } } else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) { @@ -3112,6 +3056,63 @@ merge(Compressor.prototype, { } } } + if (compressor.option("unsafe_Func") + && exp instanceof AST_SymbolRef + && exp.undeclared() + && exp.name == "Function") { + // new Function() => function(){} + if (self.args.length == 0) return make_node(AST_Function, self, { + argnames: [], + body: [] + }); + if (all(self.args, function(x) { + return x instanceof AST_String; + })) { + // quite a corner-case, but we can handle it: + // https://github.com/mishoo/UglifyJS2/issues/203 + // if the code argument is a constant, then we can minify it. + try { + var code = "NaN(function(" + self.args.slice(0, -1).map(function(arg) { + return arg.value; + }).join(",") + "){" + self.args[self.args.length - 1].value + "})"; + var ast = parse(code); + var mangle = { ie8: compressor.option("ie8") }; + ast.figure_out_scope(mangle); + var comp = new Compressor(compressor.options); + ast = ast.transform(comp); + ast.figure_out_scope(mangle); + ast.mangle_names(); + var fun; + ast.walk(new TreeWalker(function(node) { + if (fun) return true; + if (node instanceof AST_Lambda) { + fun = node; + return true; + } + })); + var args = fun.argnames.map(function(arg, i) { + return make_node(AST_String, self.args[i], { + value: arg.print_to_string() + }); + }); + var code = OutputStream(); + AST_BlockStatement.prototype._codegen.call(fun, fun, code); + code = code.toString().replace(/^\{|\}$/g, ""); + args.push(make_node(AST_String, self.args[self.args.length - 1], { + value: code + })); + self.args = args; + return self; + } catch (ex) { + if (ex instanceof JS_Parse_Error) { + compressor.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start); + compressor.warn(ex.toString()); + } else { + throw ex; + } + } + } + } if (exp instanceof AST_Function) { if (exp.body[0] instanceof AST_Return) { var value = exp.body[0].value; diff --git a/test/compress/functions.js b/test/compress/functions.js index 5ebd9d19..6a9f2aed 100644 --- a/test/compress/functions.js +++ b/test/compress/functions.js @@ -245,3 +245,25 @@ hoist_funs_strict: { ] node_version: ">=4" } + +issue_203: { + options = { + keep_fargs: false, + side_effects: true, + unsafe_Func: true, + unused: true, + } + input: { + var m = {}; + var fn = Function("require", "module", "exports", "module.exports = 42;"); + fn(null, m, m.exports); + console.log(m.exports); + } + expect: { + var m = {}; + var fn = Function("a", "b", "b.exports=42"); + fn(null, m, m.exports); + console.log(m.exports); + } + expect_stdout: "42" +} -- 2.34.1