inline Function() as code

- improve upon #203
- bail out on any local/global variable collisions
This commit is contained in:
alexlamsl 2017-02-04 16:01:59 +08:00
parent 7f8d72d9d3
commit 32003fb54b
3 changed files with 75 additions and 54 deletions

View File

@ -2165,53 +2165,29 @@ merge(Compressor.prototype, {
argnames: [], argnames: [],
body: [] body: []
}); });
if (all(self.args, function(x){ return x instanceof AST_String })) { // new Function("x", "return x") => function(x){return x}
// quite a corner-case, but we can handle it: try {
// https://github.com/mishoo/UglifyJS2/issues/203 var args = self.args.map(function(arg) {
// if the code argument is a constant, then we can minify it. if (!(arg instanceof AST_String)) throw self;
try { return arg.value;
var code = "(function(" + self.args.slice(0, -1).map(function(arg){ });
return arg.value; var code = args.pop();
}).join(",") + "){" + self.args[self.args.length - 1].value + "})()"; var ast = parse("(function(" + args.join() + "){" + code + "})");
var ast = parse(code); ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") });
ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") }); var scope = compressor.find_parent(AST_Scope);
var comp = new Compressor(compressor.options); ast.globals.each(function(g) {
ast = ast.transform(comp); var def = scope.find_variable(g.name);
ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") }); if (def && !(def.scope instanceof AST_Toplevel)) throw self;
ast.mangle_names(); });
var fun; var fun = ast.body[0].body;
try { fun.parent_scope = scope;
ast.walk(new TreeWalker(function(node){ return fun;
if (node instanceof AST_Lambda) { } catch(ex) {
fun = node; if (ex instanceof JS_Parse_Error) {
throw ast; 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) {
} catch(ex) { throw 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; break;

View File

@ -262,13 +262,6 @@ function makePredicate(words) {
return new Function("str", f); 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() { function Dictionary() {
this._values = Object.create(null); this._values = Object.create(null);
this._size = 0; this._size = 0;

View File

@ -6,3 +6,55 @@ non_ascii_function_identifier_name: {
} }
expect_exact: "function fooλ(δλ){}function λ(δλ){}(function λ(δλ){})();" 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 ];
};
}
}
}