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: [],
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;

View File

@ -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;

View File

@ -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 ];
};
}
}
}