Compare commits

..

56 Commits

Author SHA1 Message Date
Alex Lam S.L
370f2cc906 Merge pull request #2104 from alexlamsl/harmony-v3.0.17
Merging from master for 3.0.17
2017-06-15 23:07:22 +08:00
alexlamsl
78cf35f89c Merge branch 'master' into harmony-v3.0.17 2017-06-15 19:01:36 +08:00
Alex Lam S.L
57dc4fb32f v3.0.17 2017-06-15 18:59:37 +08:00
Alex Lam S.L
b85a358deb suppress inline of this (#2103)
fixes #2101
2017-06-15 12:14:16 +08:00
kzc
100e18305d first cut of async/await (#2098)
- async arrow functions not yet supported


fixes #1789
2017-06-15 06:15:48 +08:00
Alex Lam S.L
43697958f3 avoid intermittent test time-out failures (#2100) 2017-06-15 04:47:57 +08:00
Alex Lam S.L
3f961bbba0 compute uses_arguments correctly in figure_out_scope() (#2099)
fixes #2097
2017-06-15 03:28:26 +08:00
Alex Lam S.L
7cc03d4d40 fix parsing of expect_stdout (#2096)
fixes #2095
2017-06-15 01:26:49 +08:00
Alex Lam S.L
0a1e523cd5 fix parsing of expect_stdout (#2096)
fixes #2095
2017-06-15 01:00:03 +08:00
Alex Lam S.L
c28056d7ed Merge pull request #2094 from alexlamsl/harmony-v3.0.16
Merging from master for 3.0.16
2017-06-14 19:25:21 +08:00
alexlamsl
8af362ed57 Merge branch 'master' into harmony-v3.0.16 2017-06-14 17:09:30 +08:00
Alex Lam S.L
4231f7323e v3.0.16 2017-06-14 16:45:09 +08:00
Alex Lam S.L
68138f2281 fix reduce_vars on AST_Arrow (#2091)
fixes #2090
2017-06-14 16:40:37 +08:00
kzc
da2de350c3 add comment about quote_style and gzip (#2092) 2017-06-14 12:23:03 +08:00
Alex Lam S.L
41beae4dd7 cache web assets between CI runs (#2089)
- skip `test/jetstream.js` for `node@0.12`
2017-06-14 11:53:10 +08:00
Ziad El Khoury Hanna
82db9188ac fix CLI parsing of --source-map content (#2088)
fixes #2082
2017-06-13 16:30:46 +08:00
Alex Lam S.L
3dc9e140e4 add Node.js 8 to Travis CI (#2086)
- explicitly terminate `test/jetstream.js` upon completion
- log verbose output from `test/benchmark.js` & `test/jetstream.js`
- remove obsolete workaround for Travis CI
2017-06-13 06:21:16 +08:00
Alex Lam S.L
fed0096556 allow expect_stdout to specify Error (#2087) 2017-06-13 04:57:26 +08:00
Alex Lam S.L
2bdc8802dd fix variable accounting in inline (#2085)
fixes #2084
2017-06-13 01:40:14 +08:00
Alex Lam S.L
5ef7cb372a suppress false positives for-in loops (#2080)
fixes #2079
2017-06-10 13:55:17 +08:00
Alex Lam S.L
4ad7b1dae4 fix portability of sandbox.run_code() on Node.js 0.1x (#2078) 2017-06-10 01:08:58 +08:00
Alex Lam S.L
9186859cb7 fix non-string parameters (#2076)
`Stream.write()` is not as versatile as `console.log()`
2017-06-10 00:11:40 +08:00
Alex Lam S.L
47c0713747 report test/ufuzz.js failures in process.stderr (#2074) 2017-06-09 15:56:28 +08:00
Alex Lam S.L
293c566d6c marshal mangle[.properties].reserved from non-Array values (#2072) 2017-06-09 04:29:12 +08:00
Alex Lam S.L
9c306406f1 fix iteration over object with inherited properties (#2068)
fixes #2055
2017-06-08 03:27:03 +08:00
Alex Lam S.L
9db0695b10 fix cascade on multi-branch evaluations (#2067)
Partially reverts #2059 as this has better coverage and performance.

fixes #2062
2017-06-07 19:52:01 +08:00
Alex Lam S.L
a7971f4e34 fix unused crash with top-level AST_Var (#2066)
fixes #2063
2017-06-07 19:12:35 +08:00
Alex Lam S.L
f2af093402 fix CLI output corruption (#2061)
Using `console.error()` & `console.log()` result in inconsistent formatting across Node.js versions.

Avoid this issue by directly writing to `process.stderr` & `process.stdout` instead.

Miscellaneous
- prettify invalid option listing
2017-06-07 04:25:32 +08:00
Alex Lam S.L
b9ad53d1ab fix inline handling of AST_Call.args (#2059) 2017-06-06 22:55:25 +08:00
Alex Lam S.L
b0eab71470 implement test/jetstream.js --debug (#2058) 2017-06-06 19:28:12 +08:00
Alex Lam S.L
3493a182b2 implement function inlining (#2053)
- empty body
- single `AST_Return`
- single `AST_SimpleStatement`
- avoid `/*#__PURE__*/`

Miscellaneous
- enhance single-use function substitution

fixes #281
2017-06-06 05:49:53 +08:00
Alex Lam S.L
27c5284d3d workaround webkit parsing error (#2056)
apply `webkit` to jetstream tests
2017-06-06 04:06:42 +08:00
Alex Lam S.L
540220b91b fix AST_Function scope invariance (#2052)
improve function name hack in `run_code()`
2017-06-04 19:27:43 +08:00
kzc
82fefc5d29 fix class expression statements (#2051)
- class expression statements require parentheses
- allow unused class expression statements to be dropped

fixes #1782
closes #1784
2017-06-04 02:45:26 +08:00
kzc
753932b302 drop unused arrow functions (#2050) 2017-06-04 00:20:46 +08:00
Alex Lam S.L
84634da4b5 add tests for AST_SymbolAccessor (#2049) 2017-06-03 16:08:10 +08:00
Alex Lam S.L
1743621889 clean up lib/parse.js (#2047)
- remove unused definitions
- replace `array_to_hash()`
2017-06-03 14:00:59 +08:00
Alex Lam S.L
1edbd6556f fix beautify whitespace output within AST_Destructuring (#2046)
fixes #2044
2017-06-02 18:50:39 +08:00
kzc
f330ab743a better document behavior of unsafe_Func (#2043) 2017-06-02 12:07:17 +08:00
Alex Lam S.L
888a321417 Merge pull request #2042 from alexlamsl/harmony-v3.0.15
Merging from master for 3.0.15
2017-06-01 19:28:30 +08:00
alexlamsl
ee5c03f7f1 Merge branch 'master' into harmony-v3.0.15 2017-06-01 18:26:09 +08:00
Alex Lam S.L
4377e932ca v3.0.15 2017-06-01 18:12:38 +08:00
Alex Lam S.L
bac14ba881 fix non-identifier getter/setter name (#2041)
fixes #2040
2017-06-01 18:11:16 +08:00
Alex Lam S.L
ec095ed647 whitelist unsafe evaluate candidates (#2039)
- all arguments may accept constant values
- return constant value
- free of side effects
- available & identical across locales and runtime environments
2017-06-01 04:33:05 +08:00
Alex Lam S.L
17e73121fa enhance unsafe evaluate (#2037) 2017-06-01 00:56:28 +08:00
kzc
0cb75089f0 document safari10 mangle option (#2035) 2017-05-31 23:16:20 +08:00
kzc
f71e8fd948 reformat mangle options section of README (#2036) 2017-05-31 21:52:43 +08:00
Alex Lam S.L
a1647ee0c5 Merge pull request #2034 from alexlamsl/harmony-v3.0.14
Merging from master for 3.0.14
2017-05-31 12:44:58 +08:00
alexlamsl
c814060b4a Merge branch 'master' into harmony-v3.0.14 2017-05-31 11:42:54 +08:00
Alex Lam S.L
3e62faa64f v3.0.14 2017-05-31 11:34:51 +08:00
Alex Lam S.L
e9645e017f introduce unsafe_Func (#2033)
Separate flag for #203 functionality.
2017-05-31 03:38:00 +08:00
Alex Lam S.L
55b5f2a8aa widen CLI parse error code fragment displayed (#2032)
fixes #2030
2017-05-31 01:56:52 +08:00
Alex Lam S.L
303293e4aa fix side_effects on AST_Class (#2031)
fixes #2028
2017-05-31 01:44:29 +08:00
Alex Lam S.L
23265ac253 mangle destructuring function parameters (#2029)
fixes #2025
2017-05-30 23:41:55 +08:00
Alex Lam S.L
0cc6dedccc fix block elimination (#2023)
fixes #1664
fixes #1672
2017-05-30 14:59:54 +08:00
kzc
ec63588496 fix compress of IIFE with destructuring args (#2022) 2017-05-30 13:17:06 +08:00
44 changed files with 2257 additions and 353 deletions

View File

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

View File

@@ -569,6 +569,9 @@ 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)`
when both `args` and `code` are string literals.
- `unsafe_math` (default: false) -- optimize numerical expressions like
`2 * x * 3` into `6 * x`, which may give imprecise floating point results.
@@ -610,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
- `inline` -- embed simple functions
- `join_vars` -- join consecutive `var` statements
- `cascade` -- small optimization for sequences, transform `x, x` into `x`
@@ -675,17 +680,22 @@ 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.
- `safari10` (default `false`). Pass `true` to work around the Safari 10 loop
iterator [bug](https://bugs.webkit.org/show_bug.cgi?id=171041)
"Cannot declare a let variable twice".
Examples:
@@ -760,7 +770,7 @@ can pass additional arguments that control the code output:
- `quote_style` (default `0`) -- preferred quote style for strings (affects
quoted property names and directives as well):
- `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
- `2` -- always use double 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();
for (var option in options) {
text.push("--" + (option == "output" ? "beautify" : option == "sourceMap" ? "source-map" : option) + " options:");
var defs = 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(format_object(options[option]));
text.push("");
}
return text.join("\n");
@@ -157,7 +150,7 @@ if (program.verbose) {
}
if (program.self) {
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";
simple_glob(UglifyJS.FILES).forEach(function(name) {
@@ -187,7 +180,7 @@ function convert_ast(fn) {
function run() {
UglifyJS.AST_Node.warn_function = function(msg) {
console.error("WARN:", msg);
print_error("WARN: " + msg);
};
if (program.timings) options.timings = true;
try {
@@ -216,7 +209,7 @@ function run() {
if (result.error) {
var ex = result.error;
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 lines = files[ex.filename].split(/\r?\n/);
var line = lines[ex.line - 1];
@@ -225,21 +218,22 @@ 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, " ") + "^");
print_error(line.slice(0, 80));
print_error(line.slice(0, col).replace(/\S/g, " ") + "^");
}
}
if (ex.defs) {
console.error("Supported options:");
console.error(ex.defs);
print_error("Supported options:");
print_error(format_object(ex.defs));
}
fatal(ex);
} 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 (value instanceof UglifyJS.AST_Token) return;
if (value instanceof UglifyJS.Dictionary) return;
@@ -255,7 +249,7 @@ function run() {
return value;
}, 2));
} else if (program.output == "spidermonkey") {
console.log(JSON.stringify(UglifyJS.minify(result.code, {
print(JSON.stringify(UglifyJS.minify(result.code, {
compress: false,
mangle: false,
output: {
@@ -269,7 +263,7 @@ function run() {
fs.writeFileSync(program.output + ".map", result.map);
}
} else {
console.log(result.code);
print(result.code);
}
if (program.nameCache) {
fs.writeFileSync(program.nameCache, JSON.stringify(cache, function(key, value) {
@@ -277,13 +271,13 @@ function run() {
}));
}
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) {
if (message instanceof Error) message = message.stack.replace(/^\S*?Error:/, "ERROR:")
console.error(message);
print_error(message);
process.exit(1);
}
@@ -377,10 +371,10 @@ function parse_js(flag, constants) {
function parse_source_map() {
var parse = parse_js("sourceMap", true);
return function(value, options) {
var hasContent = options && options.sourceMap && "content" in options.sourceMap;
var hasContent = options && "content" in options;
var settings = parse(value, options);
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);
}
return settings;
@@ -402,3 +396,25 @@ function to_cache(key) {
function skip_key(key) {
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

@@ -355,13 +355,14 @@ var AST_Expansion = DEFNODE("Expansion", "expression", {
}
});
var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments is_generator", {
var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments is_generator async", {
$documentation: "Base class for functions",
$propdoc: {
is_generator: "[boolean] is generatorFn or not",
name: "[AST_SymbolDeclaration?] the name of this function",
argnames: "[AST_SymbolFunarg|AST_Destructuring|AST_Expansion|AST_DefaultAssign*] array of function arguments, destructurings, or expanding arguments",
uses_arguments: "[boolean/S] tells whether this function accesses the arguments array"
uses_arguments: "[boolean/S] tells whether this function accesses the arguments array",
is_generator: "[boolean] is this a generator method",
async: "[boolean] is this method async",
},
args_as_names: function () {
var out = [];
@@ -893,11 +894,12 @@ var AST_ObjectGetter = DEFNODE("ObjectGetter", "quote static", {
$documentation: "An object getter property",
}, AST_ObjectProperty);
var AST_ConciseMethod = DEFNODE("ConciseMethod", "quote static is_generator", {
var AST_ConciseMethod = DEFNODE("ConciseMethod", "quote static is_generator async", {
$propdoc: {
quote: "[string|undefined] the original quote character, if any",
static: "[boolean] whether this method is static (classes only)",
is_generator: "[boolean] is generatorFn or not",
static: "[boolean] is this method static (classes only)",
is_generator: "[boolean] is this a generator method",
async: "[boolean] is this method async",
},
$documentation: "An ES6 concise method inside an object or class"
}, AST_ObjectProperty);
@@ -1101,7 +1103,17 @@ var AST_True = DEFNODE("True", null, {
value: true
}, AST_Boolean);
/* -----[ Yield ]----- */
var AST_Await = DEFNODE("Await", "expression", {
$documentation: "An `await` statement",
$propdoc: {
expression: "[AST_Node] the mandatory expression being awaited",
},
_walk: function(visitor) {
return visitor._visit(this, function(){
this.expression._walk(visitor);
});
}
});
var AST_Yield = DEFNODE("Yield", "expression is_star", {
$documentation: "A `yield` statement",

View File

@@ -64,6 +64,7 @@ function Compressor(options, false_by_default) {
hoist_vars : false,
ie8 : false,
if_return : !false_by_default,
inline : !false_by_default,
join_vars : !false_by_default,
keep_fargs : true,
keep_fnames : false,
@@ -82,6 +83,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,
@@ -251,7 +253,7 @@ merge(Compressor.prototype, {
})
});
}
if (node instanceof AST_Lambda && node !== self) {
if (node instanceof AST_Class || node instanceof AST_Lambda && node !== self) {
return node;
}
if (node instanceof AST_Block) {
@@ -362,7 +364,7 @@ merge(Compressor.prototype, {
safe_ids = save_ids;
return true;
}
if (node instanceof AST_Function) {
if (node instanceof AST_Function || node instanceof AST_Arrow) {
push();
var iife;
if (!node.name
@@ -372,14 +374,16 @@ merge(Compressor.prototype, {
// (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})()
// So existing transformation rules can work on them.
node.argnames.forEach(function(arg, i) {
var d = arg.definition();
if (!node.uses_arguments && d.fixed === undefined) {
d.fixed = function() {
return iife.args[i] || make_node(AST_Undefined, iife);
};
mark(d, true);
} else {
d.fixed = false;
if (arg.definition) {
var d = arg.definition();
if (!node.uses_arguments && d.fixed === undefined) {
d.fixed = function() {
return iife.args[i] || make_node(AST_Undefined, iife);
};
mark(d, true);
} else {
d.fixed = false;
}
}
});
}
@@ -666,6 +670,7 @@ merge(Compressor.prototype, {
function can_be_evicted_from_block(node) {
return !(
node instanceof AST_DefClass ||
node instanceof AST_Defun ||
node instanceof AST_Let ||
node instanceof AST_Const
);
@@ -746,6 +751,7 @@ merge(Compressor.prototype, {
// Stop immediately if these node types are encountered
var parent = tt.parent();
if (node instanceof AST_Assign && node.operator != "=" && lhs.equivalent_to(node.left)
|| node instanceof AST_Await
|| node instanceof AST_Debugger
|| node instanceof AST_Destructuring
|| node instanceof AST_IterationStatement && !(node instanceof AST_For)
@@ -1455,7 +1461,7 @@ merge(Compressor.prototype, {
});
if (value && typeof value == "object") {
var props = [];
for (var key in value) {
for (var key in value) if (HOP(value, key)) {
props.push(make_node(AST_ObjectKeyVal, orig, {
key: key,
value: to_node(value[key], orig)
@@ -1714,6 +1720,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);
});
@@ -1862,6 +1925,7 @@ merge(Compressor.prototype, {
});
def(AST_Defun, return_true);
def(AST_Function, return_false);
def(AST_Arrow, return_false);
def(AST_Class, return_false);
def(AST_DefClass, return_true);
def(AST_Binary, function(compressor){
@@ -1995,7 +2059,8 @@ merge(Compressor.prototype, {
self.body = tighten_body(self.body, compressor);
switch (self.body.length) {
case 1:
if (can_be_evicted_from_block(self.body[0])) {
if (!compressor.has_directive("use strict") && compressor.parent() instanceof AST_If
|| can_be_evicted_from_block(self.body[0])) {
return self.body[0];
}
break;
@@ -2215,7 +2280,9 @@ merge(Compressor.prototype, {
}
return node;
}
if (node instanceof AST_Definitions && !(parent instanceof AST_ForIn && parent.init === node)) {
if (node instanceof AST_Definitions
&& !(parent instanceof AST_ForIn && parent.init === node)
&& (drop_vars || !(parent instanceof AST_Toplevel) && !(node instanceof AST_Var))) {
// place uninitialized names at the start
var body = [], head = [], tail = [];
// for unused names whose initialization has
@@ -2546,6 +2613,8 @@ merge(Compressor.prototype, {
});
def(AST_Accessor, return_null);
def(AST_Function, return_null);
def(AST_Arrow, return_null);
def(AST_ClassExpression, return_null);
def(AST_Binary, function(compressor, first_in_statement){
var right = this.right.drop_side_effect_free(compressor);
if (!right) return this.left.drop_side_effect_free(compressor, first_in_statement);
@@ -3045,24 +3114,9 @@ merge(Compressor.prototype, {
OPT(AST_Call, function(self, compressor){
var exp = self.expression;
if (compressor.option("reduce_vars")
&& exp instanceof AST_SymbolRef) {
var def = exp.definition();
if (compressor.option("reduce_vars") && exp instanceof AST_SymbolRef) {
var fixed = exp.fixed_value();
if (fixed instanceof AST_Defun) {
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 (fixed instanceof AST_Function) exp = fixed;
}
if (compressor.option("unused")
&& exp instanceof AST_Function
@@ -3133,63 +3187,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({ecma: compressor.option("ecma")})
});
});
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;
case "Symbol":
// Symbol's argument is only used for debugging.
self.args = [];
@@ -3276,14 +3273,133 @@ merge(Compressor.prototype, {
}
}
}
if (exp instanceof AST_Function && !self.expression.is_generator) {
if (exp.body[0] instanceof AST_Return) {
var value = exp.body[0].value;
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 && !self.expression.is_generator && !self.expression.async) {
var stat = exp.body[0];
if (compressor.option("inline") && stat instanceof AST_Return) {
var value = stat && stat.value;
if (!value || value.is_constant_expression()) {
var args = self.args.concat(value || make_node(AST_Undefined, self));
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 && matches(node.scope.find_variable(node))
|| node instanceof AST_This && matches(node)) {
value = self;
return true;
}
function matches(ref) {
return ref && ref.scope.parent_scope === fn.parent_scope;
}
}));
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)) {
var args = self.args.concat(make_node(AST_Undefined, self));
return make_sequence(self, args).transform(compressor);
@@ -3307,6 +3423,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;
});
@@ -3381,6 +3502,7 @@ merge(Compressor.prototype, {
continue;
}
var parent = null, field;
expressions[j] = cdr = cdr.clone();
while (true) {
if (cdr.equivalent_to(left)) {
var car = expressions[i];
@@ -3417,7 +3539,7 @@ merge(Compressor.prototype, {
break;
}
parent = cdr;
cdr = cdr[field];
cdr = cdr[field] = cdr[field].clone();
}
}
end = i;
@@ -3937,12 +4059,22 @@ merge(Compressor.prototype, {
return make_node(AST_Infinity, self).optimize(compressor);
}
}
if (compressor.option("evaluate")
&& compressor.option("reduce_vars")
if (compressor.option("reduce_vars")
&& is_lhs(self, compressor.parent()) !== self) {
var d = self.definition();
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) {
var init = fixed.evaluate(compressor);
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.toplevel = null;
for (var name in files) {
for (var name in files) if (HOP(files, name)) {
options.parse.filename = name;
options.parse.toplevel = parse(files[name], options.parse);
if (options.sourceMap && options.sourceMap.content == "inline") {
@@ -136,7 +136,7 @@ function minify(files, options) {
if (options.sourceMap.includeSources) {
if (files instanceof AST_Toplevel) {
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]);
}
}

View File

@@ -73,6 +73,7 @@ function OutputStream(options) {
shebang : true,
shorthand : undefined,
source_map : null,
webkit : false,
width : 80,
wrap_iife : false,
}, true);
@@ -629,6 +630,13 @@ function OutputStream(options) {
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')) {
var p = output.parent();
return p instanceof AST_Call && p.expression === this;
@@ -642,6 +650,10 @@ function OutputStream(options) {
return p instanceof AST_PropAccess && p.expression === this;
});
PARENS(AST_ClassExpression, function(output){
return output.parent() instanceof AST_SimpleStatement;
});
// same goes for an object literal, because otherwise it would be
// interpreted as a block of code.
PARENS(AST_Object, function(output){
@@ -805,17 +817,15 @@ function OutputStream(options) {
DEFPRINT(AST_Destructuring, function (self, output) {
output.print(self.is_array ? "[" : "{");
var first = true;
var len = self.names.length;
self.names.forEach(function (name, i) {
if (first) first = false; else { output.comma(); output.space(); }
if (i > 0) output.comma();
name.print(output);
// If the final element is a hole, we need to make sure it
// doesn't look like a trailing comma, by inserting an actual
// trailing comma.
if (i === len - 1 && name instanceof AST_Hole)
output.comma();
})
if (i == len - 1 && name instanceof AST_Hole) output.comma();
});
output.print(self.is_array ? "]" : "}");
});
@@ -968,6 +978,10 @@ function OutputStream(options) {
AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword){
var self = this;
if (!nokeyword) {
if (this.async) {
output.print("async");
output.space();
}
output.print("function");
if (this.is_generator) {
output.star();
@@ -1073,6 +1087,22 @@ function OutputStream(options) {
}
});
DEFPRINT(AST_Await, function(self, output){
output.print("await");
output.space();
var e = self.expression;
var parens = !(
e instanceof AST_Call
|| e instanceof AST_SymbolRef
|| e instanceof AST_PropAccess
|| e instanceof AST_Unary
|| e instanceof AST_Constant
);
if (parens) output.print("(");
self.expression.print(output);
if (parens) output.print(")");
});
/* -----[ loop control ]----- */
AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){
output.print(kind);
@@ -1536,7 +1566,8 @@ function OutputStream(options) {
DEFPRINT(AST_NewTarget, function(self, output) {
output.print("new.target");
});
AST_ObjectProperty.DEFMETHOD("print_property_name", function(key, quote, output) {
function print_property_name(key, quote, output) {
if (output.option("quote_keys")) {
output.print_string(key + "");
} else if ((typeof key == "number"
@@ -1553,7 +1584,8 @@ function OutputStream(options) {
} else {
output.print_string(key, quote);
}
});
}
DEFPRINT(AST_ObjectKeyVal, function(self, output){
function get_name(self) {
var def = self.definition();
@@ -1566,7 +1598,7 @@ function OutputStream(options) {
is_identifier_string(self.key) &&
get_name(self.value) === self.key
) {
self.print_property_name(self.key, self.quote, output);
print_property_name(self.key, self.quote, output);
} else if (allowShortHand &&
self.value instanceof AST_DefaultAssign &&
@@ -1574,12 +1606,14 @@ function OutputStream(options) {
is_identifier_string(self.key) &&
get_name(self.value.left) === self.key
) {
self.print_property_name(self.key, self.quote, output);
print_property_name(self.key, self.quote, output);
output.space();
output.print("=");
output.space();
self.value.right.print(output);
} else {
if (!(self.key instanceof AST_Node)) {
self.print_property_name(self.key, self.quote, output);
print_property_name(self.key, self.quote, output);
} else {
output.with_square(function() {
self.key.print(output);
@@ -1589,15 +1623,18 @@ function OutputStream(options) {
self.value.print(output);
}
});
AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, self, output) {
AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, output) {
var self = this;
if (self.static) {
output.print("static");
output.space();
}
output.print(type);
output.space();
if (type) {
output.print(type);
output.space();
}
if (self.key instanceof AST_SymbolMethod) {
self.print_property_name(self.key.name, self.quote, output);
print_property_name(self.key.name, self.quote, output);
} else {
output.with_square(function() {
self.key.print(output);
@@ -1606,27 +1643,13 @@ function OutputStream(options) {
self.value._do_print(output, true);
});
DEFPRINT(AST_ObjectSetter, function(self, output){
self._print_getter_setter("set", self, output);
self._print_getter_setter("set", output);
});
DEFPRINT(AST_ObjectGetter, function(self, output){
self._print_getter_setter("get", self, output);
self._print_getter_setter("get", output);
});
DEFPRINT(AST_ConciseMethod, function(self, output){
if (self.static) {
output.print("static");
output.space();
}
if (self.is_generator) {
output.print("*");
}
if (self.key instanceof AST_SymbolMethod) {
self.print_property_name(self.key.name, self.quote, output);
} else {
output.with_square(function() {
self.key.print(output);
});
}
self.value._do_print(output, true);
self._print_getter_setter(self.is_generator && "*" || self.async && "async", output);
});
AST_Symbol.DEFMETHOD("_do_print", function(output){
var def = this.definition();

View File

@@ -47,7 +47,7 @@
var KEYWORDS = 'break case catch class const continue debugger default delete do else export extends finally for function if in instanceof new return switch throw try typeof var let void while with import';
var KEYWORDS_ATOM = 'false null true';
var RESERVED_WORDS = 'enum implements interface package private protected public static super this ' + KEYWORDS_ATOM + " " + KEYWORDS;
var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case yield';
var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case yield await';
KEYWORDS = makePredicate(KEYWORDS);
RESERVED_WORDS = makePredicate(RESERVED_WORDS);
@@ -122,8 +122,6 @@ var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,;:"));
var PUNC_CHARS = makePredicate(characters("[]{}(),;:"));
var REGEXP_MODIFIERS = makePredicate(characters("gmsiy"));
/* -----[ Tokenizer ]----- */
// 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 = array_to_hash([ "atom", "num", "string", "regexp", "name" ]);
var ATOMIC_START_TOKEN = makePredicate([ "atom", "num", "string", "regexp", "name" ]);
/* -----[ Parser ]----- */
@@ -877,6 +873,7 @@ function parse($TEXT, options) {
prev : null,
peeked : null,
in_function : 0,
in_async : -1,
in_generator : -1,
in_directives : true,
in_loop : 0,
@@ -947,6 +944,10 @@ function parse($TEXT, options) {
return S.in_generator === S.in_function;
}
function is_in_async() {
return S.in_async === S.in_function;
}
function semicolon(optional) {
if (is("punc", ";")) next();
else if (!optional && !can_insert_semicolon()) unexpected();
@@ -1003,6 +1004,11 @@ function parse($TEXT, options) {
return simple_statement();
case "name":
if (S.token.value == "async" && is_token(peek(), "keyword", "function")) {
next();
next();
return function_(AST_Defun, false, true);
}
return is_token(peek(), "punc", ":")
? labeled_statement()
: simple_statement();
@@ -1159,6 +1165,9 @@ function parse($TEXT, options) {
// Ecma-262, 12.1.1 Static Semantics: Early Errors
token_error(S.prev, "Yield cannot be used as label inside generators");
}
if (label.name === "await" && is_in_async()) {
token_error(S.prev, "await cannot be used as label inside async function");
}
if (find_if(function(l){ return l.name == label.name }, S.labels)) {
// ECMA-262, 12.12: An ECMAScript program is considered
// syntactically incorrect if it contains a
@@ -1289,13 +1298,14 @@ function parse($TEXT, options) {
});
};
var function_ = function(ctor, is_generator_property) {
var function_ = function(ctor, is_generator_property, is_async) {
if (is_generator_property && is_async) croak("generators cannot be async");
var start = S.token
var in_statement = ctor === AST_Defun;
var is_generator = is("operator", "*");
if (is_generator) {
next();
next();
}
var name = is("name") ? as_symbol(in_statement ? AST_SymbolDefun : AST_SymbolLambda) : null;
@@ -1303,11 +1313,12 @@ function parse($TEXT, options) {
unexpected();
var args = parameters();
var body = _function_body(true, is_generator || is_generator_property, name, args);
var body = _function_body(true, is_generator || is_generator_property, is_async, name, args);
return new ctor({
start : args.start,
end : body.end,
is_generator: is_generator,
async : is_async,
name : name,
argnames: args,
body : body
@@ -1628,13 +1639,16 @@ function parse($TEXT, options) {
return a;
}
function _function_body(block, generator, name, args) {
function _function_body(block, generator, is_async, name, args) {
var loop = S.in_loop;
var labels = S.labels;
var current_generator = S.in_generator;
var current_async = S.in_async;
++S.in_function;
if (generator)
S.in_generator = S.in_function;
if (is_async)
S.in_async = S.in_function;
if (block)
S.in_directives = true;
S.in_loop = 0;
@@ -1654,9 +1668,22 @@ function parse($TEXT, options) {
S.in_loop = loop;
S.labels = labels;
S.in_generator = current_generator;
S.in_async = current_async;
return a;
}
function _await_expression() {
// Previous token must be "await" and not be interpreted as an identifier
if (!is_in_async()) {
croak("Unexpected await expression outside async function",
S.prev.line, S.prev.col, S.prev.pos);
}
// the await expression is parsed as a unary expression in Babel
return new AST_Await({
expression : maybe_unary(true),
});
}
function _yield_expression() {
// Previous token must be keyword yield and not be interpret as an identifier
if (!is_in_generator()) {
@@ -1665,7 +1692,6 @@ function parse($TEXT, options) {
}
var star = false;
var has_expression = true;
var tmp;
// Attempt to get expression or star (and then the mandatory expression)
// behind yield on the same line.
@@ -1872,7 +1898,6 @@ function parse($TEXT, options) {
var tok = S.token, ret;
switch (tok.type) {
case "name":
case "keyword":
ret = _make_symbol(AST_SymbolRef);
break;
case "num":
@@ -1902,13 +1927,6 @@ function parse($TEXT, options) {
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();
return ret;
@@ -1998,6 +2016,14 @@ function parse($TEXT, options) {
}
unexpected();
}
if (is("name", "async") && is_token(peek(), "keyword", "function")) {
next();
next();
var func = function_(AST_Function, false, true);
func.start = start;
func.end = prev();
return subscripts(func, allow_calls);
}
if (is("keyword", "function")) {
next();
var func = function_(AST_Function);
@@ -2015,7 +2041,7 @@ function parse($TEXT, options) {
if (is("template_head")) {
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);
}
unexpected();
@@ -2079,8 +2105,8 @@ function parse($TEXT, options) {
});
});
var create_accessor = embed_tokens(function(is_generator) {
return function_(AST_Accessor, is_generator);
var create_accessor = embed_tokens(function(is_generator, is_async) {
return function_(AST_Accessor, is_generator, is_async);
});
var object_or_object_destructuring_ = embed_tokens(function() {
@@ -2196,6 +2222,7 @@ function parse($TEXT, options) {
}
return name;
}
var is_async = false;
var is_static = false;
var is_generator = false;
var property_token = start;
@@ -2204,6 +2231,11 @@ function parse($TEXT, options) {
property_token = S.token;
name = as_property_name();
}
if (name === "async" && !is("punc", "(")) {
is_async = true;
property_token = S.token;
name = as_property_name();
}
if (name === null) {
is_generator = true;
property_token = S.token;
@@ -2218,10 +2250,11 @@ function parse($TEXT, options) {
start : start,
static : is_static,
is_generator: is_generator,
async : is_async,
key : name,
quote : name instanceof AST_SymbolMethod ?
property_token.quote : undefined,
value : create_accessor(is_generator),
value : create_accessor(is_generator, is_async),
end : prev()
});
return node;
@@ -2586,6 +2619,14 @@ function parse($TEXT, options) {
var maybe_unary = function(allow_calls) {
var start = S.token;
if (start.type == "name" && start.value == "await") {
if (is_in_async()) {
next();
return _await_expression();
} else if (S.input.has_directive("use strict")) {
token_error(S.token, "Unexpected await identifier inside strict mode")
}
}
if (is("operator") && UNARY_PREFIX(start.value)) {
next();
handle_regexp();

View File

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

View File

@@ -51,7 +51,6 @@ function SymbolDef(scope, index, orig) {
this.global = false;
this.export = false;
this.mangled_name = null;
this.object_destructuring_arg = false;
this.undeclared = false;
this.index = index;
this.id = SymbolDef.next_id++;
@@ -65,7 +64,6 @@ SymbolDef.prototype = {
return (this.global && !options.toplevel)
|| this.export
|| this.object_destructuring_arg
|| this.undeclared
|| (!options.eval && (this.scope.uses_eval || this.scope.uses_with))
|| (options.keep_fnames
@@ -168,9 +166,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
if (node instanceof AST_Symbol) {
node.scope = scope;
}
if (node instanceof AST_SymbolFunarg) {
node.object_destructuring_arg = !!in_destructuring;
}
if (node instanceof AST_Label) {
node.thedef = node;
node.references = [];
@@ -251,11 +246,10 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
}
}
var sym = node.scope.find_variable(name);
if (node.scope instanceof AST_Lambda && name == "arguments") {
node.scope.uses_arguments = true;
}
if (!sym) {
sym = self.def_global(node);
} else if (sym.scope instanceof AST_Lambda && name == "arguments") {
sym.scope.uses_arguments = true;
}
node.thedef = sym;
node.reference(options);
@@ -378,7 +372,6 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol){
if (!this.variables.has(symbol.name)) {
def = new SymbolDef(this, this.variables.size(), symbol);
this.variables.set(symbol.name, def);
def.object_destructuring_arg = symbol.object_destructuring_arg;
def.global = !this.parent_scope;
} else {
def = this.variables.get(symbol.name);
@@ -456,7 +449,7 @@ AST_Symbol.DEFMETHOD("global", function(){
});
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
return defaults(options, {
options = defaults(options, {
eval : false,
ie8 : false,
keep_classnames: false,
@@ -464,6 +457,8 @@ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
reserved : [],
toplevel : false,
});
if (!Array.isArray(options.reserved)) options.reserved = [];
return options;
});
AST_Toplevel.DEFMETHOD("mangle_names", function(options){
@@ -594,6 +589,8 @@ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
base54.consider("finally");
else if (node instanceof AST_Yield)
base54.consider("yield");
else if (node instanceof AST_Await)
base54.consider("await");
else if (node instanceof AST_Symbol && node.unmangleable(options))
base54.consider(node.name);
else if (node instanceof AST_Unary || node instanceof AST_Binary)

View File

@@ -199,6 +199,10 @@ TreeTransformer.prototype = new TreeWalker;
if (self.expression) self.expression = self.expression.transform(tw);
});
_(AST_Await, function(self, tw){
self.expression = self.expression.transform(tw);
});
_(AST_Unary, function(self, tw){
self.expression = self.expression.transform(tw);
});

View File

@@ -43,13 +43,6 @@
"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) {
return Array.prototype.slice.call(a, start || 0);
};

View File

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

View File

@@ -4,6 +4,7 @@
"use strict";
var createHash = require("crypto").createHash;
var fetch = require("./fetch");
var fork = require("child_process").fork;
var args = process.argv.slice(2);
if (!args.length) {
@@ -52,7 +53,8 @@ urls.forEach(function(url) {
output: 0,
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 });
res.on("data", function(data) {
results[url].input += data.length;

View File

@@ -135,3 +135,70 @@ arrow_with_regexp: {
num => /\d{11,14}/.test( num )
}
}
arrow_unused: {
options = {
toplevel: false,
side_effects: true,
unused: true,
}
input: {
top => dog;
let fn = a => { console.log(a * a); };
let u = (x, y) => x - y + g;
(() => { console.log("0"); })();
!function(x) {
(() => { console.log("1"); })();
let unused = x => { console.log(x); };
let baz = e => e + e;
console.log(baz(x));
}(1);
fn(3);
}
expect: {
let fn = a => { console.log(a * a); };
let u = (x, y) => x - y + g;
(() => { console.log("0"); })();
!function(x) {
(() => { console.log("1"); })();
let baz = e => e + e;
console.log(baz(x));
}(1);
fn(3);
}
expect_stdout: [ "0", "1", "2", "9" ]
node_version: ">=6"
}
arrow_unused_toplevel: {
options = {
toplevel: true,
side_effects: true,
unused: true,
}
input: {
top => dog;
let fn = a => { console.log(a * a); };
let u = (x, y) => x - y + g;
(() => { console.log("0"); })();
!function(x) {
(() => { console.log("1"); })();
let unused = x => { console.log(x); };
let baz = e => e + e;
console.log(baz(x));
}(1);
fn(3);
}
expect: {
let fn = a => { console.log(a * a); };
(() => { console.log("0"); })();
!function(x) {
(() => { console.log("1"); })();
let baz = e => e + e;
console.log(baz(x));
}(1);
fn(3);
}
expect_stdout: [ "0", "1", "2", "9" ]
node_version: ">=6"
}

200
test/compress/async.js Normal file
View File

@@ -0,0 +1,200 @@
await_precedence: {
input: {
async function f1() { await x + y; }
async function f2() { await (x + y); }
}
expect_exact: "async function f1(){await x+y}async function f2(){await(x+y)}"
}
async_function_declaration: {
options = {
side_effects: true,
unused: true,
}
input: {
async function f0() {}
async function f1() { await x + y; }
async function f2() { await (x + y); }
async function f3() { await x + await y; }
async function f4() { await (x + await y); }
async function f5() { await x; await y; }
async function f6() { await x, await y; }
}
expect: {
async function f0() {}
async function f1() { await x, y; }
async function f2() { await (x + y); }
async function f3() { await x, await y; }
async function f4() { await (x + await y); }
async function f5() { await x; await y; }
async function f6() { await x, await y; }
}
}
async_function_expression: {
options = {
evaluate: true,
side_effects: true,
unused: true,
}
input: {
var named = async function foo() {
await bar(1 + 0) + (2 + 0);
}
var anon = async function() {
await (1 + 0) + bar(2 + 0);
}
}
expect: {
var named = async function() {
await bar(1);
};
var anon = async function() {
await 1, bar(2);
};
}
}
async_class: {
options = {
evaluate: true,
}
input: {
class Foo {
async m1() {
return await foo(1 + 2);
}
static async m2() {
return await foo(3 + 4);
}
}
}
expect: {
class Foo {
async m1() {
return await foo(3);
}
static async m2() {
return await foo(7);
}
}
}
}
async_object_literal: {
options = {
evaluate: true,
}
input: {
var obj = {
async a() {
await foo(1 + 0);
},
anon: async function(){
await foo(2 + 0);
}
};
}
expect: {
var obj = {
async a() {
await foo(1);
},
anon: async function() {
await foo(2);
}
};
}
}
async_export: {
input: {
export async function run() {};
export default async function def() {};
}
expect: {
export async function run() {};
export default async function def() {};
}
}
async_inline: {
options = {
collapse_vars: true,
conditionals: true,
evaluate: true,
inline: true,
negate_iife: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
(async function(){ return await 3; })();
(async function(x){ await console.log(x); })(4);
function echo(x) { return x; }
echo( async function(){ return await 1; }() );
echo( async function(x){ await console.log(x); }(2) );
function top() { console.log("top"); }
top();
async function async_top() { console.log("async_top"); }
async_top();
}
expect: {
!async function(){await 3}();
!async function(x){await console.log(4)}();
function echo(x){return x}
echo(async function(){return await 1}());
echo(async function(x){await console.log(2)}());
console.log("top");
!async function(){console.log("async_top")}();
}
expect_stdout: [
"4",
"2",
"top",
"async_top",
]
node_version: ">=8"
}
async_identifiers: {
input: {
let async = function(x){ console.log("async", x); };
let await = function(x){ console.log("await", x); };
async(1);
await(2);
}
expect: {
let async = function(x){ console.log("async", x); };
let await = function(x){ console.log("await", x); };
async(1);
await(2);
}
expect_stdout: [
"async 1",
"await 2",
]
node_version: ">=8"
}
/* FIXME: add test when supported by parser
async_arrow: {
input: {
let a1 = async x => await foo(x);
let a2 = async () => await bar();
let a3 = async (x) => await baz(x);
let a4 = async (x, y) => { await far(x, y); }
let a5 = async ({x = [1], y: z = 2}) => { await wow(x, y); }
}
expect: {
}
}
*/

View File

@@ -47,3 +47,142 @@ keep_some_blocks: {
} else stuff();
}
}
issue_1664: {
input: {
var a = 1;
function f() {
if (undefined) a = 2;
{
function undefined() {}
undefined();
}
}
f();
console.log(a);
}
expect: {
var a = 1;
function f() {
if (undefined) a = 2;
{
function undefined() {}
undefined();
}
}
f();
console.log(a);
}
expect_stdout: "1"
node_version: ">=6"
}
issue_1672_for: {
input: {
switch (function() {
return xxx;
}) {
case xxx:
for (; console.log("FAIL");) {
function xxx() {}
}
break;
}
}
expect: {
switch (function() {
return xxx;
}) {
case xxx:
for (; console.log("FAIL");) {
function xxx() {}
}
break;
}
}
expect_stdout: true
node_version: ">=6"
}
issue_1672_for_strict: {
input: {
"use strict";
switch (function() {
return xxx;
}) {
case xxx:
for (; console.log("FAIL");) {
function xxx() {}
}
break;
}
}
expect: {
"use strict";
switch (function() {
return xxx;
}) {
case xxx:
for (; console.log("FAIL");) {
function xxx() {}
}
break;
}
}
expect_stdout: true
node_version: ">=6"
}
issue_1672_if: {
input: {
switch (function() {
return xxx;
}) {
case xxx:
if (console.log("FAIL")) {
function xxx() {}
}
break;
}
}
expect: {
switch (function() {
return xxx;
}) {
case xxx:
if (console.log("FAIL")) function xxx() {}
break;
}
}
expect_stdout: true
node_version: ">=6"
}
issue_1672_if_strict: {
input: {
"use strict";
switch (function() {
return xxx;
}) {
case xxx:
if (console.log("FAIL")) {
function xxx() {}
}
break;
}
}
expect: {
"use strict";
switch (function() {
return xxx;
}) {
case xxx:
if (console.log("FAIL")) {
function xxx() {}
}
break;
}
}
expect_stdout: true
node_version: ">=6"
}

View File

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

View File

@@ -543,3 +543,99 @@ mangle_destructuring_decl_array: {
expect_stdout: "8 7 6 undefined 2 [ 3 ]"
node_version: ">=6"
}
anon_func_with_destructuring_args: {
options = {
evaluate: true,
unused: true,
toplevel: true,
}
mangle = {
toplevel: true,
}
beautify = {
ecma: 5,
}
input: {
(function({foo = 1 + 0, bar = 2}, [car = 3, far = 4]) {
console.log(foo, bar, car, far);
})({bar: 5 - 0}, [, 6]);
}
expect: {
(function({foo: o = 1, bar: n = 2}, [a = 3, b = 4]) {
console.log(o, n, a, b);
})({bar: 5}, [, 6]);
}
expect_stdout: "1 5 3 6"
node_version: ">=6"
}
arrow_func_with_destructuring_args: {
options = {
evaluate: true,
unused: true,
toplevel: true,
}
mangle = {
toplevel: true,
}
beautify = {
ecma: 5,
}
input: {
(({foo = 1 + 0, bar = 2}, [car = 3, far = 4]) => {
console.log(foo, bar, car, far);
})({bar: 5 - 0}, [, 6]);
}
expect: {
(({foo: o = 1, bar: n = 2}, [a = 3, b = 4]) => {
console.log(o, n, a, b);
})({bar: 5}, [, 6]);
}
expect_stdout: "1 5 3 6"
node_version: ">=6"
}
issue_2044_ecma_5: {
beautify = {
beautify: false,
ecma: 5,
}
input: {
({x : a = 1, y = 2 + b, z = 3 - c} = obj);
}
expect_exact: "({x:a=1,y:y=2+b,z:z=3-c}=obj);"
}
issue_2044_ecma_6: {
beautify = {
beautify: false,
ecma: 6,
}
input: {
({x : a = 1, y = 2 + b, z = 3 - c} = obj);
}
expect_exact: "({x:a=1,y=2+b,z=3-c}=obj);"
}
issue_2044_ecma_5_beautify: {
beautify = {
beautify: true,
ecma: 5,
}
input: {
({x : a = 1, y = 2 + b, z = 3 - c} = obj);
}
expect_exact: "({x: a = 1, y: y = 2 + b, z: z = 3 - c} = obj);"
}
issue_2044_ecma_6_beautify: {
beautify = {
beautify: true,
ecma: 6,
}
input: {
({x : a = 1, y = 2 + b, z = 3 - c} = obj);
}
expect_exact: "({x: a = 1, y = 2 + b, z = 3 - c} = obj);"
}

View File

@@ -863,12 +863,12 @@ issue_1583: {
expect: {
function m(t) {
(function(e) {
t = (function() {
return (function(a) {
return a;
})(function(a) {});
})();
})();
t = e();
})(function() {
return (function(a) {
return a;
})(function(a) {});
});
}
}
}
@@ -1280,3 +1280,17 @@ issue_1968: {
expect_stdout: "5"
node_version: ">=6"
}
issue_2063: {
options = {
unused: true,
}
input: {
var a;
var a;
}
expect: {
var a;
var a;
}
}

View File

@@ -738,6 +738,7 @@ unsafe_prototype_function: {
call_args: {
options = {
evaluate: true,
inline: true,
reduce_vars: true,
}
input: {
@@ -758,6 +759,7 @@ call_args: {
call_args_drop_param: {
options = {
evaluate: true,
inline: true,
keep_fargs: false,
reduce_vars: true,
unused: true,
@@ -873,13 +875,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]
);
}
}
@@ -1130,3 +1134,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"
}

View File

@@ -21,6 +21,7 @@ iifes_returning_constants_keep_fargs_true: {
join_vars : true,
reduce_vars : true,
cascade : true,
inline : true,
}
input: {
(function(){ return -1.23; }());
@@ -56,6 +57,7 @@ iifes_returning_constants_keep_fargs_false: {
join_vars : true,
reduce_vars : true,
cascade : true,
inline : true,
}
input: {
(function(){ return -1.23; }());
@@ -82,6 +84,7 @@ issue_485_crashing_1530: {
conditionals: true,
dead_code: true,
evaluate: true,
inline: true,
}
input: {
(function(a) {
@@ -154,6 +157,7 @@ function_returning_constant_literal: {
evaluate: true,
cascade: true,
unused: true,
inline: true,
}
input: {
function greeter() {
@@ -245,3 +249,168 @@ 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"
}
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"
}
issue_2097: {
options = {
negate_iife: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
try {
throw 0;
} catch (e) {
console.log(arguments[0]);
}
}
f(1);
}
expect: {
!function() {
try {
throw 0;
} catch (e) {
console.log(arguments[0]);
}
}(1);
}
expect_stdout: "1"
}
issue_2101: {
options = {
inline: true,
}
input: {
a = {};
console.log(function() {
return function() {
return this.a;
}();
}() === function() {
return a;
}());
}
expect: {
a = {};
console.log(function() {
return this.a;
}() === a);
}
expect_stdout: "true"
}
inner_ref: {
options = {
inline: true,
unused: true,
}
input: {
console.log(function(a) {
return function() {
return a;
}();
}(1), function(a) {
return function(a) {
return a;
}();
}(2));
}
expect: {
console.log(function(a) {
return a;
}(1), function(a) {
return a;
}());
}
expect_stdout: "1 undefined"
}

View File

@@ -583,3 +583,84 @@ class_extends_regex: {
}
expect_exact: "function f(){class rx1 extends(/rx/){}}"
}
issue_2028: {
options = {
side_effects: true,
}
input: {
var a = {};
(function(x) {
x.X = function() {
return X;
};
class X {
static hello() {
console.log("hello");
}
}
}(a));
a.X().hello();
}
expect: {
var a = {};
(function(x) {
x.X = function() {
return X;
};
class X {
static hello() {
console.log("hello");
}
}
}(a));
a.X().hello();
}
expect_stdout: "hello"
node_version: ">=6"
}
class_expression_statement: {
options = {
toplevel: false,
side_effects: false,
unused: false,
}
input: {
(class {});
(class NamedClassExpr {});
let expr = (class AnotherClassExpr {});
class C {}
}
expect_exact: "(class{});(class NamedClassExpr{});let expr=class AnotherClassExpr{};class C{}"
}
class_expression_statement_unused: {
options = {
toplevel: false,
side_effects: true,
unused: true,
}
input: {
(class {});
(class NamedClassExpr {});
let expr = (class AnotherClassExpr {});
class C {}
}
expect_exact: "let expr=class{};class C{}"
}
class_expression_statement_unused_toplevel: {
options = {
toplevel: true,
side_effects: true,
unused: true,
}
input: {
(class {});
(class NamedClassExpr {});
let expr = (class AnotherClassExpr {});
class C {}
}
expect_exact: ""
}

View File

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

View File

@@ -1,7 +1,8 @@
compress_new_function: {
options = {
unsafe: true
unsafe: true,
unsafe_Func: true,
}
input: {
new Function("aa, bb", 'return aa;');
@@ -14,6 +15,7 @@ compress_new_function: {
compress_new_function_with_destruct: {
options = {
unsafe: true,
unsafe_Func: true,
ecma: 6
}
beautify = {
@@ -26,9 +28,7 @@ compress_new_function_with_destruct: {
}
expect: {
Function("a", "[b]", "return a");
Function("a", "{bb}", "return a");
Function("[[a]]", "[{bb}]", 'return a');
Function("a", "{bb:b}", "return a");
Function("[[a]]", "[{bb:b}]", 'return a');
}
}

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

View File

@@ -1,4 +1,4 @@
eval_let: {
eval_let_6: {
input: {
eval("let a;");
console.log();
@@ -10,3 +10,29 @@ eval_let: {
expect_stdout: ""
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

@@ -557,3 +557,105 @@ native_prototype: {
"".indexOf.call(e, "bar");
}
}
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: {
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"
}
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 = {
conditionals : true,
evaluate : true,
inline : true,
global_defs : {
C : 0
},
@@ -1032,6 +1033,7 @@ defun_inline_2: {
defun_inline_3: {
options = {
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
@@ -1054,6 +1056,7 @@ defun_inline_3: {
defun_call: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
@@ -1080,6 +1083,7 @@ defun_call: {
defun_redefine: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
@@ -1112,6 +1116,7 @@ defun_redefine: {
func_inline: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
@@ -1138,6 +1143,7 @@ func_inline: {
func_modified: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
@@ -1311,19 +1317,47 @@ iife_func_side_effects: {
unused: true,
}
input: {
function x() {
console.log("x");
}
function y() {
console.log("y");
}
function z() {
console.log("z");
}
(function(a, b, c) {
return b();
function y() {
console.log("FAIL");
}
return y + b();
})(x(), function() {
return y();
}, z());
}
expect: {
function x() {
console.log("x");
}
function y() {
console.log("y");
}
function z() {
console.log("z");
}
(function(a, b, c) {
return function() {
return y();
}();
})(x(), 0, z());
console.log("FAIL");
} + b();
})(x(), function() {
return y();
}, z());
}
expect_stdout: [
"x",
"z",
"y",
]
}
issue_1595_1: {
@@ -1687,6 +1721,7 @@ redefine_arguments_1: {
redefine_arguments_2: {
options = {
evaluate: true,
inline: true,
keep_fargs: false,
reduce_vars: true,
side_effects: true,
@@ -1723,6 +1758,7 @@ redefine_arguments_2: {
redefine_arguments_3: {
options = {
evaluate: true,
inline: true,
keep_fargs: false,
passes: 3,
reduce_vars: true,
@@ -1799,6 +1835,7 @@ redefine_farg_1: {
redefine_farg_2: {
options = {
evaluate: true,
inline: true,
keep_fargs: false,
reduce_vars: true,
side_effects: true,
@@ -1835,6 +1872,7 @@ redefine_farg_2: {
redefine_farg_3: {
options = {
evaluate: true,
inline: true,
keep_fargs: false,
passes: 3,
reduce_vars: true,
@@ -2537,3 +2575,53 @@ accessor: {
}
expect_stdout: "1 1"
}
issue_2090_1: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
console.log(function() {
var x = 1;
[].forEach(() => x = 2);
return x;
}());
}
expect: {
console.log(function() {
var x = 1;
[].forEach(() => x = 2);
return x;
}());
}
expect_stdout: "1"
node_version: ">=4"
}
issue_2090_2: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
console.log(function() {
var x = 1;
[].forEach(() => {
x = 2;
});
return x;
}());
}
expect: {
console.log(function() {
var x = 1;
[].forEach(() => {
x = 2;
});
return x;
}());
}
expect_stdout: "1"
node_version: ">=4"
}

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

View File

@@ -9,7 +9,7 @@ function read(path) {
describe("bin/uglifyjs", function () {
var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs';
it("should produce a functional build when using --self", function (done) {
this.timeout(15000);
this.timeout(30000);
var command = uglifyjscmd + ' --self -cm --wrap WrappedUglifyJS';
@@ -77,6 +77,23 @@ describe("bin/uglifyjs", function () {
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) {
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) {
assert.ok(err);
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();
});
});

View File

@@ -31,7 +31,7 @@ describe("bin/uglifyjs with input file globs", function() {
exec(command, function(err, stdout) {
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();
});
});

View File

@@ -13,6 +13,13 @@ describe("minify", function() {
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() {
it("Should preserve quotes in object literals", function() {
var js = 'var foo = {"x": 1, y: 2, \'z\': 3};';
@@ -106,7 +113,7 @@ describe("minify", function() {
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[0], "inline source map not found");
} finally {
@@ -207,5 +214,17 @@ describe("minify", function() {
assert.ok(err instanceof Error);
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,23 +1,20 @@
var assert = require("assert");
var semver = require("semver");
var spawn = require("child_process").spawn;
if (!process.env.UGLIFYJS_TEST_ALL) return;
function run(command, args, done) {
var id = setInterval(function() {
process.stdout.write("\0");
}, 5 * 60 * 1000);
spawn(command, args, {
stdio: "ignore"
stdio: [ "ignore", 1, 2 ]
}).on("exit", function(code) {
clearInterval(id);
assert.strictEqual(code, 0);
done();
});
}
describe("test/benchmark.js", function() {
this.timeout(5 * 60 * 1000);
this.timeout(10 * 60 * 1000);
[
"-b",
"-b bracketize",
@@ -36,11 +33,9 @@ describe("test/benchmark.js", function() {
});
});
if (semver.satisfies(process.version, "0.12")) return;
describe("test/jetstream.js", function() {
this.timeout(20 * 60 * 1000);
it("Should install phantomjs-prebuilt", function(done) {
run("npm", ["install", "phantomjs-prebuilt@2.1.14"], done);
});
[
"-mc",
"-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) {
var args = options.split(/ /);
args.unshift("test/jetstream.js");
args.push("-b", "beautify=false,webkit");
run(process.argv[0], args, done);
});
});

View File

@@ -4,7 +4,7 @@ var uglify = require("../node");
describe("spidermonkey export/import sanity test", function() {
it("should produce a functional build when using --self with spidermonkey", function(done) {
this.timeout(30000);
this.timeout(60000);
var uglifyjs = '"' + process.argv[0] + '" bin/uglifyjs';
var command = uglifyjs + " --self -cm --wrap SpiderUglify -o spidermonkey | " +

View File

@@ -294,8 +294,22 @@ function parse_test(file) {
if (label.name == "expect_exact" || label.name == "node_version") {
test[label.name] = read_string(stat);
} else if (label.name == "expect_stdout") {
if (stat.TYPE == "SimpleStatement" && stat.body instanceof U.AST_Boolean) {
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 {
test[label.name] = read_string(stat) + "\n";
}

View File

@@ -19,23 +19,25 @@ function safe_log(arg, level) {
var FUNC_TOSTRING = [
"Function.prototype.toString = Function.prototype.valueOf = function() {",
" var id = 0;",
" var id = 100000;",
" return function() {",
' if (this === Array) return "[Function: Array]";',
' if (this === Object) return "[Function: Object]";',
" var i = this.name;",
' if (typeof i != "number") {',
" i = ++id;",
].concat(Object.getOwnPropertyDescriptor(Function.prototype, "name").configurable ? [
' Object.defineProperty(this, "name", {',
" get: function() {",
" return i;",
" }",
" });",
] : [], [
" }",
' return "[Function: " + i + "]";',
" }",
"}();",
].join("\n");
]).join("\n");
exports.run_code = function(code) {
var stdout = "";
var original_write = process.stdout.write;
@@ -50,7 +52,10 @@ exports.run_code = function(code) {
"}();",
].join("\n"), {
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 safe_log(arg, 3);
}));

View File

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