Compare commits

..

26 Commits

Author SHA1 Message Date
Alex Lam S.L
491f16c766 v2.8.16 2017-03-25 03:21:16 +08:00
Alex Lam S.L
a30092e20f fix invalid AST_For.init (#1657)
Turns out the only place in `Compressor` which can generate invalid `AST_For.init` is within `drop_unused()`, so focus the fix-up efforts.

supercedes #1652
fixes #1656
2017-03-25 03:18:36 +08:00
Alex Lam S.L
b1abe92e1a introduce ufuzz.js (#1655)
closes #1647
2017-03-25 01:46:12 +08:00
Alex Lam S.L
b454ce667e Update ISSUE_TEMPLATE.md 2017-03-24 23:12:58 +08:00
Alex Lam S.L
32283a0def fix cascade of evaluate optimisation (#1654)
Operator has changed, so break out from rest of the rules.

fixes #1649
2017-03-24 22:09:19 +08:00
Alex Lam S.L
ac51d4c5a0 fix corner case in AST_For.init (#1652)
Enforce `null` as value for empty initialisation blocks.

fixes #1648
2017-03-24 19:31:17 +08:00
Alex Lam S.L
0432a7abb9 fix assignment extraction from conditional (#1651)
fixes #1645
fixes #1646
2017-03-24 18:52:48 +08:00
Alex Lam S.L
f3a1694a41 fix assignment substitution in sequences (#1643)
take side effects of binary boolean operations into account

fixes #1639
2017-03-24 14:30:31 +08:00
Alex Lam S.L
2e0dc97003 improve error marker placement (#1644)
For AST_UnaryPrefix, points to the operator rather than end of expression.
2017-03-24 14:28:40 +08:00
Alex Lam S.L
701035621d fix expect_stdout (#1642)
`compress()` may modify input ASTs

add tests for #1627 & #1640
2017-03-24 13:19:50 +08:00
kzc
79334dda10 fix regression: CLI options with hyphens like -b ascii-only (#1640)
fixes #1637
2017-03-24 11:55:03 +08:00
Alex Lam S.L
e918748d88 improve collapsible value detection (#1638)
- #1634 bars variables with cross-scope references in between to collapse
- but if assigned value is side-effect-free, no states can be modified, so it is safe to move
2017-03-24 02:55:32 +08:00
Alex Lam S.L
6b2f34769a v2.8.15 2017-03-23 13:36:47 +08:00
Alex Lam S.L
48ffbef51d account for cross-scope modifications in collapse_vars (#1634)
mostly done by @kzc

fixes #1631
2017-03-23 07:17:34 +08:00
Alex Lam S.L
c0f3feae9f introduce compressor.info() (#1633)
report the following only when `options.warnings = "verbose"`
- unused elements due to inlining
- collpased variables
2017-03-23 06:49:49 +08:00
Alex Lam S.L
a00040dd93 fix a bug in simple_glob (#1632)
- "?" should not match "/"
- other minor clean-ups
2017-03-23 06:11:16 +08:00
Alex Lam S.L
ee95c1b38b metadata cleanup (#1630)
- mention performance anomaly in Node 7 and drop from CI
- remove unused npm "scripts"
- mark browserify dependency as optional
- stop `test/mozilla-ast.js` from spamming console output in later versions of Node.js
2017-03-23 01:31:46 +08:00
Alex Lam S.L
4bceb85cbf throw parse error on invalid assignments (#1627)
fixes #1626
2017-03-21 14:11:32 +08:00
Alex Lam S.L
30a75049f5 v2.8.14 2017-03-19 15:24:57 +08:00
Alex Lam S.L
a3cc3a9b87 make expect_stdout work on Node.js 0.12 (#1623)
That particular version of Node.js has messed up error messages, so provide a version-specific workaround.

Also fixed an formatting issue which would cause `expect_stdout` to fail if error message contains excerpts of input.

Apply `expect_stdout` to more applicable tests.
2017-03-19 12:00:32 +08:00
Alex Lam S.L
96f8befdd7 fix commit 88fb83a (#1622)
The following is wrong:
    `a == (b ? a : c)` => `b`
Because:
- `b` may not be boolean
- `a` might have side effects
- `a == a` is not always `true` (think `NaN`)
- `a == c` is not always `false`
2017-03-19 11:59:42 +08:00
Alex Lam S.L
cd58635dcc fix AST_Binary.lift_sequences() (#1621)
Commit eab99a1c fails to account for side effects from compound assignments.
2017-03-19 03:04:22 +08:00
Alex Lam S.L
274331d0ea transform String.charAt() to index access (#1620)
Guarded by `unsafe` as `charAt()` can be overridden.
2017-03-19 02:17:15 +08:00
Alex Lam S.L
0489d6de64 handle runtime errors in expect_stdout (#1618)
allow test to pass if both `input` and `expect` throws the same kind of error
2017-03-18 02:33:51 +08:00
Alex Lam S.L
fb092839c2 fix top-level directives in compress tests (#1615)
`input` and `expect` are parsed as `AST_BlockStatement` which does not support `AST_Directive` by default.

Emulate that by transforming preceding `AST_SimpleStatement`s of `AST_String` into `AST_Directive`.
2017-03-18 01:56:15 +08:00
Christian Maughan Tegnér
b7c112eefe Add --in-source-map inline documentation (#1611) 2017-03-17 03:08:38 +08:00
46 changed files with 1209 additions and 229 deletions

View File

@@ -1,7 +1,9 @@
- Bug report or feature request? - Bug report or feature request? <!-- Note: sub-optimal but correct code is not a bug -->
- `uglify-js` version (`uglifyjs -V`) - `uglify-js` version (`uglifyjs -V`)
- JavaScript input - ideally as small as possible. - JavaScript input - ideally as small as possible.
- The `uglifyjs` CLI command executed or `minify()` options used. - The `uglifyjs` CLI command executed or `minify()` options used.
- An example of JavaScript output produced and/or the error or warning. - An example of JavaScript output produced and/or the error or warning.
<!--
Note: the release version of `uglify-js` only supports ES5. Those wishing to minify ES6 should use the experimental [`harmony`](https://github.com/mishoo/UglifyJS2#harmony) branch. Note: the release version of uglify-js only supports ES5. Those wishing
to minify ES6 should use the experimental harmony branch.
-->

View File

@@ -5,7 +5,6 @@ node_js:
- "0.12" - "0.12"
- "4" - "4"
- "6" - "6"
- "7"
env: env:
- UGLIFYJS_TEST_ALL=1 - UGLIFYJS_TEST_ALL=1
matrix: matrix:

View File

@@ -10,8 +10,10 @@ There's also an
[in-browser online demo](http://lisperator.net/uglifyjs/#demo) (for Firefox, [in-browser online demo](http://lisperator.net/uglifyjs/#demo) (for Firefox,
Chrome and probably Safari). Chrome and probably Safari).
Note: release versions of `uglify-js` only support ECMAScript 5 (ES5). If you wish to minify #### Note:
- release versions of `uglify-js` only support ECMAScript 5 (ES5). If you wish to minify
ES2015+ (ES6+) code then please use the [harmony](#harmony) development branch. ES2015+ (ES6+) code then please use the [harmony](#harmony) development branch.
- Node 7 has a known performance regression and runs `uglify-js` twice as slow.
Install Install
------- -------
@@ -68,7 +70,8 @@ The available options are:
--source-map-inline Write base64-encoded source map to the end of js output. --source-map-inline Write base64-encoded source map to the end of js output.
--in-source-map Input source map, useful if you're compressing --in-source-map Input source map, useful if you're compressing
JS that was generated from some other original JS that was generated from some other original
code. code. Specify "inline" if the source map is included
inline with the sources.
--screw-ie8 Use this flag if you don't wish to support --screw-ie8 Use this flag if you don't wish to support
Internet Explorer 6/7/8. Internet Explorer 6/7/8.
By default UglifyJS will not try to be IE-proof. By default UglifyJS will not try to be IE-proof.
@@ -200,9 +203,10 @@ compressed JS by mapping every token in the compiled JS to its original
location. location.
To use this feature you need to pass `--in-source-map To use this feature you need to pass `--in-source-map
/path/to/input/source.map`. Normally the input source map should also point /path/to/input/source.map` or `--in-source-map inline` if the source map is
to the file containing the generated JS, so if that's correct you can omit included inline with the sources. Normally the input source map should also
input files from the command line. point to the file containing the generated JS, so if that's correct you can
omit input files from the command line.
## Mangler options ## Mangler options

View File

@@ -561,7 +561,7 @@ function getOptions(flag, constants) {
var ast; var ast;
try { try {
ast = UglifyJS.parse(x, { expression: true }); ast = UglifyJS.parse(x, { cli: true, expression: true });
} catch(ex) { } catch(ex) {
if (ex instanceof UglifyJS.JS_Parse_Error) { if (ex instanceof UglifyJS.JS_Parse_Error) {
print_error("Error parsing arguments for flag `" + flag + "': " + x); print_error("Error parsing arguments for flag `" + flag + "': " + x);

View File

@@ -131,6 +131,11 @@ merge(Compressor.prototype, {
} }
return node; return node;
}, },
info: function() {
if (this.options.warnings == "verbose") {
AST_Node.warn.apply(AST_Node, arguments);
}
},
warn: function(text, props) { warn: function(text, props) {
if (this.options.warnings) { if (this.options.warnings) {
// only emit unique warnings // only emit unique warnings
@@ -587,9 +592,10 @@ merge(Compressor.prototype, {
// Restrict var replacement to constants if side effects encountered. // Restrict var replacement to constants if side effects encountered.
if (side_effects_encountered |= lvalues_encountered) continue; if (side_effects_encountered |= lvalues_encountered) continue;
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
// Non-constant single use vars can only be replaced in same scope. // Non-constant single use vars can only be replaced in same scope.
if (ref.scope !== self) { if (ref.scope !== self) {
side_effects_encountered |= var_decl.value.has_side_effects(compressor); side_effects_encountered |= value_has_side_effects;
continue; continue;
} }
@@ -614,12 +620,25 @@ merge(Compressor.prototype, {
|| node instanceof AST_IterationStatement || node instanceof AST_IterationStatement
|| (parent instanceof AST_If && node !== parent.condition) || (parent instanceof AST_If && node !== parent.condition)
|| (parent instanceof AST_Conditional && node !== parent.condition) || (parent instanceof AST_Conditional && node !== parent.condition)
|| (node instanceof AST_SymbolRef
&& value_has_side_effects
&& !are_references_in_scope(node.definition(), self))
|| (parent instanceof AST_Binary || (parent instanceof AST_Binary
&& (parent.operator == "&&" || parent.operator == "||") && (parent.operator == "&&" || parent.operator == "||")
&& node === parent.right) && node === parent.right)
|| (parent instanceof AST_Switch && node !== parent.expression)) { || (parent instanceof AST_Switch && node !== parent.expression)) {
return side_effects_encountered = unwind = true, node; return side_effects_encountered = unwind = true, node;
} }
function are_references_in_scope(def, scope) {
if (def.orig.length === 1
&& def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope !== scope) return false;
var refs = def.references;
for (var i = 0, len = refs.length; i < len; i++) {
if (refs[i].scope !== scope) return false;
}
return true;
}
}, },
function postorder(node) { function postorder(node) {
if (unwind) return node; if (unwind) return node;
@@ -664,7 +683,7 @@ merge(Compressor.prototype, {
// Further optimize statement after substitution. // Further optimize statement after substitution.
stat.reset_opt_flags(compressor); stat.reset_opt_flags(compressor);
compressor.warn("Collapsing " + (is_constant ? "constant" : "variable") + compressor.info("Collapsing " + (is_constant ? "constant" : "variable") +
" " + var_name + " [{file}:{line},{col}]", node.start); " " + var_name + " [{file}:{line},{col}]", node.start);
CHANGED = true; CHANGED = true;
return value; return value;
@@ -1828,7 +1847,7 @@ merge(Compressor.prototype, {
sym.__unused = true; sym.__unused = true;
if (trim) { if (trim) {
a.pop(); a.pop();
compressor.warn("Dropping unused function argument {name} [{file}:{line},{col}]", { compressor[sym.unreferenced() ? "warn" : "info"]("Dropping unused function argument {name} [{file}:{line},{col}]", {
name : sym.name, name : sym.name,
file : sym.start.file, file : sym.start.file,
line : sym.start.line, line : sym.start.line,
@@ -1843,7 +1862,7 @@ merge(Compressor.prototype, {
} }
if (drop_funcs && node instanceof AST_Defun && node !== self) { if (drop_funcs && node instanceof AST_Defun && node !== self) {
if (!(node.name.definition().id in in_use_ids)) { if (!(node.name.definition().id in in_use_ids)) {
compressor.warn("Dropping unused function {name} [{file}:{line},{col}]", { compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", {
name : node.name.name, name : node.name.name,
file : node.name.start.file, file : node.name.start.file,
line : node.name.start.line, line : node.name.start.line,
@@ -1867,7 +1886,7 @@ merge(Compressor.prototype, {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w); compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w);
return true; return true;
} }
compressor.warn("Dropping unused variable {name} [{file}:{line},{col}]", w); compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", w);
return false; return false;
}); });
// place uninitialized names at the start // place uninitialized names at the start
@@ -1942,6 +1961,9 @@ merge(Compressor.prototype, {
return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, { return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, {
body: body body: body
}); });
} else if (is_empty(node.init)) {
node.init = null;
return node;
} }
} }
if (node instanceof AST_Scope && node !== self) if (node instanceof AST_Scope && node !== self)
@@ -2656,7 +2678,7 @@ merge(Compressor.prototype, {
if (self.args.length != 1) { if (self.args.length != 1) {
return make_node(AST_Array, self, { return make_node(AST_Array, self, {
elements: self.args elements: self.args
}).transform(compressor); }).optimize(compressor);
} }
break; break;
case "Object": case "Object":
@@ -2674,7 +2696,7 @@ merge(Compressor.prototype, {
left: self.args[0], left: self.args[0],
operator: "+", operator: "+",
right: make_node(AST_String, self, { value: "" }) right: make_node(AST_String, self, { value: "" })
}).transform(compressor); }).optimize(compressor);
break; break;
case "Number": case "Number":
if (self.args.length == 0) return make_node(AST_Number, self, { if (self.args.length == 0) return make_node(AST_Number, self, {
@@ -2683,7 +2705,7 @@ merge(Compressor.prototype, {
if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, { if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
expression: self.args[0], expression: self.args[0],
operator: "+" operator: "+"
}).transform(compressor); }).optimize(compressor);
case "Boolean": case "Boolean":
if (self.args.length == 0) return make_node(AST_False, self); if (self.args.length == 0) return make_node(AST_False, self);
if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, { if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
@@ -2692,7 +2714,7 @@ merge(Compressor.prototype, {
operator: "!" operator: "!"
}), }),
operator: "!" operator: "!"
}).transform(compressor); }).optimize(compressor);
break; break;
case "Function": case "Function":
// new Function() => function(){} // new Function() => function(){}
@@ -2757,7 +2779,7 @@ merge(Compressor.prototype, {
left: make_node(AST_String, self, { value: "" }), left: make_node(AST_String, self, { value: "" }),
operator: "+", operator: "+",
right: exp.expression right: exp.expression
}).transform(compressor); }).optimize(compressor);
} }
else if (exp instanceof AST_Dot && exp.expression instanceof AST_Array && exp.property == "join") EXIT: { else if (exp instanceof AST_Dot && exp.expression instanceof AST_Array && exp.property == "join") EXIT: {
var separator; var separator;
@@ -2811,7 +2833,7 @@ merge(Compressor.prototype, {
left : prev, left : prev,
right : el right : el
}); });
}, first).transform(compressor); }, first).optimize(compressor);
} }
// need this awkward cloning to not affect original element // need this awkward cloning to not affect original element
// best_of will decide which one to get through. // best_of will decide which one to get through.
@@ -2821,6 +2843,16 @@ merge(Compressor.prototype, {
node.expression.expression.elements = elements; node.expression.expression.elements = elements;
return best_of(compressor, self, node); return best_of(compressor, self, node);
} }
else if (exp instanceof AST_Dot && exp.expression.is_string(compressor) && exp.property == "charAt") {
var arg = self.args[0];
var index = arg ? arg.evaluate(compressor) : 0;
if (index !== arg) {
return make_node(AST_Sub, exp, {
expression: exp.expression,
property: make_node_from_constant(index | 0, arg || exp)
}).optimize(compressor);
}
}
} }
if (exp instanceof AST_Function) { if (exp instanceof AST_Function) {
if (exp.body[0] instanceof AST_Return) { if (exp.body[0] instanceof AST_Return) {
@@ -2905,7 +2937,12 @@ merge(Compressor.prototype, {
return car; return car;
} }
if (cdr instanceof AST_Binary && !(cdr instanceof AST_Assign)) { if (cdr instanceof AST_Binary && !(cdr instanceof AST_Assign)) {
field = cdr.left.is_constant() ? "right" : "left"; if (cdr.left.is_constant()) {
if (cdr.operator == "||" || cdr.operator == "&&") break;
field = "right";
} else {
field = "left";
}
} else if (cdr instanceof AST_Call } else if (cdr instanceof AST_Call
|| cdr instanceof AST_Unary && cdr.operator != "++" && cdr.operator != "--") { || cdr instanceof AST_Unary && cdr.operator != "++" && cdr.operator != "--") {
field = "expression"; field = "expression";
@@ -2989,14 +3026,6 @@ merge(Compressor.prototype, {
return self; return self;
}); });
function has_side_effects_or_prop_access(node, compressor) {
var save_pure_getters = compressor.option("pure_getters");
compressor.options.pure_getters = false;
var ret = node.has_side_effects(compressor);
compressor.options.pure_getters = save_pure_getters;
return ret;
}
AST_Binary.DEFMETHOD("lift_sequences", function(compressor){ AST_Binary.DEFMETHOD("lift_sequences", function(compressor){
if (compressor.option("sequences")) { if (compressor.option("sequences")) {
if (this.left instanceof AST_Seq) { if (this.left instanceof AST_Seq) {
@@ -3004,18 +3033,23 @@ merge(Compressor.prototype, {
var x = seq.to_array(); var x = seq.to_array();
this.left = x.pop(); this.left = x.pop();
x.push(this); x.push(this);
seq = AST_Seq.from_array(x).transform(compressor); return AST_Seq.from_array(x).optimize(compressor);
return seq;
} }
if (this.right instanceof AST_Seq if (this.right instanceof AST_Seq && !this.left.has_side_effects(compressor)) {
&& this instanceof AST_Assign var assign = this.operator == "=" && this.left instanceof AST_SymbolRef;
&& !has_side_effects_or_prop_access(this.left, compressor)) { var root = this.right;
var seq = this.right; var cursor, seq = root;
var x = seq.to_array(); while (assign || !seq.car.has_side_effects(compressor)) {
this.right = x.pop(); cursor = seq;
x.push(this); if (seq.cdr instanceof AST_Seq) {
seq = AST_Seq.from_array(x).transform(compressor); seq = seq.cdr;
return seq; } else break;
}
if (cursor) {
this.right = cursor.cdr;
cursor.cdr = this;
return root.optimize(compressor);
}
} }
} }
return this; return this;
@@ -3050,32 +3084,6 @@ merge(Compressor.prototype, {
reverse(); reverse();
} }
} }
if (/^[!=]==?$/.test(self.operator)) {
if (self.left instanceof AST_SymbolRef && self.right instanceof AST_Conditional) {
if (self.right.consequent instanceof AST_SymbolRef
&& self.right.consequent.definition() === self.left.definition()) {
if (/^==/.test(self.operator)) return self.right.condition;
if (/^!=/.test(self.operator)) return self.right.condition.negate(compressor);
}
if (self.right.alternative instanceof AST_SymbolRef
&& self.right.alternative.definition() === self.left.definition()) {
if (/^==/.test(self.operator)) return self.right.condition.negate(compressor);
if (/^!=/.test(self.operator)) return self.right.condition;
}
}
if (self.right instanceof AST_SymbolRef && self.left instanceof AST_Conditional) {
if (self.left.consequent instanceof AST_SymbolRef
&& self.left.consequent.definition() === self.right.definition()) {
if (/^==/.test(self.operator)) return self.left.condition;
if (/^!=/.test(self.operator)) return self.left.condition.negate(compressor);
}
if (self.left.alternative instanceof AST_SymbolRef
&& self.left.alternative.definition() === self.right.definition()) {
if (/^==/.test(self.operator)) return self.left.condition.negate(compressor);
if (/^!=/.test(self.operator)) return self.left.condition;
}
}
}
} }
self = self.lift_sequences(compressor); self = self.lift_sequences(compressor);
if (compressor.option("comparisons")) switch (self.operator) { if (compressor.option("comparisons")) switch (self.operator) {
@@ -3286,6 +3294,7 @@ merge(Compressor.prototype, {
left: self.left, left: self.left,
right: self.right.expression right: self.right.expression
}); });
break;
} }
// -a + b => b - a // -a + b => b - a
if (self.left instanceof AST_UnaryPrefix if (self.left instanceof AST_UnaryPrefix
@@ -3297,6 +3306,7 @@ merge(Compressor.prototype, {
left: self.right, left: self.right,
right: self.left.expression right: self.left.expression
}); });
break;
} }
case "*": case "*":
associative = compressor.option("unsafe_math"); associative = compressor.option("unsafe_math");
@@ -3545,19 +3555,17 @@ merge(Compressor.prototype, {
} }
var consequent = self.consequent; var consequent = self.consequent;
var alternative = self.alternative; var alternative = self.alternative;
// if (foo) exp = something; else exp = something_else;
// |
// v
// exp = foo ? something : something_else;
if (consequent instanceof AST_Assign if (consequent instanceof AST_Assign
&& alternative instanceof AST_Assign && alternative instanceof AST_Assign
&& consequent.operator == alternative.operator && consequent.operator == alternative.operator
&& consequent.left.equivalent_to(alternative.left) && consequent.left.equivalent_to(alternative.left)
&& (!consequent.left.has_side_effects(compressor) && (!self.condition.has_side_effects(compressor)
|| !self.condition.has_side_effects(compressor)) || consequent.operator == "="
) { && !consequent.left.has_side_effects(compressor))) {
/*
* Stuff like this:
* if (foo) exp = something; else exp = something_else;
* ==>
* exp = foo ? something : something_else;
*/
return make_node(AST_Assign, self, { return make_node(AST_Assign, self, {
operator: consequent.operator, operator: consequent.operator,
left: consequent.left, left: consequent.left,

View File

@@ -799,7 +799,7 @@ function OutputStream(options) {
output.print("for"); output.print("for");
output.space(); output.space();
output.with_parens(function(){ output.with_parens(function(){
if (self.init && !(self.init instanceof AST_EmptyStatement)) { if (self.init) {
if (self.init instanceof AST_Definitions) { if (self.init instanceof AST_Definitions) {
self.init.print(output); self.init.print(output);
} else { } else {

View File

@@ -695,6 +695,7 @@ function parse($TEXT, options) {
html5_comments : true, html5_comments : true,
bare_returns : false, bare_returns : false,
shebang : true, shebang : true,
cli : false,
}); });
var S = { var S = {
@@ -1456,7 +1457,7 @@ function parse($TEXT, options) {
function make_unary(ctor, op, expr) { function make_unary(ctor, op, expr) {
if ((op == "++" || op == "--") && !is_assignable(expr)) if ((op == "++" || op == "--") && !is_assignable(expr))
croak("Invalid use of " + op + " operator"); croak("Invalid use of " + op + " operator", null, ctor === AST_UnaryPrefix ? expr.start.col - 1 : null);
return new ctor({ operator: op, expression: expr }); return new ctor({ operator: op, expression: expr });
}; };
@@ -1501,9 +1502,8 @@ function parse($TEXT, options) {
}; };
function is_assignable(expr) { function is_assignable(expr) {
if (!options.strict) return true; if (options.cli) return true;
if (expr instanceof AST_This) return false; return expr instanceof AST_PropAccess || expr instanceof AST_SymbolRef;
return (expr instanceof AST_PropAccess || expr instanceof AST_Symbol);
}; };
var maybe_assign = function(no_in) { var maybe_assign = function(no_in) {

View File

@@ -4,7 +4,7 @@
"homepage": "http://lisperator.net/uglifyjs", "homepage": "http://lisperator.net/uglifyjs",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)", "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"version": "2.8.13", "version": "2.8.16",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },
@@ -30,7 +30,6 @@
], ],
"dependencies": { "dependencies": {
"source-map": "~0.5.1", "source-map": "~0.5.1",
"uglify-to-browserify": "~1.0.0",
"yargs": "~3.10.0" "yargs": "~3.10.0"
}, },
"devDependencies": { "devDependencies": {
@@ -40,13 +39,15 @@
"estraverse": "~1.5.1", "estraverse": "~1.5.1",
"mocha": "~2.3.4" "mocha": "~2.3.4"
}, },
"optionalDependencies": {
"uglify-to-browserify": "~1.0.0"
},
"browserify": { "browserify": {
"transform": [ "transform": [
"uglify-to-browserify" "uglify-to-browserify"
] ]
}, },
"scripts": { "scripts": {
"shrinkwrap": "rm ./npm-shrinkwrap.json; rm -rf ./node_modules; npm i && npm shrinkwrap && npm outdated",
"test": "node test/run-tests.js" "test": "node test/run-tests.js"
}, },
"keywords": ["uglify", "uglify-js", "minify", "minifier"] "keywords": ["uglify", "uglify-js", "minify", "minifier"]

View File

@@ -43,6 +43,7 @@ collapse_vars_side_effects_1: {
z = i += 4; z = i += 4;
log(x, z, y, i); log(x, z, y, i);
} }
f1(), f2(), f3(), f4();
} }
expect: { expect: {
function f1() { function f1() {
@@ -73,7 +74,9 @@ collapse_vars_side_effects_1: {
y = i += 3; y = i += 3;
log(x, i += 4, y, i); log(x, i += 4, y, i);
} }
f1(), f2(), f3(), f4();
} }
expect_stdout: true
} }
collapse_vars_side_effects_2: { collapse_vars_side_effects_2: {
@@ -823,6 +826,7 @@ collapse_vars_repeated: {
console.log(e + "!"); console.log(e + "!");
})("!"); })("!");
} }
expect_stdout: true
} }
collapse_vars_closures: { collapse_vars_closures: {
@@ -1109,6 +1113,7 @@ collapse_vars_eval_and_with: {
return function() { with (o) console.log(a) }; return function() { with (o) console.log(a) };
})()(); })()();
} }
expect_stdout: true
} }
collapse_vars_constants: { collapse_vars_constants: {
@@ -1168,6 +1173,7 @@ collapse_vars_arguments: {
(function(){console.log(arguments);})(7, 1); (function(){console.log(arguments);})(7, 1);
})(); })();
} }
expect_stdout: true
} }
collapse_vars_short_circuit: { collapse_vars_short_circuit: {
@@ -1317,6 +1323,7 @@ collapse_vars_regexp: {
console.log(result[0]); console.log(result[0]);
})(); })();
} }
expect_stdout: true
} }
issue_1537: { issue_1537: {
@@ -1408,3 +1415,178 @@ issue_1605_2: {
(new Object).p = 1; (new Object).p = 1;
} }
} }
issue_1631_1: {
options = {
cascade: true,
collapse_vars: true,
hoist_funs: true,
join_vars: true,
sequences: true,
side_effects: true,
}
input: {
var pc = 0;
function f(x) {
pc = 200;
return 100;
}
function x() {
var t = f();
pc += t;
return pc;
}
console.log(x());
}
expect: {
function f(x) {
return pc = 200, 100;
}
function x() {
var t = f();
return pc += t;
}
var pc = 0;
console.log(x());
}
expect_stdout: "300"
}
issue_1631_2: {
options = {
cascade: true,
collapse_vars: true,
hoist_funs: true,
join_vars: true,
sequences: true,
side_effects: true,
}
input: {
var a = 0, b = 1;
function f() {
a = 2;
return 4;
}
function g() {
var t = f();
b = a + t;
return b;
}
console.log(g());
}
expect: {
function f() {
return a = 2, 4;
}
function g() {
var t = f();
return b = a + t;
}
var a = 0, b = 1;
console.log(g());
}
expect_stdout: "6"
}
issue_1631_3: {
options = {
cascade: true,
collapse_vars: true,
hoist_funs: true,
join_vars: true,
sequences: true,
side_effects: true,
}
input: {
function g() {
var a = 0, b = 1;
function f() {
a = 2;
return 4;
}
var t = f();
b = a + t;
return b;
}
console.log(g());
}
expect: {
function g() {
function f() {
return a = 2, 4;
}
var a = 0, b = 1, t = f();
return b = a + t;
}
console.log(g());
}
expect_stdout: "6"
}
var_side_effects_1: {
options = {
collapse_vars: true,
}
input: {
var print = console.log.bind(console);
function foo(x) {
var twice = x * 2;
print('Foo:', twice);
}
foo(10);
}
expect: {
var print = console.log.bind(console);
function foo(x) {
print('Foo:', 2 * x);
}
foo(10);
}
expect_stdout: true
}
var_side_effects_2: {
options = {
collapse_vars: true,
}
input: {
var print = console.log.bind(console);
function foo(x) {
var twice = x.y * 2;
print('Foo:', twice);
}
foo({ y: 10 });
}
expect: {
var print = console.log.bind(console);
function foo(x) {
var twice = 2 * x.y;
print('Foo:', twice);
}
foo({ y: 10 });
}
expect_stdout: true
}
var_side_effects_3: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
var print = console.log.bind(console);
function foo(x) {
var twice = x.y * 2;
print('Foo:', twice);
}
foo({ y: 10 });
}
expect: {
var print = console.log.bind(console);
function foo(x) {
print('Foo:', 2 * x.y);
}
foo({ y: 10 });
}
expect_stdout: true
}

View File

@@ -51,6 +51,7 @@ concat_2: {
"1" + "2" + "3" "1" + "2" + "3"
); );
} }
expect_stdout: true
} }
concat_3: { concat_3: {
@@ -79,6 +80,7 @@ concat_3: {
1 + 2 + "3" + "4" + "5" 1 + 2 + "3" + "4" + "5"
); );
} }
expect_stdout: true
} }
concat_4: { concat_4: {
@@ -107,6 +109,7 @@ concat_4: {
1 + "2" + "3" + "4" + "5" 1 + "2" + "3" + "4" + "5"
); );
} }
expect_stdout: true
} }
concat_5: { concat_5: {
@@ -135,6 +138,7 @@ concat_5: {
"1" + 2 + "3" + "4" + "5" "1" + 2 + "3" + "4" + "5"
); );
} }
expect_stdout: true
} }
concat_6: { concat_6: {
@@ -163,6 +167,7 @@ concat_6: {
"1" + "2" + "3" + "4" + "5" "1" + "2" + "3" + "4" + "5"
); );
} }
expect_stdout: true
} }
concat_7: { concat_7: {
@@ -188,6 +193,7 @@ concat_7: {
x += "foo" x += "foo"
); );
} }
expect_stdout: true
} }
concat_8: { concat_8: {
@@ -213,4 +219,5 @@ concat_8: {
x += "foo" x += "foo"
); );
} }
expect_stdout: true
} }

