enhance evaluate (#3714)
This commit is contained in:
@@ -3073,10 +3073,10 @@ merge(Compressor.prototype, {
|
|||||||
// is returned.
|
// is returned.
|
||||||
// They can be distinguished as constant value is never a
|
// They can be distinguished as constant value is never a
|
||||||
// descendant of AST_Node.
|
// descendant of AST_Node.
|
||||||
AST_Node.DEFMETHOD("evaluate", function(compressor) {
|
AST_Node.DEFMETHOD("evaluate", function(compressor, ignore_side_effects) {
|
||||||
if (!compressor.option("evaluate")) return this;
|
if (!compressor.option("evaluate")) return this;
|
||||||
var cached = [];
|
var cached = [];
|
||||||
var val = this._eval(compressor, cached, 1);
|
var val = this._eval(compressor, ignore_side_effects, cached, 1);
|
||||||
cached.forEach(function(node) {
|
cached.forEach(function(node) {
|
||||||
delete node._eval;
|
delete node._eval;
|
||||||
});
|
});
|
||||||
@@ -3104,6 +3104,19 @@ merge(Compressor.prototype, {
|
|||||||
def(AST_Constant, function() {
|
def(AST_Constant, function() {
|
||||||
return this.value;
|
return this.value;
|
||||||
});
|
});
|
||||||
|
def(AST_Assign, function(compressor, ignore_side_effects, cached, depth) {
|
||||||
|
if (!ignore_side_effects) return this;
|
||||||
|
if (this.operator != "=") return this;
|
||||||
|
var node = this.right;
|
||||||
|
var value = node._eval(compressor, ignore_side_effects, cached, depth);
|
||||||
|
return value === node ? this : value;
|
||||||
|
});
|
||||||
|
def(AST_Sequence, function(compressor, ignore_side_effects, cached, depth) {
|
||||||
|
if (!ignore_side_effects) return this;
|
||||||
|
var node = this.tail_node();
|
||||||
|
var value = node._eval(compressor, ignore_side_effects, cached, depth);
|
||||||
|
return value === node ? this : value;
|
||||||
|
});
|
||||||
def(AST_Function, function(compressor) {
|
def(AST_Function, function(compressor) {
|
||||||
if (compressor.option("unsafe")) {
|
if (compressor.option("unsafe")) {
|
||||||
var fn = function() {};
|
var fn = function() {};
|
||||||
@@ -3115,12 +3128,12 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
});
|
});
|
||||||
def(AST_Array, function(compressor, cached, depth) {
|
def(AST_Array, function(compressor, ignore_side_effects, cached, depth) {
|
||||||
if (compressor.option("unsafe")) {
|
if (compressor.option("unsafe")) {
|
||||||
var elements = [];
|
var elements = [];
|
||||||
for (var i = 0; i < this.elements.length; i++) {
|
for (var i = 0; i < this.elements.length; i++) {
|
||||||
var element = this.elements[i];
|
var element = this.elements[i];
|
||||||
var value = element._eval(compressor, cached, depth);
|
var value = element._eval(compressor, ignore_side_effects, cached, depth);
|
||||||
if (element === value) return this;
|
if (element === value) return this;
|
||||||
elements.push(value);
|
elements.push(value);
|
||||||
}
|
}
|
||||||
@@ -3128,7 +3141,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
});
|
});
|
||||||
def(AST_Object, function(compressor, cached, depth) {
|
def(AST_Object, function(compressor, ignore_side_effects, cached, depth) {
|
||||||
if (compressor.option("unsafe")) {
|
if (compressor.option("unsafe")) {
|
||||||
var val = {};
|
var val = {};
|
||||||
for (var i = 0; i < this.properties.length; i++) {
|
for (var i = 0; i < this.properties.length; i++) {
|
||||||
@@ -3137,14 +3150,14 @@ merge(Compressor.prototype, {
|
|||||||
if (key instanceof AST_Symbol) {
|
if (key instanceof AST_Symbol) {
|
||||||
key = key.name;
|
key = key.name;
|
||||||
} else if (key instanceof AST_Node) {
|
} else if (key instanceof AST_Node) {
|
||||||
key = key._eval(compressor, cached, depth);
|
key = key._eval(compressor, ignore_side_effects, cached, depth);
|
||||||
if (key === prop.key) return this;
|
if (key === prop.key) return this;
|
||||||
}
|
}
|
||||||
if (typeof Object.prototype[key] === 'function') {
|
if (typeof Object.prototype[key] === 'function') {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
if (prop.value instanceof AST_Function) continue;
|
if (prop.value instanceof AST_Function) continue;
|
||||||
val[key] = prop.value._eval(compressor, cached, depth);
|
val[key] = prop.value._eval(compressor, ignore_side_effects, cached, depth);
|
||||||
if (val[key] === prop.value) return this;
|
if (val[key] === prop.value) return this;
|
||||||
}
|
}
|
||||||
return val;
|
return val;
|
||||||
@@ -3152,7 +3165,7 @@ merge(Compressor.prototype, {
|
|||||||
return this;
|
return this;
|
||||||
});
|
});
|
||||||
var non_converting_unary = makePredicate("! typeof void");
|
var non_converting_unary = makePredicate("! typeof void");
|
||||||
def(AST_UnaryPrefix, function(compressor, cached, depth) {
|
def(AST_UnaryPrefix, function(compressor, ignore_side_effects, cached, depth) {
|
||||||
var e = this.expression;
|
var e = this.expression;
|
||||||
// Function would be evaluated to an array and so typeof would
|
// Function would be evaluated to an array and so typeof would
|
||||||
// incorrectly return 'object'. Hence making is a special case.
|
// incorrectly return 'object'. Hence making is a special case.
|
||||||
@@ -3164,7 +3177,7 @@ merge(Compressor.prototype, {
|
|||||||
return typeof function(){};
|
return typeof function(){};
|
||||||
}
|
}
|
||||||
if (!non_converting_unary[this.operator]) depth++;
|
if (!non_converting_unary[this.operator]) depth++;
|
||||||
var v = e._eval(compressor, cached, depth);
|
var v = e._eval(compressor, ignore_side_effects, cached, depth);
|
||||||
if (v === this.expression) return this;
|
if (v === this.expression) return this;
|
||||||
switch (this.operator) {
|
switch (this.operator) {
|
||||||
case "!": return !v;
|
case "!": return !v;
|
||||||
@@ -3187,12 +3200,12 @@ merge(Compressor.prototype, {
|
|||||||
return this;
|
return this;
|
||||||
});
|
});
|
||||||
var non_converting_binary = makePredicate("&& || === !==");
|
var non_converting_binary = makePredicate("&& || === !==");
|
||||||
def(AST_Binary, function(compressor, cached, depth) {
|
def(AST_Binary, function(compressor, ignore_side_effects, cached, depth) {
|
||||||
if (!non_converting_binary[this.operator]) depth++;
|
if (!non_converting_binary[this.operator]) depth++;
|
||||||
var left = this.left._eval(compressor, cached, depth);
|
var left = this.left._eval(compressor, ignore_side_effects, cached, depth);
|
||||||
if (left === this.left) return this;
|
if (left === this.left) return this;
|
||||||
if (this.operator == (left ? "||" : "&&")) return left;
|
if (this.operator == (left ? "||" : "&&")) return left;
|
||||||
var right = this.right._eval(compressor, cached, depth);
|
var right = this.right._eval(compressor, ignore_side_effects, cached, depth);
|
||||||
if (right === this.right) return this;
|
if (right === this.right) return this;
|
||||||
var result;
|
var result;
|
||||||
switch (this.operator) {
|
switch (this.operator) {
|
||||||
@@ -3235,14 +3248,14 @@ merge(Compressor.prototype, {
|
|||||||
return (match[1] || ".").length - 1 - (match[2] || "").slice(1);
|
return (match[1] || ".").length - 1 - (match[2] || "").slice(1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
def(AST_Conditional, function(compressor, cached, depth) {
|
def(AST_Conditional, function(compressor, ignore_side_effects, cached, depth) {
|
||||||
var condition = this.condition._eval(compressor, cached, depth);
|
var condition = this.condition._eval(compressor, ignore_side_effects, cached, depth);
|
||||||
if (condition === this.condition) return this;
|
if (condition === this.condition) return this;
|
||||||
var node = condition ? this.consequent : this.alternative;
|
var node = condition ? this.consequent : this.alternative;
|
||||||
var value = node._eval(compressor, cached, depth);
|
var value = node._eval(compressor, ignore_side_effects, cached, depth);
|
||||||
return value === node ? this : value;
|
return value === node ? this : value;
|
||||||
});
|
});
|
||||||
def(AST_SymbolRef, function(compressor, cached, depth) {
|
def(AST_SymbolRef, function(compressor, ignore_side_effects, cached, depth) {
|
||||||
var fixed = this.fixed_value();
|
var fixed = this.fixed_value();
|
||||||
if (!fixed) return this;
|
if (!fixed) return this;
|
||||||
var value;
|
var value;
|
||||||
@@ -3250,7 +3263,7 @@ merge(Compressor.prototype, {
|
|||||||
value = fixed._eval();
|
value = fixed._eval();
|
||||||
} else {
|
} else {
|
||||||
this._eval = return_this;
|
this._eval = return_this;
|
||||||
value = fixed._eval(compressor, cached, depth);
|
value = fixed._eval(compressor, ignore_side_effects, cached, depth);
|
||||||
delete this._eval;
|
delete this._eval;
|
||||||
if (value === fixed) return this;
|
if (value === fixed) return this;
|
||||||
fixed._eval = function() {
|
fixed._eval = function() {
|
||||||
@@ -3307,11 +3320,11 @@ merge(Compressor.prototype, {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
var regexp_props = makePredicate("global ignoreCase multiline source");
|
var regexp_props = makePredicate("global ignoreCase multiline source");
|
||||||
def(AST_PropAccess, function(compressor, cached, depth) {
|
def(AST_PropAccess, function(compressor, ignore_side_effects, cached, depth) {
|
||||||
if (compressor.option("unsafe")) {
|
if (compressor.option("unsafe")) {
|
||||||
var key = this.property;
|
var key = this.property;
|
||||||
if (key instanceof AST_Node) {
|
if (key instanceof AST_Node) {
|
||||||
key = key._eval(compressor, cached, depth);
|
key = key._eval(compressor, ignore_side_effects, cached, depth);
|
||||||
if (key === this.property) return this;
|
if (key === this.property) return this;
|
||||||
}
|
}
|
||||||
var exp = this.expression;
|
var exp = this.expression;
|
||||||
@@ -3321,7 +3334,7 @@ merge(Compressor.prototype, {
|
|||||||
if (!static_value || !static_value[key]) return this;
|
if (!static_value || !static_value[key]) return this;
|
||||||
val = global_objs[exp.name];
|
val = global_objs[exp.name];
|
||||||
} else {
|
} else {
|
||||||
val = exp._eval(compressor, cached, depth + 1);
|
val = exp._eval(compressor, ignore_side_effects, cached, depth + 1);
|
||||||
if (val == null || val === exp) return this;
|
if (val == null || val === exp) return this;
|
||||||
if (val instanceof RegExp) {
|
if (val instanceof RegExp) {
|
||||||
if (!regexp_props[key]) return this;
|
if (!regexp_props[key]) return this;
|
||||||
@@ -3340,7 +3353,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
});
|
});
|
||||||
def(AST_Call, function(compressor, cached, depth) {
|
def(AST_Call, function(compressor, ignore_side_effects, cached, depth) {
|
||||||
var exp = this.expression;
|
var exp = this.expression;
|
||||||
var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
|
var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
|
||||||
if (fn instanceof AST_Lambda) {
|
if (fn instanceof AST_Lambda) {
|
||||||
@@ -3361,14 +3374,14 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
fn.evaluating = true;
|
fn.evaluating = true;
|
||||||
var val = stat.value._eval(compressor, cached, depth);
|
var val = stat.value._eval(compressor, ignore_side_effects, cached, depth);
|
||||||
delete fn.evaluating;
|
delete fn.evaluating;
|
||||||
if (val === stat.value) return this;
|
if (val === stat.value) return this;
|
||||||
return val;
|
return val;
|
||||||
} else if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
|
} else if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
|
||||||
var key = exp.property;
|
var key = exp.property;
|
||||||
if (key instanceof AST_Node) {
|
if (key instanceof AST_Node) {
|
||||||
key = key._eval(compressor, cached, depth);
|
key = key._eval(compressor, ignore_side_effects, cached, depth);
|
||||||
if (key === exp.property) return this;
|
if (key === exp.property) return this;
|
||||||
}
|
}
|
||||||
var val;
|
var val;
|
||||||
@@ -3378,7 +3391,7 @@ merge(Compressor.prototype, {
|
|||||||
if (!static_fn || !static_fn[key]) return this;
|
if (!static_fn || !static_fn[key]) return this;
|
||||||
val = global_objs[e.name];
|
val = global_objs[e.name];
|
||||||
} else {
|
} else {
|
||||||
val = e._eval(compressor, cached, depth + 1);
|
val = e._eval(compressor, ignore_side_effects, cached, depth + 1);
|
||||||
if (val == null || val === e) return this;
|
if (val == null || val === e) return this;
|
||||||
var native_fn = native_fns[val.constructor.name];
|
var native_fn = native_fns[val.constructor.name];
|
||||||
if (!native_fn || !native_fn[key]) return this;
|
if (!native_fn || !native_fn[key]) return this;
|
||||||
@@ -3403,7 +3416,7 @@ merge(Compressor.prototype, {
|
|||||||
var values = [];
|
var values = [];
|
||||||
for (var i = 0; i < args.length; i++) {
|
for (var i = 0; i < args.length; i++) {
|
||||||
var arg = args[i];
|
var arg = args[i];
|
||||||
var value = arg._eval(compressor, cached, depth);
|
var value = arg._eval(compressor, ignore_side_effects, cached, depth);
|
||||||
if (arg === value) return;
|
if (arg === value) return;
|
||||||
values.push(value);
|
values.push(value);
|
||||||
}
|
}
|
||||||
@@ -4478,7 +4491,7 @@ merge(Compressor.prototype, {
|
|||||||
node.in_bool = true;
|
node.in_bool = true;
|
||||||
var value = node.value;
|
var value = node.value;
|
||||||
if (value) {
|
if (value) {
|
||||||
var ev = value.is_truthy() || value.tail_node().evaluate(compressor);
|
var ev = value.is_truthy() || value.evaluate(compressor, true);
|
||||||
if (!ev) {
|
if (!ev) {
|
||||||
value = value.drop_side_effect_free(compressor);
|
value = value.drop_side_effect_free(compressor);
|
||||||
node.value = value ? make_sequence(node.value, [
|
node.value = value ? make_sequence(node.value, [
|
||||||
@@ -4878,7 +4891,7 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
OPT(AST_Do, function(self, compressor) {
|
OPT(AST_Do, function(self, compressor) {
|
||||||
if (!compressor.option("loops")) return self;
|
if (!compressor.option("loops")) return self;
|
||||||
var cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
|
var cond = self.condition.is_truthy() || self.condition.evaluate(compressor, true);
|
||||||
if (!(cond instanceof AST_Node)) {
|
if (!(cond instanceof AST_Node)) {
|
||||||
if (cond) return make_node(AST_For, self, {
|
if (cond) return make_node(AST_For, self, {
|
||||||
body: make_node(AST_BlockStatement, self.body, {
|
body: make_node(AST_BlockStatement, self.body, {
|
||||||
@@ -5008,16 +5021,14 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
if (self.condition) {
|
if (self.condition) {
|
||||||
var cond = self.condition.evaluate(compressor);
|
var cond = self.condition.evaluate(compressor);
|
||||||
if (!(cond instanceof AST_Node)) {
|
|
||||||
if (cond) self.condition = null;
|
|
||||||
else if (!compressor.option("dead_code")) {
|
|
||||||
var orig = self.condition;
|
|
||||||
self.condition = make_node_from_constant(cond, self.condition);
|
|
||||||
self.condition = best_of_expression(self.condition.transform(compressor), orig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cond instanceof AST_Node) {
|
if (cond instanceof AST_Node) {
|
||||||
cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
|
cond = self.condition.is_truthy() || self.condition.evaluate(compressor, true);
|
||||||
|
} else if (cond) {
|
||||||
|
self.condition = null;
|
||||||
|
} else if (!compressor.option("dead_code")) {
|
||||||
|
var orig = self.condition;
|
||||||
|
self.condition = make_node_from_constant(cond, self.condition);
|
||||||
|
self.condition = best_of_expression(self.condition.transform(compressor), orig);
|
||||||
}
|
}
|
||||||
if (!cond) {
|
if (!cond) {
|
||||||
if (compressor.option("dead_code")) {
|
if (compressor.option("dead_code")) {
|
||||||
@@ -5109,7 +5120,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
if (compressor.option("dead_code")) {
|
if (compressor.option("dead_code")) {
|
||||||
if (cond instanceof AST_Node) {
|
if (cond instanceof AST_Node) {
|
||||||
cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
|
cond = self.condition.is_truthy() || self.condition.evaluate(compressor, true);
|
||||||
}
|
}
|
||||||
if (!cond) {
|
if (!cond) {
|
||||||
AST_Node.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
|
AST_Node.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
|
||||||
@@ -5255,7 +5266,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
if (!compressor.option("dead_code")) return self;
|
if (!compressor.option("dead_code")) return self;
|
||||||
if (value instanceof AST_Node) {
|
if (value instanceof AST_Node) {
|
||||||
value = self.expression.tail_node().evaluate(compressor);
|
value = self.expression.evaluate(compressor, true);
|
||||||
}
|
}
|
||||||
var decl = [];
|
var decl = [];
|
||||||
var body = [];
|
var body = [];
|
||||||
@@ -5277,7 +5288,7 @@ merge(Compressor.prototype, {
|
|||||||
eliminate_branch(branch, body[body.length - 1]);
|
eliminate_branch(branch, body[body.length - 1]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (exp instanceof AST_Node) exp = branch.expression.tail_node().evaluate(compressor);
|
if (exp instanceof AST_Node) exp = branch.expression.evaluate(compressor, true);
|
||||||
if (exp === value) {
|
if (exp === value) {
|
||||||
exact_match = branch;
|
exact_match = branch;
|
||||||
if (default_branch) {
|
if (default_branch) {
|
||||||
@@ -6461,7 +6472,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
// (x || false) && y => x ? y : false
|
// (x || false) && y => x ? y : false
|
||||||
if (self.left.operator == "||") {
|
if (self.left.operator == "||") {
|
||||||
var lr = self.left.right.tail_node().evaluate(compressor);
|
var lr = self.left.right.evaluate(compressor, true);
|
||||||
if (!lr) return make_node(AST_Conditional, self, {
|
if (!lr) return make_node(AST_Conditional, self, {
|
||||||
condition: self.left.left,
|
condition: self.left.left,
|
||||||
consequent: self.right,
|
consequent: self.right,
|
||||||
@@ -6495,7 +6506,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
// x && true || y => x ? true : y
|
// x && true || y => x ? true : y
|
||||||
if (self.left.operator == "&&") {
|
if (self.left.operator == "&&") {
|
||||||
var lr = self.left.right.is_truthy() || self.left.right.tail_node().evaluate(compressor);
|
var lr = self.left.right.is_truthy() || self.left.right.evaluate(compressor, true);
|
||||||
if (lr && !(lr instanceof AST_Node)) return make_node(AST_Conditional, self, {
|
if (lr && !(lr instanceof AST_Node)) return make_node(AST_Conditional, self, {
|
||||||
condition: self.left.left,
|
condition: self.left.left,
|
||||||
consequent: self.left.right,
|
consequent: self.left.right,
|
||||||
|
|||||||
@@ -2071,13 +2071,8 @@ issue_1670_6: {
|
|||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
(function(a) {
|
(function(a) {
|
||||||
switch (1) {
|
a = 1;
|
||||||
case a = 1:
|
console.log(a);
|
||||||
console.log(a);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.log(2);
|
|
||||||
}
|
|
||||||
})(1);
|
})(1);
|
||||||
}
|
}
|
||||||
expect_stdout: "1"
|
expect_stdout: "1"
|
||||||
|
|||||||
Reference in New Issue
Block a user