enhance conditionals (#3805)
This commit is contained in:
@@ -2363,13 +2363,33 @@ merge(Compressor.prototype, {
|
||||
exprs = exprs.slice(0, i + 1).concat(tail);
|
||||
}
|
||||
if (defn instanceof AST_Definitions) {
|
||||
var def = defn.definitions[defn.definitions.length - 1];
|
||||
keep = keep || 0;
|
||||
for (var i = defn.definitions.length; --i >= 0;) {
|
||||
var def = defn.definitions[i];
|
||||
if (!def.value) continue;
|
||||
if (trim_assigns(def.name, def.value, exprs)) trimmed = true;
|
||||
if (join_var_assign(defn.definitions, exprs, keep || 0)) trimmed = true;
|
||||
if (merge_conditional_assignments(def, exprs, keep)) trimmed = true;
|
||||
break;
|
||||
}
|
||||
if (join_var_assign(defn.definitions, exprs, keep)) trimmed = true;
|
||||
}
|
||||
return trimmed && exprs;
|
||||
}
|
||||
|
||||
function merge_conditional_assignments(var_def, exprs, keep) {
|
||||
if (!compressor.option("conditionals")) return;
|
||||
var trimmed = false;
|
||||
var def = var_def.name.definition();
|
||||
while (exprs.length > keep) {
|
||||
var cond = to_conditional_assignment(compressor, def, var_def.value, exprs[0]);
|
||||
if (!cond) break;
|
||||
var_def.value = cond;
|
||||
exprs.shift();
|
||||
trimmed = true;
|
||||
}
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
function join_var_assign(definitions, exprs, keep) {
|
||||
var trimmed = false;
|
||||
while (exprs.length > keep) {
|
||||
@@ -6294,11 +6314,44 @@ merge(Compressor.prototype, {
|
||||
return self;
|
||||
});
|
||||
|
||||
// (a = b, x && a = c) => a = x ? c : b
|
||||
// (a = b, x || a = c) => a = x ? b : c
|
||||
function to_conditional_assignment(compressor, def, value, node) {
|
||||
if (!(node instanceof AST_Binary)) return;
|
||||
if (!lazy_op[node.operator]) return;
|
||||
if (!(node.right instanceof AST_Assign)) return;
|
||||
if (node.right.operator != "=") return;
|
||||
if (!(node.right.left instanceof AST_SymbolRef)) return;
|
||||
if (node.right.left.definition() !== def) return;
|
||||
if (value.has_side_effects(compressor)) return;
|
||||
if (!safe_from_assignment(node.left)) return;
|
||||
if (!safe_from_assignment(node.right.right)) return;
|
||||
def.replaced++;
|
||||
return node.operator == "&&" ? make_node(AST_Conditional, node, {
|
||||
condition: node.left,
|
||||
consequent: node.right.right,
|
||||
alternative: value
|
||||
}) : make_node(AST_Conditional, node, {
|
||||
condition: node.left,
|
||||
consequent: value,
|
||||
alternative: node.right.right
|
||||
});
|
||||
|
||||
function safe_from_assignment(node) {
|
||||
if (node.has_side_effects(compressor)) return;
|
||||
var hit = false;
|
||||
node.walk(new TreeWalker(function(node) {
|
||||
if (hit) return true;
|
||||
if (node instanceof AST_SymbolRef && node.definition() === def) return hit = true;
|
||||
}));
|
||||
return !hit;
|
||||
}
|
||||
}
|
||||
|
||||
OPT(AST_Sequence, function(self, compressor) {
|
||||
if (!compressor.option("side_effects")) return self;
|
||||
var expressions = [];
|
||||
filter_for_side_effects();
|
||||
var expressions = filter_for_side_effects();
|
||||
var end = expressions.length - 1;
|
||||
merge_conditional_assignments();
|
||||
trim_right_for_undefined();
|
||||
if (end == 0) {
|
||||
self = maintain_this_binding(compressor, compressor.parent(), compressor.self(), expressions[0]);
|
||||
@@ -6309,6 +6362,8 @@ merge(Compressor.prototype, {
|
||||
return self;
|
||||
|
||||
function filter_for_side_effects() {
|
||||
if (!compressor.option("side_effects")) return self.expressions;
|
||||
var expressions = [];
|
||||
var first = first_in_statement(compressor);
|
||||
var last = self.expressions.length - 1;
|
||||
self.expressions.forEach(function(expr, index) {
|
||||
@@ -6318,9 +6373,11 @@ merge(Compressor.prototype, {
|
||||
first = false;
|
||||
}
|
||||
});
|
||||
return expressions;
|
||||
}
|
||||
|
||||
function trim_right_for_undefined() {
|
||||
if (!compressor.option("side_effects")) return;
|
||||
while (end > 0 && is_undefined(expressions[end], compressor)) end--;
|
||||
if (end < expressions.length - 1) {
|
||||
expressions[end] = make_node(AST_UnaryPrefix, self, {
|
||||
@@ -6330,6 +6387,22 @@ merge(Compressor.prototype, {
|
||||
expressions.length = end + 1;
|
||||
}
|
||||
}
|
||||
|
||||
function merge_conditional_assignments() {
|
||||
if (!compressor.option("conditionals")) return;
|
||||
for (var i = 0; i < end; i++) {
|
||||
var assign = expressions[i];
|
||||
if (!(assign instanceof AST_Assign)) continue;
|
||||
if (assign.operator != "=") continue;
|
||||
if (!(assign.left instanceof AST_SymbolRef)) continue;
|
||||
var def = assign.left.definition();
|
||||
var cond = to_conditional_assignment(compressor, def, assign.right, expressions[i + 1]);
|
||||
if (!cond) continue;
|
||||
assign.right = cond;
|
||||
expressions.splice(i + 1, 1);
|
||||
end--;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
OPT(AST_UnaryPostfix, function(self, compressor) {
|
||||
|
||||
@@ -1666,3 +1666,96 @@ issue_3668: {
|
||||
}
|
||||
expect_stdout: "undefined"
|
||||
}
|
||||
|
||||
conditional_assignments_1: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
sequences: true,
|
||||
}
|
||||
input: {
|
||||
function f(a, b, c, d) {
|
||||
a = b;
|
||||
if (c) a = d;
|
||||
return a;
|
||||
}
|
||||
function g(a, b, c, d) {
|
||||
a = b;
|
||||
if (c); else a = d;
|
||||
return a;
|
||||
}
|
||||
console.log(f(0, "FAIL", 1, "PASS"), g(0, "PASS", 1, "FAIL"));
|
||||
}
|
||||
expect: {
|
||||
function f(a, b, c, d) {
|
||||
return a = c ? d : b, a;
|
||||
}
|
||||
function g(a, b, c, d) {
|
||||
return a = c ? b : d, a;
|
||||
}
|
||||
console.log(f(0, "FAIL", 1, "PASS"), g(0, "PASS", 1, "FAIL"));
|
||||
}
|
||||
expect_stdout: "PASS PASS"
|
||||
}
|
||||
|
||||
conditional_assignments_2: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
sequences: true,
|
||||
}
|
||||
input: {
|
||||
function f1(b, c, d) {
|
||||
a = b;
|
||||
if (c) a = d;
|
||||
return a;
|
||||
}
|
||||
function f2(a, c, d) {
|
||||
a = b;
|
||||
if (c) a = d;
|
||||
return a;
|
||||
}
|
||||
function f3(a, b, d) {
|
||||
a = b;
|
||||
if (c) a = d;
|
||||
return a;
|
||||
}
|
||||
function f4(a, b, c) {
|
||||
a = b;
|
||||
if (c) a = d;
|
||||
return a;
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function f1(b, c, d) {
|
||||
return a = c ? d : b, a;
|
||||
}
|
||||
function f2(a, c, d) {
|
||||
return a = b, c && (a = d), a;
|
||||
}
|
||||
function f3(a, b, d) {
|
||||
return a = b, c && (a = d), a;
|
||||
}
|
||||
function f4(a, b, c) {
|
||||
return a = b, c && (a = d), a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
conditional_assignments_3: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
sequences: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function(a, b) {
|
||||
a = "PASS";
|
||||
if (b) a = a;
|
||||
return a;
|
||||
}(0, 1));
|
||||
}
|
||||
expect: {
|
||||
console.log(function(a, b) {
|
||||
return a = "PASS", b && (a = a), a;
|
||||
}(0, 1));
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -896,3 +896,96 @@ loop_body_3: {
|
||||
for (var a, b, c; x;);
|
||||
}
|
||||
}
|
||||
|
||||
conditional_assignments_1: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
join_vars: true,
|
||||
sequences: true,
|
||||
}
|
||||
input: {
|
||||
function f(b, c, d) {
|
||||
var a = b;
|
||||
if (c) a = d;
|
||||
return a;
|
||||
}
|
||||
function g(b, c, d) {
|
||||
var a = b;
|
||||
if (c); else a = d;
|
||||
return a;
|
||||
}
|
||||
console.log(f("FAIL", 1, "PASS"), g("PASS", 1, "FAIL"));
|
||||
}
|
||||
expect: {
|
||||
function f(b, c, d) {
|
||||
var a = c ? d : b;
|
||||
return a;
|
||||
}
|
||||
function g(b, c, d) {
|
||||
var a = c ? b : d;
|
||||
return a;
|
||||
}
|
||||
console.log(f("FAIL", 1, "PASS"), g("PASS", 1, "FAIL"));
|
||||
}
|
||||
expect_stdout: "PASS PASS"
|
||||
}
|
||||
|
||||
conditional_assignments_2: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
join_vars: true,
|
||||
sequences: true,
|
||||
}
|
||||
input: {
|
||||
function f1(c, d) {
|
||||
var a = b;
|
||||
if (c) a = d;
|
||||
return a;
|
||||
}
|
||||
function f2(b, d) {
|
||||
var a = b;
|
||||
if (c) a = d;
|
||||
return a;
|
||||
}
|
||||
function f3(b, c) {
|
||||
var a = b;
|
||||
if (c) a = d;
|
||||
return a;
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function f1(c, d) {
|
||||
var a = b;
|
||||
return c && (a = d), a;
|
||||
}
|
||||
function f2(b, d) {
|
||||
var a = b;
|
||||
return c && (a = d), a;
|
||||
}
|
||||
function f3(b, c) {
|
||||
var a = b;
|
||||
return c && (a = d), a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
conditional_assignments_3: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
sequences: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function(b) {
|
||||
var a = "PASS";
|
||||
if (b) a = a;
|
||||
return a;
|
||||
}(0, 1));
|
||||
}
|
||||
expect: {
|
||||
console.log(function(b) {
|
||||
var a = "PASS";
|
||||
return b && (a = a), a;
|
||||
}(0, 1));
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user