Merge branch 'master' into harmony-v3.0.16

This commit is contained in:
alexlamsl
2017-06-14 17:09:30 +08:00
35 changed files with 1155 additions and 208 deletions

View File

@@ -4,8 +4,11 @@ node_js:
- "0.12" - "0.12"
- "4" - "4"
- "6" - "6"
- "8"
env: env:
- UGLIFYJS_TEST_ALL=1 - UGLIFYJS_TEST_ALL=1
matrix: matrix:
fast_finish: true fast_finish: true
sudo: false sudo: false
cache:
directories: tmp

View File

@@ -569,7 +569,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 comparison are switching. Compression only works if both `comparisons` and
`unsafe_comps` are both set to true. `unsafe_comps` are both set to true.
- `unsafe_Func` (default: false) -- compress and mangle `Function(args, code)`. - `unsafe_Func` (default: false) -- compress and mangle `Function(args, code)`
when both `args` and `code` are string literals.
- `unsafe_math` (default: false) -- optimize numerical expressions like - `unsafe_math` (default: false) -- optimize numerical expressions like
`2 * x * 3` into `6 * x`, which may give imprecise floating point results. `2 * x * 3` into `6 * x`, which may give imprecise floating point results.
@@ -612,6 +613,8 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `if_return` -- optimizations for if/return and if/continue - `if_return` -- optimizations for if/return and if/continue
- `inline` -- embed simple functions
- `join_vars` -- join consecutive `var` statements - `join_vars` -- join consecutive `var` statements
- `cascade` -- small optimization for sequences, transform `x, x` into `x` - `cascade` -- small optimization for sequences, transform `x, x` into `x`
@@ -767,7 +770,7 @@ can pass additional arguments that control the code output:
- `quote_style` (default `0`) -- preferred quote style for strings (affects - `quote_style` (default `0`) -- preferred quote style for strings (affects
quoted property names and directives as well): quoted property names and directives as well):
- `0` -- prefers double quotes, switches to single quotes when there are - `0` -- prefers double quotes, switches to single quotes when there are
more double quotes in the string itself. more double quotes in the string itself. `0` is best for gzip size.
- `1` -- always use single quotes - `1` -- always use single quotes
- `2` -- always use double quotes - `2` -- always use double quotes
- `3` -- always use the original quotes - `3` -- always use the original quotes

View File

