Merge branch 'master' into harmony-v3.3.10

This commit is contained in:
alexlamsl
2018-02-08 18:26:42 +08:00
12 changed files with 1201 additions and 119 deletions

View File

@@ -327,6 +327,7 @@ merge(Compressor.prototype, {
function reset_def(compressor, def) {
def.assignments = 0;
def.chained = false;
def.direct_access = false;
def.escaped = false;
if (def.scope.uses_eval || def.scope.uses_with) {
@@ -512,19 +513,27 @@ merge(Compressor.prototype, {
node.left.walk(suppressor);
return;
}
if (node.operator != "=" || !(node.left instanceof AST_SymbolRef)) return;
if (!(node.left instanceof AST_SymbolRef)) return;
var d = node.left.definition();
if (safe_to_assign(tw, d, node.right)) {
d.references.push(node.left);
d.assignments++;
d.fixed = function() {
return node.right;
};
mark(tw, d, false);
node.right.walk(tw);
mark(tw, d, true);
return true;
}
var fixed = d.fixed;
if (!fixed && node.operator != "=") return;
if (!safe_to_assign(tw, d, node.right)) return;
d.references.push(node.left);
d.assignments++;
if (node.operator != "=") d.chained = true;
d.fixed = node.operator == "=" ? function() {
return node.right;
} : function() {
return make_node(AST_Binary, node, {
operator: node.operator.slice(0, -1),
left: fixed instanceof AST_Node ? fixed : fixed(),
right: node.right
});
};
mark(tw, d, false);
node.right.walk(tw);
mark(tw, d, true);
return true;
});
def(AST_Binary, function(tw) {
if (!lazy_op(this.operator)) return;
@@ -722,6 +731,32 @@ merge(Compressor.prototype, {
if (this.bfinally) this.bfinally.walk(tw);
return true;
});
def(AST_Unary, function(tw, descend) {
var node = this;
if (node.operator != "++" && node.operator != "--") return;
if (!(node.expression instanceof AST_SymbolRef)) return;
var d = node.expression.definition();
var fixed = d.fixed;
if (!fixed) return;
if (!safe_to_assign(tw, d, true)) return;
d.references.push(node.expression);
d.assignments++;
d.chained = true;
d.fixed = function() {
return make_node(AST_Binary, node, {
operator: node.operator.slice(0, -1),
left: make_node(AST_UnaryPrefix, node, {
operator: "+",
expression: fixed instanceof AST_Node ? fixed : fixed()
}),
right: make_node(AST_Number, node, {
value: 1
})
});
};
mark(tw, d, true);
return true;
});
def(AST_VarDef, function(tw, descend) {
var node = this;
if (node.name instanceof AST_Destructuring) {
@@ -729,19 +764,19 @@ merge(Compressor.prototype, {
return;
}
var d = node.name.definition();
if (safe_to_assign(tw, d, node.value)) {
if (node.value) {
if (node.value) {
if (safe_to_assign(tw, d, node.value)) {
d.fixed = function() {
return node.value;
};
tw.loop_ids[d.id] = tw.in_loop;
mark(tw, d, false);
descend();
mark(tw, d, true);
return true;
} else {
d.fixed = false;
}
mark(tw, d, true);
return true;
} else if (node.value) {
d.fixed = false;
}
});
def(AST_While, function(tw, descend, compressor) {
@@ -995,32 +1030,11 @@ merge(Compressor.prototype, {
var stat_index = statements.length;
var scanner = new TreeTransformer(function(node, descend) {
if (abort) return node;
// Scan case expressions first in a switch statement
if (node instanceof AST_Switch) {
if (!hit) {
if (node !== hit_stack[hit_index]) return node;
hit_index++;
}
node.expression = node.expression.transform(scanner);
for (var i = 0, len = node.body.length; !abort && i < len; i++) {
var branch = node.body[i];
if (branch instanceof AST_Case) {
if (!hit) {
if (branch !== hit_stack[hit_index]) continue;
hit_index++;
}
branch.expression = branch.expression.transform(scanner);
if (side_effects || !replace_all) break;
}
}
abort = true;
return node;
}
// Skip nodes before `candidate` as quickly as possible
if (!hit) {
if (node !== hit_stack[hit_index]) return node;
hit_index++;
if (hit_index < hit_stack.length) return;
if (hit_index < hit_stack.length) return handle_custom_scan_order(node);
hit = true;
stop_after = find_stop(node, 0);
if (stop_after === node) abort = true;
@@ -1034,6 +1048,7 @@ merge(Compressor.prototype, {
|| node instanceof AST_Debugger
|| node instanceof AST_Destructuring
|| node instanceof AST_IterationStatement && !(node instanceof AST_For)
|| node instanceof AST_LoopControl
|| node instanceof AST_Try
|| node instanceof AST_With
|| parent instanceof AST_For && node !== parent.init
@@ -1042,10 +1057,21 @@ merge(Compressor.prototype, {
abort = true;
return node;
}
// Stop only if candidate is found within conditional branches
if (!stop_if_hit && (side_effects || !replace_all)
&& (parent instanceof AST_Binary && lazy_op(parent.operator) && parent.left !== node
|| parent instanceof AST_Conditional && parent.condition !== node
|| parent instanceof AST_If && parent.condition !== node)) {
stop_if_hit = parent;
}
// Replace variable with assignment when found
if (can_replace
&& !(node instanceof AST_SymbolDeclaration)
&& lhs.equivalent_to(node)) {
if (stop_if_hit) {
abort = true;
return node;
}
if (is_lhs(node, parent)) {
if (value_def) replaced++;
return node;
@@ -1090,26 +1116,25 @@ merge(Compressor.prototype, {
var sym;
if (node instanceof AST_Call
|| node instanceof AST_Exit
&& (side_effects || lhs instanceof AST_PropAccess || may_modify(lhs))
|| node instanceof AST_PropAccess
&& (side_effects || node.expression.may_throw_on_access(compressor))
|| node instanceof AST_SymbolRef
&& (lvalues[node.name]
|| side_effects && !references_in_scope(node.definition()))
|| (sym = lhs_or_def(node))
&& (lvalues[node.name] || side_effects && may_modify(node))
|| node instanceof AST_VarDef && node.value
&& (node.name.name in lvalues || side_effects && may_modify(node.name))
|| (sym = is_lhs(node.left, node))
&& (sym instanceof AST_PropAccess || sym.name in lvalues)
|| may_throw
&& (in_try ? node.has_side_effects(compressor) : side_effects_external(node))
|| (side_effects || !replace_all)
&& (parent instanceof AST_Binary && lazy_op(parent.operator)
|| parent instanceof AST_Conditional
|| parent instanceof AST_If)) {
&& (in_try ? node.has_side_effects(compressor) : side_effects_external(node))) {
stop_after = node;
if (node instanceof AST_Scope) abort = true;
}
// Skip (non-executed) functions
if (node instanceof AST_Scope) return node;
return handle_custom_scan_order(node);
}, function(node) {
if (!abort && stop_after === node) abort = true;
if (abort) return;
if (stop_after === node) abort = true;
if (stop_if_hit === node) stop_if_hit = null;
});
var multi_replacer = new TreeTransformer(function(node) {
if (abort) return node;
@@ -1148,6 +1173,7 @@ merge(Compressor.prototype, {
var candidate = hit_stack[hit_stack.length - 1];
var value_def = null;
var stop_after = null;
var stop_if_hit = null;
var lhs = get_lhs(candidate);
if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) continue;
// Locate symbols which may execute code outside of scanning range
@@ -1191,6 +1217,28 @@ merge(Compressor.prototype, {
}
}
function handle_custom_scan_order(node) {
// Skip (non-executed) functions
if (node instanceof AST_Scope) return node;
// Scan case expressions first in a switch statement
if (node instanceof AST_Switch) {
node.expression = node.expression.transform(scanner);
for (var i = 0, len = node.body.length; !abort && i < len; i++) {
var branch = node.body[i];
if (branch instanceof AST_Case) {
if (!hit) {
if (branch !== hit_stack[hit_index]) continue;
hit_index++;
}
branch.expression = branch.expression.transform(scanner);
if (side_effects || !replace_all) break;
}
}
abort = true;
return node;
}
}
function has_overlapping_symbol(fn, arg, fn_strict) {
var found = false, scan_this = !(fn instanceof AST_Arrow);
arg.walk(new TreeWalker(function(node, descend) {
@@ -1334,18 +1382,49 @@ merge(Compressor.prototype, {
hit_stack.pop();
}
function find_stop(node, level) {
function find_stop(node, level, write_only) {
var parent = scanner.parent(level);
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Assign) {
if (write_only
&& !(parent.left instanceof AST_PropAccess
|| parent.left.name in lvalues)) {
return find_stop(parent, level + 1, write_only);
}
return node;
}
if (parent instanceof AST_Binary) {
if (write_only && (!lazy_op(parent.operator) || parent.left === node)) {
return find_stop(parent, level + 1, write_only);
}
return node;
}
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_Conditional) {
if (write_only && parent.condition === node) {
return find_stop(parent, level + 1, write_only);
}
return node;
}
if (parent instanceof AST_Definitions) {
return find_stop(parent, level + 1, true);
}
if (parent instanceof AST_Exit) {
return write_only ? find_stop(parent, level + 1, write_only) : node;
}
if (parent instanceof AST_If) {
if (write_only && parent.condition === node) {
return find_stop(parent, level + 1, write_only);
}
return node;
}
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Sequence) {
return find_stop(parent, level + 1, parent.tail_node() !== node);
}
if (parent instanceof AST_SimpleStatement) {
return find_stop(parent, level + 1, true);
}
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_VarDef) return node;
return null;
@@ -1394,11 +1473,6 @@ merge(Compressor.prototype, {
return lvalues;
}
function lhs_or_def(node) {
if (node instanceof AST_VarDef) return node.value && node.name;
return is_lhs(node.left, node);
}
function remove_candidate(expr) {
if (expr.name instanceof AST_SymbolFunarg) {
var iife = compressor.parent(), argnames = compressor.self().argnames;
@@ -1437,31 +1511,25 @@ merge(Compressor.prototype, {
return get_rvalue(expr).has_side_effects(compressor);
}
function references_in_scope(def) {
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return true;
if (def.scope.get_defun_scope() !== scope) return false;
return def.references.every(function(ref) {
function may_modify(sym) {
var def = sym.definition();
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return false;
if (def.scope.get_defun_scope() !== scope) return true;
return !all(def.references, function(ref) {
return ref.scope.get_defun_scope() === scope;
});
}
function side_effects_external(node, lhs) {
if (node instanceof AST_Assign) {
return side_effects_external(node.left, true)
|| side_effects_external(node.right);
}
if (node instanceof AST_Definitions) return false;
if (node instanceof AST_Assign) return side_effects_external(node.left, true);
if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
if (lhs) {
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
if (node instanceof AST_Sub) {
return side_effects_external(node.expression, true)
|| side_effects_external(node.property);
}
if (node instanceof AST_Sub) return side_effects_external(node.expression, true);
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
}
return node.has_side_effects(compressor);
return false;
}
}
@@ -2291,6 +2359,8 @@ merge(Compressor.prototype, {
"split",
"substr",
"substring",
"toLowerCase",
"toUpperCase",
"trim",
].concat(object_fns),
};
@@ -2843,8 +2913,13 @@ merge(Compressor.prototype, {
return any(this.elements, compressor);
});
def(AST_Assign, function(compressor){
return this.operator != "=" && this.left.may_throw(compressor)
|| this.right.may_throw(compressor);
if (this.right.may_throw(compressor)) return true;
if (!compressor.has_directive("use strict")
&& this.operator == "="
&& this.left instanceof AST_SymbolRef) {
return false;
}
return this.left.may_throw(compressor);
});
def(AST_Binary, function(compressor){
return this.left.may_throw(compressor)
@@ -3126,7 +3201,7 @@ merge(Compressor.prototype, {
} else {
var node_def = def.name.definition();;
initializations.add(node_def.id, def.value);
if (def.name.fixed_value() === def.value) {
if (!node_def.chained && def.name.fixed_value() === def.value) {
fixed_ids[node_def.id] = def;
}
}
@@ -3374,7 +3449,7 @@ merge(Compressor.prototype, {
&& self.variables.get(sym.name) === (node_def = sym.definition())) {
if (node instanceof AST_Assign) {
node.right.walk(tw);
if (node.left.fixed_value() === node.right) {
if (!node_def.chained && node.left.fixed_value() === node.right) {
fixed_ids[node_def.id] = node;
}
}
@@ -5034,6 +5109,7 @@ merge(Compressor.prototype, {
if (compressor.option("comparisons")) switch (self.operator) {
case "===":
case "!==":
var is_strict_comparison = true;
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()) ||
@@ -5043,8 +5119,12 @@ merge(Compressor.prototype, {
// XXX: intentionally falling down to the next case
case "==":
case "!=":
// void 0 == x => null == x
if (!is_strict_comparison && is_undefined(self.left, compressor)) {
self.left = make_node(AST_Null, self.left);
}
// "undefined" == typeof x => undefined === x
if (compressor.option("typeofs")
else if (compressor.option("typeofs")
&& self.left instanceof AST_String
&& self.left.value == "undefined"
&& self.right instanceof AST_UnaryPrefix
@@ -5065,6 +5145,35 @@ merge(Compressor.prototype, {
return make_node(self.operator[0] == "=" ? AST_True : AST_False, self);
}
break;
case "&&":
case "||":
var lhs = self.left;
if (lhs.operator == self.operator) {
lhs = lhs.right;
}
if (lhs instanceof AST_Binary
&& lhs.operator == (self.operator == "&&" ? "!==" : "===")
&& self.right instanceof AST_Binary
&& lhs.operator == self.right.operator
&& (is_undefined(lhs.left, compressor) && self.right.left instanceof AST_Null
|| lhs.left instanceof AST_Null && is_undefined(self.right.left, compressor))
&& !lhs.right.has_side_effects(compressor)
&& lhs.right.equivalent_to(self.right.right)) {
var combined = make_node(AST_Binary, self, {
operator: lhs.operator.slice(0, -1),
left: make_node(AST_Null, self),
right: lhs.right
});
if (lhs !== self.left) {
combined = make_node(AST_Binary, self, {
operator: self.operator,
left: self.left.left,
right: combined
});
}
return combined;
}
break;
}
if (self.operator == "+" && compressor.in_boolean_context()) {
var ll = self.left.evaluate(compressor);
@@ -5521,7 +5630,7 @@ merge(Compressor.prototype, {
var name_length = d.name.length;
var overhead = 0;
if (compressor.option("unused") && !compressor.exposed(d)) {
overhead = (name_length + 2 + value_length) / d.references.length;
overhead = (name_length + 2 + value_length) / (d.references.length - d.assignments);
}
d.should_replace = value_length <= name_length + overhead ? fn : false;
} else {
@@ -5642,6 +5751,7 @@ merge(Compressor.prototype, {
if (in_try(level, parent instanceof AST_Throw)) break;
if (is_reachable(def.scope, [ def ])) break;
if (self.operator == "=") return self.right;
def.fixed = false;
return make_node(AST_Binary, self, {
operator: self.operator.slice(0, -1),
left: self.left,