Merge branch 'master' into harmony-v3.3.10
This commit is contained in:
278
lib/compress.js
278
lib/compress.js
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user