Compare commits

...

9 Commits

Author SHA1 Message Date
Alex Lam S.L
36b2d35bf3 v3.7.7 2020-02-02 00:24:50 +00:00
Alex Lam S.L
79c60032a5 fix corner case in collapse_vars (#3701)
fixes #3700
2020-01-30 09:04:44 +08:00
Alex Lam S.L
a3754068dd fix corner case in collapse_vars (#3699)
fixes #3698
2020-01-30 00:08:53 +08:00
Alex Lam S.L
2ba5f391e0 enhance collapse_vars (#3697) 2020-01-29 08:52:20 +08:00
Alex Lam S.L
87119e44a0 fix corner case in sign propagation (#3696)
- migrate de-facto functionality to `evaluate`

fixes #3695
2020-01-28 22:44:18 +08:00
Alex Lam S.L
b499e03f82 enhance conditionals (#3694) 2020-01-28 12:33:21 +08:00
Alex Lam S.L
a478f275e4 enhance sequences (#3693) 2020-01-28 09:58:01 +08:00
Alex Lam S.L
e9e76dcf04 fix corner case in string concatenations (#3692)
- migrate de-facto compression to `conditionals` & `strings`

fixes #3689
2020-01-28 07:33:11 +08:00
Alex Lam S.L
0dcedad2d5 fix corner case in booleans (#3691)
fixes #3690
2020-01-28 02:04:44 +08:00
14 changed files with 733 additions and 163 deletions

View File

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

View File

@@ -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;
}
@@ -1381,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;
@@ -3826,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);
@@ -3840,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")) {
@@ -4453,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
@@ -4461,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
@@ -5344,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 = [];
@@ -5399,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();
@@ -5988,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;
@@ -6046,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")
@@ -6075,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);
@@ -6111,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, {
@@ -6188,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()
@@ -6198,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
@@ -6338,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
@@ -6357,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;
@@ -6727,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) {
@@ -7071,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")) {
@@ -7115,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
@@ -7180,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
@@ -7284,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)) {
@@ -7297,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, [
@@ -7309,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)) {
@@ -7323,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)) {
@@ -7474,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")) {
@@ -7638,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,

View File

@@ -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.6",
"version": "3.7.7",
"engines": {
"node": ">=0.8.0"
},

View File

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

View File

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

View File

@@ -7491,3 +7491,277 @@ issue_3671: {
}
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"
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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]',
]
}

View File

@@ -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) {
@@ -1233,3 +1236,18 @@ issue_3684: {
"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"
}

View File

@@ -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",
]
}