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