View File

@@ -797,3 +797,139 @@ no_evaluate: {
} }
} }
} }
equality_conditionals_false: {
options = {
conditionals: false,
sequences: true,
}
input: {
function f(a, b, c) {
console.log(
a == (b ? a : a),
a == (b ? a : c),
a != (b ? a : a),
a != (b ? a : c),
a === (b ? a : a),
a === (b ? a : c),
a !== (b ? a : a),
a !== (b ? a : c)
);
}
f(0, 0, 0);
f(0, true, 0);
f(1, 2, 3);
f(1, null, 3);
f(NaN);
f(NaN, "foo");
}
expect: {
function f(a, b, c) {
console.log(
a == (b ? a : a),
a == (b ? a : c),
a != (b ? a : a),
a != (b ? a : c),
a === (b ? a : a),
a === (b ? a : c),
a !== (b ? a : a),
a !== (b ? a : c)
);
}
f(0, 0, 0),
f(0, true, 0),
f(1, 2, 3),
f(1, null, 3),
f(NaN),
f(NaN, "foo");
}
expect_stdout: true
}
equality_conditionals_true: {
options = {
conditionals: true,
sequences: true,
}
input: {
function f(a, b, c) {
console.log(
a == (b ? a : a),
a == (b ? a : c),
a != (b ? a : a),
a != (b ? a : c),
a === (b ? a : a),
a === (b ? a : c),
a !== (b ? a : a),
a !== (b ? a : c)
);
}
f(0, 0, 0);
f(0, true, 0);
f(1, 2, 3);
f(1, null, 3);
f(NaN);
f(NaN, "foo");
}
expect: {
function f(a, b, c) {
console.log(
(b, a == a),
a == (b ? a : c),
(b, a != a),
a != (b ? a : c),
(b, a === a),
a === (b ? a : c),
(b, a !== a),
a !== (b ? a : c)
);
}
f(0, 0, 0),
f(0, true, 0),
f(1, 2, 3),
f(1, null, 3),
f(NaN),
f(NaN, "foo");
}
expect_stdout: true
}
issue_1645_1: {
options = {
conditionals: true,
}
input: {
var a = 100, b = 10;
(b = a) ? a++ + (b += a) ? b += a : b += a : b ^= a;
console.log(a, b);
}
expect: {
var a = 100, b = 10;
(b = a) ? (a++ + (b += a), b += a) : b ^= a;
console.log(a,b);
}
expect_stdout: true
}
issue_1645_2: {
options = {
conditionals: true,
}
input: {
var a = 0;
function f() {
return a++;
}
f() ? a += 2 : a += 4;
console.log(a);
}
expect: {
var a = 0;
function f(){
return a++;
}
f() ? a += 2 : a += 4;
console.log(a);
}
expect_stdout: true
}

