fix corner cases with spread syntax (#4358)

This commit is contained in:
Alex Lam S.L
2020-12-10 22:59:21 +00:00
committed by GitHub
parent 77e1bda426
commit 57105b299e
5 changed files with 357 additions and 60 deletions

View File

@@ -3662,6 +3662,20 @@ merge(Compressor.prototype, {
],
});
// Accomodate when compress option evaluate=false
// as well as the common constant expressions !0 and -1
(function(def) {
def(AST_Node, return_false);
def(AST_Constant, return_true);
def(AST_RegExp, return_false);
var unaryPrefix = makePredicate("! ~ - + void");
def(AST_UnaryPrefix, function() {
return unaryPrefix[this.operator] && this.expression instanceof AST_Constant;
});
})(function(node, func) {
node.DEFMETHOD("is_constant", func);
});
// methods to evaluate a constant expression
(function(def) {
// If the node has been successfully reduced to a constant,
@@ -3686,18 +3700,6 @@ merge(Compressor.prototype, {
if (typeof val == "function" || typeof val == "object") return this;
return val;
});
var unaryPrefix = makePredicate("! ~ - + void");
AST_Node.DEFMETHOD("is_constant", function() {
// Accomodate when compress option evaluate=false
// as well as the common constant expressions !0 and -1
if (this instanceof AST_Constant) {
return !(this instanceof AST_RegExp);
} else {
return this instanceof AST_UnaryPrefix
&& this.expression instanceof AST_Constant
&& unaryPrefix[this.operator];
}
});
var scan_modified = new TreeWalker(function(node) {
if (node instanceof AST_Assign) modified(node.left);
if (node instanceof AST_Unary && unary_arithmetic[node.operator]) modified(node.expression);
@@ -6453,20 +6455,24 @@ merge(Compressor.prototype, {
}
});
var values = trim(exprs, compressor, first_in_statement, function(node, compressor, first_in_statement) {
var exp = node.expression;
if (exp instanceof AST_Object) return node;
if (exp instanceof AST_PropAccess) return node;
var exp = node.expression.tail_node();
if (exp instanceof AST_SymbolRef) {
exp = exp.fixed_value();
if (!exp) return node;
if (exp instanceof AST_SymbolRef) return node;
if (exp instanceof AST_PropAccess) return node;
if (!(exp instanceof AST_Object)) return null;
return all(exp.properties, function(prop) {
return !(prop instanceof AST_ObjectGetter || prop instanceof AST_Spread);
}) ? null : node;
exp = exp.tail_node();
}
return exp.drop_side_effect_free(compressor, first_in_statement);
if (exp instanceof AST_Array
|| exp.TYPE == "Binary" && !lazy_op[exp.operator]
|| exp instanceof AST_Constant
|| exp instanceof AST_Lambda
|| exp instanceof AST_Object && all(exp.properties, function(prop) {
return !(prop instanceof AST_ObjectGetter || prop instanceof AST_Spread);
})
|| exp instanceof AST_This
|| exp instanceof AST_Unary) {
return node.expression.drop_side_effect_free(compressor, first_in_statement);
}
return node;
});
if (!values) return null;
if (values === exprs && !all(values, function(node) {
@@ -10043,26 +10049,36 @@ merge(Compressor.prototype, {
OPT(AST_Object, function(self, compressor) {
if (!compressor.option("objects")) return self;
var changed = false;
var computed_int = false;
var has_computed = false;
var found = false;
var generated = false;
var keep_duplicate = compressor.has_directive("use strict");
var keys = new Dictionary();
var values = [];
self.properties.forEach(function(prop) {
if (!(prop instanceof AST_Spread)) return process(prop);
found = true;
var exp = prop.expression;
if (exp instanceof AST_Object && all(exp.properties, function(node) {
return node instanceof AST_ObjectKeyVal;
if (compressor.option("spread") && exp instanceof AST_Object && all(exp.properties, function(prop) {
return !(prop instanceof AST_ObjectGetter || prop instanceof AST_Spread);
})) {
changed = true;
has_computed = true;
exp.properties.forEach(process);
exp.properties.forEach(function(prop) {
if (prop instanceof AST_ObjectKeyVal) process(prop);
});
} else {
generated = true;
flush();
values.push(prop);
}
});
flush();
if (!changed) return self;
if (found && generated && values.length == 1) {
var value = values[0];
if (value instanceof AST_ObjectProperty && value.key instanceof AST_Number) {
value.key = "" + value.key.value;
}
}
return changed ? make_node(AST_Object, self, {
properties: values
}) : self;
@@ -10071,8 +10087,8 @@ merge(Compressor.prototype, {
keys.each(function(props) {
if (props.length == 1) return values.push(props[0]);
changed = true;
var tail = keep_duplicate && props.pop();
values.push(make_node(AST_ObjectKeyVal, self, {
var tail = keep_duplicate && !generated && props.pop();
values.push(props.length == 1 ? props[0] : make_node(AST_ObjectKeyVal, self, {
key: props[0].key,
value: make_sequence(self, props.map(function(prop) {
return prop.value;
@@ -10086,9 +10102,13 @@ merge(Compressor.prototype, {
function process(prop) {
var key = prop.key;
if (key instanceof AST_Node) {
has_computed = true;
found = true;
key = key.evaluate(compressor);
if (key !== prop.key) key = prop.key = "" + key;
if (key === prop.key) {
generated = true;
} else {
key = prop.key = "" + key;
}
}
if (prop instanceof AST_ObjectKeyVal && typeof key == "string") {
if (prop.value.has_side_effects(compressor)) flush();
@@ -10097,8 +10117,8 @@ merge(Compressor.prototype, {
flush();
values.push(prop);
}
if (has_computed && !computed_int && typeof key == "string" && /^[0-9]+$/.test(key)) {
computed_int = true;
if (found && !generated && typeof key == "string" && /^[0-9]+$/.test(key)) {
generated = true;
prop.key = make_node(AST_Number, prop, {
value: +key
});