Compare commits

...

22 Commits

Author SHA1 Message Date
Alex Lam S.L
6b2f34769a v2.8.15 2017-03-23 13:36:47 +08:00
Alex Lam S.L
48ffbef51d account for cross-scope modifications in collapse_vars (#1634)
mostly done by @kzc

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

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

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

Emulate that by transforming preceding `AST_SimpleStatement`s of `AST_String` into `AST_Directive`.
2017-03-18 01:56:15 +08:00
Christian Maughan Tegnér
b7c112eefe Add --in-source-map inline documentation (#1611) 2017-03-17 03:08:38 +08:00
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
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
41 changed files with 1164 additions and 345 deletions

View File

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

View File

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

View File

@@ -984,8 +984,8 @@ TreeWalker.prototype = {
push: function (node) {
if (node instanceof AST_Lambda) {
this.directives = Object.create(this.directives);
} else if (node instanceof AST_Directive) {
this.directives[node.value] = this.directives[node.value] ? "up" : true;
} else if (node instanceof AST_Directive && !this.directives[node.value]) {
this.directives[node.value] = node;
}
this.stack.push(node);
},
@@ -1013,7 +1013,7 @@ TreeWalker.prototype = {
for (var i = 0; i < node.body.length; ++i) {
var st = node.body[i];
if (!(st instanceof AST_Directive)) break;
if (st.value == type) return true;
if (st.value == type) return st;
}
}
},

View File

@@ -98,10 +98,10 @@ function Compressor(options, false_by_default) {
this.top_retain = function(def) {
return top_retain.test(def.name);
};
} else if (typeof top_retain === "function") {
} else if (typeof top_retain == "function") {
this.top_retain = top_retain;
} else if (top_retain) {
if (typeof top_retain === "string") {
if (typeof top_retain == "string") {
top_retain = top_retain.split(/,/);
}
this.top_retain = function(def) {
@@ -131,6 +131,11 @@ merge(Compressor.prototype, {
}
return node;
},
info: function() {
if (this.options.warnings == "verbose") {
AST_Node.warn.apply(AST_Node, arguments);
}
},
warn: function(text, props) {
if (this.options.warnings) {
// only emit unique warnings
@@ -151,14 +156,25 @@ merge(Compressor.prototype, {
node = node.hoist_declarations(this);
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);
node = node.optimize(this);
if (was_scope && node instanceof AST_Scope) {
node.drop_unused(this);
descend(node, this);
// Existing code relies on how AST_Node.optimize() worked, and omitting the
// following replacement call would result in degraded efficiency of both
// output and performance.
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;
return node;
if (opt === node) opt._squeezed = true;
return opt;
}
});
@@ -171,8 +187,7 @@ merge(Compressor.prototype, {
if (compressor.has_directive("use asm")) return self;
var opt = optimizer(self, compressor);
opt._optimized = true;
if (opt === self) return opt;
return opt.transform(compressor);
return opt;
});
};
@@ -384,7 +399,7 @@ merge(Compressor.prototype, {
return new ctor(props);
};
function make_node_from_constant(compressor, val, orig) {
function make_node_from_constant(val, orig) {
switch (typeof val) {
case "string":
return make_node(AST_String, orig, {
@@ -404,9 +419,9 @@ merge(Compressor.prototype, {
return make_node(AST_Number, orig, { value: val });
case "boolean":
return make_node(val ? AST_True : AST_False, orig).optimize(compressor);
return make_node(val ? AST_True : AST_False, orig);
case "undefined":
return make_node(AST_Undefined, orig).transform(compressor);
return make_node(AST_Undefined, orig);
default:
if (val === null) {
return make_node(AST_Null, orig, { value: null });
@@ -511,6 +526,7 @@ merge(Compressor.prototype, {
var self = compressor.self();
var var_defs_removed = false;
var toplevel = compressor.option("toplevel");
for (var stat_index = statements.length; --stat_index >= 0;) {
var stat = statements[stat_index];
if (stat instanceof AST_Definitions) continue;
@@ -548,7 +564,8 @@ merge(Compressor.prototype, {
// Only interested in cases with just one reference to the variable.
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;
continue;
}
@@ -602,12 +619,24 @@ merge(Compressor.prototype, {
|| node instanceof AST_IterationStatement
|| (parent instanceof AST_If && node !== parent.condition)
|| (parent instanceof AST_Conditional && node !== parent.condition)
|| (node instanceof AST_SymbolRef
&& !are_references_in_scope(node.definition(), self))
|| (parent instanceof AST_Binary
&& (parent.operator == "&&" || parent.operator == "||")
&& node === parent.right)
|| (parent instanceof AST_Switch && node !== parent.expression)) {
return side_effects_encountered = unwind = true, node;
}
function are_references_in_scope(def, scope) {
if (def.orig.length === 1
&& def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope !== scope) return false;
var refs = def.references;
for (var i = 0, len = refs.length; i < len; i++) {
if (refs[i].scope !== scope) return false;
}
return true;
}
},
function postorder(node) {
if (unwind) return node;
@@ -652,7 +681,7 @@ merge(Compressor.prototype, {
// Further optimize statement after substitution.
stat.reset_opt_flags(compressor);
compressor.warn("Collapsing " + (is_constant ? "constant" : "variable") +
compressor.info("Collapsing " + (is_constant ? "constant" : "variable") +
" " + var_name + " [{file}:{line},{col}]", node.start);
CHANGED = true;
return value;
@@ -914,7 +943,7 @@ merge(Compressor.prototype, {
if (stat instanceof AST_LoopControl) {
var lct = compressor.loopcontrol_target(stat.label);
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)) {
if (stat.label) {
@@ -1198,11 +1227,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 (Array.isArray(value)) return make_node(AST_Array, orig, {
elements: value.map(function(value) {
return to_node(compressor, value, orig);
return to_node(value, orig);
})
});
if (value && typeof value == "object") {
@@ -1210,14 +1239,14 @@ merge(Compressor.prototype, {
for (var key in value) {
props.push(make_node(AST_ObjectKeyVal, orig, {
key: key,
value: to_node(compressor, value[key], orig)
value: to_node(value[key], orig)
}));
}
return make_node(AST_Object, orig, {
properties: props
});
}
return make_node_from_constant(compressor, value, orig);
return make_node_from_constant(value, orig);
}
def(AST_Node, noop);
def(AST_Dot, function(compressor, suffix){
@@ -1228,7 +1257,7 @@ merge(Compressor.prototype, {
var name;
var defines = compressor.option("global_defs");
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);
node.walk(new TreeWalker(function(node) {
if (node instanceof AST_SymbolRef) {
@@ -1243,45 +1272,40 @@ merge(Compressor.prototype, {
node.DEFMETHOD("_find_defs", func);
});
function best_of(ast1, ast2) {
function best_of_expression(ast1, ast2) {
return ast1.print_to_string().length >
ast2.print_to_string().length
? ast2 : ast1;
}
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
}), make_node(AST_SimpleStatement, ast2, {
body: ast2
})).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
(function (def){
// The evaluate method returns an array with one or two
// elements. If the node has been successfully reduced to a
// constant, then the second element tells us the value;
// otherwise the second element is missing. The first element
// of the array is always an AST_Node descendant; if
// evaluation was successful it's a node that represents the
// constant; otherwise it's the original or a replacement node.
// If the node has been successfully reduced to a constant,
// then its value is returned; otherwise the element itself
// is returned.
// They can be distinguished as constant value is never a
// descendant of AST_Node.
AST_Node.DEFMETHOD("evaluate", function(compressor){
if (!compressor.option("evaluate")) return [ this ];
var val;
if (!compressor.option("evaluate")) return this;
try {
val = this._eval(compressor);
var val = this._eval(compressor);
return !val || val instanceof RegExp || typeof val != "object" ? val : this;
} catch(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("! ~ - +");
AST_Node.DEFMETHOD("is_constant", function(){
@@ -1319,8 +1343,8 @@ merge(Compressor.prototype, {
}));
}
var result = this.evaluate(compressor);
if (result.length > 1) {
return result[1];
if (result !== this) {
return result;
}
throw new Error(string_template("Cannot evaluate constant [{file}:{line},{col}]", this.start));
});
@@ -1480,9 +1504,9 @@ merge(Compressor.prototype, {
var stat = make_node(AST_SimpleStatement, 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(){
return basic_negation(this);
@@ -1646,8 +1670,8 @@ merge(Compressor.prototype, {
return thing && thing.aborts();
};
(function(def){
def(AST_Statement, function(){ return null });
def(AST_Jump, function(){ return this });
def(AST_Statement, return_null);
def(AST_Jump, return_this);
function block_aborts(){
var n = this.body.length;
return n > 0 && aborts(this.body[n - 1]);
@@ -1664,7 +1688,7 @@ merge(Compressor.prototype, {
/* -----[ optimizers ]----- */
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 self;
@@ -1821,7 +1845,7 @@ merge(Compressor.prototype, {
sym.__unused = true;
if (trim) {
a.pop();
compressor.warn("Dropping unused function argument {name} [{file}:{line},{col}]", {
compressor[sym.unreferenced() ? "warn" : "info"]("Dropping unused function argument {name} [{file}:{line},{col}]", {
name : sym.name,
file : sym.start.file,
line : sym.start.line,
@@ -1836,7 +1860,7 @@ merge(Compressor.prototype, {
}
if (drop_funcs && node instanceof AST_Defun && node !== self) {
if (!(node.name.definition().id in in_use_ids)) {
compressor.warn("Dropping unused function {name} [{file}:{line},{col}]", {
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", {
name : node.name.name,
file : node.name.start.file,
line : node.name.start.line,
@@ -1860,7 +1884,7 @@ merge(Compressor.prototype, {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w);
return true;
}
compressor.warn("Dropping unused variable {name} [{file}:{line},{col}]", w);
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", w);
return false;
});
// place uninitialized names at the start
@@ -1981,7 +2005,7 @@ merge(Compressor.prototype, {
vars.set(def.name.name, def);
++vars_found;
});
var seq = node.to_assignments();
var seq = node.to_assignments(compressor);
var p = tt.parent();
if (p instanceof AST_ForIn && p.init === node) {
if (seq == null) {
@@ -2077,14 +2101,6 @@ merge(Compressor.prototype, {
// drop_side_effect_free()
// remove side-effect-free parts which only affects return value
(function(def){
function return_this() {
return this;
}
function return_null() {
return null;
}
// Drop side-effect-free elements from an array of expressions.
// Returns an array of expressions with side-effects or null
// if all elements were dropped. Note: original array may be
@@ -2241,27 +2257,24 @@ merge(Compressor.prototype, {
});
OPT(AST_DWLoop, function(self, compressor){
var cond = self.condition.evaluate(compressor);
self.condition = cond[0];
if (!compressor.option("loops")) return self;
if (cond.length > 1) {
if (cond[1]) {
var cond = self.condition.evaluate(compressor);
if (cond !== self.condition) {
if (cond) {
return make_node(AST_For, self, {
body: self.body
});
} else if (self instanceof AST_While) {
if (compressor.option("dead_code")) {
var a = [];
extract_declarations_from_unreachable_code(compressor, self.body, a);
return make_node(AST_BlockStatement, self, { body: a });
}
} else if (compressor.option("dead_code") && self instanceof AST_While) {
var a = [];
extract_declarations_from_unreachable_code(compressor, self.body, a);
return make_node(AST_BlockStatement, self, { body: a });
} else {
// self instanceof AST_Do
return self;
cond = make_node_from_constant(cond, self.condition).transform(compressor);
self.condition = best_of_expression(cond, self.condition);
}
}
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;
});
@@ -2283,7 +2296,7 @@ merge(Compressor.prototype, {
var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body;
if (first instanceof AST_If) {
if (first.body instanceof AST_Break
&& compressor.loopcontrol_target(first.body.label) === self) {
&& compressor.loopcontrol_target(first.body.label) === compressor.self()) {
if (self.condition) {
self.condition = make_node(AST_Binary, self.condition, {
left: self.condition,
@@ -2296,7 +2309,7 @@ merge(Compressor.prototype, {
drop_it(first.alternative);
}
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) {
self.condition = make_node(AST_Binary, self.condition, {
left: self.condition,
@@ -2312,27 +2325,25 @@ merge(Compressor.prototype, {
};
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 (cond) {
if (cond.length > 1 && !cond[1]) {
if (compressor.option("dead_code")) {
var a = [];
if (self.init instanceof AST_Statement) {
a.push(self.init);
}
else if (self.init) {
a.push(make_node(AST_SimpleStatement, self.init, {
body: self.init
}));
}
extract_declarations_from_unreachable_code(compressor, self.body, a);
return make_node(AST_BlockStatement, self, { body: a });
if (self.condition) {
var cond = self.condition.evaluate(compressor);
if (compressor.option("dead_code") && !cond) {
var a = [];
if (self.init instanceof AST_Statement) {
a.push(self.init);
}
else if (self.init) {
a.push(make_node(AST_SimpleStatement, self.init, {
body: self.init
}));
}
extract_declarations_from_unreachable_code(compressor, 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);
@@ -2348,9 +2359,8 @@ merge(Compressor.prototype, {
// “has no side effects”; also it doesn't work for cases like
// `x && true`, though it probably should.
var cond = self.condition.evaluate(compressor);
self.condition = cond[0];
if (cond.length > 1) {
if (cond[1]) {
if (cond !== self.condition) {
if (cond) {
compressor.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
if (compressor.option("dead_code")) {
var a = [];
@@ -2358,7 +2368,7 @@ merge(Compressor.prototype, {
extract_declarations_from_unreachable_code(compressor, self.alternative, a);
}
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 {
compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
@@ -2366,9 +2376,11 @@ merge(Compressor.prototype, {
var a = [];
extract_declarations_from_unreachable_code(compressor, self.body, a);
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 self_condition_length = self.condition.print_to_string().length;
@@ -2385,8 +2397,8 @@ merge(Compressor.prototype, {
}
if (is_empty(self.body) && is_empty(self.alternative)) {
return make_node(AST_SimpleStatement, self.condition, {
body: self.condition
}).transform(compressor);
body: self.condition.clone()
}).optimize(compressor);
}
if (self.body instanceof AST_SimpleStatement
&& self.alternative instanceof AST_SimpleStatement) {
@@ -2396,7 +2408,7 @@ merge(Compressor.prototype, {
consequent : statement_to_expression(self.body),
alternative : statement_to_expression(self.alternative)
})
}).transform(compressor);
}).optimize(compressor);
}
if (is_empty(self.alternative) && self.body instanceof AST_SimpleStatement) {
if (self_condition_length === negated_length && !negated_is_best
@@ -2412,14 +2424,14 @@ merge(Compressor.prototype, {
left : negated,
right : statement_to_expression(self.body)
})
}).transform(compressor);
}).optimize(compressor);
return make_node(AST_SimpleStatement, self, {
body: make_node(AST_Binary, self, {
operator : "&&",
left : self.condition,
right : statement_to_expression(self.body)
})
}).transform(compressor);
}).optimize(compressor);
}
if (self.body instanceof AST_EmptyStatement
&& self.alternative
@@ -2430,7 +2442,7 @@ merge(Compressor.prototype, {
left : self.condition,
right : statement_to_expression(self.alternative)
})
}).transform(compressor);
}).optimize(compressor);
}
if (self.body instanceof AST_Exit
&& self.alternative instanceof AST_Exit
@@ -2440,18 +2452,21 @@ merge(Compressor.prototype, {
condition : self.condition,
consequent : self.body.value || make_node(AST_Undefined, self.body),
alternative : self.alternative.value || make_node(AST_Undefined, self.alternative)
})
}).transform(compressor);
}).transform(compressor)
}).optimize(compressor);
}
if (self.body instanceof AST_If
&& !self.body.alternative
&& !self.alternative) {
self.condition = make_node(AST_Binary, self.condition, {
operator: "&&",
left: self.condition,
right: self.body.condition
}).transform(compressor);
self.body = self.body.body;
self = make_node(AST_If, self, {
condition: make_node(AST_Binary, self.condition, {
operator: "&&",
left: self.condition,
right: self.body.condition
}),
body: self.body.body,
alternative: null
});
}
if (aborts(self.body)) {
if (self.alternative) {
@@ -2459,7 +2474,7 @@ merge(Compressor.prototype, {
self.alternative = null;
return make_node(AST_BlockStatement, self, {
body: [ self, alt ]
}).transform(compressor);
}).optimize(compressor);
}
}
if (aborts(self.alternative)) {
@@ -2469,7 +2484,7 @@ merge(Compressor.prototype, {
self.alternative = null;
return make_node(AST_BlockStatement, self, {
body: [ self, body ]
}).transform(compressor);
}).optimize(compressor);
}
return self;
});
@@ -2493,12 +2508,12 @@ merge(Compressor.prototype, {
}
break;
}
var exp = self.expression.evaluate(compressor);
out: if (exp.length == 2) try {
var value = self.expression.evaluate(compressor);
out: if (value !== self.expression) try {
// 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;
var value = exp[1];
var in_if = false;
var in_block = false;
var started = false;
@@ -2545,11 +2560,11 @@ merge(Compressor.prototype, {
if (stopped) return MAP.skip;
if (node instanceof AST_Case) {
var exp = node.expression.evaluate(compressor);
if (exp.length < 2) {
if (exp === node.expression) {
// got a case with non-constant expression, baling out
throw self;
}
if (exp[1] === value || started) {
if (exp === value || started) {
started = true;
if (aborts(node)) stopped = true;
descend(node, this);
@@ -2583,7 +2598,8 @@ merge(Compressor.prototype, {
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){
if (def.value) {
var name = make_node(AST_SymbolRef, def.name, def.name);
@@ -2592,6 +2608,7 @@ merge(Compressor.prototype, {
left : name,
right : def.value
}));
if (reduce_vars) name.definition().fixed = false;
}
return a;
}, []);
@@ -2656,7 +2673,7 @@ merge(Compressor.prototype, {
if (self.args.length != 1) {
return make_node(AST_Array, self, {
elements: self.args
}).transform(compressor);
}).optimize(compressor);
}
break;
case "Object":
@@ -2674,7 +2691,7 @@ merge(Compressor.prototype, {
left: self.args[0],
operator: "+",
right: make_node(AST_String, self, { value: "" })
}).transform(compressor);
}).optimize(compressor);
break;
case "Number":
if (self.args.length == 0) return make_node(AST_Number, self, {
@@ -2683,7 +2700,7 @@ merge(Compressor.prototype, {
if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
expression: self.args[0],
operator: "+"
}).transform(compressor);
}).optimize(compressor);
case "Boolean":
if (self.args.length == 0) return make_node(AST_False, self);
if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
@@ -2692,7 +2709,7 @@ merge(Compressor.prototype, {
operator: "!"
}),
operator: "!"
}).transform(compressor);
}).optimize(compressor);
break;
case "Function":
// new Function() => function(){}
@@ -2757,21 +2774,20 @@ merge(Compressor.prototype, {
left: make_node(AST_String, self, { value: "" }),
operator: "+",
right: exp.expression
}).transform(compressor);
}).optimize(compressor);
}
else if (exp instanceof AST_Dot && exp.expression instanceof AST_Array && exp.property == "join") EXIT: {
var separator;
if (self.args.length > 0) {
separator = self.args[0].evaluate(compressor);
if (separator.length < 2) break EXIT; // not a constant
separator = separator[1];
if (separator === self.args[0]) break EXIT; // not a constant
}
var elements = [];
var consts = [];
exp.expression.elements.forEach(function(el) {
el = el.evaluate(compressor);
if (el.length > 1) {
consts.push(el[1]);
var value = el.evaluate(compressor);
if (value !== el) {
consts.push(value);
} else {
if (consts.length > 0) {
elements.push(make_node(AST_String, self, {
@@ -2779,7 +2795,7 @@ merge(Compressor.prototype, {
}));
consts.length = 0;
}
elements.push(el[0]);
elements.push(el);
}
});
if (consts.length > 0) {
@@ -2812,7 +2828,7 @@ merge(Compressor.prototype, {
left : prev,
right : el
});
}, first).transform(compressor);
}, first).optimize(compressor);
}
// need this awkward cloning to not affect original element
// best_of will decide which one to get through.
@@ -2820,7 +2836,17 @@ merge(Compressor.prototype, {
node.expression = node.expression.clone();
node.expression.expression = node.expression.expression.clone();
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) {
@@ -2966,8 +2992,7 @@ merge(Compressor.prototype, {
return e.expression;
}
if (e instanceof AST_Binary) {
var statement = first_in_statement(compressor);
self = (statement ? best_of_statement : best_of)(self, e.negate(compressor, statement));
self = best_of(compressor, self, e.negate(compressor, first_in_statement(compressor)));
}
break;
case "typeof":
@@ -2980,17 +3005,17 @@ merge(Compressor.prototype, {
}).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);
if (ev !== self) {
ev = make_node_from_constant(ev, self).optimize(compressor);
return best_of(compressor, ev, self);
}
}
return self;
});
function has_side_effects_or_prop_access(node, compressor) {
var save_pure_getters = compressor.option("pure_getters");
compressor.options.pure_getters = false;
var ret = node.has_side_effects(compressor);
compressor.options.pure_getters = save_pure_getters;
return ret;
}
AST_Binary.DEFMETHOD("lift_sequences", function(compressor){
if (compressor.option("sequences")) {
if (this.left instanceof AST_Seq) {
@@ -2998,18 +3023,23 @@ merge(Compressor.prototype, {
var x = seq.to_array();
this.left = x.pop();
x.push(this);
seq = AST_Seq.from_array(x).transform(compressor);
return seq;
return AST_Seq.from_array(x).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;
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);
}
}
}
return this;
@@ -3018,16 +3048,6 @@ merge(Compressor.prototype, {
var commutativeOperators = makePredicate("== === != !== * & | ^");
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() {
return self.left instanceof AST_Constant
|| self.right instanceof AST_Constant
@@ -3054,32 +3074,6 @@ merge(Compressor.prototype, {
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);
if (compressor.option("comparisons")) switch (self.operator) {
@@ -3112,48 +3106,48 @@ merge(Compressor.prototype, {
case "&&":
var ll = self.left.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);
return make_node(AST_Seq, self, {
car: self.left,
cdr: make_node(AST_False, self)
}).optimize(compressor);
}
if (ll.length > 1 && ll[1]) {
return rr[0];
if (ll !== self.left && ll) {
return self.right.optimize(compressor);
}
if (rr.length > 1 && rr[1]) {
return ll[0];
if (rr !== self.right && rr) {
return self.left.optimize(compressor);
}
break;
case "||":
var ll = self.left.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);
return make_node(AST_Seq, self, {
car: self.left,
cdr: make_node(AST_True, self)
}).optimize(compressor);
}
if (ll.length > 1 && !ll[1]) {
return rr[0];
if (!ll) {
return self.right.optimize(compressor);
}
if (rr.length > 1 && !rr[1]) {
return ll[0];
if (!rr) {
return self.left.optimize(compressor);
}
break;
case "+":
var ll = self.left.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);
return make_node(AST_Seq, self, {
car: self.right,
cdr: make_node(AST_True, self)
}).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);
return make_node(AST_Seq, self, {
car: self.left,
@@ -3165,12 +3159,11 @@ merge(Compressor.prototype, {
if (compressor.option("comparisons") && self.is_boolean()) {
if (!(compressor.parent() instanceof AST_Binary)
|| compressor.parent() instanceof AST_Assign) {
var statement = first_in_statement(compressor);
var negated = make_node(AST_UnaryPrefix, self, {
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")) {
switch (self.operator) {
@@ -3322,9 +3315,9 @@ merge(Compressor.prototype, {
});
if (self.right instanceof AST_Constant
&& !(self.left instanceof AST_Constant)) {
self = best_of(reversed, self);
self = best_of(compressor, reversed, self);
} else {
self = best_of(self, reversed);
self = best_of(compressor, self, reversed);
}
}
if (associative && self.is_number(compressor)) {
@@ -3421,7 +3414,12 @@ merge(Compressor.prototype, {
self.right = self.right.right;
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){
@@ -3436,11 +3434,11 @@ merge(Compressor.prototype, {
&& (!self.scope.uses_with || !compressor.find_parent(AST_With))) {
switch (self.name) {
case "undefined":
return make_node(AST_Undefined, self).transform(compressor);
return make_node(AST_Undefined, self).optimize(compressor);
case "NaN":
return make_node(AST_NaN, self).transform(compressor);
return make_node(AST_NaN, self).optimize(compressor);
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")) {
@@ -3448,18 +3446,20 @@ merge(Compressor.prototype, {
if (d.fixed) {
if (d.should_replace === undefined) {
var init = d.fixed.evaluate(compressor);
if (init.length > 1) {
var value = init[0].print_to_string().length;
if (init !== d.fixed) {
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 freq = d.references.length;
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 {
d.should_replace = false;
}
}
if (d.should_replace) {
return d.should_replace;
return d.should_replace.clone(true);
}
}
}
@@ -3524,8 +3524,8 @@ merge(Compressor.prototype, {
return AST_Seq.cons(car, self);
}
var cond = self.condition.evaluate(compressor);
if (cond.length > 1) {
if (cond[1]) {
if (cond !== self.condition) {
if (cond) {
compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), self, self.consequent);
} else {
@@ -3533,9 +3533,8 @@ merge(Compressor.prototype, {
return maintain_this_binding(compressor.parent(), self, self.alternative);
}
}
var statement = first_in_statement(compressor);
var negated = cond[0].negate(compressor, statement);
if ((statement ? best_of_statement : best_of)(cond[0], negated) === negated) {
var negated = cond.negate(compressor, first_in_statement(compressor));
if (best_of(compressor, cond, negated) === negated) {
self = make_node(AST_Conditional, self, {
condition: negated,
consequent: self.alternative,
@@ -3715,7 +3714,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){
@@ -3754,13 +3758,17 @@ merge(Compressor.prototype, {
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) {
if (compressor.option("booleans") && compressor.in_boolean_context()) {
var best = first_in_statement(compressor) ? best_of_statement : best_of;
return best(self, make_node(AST_Seq, self, {
return best_of(compressor, self, make_node(AST_Seq, self, {
car: self,
cdr: make_node(AST_True, self)
}).optimize(compressor));

View File

@@ -1501,9 +1501,7 @@ function parse($TEXT, options) {
};
function is_assignable(expr) {
if (!options.strict) return true;
if (expr instanceof AST_This) return false;
return (expr instanceof AST_PropAccess || expr instanceof AST_Symbol);
return expr instanceof AST_PropAccess || expr instanceof AST_SymbolRef;
};
var maybe_assign = function(no_in) {

View File

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

View File

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

View File

@@ -43,6 +43,7 @@ collapse_vars_side_effects_1: {
z = i += 4;
log(x, z, y, i);
}
f1(), f2(), f3(), f4();
}
expect: {
function f1() {
@@ -73,7 +74,9 @@ collapse_vars_side_effects_1: {
y = i += 3;
log(x, i += 4, y, i);
}
f1(), f2(), f3(), f4();
}
expect_stdout: true
}
collapse_vars_side_effects_2: {
@@ -823,6 +826,7 @@ collapse_vars_repeated: {
console.log(e + "!");
})("!");
}
expect_stdout: true
}
collapse_vars_closures: {
@@ -1109,6 +1113,7 @@ collapse_vars_eval_and_with: {
return function() { with (o) console.log(a) };
})()();
}
expect_stdout: true
}
collapse_vars_constants: {
@@ -1152,7 +1157,8 @@ collapse_vars_arguments: {
options = {
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,
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: {
var outer = function() {
@@ -1167,6 +1173,7 @@ collapse_vars_arguments: {
(function(){console.log(arguments);})(7, 1);
})();
}
expect_stdout: true
}
collapse_vars_short_circuit: {
@@ -1316,6 +1323,7 @@ collapse_vars_regexp: {
console.log(result[0]);
})();
}
expect_stdout: true
}
issue_1537: {
@@ -1335,6 +1343,7 @@ issue_1537: {
issue_1562: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
var v = 1, B = 2;
@@ -1363,3 +1372,153 @@ issue_1562: {
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;
}
}
issue_1631_1: {
options = {
cascade: true,
collapse_vars: true,
hoist_funs: true,
join_vars: true,
sequences: true,
side_effects: true,
}
input: {
var pc = 0;
function f(x) {
pc = 200;
return 100;
}
function x() {
var t = f();
pc += t;
return pc;
}
console.log(x());
}
expect: {
function f(x) {
return pc = 200, 100;
}
function x() {
var t = f();
return pc += t;
}
var pc = 0;
console.log(x());
}
expect_stdout: "300"
}
issue_1631_2: {
options = {
cascade: true,
collapse_vars: true,
hoist_funs: true,
join_vars: true,
sequences: true,
side_effects: true,
}
input: {
var a = 0, b = 1;
function f() {
a = 2;
return 4;
}
function g() {
var t = f();
b = a + t;
return b;
}
console.log(g());
}
expect: {
function f() {
return a = 2, 4;
}
function g() {
var t = f();
return b = a + t;
}
var a = 0, b = 1;
console.log(g());
}
expect_stdout: "6"
}
issue_1631_3: {
options = {
cascade: true,
collapse_vars: true,
hoist_funs: true,
join_vars: true,
sequences: true,
side_effects: true,
}
input: {
function g() {
var a = 0, b = 1;
function f() {
a = 2;
return 4;
}
var t = f();
b = a + t;
return b;
}
console.log(g());
}
expect: {
function g() {
function f() {
return a = 2, 4;
}
var a = 0, b = 1, t = f();
return b = a + t;
}
console.log(g());
}
expect_stdout: "6"
}

View File

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

View File

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

View File

@@ -200,6 +200,7 @@ negative_zero: {
1 / (-0)
);
}
expect_stdout: true
}
positive_zero: {
@@ -220,6 +221,7 @@ positive_zero: {
1 / (0)
);
}
expect_stdout: true
}
unsafe_constant: {
@@ -243,6 +245,7 @@ unsafe_constant: {
(void 0).a
);
}
expect_stdout: true
}
unsafe_object: {
@@ -266,6 +269,7 @@ unsafe_object: {
1..b + 1
);
}
expect_stdout: true
}
unsafe_object_nested: {
@@ -289,6 +293,7 @@ unsafe_object_nested: {
2
);
}
expect_stdout: true
}
unsafe_object_complex: {
@@ -312,6 +317,7 @@ unsafe_object_complex: {
2
);
}
expect_stdout: true
}
unsafe_object_repeated: {
@@ -335,6 +341,7 @@ unsafe_object_repeated: {
1..b + 1
);
}
expect_stdout: true
}
unsafe_object_accessor: {
@@ -384,6 +391,7 @@ unsafe_function: {
({a:{b:1},b:function(){}}).a.b + 1
);
}
expect_stdout: true
}
unsafe_integer_key: {
@@ -411,6 +419,7 @@ unsafe_integer_key: {
1["1"] + 1
);
}
expect_stdout: true
}
unsafe_integer_key_complex: {
@@ -438,6 +447,7 @@ unsafe_integer_key_complex: {
2
);
}
expect_stdout: true
}
unsafe_float_key: {
@@ -465,6 +475,7 @@ unsafe_float_key: {
1["3.14"] + 1
);
}
expect_stdout: true
}
unsafe_float_key_complex: {
@@ -492,6 +503,7 @@ unsafe_float_key_complex: {
2
);
}
expect_stdout: true
}
unsafe_array: {
@@ -527,6 +539,7 @@ unsafe_array: {
(void 0)[1] + 1
);
}
expect_stdout: true
}
unsafe_string: {
@@ -554,6 +567,7 @@ unsafe_string: {
"11"
);
}
expect_stdout: true
}
unsafe_array_bad_index: {
@@ -575,6 +589,7 @@ unsafe_array_bad_index: {
[1, 2, 3, 4][3.14] + 1
);
}
expect_stdout: true
}
unsafe_string_bad_index: {
@@ -596,6 +611,7 @@ unsafe_string_bad_index: {
"1234"[3.14] + 1
);
}
expect_stdout: true
}
unsafe_prototype_function: {
@@ -642,6 +658,7 @@ call_args: {
console.log(1);
+(1, 1);
}
expect_stdout: true
}
call_args_drop_param: {
@@ -663,6 +680,7 @@ call_args_drop_param: {
console.log(1);
+(b, 1);
}
expect_stdout: true
}
in_boolean_context: {
@@ -700,4 +718,74 @@ in_boolean_context: {
(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((a(), b(), 6));
}
expect_stdout: true
}
iifes_returning_constants_keep_fargs_false: {
@@ -73,6 +74,7 @@ iifes_returning_constants_keep_fargs_false: {
console.log(6);
console.log((a(), b(), 6));
}
expect_stdout: true
}
issue_485_crashing_1530: {

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,6 +14,7 @@ issue_1321_no_debug: {
x["a"] = 2 * x.b;
console.log(x.b, x["a"]);
}
expect_stdout: true
}
issue_1321_debug: {
@@ -33,6 +34,7 @@ issue_1321_debug: {
x["_$foo$_"] = 2 * x.a;
console.log(x.a, x["_$foo$_"]);
}
expect_stdout: true
}
issue_1321_with_quoted: {
@@ -51,4 +53,5 @@ issue_1321_with_quoted: {
x["b"] = 2 * x.a;
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_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;
if (CONST_FOO_ANN) console.log('reachable');
}
expect_stdout: true
}
drop_console_2: {
@@ -225,6 +226,7 @@ issue_1254_negate_iife_true: {
})()();
}
expect_exact: '(function(){return function(){console.log("test")}})()();'
expect_stdout: true
}
issue_1254_negate_iife_nested: {
@@ -240,6 +242,7 @@ issue_1254_negate_iife_nested: {
})()()()()();
}
expect_exact: '(function(){return function(){console.log("test")}})()()()()();'
expect_stdout: true
}
conditional: {

View File

@@ -29,4 +29,5 @@ dont_mangle_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: {
options = {
collapse_vars: true,
toplevel: true,
};
input: {
var c = a; c();

View File

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

View File

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

View File

@@ -166,6 +166,7 @@ keep_collapse_const_in_own_block_scope: {
console.log(i);
console.log(c);
}
expect_stdout: true
}
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(c);
}
expect_stdout: true
}
evaluate: {

View File

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

View File

@@ -58,6 +58,7 @@ reduce_vars: {
})();
console.log(2);
}
expect_stdout: true
}
modified: {
@@ -166,7 +167,7 @@ modified: {
console.log(A ? 'yes' : 'no');
console.log(B ? 'yes' : 'no');
}
}
}
}
unsafe_evaluate: {
@@ -401,6 +402,7 @@ iife: {
console.log(0, 1 * b, 5);
}(1, 2, 3);
}
expect_stdout: true
}
iife_new: {
@@ -420,6 +422,7 @@ iife_new: {
console.log(0, 1 * b, 5);
}(1, 2, 3);
}
expect_stdout: true
}
multi_def: {
@@ -707,6 +710,7 @@ toplevel_on: {
expect: {
console.log(3);
}
expect_stdout: true
}
toplevel_off: {
@@ -724,6 +728,7 @@ toplevel_off: {
var x = 3;
console.log(x);
}
expect_stdout: true
}
toplevel_on_loops_1: {
@@ -751,6 +756,7 @@ toplevel_on_loops_1: {
})();
while (x);
}
expect_stdout: true
}
toplevel_off_loops_1: {
@@ -779,6 +785,7 @@ toplevel_off_loops_1: {
bar();
while (x);
}
expect_stdout: true
}
toplevel_on_loops_2: {
@@ -1121,6 +1128,7 @@ defun_label: {
}(2));
}();
}
expect_stdout: true
}
double_reference: {
@@ -1164,6 +1172,7 @@ iife_arguments_1: {
return f;
});
}
expect_stdout: true
}
iife_arguments_2: {
@@ -1186,6 +1195,7 @@ iife_arguments_2: {
}() === arguments[0]);
})();
}
expect_stdout: true
}
iife_eval_1: {
@@ -1207,6 +1217,7 @@ iife_eval_1: {
return f;
});
}
expect_stdout: true
}
iife_eval_2: {
@@ -1230,6 +1241,7 @@ iife_eval_2: {
console.log(x() === eval("x"));
})();
}
expect_stdout: true
}
iife_func_side_effects: {
@@ -1326,4 +1338,29 @@ issue_1595_4: {
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: {
options = { screw_ie8: true };
options = {
screw_ie8: true,
}
beautify = {
screw_ie8: true,
ascii_only: true
};
input: f("\v");
expect_exact: 'f("\\v");';
ascii_only: true,
}
input: {
f("\v");
}
expect_exact: 'f("\\v");'
}
dont_screw: {
options = { screw_ie8: false };
beautify = { screw_ie8: false, ascii_only: true };
input: f("\v");
expect_exact: 'f("\\x0B");';
options = {
screw_ie8: false,
}
beautify = {
screw_ie8: false,
ascii_only: true,
}
input: {
f("\v");
}
expect_exact: 'f("\\x0B");'
}
do_screw_constants: {
@@ -119,6 +128,7 @@ do_screw_try_catch_undefined: {
return void 0===o
}
}
expect_stdout: true
}
dont_screw_try_catch_undefined: {
@@ -147,6 +157,7 @@ dont_screw_try_catch_undefined: {
return n === undefined
}
}
expect_stdout: true
}
reduce_vars: {
@@ -199,6 +210,7 @@ issue_1586_1: {
}
}
expect_exact: "function f(){try{}catch(c){console.log(c.message)}}"
expect_stdout: true
}
issue_1586_2: {
@@ -217,4 +229,5 @@ issue_1586_2: {
}
}
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) {}
with (x = 5, obj);
}
expect_stdout: true
}
lift_sequences_1: {
@@ -103,15 +104,18 @@ lift_sequences_1: {
lift_sequences_2: {
options = { sequences: true, evaluate: true };
input: {
var foo, bar;
var foo = 1, bar;
foo.x = (foo = {}, 10);
bar = (bar = {}, 10);
console.log(foo, bar);
}
expect: {
var foo, bar;
var foo = 1, bar;
foo.x = (foo = {}, 10),
bar = {}, bar = 10;
bar = {}, bar = 10,
console.log(foo, bar);
}
expect_stdout: true
}
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: {
options = { sequences: true };
input: {
@@ -230,6 +251,7 @@ negate_iife_for: {
for (!function() {}(), i = 0; i < 5; i++) console.log(i);
for (function() {}(); i < 5; i++) console.log(i);
}
expect_stdout: true
}
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

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

View File

@@ -152,7 +152,7 @@ describe("bin/uglifyjs", function () {
});
});
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) {
if (err) throw err;

View File

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

View File

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

View File

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

View File

@@ -6,10 +6,22 @@ var U = require("../tools/node");
var path = require("path");
var fs = require("fs");
var assert = require("assert");
var vm = require("vm");
var tests_dir = path.dirname(module.filename);
var failures = 0;
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();
if (failures) {
@@ -71,10 +83,15 @@ function test_directory(dir) {
}
function as_toplevel(input, mangle_options) {
if (input instanceof U.AST_BlockStatement) input = input.body;
else if (input instanceof U.AST_Statement) input = [ input ];
else throw new Error("Unsupported input syntax");
var toplevel = new U.AST_Toplevel({ body: input });
if (!(input instanceof U.AST_BlockStatement))
throw new Error("Unsupported input syntax");
for (var i = 0; i < input.body.length; i++) {
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);
return toplevel;
}
@@ -97,7 +114,7 @@ function run_compress_tests() {
U.AST_Node.warn_function = function(text) {
warnings_emitted.push("WARN: " + text);
};
options.warnings = true;
if (!options.warnings) options.warnings = true;
}
var cmp = new U.Compressor(options, true);
var output_options = test.beautify || {};
@@ -165,6 +182,36 @@ function run_compress_tests() {
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));
@@ -215,9 +262,9 @@ function parse_test(file) {
}
function read_string(stat) {
if (stat.TYPE === "SimpleStatement") {
if (stat.TYPE == "SimpleStatement") {
var body = stat.body;
out: switch(body.TYPE) {
switch(body.TYPE) {
case "String":
return body.value;
case "Array":
@@ -243,23 +290,26 @@ function parse_test(file) {
return true;
}
if (node instanceof U.AST_LabeledStatement) {
var label = node.label;
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}]", {
name: node.label.name,
line: node.label.start.line,
col: node.label.start.col
name: label.name,
line: label.start.line,
col: label.start.col
})
);
var stat = node.body;
if (stat instanceof U.AST_BlockStatement) {
if (stat.body.length == 1) stat = stat.body[0];
else if (stat.body.length == 0) stat = new U.AST_EmptyStatement();
}
if (node.label.name === "expect_exact") {
test[node.label.name] = read_string(stat);
if (label.name == "expect_exact") {
test[label.name] = read_string(stat);
} else if (label.name == "expect_stdout") {
if (stat.TYPE == "SimpleStatement" && stat.body instanceof U.AST_Boolean) {
test[label.name] = stat.body.value;
} else {
test[label.name] = read_string(stat) + "\n";
}
} else {
test[node.label.name] = stat;
test[label.name] = stat;
}
return true;
}
@@ -281,3 +331,19 @@ function evaluate(code) {
code = make_code(code, { beautify: true });
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;
}
}

View File

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

View File

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