View File

@@ -162,4 +162,5 @@ regexp_literal_not_const: {
while (result = REGEXP_LITERAL.exec("acdabcdeabbb")) console.log(result[0]); while (result = REGEXP_LITERAL.exec("acdabcdeabbb")) console.log(result[0]);
})(); })();
} }
expect_stdout: true
} }

View File

@@ -87,6 +87,7 @@ dead_code_constant_boolean_should_warn_more: {
var x = 10, y; var x = 10, y;
var moo; var moo;
} }
expect_stdout: true
} }
dead_code_const_declaration: { dead_code_const_declaration: {
@@ -113,6 +114,7 @@ dead_code_const_declaration: {
var moo; var moo;
function bar() {} function bar() {}
} }
expect_stdout: true
} }
dead_code_const_annotation: { dead_code_const_annotation: {
@@ -140,6 +142,7 @@ dead_code_const_annotation: {
var moo; var moo;
function bar() {} function bar() {}
} }
expect_stdout: true
} }
dead_code_const_annotation_regex: { dead_code_const_annotation_regex: {
@@ -163,6 +166,7 @@ dead_code_const_annotation_regex: {
var CONST_FOO_ANN = !1; var CONST_FOO_ANN = !1;
CONST_FOO_ANN && console.log('reachable'); CONST_FOO_ANN && console.log('reachable');
} }
expect_stdout: true
} }
dead_code_const_annotation_complex_scope: { dead_code_const_annotation_complex_scope: {
@@ -208,4 +212,5 @@ dead_code_const_annotation_complex_scope: {
var meat = 'beef'; var meat = 'beef';
var pork = 'bad'; var pork = 'bad';
} }
expect_stdout: true
} }

