enhance conditionals (#3805)
This commit is contained in:
@@ -2363,13 +2363,33 @@ merge(Compressor.prototype, {
|
|||||||
exprs = exprs.slice(0, i + 1).concat(tail);
|
exprs = exprs.slice(0, i + 1).concat(tail);
|
||||||
}
|
}
|
||||||
if (defn instanceof AST_Definitions) {
|
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 (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;
|
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) {
|
function join_var_assign(definitions, exprs, keep) {
|
||||||
var trimmed = false;
|
var trimmed = false;
|
||||||
while (exprs.length > keep) {
|
while (exprs.length > keep) {
|
||||||
@@ -6294,11 +6314,44 @@ merge(Compressor.prototype, {
|
|||||||
return self;
|
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) {
|
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;
|
var end = expressions.length - 1;
|
||||||
|
merge_conditional_assignments();
|
||||||
trim_right_for_undefined();
|
trim_right_for_undefined();
|
||||||
if (end == 0) {
|
if (end == 0) {
|
||||||
self = maintain_this_binding(compressor, compressor.parent(), compressor.self(), expressions[0]);
|
self = maintain_this_binding(compressor, compressor.parent(), compressor.self(), expressions[0]);
|
||||||
@@ -6309,6 +6362,8 @@ merge(Compressor.prototype, {
|
|||||||
return self;
|
return self;
|
||||||
|
|
||||||
function filter_for_side_effects() {
|
function filter_for_side_effects() {
|
||||||
|
if (!compressor.option("side_effects")) return self.expressions;
|
||||||
|
var expressions = [];
|
||||||
var first = first_in_statement(compressor);
|
var first = first_in_statement(compressor);
|
||||||
var last = self.expressions.length - 1;
|
var last = self.expressions.length - 1;
|
||||||
self.expressions.forEach(function(expr, index) {
|
self.expressions.forEach(function(expr, index) {
|
||||||
@@ -6318,9 +6373,11 @@ merge(Compressor.prototype, {
|
|||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return expressions;
|
||||||
}
|
}
|
||||||
|
|
||||||
function trim_right_for_undefined() {
|
function trim_right_for_undefined() {
|
||||||
|
if (!compressor.option("side_effects")) return;
|
||||||
while (end > 0 && is_undefined(expressions[end], compressor)) end--;
|
while (end > 0 && is_undefined(expressions[end], compressor)) end--;
|
||||||
if (end < expressions.length - 1) {
|
if (end < expressions.length - 1) {
|
||||||
expressions[end] = make_node(AST_UnaryPrefix, self, {
|
expressions[end] = make_node(AST_UnaryPrefix, self, {
|
||||||
@@ -6330,6 +6387,22 @@ merge(Compressor.prototype, {
|
|||||||
expressions.length = end + 1;
|
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) {
|
OPT(AST_UnaryPostfix, function(self, compressor) {
|
||||||
|
|||||||
@@ -1666,3 +1666,96 @@ issue_3668: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "undefined"
|
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;);
|
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