From 32003fb54b3b6484dddcccf8ba647f0aa4ec2005 Mon Sep 17 00:00:00 2001 From: alexlamsl Date: Sat, 4 Feb 2017 16:01:59 +0800 Subject: [PATCH] inline Function() as code - improve upon #203 - bail out on any local/global variable collisions --- lib/compress.js | 70 +++++++++++++------------------------- lib/utils.js | 7 ---- test/compress/functions.js | 52 ++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 54 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 4e45df92..614b1ebd 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -2165,53 +2165,29 @@ merge(Compressor.prototype, { 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); - ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") }); - var comp = new Compressor(compressor.options); - ast = ast.transform(comp); - ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") }); - 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; - } + // new Function("x", "return x") => function(x){return x} + try { + var args = self.args.map(function(arg) { + if (!(arg instanceof AST_String)) throw self; + return arg.value; + }); + var code = args.pop(); + var ast = parse("(function(" + args.join() + "){" + code + "})"); + ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") }); + var scope = compressor.find_parent(AST_Scope); + ast.globals.each(function(g) { + var def = scope.find_variable(g.name); + if (def && !(def.scope instanceof AST_Toplevel)) throw self; + }); + var fun = ast.body[0].body; + fun.parent_scope = scope; + return fun; + } 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 if (ex !== self) { + throw ex; } } break; diff --git a/lib/utils.js b/lib/utils.js index d0a21ac9..d85ac9a1 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -262,13 +262,6 @@ function makePredicate(words) { return new Function("str", f); }; -function all(array, predicate) { - for (var i = array.length; --i >= 0;) - if (!predicate(array[i])) - return false; - return true; -}; - function Dictionary() { this._values = Object.create(null); this._size = 0; diff --git a/test/compress/functions.js b/test/compress/functions.js index 3a8701b7..b8652ae0 100644 --- a/test/compress/functions.js +++ b/test/compress/functions.js @@ -6,3 +6,55 @@ non_ascii_function_identifier_name: { } expect_exact: "function fooλ(δλ){}function λ(δλ){}(function λ(δλ){})();" } + +unsafe_eval: { + options = { + unsafe: true, + } + input: { + var a = Function("x", "return x"); + + function e() { + var a; + return Function("return a"); + } + + function f() { + return Function("return a"); + } + + function g(a) { + return Function("b", "return [ a, b ]"); + } + + function h(b) { + return Function("b", "c", "return [ a, b, c ]"); + } + } + expect: { + var a = function(x) { + return x; + }; + + function e() { + var a; + return Function("return a"); + } + + function f() { + return function() { + return a; + }; + } + + function g(a) { + return Function("b", "return [ a, b ]"); + } + + function h(b) { + return function(b, c) { + return [ a, b, c ]; + }; + } + } +}