View File

@@ -791,3 +791,17 @@ issue_1583: {
} }
} }
} }
issue_1656: {
options = {
toplevel: true,
unused: true,
}
beautify = {
beautify: true,
}
input: {
for(var a=0;;);
}
expect_exact: "for (;;) ;"
}

View File

@@ -200,6 +200,7 @@ negative_zero: {
1 / (-0) 1 / (-0)
); );
} }
expect_stdout: true
} }
positive_zero: { positive_zero: {
@@ -220,6 +221,7 @@ positive_zero: {
1 / (0) 1 / (0)
); );
} }
expect_stdout: true
} }
unsafe_constant: { unsafe_constant: {
@@ -243,6 +245,7 @@ unsafe_constant: {
(void 0).a (void 0).a
); );
} }
expect_stdout: true
} }
unsafe_object: { unsafe_object: {
@@ -266,6 +269,7 @@ unsafe_object: {
1..b + 1 1..b + 1
); );
} }
expect_stdout: true
} }
unsafe_object_nested: { unsafe_object_nested: {
@@ -289,6 +293,7 @@ unsafe_object_nested: {
2 2
); );
} }
expect_stdout: true
} }
unsafe_object_complex: { unsafe_object_complex: {
@@ -312,6 +317,7 @@ unsafe_object_complex: {
2 2
); );
} }
expect_stdout: true
} }
unsafe_object_repeated: { unsafe_object_repeated: {
@@ -335,6 +341,7 @@ unsafe_object_repeated: {
1..b + 1 1..b + 1
); );
} }
expect_stdout: true
} }
unsafe_object_accessor: { unsafe_object_accessor: {
@@ -384,6 +391,7 @@ unsafe_function: {
({a:{b:1},b:function(){}}).a.b + 1 ({a:{b:1},b:function(){}}).a.b + 1
); );
} }
expect_stdout: true
} }
unsafe_integer_key: { unsafe_integer_key: {
@@ -411,6 +419,7 @@ unsafe_integer_key: {
1["1"] + 1 1["1"] + 1
); );
} }
expect_stdout: true
} }
unsafe_integer_key_complex: { unsafe_integer_key_complex: {
@@ -438,6 +447,7 @@ unsafe_integer_key_complex: {
2 2
); );
} }
expect_stdout: true
} }
unsafe_float_key: { unsafe_float_key: {
@@ -465,6 +475,7 @@ unsafe_float_key: {
1["3.14"] + 1 1["3.14"] + 1
); );
} }
expect_stdout: true
} }
unsafe_float_key_complex: { unsafe_float_key_complex: {
@@ -492,6 +503,7 @@ unsafe_float_key_complex: {
2 2
); );
} }
expect_stdout: true
} }
unsafe_array: { unsafe_array: {
@@ -527,6 +539,7 @@ unsafe_array: {
(void 0)[1] + 1 (void 0)[1] + 1
); );
} }
expect_stdout: true
} }
unsafe_string: { unsafe_string: {
@@ -554,6 +567,7 @@ unsafe_string: {
"11" "11"
); );
} }
expect_stdout: true
} }
unsafe_array_bad_index: { unsafe_array_bad_index: {
@@ -575,6 +589,7 @@ unsafe_array_bad_index: {
[1, 2, 3, 4][3.14] + 1 [1, 2, 3, 4][3.14] + 1
); );
} }
expect_stdout: true
} }
unsafe_string_bad_index: { unsafe_string_bad_index: {
@@ -596,6 +611,7 @@ unsafe_string_bad_index: {
"1234"[3.14] + 1 "1234"[3.14] + 1
); );
} }
expect_stdout: true
} }
unsafe_prototype_function: { unsafe_prototype_function: {
@@ -642,6 +658,7 @@ call_args: {
console.log(1); console.log(1);
+(1, 1); +(1, 1);
} }
expect_stdout: true
} }
call_args_drop_param: { call_args_drop_param: {
@@ -663,6 +680,7 @@ call_args_drop_param: {
console.log(1); console.log(1);
+(b, 1); +(b, 1);
} }
expect_stdout: true
} }
in_boolean_context: { in_boolean_context: {
@@ -700,4 +718,87 @@ in_boolean_context: {
(foo(), !1) (foo(), !1)
); );
} }
expect_stdout: true
}
unsafe_charAt: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
"1234" + 1,
"1234".charAt(0) + 1,
"1234".charAt(6 - 5) + 1,
("12" + "34").charAt(0) + 1,
("12" + "34").charAt(6 - 5) + 1,
[1, 2, 3, 4].join("").charAt(0) + 1
);
}
expect: {
console.log(
"12341",
"11",
"21",
"11",
"21",
"11"
);
}
expect_stdout: true
}
unsafe_charAt_bad_index: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
"1234".charAt() + 1,
"1234".charAt("a") + 1,
"1234".charAt(3.14) + 1
);
}
expect: {
console.log(
"11",
"11",
"41"
);
}
expect_stdout: true
}
unsafe_charAt_noop: {
options = {
evaluate : true,
unsafe : true
}
input: {
console.log(
s.charAt(0),
"string".charAt(x)
);
}
expect: {
console.log(
s.charAt(0),
"string".charAt(x)
);
}
}
issue_1649: {
options = {
evaluate: true,
}
input: {
console.log(-1 + -1);
}
expect: {
console.log(-2);
}
expect_stdout: "-2";
} }

View File

