Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36b2d35bf3 | ||
|
|
79c60032a5 | ||
|
|
a3754068dd | ||
|
|
2ba5f391e0 | ||
|
|
87119e44a0 | ||
|
|
b499e03f82 | ||
|
|
a478f275e4 | ||
|
|
e9e76dcf04 | ||
|
|
0dcedad2d5 | ||
|
|
36a430cd1e | ||
|
|
41a6eb892a | ||
|
|
91d87ae663 | ||
|
|
5beb7e4797 | ||
|
|
46caaa82ba | ||
|
|
5d258259a4 | ||
|
|
14c35739dd | ||
|
|
f5ceff6e4b | ||
|
|
4d6771b9b1 |
@@ -87,6 +87,7 @@ a double dash to prevent input files being used as option arguments:
|
||||
`wrap_iife` Wrap IIFEs in parenthesis. Note: you may
|
||||
want to disable `negate_iife` under
|
||||
compressor options.
|
||||
-O, --output-opts [options] Specify output options (`beautify` disabled by default).
|
||||
-o, --output <file> Output file path (default STDOUT). Specify `ast` or
|
||||
`spidermonkey` to write UglifyJS or SpiderMonkey AST
|
||||
as JSON to STDOUT respectively.
|
||||
@@ -735,6 +736,8 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
|
||||
annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For
|
||||
example: `/*@__PURE__*/foo();`
|
||||
|
||||
- `strings` (default: `true`) -- compact string concatenations.
|
||||
|
||||
- `switches` (default: `true`) -- de-duplicate and remove unreachable `switch` branches
|
||||
|
||||
- `toplevel` (default: `false`) -- drop unreferenced functions (`"funcs"`) and/or
|
||||
|
||||
@@ -36,6 +36,7 @@ program.option("-c, --compress [options]", "Enable compressor/specify compressor
|
||||
program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js());
|
||||
program.option("--mangle-props [options]", "Mangle properties/specify mangler options.", parse_js());
|
||||
program.option("-b, --beautify [options]", "Beautify output/specify output options.", parse_js());
|
||||
program.option("-O, --output-opts [options]", "Output options (beautify disabled).", parse_js());
|
||||
program.option("-o, --output <file>", "Output file (default STDOUT).");
|
||||
program.option("--comments [filter]", "Preserve copyright comments in the output.");
|
||||
program.option("--config-file <file>", "Read minify() options from JSON file.");
|
||||
@@ -93,6 +94,10 @@ if (program.beautify) {
|
||||
options.output.beautify = true;
|
||||
}
|
||||
}
|
||||
if (program.outputOpts) {
|
||||
if (program.beautify) fatal("--beautify cannot be used with --output-opts");
|
||||
options.output = typeof program.outputOpts == "object" ? program.outputOpts : {};
|
||||
}
|
||||
if (program.comments) {
|
||||
if (typeof options.output != "object") options.output = {};
|
||||
options.output.comments = typeof program.comments == "string" ? program.comments : "some";
|
||||
|
||||
380
lib/compress.js
380
lib/compress.js
@@ -83,6 +83,7 @@ function Compressor(options, false_by_default) {
|
||||
reduce_vars : !false_by_default,
|
||||
sequences : !false_by_default,
|
||||
side_effects : !false_by_default,
|
||||
strings : !false_by_default,
|
||||
switches : !false_by_default,
|
||||
top_retain : null,
|
||||
toplevel : !!(options && options["top_retain"]),
|
||||
@@ -1093,6 +1094,7 @@ merge(Compressor.prototype, {
|
||||
collapse(statements, compressor);
|
||||
}
|
||||
} while (CHANGED && max_iter-- > 0);
|
||||
return statements;
|
||||
|
||||
function find_loop_scope_try() {
|
||||
var node = compressor.self(), level = 0;
|
||||
@@ -1149,11 +1151,10 @@ merge(Compressor.prototype, {
|
||||
if (node.single_use && parent instanceof AST_VarDef && parent.value === node) return node;
|
||||
// Replace variable with assignment when found
|
||||
var hit_rhs;
|
||||
if (can_replace
|
||||
&& !(node instanceof AST_SymbolDeclaration)
|
||||
if (!(node instanceof AST_SymbolDeclaration)
|
||||
&& (scan_lhs && lhs.equivalent_to(node)
|
||||
|| scan_rhs && (hit_rhs = scan_rhs(node, this)))) {
|
||||
if (stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) {
|
||||
if (!can_replace || stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) {
|
||||
if (!hit_rhs || !value_def) abort = true;
|
||||
return node;
|
||||
}
|
||||
@@ -1356,7 +1357,10 @@ merge(Compressor.prototype, {
|
||||
return node.operator != "=" && lhs.equivalent_to(node.left);
|
||||
}
|
||||
if (node instanceof AST_Call) {
|
||||
return lhs instanceof AST_PropAccess && lhs.equivalent_to(node.expression);
|
||||
if (!(lhs instanceof AST_PropAccess)) return false;
|
||||
if (!lhs.equivalent_to(node.expression)) return false;
|
||||
var rhs = get_rvalue(candidate);
|
||||
return !(rhs instanceof AST_Function && !rhs.contains_this());
|
||||
}
|
||||
if (node instanceof AST_Debugger) return true;
|
||||
if (node instanceof AST_Defun) return funarg && lhs.name === node.name.name;
|
||||
@@ -1367,7 +1371,7 @@ merge(Compressor.prototype, {
|
||||
if (replace_all) return false;
|
||||
return node instanceof AST_SymbolRef
|
||||
&& !node.is_declared(compressor)
|
||||
&& !(parent instanceof AST_Assign && parent.left === node);
|
||||
&& !(parent instanceof AST_Assign && parent.operator == "=" && parent.left === node);
|
||||
}
|
||||
|
||||
function in_conditional(node, parent) {
|
||||
@@ -1378,7 +1382,34 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
function is_last_node(node, parent) {
|
||||
if (node instanceof AST_Call) return true;
|
||||
if (node instanceof AST_Call) {
|
||||
var fn = node.expression;
|
||||
if (fn instanceof AST_SymbolRef) fn = fn.fixed_value();
|
||||
if (!(fn instanceof AST_Lambda)) return true;
|
||||
if (fn.collapse_scanning) return false;
|
||||
fn.collapse_scanning = true;
|
||||
var replace = can_replace;
|
||||
can_replace = false;
|
||||
var after = stop_after;
|
||||
var if_hit = stop_if_hit;
|
||||
var rhs_fn = scan_rhs;
|
||||
for (var i = 0; !abort && i < fn.body.length; i++) {
|
||||
var stat = fn.body[i];
|
||||
if (stat instanceof AST_Return) {
|
||||
if (stat.value) stat.value.transform(scanner);
|
||||
break;
|
||||
}
|
||||
stat.transform(scanner);
|
||||
}
|
||||
scan_rhs = rhs_fn;
|
||||
stop_if_hit = if_hit;
|
||||
stop_after = after;
|
||||
can_replace = replace;
|
||||
delete fn.collapse_scanning;
|
||||
if (!abort) return false;
|
||||
abort = false;
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_Exit) {
|
||||
if (in_try) {
|
||||
if (in_try.bfinally) return true;
|
||||
@@ -2482,6 +2513,61 @@ merge(Compressor.prototype, {
|
||||
node.DEFMETHOD("is_truthy", func);
|
||||
});
|
||||
|
||||
// is_negative_zero()
|
||||
// return true if the node may represent -0
|
||||
(function(def) {
|
||||
def(AST_Node, return_true);
|
||||
def(AST_Array, return_false);
|
||||
function binary(op, left, right) {
|
||||
switch (op) {
|
||||
case "-":
|
||||
return left.is_negative_zero()
|
||||
&& (!(right instanceof AST_Constant) || right.value == 0);
|
||||
case "&&":
|
||||
case "||":
|
||||
return left.is_negative_zero() || right.is_negative_zero();
|
||||
case "*":
|
||||
case "/":
|
||||
return true;
|
||||
case "%":
|
||||
return left.is_negative_zero();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
def(AST_Assign, function() {
|
||||
var op = this.operator;
|
||||
if (op == "=") return this.right.is_negative_zero();
|
||||
return binary(op.slice(0, -1), this.left, this.right);
|
||||
});
|
||||
def(AST_Binary, function() {
|
||||
return binary(this.operator, this.left, this.right);
|
||||
});
|
||||
def(AST_Constant, function() {
|
||||
return this.value == 0 && 1 / this.value < 0;
|
||||
});
|
||||
def(AST_Lambda, return_false);
|
||||
def(AST_Object, return_false);
|
||||
def(AST_RegExp, return_false);
|
||||
def(AST_Sequence, function() {
|
||||
return this.tail_node().is_negative_zero();
|
||||
});
|
||||
def(AST_SymbolRef, function() {
|
||||
var fixed = this.fixed_value();
|
||||
if (!fixed) return true;
|
||||
this.is_negative_zero = return_true;
|
||||
var result = fixed.is_negative_zero();
|
||||
delete this.is_negative_zero;
|
||||
return result;
|
||||
});
|
||||
def(AST_UnaryPrefix, function() {
|
||||
return this.operator == "+" && this.expression.is_negative_zero()
|
||||
|| this.operator == "-";
|
||||
});
|
||||
})(function(node, func) {
|
||||
node.DEFMETHOD("is_negative_zero", func);
|
||||
});
|
||||
|
||||
// may_throw_on_access()
|
||||
// returns true if this node may be null, undefined or contain `AST_Accessor`
|
||||
(function(def) {
|
||||
@@ -3768,12 +3854,12 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
OPT(AST_Block, function(self, compressor) {
|
||||
tighten_body(self.body, compressor);
|
||||
self.body = tighten_body(self.body, compressor);
|
||||
return self;
|
||||
});
|
||||
|
||||
OPT(AST_BlockStatement, function(self, compressor) {
|
||||
tighten_body(self.body, compressor);
|
||||
self.body = tighten_body(self.body, compressor);
|
||||
switch (self.body.length) {
|
||||
case 1: return self.body[0];
|
||||
case 0: return make_node(AST_EmptyStatement, self);
|
||||
@@ -3782,7 +3868,7 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
OPT(AST_Lambda, function(self, compressor) {
|
||||
tighten_body(self.body, compressor);
|
||||
self.body = tighten_body(self.body, compressor);
|
||||
if (compressor.option("side_effects")
|
||||
&& self.body.length == 1
|
||||
&& self.body[0] === compressor.has_directive("use strict")) {
|
||||
@@ -4196,7 +4282,7 @@ merge(Compressor.prototype, {
|
||||
if (node instanceof AST_Assign) {
|
||||
if (node.write_only === "p" && node.right.may_throw_on_access(compressor)) return;
|
||||
var right = get_rhs(node);
|
||||
if (init && node.write_only && node_def.scope === self && !right.has_side_effects(compressor)) {
|
||||
if (init && node.write_only === true && node_def.scope === self && !right.has_side_effects(compressor)) {
|
||||
initializations.add(node_def.id, right);
|
||||
} else {
|
||||
right.walk(tw);
|
||||
@@ -4395,7 +4481,7 @@ merge(Compressor.prototype, {
|
||||
var ev = value.is_truthy() || value.tail_node().evaluate(compressor);
|
||||
if (!ev) {
|
||||
value = value.drop_side_effect_free(compressor);
|
||||
if (node.value !== value) node.value = value ? make_sequence(node.value, [
|
||||
node.value = value ? make_sequence(node.value, [
|
||||
value,
|
||||
make_node(AST_Number, node.value, {
|
||||
value: 0
|
||||
@@ -4403,7 +4489,7 @@ merge(Compressor.prototype, {
|
||||
]) : null;
|
||||
} else if (ev && !(ev instanceof AST_Node)) {
|
||||
value = value.drop_side_effect_free(compressor);
|
||||
if (node.value !== value) node.value = value ? make_sequence(node.value, [
|
||||
node.value = value ? make_sequence(node.value, [
|
||||
value,
|
||||
make_node(AST_Number, node.value, {
|
||||
value: 1
|
||||
@@ -5286,7 +5372,7 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
OPT(AST_Try, function(self, compressor) {
|
||||
tighten_body(self.body, compressor);
|
||||
self.body = tighten_body(self.body, compressor);
|
||||
if (self.bcatch && self.bfinally && all(self.bfinally.body, is_empty)) self.bfinally = null;
|
||||
if (compressor.option("dead_code") && all(self.body, is_empty)) {
|
||||
var body = [];
|
||||
@@ -5341,25 +5427,32 @@ merge(Compressor.prototype, {
|
||||
return self.definitions.length ? self : make_node(AST_EmptyStatement, self);
|
||||
});
|
||||
|
||||
AST_Call.DEFMETHOD("lift_sequences", function(compressor) {
|
||||
if (!compressor.option("sequences")) return this;
|
||||
var exp = this.expression;
|
||||
if (!(exp instanceof AST_Sequence)) return this;
|
||||
var tail = exp.tail_node();
|
||||
if (needs_unbinding(compressor, tail) && !(this instanceof AST_New)) return this;
|
||||
var expressions = exp.expressions.slice(0, -1);
|
||||
var node = this.clone();
|
||||
node.expression = tail;
|
||||
expressions.push(node);
|
||||
return make_sequence(this, expressions).optimize(compressor);
|
||||
});
|
||||
function lift_sequence_in_expression(node, compressor) {
|
||||
var exp = node.expression;
|
||||
if (!(exp instanceof AST_Sequence)) return node;
|
||||
var x = exp.expressions.slice();
|
||||
var e = node.clone();
|
||||
e.expression = x.pop();
|
||||
x.push(e);
|
||||
return make_sequence(node, x);
|
||||
}
|
||||
|
||||
OPT(AST_Call, function(self, compressor) {
|
||||
var seq = self.lift_sequences(compressor);
|
||||
if (seq !== self) {
|
||||
return seq;
|
||||
}
|
||||
var exp = self.expression;
|
||||
if (compressor.option("sequences")) {
|
||||
if (exp instanceof AST_PropAccess) {
|
||||
var seq = lift_sequence_in_expression(exp, compressor);
|
||||
if (seq !== exp) {
|
||||
var call = self.clone();
|
||||
call.expression = seq.expressions.pop();
|
||||
seq.expressions.push(call);
|
||||
return seq.optimize(compressor);
|
||||
}
|
||||
} else if (!needs_unbinding(compressor, exp.tail_node())) {
|
||||
var seq = lift_sequence_in_expression(self, compressor);
|
||||
if (seq !== self) return seq.optimize(compressor);
|
||||
}
|
||||
}
|
||||
var fn = exp;
|
||||
if (compressor.option("reduce_vars") && fn instanceof AST_SymbolRef) {
|
||||
fn = fn.fixed_value();
|
||||
@@ -5930,9 +6023,9 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
OPT(AST_New, function(self, compressor) {
|
||||
var seq = self.lift_sequences(compressor);
|
||||
if (seq !== self) {
|
||||
return seq;
|
||||
if (compressor.option("sequences")) {
|
||||
var seq = lift_sequence_in_expression(self, compressor);
|
||||
if (seq !== self) return seq.optimize(compressor);
|
||||
}
|
||||
if (compressor.option("unsafe")) {
|
||||
var exp = self.expression;
|
||||
@@ -5988,21 +6081,16 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
});
|
||||
|
||||
AST_Unary.DEFMETHOD("lift_sequences", function(compressor) {
|
||||
if (compressor.option("sequences") && this.expression instanceof AST_Sequence) {
|
||||
var x = this.expression.expressions.slice();
|
||||
var e = this.clone();
|
||||
e.expression = x.pop();
|
||||
x.push(e);
|
||||
return make_sequence(this, x).optimize(compressor);
|
||||
}
|
||||
return this;
|
||||
});
|
||||
|
||||
OPT(AST_UnaryPostfix, function(self, compressor) {
|
||||
return self.lift_sequences(compressor);
|
||||
if (compressor.option("sequences")) {
|
||||
var seq = lift_sequence_in_expression(self, compressor);
|
||||
if (seq !== self) return seq.optimize(compressor);
|
||||
}
|
||||
return self;
|
||||
});
|
||||
|
||||
var SIGN_OPS = makePredicate("+ -");
|
||||
var MULTIPLICATIVE_OPS = makePredicate("* / %");
|
||||
OPT(AST_UnaryPrefix, function(self, compressor) {
|
||||
var e = self.expression;
|
||||
if (compressor.option("evaluate")
|
||||
@@ -6017,9 +6105,9 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
return make_sequence(self, [ e, make_node(AST_True, self) ]).optimize(compressor);
|
||||
}
|
||||
var seq = self.lift_sequences(compressor);
|
||||
if (seq !== self) {
|
||||
return seq;
|
||||
if (compressor.option("sequences")) {
|
||||
var seq = lift_sequence_in_expression(self, compressor);
|
||||
if (seq !== self) return seq.optimize(compressor);
|
||||
}
|
||||
if (compressor.option("side_effects") && self.operator == "void") {
|
||||
e = e.drop_side_effect_free(compressor);
|
||||
@@ -6053,12 +6141,12 @@ merge(Compressor.prototype, {
|
||||
])).optimize(compressor);
|
||||
}
|
||||
}
|
||||
if (self.operator == "-" && e instanceof AST_Infinity) {
|
||||
e = e.transform(compressor);
|
||||
}
|
||||
if (e instanceof AST_Binary
|
||||
&& (self.operator == "+" || self.operator == "-")
|
||||
&& (e.operator == "*" || e.operator == "/" || e.operator == "%")) {
|
||||
if (self.operator == "-" && e instanceof AST_Infinity) e = e.transform(compressor);
|
||||
if (compressor.option("evaluate")
|
||||
&& e instanceof AST_Binary
|
||||
&& SIGN_OPS[self.operator]
|
||||
&& MULTIPLICATIVE_OPS[e.operator]
|
||||
&& (e.left.is_constant() || !e.right.has_side_effects(compressor))) {
|
||||
return make_node(AST_Binary, self, {
|
||||
operator: e.operator,
|
||||
left: make_node(AST_UnaryPrefix, e.left, {
|
||||
@@ -6108,7 +6196,6 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
var indexFns = makePredicate("indexOf lastIndexOf");
|
||||
var minus_zero_op = makePredicate("- * / %");
|
||||
var commutativeOperators = makePredicate("== === != !== * & | ^");
|
||||
function is_object(node) {
|
||||
return node instanceof AST_Array
|
||||
@@ -6131,6 +6218,18 @@ merge(Compressor.prototype, {
|
||||
self.right = tmp;
|
||||
}
|
||||
}
|
||||
function swap_chain() {
|
||||
var rhs = self.right;
|
||||
self.left = make_node(AST_Binary, self, {
|
||||
operator: self.operator,
|
||||
left: self.left,
|
||||
right: rhs.left,
|
||||
start: self.left.start,
|
||||
end: rhs.left.end
|
||||
});
|
||||
self.right = rhs.right;
|
||||
self.left = self.left.transform(compressor);
|
||||
}
|
||||
if (commutativeOperators[self.operator]
|
||||
&& self.right.is_constant()
|
||||
&& !self.left.is_constant()
|
||||
@@ -6141,7 +6240,8 @@ merge(Compressor.prototype, {
|
||||
// result. hence, force switch.
|
||||
reverse();
|
||||
}
|
||||
self = self.lift_sequences(compressor);
|
||||
var seq = self.lift_sequences(compressor);
|
||||
if (seq !== self) return seq;
|
||||
if (compressor.option("assignments") && lazy_op[self.operator]) {
|
||||
var assign = self.right;
|
||||
// a || (a = x) => a = a || x
|
||||
@@ -6281,17 +6381,28 @@ merge(Compressor.prototype, {
|
||||
case ">=": reverse("<="); break;
|
||||
}
|
||||
}
|
||||
if (self.operator == "+") {
|
||||
// x && (y && z) => x && y && z
|
||||
// x || (y || z) => x || y || z
|
||||
if (compressor.option("conditionals")
|
||||
&& lazy_op[self.operator]
|
||||
&& self.right instanceof AST_Binary
|
||||
&& self.operator == self.right.operator) {
|
||||
swap_chain();
|
||||
}
|
||||
if (compressor.option("strings") && self.operator == "+") {
|
||||
// "foo" + 42 + "" => "foo" + 42
|
||||
if (self.right instanceof AST_String
|
||||
&& self.right.value == ""
|
||||
&& self.left.is_string(compressor)) {
|
||||
return self.left.optimize(compressor);
|
||||
}
|
||||
// "" + ("foo" + 42) => "foo" + 42
|
||||
if (self.left instanceof AST_String
|
||||
&& self.left.value == ""
|
||||
&& self.right.is_string(compressor)) {
|
||||
return self.right.optimize(compressor);
|
||||
}
|
||||
// "" + 42 + "foo" => 42 + "foo"
|
||||
if (self.left instanceof AST_Binary
|
||||
&& self.left.operator == "+"
|
||||
&& self.left.left instanceof AST_String
|
||||
@@ -6300,6 +6411,15 @@ merge(Compressor.prototype, {
|
||||
self.left = self.left.right;
|
||||
return self.optimize(compressor);
|
||||
}
|
||||
// "x" + (y + "z") => "x" + y + "z"
|
||||
// x + ("y" + z) => x + "y" + z
|
||||
if (self.right instanceof AST_Binary
|
||||
&& self.operator == self.right.operator
|
||||
&& (self.left.is_string(compressor) && self.right.is_string(compressor)
|
||||
|| self.right.left.is_string(compressor)
|
||||
&& (self.left.is_constant() || !self.right.right.has_side_effects(compressor)))) {
|
||||
swap_chain();
|
||||
}
|
||||
}
|
||||
if (compressor.option("evaluate")) {
|
||||
var associative = true;
|
||||
@@ -6529,7 +6649,10 @@ merge(Compressor.prototype, {
|
||||
&& self.right.is_number(compressor)
|
||||
&& (self.operator != "+"
|
||||
|| self.right.left.is_boolean(compressor)
|
||||
|| self.right.left.is_number(compressor))) {
|
||||
|| self.right.left.is_number(compressor))
|
||||
&& (self.operator != "-" || !self.left.is_negative_zero())
|
||||
&& (self.right.left.is_constant_expression()
|
||||
|| !self.right.right.has_side_effects(compressor))) {
|
||||
self = make_node(AST_Binary, self, {
|
||||
operator: align(self.operator, self.right.operator),
|
||||
left: make_node(AST_Binary, self.left, {
|
||||
@@ -6562,7 +6685,12 @@ merge(Compressor.prototype, {
|
||||
self = make_binary(self, self.left.operator, lhs, self.left.right);
|
||||
} else if (self.left.right instanceof AST_Constant) {
|
||||
var rhs = make_binary(self.left, align(self.left.operator, self.operator), self.left.right, self.right, self.left.right.start, self.right.end);
|
||||
self = make_binary(self, self.left.operator, self.left.left, rhs);
|
||||
if (self.left.operator != "-"
|
||||
|| !self.right.value
|
||||
|| rhs.evaluate(compressor)
|
||||
|| !self.left.left.is_negative_zero()) {
|
||||
self = make_binary(self, self.left.operator, self.left.left, rhs);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -6575,7 +6703,7 @@ merge(Compressor.prototype, {
|
||||
operator: "+",
|
||||
expression: self.right
|
||||
}).optimize(compressor);
|
||||
if (self.right.is_number(compressor) && !may_be_minus_zero(self.right)) return self.right;
|
||||
if (self.right.is_number(compressor) && !self.right.is_negative_zero()) return self.right;
|
||||
}
|
||||
break;
|
||||
// 1 * n => n
|
||||
@@ -6596,7 +6724,7 @@ merge(Compressor.prototype, {
|
||||
operator: "+",
|
||||
expression: self.left
|
||||
}).optimize(compressor);
|
||||
if (self.left.is_number(compressor) && !may_be_minus_zero(self.left)) return self.left;
|
||||
if (self.left.is_number(compressor) && !self.left.is_negative_zero()) return self.left;
|
||||
}
|
||||
break;
|
||||
// n - 0 => n
|
||||
@@ -6662,26 +6790,6 @@ merge(Compressor.prototype, {
|
||||
return node.optimize(compressor);
|
||||
}
|
||||
}
|
||||
// x && (y && z) => x && y && z
|
||||
// x || (y || z) => x || y || z
|
||||
// x + ("y" + z) => x + "y" + z
|
||||
// "x" + (y + "z") => "x" + y + "z"
|
||||
if (self.right instanceof AST_Binary
|
||||
&& self.right.operator == self.operator
|
||||
&& (lazy_op[self.operator]
|
||||
|| (self.operator == "+"
|
||||
&& (self.right.left.is_string(compressor)
|
||||
|| (self.left.is_string(compressor)
|
||||
&& self.right.right.is_string(compressor))))))
|
||||
{
|
||||
self.left = make_node(AST_Binary, self.left, {
|
||||
operator : self.operator,
|
||||
left : self.left,
|
||||
right : self.right.left
|
||||
});
|
||||
self.right = self.right.right;
|
||||
return self.transform(compressor);
|
||||
}
|
||||
return try_evaluate(compressor, self);
|
||||
|
||||
function align(ref, op) {
|
||||
@@ -6751,20 +6859,6 @@ merge(Compressor.prototype, {
|
||||
&& self.left.expression instanceof AST_Number && self.left.expression.value == 1;
|
||||
}
|
||||
}
|
||||
|
||||
function may_be_minus_zero(node) {
|
||||
var ev = node.evaluate(compressor);
|
||||
if (ev instanceof AST_Node) {
|
||||
var op = ev.operator;
|
||||
if (!op) return true;
|
||||
if (ev instanceof AST_Assign) {
|
||||
if (op == "=") return may_be_minus_zero(ev.right);
|
||||
op = op.slice(0, -1);
|
||||
}
|
||||
if (minus_zero_op[op]) return true;
|
||||
if (ev instanceof AST_UnaryPrefix && op == "+") return true;
|
||||
} else if (ev == 0 && 1 / ev < 0) return true;
|
||||
}
|
||||
});
|
||||
|
||||
function recursive_ref(compressor, def) {
|
||||
@@ -7020,7 +7114,7 @@ merge(Compressor.prototype, {
|
||||
return reachable;
|
||||
}
|
||||
|
||||
var ASSIGN_OPS = makePredicate("+ - / * % >> << >>> | ^ &");
|
||||
var ASSIGN_OPS = makePredicate("+ - * / % >> << >>> | ^ &");
|
||||
var ASSIGN_OPS_COMMUTATIVE = makePredicate("* | ^ &");
|
||||
OPT(AST_Assign, function(self, compressor) {
|
||||
if (compressor.option("dead_code")) {
|
||||
@@ -7064,7 +7158,8 @@ merge(Compressor.prototype, {
|
||||
|| parent instanceof AST_UnaryPrefix);
|
||||
}
|
||||
}
|
||||
self = self.lift_sequences(compressor);
|
||||
var seq = self.lift_sequences(compressor);
|
||||
if (seq !== self) return seq;
|
||||
if (!compressor.option("assignments")) return self;
|
||||
if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) {
|
||||
// x = expr1 OP expr2
|
||||
@@ -7129,23 +7224,24 @@ merge(Compressor.prototype, {
|
||||
expressions.push(self);
|
||||
return make_sequence(self, expressions);
|
||||
}
|
||||
var cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
|
||||
if (!cond) {
|
||||
var condition = self.condition.is_truthy() || self.condition.evaluate(compressor);
|
||||
if (!condition) {
|
||||
AST_Node.warn("Condition always false [{file}:{line},{col}]", self.start);
|
||||
return make_sequence(self, [ self.condition, self.alternative ]).optimize(compressor);
|
||||
} else if (!(cond instanceof AST_Node)) {
|
||||
} else if (!(condition instanceof AST_Node)) {
|
||||
AST_Node.warn("Condition always true [{file}:{line},{col}]", self.start);
|
||||
return make_sequence(self, [ self.condition, self.consequent ]).optimize(compressor);
|
||||
}
|
||||
var negated = cond.negate(compressor, first_in_statement(compressor));
|
||||
if (best_of(compressor, cond, negated) === negated) {
|
||||
var negated = condition.negate(compressor, first_in_statement(compressor));
|
||||
if (best_of(compressor, condition, negated) === negated) {
|
||||
self = make_node(AST_Conditional, self, {
|
||||
condition: negated,
|
||||
consequent: self.alternative,
|
||||
alternative: self.consequent
|
||||
});
|
||||
negated = condition;
|
||||
condition = self.condition;
|
||||
}
|
||||
var condition = self.condition;
|
||||
var consequent = self.consequent;
|
||||
var alternative = self.alternative;
|
||||
// x ? x : y => x || y
|
||||
@@ -7233,6 +7329,19 @@ merge(Compressor.prototype, {
|
||||
alternative: alternative
|
||||
});
|
||||
}
|
||||
// x ? (y ? a : b) : a => !x || y ? a : b
|
||||
if (consequent instanceof AST_Conditional
|
||||
&& consequent.consequent.equivalent_to(alternative)) {
|
||||
return make_node(AST_Conditional, self, {
|
||||
condition: make_node(AST_Binary, self, {
|
||||
left: negated,
|
||||
operator: "||",
|
||||
right: consequent.condition
|
||||
}),
|
||||
consequent: alternative,
|
||||
alternative: consequent.alternative
|
||||
});
|
||||
}
|
||||
// x ? a : (y ? a : b) => x || y ? a : b
|
||||
if (alternative instanceof AST_Conditional
|
||||
&& consequent.equivalent_to(alternative.consequent)) {
|
||||
@@ -7246,7 +7355,20 @@ merge(Compressor.prototype, {
|
||||
alternative: alternative.alternative
|
||||
});
|
||||
}
|
||||
// x ? (y, w) : (z, w) => x ? y : z, w
|
||||
// x ? b : (y ? a : b) => !x && y ? a : b
|
||||
if (alternative instanceof AST_Conditional
|
||||
&& consequent.equivalent_to(alternative.alternative)) {
|
||||
return make_node(AST_Conditional, self, {
|
||||
condition: make_node(AST_Binary, self, {
|
||||
left: negated,
|
||||
operator: "&&",
|
||||
right: alternative.condition
|
||||
}),
|
||||
consequent: alternative.consequent,
|
||||
alternative: consequent
|
||||
});
|
||||
}
|
||||
// x ? (a, c) : (b, c) => x ? a : b, c
|
||||
if ((consequent instanceof AST_Sequence || alternative instanceof AST_Sequence)
|
||||
&& consequent.tail_node().equivalent_to(alternative.tail_node())) {
|
||||
return make_sequence(self, [
|
||||
@@ -7258,7 +7380,21 @@ merge(Compressor.prototype, {
|
||||
consequent.tail_node()
|
||||
]).optimize(compressor);
|
||||
}
|
||||
// x ? y || z : z => x && y || z
|
||||
// x ? y && a : a => (!x || y) && a
|
||||
if (consequent instanceof AST_Binary
|
||||
&& consequent.operator == "&&"
|
||||
&& consequent.right.equivalent_to(alternative)) {
|
||||
return make_node(AST_Binary, self, {
|
||||
operator: "&&",
|
||||
left: make_node(AST_Binary, self, {
|
||||
operator: "||",
|
||||
left: negated,
|
||||
right: consequent.left
|
||||
}),
|
||||
right: alternative
|
||||
}).optimize(compressor);
|
||||
}
|
||||
// x ? y || a : a => x && y || a
|
||||
if (consequent instanceof AST_Binary
|
||||
&& consequent.operator == "||"
|
||||
&& consequent.right.equivalent_to(alternative)) {
|
||||
@@ -7272,6 +7408,34 @@ merge(Compressor.prototype, {
|
||||
right: alternative
|
||||
}).optimize(compressor);
|
||||
}
|
||||
// x ? a : y && a => (x || y) && a
|
||||
if (alternative instanceof AST_Binary
|
||||
&& alternative.operator == "&&"
|
||||
&& alternative.right.equivalent_to(consequent)) {
|
||||
return make_node(AST_Binary, self, {
|
||||
operator: "&&",
|
||||
left: make_node(AST_Binary, self, {
|
||||
operator: "||",
|
||||
left: condition,
|
||||
right: alternative.left
|
||||
}),
|
||||
right: consequent
|
||||
}).optimize(compressor);
|
||||
}
|
||||
// x ? a : y || a => !x && y || a
|
||||
if (alternative instanceof AST_Binary
|
||||
&& alternative.operator == "||"
|
||||
&& alternative.right.equivalent_to(consequent)) {
|
||||
return make_node(AST_Binary, self, {
|
||||
operator: "||",
|
||||
left: make_node(AST_Binary, self, {
|
||||
operator: "&&",
|
||||
left: negated,
|
||||
right: alternative.left
|
||||
}),
|
||||
right: consequent
|
||||
}).optimize(compressor);
|
||||
}
|
||||
var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
|
||||
if (is_true(consequent)) {
|
||||
if (is_false(alternative)) {
|
||||
@@ -7423,6 +7587,10 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
OPT(AST_Sub, function(self, compressor) {
|
||||
if (compressor.option("sequences") && compressor.parent().TYPE != "Call") {
|
||||
var seq = lift_sequence_in_expression(self, compressor);
|
||||
if (seq !== self) return seq.optimize(compressor);
|
||||
}
|
||||
var expr = self.expression;
|
||||
var prop = self.property;
|
||||
if (compressor.option("properties")) {
|
||||
@@ -7587,6 +7755,10 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
OPT(AST_Dot, function(self, compressor) {
|
||||
if (compressor.option("sequences") && compressor.parent().TYPE != "Call") {
|
||||
var seq = lift_sequence_in_expression(self, compressor);
|
||||
if (seq !== self) return seq.optimize(compressor);
|
||||
}
|
||||
if (self.property == "arguments" || self.property == "caller") {
|
||||
AST_Node.warn("Function.prototype.{prop} not supported [{file}:{line},{col}]", {
|
||||
prop: self.property,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
||||
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
||||
"license": "BSD-2-Clause",
|
||||
"version": "3.7.4",
|
||||
"version": "3.7.7",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
},
|
||||
|
||||
@@ -16,6 +16,7 @@ holes_and_undefined: {
|
||||
constant_join: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
strings: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
@@ -65,6 +66,7 @@ constant_join: {
|
||||
constant_join_2: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
strings: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
@@ -94,9 +96,11 @@ constant_join_2: {
|
||||
constant_join_3: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
strings: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
var foo, bar, baz;
|
||||
var a = [ null ].join();
|
||||
var b = [ , ].join();
|
||||
var c = [ , 1, , 3 ].join();
|
||||
@@ -111,6 +115,7 @@ constant_join_3: {
|
||||
var l = [ foo, bar + "baz" ].join("");
|
||||
}
|
||||
expect: {
|
||||
var foo, bar, baz;
|
||||
var a = "";
|
||||
var b = "";
|
||||
var c = ",1,,3";
|
||||
|
||||
@@ -131,3 +131,25 @@ issue_3658: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3690: {
|
||||
options = {
|
||||
booleans: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function(a) {
|
||||
return function() {
|
||||
return a = [ this ];
|
||||
}() ? "PASS" : "FAIL";
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(function(a) {
|
||||
return function() {
|
||||
return 1;
|
||||
}() ? "PASS" : "FAIL";
|
||||
}());
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -7464,3 +7464,304 @@ issue_3651: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3671: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a = 0;
|
||||
try {
|
||||
a++;
|
||||
A += 0;
|
||||
a = 1 + a;
|
||||
} catch (e) {
|
||||
console.log(a);
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
var a = 0;
|
||||
try {
|
||||
a++;
|
||||
A += 0;
|
||||
a = 1 + a;
|
||||
} catch (e) {
|
||||
console.log(a);
|
||||
}
|
||||
}
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
call_1: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
(function(a) {
|
||||
a = console;
|
||||
(function() {})();
|
||||
a.log("PASS");
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
(function(a) {
|
||||
(function() {})();
|
||||
(a = console).log("PASS");
|
||||
})();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
call_1_symbol: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
(function(a) {
|
||||
function f() {}
|
||||
a = console;
|
||||
f();
|
||||
a.log(typeof f);
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
(function(a) {
|
||||
function f() {}
|
||||
f();
|
||||
(a = console).log(typeof f);
|
||||
})();
|
||||
}
|
||||
expect_stdout: "function"
|
||||
}
|
||||
|
||||
call_2: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
(function(a) {
|
||||
a = console;
|
||||
(function() {
|
||||
return 42;
|
||||
console.log("FAIL");
|
||||
})();
|
||||
a.log("PASS");
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
(function(a) {
|
||||
(function() {
|
||||
return 42;
|
||||
console.log("FAIL");
|
||||
})();
|
||||
(a = console).log("PASS");
|
||||
})();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
call_2_symbol: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
(function(a) {
|
||||
function f() {
|
||||
return 42;
|
||||
console.log("FAIL");
|
||||
}
|
||||
a = console;
|
||||
f();
|
||||
a.log(typeof f);
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
(function(a) {
|
||||
function f() {
|
||||
return 42;
|
||||
console.log("FAIL");
|
||||
}
|
||||
f();
|
||||
(a = console).log(typeof f);
|
||||
})();
|
||||
}
|
||||
expect_stdout: "function"
|
||||
}
|
||||
|
||||
call_3: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
(function(a) {
|
||||
a = console;
|
||||
(function() {
|
||||
a = {
|
||||
log: function() {
|
||||
console.log("PASS");
|
||||
}
|
||||
}
|
||||
})();
|
||||
a.log("FAIL");
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
(function(a) {
|
||||
a = console;
|
||||
(function() {
|
||||
a = {
|
||||
log: function() {
|
||||
console.log("PASS");
|
||||
}
|
||||
}
|
||||
})();
|
||||
a.log("FAIL");
|
||||
})();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
call_3_symbol: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
(function(a) {
|
||||
function f() {
|
||||
a = {
|
||||
log: function() {
|
||||
console.log(typeof f);
|
||||
}
|
||||
}
|
||||
}
|
||||
a = console;
|
||||
f();
|
||||
a.log("FAIL");
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
(function(a) {
|
||||
function f() {
|
||||
a = {
|
||||
log: function() {
|
||||
console.log(typeof f);
|
||||
}
|
||||
}
|
||||
}
|
||||
a = console;
|
||||
f();
|
||||
a.log("FAIL");
|
||||
})();
|
||||
}
|
||||
expect_stdout: "function"
|
||||
}
|
||||
|
||||
issue_3698_1: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var log = console.log;
|
||||
var a, b = 0, c = 0;
|
||||
(function() {
|
||||
a = b;
|
||||
})(b++, (b++, c++));
|
||||
log(a, b, c);
|
||||
}
|
||||
expect: {
|
||||
var log = console.log;
|
||||
var a, b = 0, c = 0;
|
||||
(function() {
|
||||
a = b;
|
||||
})(b++, (b++, c++));
|
||||
log(a, b, c);
|
||||
}
|
||||
expect_stdout: "2 2 1"
|
||||
}
|
||||
|
||||
issue_3698_2: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
var log = console.log;
|
||||
var a, b = 0, c = 0, d = 1;
|
||||
(function f() {
|
||||
a = b;
|
||||
d-- && f();
|
||||
})(b++, (b++, c++));
|
||||
log(a, b, c, d);
|
||||
}
|
||||
expect: {
|
||||
var log = console.log;
|
||||
var a, b = 0, c = 0, d = 1;
|
||||
(function f() {
|
||||
a = b;
|
||||
d-- && f();
|
||||
})(b++, (b++, c++));
|
||||
log(a, b, c, d);
|
||||
}
|
||||
expect_stdout: "2 2 1 -1"
|
||||
}
|
||||
|
||||
issue_3698_3: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a = 0, b = 0;
|
||||
(function f(c) {
|
||||
{
|
||||
b++;
|
||||
var bar_1 = (b = 1 + b, c = 0);
|
||||
a-- && f();
|
||||
}
|
||||
})();
|
||||
console.log(b);
|
||||
}
|
||||
expect: {
|
||||
var a = 0, b = 0;
|
||||
(function f(c) {
|
||||
var bar_1 = (b = 1 + ++b, c = 0);
|
||||
a-- && f();
|
||||
})();
|
||||
console.log(b);
|
||||
}
|
||||
expect_stdout: "2"
|
||||
}
|
||||
|
||||
issue_3700: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a = "FAIL";
|
||||
try {
|
||||
a = "PASS";
|
||||
(function() {
|
||||
throw 0;
|
||||
})();
|
||||
a = 1 + a;
|
||||
} catch (e) {
|
||||
}
|
||||
console.log(a);
|
||||
}
|
||||
expect: {
|
||||
var a = "FAIL";
|
||||
try {
|
||||
a = "PASS";
|
||||
(function() {
|
||||
throw 0;
|
||||
})();
|
||||
a = 1 + a;
|
||||
} catch (e) {
|
||||
}
|
||||
console.log(a);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -26,7 +26,9 @@ concat_1: {
|
||||
}
|
||||
|
||||
concat_2: {
|
||||
options = {}
|
||||
options = {
|
||||
strings: true,
|
||||
}
|
||||
input: {
|
||||
console.log(
|
||||
1 + (2 + 3),
|
||||
@@ -55,7 +57,9 @@ concat_2: {
|
||||
}
|
||||
|
||||
concat_3: {
|
||||
options = {}
|
||||
options = {
|
||||
strings: true,
|
||||
}
|
||||
input: {
|
||||
console.log(
|
||||
1 + 2 + (3 + 4 + 5),
|
||||
@@ -84,7 +88,9 @@ concat_3: {
|
||||
}
|
||||
|
||||
concat_4: {
|
||||
options = {}
|
||||
options = {
|
||||
strings: true,
|
||||
}
|
||||
input: {
|
||||
console.log(
|
||||
1 + "2" + (3 + 4 + 5),
|
||||
@@ -113,7 +119,9 @@ concat_4: {
|
||||
}
|
||||
|
||||
concat_5: {
|
||||
options = {}
|
||||
options = {
|
||||
strings: true,
|
||||
}
|
||||
input: {
|
||||
console.log(
|
||||
"1" + 2 + (3 + 4 + 5),
|
||||
@@ -142,7 +150,9 @@ concat_5: {
|
||||
}
|
||||
|
||||
concat_6: {
|
||||
options = {}
|
||||
options = {
|
||||
strings: true,
|
||||
}
|
||||
input: {
|
||||
console.log(
|
||||
"1" + "2" + (3 + 4 + 5),
|
||||
@@ -171,6 +181,9 @@ concat_6: {
|
||||
}
|
||||
|
||||
concat_7: {
|
||||
options = {
|
||||
strings: true,
|
||||
}
|
||||
input: {
|
||||
console.log(
|
||||
"" + 1,
|
||||
@@ -197,6 +210,9 @@ concat_7: {
|
||||
}
|
||||
|
||||
concat_8: {
|
||||
options = {
|
||||
strings: true,
|
||||
}
|
||||
input: {
|
||||
console.log(
|
||||
1 + "",
|
||||
@@ -221,3 +237,20 @@ concat_8: {
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
issue_3689: {
|
||||
options = {
|
||||
strings: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function(a) {
|
||||
return a + ("" + (a[0] = 0));
|
||||
}([]));
|
||||
}
|
||||
expect: {
|
||||
console.log(function(a) {
|
||||
return a + ("" + (a[0] = 0));
|
||||
}([]));
|
||||
}
|
||||
expect_stdout: "00"
|
||||
}
|
||||
|
||||
@@ -294,6 +294,45 @@ cond_5: {
|
||||
}
|
||||
}
|
||||
|
||||
cond_6: {
|
||||
options = {
|
||||
booleans: true,
|
||||
conditionals: true,
|
||||
}
|
||||
input: {
|
||||
x ? a : b;
|
||||
x ? a : a;
|
||||
|
||||
x ? y ? a : b : c;
|
||||
x ? y ? a : a : b;
|
||||
x ? y ? a : b : b;
|
||||
x ? y ? a : b : a;
|
||||
x ? y ? a : a : a;
|
||||
|
||||
x ? a : y ? b : c;
|
||||
x ? a : y ? a : b;
|
||||
x ? a : y ? b : b;
|
||||
x ? a : y ? b : a;
|
||||
x ? a : y ? a : a;
|
||||
}
|
||||
expect: {
|
||||
x ? a : b;
|
||||
x, a;
|
||||
|
||||
x ? y ? a : b : c;
|
||||
x ? (y, a) : b;
|
||||
x && y ? a : b;
|
||||
!x || y ? a : b;
|
||||
x && y, a;
|
||||
|
||||
x ? a : y ? b : c;
|
||||
x || y ? a : b;
|
||||
x ? a : (y, b);
|
||||
!x && y ? b : a;
|
||||
!x && y, a;
|
||||
}
|
||||
}
|
||||
|
||||
cond_7: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
@@ -726,6 +765,24 @@ cond_11: {
|
||||
expect_stdout: "foo bar"
|
||||
}
|
||||
|
||||
cond_12: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
}
|
||||
input: {
|
||||
x ? y && a : a;
|
||||
x ? y || a : a;
|
||||
x ? a : y && a;
|
||||
x ? a : y || a;
|
||||
}
|
||||
expect: {
|
||||
(!x || y) && a;
|
||||
x && y || a;
|
||||
(x || y) && a;
|
||||
!x && y || a;
|
||||
}
|
||||
}
|
||||
|
||||
ternary_boolean_consequent: {
|
||||
options = {
|
||||
booleans: true,
|
||||
|
||||
@@ -830,6 +830,7 @@ issue_3552: {
|
||||
unreachable_assign: {
|
||||
options = {
|
||||
dead_code: true,
|
||||
strings: true,
|
||||
}
|
||||
input: {
|
||||
console.log(A = "P" + (A = "A" + (B = "S" + (A = B = "S"))), A, B);
|
||||
|
||||
@@ -2355,3 +2355,23 @@ issue_3664: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3673: {
|
||||
options = {
|
||||
pure_getters: "strict",
|
||||
side_effects: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var a;
|
||||
(a = [ a ]).p = 42;
|
||||
console.log("PASS");
|
||||
}
|
||||
expect: {
|
||||
var a;
|
||||
(a = [ a ]).p = 42;
|
||||
console.log("PASS");
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -2244,7 +2244,7 @@ issue_3076: {
|
||||
var c = "PASS";
|
||||
(function(b) {
|
||||
var n = 2;
|
||||
while (--b + (e = void 0, e && (c = "FAIL"), e = 5, 1).toString() && --n > 0);
|
||||
while (--b + (e = void 0, e && (c = "FAIL"), e = 5, 1..toString()) && --n > 0);
|
||||
var e;
|
||||
})(2),
|
||||
console.log(c);
|
||||
@@ -3700,3 +3700,88 @@ pr_3595_4: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3679_1: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
inline: true,
|
||||
pure_getters: "strict",
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
var f = function() {};
|
||||
f.g = function() {
|
||||
console.log("PASS");
|
||||
};
|
||||
f.g();
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
console.log("PASS");
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3679_2: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
inline: true,
|
||||
passes: 2,
|
||||
pure_getters: "strict",
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
"use strict";
|
||||
var f = function() {};
|
||||
f.g = function() {
|
||||
console.log("PASS");
|
||||
};
|
||||
f.g();
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
(function() {
|
||||
"use strict";
|
||||
console.log("PASS");
|
||||
})();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3679_3: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
inline: true,
|
||||
functions: true,
|
||||
pure_getters: "strict",
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
var f = function() {};
|
||||
f.p = "PASS";
|
||||
f.g = function() {
|
||||
console.log(f.p);
|
||||
};
|
||||
f.g();
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
(function() {
|
||||
function f() {};
|
||||
f.p = "PASS";
|
||||
(f.g = function() {
|
||||
console.log(f.p);
|
||||
})();
|
||||
})();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ mangle_props: {
|
||||
obj[1/0],
|
||||
obj["Infinity"],
|
||||
obj[-1/0],
|
||||
obj[-1/0],
|
||||
obj[-(1/0)],
|
||||
obj["-Infinity"],
|
||||
obj[null],
|
||||
obj["null"]
|
||||
|
||||
@@ -1,98 +1,111 @@
|
||||
issue_269_1: {
|
||||
options = {
|
||||
options = {
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
f(
|
||||
String(x),
|
||||
Number(x),
|
||||
Boolean(x),
|
||||
input: {
|
||||
var x = {};
|
||||
console.log(
|
||||
String(x),
|
||||
Number(x),
|
||||
Boolean(x),
|
||||
|
||||
String(),
|
||||
Number(),
|
||||
Boolean()
|
||||
);
|
||||
}
|
||||
expect: {
|
||||
f(
|
||||
x + '', +x, !!x,
|
||||
'', 0, false
|
||||
);
|
||||
}
|
||||
String(),
|
||||
Number(),
|
||||
Boolean()
|
||||
);
|
||||
}
|
||||
expect: {
|
||||
var x = {};
|
||||
console.log(
|
||||
x + "", +x, !!x,
|
||||
"", 0, false
|
||||
);
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
issue_269_dangers: {
|
||||
options = {
|
||||
options = {
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
f(
|
||||
String(x, x),
|
||||
Number(x, x),
|
||||
Boolean(x, x)
|
||||
);
|
||||
}
|
||||
expect: {
|
||||
f(String(x, x), Number(x, x), Boolean(x, x));
|
||||
}
|
||||
input: {
|
||||
var x = {};
|
||||
console.log(
|
||||
String(x, x),
|
||||
Number(x, x),
|
||||
Boolean(x, x)
|
||||
);
|
||||
}
|
||||
expect: {
|
||||
var x = {};
|
||||
console.log(String(x, x), Number(x, x), Boolean(x, x));
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
issue_269_in_scope: {
|
||||
options = {
|
||||
options = {
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
var String, Number, Boolean;
|
||||
f(
|
||||
String(x),
|
||||
Number(x, x),
|
||||
Boolean(x)
|
||||
);
|
||||
}
|
||||
expect: {
|
||||
var String, Number, Boolean;
|
||||
f(String(x), Number(x, x), Boolean(x));
|
||||
}
|
||||
input: {
|
||||
var String, Number, Boolean;
|
||||
var x = {};
|
||||
console.log(
|
||||
String(x),
|
||||
Number(x, x),
|
||||
Boolean(x)
|
||||
);
|
||||
}
|
||||
expect: {
|
||||
var String, Number, Boolean;
|
||||
var x = {};
|
||||
console.log(String(x), Number(x, x), Boolean(x));
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
strings_concat: {
|
||||
options = {
|
||||
options = {
|
||||
strings: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
f(
|
||||
String(x + 'str'),
|
||||
String('str' + x)
|
||||
);
|
||||
}
|
||||
expect: {
|
||||
f(
|
||||
x + 'str',
|
||||
'str' + x
|
||||
);
|
||||
}
|
||||
input: {
|
||||
var x = {};
|
||||
console.log(
|
||||
String(x + "str"),
|
||||
String("str" + x)
|
||||
);
|
||||
}
|
||||
expect: {
|
||||
var x = {};
|
||||
console.log(
|
||||
x + "str",
|
||||
"str" + x
|
||||
);
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
regexp: {
|
||||
options = {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
RegExp("foo");
|
||||
RegExp("bar", "ig");
|
||||
RegExp(foo);
|
||||
RegExp("bar", ig);
|
||||
RegExp("should", "fail");
|
||||
}
|
||||
expect: {
|
||||
/foo/;
|
||||
/bar/ig;
|
||||
RegExp(foo);
|
||||
RegExp("bar", ig);
|
||||
RegExp("should", "fail");
|
||||
}
|
||||
expect_warnings: [
|
||||
'WARN: Error converting RegExp("should","fail") [test/compress/issue-269.js:5,2]',
|
||||
]
|
||||
input: {
|
||||
RegExp("foo");
|
||||
RegExp("bar", "ig");
|
||||
RegExp(foo);
|
||||
RegExp("bar", ig);
|
||||
RegExp("should", "fail");
|
||||
}
|
||||
expect: {
|
||||
/foo/;
|
||||
/bar/ig;
|
||||
RegExp(foo);
|
||||
RegExp("bar", ig);
|
||||
RegExp("should", "fail");
|
||||
}
|
||||
expect_warnings: [
|
||||
'WARN: Error converting RegExp("should","fail") [test/compress/issue-269.js:5,8]',
|
||||
]
|
||||
}
|
||||
|
||||
@@ -669,6 +669,9 @@ issue_1710: {
|
||||
}
|
||||
|
||||
unary_binary_parenthesis: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
}
|
||||
input: {
|
||||
var v = [ 0, 1, NaN, Infinity, null, undefined, true, false, "", "foo", /foo/ ];
|
||||
v.forEach(function(x) {
|
||||
@@ -1075,11 +1078,11 @@ issue_3653: {
|
||||
}
|
||||
expect: {
|
||||
console.log(0 - (console && 0));
|
||||
console.log(0 - (console && 0) + 0);
|
||||
console.log(0 - (console && 0));
|
||||
console.log(0 - (0 - (console && 0)));
|
||||
console.log(0 - (console && 0));
|
||||
console.log(1 / (0 - (console && 0)));
|
||||
console.log(0 - (console && 0) + 0);
|
||||
console.log(0 - (console && 0));
|
||||
console.log(0 - (console && 0));
|
||||
console.log(0 - (console && 0));
|
||||
console.log(0 - (console && 0));
|
||||
@@ -1135,3 +1138,116 @@ issue_3655: {
|
||||
"0",
|
||||
]
|
||||
}
|
||||
|
||||
issue_3676_1: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe_math: true,
|
||||
}
|
||||
input: {
|
||||
var a = [];
|
||||
console.log(false - (a - (a[1] = 42)));
|
||||
}
|
||||
expect: {
|
||||
var a = [];
|
||||
console.log(false - (a - (a[1] = 42)));
|
||||
}
|
||||
expect_stdout: "NaN"
|
||||
}
|
||||
|
||||
issue_3676_2: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe_math: true,
|
||||
}
|
||||
input: {
|
||||
var a;
|
||||
console.log(false - ((a = []) - (a[1] = 42)));
|
||||
}
|
||||
expect: {
|
||||
var a;
|
||||
console.log(false - ((a = []) - (a[1] = 42)));
|
||||
}
|
||||
expect_stdout: "NaN"
|
||||
}
|
||||
|
||||
issue_3682_1: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe_math: true,
|
||||
}
|
||||
input: {
|
||||
var a = -0;
|
||||
console.log(1 / (a - 1 + 1));
|
||||
}
|
||||
expect: {
|
||||
var a = -0;
|
||||
console.log(1 / (a - 1 + 1));
|
||||
}
|
||||
expect_stdout: "Infinity"
|
||||
}
|
||||
|
||||
issue_3682_2: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe_math: true,
|
||||
}
|
||||
input: {
|
||||
var a = -0, b = 1;
|
||||
console.log(1 / (a - (b - b)));
|
||||
}
|
||||
expect: {
|
||||
var a = -0, b = 1;
|
||||
console.log(1 / (a - (b - b)));
|
||||
}
|
||||
expect_stdout: "-Infinity"
|
||||
}
|
||||
|
||||
issue_3682_3: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe_math: true,
|
||||
}
|
||||
input: {
|
||||
var a = -0, b = 1, c = -1;
|
||||
console.log(1 / (a - (+b + +c)));
|
||||
}
|
||||
expect: {
|
||||
var a = -0, b = 1, c = -1;
|
||||
console.log(1 / (a - (+b + +c)));
|
||||
}
|
||||
expect_stdout: "-Infinity"
|
||||
}
|
||||
|
||||
issue_3684: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
}
|
||||
input: {
|
||||
console.log(1 / (-1 * (0 & console) + 0));
|
||||
console.log(1 / ((0 & console) / -1 + 0));
|
||||
}
|
||||
expect: {
|
||||
console.log(1 / (-1 * (0 & console) + 0));
|
||||
console.log(1 / ((0 & console) / -1 + 0));
|
||||
}
|
||||
expect_stdout: [
|
||||
"Infinity",
|
||||
"Infinity",
|
||||
]
|
||||
}
|
||||
|
||||
issue_3695: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
}
|
||||
input: {
|
||||
var a = [];
|
||||
console.log(+(a * (a[0] = false)));
|
||||
}
|
||||
expect: {
|
||||
var a = [];
|
||||
console.log(+(a * (a[0] = false)));
|
||||
}
|
||||
expect_stdout: "NaN"
|
||||
}
|
||||
|
||||
@@ -910,15 +910,23 @@ call: {
|
||||
console.log(this === b ? "bar" : "baz");
|
||||
};
|
||||
(a, b)();
|
||||
(a, b).c();
|
||||
(a, b.c)();
|
||||
(a, b)["c"]();
|
||||
(a, b["c"])();
|
||||
(a, function() {
|
||||
console.log(this === a);
|
||||
})();
|
||||
new (a, b)();
|
||||
new (a, b).c();
|
||||
new (a, b.c)();
|
||||
new (a, b)["c"]();
|
||||
new (a, b["c"])();
|
||||
new (a, function() {
|
||||
console.log(this === a);
|
||||
})();
|
||||
console.log(typeof (a, b).c);
|
||||
console.log(typeof (a, b)["c"]);
|
||||
}
|
||||
expect: {
|
||||
var a = function() {
|
||||
@@ -931,23 +939,39 @@ call: {
|
||||
console.log(this === b ? "bar" : "baz");
|
||||
},
|
||||
b(),
|
||||
b.c(),
|
||||
(a, b.c)(),
|
||||
b["c"](),
|
||||
(a, b["c"])(),
|
||||
function() {
|
||||
console.log(this === a);
|
||||
}(),
|
||||
new b(),
|
||||
new b.c(),
|
||||
new b.c(),
|
||||
new b["c"](),
|
||||
new b["c"](),
|
||||
new function() {
|
||||
console.log(this === a);
|
||||
}();
|
||||
}(),
|
||||
console.log((a, typeof b.c)),
|
||||
console.log((a, typeof b["c"]));
|
||||
}
|
||||
expect_stdout: [
|
||||
"foo",
|
||||
"bar",
|
||||
"baz",
|
||||
"bar",
|
||||
"baz",
|
||||
"true",
|
||||
"foo",
|
||||
"baz",
|
||||
"baz",
|
||||
"baz",
|
||||
"baz",
|
||||
"false",
|
||||
"function",
|
||||
"function",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
17
test/input/issue-1482/beautify.js
Normal file
17
test/input/issue-1482/beautify.js
Normal file
@@ -0,0 +1,17 @@
|
||||
if (x) foo();
|
||||
|
||||
if (x) foo(); else baz();
|
||||
|
||||
if (x) foo(); else if (y) bar(); else baz();
|
||||
|
||||
if (x) if (y) foo(); else bar(); else baz();
|
||||
|
||||
if (x) foo(); else if (y) bar(); else if (z) baz(); else moo();
|
||||
|
||||
function f() {
|
||||
if (x) foo();
|
||||
if (x) foo(); else baz();
|
||||
if (x) foo(); else if (y) bar(); else baz();
|
||||
if (x) if (y) foo(); else bar(); else baz();
|
||||
if (x) foo(); else if (y) bar(); else if (z) baz(); else moo();
|
||||
}
|
||||
@@ -1,17 +1 @@
|
||||
if (x) foo();
|
||||
|
||||
if (x) foo(); else baz();
|
||||
|
||||
if (x) foo(); else if (y) bar(); else baz();
|
||||
|
||||
if (x) if (y) foo(); else bar(); else baz();
|
||||
|
||||
if (x) foo(); else if (y) bar(); else if (z) baz(); else moo();
|
||||
|
||||
function f() {
|
||||
if (x) foo();
|
||||
if (x) foo(); else baz();
|
||||
if (x) foo(); else if (y) bar(); else baz();
|
||||
if (x) if (y) foo(); else bar(); else baz();
|
||||
if (x) foo(); else if (y) bar(); else if (z) baz(); else moo();
|
||||
}
|
||||
if(x)foo();if(x)foo();else baz();if(x)foo();else if(y)bar();else baz();if(x)if(y)foo();else bar();else baz();if(x)foo();else if(y)bar();else if(z)baz();else moo();function f(){if(x)foo();if(x)foo();else baz();if(x)foo();else if(y)bar();else baz();if(x)if(y)foo();else bar();else baz();if(x)foo();else if(y)bar();else if(z)baz();else moo()}
|
||||
|
||||
@@ -176,7 +176,7 @@ describe("bin/uglifyjs", function() {
|
||||
var command = uglifyjscmd + ' test/input/issue-1482/input.js -b';
|
||||
exec(command, function(err, stdout) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(stdout, read("test/input/issue-1482/default.js"));
|
||||
assert.strictEqual(stdout, read("test/input/issue-1482/beautify.js"));
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -188,6 +188,22 @@ describe("bin/uglifyjs", function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should work with `--output-opts`", function(done) {
|
||||
var command = uglifyjscmd + ' test/input/issue-1482/input.js -O';
|
||||
exec(command, function(err, stdout) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(stdout, read("test/input/issue-1482/default.js"));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should fail when both --beautify & --output-opts are specified", function(done) {
|
||||
var command = uglifyjscmd + " test/input/issue-520/input.js -bO";
|
||||
exec(command, function(err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
assert.strictEqual(stderr, "ERROR: --beautify cannot be used with --output-opts\n");
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should process inline source map", function(done) {
|
||||
var command = [
|
||||
uglifyjscmd,
|
||||
|
||||
Reference in New Issue
Block a user