Merge branch 'master' into harmony-v2.8.6

This commit is contained in:
alexlamsl
2017-03-05 16:03:56 +08:00
20 changed files with 1089 additions and 142 deletions

View File

@@ -54,6 +54,7 @@ function Compressor(options, false_by_default) {
drop_debugger : !false_by_default,
unsafe : false,
unsafe_comps : false,
unsafe_math : false,
unsafe_proto : false,
conditionals : !false_by_default,
comparisons : !false_by_default,
@@ -80,6 +81,7 @@ function Compressor(options, false_by_default) {
ecma : 5,
drop_console : false,
angular : false,
expression : false,
warnings : true,
global_defs : {},
passes : 1,
@@ -116,12 +118,18 @@ Compressor.prototype = new TreeTransformer;
merge(Compressor.prototype, {
option: function(key) { return this.options[key] },
compress: function(node) {
if (this.option("expression")) {
node = node.process_expression(true);
}
var passes = +this.options.passes || 1;
for (var pass = 0; pass < passes && pass < 3; ++pass) {
if (pass > 0 || this.option("reduce_vars"))
node.reset_opt_flags(this, true);
node = node.transform(this);
}
if (this.option("expression")) {
node = node.process_expression(false);
}
return node;
},
warn: function(text, props) {
@@ -178,8 +186,45 @@ merge(Compressor.prototype, {
return this.print_to_string() == node.print_to_string();
});
AST_Node.DEFMETHOD("process_expression", function(insert) {
var self = this;
var tt = new TreeTransformer(function(node) {
if (insert && node instanceof AST_SimpleStatement) {
return make_node(AST_Return, node, {
value: node.body
});
}
if (!insert && node instanceof AST_Return) {
return make_node(AST_SimpleStatement, node, {
body: node.value || make_node(AST_Undefined, node)
});
}
if (node instanceof AST_Lambda && node !== self) {
return node;
}
if (node instanceof AST_Block) {
var index = node.body.length - 1;
if (index >= 0) {
node.body[index] = node.body[index].transform(tt);
}
}
if (node instanceof AST_If) {
node.body = node.body.transform(tt);
if (node.alternative) {
node.alternative = node.alternative.transform(tt);
}
}
if (node instanceof AST_With) {
node.body = node.body.transform(tt);
}
return node;
});
return self.transform(tt);
});
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan){
var reduce_vars = rescan && compressor.option("reduce_vars");
var ie8 = !compressor.option("screw_ie8");
var safe_ids = [];
push();
var suppressor = new TreeWalker(function(node) {
@@ -189,7 +234,7 @@ merge(Compressor.prototype, {
d.fixed = false;
}
});
var tw = new TreeWalker(function(node){
var tw = new TreeWalker(function(node, descend){
if (!(node instanceof AST_Directive || node instanceof AST_Constant)) {
node._squeezed = false;
node._optimized = false;
@@ -204,6 +249,9 @@ merge(Compressor.prototype, {
d.fixed = false;
}
}
if (ie8 && node instanceof AST_SymbolCatch) {
node.definition().fixed = false;
}
if (node instanceof AST_VarDef) {
if (node.name instanceof AST_Destructuring) {
node.name.walk(suppressor);
@@ -262,6 +310,12 @@ merge(Compressor.prototype, {
pop();
return true;
}
if (node instanceof AST_Catch) {
push();
descend();
pop();
return true;
}
}
});
this.walk(tw);
@@ -494,11 +548,9 @@ merge(Compressor.prototype, {
// Constant single use vars can be replaced in any scope.
if (var_decl.value.is_constant()) {
var ctt = new TreeTransformer(function(node) {
if (node === ref) {
var parent = ctt.parent();
if (!(parent instanceof AST_ForIn && parent.init === node)) {
return replace_var(node, parent, true);
}
if (node === ref
&& !ctt.find_parent(AST_ForIn)) {
return replace_var(node, ctt.parent(), true);
}
});
stat.transform(ctt);
@@ -568,10 +620,7 @@ merge(Compressor.prototype, {
return statements;
function is_lvalue(node, parent) {
return node instanceof AST_SymbolRef && (
(parent instanceof AST_Assign && node === parent.left)
|| (parent instanceof AST_Unary && parent.expression === node
&& (parent.operator == "++" || parent.operator == "--")));
return node instanceof AST_SymbolRef && isLHS(node, parent);
}
function replace_var(node, parent, is_constant) {
if (is_lvalue(node, parent)) return node;
@@ -744,7 +793,7 @@ merge(Compressor.prototype, {
CHANGED = true;
stat = stat.clone();
stat.alternative = ret[0] || make_node(AST_Return, stat, {
value: make_node(AST_Undefined, stat)
value: null
});
ret[0] = stat.transform(compressor);
continue loop;
@@ -777,7 +826,7 @@ merge(Compressor.prototype, {
&& !stat.alternative) {
CHANGED = true;
ret.push(make_node(AST_Return, ret[0], {
value: make_node(AST_Undefined, ret[0])
value: null
}).transform(compressor));
ret.unshift(stat);
continue loop;
@@ -1034,6 +1083,10 @@ merge(Compressor.prototype, {
}));
};
function is_undefined(node) {
return node instanceof AST_Undefined || node.is_undefined;
}
/* -----[ boolean/negation helpers ]----- */
// methods to determine whether an expression has a boolean result type
@@ -1064,6 +1117,34 @@ merge(Compressor.prototype, {
node.DEFMETHOD("is_boolean", func);
});
// methods to determine if an expression has a numeric result type
(function (def){
def(AST_Node, return_false);
def(AST_Number, return_true);
var unary = makePredicate("+ - ~ ++ --");
def(AST_Unary, function(){
return unary(this.operator);
});
var binary = makePredicate("- * / % & | ^ << >> >>>");
def(AST_Binary, function(compressor){
return binary(this.operator) || this.operator == "+"
&& this.left.is_number(compressor)
&& this.right.is_number(compressor);
});
var assign = makePredicate("-= *= /= %= &= |= ^= <<= >>= >>>=");
def(AST_Assign, function(compressor){
return assign(this.operator) || this.right.is_number(compressor);
});
def(AST_Seq, function(compressor){
return this.cdr.is_number(compressor);
});
def(AST_Conditional, function(compressor){
return this.consequent.is_number(compressor) && this.alternative.is_number(compressor);
});
})(function(node, func){
node.DEFMETHOD("is_number", func);
});
// methods to determine if an expression has a string result type
(function (def){
def(AST_Node, function(){ return false });
@@ -1092,7 +1173,7 @@ merge(Compressor.prototype, {
});
function isLHS(node, parent) {
return parent instanceof AST_Unary && (parent.operator === "++" || parent.operator === "--")
return parent instanceof AST_Unary && (parent.operator == "++" || parent.operator == "--")
|| parent instanceof AST_Assign && parent.left === node;
}
@@ -2117,7 +2198,14 @@ merge(Compressor.prototype, {
def(AST_Constant, return_null);
def(AST_This, return_null);
def(AST_Call, function(compressor, first_in_statement){
if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) return this;
if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) {
if (this.expression instanceof AST_Function) {
var node = this.clone();
node.expression = node.expression.process_expression(false);
return node;
}
return this;
}
if (this.pure) {
compressor.warn("Dropping __PURE__ call [{file}:{line},{col}]", this.start);
this.pure.value = this.pure.value.replace(/[@#]__PURE__/g, ' ');
@@ -2441,8 +2529,8 @@ merge(Compressor.prototype, {
return make_node(self.body.CTOR, self, {
value: make_node(AST_Conditional, self, {
condition : self.condition,
consequent : self.body.value || make_node(AST_Undefined, self.body).optimize(compressor),
alternative : self.alternative.value || make_node(AST_Undefined, self.alternative).optimize(compressor)
consequent : self.body.value || make_node(AST_Undefined, self.body),
alternative : self.alternative.value || make_node(AST_Undefined, self.alternative)
})
}).transform(compressor);
}
@@ -2623,12 +2711,13 @@ merge(Compressor.prototype, {
});
OPT(AST_Call, function(self, compressor){
var exp = self.expression;
if (compressor.option("unused")
&& self.expression instanceof AST_Function
&& !self.expression.uses_arguments
&& !self.expression.uses_eval
&& self.args.length > self.expression.argnames.length) {
var end = self.expression.argnames.length;
&& exp instanceof AST_Function
&& !exp.uses_arguments
&& !exp.uses_eval
&& self.args.length > exp.argnames.length) {
var end = exp.argnames.length;
for (var i = end, len = self.args.length; i < len; i++) {
var node = self.args[i].drop_side_effect_free(compressor);
if (node) {
@@ -2638,7 +2727,6 @@ merge(Compressor.prototype, {
self.args.length = end;
}
if (compressor.option("unsafe")) {
var exp = self.expression;
if (exp instanceof AST_SymbolRef && exp.undeclared()) {
switch (exp.name) {
case "Array":
@@ -2817,17 +2905,24 @@ merge(Compressor.prototype, {
return best_of(self, node);
}
}
if (compressor.option("side_effects")) {
if (self.expression instanceof AST_Function
&& !self.expression.is_generator
&& self.args.length == 0
&& !AST_Block.prototype.has_side_effects.call(self.expression, compressor)) {
return make_node(AST_Undefined, self).transform(compressor);
if (exp instanceof AST_Function && !self.expression.is_generator) {
if (exp.body[0] instanceof AST_Return) {
var value = exp.body[0].value;
if (!value || value.is_constant()) {
var args = self.args.concat(value || make_node(AST_Undefined, self));
return AST_Seq.from_array(args).transform(compressor);
}
}
if (compressor.option("side_effects")) {
if (!AST_Block.prototype.has_side_effects.call(exp, compressor)) {
var args = self.args.concat(make_node(AST_Undefined, self));
return AST_Seq.from_array(args).transform(compressor);
}
}
}
if (compressor.option("drop_console")) {
if (self.expression instanceof AST_PropAccess) {
var name = self.expression.expression;
if (exp instanceof AST_PropAccess) {
var name = exp.expression;
while (name.expression) {
name = name.expression;
}
@@ -2838,12 +2933,6 @@ merge(Compressor.prototype, {
}
}
}
if (self.args.length == 0
&& self.expression instanceof AST_Function
&& self.expression.body[0] instanceof AST_Return
&& self.expression.body[0].value.is_constant()) {
return self.expression.body[0].value;
}
if (compressor.option("negate_iife")
&& compressor.parent() instanceof AST_SimpleStatement
&& is_iife_call(self)) {
@@ -2875,23 +2964,37 @@ merge(Compressor.prototype, {
self.car = self.car.drop_side_effect_free(compressor, first_in_statement(compressor));
if (!self.car) return maintain_this_binding(compressor.parent(), self, self.cdr);
if (compressor.option("cascade")) {
var left;
if (self.car instanceof AST_Assign
&& !self.car.left.has_side_effects(compressor)) {
if (self.car.left.equivalent_to(self.cdr)) {
return self.car;
}
if (self.cdr instanceof AST_Call
&& self.cdr.expression.equivalent_to(self.car.left)) {
self.cdr.expression = self.car;
return self.cdr;
}
left = self.car.left;
} else if (self.car instanceof AST_UnaryPostfix
&& (self.car.operator == "++" || self.car.operator == "--")) {
left = self.car.expression;
}
if (!self.car.has_side_effects(compressor)
&& self.car.equivalent_to(self.cdr)) {
return self.car;
if (left) {
var parent, field;
var cdr = self.cdr;
while (true) {
if (cdr.equivalent_to(left)) {
if (parent) {
parent[field] = self.car;
return self.cdr;
}
return self.car;
}
if (cdr instanceof AST_Binary && !(cdr instanceof AST_Assign)) {
field = cdr.left.is_constant() ? "right" : "left";
} else if (cdr instanceof AST_Call
|| cdr instanceof AST_Unary && cdr.operator != "++" && cdr.operator != "--") {
field = "expression";
} else break;
parent = cdr;
cdr = cdr[field];
}
}
}
if (self.cdr instanceof AST_Undefined) {
if (is_undefined(self.cdr)) {
return make_node(AST_UnaryPrefix, self, {
operator : "void",
expression : self.car
@@ -2930,7 +3033,7 @@ merge(Compressor.prototype, {
self.expression = e;
return self;
} else {
return make_node(AST_Undefined, self);
return make_node(AST_Undefined, self).transform(compressor);
}
}
if (compressor.option("booleans") && compressor.in_boolean_context()) {
@@ -3003,8 +3106,14 @@ merge(Compressor.prototype, {
right: rhs[0]
}).optimize(compressor);
}
function reverse(op, force) {
if (force || !(self.left.has_side_effects(compressor) || self.right.has_side_effects(compressor))) {
function reversible() {
return self.left instanceof AST_Constant
|| self.right instanceof AST_Constant
|| !self.left.has_side_effects(compressor)
&& !self.right.has_side_effects(compressor);
}
function reverse(op) {
if (reversible()) {
if (op) self.operator = op;
var tmp = self.left;
self.left = self.right;
@@ -3020,7 +3129,7 @@ merge(Compressor.prototype, {
if (!(self.left instanceof AST_Binary
&& PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
reverse(null, true);
reverse();
}
}
if (/^[!=]==?$/.test(self.operator)) {
@@ -3055,6 +3164,7 @@ merge(Compressor.prototype, {
case "===":
case "!==":
if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
(self.left.is_number(compressor) && self.right.is_number(compressor)) ||
(self.left.is_boolean() && self.right.is_boolean())) {
self.operator = self.operator.substr(0, 2);
}
@@ -3192,7 +3302,10 @@ merge(Compressor.prototype, {
}
break;
}
if (self.operator == "+") {
var associative = true;
switch (self.operator) {
case "+":
// "foo" + ("bar" + x) => "foobar" + x
if (self.left instanceof AST_Constant
&& self.right instanceof AST_Binary
&& self.right.operator == "+"
@@ -3200,7 +3313,7 @@ merge(Compressor.prototype, {
&& self.right.is_string(compressor)) {
self = make_node(AST_Binary, self, {
operator: "+",
left: make_node(AST_String, null, {
left: make_node(AST_String, self.left, {
value: "" + self.left.getValue() + self.right.left.getValue(),
start: self.left.start,
end: self.right.left.end
@@ -3208,6 +3321,7 @@ merge(Compressor.prototype, {
right: self.right.right
});
}
// (x + "foo") + "bar" => x + "foobar"
if (self.right instanceof AST_Constant
&& self.left instanceof AST_Binary
&& self.left.operator == "+"
@@ -3216,13 +3330,14 @@ merge(Compressor.prototype, {
self = make_node(AST_Binary, self, {
operator: "+",
left: self.left.left,
right: make_node(AST_String, null, {
right: make_node(AST_String, self.right, {
value: "" + self.left.right.getValue() + self.right.getValue(),
start: self.left.right.start,
end: self.right.end
})
});
}
// (x + "foo") + ("bar" + y) => (x + "foobar") + y
if (self.left instanceof AST_Binary
&& self.left.operator == "+"
&& self.left.is_string(compressor)
@@ -3236,7 +3351,7 @@ merge(Compressor.prototype, {
left: make_node(AST_Binary, self.left, {
operator: "+",
left: self.left.left,
right: make_node(AST_String, null, {
right: make_node(AST_String, self.left.right, {
value: "" + self.left.right.getValue() + self.right.left.getValue(),
start: self.left.right.start,
end: self.right.left.end
@@ -3245,6 +3360,122 @@ merge(Compressor.prototype, {
right: self.right.right
});
}
// a + -b => a - b
if (self.right instanceof AST_UnaryPrefix
&& self.right.operator == "-"
&& self.left.is_number(compressor)) {
self = make_node(AST_Binary, self, {
operator: "-",
left: self.left,
right: self.right.expression
});
}
// -a + b => b - a
if (self.left instanceof AST_UnaryPrefix
&& self.left.operator == "-"
&& reversible()
&& self.right.is_number(compressor)) {
self = make_node(AST_Binary, self, {
operator: "-",
left: self.right,
right: self.left.expression
});
}
case "*":
associative = compressor.option("unsafe_math");
case "&":
case "|":
case "^":
// a + +b => +b + a
if (self.left.is_number(compressor)
&& self.right.is_number(compressor)
&& reversible()
&& !(self.left instanceof AST_Binary
&& self.left.operator != self.operator
&& PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
var reversed = make_node(AST_Binary, self, {
operator: self.operator,
left: self.right,
right: self.left
});
if (self.right instanceof AST_Constant
&& !(self.left instanceof AST_Constant)) {
self = best_of(reversed, self);
} else {
self = best_of(self, reversed);
}
}
if (associative && self.is_number(compressor)) {
// a + (b + c) => (a + b) + c
if (self.right instanceof AST_Binary
&& self.right.operator == self.operator) {
self = make_node(AST_Binary, self, {
operator: self.operator,
left: make_node(AST_Binary, self.left, {
operator: self.operator,
left: self.left,
right: self.right.left,
start: self.left.start,
end: self.right.left.end
}),
right: self.right.right
});
}
// (n + 2) + 3 => 5 + n
// (2 * n) * 3 => 6 + n
if (self.right instanceof AST_Constant
&& self.left instanceof AST_Binary
&& self.left.operator == self.operator) {
if (self.left.left instanceof AST_Constant) {
self = make_node(AST_Binary, self, {
operator: self.operator,
left: make_node(AST_Binary, self.left, {
operator: self.operator,
left: self.left.left,
right: self.right,
start: self.left.left.start,
end: self.right.end
}),
right: self.left.right
});
} else if (self.left.right instanceof AST_Constant) {
self = make_node(AST_Binary, self, {
operator: self.operator,
left: make_node(AST_Binary, self.left, {
operator: self.operator,
left: self.left.right,
right: self.right,
start: self.left.right.start,
end: self.right.end
}),
right: self.left.left
});
}
}
// (a | 1) | (2 | d) => (3 | a) | b
if (self.left instanceof AST_Binary
&& self.left.operator == self.operator
&& self.left.right instanceof AST_Constant
&& self.right instanceof AST_Binary
&& self.right.operator == self.operator
&& self.right.left instanceof AST_Constant) {
self = make_node(AST_Binary, self, {
operator: self.operator,
left: make_node(AST_Binary, self.left, {
operator: self.operator,
left: make_node(AST_Binary, self.left.left, {
operator: self.operator,
left: self.left.right,
right: self.right.left,
start: self.left.right.start,
end: self.right.left.end
}),
right: self.left.left
}),
right: self.right.right
});
}
}
}
}
// x && (y && z) ==> x && y && z
@@ -3277,11 +3508,13 @@ merge(Compressor.prototype, {
return def;
}
// testing against !self.scope.uses_with first is an optimization
if (self.undeclared() && !isLHS(self, compressor.parent())
if (compressor.option("screw_ie8")
&& self.undeclared()
&& !isLHS(self, compressor.parent())
&& (!self.scope.uses_with || !compressor.find_parent(AST_With))) {
switch (self.name) {
case "undefined":
return make_node(AST_Undefined, self);
return make_node(AST_Undefined, self).transform(compressor);
case "NaN":
return make_node(AST_NaN, self).transform(compressor);
case "Infinity":
@@ -3324,11 +3557,13 @@ merge(Compressor.prototype, {
var scope = compressor.find_parent(AST_Scope);
var undef = scope.find_variable("undefined");
if (undef) {
return make_node(AST_SymbolRef, self, {
var ref = make_node(AST_SymbolRef, self, {
name : "undefined",
scope : scope,
thedef : undef
});
ref.is_undefined = true;
return ref;
}
}
return self;
@@ -3631,7 +3866,7 @@ merge(Compressor.prototype, {
OPT(AST_RegExp, literals_in_boolean_context);
OPT(AST_Return, function(self, compressor){
if (self.value instanceof AST_Undefined) {
if (self.value && is_undefined(self.value)) {
self.value = null;
}
return self;