@@ -39,6 +39,7 @@ iifes_returning_constants_keep_fargs_true: {
console.log(6); console.log(6);
console.log((a(), b(), 6)); console.log((a(), b(), 6));
} }
expect_stdout: true
} }
iifes_returning_constants_keep_fargs_false: { iifes_returning_constants_keep_fargs_false: {
@@ -73,6 +74,7 @@ iifes_returning_constants_keep_fargs_false: {
console.log(6); console.log(6);
console.log((a(), b(), 6)); console.log((a(), b(), 6));
} }
expect_stdout: true
} }
issue_485_crashing_1530: { issue_485_crashing_1530: {

View File

@@ -47,22 +47,6 @@ html_comment_in_greater_than_or_equal: {
expect_exact: "function f(a,b){return a-- >=b}"; expect_exact: "function f(a,b){return a-- >=b}";
} }
html_comment_in_right_shift_assign: {
input: {
// Note: illegal javascript
function f(a, b) { return a-- >>= b; }
}
expect_exact: "function f(a,b){return a-- >>=b}";
}
html_comment_in_zero_fill_right_shift_assign: {
input: {
// Note: illegal javascript
function f(a, b) { return a-- >>>= b; }
}
expect_exact: "function f(a,b){return a-- >>>=b}";
}
html_comment_in_string_literal: { html_comment_in_string_literal: {
input: { input: {
function f() { return "<!--HTML-->comment in<!--string literal-->"; } function f() { return "<!--HTML-->comment in<!--string literal-->"; }

View File

@@ -39,7 +39,7 @@ non_hoisted_function_after_return_2a: {
hoist_funs: false, dead_code: true, conditionals: true, comparisons: true, hoist_funs: false, dead_code: true, conditionals: true, comparisons: true,
evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true, evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true,
if_return: true, join_vars: true, cascade: true, side_effects: true, if_return: true, join_vars: true, cascade: true, side_effects: true,
collapse_vars: false, passes: 2 collapse_vars: false, passes: 2, warnings: "verbose"
} }
input: { input: {
function foo(x) { function foo(x) {
@@ -75,7 +75,7 @@ non_hoisted_function_after_return_2a: {
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:53,12]", "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:53,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:56,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:56,12]",
"WARN: Dropping unused variable b [test/compress/issue-1034.js:51,20]", "WARN: Dropping unused variable b [test/compress/issue-1034.js:51,20]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:53,16]" "WARN: Dropping unused variable c [test/compress/issue-1034.js:53,16]",
] ]
} }
@@ -114,8 +114,5 @@ non_hoisted_function_after_return_2b: {
"WARN: Dropping unreachable code [test/compress/issue-1034.js:97,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:97,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:97,12]", "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:97,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:101,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:101,12]",
"WARN: Dropping unused variable b [test/compress/issue-1034.js:95,20]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:97,16]"
] ]
} }

View File

@@ -49,4 +49,3 @@ mangle_keep_fnames_true: {
} }
} }
} }

View File

@@ -46,4 +46,5 @@ string_plus_optimization: {
} }
foo(); foo();
} }
expect_stdout: true
} }

View File

@@ -14,6 +14,7 @@ issue_1321_no_debug: {
x["a"] = 2 * x.b; x["a"] = 2 * x.b;
console.log(x.b, x["a"]); console.log(x.b, x["a"]);
} }
expect_stdout: true
} }
issue_1321_debug: { issue_1321_debug: {
@@ -33,6 +34,7 @@ issue_1321_debug: {
x["_$foo$_"] = 2 * x.a; x["_$foo$_"] = 2 * x.a;
console.log(x.a, x["_$foo$_"]); console.log(x.a, x["_$foo$_"]);
} }
expect_stdout: true
} }
issue_1321_with_quoted: { issue_1321_with_quoted: {
@@ -51,4 +53,5 @@ issue_1321_with_quoted: {
x["b"] = 2 * x.a; x["b"] = 2 * x.a;
console.log(x.a, x["b"]); console.log(x.a, x["b"]);
} }
expect_stdout: true
} }

View File

@@ -42,4 +42,5 @@ conditional_false_stray_else_in_loop: {
} }
} }
expect_exact: "for(var i=1;i<=4;++i)if(!(i<=2))console.log(i);" expect_exact: "for(var i=1;i<=4;++i)if(!(i<=2))console.log(i);"
expect_stdout: true
} }

View File

@@ -85,3 +85,15 @@ unsafe_undefined: {
} }
expect_stdout: true expect_stdout: true
} }
runtime_error: {
input: {
const a = 1;
console.log(a++);
}
expect: {
const a = 1;
console.log(a++);
}
expect_stdout: true
}

View File

@@ -0,0 +1,88 @@
issue_1639_1: {
options = {
booleans: true,
cascade: true,
conditionals: true,
evaluate: true,
join_vars: true,
loops: true,
sequences: true,
side_effects: true,
}
input: {
var a = 100, b = 10;
var L1 = 5;
while (--L1 > 0) {
if ((--b), false) {
if (b) {
var ignore = 0;
}
}
}
console.log(a, b);
}
expect: {
for (var a = 100, b = 10, L1 = 5; --L1 > 0;)
if (--b, !1) var ignore = 0;
console.log(a, b);
}
expect_stdout: true
}
issue_1639_2: {
options = {
booleans: true,
cascade: true,
conditionals: true,
evaluate: true,
join_vars: true,
sequences: true,
side_effects: true,
}
input: {
var a = 100, b = 10;
function f19() {
if (++a, false)
if (a)
if (++a);
}
f19();
console.log(a, b);
}
expect: {
var a = 100, b = 10;
function f19() {
++a, 1;
}
f19(),
console.log(a, b);
}
expect_stdout: true
}
issue_1639_3: {
options = {
booleans: true,
cascade: true,
conditionals: true,
evaluate: true,
sequences: true,
side_effects: true,
}
input: {
var a = 100, b = 10;
a++ && false && a ? 0 : 0;
console.log(a, b);
}
expect: {
var a = 100, b = 10;
a++,
console.log(a, b);
}
expect_stdout: true
}

View File

@@ -0,0 +1,45 @@
f7: {
options = {
booleans: true,
cascade: true,
collapse_vars: true,
comparisons: true,
conditionals: true,
dead_code: true,
drop_debugger: true,
evaluate: true,
hoist_funs: true,
if_return: true,
join_vars: true,
loops: true,
negate_iife: true,
passes: 3,
properties: true,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
beautify = {
beautify: true,
}
input: {
var a = 100, b = 10;
function f22464() {
var brake146670 = 5;
while (((b = a) ? !a : ~a ? null : b += a) && --brake146670 > 0) {
}
}
f22464();
console.log(a, b);
}
expect_exact: [
"var a = 100, b = 10;",
"",
"!function() {",
" for (;b = a, !1; ) ;",
"}(), console.log(a, b);",
]
expect_stdout: true
}

View File

@@ -48,6 +48,7 @@ dead_code_const_annotation_regex: {
var CONST_FOO_ANN = !1; var CONST_FOO_ANN = !1;
if (CONST_FOO_ANN) console.log('reachable'); if (CONST_FOO_ANN) console.log('reachable');
} }
expect_stdout: true
} }
drop_console_2: { drop_console_2: {
@@ -225,6 +226,7 @@ issue_1254_negate_iife_true: {
})()(); })()();
} }
expect_exact: '(function(){return function(){console.log("test")}})()();' expect_exact: '(function(){return function(){console.log("test")}})()();'
expect_stdout: true
} }
issue_1254_negate_iife_nested: { issue_1254_negate_iife_nested: {
@@ -240,6 +242,7 @@ issue_1254_negate_iife_nested: {
})()()()()(); })()()()()();
} }
expect_exact: '(function(){return function(){console.log("test")}})()()()()();' expect_exact: '(function(){return function(){console.log("test")}})()()()()();'
expect_stdout: true
} }
conditional: { conditional: {

View File

@@ -29,4 +29,5 @@ dont_mangle_arguments: {
})(5,6,7); })(5,6,7);
} }
expect_exact: "(function(){var arguments=arguments,o=9;console.log(o,arguments)})(5,6,7);" expect_exact: "(function(){var arguments=arguments,o=9;console.log(o,arguments)})(5,6,7);"
expect_stdout: true
} }

View File

@@ -42,6 +42,7 @@ eval_collapse_vars: {
eval("console.log(a);"); eval("console.log(a);");
})(eval); })(eval);
} }
expect_stdout: true
} }
eval_unused: { eval_unused: {

View File

@@ -9,6 +9,7 @@ labels_1: {
expect: { expect: {
foo || console.log("bar"); foo || console.log("bar");
} }
expect_stdout: true
} }
labels_2: { labels_2: {
@@ -40,6 +41,7 @@ labels_3: {
for (var i = 0; i < 5; ++i) for (var i = 0; i < 5; ++i)
i < 3 || console.log(i); i < 3 || console.log(i);
} }
expect_stdout: true
} }
labels_4: { labels_4: {
@@ -54,6 +56,7 @@ labels_4: {
for (var i = 0; i < 5; ++i) for (var i = 0; i < 5; ++i)
i < 3 || console.log(i); i < 3 || console.log(i);
} }
expect_stdout: true
} }
labels_5: { labels_5: {

View File

@@ -166,6 +166,7 @@ keep_collapse_const_in_own_block_scope: {
console.log(i); console.log(i);
console.log(c); console.log(c);
} }
expect_stdout: true
} }
keep_collapse_const_in_own_block_scope_2: { keep_collapse_const_in_own_block_scope_2: {
@@ -186,6 +187,7 @@ keep_collapse_const_in_own_block_scope_2: {
console.log(i); console.log(i);
console.log(c); console.log(c);
} }
expect_stdout: true
} }
evaluate: { evaluate: {
@@ -438,3 +440,21 @@ issue_186_beautify_bracketize_ie8: {
'}', '}',
] ]
} }
issue_1648: {
options = {
join_vars: true,
loops: true,
passes: 2,
sequences: true,
unused: true,
}
input: {
function f() {
x();
var b = 1;
while (1);
}
}
expect_exact: "function f(){for(x();1;);}"
}

View File

