Compare commits

..

29 Commits

Author SHA1 Message Date
Alex Lam S.L
7906033e82 Merge pull request #1624 from alexlamsl/harmony-v2.8.14
Merging from master for 2.8.14
2017-03-19 18:24:29 +08:00
alexlamsl
4bf21ce5c1 add expect_stdout to tests 2017-03-19 15:35:39 +08:00
alexlamsl
44d6b47bdc Merge branch 'master' into harmony-v2.8.14 2017-03-19 15:31:18 +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
129e449c8e Merge pull request #1614 from alexlamsl/harmony-v2.8.13
Merging from master for 2.8.13
2017-03-18 13:02:09 +08:00
alexlamsl
75c3c8963f Merge branch 'master' into harmony-v2.8.13 2017-03-18 02:52:45 +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
Alex Zaworski
d26b7522d9 Allow 'name' as object literal shorthand property (#1617)
fixes #1613
2017-03-18 01:29:13 +08:00
Christian Maughan Tegnér
b7c112eefe Add --in-source-map inline documentation (#1611) 2017-03-17 03:08:38 +08:00
Alex Lam S.L
b2b8a0d386 v2.8.13 2017-03-17 02:01:33 +08:00
Alex Lam S.L
ac40301813 fix chained evaluation (#1610)
`reduce_vars` enables substitution of variables but did not clone the value's `AST_Node`.

This confuses `collapse_vars` and result in invalid AST and subsequent crash.

fixes #1609
2017-03-17 00:26:48 +08:00
Alex Lam S.L
3563d8c09e extend test/run-tests.js to optionally execute uglified output (#1604)
fixes #1588
2017-03-16 23:20:06 +08:00
Alex Lam S.L
5ae04b3545 make collapse_vars consistent with toplevel (#1608)
fixes #1605
2017-03-16 13:22:26 +08:00
Alex Lam S.L
a80b228d8b fix hoist_vars on reduce_vars (#1607)
`hoist_vars` converts variable declarations into plain assignments, which then confuses `reduce_vars`

fixes #1606
2017-03-16 12:03:30 +08:00
Alex Lam S.L
cf4bf4ceb1 fix stack issues with AST_Node.evaluate() (#1603)
As patched in #1597, `make_node_from_constant()` makes inconsistent and sometimes incorrect calls to `optimize()` and `transform()`.

Fix those issues properly by changing the semantics of `evaluate()` and `make_node_from_constant()`, with the side effect that `evaluate()` no longer eagerly converts constant to `AST_Node`.
2017-03-16 01:02:59 +08:00
Alex Lam S.L
8223b2e0db fix AST_Node.optimize() (#1602)
Liberal use of `Compressor.transform()` and `AST_Node.optimize()` presents an issue for look-up operations like `TreeWalker.in_boolean_context()` and `TreeWalker.parent()`.

This is an incremental fix such that `AST_Node.optimize()` would now contain the correct stack information when called correctly.
2017-03-15 18:44:13 +08:00
Alex Lam S.L
2fd86d3cb0 Merge pull request #1601 from alexlamsl/harmony-v2.8.12
Merging from master for 2.8.12
2017-03-14 14:29:32 +08:00
Alex Lam S.L
381bd3836e minor clean-ups (#1600)
- remove obsolete optimisation in `AST_Binary` after #1477
- improve `TreeWalker.has_directive()` readability and resilience against multiple visits
2017-03-14 13:19:05 +08:00
alexlamsl
8f7ab602e2 Merge branch 'master' into harmony-v2.8.12 2017-03-14 13:17:42 +08:00
Alex Lam S.L
1dd339f95e fix unused crashes (#1599)
- `AST_DefaultAssign` on `keep_fargs`
- `AST_Expansion on` `keep_fargs`
- `AST_Destructuring` on top-level declarations without `toplevel`
2017-03-14 13:13:43 +08:00
Alex Lam S.L
919d5e3482 v2.8.12 2017-03-11 05:00:55 +08:00
Alex Lam S.L
e3a3db73ae temporary fix for boolean bug (#1597)
fixes #1592
2017-03-11 04:59:55 +08:00
Alex Lam S.L
d9344f30b8 disallow parameter substitution for named IIFEs (#1596)
Self-referenced function has non-fixed values assigned to its parameters.

Let `unused` & `!keep_fnames` do the scanning, then apply `reduce_vars` only to unnamed functions.

fixes #1595
2017-03-11 03:34:55 +08:00
Alex Lam S.L
be80f7e706 support multi-line string in tests (#1590)
`expect_exact` sometimes have multiple lines and `\n` are hard to read.

Use array of strings to emulate line breaks and improve readability.
2017-03-10 11:27:30 +08:00
40 changed files with 1256 additions and 349 deletions

View File

@@ -68,7 +68,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 +201,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

@@ -814,9 +814,6 @@ var AST_VarDef = DEFNODE("VarDef", "name value", {
name: "[AST_SymbolVar|AST_SymbolConst|AST_Destructuring] name of the variable", name: "[AST_SymbolVar|AST_SymbolConst|AST_Destructuring] name of the variable",
value: "[AST_Node?] initializer, or null of there's no initializer" value: "[AST_Node?] initializer, or null of there's no initializer"
}, },
is_destructuring: function() {
return this.name instanceof AST_Destructuring;
},
_walk: function(visitor) { _walk: function(visitor) {
return visitor._visit(this, function(){ return visitor._visit(this, function(){
this.name._walk(visitor); this.name._walk(visitor);
@@ -1323,11 +1320,13 @@ TreeWalker.prototype = {
push: function (node) { push: function (node) {
if (node instanceof AST_Lambda) { if (node instanceof AST_Lambda) {
this.directives = Object.create(this.directives); this.directives = Object.create(this.directives);
} else if (node instanceof AST_Directive) { } else if (node instanceof AST_Directive && !this.directives[node.value]) {
this.directives[node.value] = this.directives[node.value] ? "up" : true; this.directives[node.value] = node;
} else if (node instanceof AST_Class) { } else if (node instanceof AST_Class) {
this.directives = Object.create(this.directives); this.directives = Object.create(this.directives);
this.directives["use strict"] = this.directives["use strict"] ? "up" : true; if (!this.directives["use strict"]) {
this.directives["use strict"] = node;
}
} }
this.stack.push(node); this.stack.push(node);
}, },
@@ -1355,7 +1354,7 @@ TreeWalker.prototype = {
for (var i = 0; i < node.body.length; ++i) { for (var i = 0; i < node.body.length; ++i) {
var st = node.body[i]; var st = node.body[i];
if (!(st instanceof AST_Directive)) break; if (!(st instanceof AST_Directive)) break;
if (st.value == type) return true; if (st.value == type) return st;
} }
} }
}, },

View File

@@ -99,10 +99,10 @@ function Compressor(options, false_by_default) {
this.top_retain = function(def) { this.top_retain = function(def) {
return top_retain.test(def.name); return top_retain.test(def.name);
}; };
} else if (typeof top_retain === "function") { } else if (typeof top_retain == "function") {
this.top_retain = top_retain; this.top_retain = top_retain;
} else if (top_retain) { } else if (top_retain) {
if (typeof top_retain === "string") { if (typeof top_retain == "string") {
top_retain = top_retain.split(/,/); top_retain = top_retain.split(/,/);
} }
this.top_retain = function(def) { this.top_retain = function(def) {
@@ -152,14 +152,25 @@ merge(Compressor.prototype, {
node = node.hoist_declarations(this); node = node.hoist_declarations(this);
was_scope = true; was_scope = true;
} }
// Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
// would call AST_Node.transform() if a different instance of AST_Node is
// produced after OPT().
// This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
// Migrate and defer all children's AST_Node.transform() to below, which
// will now happen after this parent AST_Node has been properly substituted
// thus gives a consistent AST snapshot.
descend(node, this); descend(node, this);
node = node.optimize(this); // Existing code relies on how AST_Node.optimize() worked, and omitting the
if (was_scope && node instanceof AST_Scope) { // following replacement call would result in degraded efficiency of both
node.drop_unused(this); // output and performance.
descend(node, this); descend(node, this);
var opt = node.optimize(this);
if (was_scope && opt instanceof AST_Scope) {
opt.drop_unused(this);
descend(opt, this);
} }
node._squeezed = true; if (opt === node) opt._squeezed = true;
return node; return opt;
} }
}); });
@@ -172,8 +183,7 @@ merge(Compressor.prototype, {
if (compressor.has_directive("use asm")) return self; if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor); var opt = optimizer(self, compressor);
opt._optimized = true; opt._optimized = true;
if (opt === self) return opt; return opt;
return opt.transform(compressor);
}); });
}; };
@@ -284,6 +294,7 @@ merge(Compressor.prototype, {
} }
var iife; var iife;
if (node instanceof AST_Function if (node instanceof AST_Function
&& !node.name
&& (iife = tw.parent()) instanceof AST_Call && (iife = tw.parent()) instanceof AST_Call
&& iife.expression === node) { && iife.expression === node) {
// Virtually turn IIFE parameters into variable definitions: // Virtually turn IIFE parameters into variable definitions:
@@ -388,7 +399,7 @@ merge(Compressor.prototype, {
return new ctor(props); return new ctor(props);
}; };
function make_node_from_constant(compressor, val, orig) { function make_node_from_constant(val, orig) {
switch (typeof val) { switch (typeof val) {
case "string": case "string":
return make_node(AST_String, orig, { return make_node(AST_String, orig, {
@@ -408,9 +419,9 @@ merge(Compressor.prototype, {
return make_node(AST_Number, orig, { value: val }); return make_node(AST_Number, orig, { value: val });
case "boolean": case "boolean":
return make_node(val ? AST_True : AST_False, orig).transform(compressor); return make_node(val ? AST_True : AST_False, orig);
case "undefined": case "undefined":
return make_node(AST_Undefined, orig).transform(compressor); return make_node(AST_Undefined, orig);
default: default:
if (val === null) { if (val === null) {
return make_node(AST_Null, orig, { value: null }); return make_node(AST_Null, orig, { value: null });
@@ -523,6 +534,7 @@ merge(Compressor.prototype, {
var self = compressor.self(); var self = compressor.self();
var var_defs_removed = false; var var_defs_removed = false;
var toplevel = compressor.option("toplevel");
for (var stat_index = statements.length; --stat_index >= 0;) { for (var stat_index = statements.length; --stat_index >= 0;) {
var stat = statements[stat_index]; var stat = statements[stat_index];
if (stat instanceof AST_Definitions) continue; if (stat instanceof AST_Definitions) continue;
@@ -560,7 +572,8 @@ merge(Compressor.prototype, {
// Only interested in cases with just one reference to the variable. // Only interested in cases with just one reference to the variable.
var def = self.find_variable && self.find_variable(var_name); var def = self.find_variable && self.find_variable(var_name);
if (!def || !def.references || def.references.length !== 1 || var_name == "arguments") { if (!def || !def.references || def.references.length !== 1
|| var_name == "arguments" || (!toplevel && def.global)) {
side_effects_encountered = true; side_effects_encountered = true;
continue; continue;
} }
@@ -936,7 +949,7 @@ merge(Compressor.prototype, {
if (stat instanceof AST_LoopControl) { if (stat instanceof AST_LoopControl) {
var lct = compressor.loopcontrol_target(stat.label); var lct = compressor.loopcontrol_target(stat.label);
if ((stat instanceof AST_Break if ((stat instanceof AST_Break
&& lct instanceof AST_BlockStatement && !(lct instanceof AST_IterationStatement)
&& loop_body(lct) === self) || (stat instanceof AST_Continue && loop_body(lct) === self) || (stat instanceof AST_Continue
&& loop_body(lct) === self)) { && loop_body(lct) === self)) {
if (stat.label) { if (stat.label) {
@@ -1223,11 +1236,11 @@ merge(Compressor.prototype, {
} }
} }
}); });
function to_node(compressor, value, orig) { function to_node(value, orig) {
if (value instanceof AST_Node) return make_node(value.CTOR, orig, value); if (value instanceof AST_Node) return make_node(value.CTOR, orig, value);
if (Array.isArray(value)) return make_node(AST_Array, orig, { if (Array.isArray(value)) return make_node(AST_Array, orig, {
elements: value.map(function(value) { elements: value.map(function(value) {
return to_node(compressor, value, orig); return to_node(value, orig);
}) })
}); });
if (value && typeof value == "object") { if (value && typeof value == "object") {
@@ -1235,14 +1248,14 @@ merge(Compressor.prototype, {
for (var key in value) { for (var key in value) {
props.push(make_node(AST_ObjectKeyVal, orig, { props.push(make_node(AST_ObjectKeyVal, orig, {
key: key, key: key,
value: to_node(compressor, value[key], orig) value: to_node(value[key], orig)
})); }));
} }
return make_node(AST_Object, orig, { return make_node(AST_Object, orig, {
properties: props properties: props
}); });
} }
return make_node_from_constant(compressor, value, orig); return make_node_from_constant(value, orig);
} }
def(AST_Node, noop); def(AST_Node, noop);
def(AST_Dot, function(compressor, suffix){ def(AST_Dot, function(compressor, suffix){
@@ -1253,7 +1266,7 @@ merge(Compressor.prototype, {
var name; var name;
var defines = compressor.option("global_defs"); var defines = compressor.option("global_defs");
if (defines && HOP(defines, (name = this.name + suffix))) { if (defines && HOP(defines, (name = this.name + suffix))) {
var node = to_node(compressor, defines[name], this); var node = to_node(defines[name], this);
var top = compressor.find_parent(AST_Toplevel); var top = compressor.find_parent(AST_Toplevel);
node.walk(new TreeWalker(function(node) { node.walk(new TreeWalker(function(node) {
if (node instanceof AST_SymbolRef) { if (node instanceof AST_SymbolRef) {
@@ -1268,45 +1281,40 @@ merge(Compressor.prototype, {
node.DEFMETHOD("_find_defs", func); node.DEFMETHOD("_find_defs", func);
}); });
function best_of(ast1, ast2) { function best_of_expression(ast1, ast2) {
return ast1.print_to_string().length > return ast1.print_to_string().length >
ast2.print_to_string().length ast2.print_to_string().length
? ast2 : ast1; ? ast2 : ast1;
} }
function best_of_statement(ast1, ast2) { function best_of_statement(ast1, ast2) {
return best_of(make_node(AST_SimpleStatement, ast1, { return best_of_expression(make_node(AST_SimpleStatement, ast1, {
body: ast1 body: ast1
}), make_node(AST_SimpleStatement, ast2, { }), make_node(AST_SimpleStatement, ast2, {
body: ast2 body: ast2
})).body; })).body;
} }
function best_of(compressor, ast1, ast2) {
return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2);
}
// methods to evaluate a constant expression // methods to evaluate a constant expression
(function (def){ (function (def){
// The evaluate method returns an array with one or two // If the node has been successfully reduced to a constant,
// elements. If the node has been successfully reduced to a // then its value is returned; otherwise the element itself
// constant, then the second element tells us the value; // is returned.
// otherwise the second element is missing. The first element // They can be distinguished as constant value is never a
// of the array is always an AST_Node descendant; if // descendant of AST_Node.
// evaluation was successful it's a node that represents the
// constant; otherwise it's the original or a replacement node.
AST_Node.DEFMETHOD("evaluate", function(compressor){ AST_Node.DEFMETHOD("evaluate", function(compressor){
if (!compressor.option("evaluate")) return [ this ]; if (!compressor.option("evaluate")) return this;
var val;
try { try {
val = this._eval(compressor); var val = this._eval(compressor);
return !val || val instanceof RegExp || typeof val != "object" ? val : this;
} catch(ex) { } catch(ex) {
if (ex !== def) throw ex; if (ex !== def) throw ex;
return [ this ]; return this;
} }
var node;
try {
node = make_node_from_constant(compressor, val, this);
} catch(ex) {
return [ this ];
}
return [ best_of(node, this), val ];
}); });
var unaryPrefix = makePredicate("! ~ - +"); var unaryPrefix = makePredicate("! ~ - +");
AST_Node.DEFMETHOD("is_constant", function(){ AST_Node.DEFMETHOD("is_constant", function(){
@@ -1344,8 +1352,8 @@ merge(Compressor.prototype, {
})); }));
} }
var result = this.evaluate(compressor); var result = this.evaluate(compressor);
if (result.length > 1) { if (result !== this) {
return result[1]; return result;
} }
throw new Error(string_template("Cannot evaluate constant [{file}:{line},{col}]", this.start)); throw new Error(string_template("Cannot evaluate constant [{file}:{line},{col}]", this.start));
}); });
@@ -1517,9 +1525,9 @@ merge(Compressor.prototype, {
var stat = make_node(AST_SimpleStatement, alt, { var stat = make_node(AST_SimpleStatement, alt, {
body: alt body: alt
}); });
return best_of(negated, stat) === stat ? alt : negated; return best_of_expression(negated, stat) === stat ? alt : negated;
} }
return best_of(negated, alt); return best_of_expression(negated, alt);
} }
def(AST_Node, function(){ def(AST_Node, function(){
return basic_negation(this); return basic_negation(this);
@@ -1686,8 +1694,8 @@ merge(Compressor.prototype, {
return thing && thing.aborts(); return thing && thing.aborts();
}; };
(function(def){ (function(def){
def(AST_Statement, function(){ return null }); def(AST_Statement, return_null);
def(AST_Jump, function(){ return this }); def(AST_Jump, return_this);
function block_aborts(){ function block_aborts(){
var n = this.body.length; var n = this.body.length;
return n > 0 && aborts(this.body[n - 1]); return n > 0 && aborts(this.body[n - 1]);
@@ -1705,7 +1713,7 @@ merge(Compressor.prototype, {
/* -----[ optimizers ]----- */ /* -----[ optimizers ]----- */
OPT(AST_Directive, function(self, compressor){ OPT(AST_Directive, function(self, compressor){
if (compressor.has_directive(self.value) === "up") { if (compressor.has_directive(self.value) !== self) {
return make_node(AST_EmptyStatement, self); return make_node(AST_EmptyStatement, self);
} }
return self; return self;
@@ -1789,14 +1797,18 @@ merge(Compressor.prototype, {
if (node instanceof AST_Definitions && scope === self) { if (node instanceof AST_Definitions && scope === self) {
node.definitions.forEach(function(def){ node.definitions.forEach(function(def){
if (!drop_vars) { if (!drop_vars) {
var node_def = def.name.definition(); def.name.walk(new TreeWalker(function(node) {
if (node_def.global && !(node_def.id in in_use_ids)) { if (node instanceof AST_SymbolDeclaration) {
in_use_ids[node_def.id] = true; var def = node.definition();
in_use.push(node_def); if (def.global && !(def.id in in_use_ids)) {
in_use_ids[def.id] = true;
in_use.push(def);
} }
} }
}));
}
if (def.value) { if (def.value) {
if (def.is_destructuring()) { if (def.name instanceof AST_Destructuring) {
var destructuring_cache = destructuring_value; var destructuring_cache = destructuring_value;
destructuring_value = def.value; destructuring_value = def.value;
in_definition = true; in_definition = true;
@@ -1907,7 +1919,10 @@ merge(Compressor.prototype, {
for (var a = node.argnames, i = a.length; --i >= 0;) { for (var a = node.argnames, i = a.length; --i >= 0;) {
var sym = a[i]; var sym = a[i];
if (sym instanceof AST_Expansion) { if (sym instanceof AST_Expansion) {
sym = sym.symbol; sym = sym.expression;
}
if (sym instanceof AST_DefaultAssign) {
sym = sym.left;
} }
// Do not drop destructuring arguments. // Do not drop destructuring arguments.
// They constitute a type assertion, so dropping // They constitute a type assertion, so dropping
@@ -1947,7 +1962,7 @@ merge(Compressor.prototype, {
if (node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn)) { if (node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn)) {
var def = node.definitions.filter(function(def){ var def = node.definitions.filter(function(def){
if (def.value) def.value = def.value.transform(tt); if (def.value) def.value = def.value.transform(tt);
if (def.is_destructuring()) return true; if (def.name instanceof AST_Destructuring) return true;
if (def.name.definition().id in in_use_ids) return true; if (def.name.definition().id in in_use_ids) return true;
if (!drop_vars && def.name.definition().global) return true; if (!drop_vars && def.name.definition().global) return true;
@@ -2090,11 +2105,11 @@ merge(Compressor.prototype, {
} }
if (node instanceof AST_Var && hoist_vars) { if (node instanceof AST_Var && hoist_vars) {
node.definitions.forEach(function(def){ node.definitions.forEach(function(def){
if (def.is_destructuring()) { return; } if (def.name instanceof AST_Destructuring) return;
vars.set(def.name.name, def); vars.set(def.name.name, def);
++vars_found; ++vars_found;
}); });
var seq = node.to_assignments(); var seq = node.to_assignments(compressor);
var p = tt.parent(); var p = tt.parent();
if (p instanceof AST_ForIn && p.init === node) { if (p instanceof AST_ForIn && p.init === node) {
if (seq == null) { if (seq == null) {
@@ -2190,14 +2205,6 @@ merge(Compressor.prototype, {
// drop_side_effect_free() // drop_side_effect_free()
// remove side-effect-free parts which only affects return value // remove side-effect-free parts which only affects return value
(function(def){ (function(def){
function return_this() {
return this;
}
function return_null() {
return null;
}
// Drop side-effect-free elements from an array of expressions. // Drop side-effect-free elements from an array of expressions.
// Returns an array of expressions with side-effects or null // Returns an array of expressions with side-effects or null
// if all elements were dropped. Note: original array may be // if all elements were dropped. Note: original array may be
@@ -2354,27 +2361,24 @@ merge(Compressor.prototype, {
}); });
OPT(AST_DWLoop, function(self, compressor){ OPT(AST_DWLoop, function(self, compressor){
var cond = self.condition.evaluate(compressor);
self.condition = cond[0];
if (!compressor.option("loops")) return self; if (!compressor.option("loops")) return self;
if (cond.length > 1) { var cond = self.condition.evaluate(compressor);
if (cond[1]) { if (cond !== self.condition) {
if (cond) {
return make_node(AST_For, self, { return make_node(AST_For, self, {
body: self.body body: self.body
}); });
} else if (self instanceof AST_While) { } else if (compressor.option("dead_code") && self instanceof AST_While) {
if (compressor.option("dead_code")) {
var a = []; var a = [];
extract_declarations_from_unreachable_code(compressor, self.body, a); extract_declarations_from_unreachable_code(compressor, self.body, a);
return make_node(AST_BlockStatement, self, { body: a }); return make_node(AST_BlockStatement, self, { body: a });
}
} else { } else {
// self instanceof AST_Do cond = make_node_from_constant(cond, self.condition).transform(compressor);
return self; self.condition = best_of_expression(cond, self.condition);
} }
} }
if (self instanceof AST_While) { if (self instanceof AST_While) {
return make_node(AST_For, self, self).transform(compressor); return make_node(AST_For, self, self).optimize(compressor);
} }
return self; return self;
}); });
@@ -2396,7 +2400,7 @@ merge(Compressor.prototype, {
var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body; var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body;
if (first instanceof AST_If) { if (first instanceof AST_If) {
if (first.body instanceof AST_Break if (first.body instanceof AST_Break
&& compressor.loopcontrol_target(first.body.label) === self) { && compressor.loopcontrol_target(first.body.label) === compressor.self()) {
if (self.condition) { if (self.condition) {
self.condition = make_node(AST_Binary, self.condition, { self.condition = make_node(AST_Binary, self.condition, {
left: self.condition, left: self.condition,
@@ -2409,7 +2413,7 @@ merge(Compressor.prototype, {
drop_it(first.alternative); drop_it(first.alternative);
} }
else if (first.alternative instanceof AST_Break else if (first.alternative instanceof AST_Break
&& compressor.loopcontrol_target(first.alternative.label) === self) { && compressor.loopcontrol_target(first.alternative.label) === compressor.self()) {
if (self.condition) { if (self.condition) {
self.condition = make_node(AST_Binary, self.condition, { self.condition = make_node(AST_Binary, self.condition, {
left: self.condition, left: self.condition,
@@ -2425,15 +2429,10 @@ merge(Compressor.prototype, {
}; };
OPT(AST_For, function(self, compressor){ OPT(AST_For, function(self, compressor){
var cond = self.condition;
if (cond) {
cond = cond.evaluate(compressor);
self.condition = cond[0];
}
if (!compressor.option("loops")) return self; if (!compressor.option("loops")) return self;
if (cond) { if (self.condition) {
if (cond.length > 1 && !cond[1]) { var cond = self.condition.evaluate(compressor);
if (compressor.option("dead_code")) { if (compressor.option("dead_code") && !cond) {
var a = []; var a = [];
if (self.init instanceof AST_Statement) { if (self.init instanceof AST_Statement) {
a.push(self.init); a.push(self.init);
@@ -2446,6 +2445,9 @@ merge(Compressor.prototype, {
extract_declarations_from_unreachable_code(compressor, self.body, a); extract_declarations_from_unreachable_code(compressor, self.body, a);
return make_node(AST_BlockStatement, self, { body: a }); return make_node(AST_BlockStatement, self, { body: a });
} }
if (cond !== self.condition) {
cond = make_node_from_constant(cond, self.condition).transform(compressor);
self.condition = best_of_expression(cond, self.condition);
} }
} }
if_break_in_loop(self, compressor); if_break_in_loop(self, compressor);
@@ -2461,9 +2463,8 @@ merge(Compressor.prototype, {
// “has no side effects”; also it doesn't work for cases like // “has no side effects”; also it doesn't work for cases like
// `x && true`, though it probably should. // `x && true`, though it probably should.
var cond = self.condition.evaluate(compressor); var cond = self.condition.evaluate(compressor);
self.condition = cond[0]; if (cond !== self.condition) {
if (cond.length > 1) { if (cond) {
if (cond[1]) {
compressor.warn("Condition always true [{file}:{line},{col}]", self.condition.start); compressor.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
if (compressor.option("dead_code")) { if (compressor.option("dead_code")) {
var a = []; var a = [];
@@ -2471,7 +2472,7 @@ merge(Compressor.prototype, {
extract_declarations_from_unreachable_code(compressor, self.alternative, a); extract_declarations_from_unreachable_code(compressor, self.alternative, a);
} }
a.push(self.body); a.push(self.body);
return make_node(AST_BlockStatement, self, { body: a }).transform(compressor); return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor);
} }
} else { } else {
compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start); compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
@@ -2479,9 +2480,11 @@ merge(Compressor.prototype, {
var a = []; var a = [];
extract_declarations_from_unreachable_code(compressor, self.body, a); extract_declarations_from_unreachable_code(compressor, self.body, a);
if (self.alternative) a.push(self.alternative); if (self.alternative) a.push(self.alternative);
return make_node(AST_BlockStatement, self, { body: a }).transform(compressor); return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor);
} }
} }
cond = make_node_from_constant(cond, self.condition).transform(compressor);
self.condition = best_of_expression(cond, self.condition);
} }
var negated = self.condition.negate(compressor); var negated = self.condition.negate(compressor);
var self_condition_length = self.condition.print_to_string().length; var self_condition_length = self.condition.print_to_string().length;
@@ -2498,8 +2501,8 @@ merge(Compressor.prototype, {
} }
if (is_empty(self.body) && is_empty(self.alternative)) { if (is_empty(self.body) && is_empty(self.alternative)) {
return make_node(AST_SimpleStatement, self.condition, { return make_node(AST_SimpleStatement, self.condition, {
body: self.condition body: self.condition.clone()
}).transform(compressor); }).optimize(compressor);
} }
if (self.body instanceof AST_SimpleStatement if (self.body instanceof AST_SimpleStatement
&& self.alternative instanceof AST_SimpleStatement) { && self.alternative instanceof AST_SimpleStatement) {
@@ -2509,7 +2512,7 @@ merge(Compressor.prototype, {
consequent : statement_to_expression(self.body), consequent : statement_to_expression(self.body),
alternative : statement_to_expression(self.alternative) alternative : statement_to_expression(self.alternative)
}) })
}).transform(compressor); }).optimize(compressor);
} }
if (is_empty(self.alternative) && self.body instanceof AST_SimpleStatement) { if (is_empty(self.alternative) && self.body instanceof AST_SimpleStatement) {
if (self_condition_length === negated_length && !negated_is_best if (self_condition_length === negated_length && !negated_is_best
@@ -2525,14 +2528,14 @@ merge(Compressor.prototype, {
left : negated, left : negated,
right : statement_to_expression(self.body) right : statement_to_expression(self.body)
}) })
}).transform(compressor); }).optimize(compressor);
return make_node(AST_SimpleStatement, self, { return make_node(AST_SimpleStatement, self, {
body: make_node(AST_Binary, self, { body: make_node(AST_Binary, self, {
operator : "&&", operator : "&&",
left : self.condition, left : self.condition,
right : statement_to_expression(self.body) right : statement_to_expression(self.body)
}) })
}).transform(compressor); }).optimize(compressor);
} }
if (self.body instanceof AST_EmptyStatement if (self.body instanceof AST_EmptyStatement
&& self.alternative && self.alternative
@@ -2543,7 +2546,7 @@ merge(Compressor.prototype, {
left : self.condition, left : self.condition,
right : statement_to_expression(self.alternative) right : statement_to_expression(self.alternative)
}) })
}).transform(compressor); }).optimize(compressor);
} }
if (self.body instanceof AST_Exit if (self.body instanceof AST_Exit
&& self.alternative instanceof AST_Exit && self.alternative instanceof AST_Exit
@@ -2553,18 +2556,21 @@ merge(Compressor.prototype, {
condition : self.condition, condition : self.condition,
consequent : self.body.value || make_node(AST_Undefined, self.body), consequent : self.body.value || make_node(AST_Undefined, self.body),
alternative : self.alternative.value || make_node(AST_Undefined, self.alternative) alternative : self.alternative.value || make_node(AST_Undefined, self.alternative)
}) }).transform(compressor)
}).transform(compressor); }).optimize(compressor);
} }
if (self.body instanceof AST_If if (self.body instanceof AST_If
&& !self.body.alternative && !self.body.alternative
&& !self.alternative) { && !self.alternative) {
self.condition = make_node(AST_Binary, self.condition, { self = make_node(AST_If, self, {
condition: make_node(AST_Binary, self.condition, {
operator: "&&", operator: "&&",
left: self.condition, left: self.condition,
right: self.body.condition right: self.body.condition
}).transform(compressor); }),
self.body = self.body.body; body: self.body.body,
alternative: null
});
} }
if (aborts(self.body)) { if (aborts(self.body)) {
if (self.alternative) { if (self.alternative) {
@@ -2572,7 +2578,7 @@ merge(Compressor.prototype, {
self.alternative = null; self.alternative = null;
return make_node(AST_BlockStatement, self, { return make_node(AST_BlockStatement, self, {
body: [ self, alt ] body: [ self, alt ]
}).transform(compressor); }).optimize(compressor);
} }
} }
if (aborts(self.alternative)) { if (aborts(self.alternative)) {
@@ -2582,7 +2588,7 @@ merge(Compressor.prototype, {
self.alternative = null; self.alternative = null;
return make_node(AST_BlockStatement, self, { return make_node(AST_BlockStatement, self, {
body: [ self, body ] body: [ self, body ]
}).transform(compressor); }).optimize(compressor);
} }
return self; return self;
}); });
@@ -2606,12 +2612,12 @@ merge(Compressor.prototype, {
} }
break; break;
} }
var exp = self.expression.evaluate(compressor); var value = self.expression.evaluate(compressor);
out: if (exp.length == 2) try { out: if (value !== self.expression) try {
// constant expression // constant expression
self.expression = exp[0]; var expression = make_node_from_constant(value, self.expression);
self.expression = best_of_expression(expression, self.expression);
if (!compressor.option("dead_code")) break out; if (!compressor.option("dead_code")) break out;
var value = exp[1];
var in_if = false; var in_if = false;
var in_block = false; var in_block = false;
var started = false; var started = false;
@@ -2658,11 +2664,11 @@ merge(Compressor.prototype, {
if (stopped) return MAP.skip; if (stopped) return MAP.skip;
if (node instanceof AST_Case) { if (node instanceof AST_Case) {
var exp = node.expression.evaluate(compressor); var exp = node.expression.evaluate(compressor);
if (exp.length < 2) { if (exp === node.expression) {
// got a case with non-constant expression, baling out // got a case with non-constant expression, baling out
throw self; throw self;
} }
if (exp[1] === value || started) { if (exp === value || started) {
started = true; started = true;
if (aborts(node)) stopped = true; if (aborts(node)) stopped = true;
descend(node, this); descend(node, this);
@@ -2696,15 +2702,17 @@ merge(Compressor.prototype, {
this.definitions.forEach(function(def){ def.value = null }); this.definitions.forEach(function(def){ def.value = null });
}); });
AST_Definitions.DEFMETHOD("to_assignments", function(){ AST_Definitions.DEFMETHOD("to_assignments", function(compressor){
var reduce_vars = compressor.option("reduce_vars");
var assignments = this.definitions.reduce(function(a, def){ var assignments = this.definitions.reduce(function(a, def){
if (def.value && !def.is_destructuring()) { if (def.value && !(def.name instanceof AST_Destructuring)) {
var name = make_node(AST_SymbolRef, def.name, def.name); var name = make_node(AST_SymbolRef, def.name, def.name);
a.push(make_node(AST_Assign, def, { a.push(make_node(AST_Assign, def, {
operator : "=", operator : "=",
left : name, left : name,
right : def.value right : def.value
})); }));
if (reduce_vars) name.definition().fixed = false;
} else if (def.value) { } else if (def.value) {
// Because it's a destructuring, do not turn into an assignment. // Because it's a destructuring, do not turn into an assignment.
var varDef = make_node(AST_VarDef, def, { var varDef = make_node(AST_VarDef, def, {
@@ -2783,7 +2791,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":
@@ -2801,7 +2809,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, {
@@ -2810,7 +2818,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, {
@@ -2819,7 +2827,7 @@ merge(Compressor.prototype, {
operator: "!" operator: "!"
}), }),
operator: "!" operator: "!"
}).transform(compressor); }).optimize(compressor);
break; break;
case "Function": case "Function":
// new Function() => function(){} // new Function() => function(){}
@@ -2889,21 +2897,20 @@ 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;
if (self.args.length > 0) { if (self.args.length > 0) {
separator = self.args[0].evaluate(compressor); separator = self.args[0].evaluate(compressor);
if (separator.length < 2) break EXIT; // not a constant if (separator === self.args[0]) break EXIT; // not a constant
separator = separator[1];
} }
var elements = []; var elements = [];
var consts = []; var consts = [];
exp.expression.elements.forEach(function(el) { exp.expression.elements.forEach(function(el) {
el = el.evaluate(compressor); var value = el.evaluate(compressor);
if (el.length > 1) { if (value !== el) {
consts.push(el[1]); consts.push(value);
} else { } else {
if (consts.length > 0) { if (consts.length > 0) {
elements.push(make_node(AST_String, self, { elements.push(make_node(AST_String, self, {
@@ -2911,7 +2918,7 @@ merge(Compressor.prototype, {
})); }));
consts.length = 0; consts.length = 0;
} }
elements.push(el[0]); elements.push(el);
} }
}); });
if (consts.length > 0) { if (consts.length > 0) {
@@ -2944,7 +2951,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.
@@ -2952,7 +2959,17 @@ merge(Compressor.prototype, {
node.expression = node.expression.clone(); node.expression = node.expression.clone();
node.expression.expression = node.expression.expression.clone(); node.expression.expression = node.expression.expression.clone();
node.expression.expression.elements = elements; node.expression.expression.elements = elements;
return best_of(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 && !self.expression.is_generator) { if (exp instanceof AST_Function && !self.expression.is_generator) {
@@ -3098,8 +3115,7 @@ merge(Compressor.prototype, {
return e.expression; return e.expression;
} }
if (e instanceof AST_Binary) { if (e instanceof AST_Binary) {
var statement = first_in_statement(compressor); self = best_of(compressor, self, e.negate(compressor, first_in_statement(compressor)));
self = (statement ? best_of_statement : best_of)(self, e.negate(compressor, statement));
} }
break; break;
case "typeof": case "typeof":
@@ -3112,16 +3128,16 @@ merge(Compressor.prototype, {
}).optimize(compressor); }).optimize(compressor);
} }
} }
return self.evaluate(compressor)[0]; // avoids infinite recursion of numerals
}); if (self.operator != "-" || !(self.expression instanceof AST_Number)) {
var ev = self.evaluate(compressor);
function has_side_effects_or_prop_access(node, compressor) { if (ev !== self) {
var save_pure_getters = compressor.option("pure_getters"); ev = make_node_from_constant(ev, self).optimize(compressor);
compressor.options.pure_getters = false; return best_of(compressor, ev, self);
var ret = node.has_side_effects(compressor);
compressor.options.pure_getters = save_pure_getters;
return ret;
} }
}
return self;
});
AST_Binary.DEFMETHOD("lift_sequences", function(compressor){ AST_Binary.DEFMETHOD("lift_sequences", function(compressor){
if (compressor.option("sequences")) { if (compressor.option("sequences")) {
@@ -3130,18 +3146,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 && !this.left.has_side_effects(compressor)) {
var assign = this.operator == "=" && this.left instanceof AST_SymbolRef;
var root = this.right;
var cursor, seq = root;
while (assign || !seq.car.has_side_effects(compressor)) {
cursor = seq;
if (seq.cdr instanceof AST_Seq) {
seq = seq.cdr;
} else break;
}
if (cursor) {
this.right = cursor.cdr;
cursor.cdr = this;
return root.optimize(compressor);
} }
if (this.right instanceof AST_Seq
&& this instanceof AST_Assign
&& !has_side_effects_or_prop_access(this.left, compressor)) {
var seq = this.right;
var x = seq.to_array();
this.right = x.pop();
x.push(this);
seq = AST_Seq.from_array(x).transform(compressor);
return seq;
} }
} }
return this; return this;
@@ -3150,16 +3171,6 @@ merge(Compressor.prototype, {
var commutativeOperators = makePredicate("== === != !== * & | ^"); var commutativeOperators = makePredicate("== === != !== * & | ^");
OPT(AST_Binary, function(self, compressor){ OPT(AST_Binary, function(self, compressor){
var lhs = self.left.evaluate(compressor);
var rhs = self.right.evaluate(compressor);
if (lhs.length > 1 && lhs[0].is_constant() !== self.left.is_constant()
|| rhs.length > 1 && rhs[0].is_constant() !== self.right.is_constant()) {
return make_node(AST_Binary, self, {
operator: self.operator,
left: lhs[0],
right: rhs[0]
}).optimize(compressor);
}
function reversible() { function reversible() {
return self.left instanceof AST_Constant return self.left instanceof AST_Constant
|| self.right instanceof AST_Constant || self.right instanceof AST_Constant
@@ -3186,32 +3197,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) {
@@ -3244,48 +3229,48 @@ merge(Compressor.prototype, {
case "&&": case "&&":
var ll = self.left.evaluate(compressor); var ll = self.left.evaluate(compressor);
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
if ((ll.length > 1 && !ll[1]) || (rr.length > 1 && !rr[1])) { if (!ll || !rr) {
compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start); compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);
return make_node(AST_Seq, self, { return make_node(AST_Seq, self, {
car: self.left, car: self.left,
cdr: make_node(AST_False, self) cdr: make_node(AST_False, self)
}).optimize(compressor); }).optimize(compressor);
} }
if (ll.length > 1 && ll[1]) { if (ll !== self.left && ll) {
return rr[0]; return self.right.optimize(compressor);
} }
if (rr.length > 1 && rr[1]) { if (rr !== self.right && rr) {
return ll[0]; return self.left.optimize(compressor);
} }
break; break;
case "||": case "||":
var ll = self.left.evaluate(compressor); var ll = self.left.evaluate(compressor);
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
if ((ll.length > 1 && ll[1]) || (rr.length > 1 && rr[1])) { if (ll !== self.left && ll || rr !== self.right && rr) {
compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start); compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);
return make_node(AST_Seq, self, { return make_node(AST_Seq, self, {
car: self.left, car: self.left,
cdr: make_node(AST_True, self) cdr: make_node(AST_True, self)
}).optimize(compressor); }).optimize(compressor);
} }
if (ll.length > 1 && !ll[1]) { if (!ll) {
return rr[0]; return self.right.optimize(compressor);
} }
if (rr.length > 1 && !rr[1]) { if (!rr) {
return ll[0]; return self.left.optimize(compressor);
} }
break; break;
case "+": case "+":
var ll = self.left.evaluate(compressor); var ll = self.left.evaluate(compressor);
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
if (ll.length > 1 && ll[0] instanceof AST_String && ll[1]) { if (ll && typeof ll == "string") {
compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start); compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
return make_node(AST_Seq, self, { return make_node(AST_Seq, self, {
car: self.right, car: self.right,
cdr: make_node(AST_True, self) cdr: make_node(AST_True, self)
}).optimize(compressor); }).optimize(compressor);
} }
if (rr.length > 1 && rr[0] instanceof AST_String && rr[1]) { if (rr && typeof rr == "string") {
compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start); compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
return make_node(AST_Seq, self, { return make_node(AST_Seq, self, {
car: self.left, car: self.left,
@@ -3297,12 +3282,11 @@ merge(Compressor.prototype, {
if (compressor.option("comparisons") && self.is_boolean()) { if (compressor.option("comparisons") && self.is_boolean()) {
if (!(compressor.parent() instanceof AST_Binary) if (!(compressor.parent() instanceof AST_Binary)
|| compressor.parent() instanceof AST_Assign) { || compressor.parent() instanceof AST_Assign) {
var statement = first_in_statement(compressor);
var negated = make_node(AST_UnaryPrefix, self, { var negated = make_node(AST_UnaryPrefix, self, {
operator: "!", operator: "!",
expression: self.negate(compressor, statement) expression: self.negate(compressor, first_in_statement(compressor))
}); });
self = (statement ? best_of_statement : best_of)(self, negated); self = best_of(compressor, self, negated);
} }
if (compressor.option("unsafe_comps")) { if (compressor.option("unsafe_comps")) {
switch (self.operator) { switch (self.operator) {
@@ -3454,9 +3438,9 @@ merge(Compressor.prototype, {
}); });
if (self.right instanceof AST_Constant if (self.right instanceof AST_Constant
&& !(self.left instanceof AST_Constant)) { && !(self.left instanceof AST_Constant)) {
self = best_of(reversed, self); self = best_of(compressor, reversed, self);
} else { } else {
self = best_of(self, reversed); self = best_of(compressor, self, reversed);
} }
} }
if (associative && self.is_number(compressor)) { if (associative && self.is_number(compressor)) {
@@ -3553,7 +3537,12 @@ merge(Compressor.prototype, {
self.right = self.right.right; self.right = self.right.right;
return self.transform(compressor); return self.transform(compressor);
} }
return self.evaluate(compressor)[0]; 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;
}); });
OPT(AST_SymbolRef, function(self, compressor){ OPT(AST_SymbolRef, function(self, compressor){
@@ -3568,11 +3557,11 @@ merge(Compressor.prototype, {
&& (!self.scope.uses_with || !compressor.find_parent(AST_With))) { && (!self.scope.uses_with || !compressor.find_parent(AST_With))) {
switch (self.name) { switch (self.name) {
case "undefined": case "undefined":
return make_node(AST_Undefined, self).transform(compressor); return make_node(AST_Undefined, self).optimize(compressor);
case "NaN": case "NaN":
return make_node(AST_NaN, self).transform(compressor); return make_node(AST_NaN, self).optimize(compressor);
case "Infinity": case "Infinity":
return make_node(AST_Infinity, self).transform(compressor); return make_node(AST_Infinity, self).optimize(compressor);
} }
} }
if (compressor.option("evaluate") && compressor.option("reduce_vars")) { if (compressor.option("evaluate") && compressor.option("reduce_vars")) {
@@ -3580,18 +3569,20 @@ merge(Compressor.prototype, {
if (d.fixed) { if (d.fixed) {
if (d.should_replace === undefined) { if (d.should_replace === undefined) {
var init = d.fixed.evaluate(compressor); var init = d.fixed.evaluate(compressor);
if (init.length > 1) { if (init !== d.fixed) {
var value = init[0].print_to_string().length; init = make_node_from_constant(init, d.fixed).optimize(compressor);
init = best_of_expression(init, d.fixed);
var value = init.print_to_string().length;
var name = d.name.length; var name = d.name.length;
var freq = d.references.length; var freq = d.references.length;
var overhead = d.global || !freq ? 0 : (name + 2 + value) / freq; var overhead = d.global || !freq ? 0 : (name + 2 + value) / freq;
d.should_replace = value <= name + overhead ? init[0] : false; d.should_replace = value <= name + overhead ? init : false;
} else { } else {
d.should_replace = false; d.should_replace = false;
} }
} }
if (d.should_replace) { if (d.should_replace) {
return d.should_replace; return d.should_replace.clone(true);
} }
} }
} }
@@ -3655,10 +3646,11 @@ merge(Compressor.prototype, {
var evaluateRight = self.right.evaluate(compressor); var evaluateRight = self.right.evaluate(compressor);
// `[x = undefined] = foo` ---> `[x] = foo` // `[x = undefined] = foo` ---> `[x] = foo`
if (evaluateRight.length > 1 && evaluateRight[1] === undefined) { if (evaluateRight === undefined) {
self = self.left; self = self.left;
} else { } else if (evaluateRight !== self.right) {
self.right = evaluateRight[0]; evaluateRight = make_node_from_constant(evaluateRight, self.right);
self.right = best_of_expression(evaluateRight, self.right);
} }
return self; return self;
@@ -3672,8 +3664,8 @@ merge(Compressor.prototype, {
return AST_Seq.cons(car, self); return AST_Seq.cons(car, self);
} }
var cond = self.condition.evaluate(compressor); var cond = self.condition.evaluate(compressor);
if (cond.length > 1) { if (cond !== self.condition) {
if (cond[1]) { if (cond) {
compressor.warn("Condition always true [{file}:{line},{col}]", self.start); compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), self, self.consequent); return maintain_this_binding(compressor.parent(), self, self.consequent);
} else { } else {
@@ -3681,9 +3673,8 @@ merge(Compressor.prototype, {
return maintain_this_binding(compressor.parent(), self, self.alternative); return maintain_this_binding(compressor.parent(), self, self.alternative);
} }
} }
var statement = first_in_statement(compressor); var negated = cond.negate(compressor, first_in_statement(compressor));
var negated = cond[0].negate(compressor, statement); if (best_of(compressor, cond, negated) === negated) {
if ((statement ? best_of_statement : best_of)(cond[0], negated) === negated) {
self = make_node(AST_Conditional, self, { self = make_node(AST_Conditional, self, {
condition: negated, condition: negated,
consequent: self.alternative, consequent: self.alternative,
@@ -3863,7 +3854,12 @@ merge(Compressor.prototype, {
}); });
} }
} }
return self.evaluate(compressor)[0]; 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;
}); });
OPT(AST_Dot, function(self, compressor){ OPT(AST_Dot, function(self, compressor){
@@ -3902,13 +3898,17 @@ merge(Compressor.prototype, {
break; break;
} }
} }
return self.evaluate(compressor)[0]; 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;
}); });
function literals_in_boolean_context(self, compressor) { function literals_in_boolean_context(self, compressor) {
if (compressor.option("booleans") && compressor.in_boolean_context()) { if (compressor.option("booleans") && compressor.in_boolean_context()) {
var best = first_in_statement(compressor) ? best_of_statement : best_of; return best_of(compressor, self, make_node(AST_Seq, self, {
return best(self, make_node(AST_Seq, self, {
car: self, car: self,
cdr: make_node(AST_True, self) cdr: make_node(AST_True, self)
}).optimize(compressor)); }).optimize(compressor));
@@ -3954,24 +3954,19 @@ merge(Compressor.prototype, {
var segments = []; var segments = [];
for (var i = 0; i < self.segments.length; i++) { for (var i = 0; i < self.segments.length; i++) {
if (self.segments[i] instanceof AST_Node) { var segment = self.segments[i];
var result = self.segments[i].evaluate(compressor); if (segment instanceof AST_Node) {
// No result[1] means nothing to stringify var result = segment.evaluate(compressor);
if (result.length === 1) { // Evaluate to constant value
segments.push(result[0]); // Constant value shorter than ${segment}
continue; if (result !== segment && (result + "").length <= segment.print_to_string().length + "${}".length) {
}
// Evaluate length
if (result[0].print_to_string().length + 3 /* ${} */ < (result[1]+"").length) {
segments.push(result[0]);
continue;
}
// There should always be a previous and next segment if segment is a node // There should always be a previous and next segment if segment is a node
segments[segments.length - 1].value = segments[segments.length - 1].value + result[1] + self.segments[++i].value; segments[segments.length - 1].value = segments[segments.length - 1].value + result + self.segments[++i].value;
} else { continue;
segments.push(self.segments[i]);
} }
} }
segments.push(segment);
}
self.segments = segments; self.segments = segments;
return self; return self;

View File

@@ -1998,7 +1998,6 @@ function parse($TEXT, options) {
// allow trailing comma // allow trailing comma
break; break;
start = S.token; start = S.token;
var type = start.type;
var name = as_property_name(); var name = as_property_name();
var value; var value;
@@ -2009,9 +2008,6 @@ function parse($TEXT, options) {
a.push(concise); a.push(concise);
continue; continue;
} }
if (!(start.type !== name)) {
unexpected(S.token);
}
value = new AST_SymbolRef({ value = new AST_SymbolRef({
start: prev(), start: prev(),

View File

@@ -279,7 +279,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
if (node instanceof AST_SymbolCatch) { if (node instanceof AST_SymbolCatch) {
var name = node.name; var name = node.name;
var refs = node.thedef.references; var refs = node.thedef.references;
var scope = node.thedef.scope.parent_scope; var scope = node.thedef.scope.parent_scope.parent_scope;
var def = scope.find_variable(name) || self.globals.get(name) || scope.def_variable(node); var def = scope.find_variable(name) || self.globals.get(name) || scope.def_variable(node);
refs.forEach(function(ref) { refs.forEach(function(ref) {
ref.thedef = def; ref.thedef = def;

View File

@@ -126,9 +126,11 @@ function merge(obj, ext) {
return count; return count;
}; };
function noop() {}; function noop() {}
function return_false() { return false; } function return_false() { return false; }
function return_true() { return true; } function return_true() { return true; }
function return_this() { return this; }
function return_null() { return null; }
var MAP = (function(){ var MAP = (function(){
function MAP(a, f, backwards) { function MAP(a, f, backwards) {

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.11", "version": "2.8.14",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },

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: {
@@ -1152,7 +1157,8 @@ collapse_vars_arguments: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true, comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true,
toplevel:true
} }
input: { input: {
var outer = function() { var outer = function() {
@@ -1167,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: {
@@ -1316,11 +1323,13 @@ collapse_vars_regexp: {
console.log(result[0]); console.log(result[0]);
})(); })();
} }
expect_stdout: true
} }
issue_1537: { issue_1537: {
options = { options = {
collapse_vars: true, collapse_vars: true,
toplevel: true,
} }
input: { input: {
var k = ''; var k = '';
@@ -1335,6 +1344,7 @@ issue_1537: {
issue_1537_for_of: { issue_1537_for_of: {
options = { options = {
collapse_vars: true, collapse_vars: true,
toplevel: true,
} }
input: { input: {
var k = ''; var k = '';
@@ -1349,6 +1359,7 @@ issue_1537_for_of: {
issue_1537_destructuring_1: { issue_1537_destructuring_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
toplevel: true,
} }
input: { input: {
var x = 1, y = 2; var x = 1, y = 2;
@@ -1363,6 +1374,7 @@ issue_1537_destructuring_1: {
issue_1537_destructuring_2: { issue_1537_destructuring_2: {
options = { options = {
collapse_vars: true, collapse_vars: true,
toplevel: true,
} }
input: { input: {
var x = foo(); var x = foo();
@@ -1377,6 +1389,7 @@ issue_1537_destructuring_2: {
issue_1537_destructuring_3: { issue_1537_destructuring_3: {
options = { options = {
collapse_vars: true, collapse_vars: true,
toplevel: true,
} }
input: { input: {
var x = Math.random(); var x = Math.random();
@@ -1391,6 +1404,7 @@ issue_1537_destructuring_3: {
issue_1537_destructuring_for_in: { issue_1537_destructuring_for_in: {
options = { options = {
collapse_vars: true, collapse_vars: true,
toplevel: true,
} }
input: { input: {
var x = 1, y = 2; var x = 1, y = 2;
@@ -1409,6 +1423,7 @@ issue_1537_destructuring_for_in: {
issue_1537_destructuring_for_of: { issue_1537_destructuring_for_of: {
options = { options = {
collapse_vars: true, collapse_vars: true,
toplevel: true,
} }
input: { input: {
var x = 1, y = 2; var x = 1, y = 2;
@@ -1427,6 +1442,7 @@ issue_1537_destructuring_for_of: {
issue_1562: { issue_1562: {
options = { options = {
collapse_vars: true, collapse_vars: true,
toplevel: true,
} }
input: { input: {
var v = 1, B = 2; var v = 1, B = 2;
@@ -1455,3 +1471,46 @@ issue_1562: {
for (; f(z + 2) ;) bar(30); for (; f(z + 2) ;) bar(30);
} }
} }
issue_1605_1: {
options = {
collapse_vars: true,
toplevel: false,
}
input: {
function foo(x) {
var y = x;
return y;
}
var o = new Object;
o.p = 1;
}
expect: {
function foo(x) {
return x;
}
var o = new Object;
o.p = 1;
}
}
issue_1605_2: {
options = {
collapse_vars: true,
toplevel: "vars",
}
input: {
function foo(x) {
var y = x;
return y;
}
var o = new Object;
o.p = 1;
}
expect: {
function foo(x) {
return x;
}
(new Object).p = 1;
}
}

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,99 @@ 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
}

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_block_decls_die: { dead_code_block_decls_die: {
@@ -135,6 +136,7 @@ dead_code_const_declaration: {
var moo; var moo;
function bar() {} function bar() {}
} }
expect_stdout: true
} }
dead_code_const_annotation: { dead_code_const_annotation: {
@@ -162,6 +164,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: {
@@ -185,6 +188,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: {
@@ -230,4 +234,5 @@ dead_code_const_annotation_complex_scope: {
var meat = 'beef'; var meat = 'beef';
var pork = 'bad'; var pork = 'bad';
} }
expect_stdout: true
} }

View File

@@ -293,3 +293,17 @@ reduce_vars: {
for ([x,y] in pairs); for ([x,y] in pairs);
} }
} }
unused: {
options = {
unused: true,
}
input: {
let { foo: [, , ...a] } = { foo: [1, 2, 3, 4], bar: 5 };
console.log(a);
}
expect: {
let { foo: [, , ...a] } = { foo: [1, 2, 3, 4], bar: 5 };
console.log(a);
}
}

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
} }
pow: { pow: {
@@ -337,6 +339,7 @@ unsafe_constant: {
(void 0).a (void 0).a
); );
} }
expect_stdout: true
} }
unsafe_object: { unsafe_object: {
@@ -360,6 +363,7 @@ unsafe_object: {
1..b + 1 1..b + 1
); );
} }
expect_stdout: true
} }
unsafe_object_nested: { unsafe_object_nested: {
@@ -383,6 +387,7 @@ unsafe_object_nested: {
2 2
); );
} }
expect_stdout: true
} }
unsafe_object_complex: { unsafe_object_complex: {
@@ -406,6 +411,7 @@ unsafe_object_complex: {
2 2
); );
} }
expect_stdout: true
} }
unsafe_object_repeated: { unsafe_object_repeated: {
@@ -429,6 +435,7 @@ unsafe_object_repeated: {
1..b + 1 1..b + 1
); );
} }
expect_stdout: true
} }
unsafe_object_accessor: { unsafe_object_accessor: {
@@ -478,6 +485,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: {
@@ -505,6 +513,7 @@ unsafe_integer_key: {
1["1"] + 1 1["1"] + 1
); );
} }
expect_stdout: true
} }
unsafe_integer_key_complex: { unsafe_integer_key_complex: {
@@ -532,6 +541,7 @@ unsafe_integer_key_complex: {
2 2
); );
} }
expect_stdout: true
} }
unsafe_float_key: { unsafe_float_key: {
@@ -559,6 +569,7 @@ unsafe_float_key: {
1["3.14"] + 1 1["3.14"] + 1
); );
} }
expect_stdout: true
} }
unsafe_float_key_complex: { unsafe_float_key_complex: {
@@ -586,6 +597,7 @@ unsafe_float_key_complex: {
2 2
); );
} }
expect_stdout: true
} }
unsafe_array: { unsafe_array: {
@@ -621,6 +633,7 @@ unsafe_array: {
(void 0)[1] + 1 (void 0)[1] + 1
); );
} }
expect_stdout: true
} }
unsafe_string: { unsafe_string: {
@@ -648,6 +661,7 @@ unsafe_string: {
"11" "11"
); );
} }
expect_stdout: true
} }
unsafe_array_bad_index: { unsafe_array_bad_index: {
@@ -669,6 +683,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: {
@@ -690,6 +705,7 @@ unsafe_string_bad_index: {
"1234"[3.14] + 1 "1234"[3.14] + 1
); );
} }
expect_stdout: true
} }
unsafe_prototype_function: { unsafe_prototype_function: {
@@ -736,6 +752,7 @@ call_args: {
console.log(1); console.log(1);
+(1, 1); +(1, 1);
} }
expect_stdout: true
} }
call_args_drop_param: { call_args_drop_param: {
@@ -757,6 +774,7 @@ call_args_drop_param: {
console.log(1); console.log(1);
+(b, 1); +(b, 1);
} }
expect_stdout: true
} }
in_boolean_context: { in_boolean_context: {
@@ -794,4 +812,74 @@ 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)
);
}
} }

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

@@ -286,3 +286,48 @@ fat_arrow_as_param: {
} }
expect_exact: "foo(x=>x);foo(x=>x,y=>y);foo(x=>(x,x));foo(x=>(x,x),y=>(y,y));" expect_exact: "foo(x=>x);foo(x=>x,y=>y);foo(x=>(x,x));foo(x=>(x,x),y=>(y,y));"
} }
default_assign: {
options = {
keep_fargs: false,
unused: true,
}
input: {
function f(a, b = 3) {
console.log(a);
}
}
expect: {
function f(a) {
console.log(a);
}
}
}
expansion: {
options = {
keep_fargs: false,
unused: true,
}
input: {
function f(a, ...b) {
console.log(a);
}
}
expect: {
function f(a) {
console.log(a);
}
}
}
issue_1613: {
mangle = { toplevel: true };
input: {
const name = 1;
const foo = {
name
};
}
expect_exact: "const n=1;const c={name:n};"
}

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

@@ -13,9 +13,9 @@ same_variable_in_multiple_for_loop: {
join_vars: true, join_vars: true,
cascade: true, cascade: true,
side_effects: true, side_effects: true,
collapse_vars: true collapse_vars: true,
}; }
mangle = {}; mangle = {}
input: { input: {
for (let i = 0; i < 3; i++) { for (let i = 0; i < 3; i++) {
let a = 100; let a = 100;
@@ -38,6 +38,7 @@ same_variable_in_multiple_for_loop: {
} }
} }
} }
expect_stdout: true
} }
same_variable_in_multiple_forOf: { same_variable_in_multiple_forOf: {
@@ -55,9 +56,9 @@ same_variable_in_multiple_forOf: {
join_vars: true, join_vars: true,
cascade: true, cascade: true,
side_effects: true, side_effects: true,
collapse_vars: true collapse_vars: true,
}; }
mangle = {}; mangle = {}
input: { input: {
var test = [ "a", "b", "c" ]; var test = [ "a", "b", "c" ];
for (let tmp of test) { for (let tmp of test) {
@@ -79,6 +80,7 @@ same_variable_in_multiple_forOf: {
console.log(o); console.log(o);
} }
} }
expect_stdout: true
} }
same_variable_in_multiple_forIn: { same_variable_in_multiple_forIn: {
@@ -96,9 +98,9 @@ same_variable_in_multiple_forIn: {
join_vars: true, join_vars: true,
cascade: true, cascade: true,
side_effects: true, side_effects: true,
collapse_vars: true collapse_vars: true,
}; }
mangle = {}; mangle = {}
input: { input: {
var test = [ "a", "b", "c" ]; var test = [ "a", "b", "c" ];
for (let tmp in test) { for (let tmp in test) {
@@ -120,6 +122,7 @@ same_variable_in_multiple_forIn: {
console.log(o); console.log(o);
} }
} }
expect_stdout: true
} }
different_variable_in_multiple_for_loop: { different_variable_in_multiple_for_loop: {
@@ -137,9 +140,9 @@ different_variable_in_multiple_for_loop: {
join_vars: true, join_vars: true,
cascade: true, cascade: true,
side_effects: true, side_effects: true,
collapse_vars: true collapse_vars: true,
}; }
mangle = {}; mangle = {}
input: { input: {
for (let i = 0; i < 3; i++) { for (let i = 0; i < 3; i++) {
let a = 100; let a = 100;
@@ -162,6 +165,7 @@ different_variable_in_multiple_for_loop: {
} }
} }
} }
expect_stdout: true
} }
different_variable_in_multiple_forOf: { different_variable_in_multiple_forOf: {
@@ -179,9 +183,9 @@ different_variable_in_multiple_forOf: {
join_vars: true, join_vars: true,
cascade: true, cascade: true,
side_effects: true, side_effects: true,
collapse_vars: true collapse_vars: true,
}; }
mangle = {}; mangle = {}
input: { input: {
var test = [ "a", "b", "c" ]; var test = [ "a", "b", "c" ];
for (let tmp of test) { for (let tmp of test) {
@@ -203,6 +207,7 @@ different_variable_in_multiple_forOf: {
console.log(o); console.log(o);
} }
} }
expect_stdout: true
} }
different_variable_in_multiple_forIn: { different_variable_in_multiple_forIn: {
@@ -220,9 +225,9 @@ different_variable_in_multiple_forIn: {
join_vars: true, join_vars: true,
cascade: true, cascade: true,
side_effects: true, side_effects: true,
collapse_vars: true collapse_vars: true,
}; }
mangle = {}; mangle = {}
input: { input: {
var test = [ "a", "b", "c" ]; var test = [ "a", "b", "c" ];
for (let tmp in test) { for (let tmp in test) {
@@ -244,6 +249,7 @@ different_variable_in_multiple_forIn: {
console.log(o); console.log(o);
} }
} }
expect_stdout: true
} }
more_variable_in_multiple_for: { more_variable_in_multiple_for: {
@@ -261,9 +267,9 @@ more_variable_in_multiple_for: {
join_vars: true, join_vars: true,
cascade: true, cascade: true,
side_effects: true, side_effects: true,
collapse_vars: true collapse_vars: true,
}; }
mangle = {}; mangle = {}
input: { input: {
for (let a = 9, i = 0; i < 20; i += a) { for (let a = 9, i = 0; i < 20; i += a) {
let b = a++ + i; let b = a++ + i;
@@ -281,5 +287,5 @@ more_variable_in_multiple_for: {
console.log(o, c, e, l, f); console.log(o, c, e, l, f);
} }
} }
expect_stdout: true
} }

View File

@@ -0,0 +1,99 @@
screw_ie8: {
options = {
screw_ie8: true,
}
mangle = {
screw_ie8: true,
}
input: {
try { throw "foo"; } catch (x) { console.log(x); }
}
expect_exact: 'try{throw"foo"}catch(o){console.log(o)}'
expect_stdout: [
"foo"
]
}
support_ie8: {
options = {
screw_ie8: false,
}
mangle = {
screw_ie8: false,
}
input: {
try { throw "foo"; } catch (x) { console.log(x); }
}
expect_exact: 'try{throw"foo"}catch(x){console.log(x)}'
expect_stdout: "foo"
}
safe_undefined: {
options = {
conditionals: true,
if_return: true,
unsafe: false,
}
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(function(n) {
return function() {
return a ? b : c ? d : void 0;
};
}(1)());
}
expect_stdout: true
}
unsafe_undefined: {
options = {
conditionals: true,
if_return: true,
unsafe: true,
}
mangle = {}
input: {
var a, c;
console.log(function(undefined) {
return function() {
if (a)
return b;
if (c)
return d;
};
}()());
}
expect: {
var a, c;
console.log(function(n) {
return function() {
return a ? b : c ? d : n;
};
}()());
}
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,56 @@
chained_evaluation_1: {
options = {
collapse_vars: true,
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
var a = 1;
(function() {
var b = a, c;
c = f(b);
c.bar = b;
})();
})();
}
expect: {
(function() {
(function() {
var c;
c = f(1);
c.bar = 1;
})();
})();
}
}
chained_evaluation_2: {
options = {
collapse_vars: true,
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
var a = "long piece of string";
(function() {
var b = a, c;
c = f(b);
c.bar = b;
})();
})();
}
expect: {
(function() {
var a = "long piece of string";
(function() {
var c;
c = f(a);
c.bar = a;
})();
})();
}
}

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

@@ -50,6 +50,7 @@ this_binding_conditionals: {
this_binding_collapse_vars: { this_binding_collapse_vars: {
options = { options = {
collapse_vars: true, collapse_vars: true,
toplevel: true,
}; };
input: { input: {
var c = a; c(); var c = a; c();

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: {
@@ -295,7 +297,15 @@ issue_186_beautify: {
else else
bar(); bar();
} }
expect_exact: 'var x = 3;\n\nif (foo()) do {\n do {\n alert(x);\n } while (--x);\n} while (x); else bar();' expect_exact: [
'var x = 3;',
'',
'if (foo()) do {',
' do {',
' alert(x);',
' } while (--x);',
'} while (x); else bar();',
]
} }
issue_186_beautify_ie8: { issue_186_beautify_ie8: {
@@ -314,7 +324,17 @@ issue_186_beautify_ie8: {
else else
bar(); bar();
} }
expect_exact: 'var x = 3;\n\nif (foo()) {\n do {\n do {\n alert(x);\n } while (--x);\n } while (x);\n} else bar();' expect_exact: [
'var x = 3;',
'',
'if (foo()) {',
' do {',
' do {',
' alert(x);',
' } while (--x);',
' } while (x);',
'} else bar();',
]
} }
issue_186_bracketize: { issue_186_bracketize: {
@@ -374,7 +394,19 @@ issue_186_beautify_bracketize: {
else else
bar(); bar();
} }
expect_exact: 'var x = 3;\n\nif (foo()) {\n do {\n do {\n alert(x);\n } while (--x);\n } while (x);\n} else {\n bar();\n}' expect_exact: [
'var x = 3;',
'',
'if (foo()) {',
' do {',
' do {',
' alert(x);',
' } while (--x);',
' } while (x);',
'} else {',
' bar();',
'}',
]
} }
issue_186_beautify_bracketize_ie8: { issue_186_beautify_bracketize_ie8: {
@@ -394,5 +426,17 @@ issue_186_beautify_bracketize_ie8: {
else else
bar(); bar();
} }
expect_exact: 'var x = 3;\n\nif (foo()) {\n do {\n do {\n alert(x);\n } while (--x);\n } while (x);\n} else {\n bar();\n}' expect_exact: [
'var x = 3;',
'',
'if (foo()) {',
' do {',
' do {',
' alert(x);',
' } while (--x);',
' } while (x);',
'} else {',
' bar();',
'}',
]
} }

View File

@@ -7,7 +7,13 @@ too_short: {
return { c: 42, d: a(), e: "foo"}; return { c: 42, d: a(), e: "foo"};
} }
} }
expect_exact: 'function f(a){\nreturn{\nc:42,\nd:a(),\ne:"foo"}}' expect_exact: [
'function f(a){',
'return{',
'c:42,',
'd:a(),',
'e:"foo"}}',
]
expect_warnings: [ expect_warnings: [
"WARN: Output exceeds 10 characters" "WARN: Output exceeds 10 characters"
] ]
@@ -22,7 +28,12 @@ just_enough: {
return { c: 42, d: a(), e: "foo"}; return { c: 42, d: a(), e: "foo"};
} }
} }
expect_exact: 'function f(a){\nreturn{c:42,\nd:a(),e:"foo"}\n}' expect_exact: [
'function f(a){',
'return{c:42,',
'd:a(),e:"foo"}',
'}',
]
expect_warnings: [ expect_warnings: [
] ]
} }

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

@@ -157,17 +157,21 @@ computed_property_names_evaluated_2: {
shorthand_properties: { shorthand_properties: {
mangle = true; mangle = true;
input: (function() { input: {
(function() {
var prop = 1; var prop = 1;
const value = {prop}; const value = {prop};
return value; return value;
})(); })();
expect: (function() { }
expect: {
(function() {
var n = 1; var n = 1;
const r = {prop:n}; const r = {prop:n};
return r; return r;
})(); })();
} }
}
concise_methods: { concise_methods: {
beautify = { beautify = {
@@ -233,9 +237,7 @@ concise_methods_with_computed_property2: {
}; };
doSomething(foo[[1]]()); doSomething(foo[[1]]());
} }
expect_exact: { expect_exact: 'var foo={[[1]](){return"success"}};doSomething(foo[[1]]());'
'var foo={[[1]](){return"success"}};doSomething(foo[[1]]());'
}
} }
concise_methods_with_various_property_names: { concise_methods_with_various_property_names: {

View File

@@ -58,6 +58,7 @@ reduce_vars: {
})(); })();
console.log(2); console.log(2);
} }
expect_stdout: true
} }
modified: { modified: {
@@ -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: {
@@ -1252,3 +1264,103 @@ iife_func_side_effects: {
})(x(), 0, z()); })(x(), 0, z());
} }
} }
issue_1595_1: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
(function f(a) {
return f(a + 1);
})(2);
}
expect: {
(function f(a) {
return f(a + 1);
})(2);
}
}
issue_1595_2: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
(function f(a) {
return g(a + 1);
})(2);
}
expect: {
(function(a) {
return g(a + 1);
})(2);
}
}
issue_1595_3: {
options = {
evaluate: true,
passes: 2,
reduce_vars: true,
unused: true,
}
input: {
(function f(a) {
return g(a + 1);
})(2);
}
expect: {
(function(a) {
return g(3);
})();
}
}
issue_1595_4: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
(function iife(a, b, c) {
console.log(a, b, c);
if (a) iife(a - 1, b, c);
})(3, 4, 5);
}
expect: {
(function iife(a, b, c) {
console.log(a, b, c);
if (a) iife(a - 1, b, c);
})(3, 4, 5);
}
expect_stdout: true
}
issue_1606: {
options = {
evaluate: true,
hoist_vars: true,
reduce_vars: true,
}
input: {
function f() {
var a;
function g(){};
var b = 2;
x(b);
}
}
expect: {
function f() {
var a, b;
function g(){};
b = 2;
x(b);
}
}
}

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: {

131
test/compress/transform.js Normal file
View File

@@ -0,0 +1,131 @@
booleans_evaluate: {
options = {
booleans: true,
evaluate: true,
}
input: {
console.log(typeof void 0 != "undefined");
console.log(1 == 1, 1 === 1)
console.log(1 != 1, 1 !== 1)
}
expect: {
console.log(!1);
console.log(!0, !0);
console.log(!1, !1);
}
expect_stdout: true
}
booleans_global_defs: {
options = {
booleans: true,
evaluate: true,
global_defs: {
A: true,
},
}
input: {
console.log(A == 1);
}
expect: {
console.log(!0);
}
expect_stdout: true
}
condition_evaluate: {
options = {
booleans: true,
dead_code: false,
evaluate: true,
loops: false,
}
input: {
while (1 === 2);
for (; 1 == true;);
if (void 0 == null);
}
expect: {
while (!1);
for (; !0;);
if (!0);
}
}
if_else_empty: {
options = {
conditionals: true,
}
input: {
if ({} ? a : b); else {}
}
expect: {
!{} ? b : a;
}
}
label_if_break: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
}
input: {
L: if (true) {
a;
break L;
}
}
expect: {
a;
}
}
while_if_break: {
options = {
conditionals: true,
loops: true,
sequences: true,
}
input: {
while (a) {
if (b) if(c) d;
if (e) break;
}
}
expect: {
for(; a && (b && c && d, !e););
}
}
if_return: {
options = {
booleans: true,
conditionals: true,
if_return: true,
sequences: true,
}
input: {
function f(w, x, y, z) {
if (x) return;
if (w) {
if (y) return;
} else if (z) return;
if (x == y) return true;
if (x) w();
if (y) z();
return true;
}
}
expect: {
function f(w, x, y, z) {
if (!x) {
if (w) {
if (y) return;
} else if (z) return;
return x == y || (x && w(), y && z(), !0);
}
}
}
}

View File

@@ -152,7 +152,7 @@ describe("bin/uglifyjs", function () {
}); });
}); });
it("Should process inline source map", function(done) { it("Should process inline source map", function(done) {
var command = uglifyjscmd + ' test/input/issue-520/input.js -cm toplevel --in-source-map inline --source-map-inline'; var command = uglifyjscmd + ' test/input/issue-520/input.js -mc toplevel --in-source-map inline --source-map-inline';
exec(command, function (err, stdout) { exec(command, function (err, stdout) {
if (err) throw err; if (err) throw err;

View File

@@ -397,7 +397,7 @@ describe("Directives", function() {
if (node instanceof uglify.AST_Symbol && node.name === "_check_") { if (node instanceof uglify.AST_Symbol && node.name === "_check_") {
checked = true; checked = true;
for (var j = 0; j < tests[i].directives.length; j++) { for (var j = 0; j < tests[i].directives.length; j++) {
assert.equal(checkWalker.has_directive(tests[i].directives[j]), true, assert.ok(checkWalker.has_directive(tests[i].directives[j]),
"Did not found directive '" + tests[i].directives[j] + "' in test " + tests[i].input) "Did not found directive '" + tests[i].directives[j] + "' in test " + tests[i].input)
} }
for (var k = 0; k < tests[i].non_directives.length; k++) { for (var k = 0; k < tests[i].non_directives.length; k++) {

View File

@@ -78,6 +78,7 @@ describe("minify", function() {
}); });
it("Should process inline source map", function() { it("Should process inline source map", function() {
var code = Uglify.minify("./test/input/issue-520/input.js", { var code = Uglify.minify("./test/input/issue-520/input.js", {
compress: { toplevel: true },
inSourceMap: "inline", inSourceMap: "inline",
sourceMapInline: true sourceMapInline: true
}).code + "\n"; }).code + "\n";

View File

@@ -6,10 +6,22 @@ var U = require("../tools/node");
var path = require("path"); var path = require("path");
var fs = require("fs"); var fs = require("fs");
var assert = require("assert"); var assert = require("assert");
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) {
@@ -71,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;
} }
@@ -165,6 +182,36 @@ function run_compress_tests() {
failed_files[file] = 1; failed_files[file] = 1;
} }
} }
if (test.expect_stdout) {
var stdout = run_code(make_code(input, output_options));
if (test.expect_stdout === true) {
test.expect_stdout = stdout;
}
if (!same_stdout(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", {
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_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;
}
}
}
} }
} }
var tests = parse_test(path.resolve(dir, file)); var tests = parse_test(path.resolve(dir, file));
@@ -214,6 +261,23 @@ function parse_test(file) {
})); }));
} }
function read_string(stat) {
if (stat.TYPE == "SimpleStatement") {
var body = stat.body;
switch(body.TYPE) {
case "String":
return body.value;
case "Array":
return body.elements.map(function(element) {
if (element.TYPE !== "String")
throw new Error("Should be array of strings");
return element.value;
}).join("\n");
}
}
throw new Error("Should be string or array of strings");
}
function get_one_test(name, block) { function get_one_test(name, block) {
var test = { name: name, options: {} }; var test = { name: name, options: {} };
var tw = new U.TreeWalker(function(node, descend){ var tw = new U.TreeWalker(function(node, descend){
@@ -226,28 +290,26 @@ function parse_test(file) {
return true; return true;
} }
if (node instanceof U.AST_LabeledStatement) { if (node instanceof U.AST_LabeledStatement) {
var label = node.label;
assert.ok( assert.ok(
["input", "expect", "expect_exact", "expect_warnings"].indexOf(node.label.name) >= 0, ["input", "expect", "expect_exact", "expect_warnings", "expect_stdout"].indexOf(label.name) >= 0,
tmpl("Unsupported label {name} [{line},{col}]", { tmpl("Unsupported label {name} [{line},{col}]", {
name: node.label.name, name: label.name,
line: node.label.start.line, line: label.start.line,
col: node.label.start.col col: label.start.col
}) })
); );
var stat = node.body; var stat = node.body;
if (stat instanceof U.AST_BlockStatement) { if (label.name == "expect_exact") {
if (stat.body.length == 1) stat = stat.body[0]; test[label.name] = read_string(stat);
else if (stat.body.length == 0) stat = new U.AST_EmptyStatement(); } else if (label.name == "expect_stdout") {
} if (stat.TYPE == "SimpleStatement" && stat.body instanceof U.AST_Boolean) {
if (node.label.name === "expect_exact") { test[label.name] = stat.body.value;
if (!(stat.TYPE === "SimpleStatement" && stat.body.TYPE === "String")) {
throw new Error(
"The value of the expect_exact clause should be a string, " +
"like `expect_exact: \"some.exact.javascript;\"`");
}
test[node.label.name] = stat.body.start.value
} else { } else {
test[node.label.name] = stat; test[label.name] = read_string(stat) + "\n";
}
} else {
test[label.name] = stat;
} }
return true; return true;
} }
@@ -269,3 +331,19 @@ function evaluate(code) {
code = make_code(code, { beautify: true }); code = make_code(code, { beautify: true });
return new Function("return(" + code + ")")(); return new Function("return(" + code + ")")();
} }
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;
}
}