@@ -30,14 +30,7 @@ else if (process.argv.indexOf("options") >= 0) program.helpInformation = functio
var options = UglifyJS.default_options(); var options = UglifyJS.default_options();
for (var option in options) { for (var option in options) {
text.push("--" + (option == "output" ? "beautify" : option == "sourceMap" ? "source-map" : option) + " options:"); text.push("--" + (option == "output" ? "beautify" : option == "sourceMap" ? "source-map" : option) + " options:");
var defs = options[option]; text.push(format_object(options[option]));
var padding = "";
Object.keys(defs).map(function(name) {
if (padding.length < name.length) padding = Array(name.length + 1).join(" ");
return [ name, JSON.stringify(defs[name]) ];
}).forEach(function(tokens) {
text.push(" " + tokens[0] + padding.slice(tokens[0].length - 2) + tokens[1]);
});
text.push(""); text.push("");
} }
return text.join("\n"); return text.join("\n");
@@ -157,7 +150,7 @@ if (program.verbose) {
} }
if (program.self) { if (program.self) {
if (program.args.length) { if (program.args.length) {
console.error("WARN: Ignoring input files since --self was passed"); print_error("WARN: Ignoring input files since --self was passed");
} }
if (!options.wrap) options.wrap = "UglifyJS"; if (!options.wrap) options.wrap = "UglifyJS";
simple_glob(UglifyJS.FILES).forEach(function(name) { simple_glob(UglifyJS.FILES).forEach(function(name) {
@@ -187,7 +180,7 @@ function convert_ast(fn) {
function run() { function run() {
UglifyJS.AST_Node.warn_function = function(msg) { UglifyJS.AST_Node.warn_function = function(msg) {
console.error("WARN:", msg); print_error("WARN: " + msg);
}; };
if (program.timings) options.timings = true; if (program.timings) options.timings = true;
try { try {
@@ -216,7 +209,7 @@ function run() {
if (result.error) { if (result.error) {
var ex = result.error; var ex = result.error;
if (ex.name == "SyntaxError") { if (ex.name == "SyntaxError") {
console.error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col); print_error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col);
var col = ex.col; var col = ex.col;
var lines = files[ex.filename].split(/\r?\n/); var lines = files[ex.filename].split(/\r?\n/);
var line = lines[ex.line - 1]; var line = lines[ex.line - 1];
@@ -230,17 +223,17 @@ function run() {
line = line.slice(col - limit); line = line.slice(col - limit);
col = limit; col = limit;
} }
console.error(line.slice(0, 80)); print_error(line.slice(0, 80));
console.error(line.slice(0, col).replace(/\S/g, " ") + "^"); print_error(line.slice(0, col).replace(/\S/g, " ") + "^");
} }
} }
if (ex.defs) { if (ex.defs) {
console.error("Supported options:"); print_error("Supported options:");
console.error(ex.defs); print_error(format_object(ex.defs));
} }
fatal(ex); fatal(ex);
} else if (program.output == "ast") { } else if (program.output == "ast") {
console.log(JSON.stringify(result.ast, function(key, value) { print(JSON.stringify(result.ast, function(key, value) {
if (skip_key(key)) return; if (skip_key(key)) return;
if (value instanceof UglifyJS.AST_Token) return; if (value instanceof UglifyJS.AST_Token) return;
if (value instanceof UglifyJS.Dictionary) return; if (value instanceof UglifyJS.Dictionary) return;
@@ -256,7 +249,7 @@ function run() {
return value; return value;
}, 2)); }, 2));
} else if (program.output == "spidermonkey") { } else if (program.output == "spidermonkey") {
console.log(JSON.stringify(UglifyJS.minify(result.code, { print(JSON.stringify(UglifyJS.minify(result.code, {
compress: false, compress: false,
mangle: false, mangle: false,
output: { output: {
@@ -270,7 +263,7 @@ function run() {
fs.writeFileSync(program.output + ".map", result.map); fs.writeFileSync(program.output + ".map", result.map);
} }
} else { } else {
console.log(result.code); print(result.code);
} }
if (program.nameCache) { if (program.nameCache) {
fs.writeFileSync(program.nameCache, JSON.stringify(cache, function(key, value) { fs.writeFileSync(program.nameCache, JSON.stringify(cache, function(key, value) {
@@ -278,13 +271,13 @@ function run() {
})); }));
} }
if (result.timings) for (var phase in result.timings) { if (result.timings) for (var phase in result.timings) {
console.error("- " + phase + ": " + result.timings[phase].toFixed(3) + "s"); print_error("- " + phase + ": " + result.timings[phase].toFixed(3) + "s");
} }
} }
function fatal(message) { function fatal(message) {
if (message instanceof Error) message = message.stack.replace(/^\S*?Error:/, "ERROR:") if (message instanceof Error) message = message.stack.replace(/^\S*?Error:/, "ERROR:")
console.error(message); print_error(message);
process.exit(1); process.exit(1);
} }
@@ -378,10 +371,10 @@ function parse_js(flag, constants) {
function parse_source_map() { function parse_source_map() {
var parse = parse_js("sourceMap", true); var parse = parse_js("sourceMap", true);
return function(value, options) { return function(value, options) {
var hasContent = options && options.sourceMap && "content" in options.sourceMap; var hasContent = options && "content" in options;
var settings = parse(value, options); var settings = parse(value, options);
if (!hasContent && settings.content && settings.content != "inline") { if (!hasContent && settings.content && settings.content != "inline") {
console.error("INFO: Using input source map:", settings.content); print_error("INFO: Using input source map: " + settings.content);
settings.content = read_file(settings.content, settings.content); settings.content = read_file(settings.content, settings.content);
} }
return settings; return settings;
@@ -403,3 +396,25 @@ function to_cache(key) {
function skip_key(key) { function skip_key(key) {
return skip_keys.indexOf(key) >= 0; return skip_keys.indexOf(key) >= 0;
} }
function format_object(obj) {
var lines = [];
var padding = "";
Object.keys(obj).map(function(name) {
if (padding.length < name.length) padding = Array(name.length + 1).join(" ");
return [ name, JSON.stringify(obj[name]) ];
}).forEach(function(tokens) {
lines.push(" " + tokens[0] + padding.slice(tokens[0].length - 2) + tokens[1]);
});
return lines.join("\n");
}
function print_error(msg) {
process.stderr.write(msg);
process.stderr.write("\n");
}
function print(txt) {
process.stdout.write(txt);
process.stdout.write("\n");
}

View File

@@ -64,6 +64,7 @@ function Compressor(options, false_by_default) {
hoist_vars : false, hoist_vars : false,
ie8 : false, ie8 : false,
if_return : !false_by_default, if_return : !false_by_default,
inline : !false_by_default,
join_vars : !false_by_default, join_vars : !false_by_default,
keep_fargs : true, keep_fargs : true,
keep_fnames : false, keep_fnames : false,
@@ -1459,7 +1460,7 @@ merge(Compressor.prototype, {
}); });
if (value && typeof value == "object") { if (value && typeof value == "object") {
var props = []; var props = [];
for (var key in value) { for (var key in value) if (HOP(value, key)) {
props.push(make_node(AST_ObjectKeyVal, orig, { props.push(make_node(AST_ObjectKeyVal, orig, {
key: key, key: key,
value: to_node(value[key], orig) value: to_node(value[key], orig)
@@ -3112,24 +3113,9 @@ merge(Compressor.prototype, {
OPT(AST_Call, function(self, compressor){ OPT(AST_Call, function(self, compressor){
var exp = self.expression; var exp = self.expression;
if (compressor.option("reduce_vars") if (compressor.option("reduce_vars") && exp instanceof AST_SymbolRef) {
&& exp instanceof AST_SymbolRef) {
var def = exp.definition();
var fixed = exp.fixed_value(); var fixed = exp.fixed_value();
if (fixed instanceof AST_Defun) { if (fixed instanceof AST_Function) exp = fixed;
def.fixed = fixed = make_node(AST_Function, fixed, fixed).clone(true);
}
if (fixed instanceof AST_Function) {
exp = fixed;
if (compressor.option("unused")
&& def.references.length == 1
&& !(def.scope.uses_arguments
&& def.orig[0] instanceof AST_SymbolFunarg)
&& !def.scope.uses_eval
&& compressor.find_parent(AST_Scope) === def.scope) {
self.expression = exp;
}
}
} }
if (compressor.option("unused") if (compressor.option("unused")
&& exp instanceof AST_Function && exp instanceof AST_Function
@@ -3344,13 +3330,70 @@ merge(Compressor.prototype, {
} }
} }
if (exp instanceof AST_Function && !self.expression.is_generator) { if (exp instanceof AST_Function && !self.expression.is_generator) {
if (exp.body[0] instanceof AST_Return) { var stat = exp.body[0];
var value = exp.body[0].value; if (compressor.option("inline") && stat instanceof AST_Return) {
var value = stat && stat.value;
if (!value || value.is_constant_expression()) { if (!value || value.is_constant_expression()) {
var args = self.args.concat(value || make_node(AST_Undefined, self)); var args = self.args.concat(value || make_node(AST_Undefined, self));
return make_sequence(self, args).transform(compressor); return make_sequence(self, args).transform(compressor);
} }
} }
if (compressor.option("inline")
&& !exp.name
&& exp.body.length == 1
&& !exp.uses_arguments
&& !exp.uses_eval
&& !self.has_pure_annotation(compressor)) {
var value;
if (stat instanceof AST_Return) {
value = stat.value.clone(true);
} else if (stat instanceof AST_SimpleStatement) {
value = make_node(AST_UnaryPrefix, stat, {
operator: "void",
expression: stat.body.clone(true)
});
}
if (value) {
var fn = exp.clone();
fn.argnames = [];
fn.body = [];
if (exp.argnames.length > 0) {
fn.body.push(make_node(AST_Var, self, {
definitions: exp.argnames.map(function(sym, i) {
return make_node(AST_VarDef, sym, {
name: sym,
value: self.args[i] || make_node(AST_Undefined, self)
});
})
}));
}
if (self.args.length > exp.argnames.length) {
fn.body.push(make_node(AST_SimpleStatement, self, {
body: make_sequence(self, self.args.slice(exp.argnames.length))
}));
}
fn.body.push(make_node(AST_Return, self, {
value: value
}));
var body = fn.transform(compressor).body;
if (body.length == 0) return make_node(AST_Undefined, self);
if (body.length == 1 && body[0] instanceof AST_Return) {
value = body[0].value;
if (!value) return make_node(AST_Undefined, self);
value.walk(new TreeWalker(function(node) {
if (value === self) return true;
if (node instanceof AST_SymbolRef && exp.variables.has(node.name)) {
value = self;
return true;
}
}));
if (value !== self) value = best_of(compressor, value, self);
} else {
value = self;
}
if (value !== self) return value;
}
}
if (compressor.option("side_effects") && all(exp.body, is_empty)) { if (compressor.option("side_effects") && all(exp.body, is_empty)) {
var args = self.args.concat(make_node(AST_Undefined, self)); var args = self.args.concat(make_node(AST_Undefined, self));
return make_sequence(self, args).transform(compressor); return make_sequence(self, args).transform(compressor);
@@ -3453,6 +3496,7 @@ merge(Compressor.prototype, {
continue; continue;
} }
var parent = null, field; var parent = null, field;
expressions[j] = cdr = cdr.clone();
while (true) { while (true) {
if (cdr.equivalent_to(left)) { if (cdr.equivalent_to(left)) {
var car = expressions[i]; var car = expressions[i];
@@ -3489,7 +3533,7 @@ merge(Compressor.prototype, {
break; break;
} }
parent = cdr; parent = cdr;
cdr = cdr[field]; cdr = cdr[field] = cdr[field].clone();
} }
} }
end = i; end = i;
@@ -4009,12 +4053,22 @@ merge(Compressor.prototype, {
return make_node(AST_Infinity, self).optimize(compressor); return make_node(AST_Infinity, self).optimize(compressor);
} }
} }
if (compressor.option("evaluate") if (compressor.option("reduce_vars")
&& compressor.option("reduce_vars")
&& is_lhs(self, compressor.parent()) !== self) { && is_lhs(self, compressor.parent()) !== self) {
var d = self.definition(); var d = self.definition();
var fixed = self.fixed_value(); var fixed = self.fixed_value();
if (fixed) { if (fixed instanceof AST_Defun) {
d.fixed = fixed = make_node(AST_Function, fixed, fixed).clone(true);
}
if (compressor.option("unused")
&& fixed instanceof AST_Function
&& d.references.length == 1
&& !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg)
&& !d.scope.uses_eval
&& compressor.find_parent(AST_Scope) === fixed.parent_scope) {
return fixed;
}
if (compressor.option("evaluate") && fixed) {
if (d.should_replace === undefined) { if (d.should_replace === undefined) {
var init = fixed.evaluate(compressor); var init = fixed.evaluate(compressor);
if (init !== fixed && (compressor.option("unsafe_regexp") || !(init instanceof RegExp))) { if (init !== fixed && (compressor.option("unsafe_regexp") || !(init instanceof RegExp))) {

View File

@@ -88,7 +88,7 @@ function minify(files, options) {
} }
options.parse = options.parse || {}; options.parse = options.parse || {};
options.parse.toplevel = null; options.parse.toplevel = null;
for (var name in files) { for (var name in files) if (HOP(files, name)) {
options.parse.filename = name; options.parse.filename = name;
options.parse.toplevel = parse(files[name], options.parse); options.parse.toplevel = parse(files[name], options.parse);
if (options.sourceMap && options.sourceMap.content == "inline") { if (options.sourceMap && options.sourceMap.content == "inline") {
@@ -136,7 +136,7 @@ function minify(files, options) {
if (options.sourceMap.includeSources) { if (options.sourceMap.includeSources) {
if (files instanceof AST_Toplevel) { if (files instanceof AST_Toplevel) {
throw new Error("original source content unavailable"); throw new Error("original source content unavailable");
} else for (var name in files) { } else for (var name in files) if (HOP(files, name)) {
options.output.source_map.get().setSourceContent(name, files[name]); options.output.source_map.get().setSourceContent(name, files[name]);
} }
} }

View File

@@ -73,6 +73,7 @@ function OutputStream(options) {
shebang : true, shebang : true,
shorthand : undefined, shorthand : undefined,
source_map : null, source_map : null,
webkit : false,
width : 80, width : 80,
wrap_iife : false, wrap_iife : false,
}, true); }, true);
@@ -629,6 +630,13 @@ function OutputStream(options) {
return true; return true;
} }
if (output.option('webkit')) {
var p = output.parent();
if (p instanceof AST_PropAccess && p.expression === this) {
return true;
}
}
if (output.option('wrap_iife')) { if (output.option('wrap_iife')) {
var p = output.parent(); var p = output.parent();
return p instanceof AST_Call && p.expression === this; return p instanceof AST_Call && p.expression === this;

View File

@@ -122,8 +122,6 @@ var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,;:"));
var PUNC_CHARS = makePredicate(characters("[]{}(),;:")); var PUNC_CHARS = makePredicate(characters("[]{}(),;:"));
var REGEXP_MODIFIERS = makePredicate(characters("gmsiy"));
/* -----[ Tokenizer ]----- */ /* -----[ Tokenizer ]----- */
// surrogate safe regexps adapted from https://github.com/mathiasbynens/unicode-8.0.0/tree/89b412d8a71ecca9ed593d9e9fa073ab64acfebe/Binary_Property // surrogate safe regexps adapted from https://github.com/mathiasbynens/unicode-8.0.0/tree/89b412d8a71ecca9ed593d9e9fa073ab64acfebe/Binary_Property
@@ -850,9 +848,7 @@ var PRECEDENCE = (function(a, ret){
{} {}
); );
var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]); var ATOMIC_START_TOKEN = makePredicate([ "atom", "num", "string", "regexp", "name" ]);
var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]);
/* -----[ Parser ]----- */ /* -----[ Parser ]----- */
@@ -1872,7 +1868,6 @@ function parse($TEXT, options) {
var tok = S.token, ret; var tok = S.token, ret;
switch (tok.type) { switch (tok.type) {
case "name": case "name":
case "keyword":
ret = _make_symbol(AST_SymbolRef); ret = _make_symbol(AST_SymbolRef);
break; break;
case "num": case "num":
@@ -1902,13 +1897,6 @@ function parse($TEXT, options) {
break; break;
} }
break; break;
case "operator":
if (!is_identifier_string(tok.value)) {
croak("Invalid getter/setter name: " + tok.value,
tok.line, tok.col, tok.pos);
}
ret = _make_symbol(AST_SymbolRef);
break;
} }
next(); next();
return ret; return ret;
@@ -2015,7 +2003,7 @@ function parse($TEXT, options) {
if (is("template_head")) { if (is("template_head")) {
return subscripts(template_string(), allow_calls); return subscripts(template_string(), allow_calls);
} }
if (ATOMIC_START_TOKEN[S.token.type]) { if (ATOMIC_START_TOKEN(S.token.type)) {
return subscripts(as_atom_node(), allow_calls); return subscripts(as_atom_node(), allow_calls);
} }
unexpected(); unexpected();

View File

@@ -96,7 +96,8 @@ function mangle_properties(ast, options) {
reserved: null, reserved: null,
}); });
var reserved = options.reserved || []; var reserved = options.reserved;
if (!Array.isArray(reserved)) reserved = [];
if (!options.builtins) find_builtins(reserved); if (!options.builtins) find_builtins(reserved);
var cache = options.cache; var cache = options.cache;

View File

@@ -450,7 +450,7 @@ AST_Symbol.DEFMETHOD("global", function(){
}); });
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
return defaults(options, { options = defaults(options, {
eval : false, eval : false,
ie8 : false, ie8 : false,
keep_classnames: false, keep_classnames: false,
@@ -458,6 +458,8 @@ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
reserved : [], reserved : [],
toplevel : false, toplevel : false,
}); });
if (!Array.isArray(options.reserved)) options.reserved = [];
return options;
}); });
AST_Toplevel.DEFMETHOD("mangle_names", function(options){ AST_Toplevel.DEFMETHOD("mangle_names", function(options){

View File

@@ -43,13 +43,6 @@
"use strict"; "use strict";
function array_to_hash(a) {
var ret = Object.create(null);
for (var i = 0; i < a.length; ++i)
ret[a[i]] = true;
return ret;
};
function slice(a, start) { function slice(a, start) {
return Array.prototype.slice.call(a, start || 0); return Array.prototype.slice.call(a, start || 0);
}; };

View File

@@ -4,7 +4,7 @@
"homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony", "homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)", "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"version": "3.0.15", "version": "3.0.16",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },

View File

@@ -4,6 +4,7 @@
"use strict"; "use strict";
var createHash = require("crypto").createHash; var createHash = require("crypto").createHash;
var fetch = require("./fetch");
var fork = require("child_process").fork; var fork = require("child_process").fork;
var args = process.argv.slice(2); var args = process.argv.slice(2);
if (!args.length) { if (!args.length) {
@@ -52,7 +53,8 @@ urls.forEach(function(url) {
output: 0, output: 0,
log: "" log: ""
}; };
require(url.slice(0, url.indexOf(":"))).get(url, function(res) { fetch(url, function(err, res) {
if (err) throw err;
var uglifyjs = fork("bin/uglifyjs", args, { silent: true }); var uglifyjs = fork("bin/uglifyjs", args, { silent: true });
res.on("data", function(data) { res.on("data", function(data) {
results[url].input += data.length; results[url].input += data.length;

View File

@@ -1146,7 +1146,7 @@ collapse_vars_constants: {
function f3(x) { function f3(x) {
var b = x.prop; var b = x.prop;
sideeffect1(); sideeffect1();
return b + -9; return b + (function() { return -9; })();
} }
} }
} }

View File

@@ -863,12 +863,12 @@ issue_1583: {
expect: { expect: {
function m(t) { function m(t) {
(function(e) { (function(e) {
t = (function() { t = e();
return (function(a) { })(function() {
return a; return (function(a) {
})(function(a) {}); return a;
})(); })(function(a) {});
})(); });
} }
} }
} }

View File

@@ -738,6 +738,7 @@ unsafe_prototype_function: {
call_args: { call_args: {
options = { options = {
evaluate: true, evaluate: true,
inline: true,
reduce_vars: true, reduce_vars: true,
} }
input: { input: {
@@ -758,6 +759,7 @@ call_args: {
call_args_drop_param: { call_args_drop_param: {
options = { options = {
evaluate: true, evaluate: true,
inline: true,
keep_fargs: false, keep_fargs: false,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,

View File

@@ -21,6 +21,7 @@ iifes_returning_constants_keep_fargs_true: {
join_vars : true, join_vars : true,
reduce_vars : true, reduce_vars : true,
cascade : true, cascade : true,
inline : true,
} }
input: { input: {
(function(){ return -1.23; }()); (function(){ return -1.23; }());
@@ -56,6 +57,7 @@ iifes_returning_constants_keep_fargs_false: {
join_vars : true, join_vars : true,
reduce_vars : true, reduce_vars : true,
cascade : true, cascade : true,
inline : true,
} }
input: { input: {
(function(){ return -1.23; }()); (function(){ return -1.23; }());
@@ -82,6 +84,7 @@ issue_485_crashing_1530: {
conditionals: true, conditionals: true,
dead_code: true, dead_code: true,
evaluate: true, evaluate: true,
inline: true,
} }
input: { input: {
(function(a) { (function(a) {
@@ -154,6 +157,7 @@ function_returning_constant_literal: {
evaluate: true, evaluate: true,
cascade: true, cascade: true,
unused: true, unused: true,
inline: true,
} }
input: { input: {
function greeter() { function greeter() {
@@ -267,3 +271,68 @@ issue_203: {
} }
expect_stdout: "42" expect_stdout: "42"
} }
no_webkit: {
beautify = {
webkit: false,
}
input: {
console.log(function() {
1 + 1;
}.a = 1);
}
expect_exact: "console.log(function(){1+1}.a=1);"
expect_stdout: "1"
}
webkit: {
beautify = {
webkit: true,
}
input: {
console.log(function() {
1 + 1;
}.a = 1);
}
expect_exact: "console.log((function(){1+1}).a=1);"
expect_stdout: "1"
}
issue_2084: {
options = {
collapse_vars: true,
conditionals: true,
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
var c = 0;
!function() {
!function(c) {
c = 1 + c;
var c = 0;
function f14(a_1) {
if (c = 1 + c, 0 !== 23..toString())
c = 1 + c, a_1 && (a_1[0] = 0);
}
f14();
}(-1);
}();
console.log(c);
}
expect: {
var c = 0;
!function(c) {
c = 1 + c,
c = 1 + (c = 0),
0 !== 23..toString() && (c = 1 + c);
}(-1),
console.log(c);
}
expect_stdout: "0"
}

View File

@@ -1,6 +1,7 @@
unary_prefix: { unary_prefix: {
options = { options = {
evaluate: true, evaluate: true,
inline: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
} }

483
test/compress/issue-281.js Normal file
View File

@@ -0,0 +1,483 @@
collapse_vars_constants: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
reduce_vars: true,
unused: true,
}
input: {
function f1(x) {
var a = 4, b = x.prop, c = 5, d = sideeffect1(), e = sideeffect2();
return b + (function() { return d - a * e - c; })();
}
function f2(x) {
var a = 4, b = x.prop, c = 5, not_used = sideeffect1(), e = sideeffect2();
return b + (function() { return -a * e - c; })();
}
}
expect: {
function f1(x) {
var b = x.prop, d = sideeffect1(), e = sideeffect2();
return b + (d - 4 * e - 5);
}
function f2(x) {
var b = x.prop;
sideeffect1();
return b + (-4 * sideeffect2() - 5);
}
}
}
modified: {
options = {
collapse_vars: true,
inline: true,
unused: true,
}
input: {
function f5(b) {
var a = function() {
return b;
}();
return b++ + a;
}
console.log(f5(1));
}
expect: {
function f5(b) {
var a = b;
return b++ + a;
}
console.log(f5(1));
}
expect_stdout: "2"
}
ref_scope: {
options = {
collapse_vars: true,
inline: true,
unused: true,
}
input: {
console.log(function() {
var a = 1, b = 2, c = 3;
var a = c++, b = b /= a;
return function() {
return a;
}() + b;
}());
}
expect: {
console.log(function() {
var a = 1, b = 2, c = 3;
b = b /= a = c++;
return a + b;
}());
}
expect_stdout: true
}
safe_undefined: {
options = {
conditionals: true,
if_return: true,
inline: true,
unsafe: false,
unused: true,
}
mangle = {}
input: {
var a, c;
console.log(function(undefined) {
return function() {
if (a)
return b;
if (c)
return d;
};
}(1)());
}
expect: {
var a, c;
console.log(a ? b : c ? d : void 0);
}
expect_stdout: true
}
negate_iife_3: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: true,
}
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
}
expect: {
t ? console.log(true) : console.log(false);
}
}
negate_iife_3_off: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: false,
}
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
}
expect: {
t ? console.log(true) : console.log(false);
}
}
negate_iife_4: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: true,
sequences: true,
}
input: {
(function(){ return t })() ? console.log(true) : console.log(false);
(function(){
console.log("something");
})();
}
expect: {
t ? console.log(true) : console.log(false), void console.log("something");
}
}
negate_iife_5: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: true,
sequences: true,
}
input: {
if ((function(){ return t })()) {
foo(true);
} else {
bar(false);
}
(function(){
console.log("something");
})();
}
expect: {
t ? foo(true) : bar(false), void console.log("something");
}
}
negate_iife_5_off: {
options = {
conditionals: true,
expression: true,
inline: true,
negate_iife: false,
sequences: true,
};
input: {
if ((function(){ return t })()) {
foo(true);
} else {
bar(false);
}
(function(){
console.log("something");
})();
}
expect: {
t ? foo(true) : bar(false), void console.log("something");
}
}
issue_1254_negate_iife_true: {
options = {
expression: true,
inline: true,
negate_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()();
}
expect_exact: 'void console.log("test");'
expect_stdout: true
}
issue_1254_negate_iife_nested: {
options = {
expression: true,
inline: true,
negate_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()()()()();
}
expect_exact: '(void console.log("test"))()()();'
}
negate_iife_issue_1073: {
options = {
conditionals: true,
evaluate: true,
inline: true,
negate_iife: true,
reduce_vars: true,
sequences: true,
unused: true,
};
input: {
new (function(a) {
return function Foo() {
this.x = a;
console.log(this);
};
}(7))();
}
expect: {
new function() {
this.x = 7,
console.log(this);
}();
}
expect_stdout: true
}
issue_1288_side_effects: {
options = {
conditionals: true,
evaluate: true,
inline: true,
negate_iife: true,
reduce_vars: true,
side_effects: true,
unused: true,
};
input: {
if (w) ;
else {
(function f() {})();
}
if (!x) {
(function() {
x = {};
})();
}
if (y)
(function() {})();
else
(function(z) {
return z;
})(0);
}
expect: {
w;
x || (x = {});
y;
}
}
inner_var_for_in_1: {
options = {
evaluate: true,
inline: true,
reduce_vars: true,
}
input: {
function f() {
var a = 1, b = 2;
for (b in (function() {
return x(a, b, c);
})()) {
var c = 3, d = 4;
x(a, b, c, d);
}
x(a, b, c, d);
}
}
expect: {
function f() {
var a = 1, b = 2;
for (b in x(1, b, c)) {
var c = 3, d = 4;
x(1, b, c, d);
}
x(1, b, c, d);
}
}
}
issue_1595_3: {
options = {
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
unused: true,
}
input: {
(function f(a) {
return g(a + 1);
})(2);
}
expect: {
g(3);
}
}
issue_1758: {
options = {
inline: true,
sequences: true,
side_effects: true,
}
input: {
console.log(function(c) {
var undefined = 42;
return function() {
c--;
c--, c.toString();
return;
}();
}());
}
expect: {
console.log(function(c) {
var undefined = 42;
return c--, c--, void c.toString();
}());
}
expect_stdout: "undefined"
}
wrap_iife: {
options = {
inline: true,
negate_iife: false,
}
beautify = {
wrap_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()();
}
expect_exact: 'void console.log("test");'
}
wrap_iife_in_expression: {
options = {
inline: true,
negate_iife: false,
}
beautify = {
wrap_iife: true,
}
input: {
foo = (function () {
return bar();
})();
}
expect_exact: 'foo=bar();'
}
wrap_iife_in_return_call: {
options = {
inline: true,
negate_iife: false,
}
beautify = {
wrap_iife: true,
}
input: {
(function() {
return (function() {
console.log('test')
})();
})()();
}
expect_exact: '(void console.log("test"))();'
}
pure_annotation: {
options = {
inline: true,
side_effects: true,
}
input: {
/*@__PURE__*/(function() {
console.log("hello");
}());
}
expect_exact: ""
}
drop_fargs: {
options = {
cascade: true,
inline: true,
keep_fargs: false,
side_effects: true,
unused: true,
}
input: {
var a = 1;
!function(a_1) {
a++;
}(a++ + (a && a.var));
console.log(a);
}
expect: {
var a = 1;
!function() {
a++;
}(++a && a.var);
console.log(a);
}
expect_stdout: "3"
}
keep_fargs: {
options = {
cascade: true,
inline: true,
keep_fargs: true,
side_effects: true,
unused: true,
}
input: {
var a = 1;
!function(a_1) {
a++;
}(a++ + (a && a.var));
console.log(a);
}
expect: {
var a = 1;
!function(a_1) {
a++;
}(++a && a.var);
console.log(a);
}
expect_stdout: "3"
}

View File

@@ -22,7 +22,8 @@ negate_iife_1_off: {
negate_iife_2: { negate_iife_2: {
options = { options = {
negate_iife: true inline: true,
negate_iife: true,
}; };
input: { input: {
(function(){ return {} })().x = 10; (function(){ return {} })().x = 10;
@@ -32,6 +33,7 @@ negate_iife_2: {
negate_iife_2_side_effects: { negate_iife_2_side_effects: {
options = { options = {
inline: true,
negate_iife: true, negate_iife: true,
side_effects: true, side_effects: true,
} }
@@ -58,6 +60,7 @@ negate_iife_3_evaluate: {
options = { options = {
conditionals: true, conditionals: true,
evaluate: true, evaluate: true,
inline: true,
negate_iife: true, negate_iife: true,
} }
input: { input: {
@@ -100,6 +103,7 @@ negate_iife_3_off_evaluate: {
options = { options = {
conditionals: true, conditionals: true,
evaluate: true, evaluate: true,
inline: true,
negate_iife: false, negate_iife: false,
} }
input: { input: {

View File

@@ -1,4 +1,4 @@
eval_let: { eval_let_6: {
input: { input: {
eval("let a;"); eval("let a;");
console.log(); console.log();
@@ -10,3 +10,29 @@ eval_let: {
expect_stdout: "" expect_stdout: ""
node_version: ">=6" node_version: ">=6"
} }
eval_let_4: {
input: {
eval("let a;");
console.log();
}
expect: {
eval("let a;");
console.log();
}
expect_stdout: SyntaxError("Block-scoped declarations (let, const, function, class) not yet supported outside strict mode")
node_version: "4"
}
eval_let_0: {
input: {
eval("let a;");
console.log();
}
expect: {
eval("let a;");
console.log();
}
expect_stdout: SyntaxError("Unexpected identifier")
node_version: "<=0.12"
}

View File

@@ -558,7 +558,75 @@ native_prototype: {
} }
} }
issue_2040: { accessor_boolean: {
input: {
var a = 1;
var b = {
get true() {
return a;
},
set false(c) {
a = c;
}
};
console.log(b.true, b.false = 2, b.true);
}
expect_exact: 'var a=1;var b={get true(){return a},set false(c){a=c}};console.log(b.true,b.false=2,b.true);'
expect_stdout: "1 2 2"
}
accessor_get_set: {
input: {
var a = 1;
var b = {
get set() {
return a;
},
set get(c) {
a = c;
}
};
console.log(b.set, b.get = 2, b.set);
}
expect_exact: 'var a=1;var b={get set(){return a},set get(c){a=c}};console.log(b.set,b.get=2,b.set);'
expect_stdout: "1 2 2"
}
accessor_null_undefined: {
input: {
var a = 1;
var b = {
get null() {
return a;
},
set undefined(c) {
a = c;
}
};
console.log(b.null, b.undefined = 2, b.null);
}
expect_exact: 'var a=1;var b={get null(){return a},set undefined(c){a=c}};console.log(b.null,b.undefined=2,b.null);'
expect_stdout: "1 2 2"
}
accessor_number: {
input: {
var a = 1;
var b = {
get 42() {
return a;
},
set 42(c) {
a = c;
}
};
console.log(b[42], b[42] = 2, b[42]);
}
expect_exact: 'var a=1;var b={get 42(){return a},set 42(c){a=c}};console.log(b[42],b[42]=2,b[42]);'
expect_stdout: "1 2 2"
}
accessor_string: {
input: { input: {
var a = 1; var a = 1;
var b = { var b = {
@@ -574,3 +642,20 @@ issue_2040: {
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_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" expect_stdout: "1 2 2"
} }
accessor_this: {
input: {
var a = 1;
var b = {
get this() {
return a;
},
set this(c) {
a = c;
}
};
console.log(b.this, b.this = 2, b.this);
}
expect_exact: 'var a=1;var b={get this(){return a},set this(c){a=c}};console.log(b.this,b.this=2,b.this);'
expect_stdout: "1 2 2"
}

View File

@@ -2,6 +2,7 @@ reduce_vars: {
options = { options = {
conditionals : true, conditionals : true,
evaluate : true, evaluate : true,
inline : true,
global_defs : { global_defs : {
C : 0 C : 0
}, },
@@ -1032,6 +1033,7 @@ defun_inline_2: {
defun_inline_3: { defun_inline_3: {
options = { options = {
evaluate: true, evaluate: true,
inline: true,
passes: 2, passes: 2,
reduce_vars: true, reduce_vars: true,
side_effects: true, side_effects: true,
@@ -1054,6 +1056,7 @@ defun_inline_3: {
defun_call: { defun_call: {
options = { options = {
inline: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
} }
@@ -1080,6 +1083,7 @@ defun_call: {
defun_redefine: { defun_redefine: {
options = { options = {
inline: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
} }
@@ -1112,6 +1116,7 @@ defun_redefine: {
func_inline: { func_inline: {
options = { options = {
inline: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
} }
@@ -1138,6 +1143,7 @@ func_inline: {
func_modified: { func_modified: {
options = { options = {
inline: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
} }
@@ -1311,19 +1317,47 @@ iife_func_side_effects: {
unused: true, unused: true,
} }
input: { input: {
function x() {
console.log("x");
}
function y() {
console.log("y");
}
function z() {
console.log("z");
}
(function(a, b, c) { (function(a, b, c) {
return b(); function y() {
console.log("FAIL");
}
return y + b();
})(x(), function() { })(x(), function() {
return y(); return y();
}, z()); }, z());
} }
expect: { expect: {
function x() {
console.log("x");
}
function y() {
console.log("y");
}
function z() {
console.log("z");
}
(function(a, b, c) { (function(a, b, c) {
return function() { return function() {
return y(); console.log("FAIL");
}(); } + b();
})(x(), 0, z()); })(x(), function() {
return y();
}, z());
} }
expect_stdout: [
"x",
"z",
"y",
]
} }
issue_1595_1: { issue_1595_1: {
@@ -1687,6 +1721,7 @@ redefine_arguments_1: {
redefine_arguments_2: { redefine_arguments_2: {
options = { options = {
evaluate: true, evaluate: true,
inline: true,
keep_fargs: false, keep_fargs: false,
reduce_vars: true, reduce_vars: true,
side_effects: true, side_effects: true,
@@ -1723,6 +1758,7 @@ redefine_arguments_2: {
redefine_arguments_3: { redefine_arguments_3: {
options = { options = {
evaluate: true, evaluate: true,
inline: true,
keep_fargs: false, keep_fargs: false,
passes: 3, passes: 3,
reduce_vars: true, reduce_vars: true,
@@ -1799,6 +1835,7 @@ redefine_farg_1: {
redefine_farg_2: { redefine_farg_2: {
options = { options = {
evaluate: true, evaluate: true,
inline: true,
keep_fargs: false, keep_fargs: false,
reduce_vars: true, reduce_vars: true,
side_effects: true, side_effects: true,
@@ -1835,6 +1872,7 @@ redefine_farg_2: {
redefine_farg_3: { redefine_farg_3: {
options = { options = {
evaluate: true, evaluate: true,
inline: true,
keep_fargs: false, keep_fargs: false,
passes: 3, passes: 3,
reduce_vars: true, reduce_vars: true,

14
test/compress/sandbox.js Normal file
View File

@@ -0,0 +1,14 @@
console_log: {
input: {
console.log("%% %s");
console.log("%% %s", "%s");
}
expect: {
console.log("%% %s");
console.log("%% %s", "%s");
}
expect_stdout: [
"%% %s",
"% %s",
]
}

View File

@@ -734,3 +734,23 @@ reassign_const: {
} }
expect_stdout: true expect_stdout: true
} }
issue_2062: {
options = {
booleans: true,
cascade: true,
conditionals: true,
side_effects: true,
}
input: {
var a = 1;
if ([ a || a++ + a--, a++ + a--, a && a.var ]);
console.log(a);
}
expect: {
var a = 1;
a || (a++, a--), a++, --a && a.var;
console.log(a);
}
expect_stdout: "1"
}

31
test/fetch.js Normal file
View File

@@ -0,0 +1,31 @@
var fs = require("fs");
var path = require("path");
try {
fs.mkdirSync("./tmp");
} catch (e) {
if (e.code != "EEXIST") throw e;
}
function local(url) {
return path.join("./tmp", encodeURIComponent(url));
}
function read(url) {
return fs.createReadStream(local(url));
}
module.exports = function(url, callback) {
var result = read(url);
result.on("error", function(e) {
if (e.code != "ENOENT") return callback(e);
require(url.slice(0, url.indexOf(":"))).get(url, function(res) {
if (res.statusCode !== 200) return callback(res);
res.pipe(fs.createWriteStream(local(url)).on("close", function() {
callback(null, read(url));
}));
});
}).on("open", function() {
callback(null, result);
});
};

View File

@@ -0,0 +1 @@
console.log(x);

View File

@@ -0,0 +1 @@
{"version": 3,"sources": ["index.js"],"mappings": ";"}

View File

@@ -3,7 +3,7 @@
"use strict"; "use strict";
var site = "http://browserbench.org/JetStream/"; var site = "http://browserbench.org/JetStream";
if (typeof phantom == "undefined") { if (typeof phantom == "undefined") {
// workaround for tty output truncation upon process.exit() // workaround for tty output truncation upon process.exit()
[process.stdout, process.stderr].forEach(function(stream){ [process.stdout, process.stderr].forEach(function(stream){
@@ -11,45 +11,69 @@ if (typeof phantom == "undefined") {
stream._handle.setBlocking(true); stream._handle.setBlocking(true);
}); });
var args = process.argv.slice(2); var args = process.argv.slice(2);
var debug = args.indexOf("--debug");
if (debug >= 0) {
args.splice(debug, 1);
debug = true;
} else {
debug = false;
}
if (!args.length) { if (!args.length) {
args.push("-mc"); args.push("-mcb", "beautify=false,webkit");
} }
args.push("--timings"); args.push("--timings");
var child_process = require("child_process"); var child_process = require("child_process");
try { var fetch = require("./fetch");
require("phantomjs-prebuilt");
} catch(e) {
child_process.execSync("npm install phantomjs-prebuilt@2.1.14");
}
var http = require("http"); var http = require("http");
var server = http.createServer(function(request, response) { var server = http.createServer(function(request, response) {
request.resume(); request.resume();
var url = decodeURIComponent(request.url.slice(1)); var url = site + request.url;
var stderr = ""; fetch(url, function(err, res) {
var uglifyjs = child_process.fork("bin/uglifyjs", args, { if (err) throw err;
silent: true response.writeHead(200, {
}).on("exit", function(code) { "Content-Type": {
console.log("uglifyjs", url.indexOf(site) == 0 ? url.slice(site.length) : url, args.join(" ")); css: "text/css",
console.log(stderr); js: "application/javascript",
if (code) throw new Error("uglifyjs failed with code " + code); png: "image/png"
}); }[url.slice(url.lastIndexOf(".") + 1)] || "text/html; charset=utf-8"
uglifyjs.stderr.on("data", function(data) { });
stderr += data; if (/\.js$/.test(url)) {
}).setEncoding("utf8"); var stderr = "";
uglifyjs.stdout.pipe(response); var uglifyjs = child_process.fork("bin/uglifyjs", args, {
http.get(url, function(res) { silent: true
res.pipe(uglifyjs.stdin); }).on("exit", function(code) {
}); console.log("uglifyjs", url.slice(site.length + 1), args.join(" "));
}).listen().on("listening", function() { console.log(stderr);
var phantomjs = require("phantomjs-prebuilt"); if (code) throw new Error("uglifyjs failed with code " + code);
var program = phantomjs.exec(process.argv[1], server.address().port); });
program.stdout.pipe(process.stdout); uglifyjs.stderr.on("data", function(data) {
program.stderr.pipe(process.stderr); stderr += data;
program.on("exit", function(code) { }).setEncoding("utf8");
server.close(); uglifyjs.stdout.pipe(response);
if (code) throw new Error("JetStream failed!"); res.pipe(uglifyjs.stdin);
console.log("JetStream completed successfully."); } else {
res.pipe(response);
}
}); });
}).listen();
server.on("listening", function() {
var port = server.address().port;
if (debug) {
console.log("http://localhost:" + port + "/");
} else {
child_process.exec("npm install phantomjs-prebuilt@2.1.14 --no-save", function(error) {
if (error) throw error;
var program = require("phantomjs-prebuilt").exec(process.argv[1], port);
program.stdout.pipe(process.stdout);
program.stderr.pipe(process.stderr);
program.on("exit", function(code) {
server.close();
if (code) throw new Error("JetStream failed!");
console.log("JetStream completed successfully.");
process.exit(0);
});
});
}
}); });
server.timeout = 0; server.timeout = 0;
} else { } else {
@@ -63,10 +87,6 @@ if (typeof phantom == "undefined") {
phantom.exit(1); phantom.exit(1);
}; };
var url = "http://localhost:" + require("system").args[1] + "/"; var url = "http://localhost:" + require("system").args[1] + "/";
page.onResourceRequested = function(requestData, networkRequest) {
if (/\.js$/.test(requestData.url))
networkRequest.changeUrl(url + encodeURIComponent(requestData.url));
}
page.onConsoleMessage = function(msg) { page.onConsoleMessage = function(msg) {
if (/Error:/i.test(msg)) { if (/Error:/i.test(msg)) {
console.error(msg); console.error(msg);
@@ -77,8 +97,8 @@ if (typeof phantom == "undefined") {
phantom.exit(); phantom.exit();
} }
}; };
page.open(site, function(status) { page.open(url, function(status) {
if (status != "success") phantomjs.exit(1); if (status != "success") phantom.exit(1);
page.evaluate(function() { page.evaluate(function() {
JetStream.switchToQuick(); JetStream.switchToQuick();
JetStream.start(); JetStream.start();

View File

@@ -77,6 +77,23 @@ describe("bin/uglifyjs", function () {
done(); done();
}); });
}); });
it("should not consider source map file content as source map file name (issue #2082)", function (done) {
var command = [
uglifyjscmd,
"test/input/issue-2082/sample.js",
"--source-map", "content=test/input/issue-2082/sample.js.map",
"--source-map", "url=inline",
].join(" ");
exec(command, function (err, stdout, stderr) {
if (err) throw err;
var stderrLines = stderr.split('\n');
assert.strictEqual(stderrLines[0], 'INFO: Using input source map: test/input/issue-2082/sample.js.map');
assert.notStrictEqual(stderrLines[1], 'INFO: Using input source map: {"version": 3,"sources": ["index.js"],"mappings": ";"}');
done();
});
});
it("Should work with --keep-fnames (mangle only)", function (done) { it("Should work with --keep-fnames (mangle only)", function (done) {
var command = uglifyjscmd + ' test/input/issue-1431/sample.js --keep-fnames -m'; var command = uglifyjscmd + ' test/input/issue-1431/sample.js --keep-fnames -m';
@@ -557,7 +574,27 @@ describe("bin/uglifyjs", function () {
exec(command, function (err, stdout, stderr) { exec(command, function (err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
assert.ok(/^Supported options:\n\{[^}]+}\nERROR: `ascii-only` is not a supported option/.test(stderr), stderr); assert.ok(/^Supported options:\n[\s\S]*?\nERROR: `ascii-only` is not a supported option/.test(stderr), stderr);
done();
});
});
it("Should work with --mangle reserved=[]", function (done) {
var command = uglifyjscmd + ' test/input/issue-505/input.js -m reserved=[callback]';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, 'function test(callback){"aaaaaaaaaaaaaaaa";callback(err,data);callback(err,data)}\n');
done();
});
});
it("Should work with --mangle reserved=false", function (done) {
var command = uglifyjscmd + ' test/input/issue-505/input.js -m reserved=false';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, 'function test(a){"aaaaaaaaaaaaaaaa";a(err,data);a(err,data)}\n');
done(); done();
}); });
}); });

View File

@@ -31,7 +31,7 @@ describe("bin/uglifyjs with input file globs", function() {
exec(command, function(err, stdout) { exec(command, function(err, stdout) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, 'var print=console.log.bind(console),a=function(n){return 3*n}(3),b=function(n){return n/2}(12);print("qux",a,b),function(n){print("Foo:",2*n)}(11);\n'); assert.strictEqual(stdout, 'var print=console.log.bind(console);print("qux",9,6),print("Foo:",2*11);\n');
done(); done();
}); });
}); });

View File

@@ -13,6 +13,13 @@ describe("minify", function() {
assert.strictEqual(result.code, 'function foo(n){return n?3:7}'); assert.strictEqual(result.code, 'function foo(n){return n?3:7}');
}); });
it("Should skip inherited keys from `files`", function() {
var files = Object.create({ skip: this });
files[0] = "alert(1 + 1)";
var result = Uglify.minify(files);
assert.strictEqual(result.code, "alert(2);");
});
describe("keep_quoted_props", function() { describe("keep_quoted_props", function() {
it("Should preserve quotes in object literals", function() { it("Should preserve quotes in object literals", function() {
var js = 'var foo = {"x": 1, y: 2, \'z\': 3};'; var js = 'var foo = {"x": 1, y: 2, \'z\': 3};';
@@ -106,7 +113,7 @@ describe("minify", function() {
content: "inline" content: "inline"
} }
}); });
assert.strictEqual(result.code, "var bar=function(){function foo(bar){return bar}return foo}();"); assert.strictEqual(result.code, "var bar=function(){return function(bar){return bar}}();");
assert.strictEqual(warnings.length, 1); assert.strictEqual(warnings.length, 1);
assert.strictEqual(warnings[0], "inline source map not found"); assert.strictEqual(warnings[0], "inline source map not found");
} finally { } finally {
@@ -207,5 +214,17 @@ describe("minify", function() {
assert.ok(err instanceof Error); assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "Error: Can't handle expression: debugger"); assert.strictEqual(err.stack.split(/\n/)[0], "Error: Can't handle expression: debugger");
}); });
it("should skip inherited properties", function() {
var foo = Object.create({ skip: this });
foo.bar = 42;
var result = Uglify.minify("alert(FOO);", {
compress: {
global_defs: {
FOO: foo
}
}
});
assert.strictEqual(result.code, "alert({bar:42});");
});
}); });
}); });

View File

@@ -1,16 +1,13 @@
var assert = require("assert"); var assert = require("assert");
var semver = require("semver");
var spawn = require("child_process").spawn; var spawn = require("child_process").spawn;
if (!process.env.UGLIFYJS_TEST_ALL) return; if (!process.env.UGLIFYJS_TEST_ALL) return;
function run(command, args, done) { function run(command, args, done) {
var id = setInterval(function() {
process.stdout.write("\0");
}, 5 * 60 * 1000);
spawn(command, args, { spawn(command, args, {
stdio: "ignore" stdio: [ "ignore", 1, 2 ]
}).on("exit", function(code) { }).on("exit", function(code) {
clearInterval(id);
assert.strictEqual(code, 0); assert.strictEqual(code, 0);
done(); done();
}); });
@@ -36,11 +33,9 @@ describe("test/benchmark.js", function() {
}); });
}); });
if (semver.satisfies(process.version, "0.12")) return;
describe("test/jetstream.js", function() { describe("test/jetstream.js", function() {
this.timeout(20 * 60 * 1000); this.timeout(20 * 60 * 1000);
it("Should install phantomjs-prebuilt", function(done) {
run("npm", ["install", "phantomjs-prebuilt@2.1.14"], done);
});
[ [
"-mc", "-mc",
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto", "-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto",
@@ -48,6 +43,7 @@ describe("test/jetstream.js", function() {
it("Should pass with options " + options, function(done) { it("Should pass with options " + options, function(done) {
var args = options.split(/ /); var args = options.split(/ /);
args.unshift("test/jetstream.js"); args.unshift("test/jetstream.js");
args.push("-b", "beautify=false,webkit");
run(process.argv[0], args, done); run(process.argv[0], args, done);
}); });
}); });

View File

@@ -294,8 +294,24 @@ function parse_test(file) {
if (label.name == "expect_exact" || label.name == "node_version") { if (label.name == "expect_exact" || label.name == "node_version") {
test[label.name] = read_string(stat); test[label.name] = read_string(stat);
} else if (label.name == "expect_stdout") { } else if (label.name == "expect_stdout") {
if (stat.TYPE == "SimpleStatement" && stat.body instanceof U.AST_Boolean) { if (stat.TYPE == "SimpleStatement") {
test[label.name] = stat.body.value; var body = stat.body;
if (body instanceof U.AST_Boolean) {
test[label.name] = body.value;
} else if (body instanceof U.AST_Call) {
var ctor = global[body.expression.name];
assert.ok(ctor === Error || ctor.prototype instanceof Error, tmpl("Unsupported expect_stdout format [{line},{col}]", {
line: label.start.line,
col: label.start.col
}));
test[label.name] = ctor.apply(null, body.args.map(function(node) {
assert.ok(node instanceof U.AST_Constant, tmpl("Unsupported expect_stdout format [{line},{col}]", {
line: label.start.line,
col: label.start.col
}));
return node.value;
}));
}
} else { } else {
test[label.name] = read_string(stat) + "\n"; test[label.name] = read_string(stat) + "\n";
} }

View File

@@ -19,23 +19,25 @@ function safe_log(arg, level) {
var FUNC_TOSTRING = [ var FUNC_TOSTRING = [
"Function.prototype.toString = Function.prototype.valueOf = function() {", "Function.prototype.toString = Function.prototype.valueOf = function() {",
" var id = 0;", " var id = 100000;",
" return function() {", " return function() {",
' if (this === Array) return "[Function: Array]";', ' if (this === Array) return "[Function: Array]";',
' if (this === Object) return "[Function: Object]";', ' if (this === Object) return "[Function: Object]";',
" var i = this.name;", " var i = this.name;",
' if (typeof i != "number") {', ' if (typeof i != "number") {',
" i = ++id;", " i = ++id;",
].concat(Object.getOwnPropertyDescriptor(Function.prototype, "name").configurable ? [
' Object.defineProperty(this, "name", {', ' Object.defineProperty(this, "name", {',
" get: function() {", " get: function() {",
" return i;", " return i;",
" }", " }",
" });", " });",
] : [], [
" }", " }",
' return "[Function: " + i + "]";', ' return "[Function: " + i + "]";',
" }", " }",
"}();", "}();",
].join("\n"); ]).join("\n");
exports.run_code = function(code) { exports.run_code = function(code) {
var stdout = ""; var stdout = "";
var original_write = process.stdout.write; var original_write = process.stdout.write;
@@ -50,7 +52,10 @@ exports.run_code = function(code) {
"}();", "}();",
].join("\n"), { ].join("\n"), {
console: { console: {
log: function() { log: function(msg) {
if (arguments.length == 1 && typeof msg == "string") {
return console.log("%s", msg);
}
return console.log.apply(console, [].map.call(arguments, function(arg) { return console.log.apply(console, [].map.call(arguments, function(arg) {
return safe_log(arg, 3); return safe_log(arg, 3);
})); }));

View File

@@ -102,23 +102,23 @@ for (var i = 2; i < process.argv.length; ++i) {
case '--help': case '--help':
case '-h': case '-h':
case '-?': case '-?':
console.log('** UglifyJS fuzzer help **'); println('** UglifyJS fuzzer help **');
console.log('Valid options (optional):'); println('Valid options (optional):');
console.log('<number>: generate this many cases (if used must be first arg)'); println('<number>: generate this many cases (if used must be first arg)');
console.log('-v: print every generated test case'); println('-v: print every generated test case');
console.log('-V: print every 100th generated test case'); println('-V: print every 100th generated test case');
console.log('-t <int>: generate this many toplevels per run (more take longer)'); println('-t <int>: generate this many toplevels per run (more take longer)');
console.log('-r <int>: maximum recursion depth for generator (higher takes longer)'); println('-r <int>: maximum recursion depth for generator (higher takes longer)');
console.log('-s1 <statement name>: force the first level statement to be this one (see list below)'); println('-s1 <statement name>: force the first level statement to be this one (see list below)');
console.log('-s2 <statement name>: force the second level statement to be this one (see list below)'); println('-s2 <statement name>: force the second level statement to be this one (see list below)');
console.log('--no-catch-redef: do not redefine catch variables'); println('--no-catch-redef: do not redefine catch variables');
console.log('--no-directive: do not generate directives'); println('--no-directive: do not generate directives');
console.log('--use-strict: generate "use strict"'); println('--use-strict: generate "use strict"');
console.log('--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise'); println('--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise');
console.log('--only-stmt <statement names>: a comma delimited white list of statements that may be generated'); println('--only-stmt <statement names>: a comma delimited white list of statements that may be generated');
console.log('--without-stmt <statement names>: a comma delimited black list of statements never to generate'); println('--without-stmt <statement names>: a comma delimited black list of statements never to generate');
console.log('List of accepted statement names: ' + Object.keys(STMT_ARG_TO_ID)); println('List of accepted statement names: ' + Object.keys(STMT_ARG_TO_ID));
console.log('** UglifyJS fuzzer exiting **'); println('** UglifyJS fuzzer exiting **');
return 0; return 0;
default: default:
// first arg may be a number. // first arg may be a number.
@@ -941,7 +941,17 @@ if (require.main !== module) {
return; return;
} }
function try_beautify(code, result) { function println(msg) {
if (typeof msg != "undefined") process.stdout.write(msg);
process.stdout.write("\n");
}
function errorln(msg) {
if (typeof msg != "undefined") process.stderr.write(msg);
process.stderr.write("\n");
}
function try_beautify(code, result, printfn) {
var beautified = UglifyJS.minify(code, { var beautified = UglifyJS.minify(code, {
compress: false, compress: false,
mangle: false, mangle: false,
@@ -951,15 +961,15 @@ function try_beautify(code, result) {
}, },
}); });
if (beautified.error) { if (beautified.error) {
console.log("// !!! beautify failed !!!"); printfn("// !!! beautify failed !!!");
console.log(beautified.error.stack); printfn(beautified.error.stack);
} else if (sandbox.same_stdout(sandbox.run_code(beautified.code), result)) { } else if (sandbox.same_stdout(sandbox.run_code(beautified.code), result)) {
console.log("// (beautified)"); printfn("// (beautified)");
console.log(beautified.code); printfn(beautified.code);
return; return;
} }
console.log("//"); printfn("//");
console.log(code); printfn(code);
} }
var default_options = UglifyJS.default_options(); var default_options = UglifyJS.default_options();
@@ -977,8 +987,8 @@ function log_suspects(minify_options, component) {
m[component] = o; m[component] = o;
var result = UglifyJS.minify(original_code, m); var result = UglifyJS.minify(original_code, m);
if (result.error) { if (result.error) {
console.log("Error testing options." + component + "." + name); errorln("Error testing options." + component + "." + name);
console.log(result.error); errorln(result.error.stack);
} else { } else {
var r = sandbox.run_code(result.code); var r = sandbox.run_code(result.code);
return sandbox.same_stdout(original_result, r); return sandbox.same_stdout(original_result, r);
@@ -986,49 +996,49 @@ function log_suspects(minify_options, component) {
} }
}); });
if (suspects.length > 0) { if (suspects.length > 0) {
console.log("Suspicious", component, "options:"); errorln("Suspicious " + component + " options:");
suspects.forEach(function(name) { suspects.forEach(function(name) {
console.log(" " + name); errorln(" " + name);
}); });
console.log(); errorln();
} }
} }
function log(options) { function log(options) {
if (!ok) console.log('\n\n\n\n\n\n!!!!!!!!!!\n\n\n'); if (!ok) errorln('\n\n\n\n\n\n!!!!!!!!!!\n\n\n');
console.log("//============================================================="); errorln("//=============================================================");
if (!ok) console.log("// !!!!!! Failed... round", round); if (!ok) errorln("// !!!!!! Failed... round " + round);
console.log("// original code"); errorln("// original code");
try_beautify(original_code, original_result); try_beautify(original_code, original_result, errorln);
console.log(); errorln();
console.log(); errorln();
console.log("//-------------------------------------------------------------"); errorln("//-------------------------------------------------------------");
if (typeof uglify_code == "string") { if (typeof uglify_code == "string") {
console.log("// uglified code"); errorln("// uglified code");
try_beautify(uglify_code, uglify_result); try_beautify(uglify_code, uglify_result, errorln);
console.log(); errorln();
console.log(); errorln();
console.log("original result:"); errorln("original result:");
console.log(original_result); errorln(typeof original_result == "string" ? original_result : original_result.stack);
console.log("uglified result:"); errorln("uglified result:");
console.log(uglify_result); errorln(typeof uglify_result == "string" ? uglify_result : uglify_result.stack);
} else { } else {
console.log("// !!! uglify failed !!!"); errorln("// !!! uglify failed !!!");
console.log(uglify_code.stack); errorln(uglify_code.stack);
if (typeof original_result != "string") { if (typeof original_result != "string") {
console.log(); errorln();
console.log(); errorln();
console.log("original stacktrace:"); errorln("original stacktrace:");
console.log(original_result.stack); errorln(original_result.stack);
} }
} }
console.log("minify(options):"); errorln("minify(options):");
options = JSON.parse(options); options = JSON.parse(options);
console.log(options); errorln(JSON.stringify(options, null, 2));
console.log(); errorln();
if (!ok && typeof uglify_code == "string") { if (!ok && typeof uglify_code == "string") {
Object.keys(default_options).forEach(log_suspects.bind(null, options)); Object.keys(default_options).forEach(log_suspects.bind(null, options));
console.log("!!!!!! Failed... round", round); errorln("!!!!!! Failed... round " + round);
} }
} }
@@ -1058,19 +1068,19 @@ for (var round = 1; round <= num_iterations; round++) {
} }
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options); if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
else if (typeof original_result != "string") { else if (typeof original_result != "string") {
console.log("//============================================================="); println("//=============================================================");
console.log("// original code"); println("// original code");
try_beautify(original_code, original_result); try_beautify(original_code, original_result, println);
console.log(); println();
console.log(); println();
console.log("original result:"); println("original result:");
console.log(original_result); println(original_result.stack);
console.log(); println();
} }
if (!ok && isFinite(num_iterations)) { if (!ok && isFinite(num_iterations)) {
console.log(); println();
process.exit(1); process.exit(1);
} }
}); });
} }
console.log(); println();