@@ -70,6 +70,7 @@ negate_iife_3_evaluate: {
expect: { expect: {
console.log(true); console.log(true);
} }
expect_stdout: true
} }
negate_iife_3_side_effects: { negate_iife_3_side_effects: {
@@ -111,6 +112,7 @@ negate_iife_3_off_evaluate: {
expect: { expect: {
console.log(true); console.log(true);
} }
expect_stdout: true
} }
negate_iife_4: { negate_iife_4: {
@@ -243,6 +245,7 @@ negate_iife_nested: {
}(7); }(7);
}).f(); }).f();
} }
expect_stdout: true
} }
negate_iife_nested_off: { negate_iife_nested_off: {
@@ -275,6 +278,7 @@ negate_iife_nested_off: {
})(7); })(7);
}).f(); }).f();
} }
expect_stdout: true
} }
negate_iife_issue_1073: { negate_iife_issue_1073: {
@@ -299,6 +303,7 @@ negate_iife_issue_1073: {
}; };
}(7))(); }(7))();
} }
expect_stdout: true
} }
issue_1254_negate_iife_false: { issue_1254_negate_iife_false: {
@@ -313,6 +318,7 @@ issue_1254_negate_iife_false: {
})()(); })()();
} }
expect_exact: '(function(){return function(){console.log("test")}})()();' expect_exact: '(function(){return function(){console.log("test")}})()();'
expect_stdout: true
} }
issue_1254_negate_iife_true: { issue_1254_negate_iife_true: {
@@ -327,6 +333,7 @@ issue_1254_negate_iife_true: {
})()(); })()();
} }
expect_exact: '!function(){return function(){console.log("test")}}()();' expect_exact: '!function(){return function(){console.log("test")}}()();'
expect_stdout: true
} }
issue_1254_negate_iife_nested: { issue_1254_negate_iife_nested: {
@@ -341,6 +348,7 @@ issue_1254_negate_iife_nested: {
})()()()()(); })()()()()();
} }
expect_exact: '!function(){return function(){console.log("test")}}()()()()();' expect_exact: '!function(){return function(){console.log("test")}}()()()()();'
expect_stdout: true
} }
issue_1288: { issue_1288: {

View File

@@ -58,6 +58,7 @@ reduce_vars: {
})(); })();
console.log(2); console.log(2);
} }
expect_stdout: true
} }
modified: { modified: {
@@ -166,7 +167,7 @@ modified: {
console.log(A ? 'yes' : 'no'); console.log(A ? 'yes' : 'no');
console.log(B ? 'yes' : 'no'); console.log(B ? 'yes' : 'no');
} }
} }
} }
unsafe_evaluate: { unsafe_evaluate: {
@@ -401,6 +402,7 @@ iife: {
console.log(0, 1 * b, 5); console.log(0, 1 * b, 5);
}(1, 2, 3); }(1, 2, 3);
} }
expect_stdout: true
} }
iife_new: { iife_new: {
@@ -420,6 +422,7 @@ iife_new: {
console.log(0, 1 * b, 5); console.log(0, 1 * b, 5);
}(1, 2, 3); }(1, 2, 3);
} }
expect_stdout: true
} }
multi_def: { multi_def: {
@@ -707,6 +710,7 @@ toplevel_on: {
expect: { expect: {
console.log(3); console.log(3);
} }
expect_stdout: true
} }
toplevel_off: { toplevel_off: {
@@ -724,6 +728,7 @@ toplevel_off: {
var x = 3; var x = 3;
console.log(x); console.log(x);
} }
expect_stdout: true
} }
toplevel_on_loops_1: { toplevel_on_loops_1: {
@@ -751,6 +756,7 @@ toplevel_on_loops_1: {
})(); })();
while (x); while (x);
} }
expect_stdout: true
} }
toplevel_off_loops_1: { toplevel_off_loops_1: {
@@ -779,6 +785,7 @@ toplevel_off_loops_1: {
bar(); bar();
while (x); while (x);
} }
expect_stdout: true
} }
toplevel_on_loops_2: { toplevel_on_loops_2: {
@@ -1121,6 +1128,7 @@ defun_label: {
}(2)); }(2));
}(); }();
} }
expect_stdout: true
} }
double_reference: { double_reference: {
@@ -1164,6 +1172,7 @@ iife_arguments_1: {
return f; return f;
}); });
} }
expect_stdout: true
} }
iife_arguments_2: { iife_arguments_2: {
@@ -1186,6 +1195,7 @@ iife_arguments_2: {
}() === arguments[0]); }() === arguments[0]);
})(); })();
} }
expect_stdout: true
} }
iife_eval_1: { iife_eval_1: {
@@ -1207,6 +1217,7 @@ iife_eval_1: {
return f; return f;
}); });
} }
expect_stdout: true
} }
iife_eval_2: { iife_eval_2: {
@@ -1230,6 +1241,7 @@ iife_eval_2: {
console.log(x() === eval("x")); console.log(x() === eval("x"));
})(); })();
} }
expect_stdout: true
} }
iife_func_side_effects: { iife_func_side_effects: {
@@ -1326,6 +1338,7 @@ issue_1595_4: {
if (a) iife(a - 1, b, c); if (a) iife(a - 1, b, c);
})(3, 4, 5); })(3, 4, 5);
} }
expect_stdout: true
} }
issue_1606: { issue_1606: {

View File

@@ -1,20 +1,29 @@
do_screw: { do_screw: {
options = { screw_ie8: true }; options = {
screw_ie8: true,
}
beautify = { beautify = {
screw_ie8: true, screw_ie8: true,
ascii_only: true ascii_only: true,
}; }
input: {
input: f("\v"); f("\v");
expect_exact: 'f("\\v");'; }
expect_exact: 'f("\\v");'
} }
dont_screw: { dont_screw: {
options = { screw_ie8: false }; options = {
beautify = { screw_ie8: false, ascii_only: true }; screw_ie8: false,
}
input: f("\v"); beautify = {
expect_exact: 'f("\\x0B");'; screw_ie8: false,
ascii_only: true,
}
input: {
f("\v");
}
expect_exact: 'f("\\x0B");'
} }
do_screw_constants: { do_screw_constants: {
@@ -119,6 +128,7 @@ do_screw_try_catch_undefined: {
return void 0===o return void 0===o
} }
} }
expect_stdout: true
} }
dont_screw_try_catch_undefined: { dont_screw_try_catch_undefined: {
@@ -147,6 +157,7 @@ dont_screw_try_catch_undefined: {
return n === undefined return n === undefined
} }
} }
expect_stdout: true
} }
reduce_vars: { reduce_vars: {
@@ -199,6 +210,7 @@ issue_1586_1: {
} }
} }
expect_exact: "function f(){try{}catch(c){console.log(c.message)}}" expect_exact: "function f(){try{}catch(c){console.log(c.message)}}"
expect_stdout: true
} }
issue_1586_2: { issue_1586_2: {
@@ -217,4 +229,5 @@ issue_1586_2: {
} }
} }
expect_exact: "function f(){try{}catch(c){console.log(c.message)}}" expect_exact: "function f(){try{}catch(c){console.log(c.message)}}"
expect_stdout: true
} }

View File

@@ -86,6 +86,7 @@ make_sequences_4: {
switch (x = 5, y) {} switch (x = 5, y) {}
with (x = 5, obj); with (x = 5, obj);
} }
expect_stdout: true
} }
lift_sequences_1: { lift_sequences_1: {
@@ -103,15 +104,18 @@ lift_sequences_1: {
lift_sequences_2: { lift_sequences_2: {
options = { sequences: true, evaluate: true }; options = { sequences: true, evaluate: true };
input: { input: {
var foo, bar; var foo = 1, bar;
foo.x = (foo = {}, 10); foo.x = (foo = {}, 10);
bar = (bar = {}, 10); bar = (bar = {}, 10);
console.log(foo, bar);
} }
expect: { expect: {
var foo, bar; var foo = 1, bar;
foo.x = (foo = {}, 10), foo.x = (foo = {}, 10),
bar = {}, bar = 10; bar = {}, bar = 10,
console.log(foo, bar);
} }
expect_stdout: true
} }
lift_sequences_3: { lift_sequences_3: {
@@ -138,6 +142,23 @@ lift_sequences_4: {
} }
} }
lift_sequences_5: {
options = {
sequences: true,
}
input: {
var a = 2, b;
a *= (b, a = 4, 3);
console.log(a);
}
expect: {
var a = 2, b;
b, a *= (a = 4, 3),
console.log(a);
}
expect_stdout: "6"
}
for_sequences: { for_sequences: {
options = { sequences: true }; options = { sequences: true };
input: { input: {
@@ -230,6 +251,7 @@ negate_iife_for: {
for (!function() {}(), i = 0; i < 5; i++) console.log(i); for (!function() {}(), i = 0; i < 5; i++) console.log(i);
for (function() {}(); i < 5; i++) console.log(i); for (function() {}(); i < 5; i++) console.log(i);
} }
expect_stdout: true
} }
iife: { iife: {

View File

@@ -13,6 +13,7 @@ booleans_evaluate: {
console.log(!0, !0); console.log(!0, !0);
console.log(!1, !1); console.log(!1, !1);
} }
expect_stdout: true
} }
booleans_global_defs: { booleans_global_defs: {

View File

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

View File

@@ -0,0 +1 @@
console.log(2 || (Math.random() /= 2));

View File

@@ -0,0 +1 @@
console.log(3 || ++this);

View File

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

View File

@@ -251,4 +251,59 @@ describe("bin/uglifyjs", function () {
done(); done();
}); });
}); });
it("Should support hyphen as shorthand", function(done) {
var command = uglifyjscmd + ' test/input/issue-1431/sample.js -m keep-fnames=true';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(f(g)()==5);\n");
done();
});
});
it("Should throw syntax error (5--)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_1.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/assign_1.js:1,18",
"console.log(1 || 5--);",
" ^",
"SyntaxError: Invalid use of -- operator"
].join("\n"));
done();
});
});
it("Should throw syntax error (Math.random() /= 2)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_2.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/assign_2.js:1,32",
"console.log(2 || (Math.random() /= 2));",
" ^",
"SyntaxError: Invalid assignment"
].join("\n"));
done();
});
});
it("Should throw syntax error (++this)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_3.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/assign_3.js:1,18",
"console.log(3 || ++this);",
" ^",
"SyntaxError: Invalid use of ++ operator"
].join("\n"));
done();
});
});
}); });

View File

@@ -1,5 +1,6 @@
var Uglify = require('../../'); var Uglify = require('../../');
var assert = require("assert"); var assert = require("assert");
var path = require("path");
describe("minify() with input file globs", function() { describe("minify() with input file globs", function() {
it("minify() with one input file glob string.", function() { it("minify() with one input file glob string.", function() {
@@ -19,6 +20,39 @@ describe("minify() with input file globs", function() {
], { ], {
compress: { toplevel: true } compress: { toplevel: true }
}); });
assert.strictEqual(result.code, 'var print=console.log.bind(console);print("qux",function(n){return 3*n}(3),function(n){return n/2}(12)),function(n){print("Foo:",2*n)}(11);'); assert.strictEqual(result.code, '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);');
});
it("should throw with non-matching glob string", function() {
var glob = "test/input/issue-1242/blah.*";
assert.strictEqual(Uglify.simple_glob(glob).length, 1);
assert.strictEqual(Uglify.simple_glob(glob)[0], glob);
assert.throws(function() {
Uglify.minify(glob);
}, "should throw file not found");
});
it('"?" in glob string should not match "/"', function() {
var glob = "test/input?issue-1242/foo.*";
assert.strictEqual(Uglify.simple_glob(glob).length, 1);
assert.strictEqual(Uglify.simple_glob(glob)[0], glob);
assert.throws(function() {
Uglify.minify(glob);
}, "should throw file not found");
});
it("should handle special characters in glob string", function() {
var result = Uglify.minify("test/input/issue-1632/^{*}[???](*)+$.??");
assert.strictEqual(result.code, "console.log(x);");
});
it("should handle array of glob strings - matching and otherwise", function() {
var dir = "test/input/issue-1242";
var matches = Uglify.simple_glob([
path.join(dir, "b*.es5"),
path.join(dir, "z*.es5"),
path.join(dir, "*.js"),
]);
assert.strictEqual(matches.length, 4);
assert.strictEqual(matches[0], path.join(dir, "bar.es5"));
assert.strictEqual(matches[1], path.join(dir, "baz.es5"));
assert.strictEqual(matches[2], path.join(dir, "z*.es5"));
assert.strictEqual(matches[3], path.join(dir, "qux.js"));
}); });
}); });

