Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4377e932ca | ||
|
|
bac14ba881 | ||
|
|
ec095ed647 | ||
|
|
17e73121fa | ||
|
|
f71e8fd948 | ||
|
|
3e62faa64f | ||
|
|
e9645e017f | ||
|
|
55b5f2a8aa |
19
README.md
19
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.
|
||||
|
||||
@@ -678,17 +680,18 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
|
||||
|
||||
## Mangle options
|
||||
|
||||
- `reserved` - pass an array of identifiers that should be excluded from mangling
|
||||
- `reserved` (default `[]`). Pass an array of identifiers that should be
|
||||
excluded from mangling. Example: `["foo", "bar"]`.
|
||||
|
||||
- `toplevel` — mangle names declared in the top level scope (disabled by
|
||||
default).
|
||||
- `toplevel` (default `false`). Pass `true` to mangle names declared in the
|
||||
top level scope.
|
||||
|
||||
- `eval` — mangle names visible in scopes where eval or with are used
|
||||
(disabled by default).
|
||||
- `keep_fnames` (default `false`). Pass `true` to not mangle function names.
|
||||
Useful for code relying on `Function.prototype.name`. See also: the `keep_fnames`
|
||||
[compress option](#compress-options).
|
||||
|
||||
- `keep_fnames` -- default `false`. Pass `true` to not mangle
|
||||
function names. Useful for code relying on `Function.prototype.name`.
|
||||
See also: the `keep_fnames` [compress option](#compress-options).
|
||||
- `eval` (default `false`). Pass `true` to mangle names visible in scopes
|
||||
where `eval` or `with` are used.
|
||||
|
||||
Examples:
|
||||
|
||||
|
||||
@@ -225,9 +225,10 @@ function run() {
|
||||
col = line.length;
|
||||
}
|
||||
if (line) {
|
||||
if (col > 40) {
|
||||
line = line.slice(col - 40);
|
||||
col = 40;
|
||||
var limit = 70;
|
||||
if (col > limit) {
|
||||
line = line.slice(col - limit);
|
||||
col = limit;
|
||||
}
|
||||
console.error(line.slice(0, 80));
|
||||
console.error(line.slice(0, col).replace(/\S/g, " ") + "^");
|
||||
|
||||
177
lib/compress.js
177
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,
|
||||
@@ -1664,6 +1665,63 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
throw def;
|
||||
});
|
||||
var object_fns = [
|
||||
'constructor',
|
||||
'toString',
|
||||
'valueOf',
|
||||
];
|
||||
var native_fns = {
|
||||
Array: makePredicate([
|
||||
'indexOf',
|
||||
'join',
|
||||
'lastIndexOf',
|
||||
'slice',
|
||||
].concat(object_fns)),
|
||||
Boolean: makePredicate(object_fns),
|
||||
Number: makePredicate([
|
||||
'toExponential',
|
||||
'toFixed',
|
||||
'toPrecision',
|
||||
].concat(object_fns)),
|
||||
RegExp: makePredicate([
|
||||
'test',
|
||||
].concat(object_fns)),
|
||||
String: makePredicate([
|
||||
'charAt',
|
||||
'charCodeAt',
|
||||
'concat',
|
||||
'indexOf',
|
||||
'italics',
|
||||
'lastIndexOf',
|
||||
'match',
|
||||
'replace',
|
||||
'search',
|
||||
'slice',
|
||||
'split',
|
||||
'substr',
|
||||
'substring',
|
||||
'trim',
|
||||
].concat(object_fns)),
|
||||
};
|
||||
def(AST_Call, function(compressor){
|
||||
var exp = this.expression;
|
||||
if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
|
||||
var key = exp.property;
|
||||
if (key instanceof AST_Node) {
|
||||
key = ev(key, compressor);
|
||||
}
|
||||
var val = ev(exp.expression, compressor);
|
||||
if ((val && native_fns[val.constructor.name] || return_false)(key)) {
|
||||
return val[key].apply(val, this.args.map(function(arg) {
|
||||
return ev(arg, compressor);
|
||||
}));
|
||||
}
|
||||
}
|
||||
throw def;
|
||||
});
|
||||
def(AST_New, function(compressor){
|
||||
throw def;
|
||||
});
|
||||
})(function(node, func){
|
||||
node.DEFMETHOD("_eval", func);
|
||||
});
|
||||
@@ -2973,63 +3031,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 +3113,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;
|
||||
@@ -3143,6 +3201,11 @@ merge(Compressor.prototype, {
|
||||
&& is_iife_call(self)) {
|
||||
return self.negate(compressor, true);
|
||||
}
|
||||
var ev = self.evaluate(compressor);
|
||||
if (ev !== self) {
|
||||
ev = make_node_from_constant(ev, self).optimize(compressor);
|
||||
return best_of(compressor, ev, self);
|
||||
}
|
||||
return self;
|
||||
});
|
||||
|
||||
|
||||
@@ -1234,9 +1234,8 @@ function OutputStream(options) {
|
||||
});
|
||||
else output.print("{}");
|
||||
});
|
||||
DEFPRINT(AST_ObjectKeyVal, function(self, output){
|
||||
var key = self.key;
|
||||
var quote = self.quote;
|
||||
|
||||
function print_property_name(key, quote, output) {
|
||||
if (output.option("quote_keys")) {
|
||||
output.print_string(key + "");
|
||||
} else if ((typeof key == "number"
|
||||
@@ -1253,20 +1252,24 @@ function OutputStream(options) {
|
||||
} else {
|
||||
output.print_string(key, quote);
|
||||
}
|
||||
}
|
||||
|
||||
DEFPRINT(AST_ObjectKeyVal, function(self, output){
|
||||
print_property_name(self.key, self.quote, output);
|
||||
output.colon();
|
||||
self.value.print(output);
|
||||
});
|
||||
DEFPRINT(AST_ObjectSetter, function(self, output){
|
||||
output.print("set");
|
||||
AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, output) {
|
||||
output.print(type);
|
||||
output.space();
|
||||
self.key.print(output);
|
||||
self.value._do_print(output, true);
|
||||
print_property_name(this.key.name, this.quote, output);
|
||||
this.value._do_print(output, true);
|
||||
});
|
||||
DEFPRINT(AST_ObjectSetter, function(self, output){
|
||||
self._print_getter_setter("set", output);
|
||||
});
|
||||
DEFPRINT(AST_ObjectGetter, function(self, output){
|
||||
output.print("get");
|
||||
output.space();
|
||||
self.key.print(output);
|
||||
self.value._do_print(output, true);
|
||||
self._print_getter_setter("get", output);
|
||||
});
|
||||
DEFPRINT(AST_Symbol, function(self, output){
|
||||
var def = self.definition();
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"homepage": "http://lisperator.net/uglifyjs",
|
||||
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
||||
"license": "BSD-2-Clause",
|
||||
"version": "3.0.13",
|
||||
"version": "3.0.15",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
},
|
||||
|
||||
@@ -780,13 +780,15 @@ unsafe_charAt_noop: {
|
||||
input: {
|
||||
console.log(
|
||||
s.charAt(0),
|
||||
"string".charAt(x)
|
||||
"string".charAt(x),
|
||||
(typeof x).charAt()
|
||||
);
|
||||
}
|
||||
expect: {
|
||||
console.log(
|
||||
s.charAt(0),
|
||||
"string".charAt(x)
|
||||
"string".charAt(x),
|
||||
(typeof x)[0]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1037,3 +1039,31 @@ issue_1964_2: {
|
||||
}
|
||||
expect_stdout: "b"
|
||||
}
|
||||
|
||||
array_slice_index: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
console.log([1,2,3].slice(1)[1]);
|
||||
}
|
||||
expect: {
|
||||
console.log(3);
|
||||
}
|
||||
expect_stdout: "3"
|
||||
}
|
||||
|
||||
string_charCodeAt: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
console.log("foo".charCodeAt("bar".length));
|
||||
}
|
||||
expect: {
|
||||
console.log(NaN);
|
||||
}
|
||||
expect_stdout: "NaN"
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -555,3 +555,20 @@ native_prototype: {
|
||||
"".indexOf.call(e, "bar");
|
||||
}
|
||||
}
|
||||
|
||||
issue_2040: {
|
||||
input: {
|
||||
var a = 1;
|
||||
var b = {
|
||||
get "a-b"() {
|
||||
return a;
|
||||
},
|
||||
set "a-b"(c) {
|
||||
a = c;
|
||||
}
|
||||
};
|
||||
console.log(b["a-b"], b["a-b"] = 2, b["a-b"]);
|
||||
}
|
||||
expect_exact: 'var a=1;var b={get"a-b"(){return a},set"a-b"(c){a=c}};console.log(b["a-b"],b["a-b"]=2,b["a-b"]);'
|
||||
expect_stdout: "1 2 2"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user