Merge branch 'master' into harmony-v2.8.13

This commit is contained in:
alexlamsl
2017-03-18 02:52:45 +08:00
19 changed files with 689 additions and 246 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

@@ -1320,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);
}, },
@@ -1352,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);
}); });
}; };
@@ -389,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, {
@@ -409,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).optimize(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 });
@@ -524,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;
@@ -561,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;
} }
@@ -937,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) {
@@ -1224,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") {
@@ -1236,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){
@@ -1254,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) {
@@ -1269,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(){
@@ -1345,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));
}); });
@@ -1518,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);
@@ -1687,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]);
@@ -1706,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;
@@ -2102,7 +2109,7 @@ merge(Compressor.prototype, {
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) {
@@ -2198,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
@@ -2362,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;
}); });
@@ -2404,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,
@@ -2417,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,
@@ -2433,27 +2429,25 @@ 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);
}
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 });
} }
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); if_break_in_loop(self, compressor);
@@ -2469,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 = [];
@@ -2479,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);
@@ -2487,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;
@@ -2506,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) {
@@ -2517,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
@@ -2533,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
@@ -2551,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
@@ -2561,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, {
operator: "&&", condition: make_node(AST_Binary, self.condition, {
left: self.condition, operator: "&&",
right: self.body.condition left: self.condition,
}).transform(compressor); right: self.body.condition
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) {
@@ -2580,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)) {
@@ -2590,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;
}); });
@@ -2614,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;
@@ -2666,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);
@@ -2704,7 +2702,8 @@ 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.name instanceof AST_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);
@@ -2713,6 +2712,7 @@ merge(Compressor.prototype, {
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, {
@@ -2903,15 +2903,14 @@ merge(Compressor.prototype, {
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, {
@@ -2919,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) {
@@ -2960,7 +2959,7 @@ 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);
} }
} }
if (exp instanceof AST_Function && !self.expression.is_generator) { if (exp instanceof AST_Function && !self.expression.is_generator) {
@@ -3106,8 +3105,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":
@@ -3120,7 +3118,15 @@ 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);
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) { function has_side_effects_or_prop_access(node, compressor) {
@@ -3158,16 +3164,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
@@ -3252,48 +3248,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,
@@ -3305,12 +3301,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) {
@@ -3462,9 +3457,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)) {
@@ -3561,7 +3556,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){
@@ -3576,11 +3576,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")) {
@@ -3588,18 +3588,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);
} }
} }
} }
@@ -3663,10 +3665,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;
@@ -3680,8 +3683,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 {
@@ -3689,9 +3692,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,
@@ -3871,7 +3873,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){
@@ -3910,13 +3917,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));
@@ -3962,23 +3973,18 @@ 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}
if (result !== segment && (result + "").length <= segment.print_to_string().length + "${}".length) {
// 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 + self.segments[++i].value;
continue; continue;
} }
// 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
segments[segments.length - 1].value = segments[segments.length - 1].value + result[1] + self.segments[++i].value;
} else {
segments.push(self.segments[i]);
} }
segments.push(segment);
} }
self.segments = segments; self.segments = segments;

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

View File

@@ -1152,7 +1152,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() {
@@ -1321,6 +1322,7 @@ collapse_vars_regexp: {
issue_1537: { issue_1537: {
options = { options = {
collapse_vars: true, collapse_vars: true,
toplevel: true,
} }
input: { input: {
var k = ''; var k = '';
@@ -1335,6 +1337,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 +1352,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 +1367,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 +1382,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 +1397,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 +1416,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 +1435,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 +1464,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

@@ -49,4 +49,3 @@ mangle_keep_fnames_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

@@ -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

@@ -157,16 +157,20 @@ computed_property_names_evaluated_2: {
shorthand_properties: { shorthand_properties: {
mangle = true; mangle = true;
input: (function() { input: {
var prop = 1; (function() {
const value = {prop}; var prop = 1;
return value; const value = {prop};
})(); return value;
expect: (function() { })();
var n = 1; }
const r = {prop:n}; expect: {
return r; (function() {
})(); var n = 1;
const r = {prop:n};
return r;
})();
}
} }
concise_methods: { concise_methods: {
@@ -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: {
@@ -507,4 +509,4 @@ variable_as_computed_property: {
} }
} }
expect_exact: "function getLine(header){return{[header]:{}}}" expect_exact: "function getLine(header){return{[header]:{}}}"
} }

View File

@@ -1327,3 +1327,27 @@ issue_1595_4: {
})(3, 4, 5); })(3, 4, 5);
} }
} }
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: {

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

@@ -0,0 +1,129 @@
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);
}
}
booleans_global_defs: {
options = {
booleans: true,
evaluate: true,
global_defs: {
A: true,
},
}
input: {
console.log(A == 1);
}
expect: {
console.log(!0);
}
}
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,6 +6,7 @@ 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;
@@ -71,10 +72,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 +171,36 @@ function run_compress_tests() {
failed_files[file] = 1; failed_files[file] = 1;
} }
} }
if (test.expect_stdout) {
var stdout = run_code(input_code);
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));
@@ -215,9 +251,9 @@ function parse_test(file) {
} }
function read_string(stat) { function read_string(stat) {
if (stat.TYPE === "SimpleStatement") { if (stat.TYPE == "SimpleStatement") {
var body = stat.body; var body = stat.body;
out: switch(body.TYPE) { switch(body.TYPE) {
case "String": case "String":
return body.value; return body.value;
case "Array": case "Array":
@@ -243,23 +279,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;
test[node.label.name] = read_string(stat); } else {
test[label.name] = read_string(stat) + "\n";
}
} else { } else {
test[node.label.name] = stat; test[label.name] = stat;
} }
return true; return true;
} }
@@ -281,3 +320,23 @@ 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;
}
}
function same_stdout(expected, actual) {
return typeof expected == typeof actual && expected.toString() == actual.toString();
}