View File

@@ -5,7 +5,7 @@ var UglifyJS = require(".."),
escodegen = require("escodegen"), escodegen = require("escodegen"),
esfuzz = require("esfuzz"), esfuzz = require("esfuzz"),
estraverse = require("estraverse"), estraverse = require("estraverse"),
prefix = Array(20).join("\b") + " "; prefix = "\r ";
// Normalizes input AST for UglifyJS in order to get correct comparison. // Normalizes input AST for UglifyJS in order to get correct comparison.
@@ -62,7 +62,7 @@ module.exports = function(options) {
var ast1 = normalizeInput(esfuzz.generate({ var ast1 = normalizeInput(esfuzz.generate({
maxDepth: options.maxDepth maxDepth: options.maxDepth
})); }));
var ast2 = var ast2 =
UglifyJS UglifyJS
.AST_Node .AST_Node

View File

@@ -11,6 +11,17 @@ var vm = require("vm");
var tests_dir = path.dirname(module.filename); var tests_dir = path.dirname(module.filename);
var failures = 0; var failures = 0;
var failed_files = {}; var failed_files = {};
var same_stdout = ~process.version.lastIndexOf("v0.12.", 0) ? function(expected, actual) {
if (typeof expected != typeof actual) return false;
if (typeof expected != "string") {
if (expected.name != actual.name) return false;
expected = expected.message.slice(expected.message.lastIndexOf("\n") + 1);
actual = actual.message.slice(actual.message.lastIndexOf("\n") + 1);
}
return expected == actual;
} : function(expected, actual) {
return typeof expected == typeof actual && expected.toString() == actual.toString();
};
run_compress_tests(); run_compress_tests();
if (failures) { if (failures) {
@@ -72,10 +83,15 @@ function test_directory(dir) {
} }
function as_toplevel(input, mangle_options) { function as_toplevel(input, mangle_options) {
if (input instanceof U.AST_BlockStatement) input = input.body; if (!(input instanceof U.AST_BlockStatement))
else if (input instanceof U.AST_Statement) input = [ input ]; throw new Error("Unsupported input syntax");
else throw new Error("Unsupported input syntax"); for (var i = 0; i < input.body.length; i++) {
var toplevel = new U.AST_Toplevel({ body: input }); var stat = input.body[i];
if (stat instanceof U.AST_SimpleStatement && stat.body instanceof U.AST_String)
input.body[i] = new U.AST_Directive(stat.body);
else break;
}
var toplevel = new U.AST_Toplevel(input);
toplevel.figure_out_scope(mangle_options); toplevel.figure_out_scope(mangle_options);
return toplevel; return toplevel;
} }
@@ -89,6 +105,23 @@ function run_compress_tests() {
function test_case(test) { function test_case(test) {
log_test(test.name); log_test(test.name);
U.base54.reset(); U.base54.reset();
var output_options = test.beautify || {};
var expect;
if (test.expect) {
expect = make_code(as_toplevel(test.expect, test.mangle), output_options);
} else {
expect = test.expect_exact;
}
var input = as_toplevel(test.input, test.mangle);
var input_code = make_code(input, output_options);
var input_formatted = make_code(test.input, {
beautify: true,
quote_style: 3,
keep_quoted_props: true
});
if (test.mangle_props) {
input = U.mangle_properties(input, test.mangle_props);
}
var options = U.defaults(test.options, { var options = U.defaults(test.options, {
warnings: false warnings: false
}); });
@@ -98,25 +131,9 @@ function run_compress_tests() {
U.AST_Node.warn_function = function(text) { U.AST_Node.warn_function = function(text) {
warnings_emitted.push("WARN: " + text); warnings_emitted.push("WARN: " + text);
}; };
options.warnings = true; if (!options.warnings) options.warnings = true;
} }
var cmp = new U.Compressor(options, true); var cmp = new U.Compressor(options, true);
var output_options = test.beautify || {};
var expect;
if (test.expect) {
expect = make_code(as_toplevel(test.expect, test.mangle), output_options);
} else {
expect = test.expect_exact;
}
var input = as_toplevel(test.input, test.mangle);
var input_code = make_code(test.input, {
beautify: true,
quote_style: 3,
keep_quoted_props: true
});
if (test.mangle_props) {
input = U.mangle_properties(input, test.mangle_props);
}
var output = cmp.compress(input); var output = cmp.compress(input);
output.figure_out_scope(test.mangle); output.figure_out_scope(test.mangle);
if (test.mangle) { if (test.mangle) {
@@ -126,7 +143,7 @@ function run_compress_tests() {
output = make_code(output, output_options); output = make_code(output, output_options);
if (expect != output) { if (expect != output) {
log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", { log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", {
input: input_code, input: input_formatted,
output: output, output: output,
expected: expect expected: expect
}); });
@@ -139,7 +156,7 @@ function run_compress_tests() {
var reparsed_ast = U.parse(output); var reparsed_ast = U.parse(output);
} catch (ex) { } catch (ex) {
log("!!! Test matched expected result but cannot parse output\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n--REPARSE ERROR--\n{error}\n\n", { log("!!! Test matched expected result but cannot parse output\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n--REPARSE ERROR--\n{error}\n\n", {
input: input_code, input: input_formatted,
output: output, output: output,
error: ex.toString(), error: ex.toString(),
}); });
@@ -158,7 +175,7 @@ function run_compress_tests() {
var actual_warnings = JSON.stringify(warnings_emitted); var actual_warnings = JSON.stringify(warnings_emitted);
if (expected_warnings != actual_warnings) { if (expected_warnings != actual_warnings) {
log("!!! failed\n---INPUT---\n{input}\n---EXPECTED WARNINGS---\n{expected_warnings}\n---ACTUAL WARNINGS---\n{actual_warnings}\n\n", { log("!!! failed\n---INPUT---\n{input}\n---EXPECTED WARNINGS---\n{expected_warnings}\n---ACTUAL WARNINGS---\n{actual_warnings}\n\n", {
input: input_code, input: input_formatted,
expected_warnings: expected_warnings, expected_warnings: expected_warnings,
actual_warnings: actual_warnings, actual_warnings: actual_warnings,
}); });
@@ -167,48 +184,33 @@ function run_compress_tests() {
} }
} }
if (test.expect_stdout) { if (test.expect_stdout) {
try { var stdout = run_code(input_code);
var stdout = run_code(input_code); if (test.expect_stdout === true) {
if (test.expect_stdout === true) { test.expect_stdout = stdout;
test.expect_stdout = stdout; }
} if (!same_stdout(test.expect_stdout, stdout)) {
if (test.expect_stdout != stdout) { log("!!! Invalid input or expected stdout\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
log("!!! Invalid input or expected stdout\n---INPUT---\n{input}\n---EXPECTED STDOUT---\n{expected}\n---ACTUAL STDOUT---\n{actual}\n\n", { input: input_formatted,
input: input_code, expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
expected: test.expect_stdout,
actual_type: typeof stdout == "string" ? "STDOUT" : "ERROR",
actual: stdout,
});
failures++;
failed_files[file] = 1;
} else {
stdout = run_code(output);
if (!same_stdout(test.expect_stdout, stdout)) {
log("!!! failed\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
input: input_formatted,
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
expected: test.expect_stdout, expected: test.expect_stdout,
actual_type: typeof stdout == "string" ? "STDOUT" : "ERROR",
actual: stdout, actual: stdout,
}); });
failures++; failures++;
failed_files[file] = 1; failed_files[file] = 1;
} else {
try {
stdout = run_code(output);
if (test.expect_stdout != stdout) {
log("!!! failed\n---INPUT---\n{input}\n---EXPECTED STDOUT---\n{expected}\n---ACTUAL STDOUT---\n{actual}\n\n", {
input: input_code,
expected: test.expect_stdout,
actual: stdout,
});
failures++;
failed_files[file] = 1;
}
} catch (ex) {
log("!!! Execution of output failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n--ERROR--\n{error}\n\n", {
input: input_code,
output: output,
error: ex.toString(),
});
failures++;
failed_files[file] = 1;
}
} }
} catch (ex) {
log("!!! Execution of input failed\n---INPUT---\n{input}\n--ERROR--\n{error}\n\n", {
input: input_code,
error: ex.toString(),
});
failures++;
failed_files[file] = 1;
} }
} }
} }
@@ -299,10 +301,6 @@ function parse_test(file) {
}) })
); );
var stat = node.body; var stat = node.body;
if (stat instanceof U.AST_BlockStatement) {
if (stat.body.length == 1) stat = stat.body[0];
else if (stat.body.length == 0) stat = new U.AST_EmptyStatement();
}
if (label.name == "expect_exact") { if (label.name == "expect_exact") {
test[label.name] = read_string(stat); test[label.name] = read_string(stat);
} else if (label.name == "expect_stdout") { } else if (label.name == "expect_stdout") {
@@ -344,6 +342,8 @@ function run_code(code) {
try { try {
new vm.Script(code).runInNewContext({ console: console }, { timeout: 5000 }); new vm.Script(code).runInNewContext({ console: console }, { timeout: 5000 });
return stdout; return stdout;
} catch (ex) {
return ex;
} finally { } finally {
process.stdout.write = original_write; process.stdout.write = original_write;
} }

225
test/ufuzz.js Normal file
View File

@@ -0,0 +1,225 @@
// ufuzz.js
// derived from https://github.com/qfox/uglyfuzzer by Peter van der Zee
"use strict";
// workaround for tty output truncation upon process.exit()
[process.stdout, process.stderr].forEach(function(stream){
if (stream._handle && stream._handle.setBlocking)
stream._handle.setBlocking(true);
});
var vm = require("vm");
var minify = require("..").minify;
function run_code(code) {
var stdout = "";
var original_write = process.stdout.write;
process.stdout.write = function(chunk) {
stdout += chunk;
};
try {
new vm.Script(code).runInNewContext({ console: console }, { timeout: 5000 });
return stdout;
} catch (ex) {
return ex;
} finally {
process.stdout.write = original_write;
}
}
function rng(max) {
return Math.floor(max * Math.random());
}
function createFunctionDecls(n, recurmax) {
if (--recurmax < 0) { return ';'; }
var s = '';
while (--n > 0) {
s += createFunctionDecl(recurmax) + '\n';
}
return s;
}
var funcs = 0;
function createFunctionDecl(recurmax) {
if (--recurmax < 0) { return ';'; }
var func = funcs++;
return 'function f' + func + '(){' + createStatements(3, recurmax) + '}\nf' + func + '();';
}
function createStatements(n, recurmax) {
if (--recurmax < 0) { return ';'; }
var s = '';
while (--n > 0) {
s += createStatement(recurmax);
}
return s;
}
var loops = 0;
function createStatement(recurmax) {
var loop = ++loops;
if (--recurmax < 0) { return ';'; }
switch (rng(7)) {
case 0:
return '{' + createStatement(recurmax) + '}';
case 1:
return 'if (' + createExpression(recurmax) + ')' + createStatement(recurmax);
case 2:
return '{var brake' + loop + ' = 5; do {' + createStatement(recurmax) + '} while ((' + createExpression(recurmax) + ') && --brake' + loop + ' > 0);}';
case 3:
return '{var brake' + loop + ' = 5; while ((' + createExpression(recurmax) + ') && --brake' + loop + ' > 0)' + createStatement(recurmax) + '}';
case 4:
return 'for (var brake' + loop + ' = 5; (' + createExpression(recurmax) + ') && brake' + loop + ' > 0; --brake' + loop + ')' + createStatement(recurmax);
case 5:
return ';';
case 6:
return createExpression() + ';';
}
}
function createExpression(recurmax) {
if (--recurmax < 0) { return '0'; }
switch (rng(8)) {
case 0:
return '(' + createUnaryOp() + 'a)';
case 1:
return '(a' + (Math.random() > 0.5 ? '++' : '--') + ')';
case 2:
return '(b ' + createAssignment() + ' a)';
case 3:
return '(' + Math.random() + ' > 0.5 ? a : b)';
case 4:
return createExpression(recurmax) + createBinaryOp() + createExpression(recurmax);
case 5:
return createValue();
case 6:
return '(' + createExpression(recurmax) + ')';
case 7:
return createExpression(recurmax) + '?(' + createExpression(recurmax) + '):(' + createExpression(recurmax) + ')';
}
}
function createValue() {
var values = [
'true',
'false',
'22',
'0',
'(-1)',
'NaN',
'undefined',
'null',
'"foo"',
'"bar"' ];
return values[rng(values.length)];
}
function createBinaryOp() {
switch (rng(6)) {
case 0:
return '+';
case 1:
return '-';
case 2:
return ',';
case 3:
return '&&';
case 4:
return '||';
case 5:
return '^';
}
}
function createAssignment() {
switch (rng(4)) {
case 0:
return '=';
case 1:
return '-=';
case 2:
return '^=';
case 3:
return '+=';
}
}
function createUnaryOp() {
switch (rng(4)) {
case 0:
return '--';
case 1:
return '++';
case 2:
return '~';
case 3:
return '!';
}
}
function log() {
console.log("//=============================================================");
console.log("// original code");
console.log("//");
console.log(original_code);
console.log();
console.log();
console.log("//-------------------------------------------------------------");
console.log("// original code (beautify'd)");
console.log("//");
console.log(beautify_code);
console.log();
console.log();
console.log("//-------------------------------------------------------------");
console.log("// uglified code");
console.log("//");
console.log(uglify_code);
console.log();
console.log();
console.log("original result:");
console.log(original_result);
console.log("beautified result:");
console.log(beautify_result);
console.log("uglified result:");
console.log(uglify_result);
}
var num_iterations = +process.argv[2] || 1/0;
var verbose = !!process.argv[3];
for (var round = 0; round < num_iterations; round++) {
process.stdout.write(round + " of " + num_iterations + "\r");
var original_code = [
"var a = 100, b = 10;",
createFunctionDecls(rng(3) + 1, 10),
"console.log(a, b);"
].join("\n");
var beautify_code = minify(original_code, {
fromString: true,
mangle: false,
compress: false,
output: {
beautify: true,
bracketize: true,
},
}).code;
var uglify_code = minify(beautify_code, {
fromString: true,
mangle: false,
compress: {
passes: 3,
},
output: {
beautify: true,
bracketize: true,
},
}).code;
var original_result = run_code(original_code);
var beautify_result = run_code(beautify_code);
var uglify_result = run_code(uglify_code);
var ok = original_result == beautify_result && original_result == uglify_result;
if (verbose || !ok) log();
if (!ok) process.exit(1);
}

View File

@@ -18,6 +18,6 @@ exports["tokenizer"] = tokenizer;
exports["is_identifier"] = is_identifier; exports["is_identifier"] = is_identifier;
exports["SymbolDef"] = SymbolDef; exports["SymbolDef"] = SymbolDef;
if (typeof DEBUG !== "undefined" && DEBUG) { if (global.UGLIFY_DEBUG) {
exports["EXPECT_DIRECTIVE"] = EXPECT_DIRECTIVE; exports["EXPECT_DIRECTIVE"] = EXPECT_DIRECTIVE;
} }

View File

@@ -7,7 +7,8 @@
var path = require("path"); var path = require("path");
var fs = require("fs"); var fs = require("fs");
var FILES = exports.FILES = [ var UglifyJS = exports;
var FILES = UglifyJS.FILES = [
"../lib/utils.js", "../lib/utils.js",
"../lib/ast.js", "../lib/ast.js",
"../lib/parse.js", "../lib/parse.js",
@@ -20,17 +21,14 @@ var FILES = exports.FILES = [
"../lib/propmangle.js", "../lib/propmangle.js",
"./exports.js", "./exports.js",
].map(function(file){ ].map(function(file){
return fs.realpathSync(path.join(path.dirname(__filename), file)); return require.resolve(file);
}); });
var UglifyJS = exports; new Function("MOZ_SourceMap", "exports", FILES.map(function(file){
new Function("MOZ_SourceMap", "exports", "DEBUG", FILES.map(function(file){
return fs.readFileSync(file, "utf8"); return fs.readFileSync(file, "utf8");
}).join("\n\n"))( }).join("\n\n"))(
require("source-map"), require("source-map"),
UglifyJS, UglifyJS
!!global.UGLIFY_DEBUG
); );
UglifyJS.AST_Node.warn_function = function(txt) { UglifyJS.AST_Node.warn_function = function(txt) {
@@ -46,7 +44,7 @@ function read_source_map(code) {
return JSON.parse(new Buffer(match[2], "base64")); return JSON.parse(new Buffer(match[2], "base64"));
} }
exports.minify = function(files, options) { UglifyJS.minify = function(files, options) {
options = UglifyJS.defaults(options, { options = UglifyJS.defaults(options, {
spidermonkey : false, spidermonkey : false,
outSourceMap : null, outSourceMap : null,
@@ -181,7 +179,7 @@ exports.minify = function(files, options) {
}; };
}; };
// exports.describe_ast = function() { // UglifyJS.describe_ast = function() {
// function doitem(ctor) { // function doitem(ctor) {
// var sub = {}; // var sub = {};
// ctor.SUBCLASSES.forEach(function(ctor){ // ctor.SUBCLASSES.forEach(function(ctor){
@@ -195,7 +193,7 @@ exports.minify = function(files, options) {
// return doitem(UglifyJS.AST_Node).sub; // return doitem(UglifyJS.AST_Node).sub;
// } // }
exports.describe_ast = function() { UglifyJS.describe_ast = function() {
var out = UglifyJS.OutputStream({ beautify: true }); var out = UglifyJS.OutputStream({ beautify: true });
function doitem(ctor) { function doitem(ctor) {
out.print("AST_" + ctor.TYPE); out.print("AST_" + ctor.TYPE);
@@ -249,13 +247,13 @@ function readReservedFile(filename, reserved) {
return reserved; return reserved;
} }
exports.readReservedFile = readReservedFile; UglifyJS.readReservedFile = readReservedFile;
exports.readDefaultReservedFile = function(reserved) { UglifyJS.readDefaultReservedFile = function(reserved) {
return readReservedFile(path.join(__dirname, "domprops.json"), reserved); return readReservedFile(require.resolve("./domprops.json"), reserved);
}; };
exports.readNameCache = function(filename, key) { UglifyJS.readNameCache = function(filename, key) {
var cache = null; var cache = null;
if (filename) { if (filename) {
try { try {
@@ -273,7 +271,7 @@ exports.readNameCache = function(filename, key) {
return cache; return cache;
}; };
exports.writeNameCache = function(filename, key, cache) { UglifyJS.writeNameCache = function(filename, key, cache) {
if (filename) { if (filename) {
var data; var data;
try { try {
@@ -294,13 +292,9 @@ exports.writeNameCache = function(filename, key, cache) {
// Example: "foo/bar/*baz??.*.js" // Example: "foo/bar/*baz??.*.js"
// Argument `glob` may be a string or an array of strings. // Argument `glob` may be a string or an array of strings.
// Returns an array of strings. Garbage in, garbage out. // Returns an array of strings. Garbage in, garbage out.
exports.simple_glob = function simple_glob(glob) { UglifyJS.simple_glob = function simple_glob(glob) {
var results = [];
if (Array.isArray(glob)) { if (Array.isArray(glob)) {
glob.forEach(function(elem) { return [].concat.apply([], glob.map(simple_glob));
results = results.concat(simple_glob(elem));
});
return results;
} }
if (glob.match(/\*|\?/)) { if (glob.match(/\*|\?/)) {
var dir = path.dirname(glob); var dir = path.dirname(glob);
@@ -308,28 +302,19 @@ exports.simple_glob = function simple_glob(glob) {
var entries = fs.readdirSync(dir); var entries = fs.readdirSync(dir);
} catch (ex) {} } catch (ex) {}
if (entries) { if (entries) {
var pattern = "^" + (path.basename(glob) var pattern = "^" + path.basename(glob)
.replace(/\(/g, "\\(") .replace(/[.+^$[\]\\(){}]/g, "\\$&")
.replace(/\)/g, "\\)")
.replace(/\{/g, "\\{")
.replace(/\}/g, "\\}")
.replace(/\[/g, "\\[")
.replace(/\]/g, "\\]")
.replace(/\+/g, "\\+")
.replace(/\^/g, "\\^")
.replace(/\$/g, "\\$")
.replace(/\*/g, "[^/\\\\]*") .replace(/\*/g, "[^/\\\\]*")
.replace(/\./g, "\\.") .replace(/\?/g, "[^/\\\\]") + "$";
.replace(/\?/g, ".")) + "$";
var mod = process.platform === "win32" ? "i" : ""; var mod = process.platform === "win32" ? "i" : "";
var rx = new RegExp(pattern, mod); var rx = new RegExp(pattern, mod);
for (var i in entries) { var results = entries.filter(function(name) {
if (rx.test(entries[i])) return rx.test(name);
results.push(dir + "/" + entries[i]); }).map(function(name) {
} return path.join(dir, name);
});
if (results.length) return results;
} }
} }
if (results.length === 0) return [ glob ];
results = [ glob ];
return results;
}; };