enhance conditionals (#3805)

This commit is contained in:
Alex Lam S.L
2020-04-20 02:42:13 +01:00
committed by GitHub
parent 88504ab869
commit a2fc32c64b
3 changed files with 265 additions and 6 deletions

View File

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

View File

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

View File

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