extend fuzzy RHS folding (#3006)

- `a = []; if (1) x();` => `if (a = []) x();`
This commit is contained in:
Alex Lam S.L
2018-03-17 03:10:21 +08:00
committed by GitHub
parent 20ca0f5906
commit ccf0e2ef4f
3 changed files with 138 additions and 58 deletions

View File

@@ -916,5 +916,25 @@ TreeWalker.prototype = {
|| node instanceof AST_Break && x instanceof AST_Switch) || node instanceof AST_Break && x instanceof AST_Switch)
return x; return x;
} }
},
in_boolean_context: function() {
var self = this.self();
for (var i = 0, p; p = this.parent(i); i++) {
if (p instanceof AST_SimpleStatement
|| p instanceof AST_Conditional && p.condition === self
|| p instanceof AST_DWLoop && p.condition === self
|| p instanceof AST_For && p.condition === self
|| p instanceof AST_If && p.condition === self
|| p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self) {
return true;
}
if (p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||")
|| p instanceof AST_Conditional
|| p.tail_node() === self) {
self = p;
} else {
return false;
}
}
} }
}; };

View File

@@ -147,27 +147,6 @@ merge(Compressor.prototype, {
return true; return true;
return false; return false;
}, },
in_boolean_context: function() {
if (!this.option("booleans")) return false;
var self = this.self();
for (var i = 0, p; p = this.parent(i); i++) {
if (p instanceof AST_SimpleStatement
|| p instanceof AST_Conditional && p.condition === self
|| p instanceof AST_DWLoop && p.condition === self
|| p instanceof AST_For && p.condition === self
|| p instanceof AST_If && p.condition === self
|| p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self) {
return true;
}
if (p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||")
|| p instanceof AST_Conditional
|| p.tail_node() === self) {
self = p;
} else {
return false;
}
}
},
compress: function(node) { compress: function(node) {
if (this.option("expression")) { if (this.option("expression")) {
node.process_expression(true); node.process_expression(true);
@@ -980,7 +959,7 @@ merge(Compressor.prototype, {
var args; var args;
var candidates = []; var candidates = [];
var stat_index = statements.length; var stat_index = statements.length;
var scanner = new TreeTransformer(function(node, descend) { var scanner = new TreeTransformer(function(node) {
if (abort) return node; if (abort) return node;
// Skip nodes before `candidate` as quickly as possible // Skip nodes before `candidate` as quickly as possible
if (!hit) { if (!hit) {
@@ -1019,7 +998,7 @@ merge(Compressor.prototype, {
if (can_replace if (can_replace
&& !(node instanceof AST_SymbolDeclaration) && !(node instanceof AST_SymbolDeclaration)
&& (scan_lhs && (hit_lhs = lhs.equivalent_to(node)) && (scan_lhs && (hit_lhs = lhs.equivalent_to(node))
|| scan_rhs && (hit_rhs = rhs.equivalent_to(node)))) { || scan_rhs && (hit_rhs = scan_rhs(node, this)))) {
if (stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) { if (stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) {
abort = true; abort = true;
return node; return node;
@@ -1388,12 +1367,16 @@ merge(Compressor.prototype, {
} }
function foldable(expr) { function foldable(expr) {
if (expr.is_constant()) return true; if (expr instanceof AST_SymbolRef) {
if (expr instanceof AST_Array) return false; var value = expr.evaluate(compressor);
if (expr instanceof AST_Function) return false; if (value === expr) return rhs_exact_match;
if (expr instanceof AST_Object) return false; return rhs_fuzzy_match(value, rhs_exact_match);
if (expr instanceof AST_RegExp) return false; }
if (expr instanceof AST_Symbol) return true; if (expr instanceof AST_This) return rhs_exact_match;
if (expr.is_truthy()) return rhs_fuzzy_match(true, return_false);
if (expr.is_constant()) {
return rhs_fuzzy_match(expr.evaluate(compressor), rhs_exact_match);
}
if (!(lhs instanceof AST_SymbolRef)) return false; if (!(lhs instanceof AST_SymbolRef)) return false;
if (expr.has_side_effects(compressor)) return false; if (expr.has_side_effects(compressor)) return false;
var circular; var circular;
@@ -1404,7 +1387,25 @@ merge(Compressor.prototype, {
circular = true; circular = true;
} }
})); }));
return !circular; return !circular && rhs_exact_match;
}
function rhs_exact_match(node) {
return rhs.equivalent_to(node);
}
function rhs_fuzzy_match(value, fallback) {
return function(node, tw) {
if (tw.in_boolean_context()) {
if (value && node.is_truthy() && !node.has_side_effects(compressor)) {
return true;
}
if (node.is_constant()) {
return !node.evaluate(compressor) == !value;
}
}
return fallback(node);
};
} }
function get_lvalues(expr) { function get_lvalues(expr) {
@@ -5089,7 +5090,7 @@ merge(Compressor.prototype, {
} }
break; break;
} }
if (self.operator == "+" && compressor.in_boolean_context()) { if (compressor.option("booleans") && self.operator == "+" && compressor.in_boolean_context()) {
var ll = self.left.evaluate(compressor); var ll = self.left.evaluate(compressor);
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
if (ll && typeof ll == "string") { if (ll && typeof ll == "string") {
@@ -5154,7 +5155,7 @@ merge(Compressor.prototype, {
} }
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
if (!rr) { if (!rr) {
if (compressor.in_boolean_context()) { if (compressor.option("booleans") && compressor.in_boolean_context()) {
compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start); compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);
return make_sequence(self, [ return make_sequence(self, [
self.left, self.left,
@@ -5163,7 +5164,8 @@ merge(Compressor.prototype, {
} else self.falsy = true; } else self.falsy = true;
} else if (!(rr instanceof AST_Node)) { } else if (!(rr instanceof AST_Node)) {
var parent = compressor.parent(); var parent = compressor.parent();
if (parent.operator == "&&" && parent.left === compressor.self() || compressor.in_boolean_context()) { if (parent.operator == "&&" && parent.left === compressor.self()
|| compressor.option("booleans") && compressor.in_boolean_context()) {
compressor.warn("Dropping side-effect-free && [{file}:{line},{col}]", self.start); compressor.warn("Dropping side-effect-free && [{file}:{line},{col}]", self.start);
return self.left.optimize(compressor); return self.left.optimize(compressor);
} }
@@ -5190,12 +5192,13 @@ merge(Compressor.prototype, {
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
if (!rr) { if (!rr) {
var parent = compressor.parent(); var parent = compressor.parent();
if (parent.operator == "||" && parent.left === compressor.self() || compressor.in_boolean_context()) { if (parent.operator == "||" && parent.left === compressor.self()
|| compressor.option("booleans") && compressor.in_boolean_context()) {
compressor.warn("Dropping side-effect-free || [{file}:{line},{col}]", self.start); compressor.warn("Dropping side-effect-free || [{file}:{line},{col}]", self.start);
return self.left.optimize(compressor); return self.left.optimize(compressor);
} }
} else if (!(rr instanceof AST_Node)) { } else if (!(rr instanceof AST_Node)) {
if (compressor.in_boolean_context()) { if (compressor.option("booleans") && compressor.in_boolean_context()) {
compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start); compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);
return make_sequence(self, [ return make_sequence(self, [
self.left, self.left,
@@ -5833,7 +5836,7 @@ merge(Compressor.prototype, {
right: alternative right: alternative
}).optimize(compressor); }).optimize(compressor);
} }
var in_bool = compressor.in_boolean_context(); var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
if (is_true(self.consequent)) { if (is_true(self.consequent)) {
if (is_false(self.alternative)) { if (is_false(self.alternative)) {
// c ? true : false ---> !!c // c ? true : false ---> !!c
@@ -5931,32 +5934,29 @@ merge(Compressor.prototype, {
}); });
OPT(AST_Boolean, function(self, compressor){ OPT(AST_Boolean, function(self, compressor){
if (!compressor.option("booleans")) return self;
if (compressor.in_boolean_context()) return make_node(AST_Number, self, { if (compressor.in_boolean_context()) return make_node(AST_Number, self, {
value: +self.value value: +self.value
}); });
if (compressor.option("booleans")) { var p = compressor.parent();
var p = compressor.parent(); if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) {
if (p instanceof AST_Binary && (p.operator == "==" compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
|| p.operator == "!=")) { operator : p.operator,
compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", { value : self.value,
operator : p.operator, file : p.start.file,
value : self.value, line : p.start.line,
file : p.start.file, col : p.start.col,
line : p.start.line, });
col : p.start.col, return make_node(AST_Number, self, {
}); value: +self.value
return make_node(AST_Number, self, {
value: +self.value
});
}
return make_node(AST_UnaryPrefix, self, {
operator: "!",
expression: make_node(AST_Number, self, {
value: 1 - self.value
})
}); });
} }
return self; return make_node(AST_UnaryPrefix, self, {
operator: "!",
expression: make_node(AST_Number, self, {
value: 1 - self.value
})
});
}); });
OPT(AST_Sub, function(self, compressor){ OPT(AST_Sub, function(self, compressor){

View File

@@ -4976,7 +4976,7 @@ collapse_rhs_array: {
expect_stdout: "false false false" expect_stdout: "false false false"
} }
collapse_rhs_boolean: { collapse_rhs_boolean_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
} }
@@ -5001,6 +5001,66 @@ collapse_rhs_boolean: {
expect_stdout: "true true true" expect_stdout: "true true true"
} }
collapse_rhs_boolean_2: {
options = {
collapse_vars: true,
}
input: {
var a;
(function f1() {
a = function() {};
if (/foo/)
console.log(typeof a);
})();
console.log(function f2() {
a = [];
return !1;
}());
}
expect: {
var a;
(function f1() {
if (a = function() {})
console.log(typeof a);
})();
console.log(function f2() {
return !(a = []);
}());
}
expect_stdout: [
"function",
"false",
]
}
collapse_rhs_boolean_3: {
options = {
booleans: true,
collapse_vars: true,
conditionals: true,
}
input: {
var a, f, g, h, i, n, s, t, x, y;
if (x()) {
n = a;
} else if (y()) {
n = f();
} else if (s) {
i = false;
n = g(true);
} else if (t) {
i = false;
n = h(true);
} else {
n = [];
}
}
expect: {
var a, f, g, h, i, n, s, t, x, y;
n = x() ? a : y() ? f() : s ? g(!(i = !1)) : t ? h(!(i = !1)) : [];
}
}
collapse_rhs_function: { collapse_rhs_function: {
options = { options = {
collapse_vars: true, collapse_vars: true,