Merge branch 'master' into harmony-v3.0.18

This commit is contained in:
alexlamsl
2017-06-18 15:49:49 +08:00
5 changed files with 397 additions and 196 deletions

View File

@@ -695,26 +695,24 @@ merge(Compressor.prototype, {
var CHANGED, max_iter = 10;
do {
CHANGED = false;
statements = eliminate_spurious_blocks(statements);
eliminate_spurious_blocks(statements);
if (compressor.option("dead_code")) {
statements = eliminate_dead_code(statements, compressor);
eliminate_dead_code(statements, compressor);
}
if (compressor.option("if_return")) {
statements = handle_if_return(statements, compressor);
handle_if_return(statements, compressor);
}
if (compressor.sequences_limit > 0) {
statements = sequencesize(statements, compressor);
sequencesize(statements, compressor);
}
if (compressor.option("join_vars")) {
statements = join_consecutive_vars(statements, compressor);
join_consecutive_vars(statements, compressor);
}
if (compressor.option("collapse_vars")) {
statements = collapse(statements, compressor);
collapse(statements, compressor);
}
} while (CHANGED && max_iter-- > 0);
return statements;
// Search from right to left for assignment-like expressions:
// - `var a = x;`
// - `a = x;`
@@ -752,6 +750,7 @@ merge(Compressor.prototype, {
var parent = tt.parent();
if (node instanceof AST_Assign && node.operator != "=" && lhs.equivalent_to(node.left)
|| node instanceof AST_Await
|| node instanceof AST_Call && lhs instanceof AST_PropAccess && lhs.equivalent_to(node.expression)
|| node instanceof AST_Debugger
|| node instanceof AST_Destructuring
|| node instanceof AST_IterationStatement && !(node instanceof AST_For)
@@ -818,7 +817,6 @@ merge(Compressor.prototype, {
if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
}
}
return statements;
function extract_candidates(expr) {
if (expr instanceof AST_Assign && !expr.left.has_side_effects(compressor)
@@ -921,59 +919,60 @@ merge(Compressor.prototype, {
function eliminate_spurious_blocks(statements) {
var seen_dirs = [];
return statements.reduce(function(a, stat){
for (var i = 0; i < statements.length;) {
var stat = statements[i];
if (stat instanceof AST_BlockStatement && all(stat.body, can_be_evicted_from_block)) {
CHANGED = true;
a.push.apply(a, eliminate_spurious_blocks(stat.body));
eliminate_spurious_blocks(stat.body);
[].splice.apply(statements, [i, 1].concat(stat.body));
i += stat.body.length;
} else if (stat instanceof AST_EmptyStatement) {
CHANGED = true;
statements.splice(i, 1);
} else if (stat instanceof AST_Directive) {
if (seen_dirs.indexOf(stat.value) < 0) {
a.push(stat);
i++;
seen_dirs.push(stat.value);
} else {
CHANGED = true;
statements.splice(i, 1);
}
} else {
a.push(stat);
}
return a;
}, []);
};
} else i++;
}
}
function handle_if_return(statements, compressor) {
var self = compressor.self();
var multiple_if_returns = has_multiple_if_returns(statements);
var in_lambda = self instanceof AST_Lambda;
var ret = []; // Optimized statements, build from tail to front
loop: for (var i = statements.length; --i >= 0;) {
for (var i = statements.length; --i >= 0;) {
var stat = statements[i];
switch (true) {
case (in_lambda && stat instanceof AST_Return && !stat.value && ret.length == 0):
var next = statements[i + 1];
if (in_lambda && stat instanceof AST_Return && !stat.value && !next) {
CHANGED = true;
// note, ret.length is probably always zero
// because we drop unreachable code before this
// step. nevertheless, it's good to check.
continue loop;
case stat instanceof AST_If:
statements.length--;
continue;
}
if (stat instanceof AST_If) {
var ab = aborts(stat.body);
if (can_merge_flow(ab)) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
var funs = extract_functions_from_statement_array(ret);
var body = as_statement_array_with_return(stat.body, ab);
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
var body = as_statement_array_with_return(stat.body, ab);
stat.body = make_node(AST_BlockStatement, stat, {
body: as_statement_array(stat.alternative).concat(ret)
body: as_statement_array(stat.alternative).concat(extract_functions())
});
stat.alternative = make_node(AST_BlockStatement, stat, {
body: body
});
ret = [ stat.transform(compressor) ].concat(funs);
continue loop;
statements[i] = stat.transform(compressor);
continue;
}
var ab = aborts(stat.alternative);
@@ -982,81 +981,71 @@ merge(Compressor.prototype, {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
var funs = extract_functions_from_statement_array(ret);
stat = stat.clone();
stat.body = make_node(AST_BlockStatement, stat.body, {
body: as_statement_array(stat.body).concat(ret)
body: as_statement_array(stat.body).concat(extract_functions())
});
var body = as_statement_array_with_return(stat.alternative, ab);
stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
body: body
});
ret = [ stat.transform(compressor) ].concat(funs);
continue loop;
statements[i] = stat.transform(compressor);
continue;
}
}
if (stat.body instanceof AST_Return) {
var value = stat.body.value;
//---
// pretty silly case, but:
// if (foo()) return; return; ==> foo(); return;
if ((in_lambda && ret.length == 0 || ret[0] instanceof AST_Return && !ret[0].value)
&& !value && !stat.alternative) {
CHANGED = true;
var cond = make_node(AST_SimpleStatement, stat.condition, {
body: stat.condition
});
ret.unshift(cond);
continue loop;
}
//---
// if (foo()) return x; return y; ==> return foo() ? x : y;
if (ret[0] instanceof AST_Return && value && ret[0].value && !stat.alternative) {
CHANGED = true;
stat = stat.clone();
stat.alternative = ret[0];
ret[0] = stat.transform(compressor);
continue loop;
}
//---
// if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
if (multiple_if_returns && (ret.length == 0 || ret[0] instanceof AST_Return)
&& value && !stat.alternative && in_lambda) {
CHANGED = true;
stat = stat.clone();
stat.alternative = ret[0] || make_node(AST_Return, stat, {
value: null
});
ret[0] = stat.transform(compressor);
continue loop;
}
//---
// if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
//
// if sequences is not enabled, this can lead to an endless loop (issue #866).
// however, with sequences on this helps producing slightly better output for
// the example code.
if (compressor.option("sequences")
&& i > 0 && statements[i - 1] instanceof AST_If && statements[i - 1].body instanceof AST_Return
&& ret.length == 1 && in_lambda && ret[0] instanceof AST_SimpleStatement
&& !stat.alternative) {
CHANGED = true;
ret.push(make_node(AST_Return, ret[0], {
value: null
}).transform(compressor));
ret.unshift(stat);
continue loop;
}
if (stat instanceof AST_If && stat.body instanceof AST_Return) {
var value = stat.body.value;
//---
// pretty silly case, but:
// if (foo()) return; return; ==> foo(); return;
if (!value && !stat.alternative
&& (in_lambda && !next || next instanceof AST_Return && !next.value)) {
CHANGED = true;
statements[i] = make_node(AST_SimpleStatement, stat.condition, {
body: stat.condition
});
continue;
}
//---
// if (foo()) return x; return y; ==> return foo() ? x : y;
if (value && !stat.alternative && next instanceof AST_Return && next.value) {
CHANGED = true;
stat = stat.clone();
stat.alternative = next;
statements.splice(i, 2, stat.transform(compressor));
continue;
}
//---
// if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
if (multiple_if_returns && in_lambda && value && !stat.alternative
&& (!next || next instanceof AST_Return)) {
CHANGED = true;
stat = stat.clone();
stat.alternative = next || make_node(AST_Return, stat, {
value: null
});
statements.splice(i, next ? 2 : 1, stat.transform(compressor));
continue;
}
//---
// if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
//
// if sequences is not enabled, this can lead to an endless loop (issue #866).
// however, with sequences on this helps producing slightly better output for
// the example code.
var prev = statements[i - 1];
if (compressor.option("sequences") && in_lambda && !stat.alternative
&& prev instanceof AST_If && prev.body instanceof AST_Return
&& i + 2 == statements.length && next instanceof AST_SimpleStatement) {
CHANGED = true;
statements.push(make_node(AST_Return, next, {
value: null
}).transform(compressor));
continue;
}
ret.unshift(stat);
break;
default:
ret.unshift(stat);
break;
}
}
return ret;
function has_multiple_if_returns(statements) {
var n = 0;
@@ -1074,15 +1063,29 @@ merge(Compressor.prototype, {
}
function can_merge_flow(ab) {
if (!ab || !all(ret, function(stat) {
return !(stat instanceof AST_Const || stat instanceof AST_Let);
})) return false;
if (!ab) return false;
for (var j = i + 1, len = statements.length; j < len; j++) {
var stat = statements[j];
if (stat instanceof AST_Const || stat instanceof AST_Let) return false;
}
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
return ab instanceof AST_Return && in_lambda && is_return_void(ab.value)
|| ab instanceof AST_Continue && self === loop_body(lct)
|| ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct;
}
function extract_functions() {
var tail = statements.slice(i + 1);
statements.length = i + 1;
return tail.filter(function(stat) {
if (stat instanceof AST_Defun) {
statements.push(stat);
return false;
}
return true;
});
}
function as_statement_array_with_return(node, ab) {
var body = as_statement_array(node).slice(0, -1);
if (ab.value) {
@@ -1092,49 +1095,52 @@ merge(Compressor.prototype, {
}
return body;
}
};
}
function eliminate_dead_code(statements, compressor) {
var has_quit = false;
var orig = statements.length;
var has_quit;
var self = compressor.self();
statements = statements.reduce(function(a, stat){
if (has_quit) {
extract_declarations_from_unreachable_code(compressor, stat, a);
} else {
if (stat instanceof AST_LoopControl) {
var lct = compressor.loopcontrol_target(stat);
if ((stat instanceof AST_Break
&& !(lct instanceof AST_IterationStatement)
&& loop_body(lct) === self) || (stat instanceof AST_Continue
&& loop_body(lct) === self)) {
if (stat.label) {
remove(stat.label.thedef.references, stat);
}
} else {
a.push(stat);
for (var i = 0, n = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (stat instanceof AST_LoopControl) {
var lct = compressor.loopcontrol_target(stat);
if (stat instanceof AST_Break
&& !(lct instanceof AST_IterationStatement)
&& loop_body(lct) === self
|| stat instanceof AST_Continue
&& loop_body(lct) === self) {
if (stat.label) {
remove(stat.label.thedef.references, stat);
}
} else {
a.push(stat);
statements[n++] = stat;
}
if (aborts(stat)) has_quit = true;
} else {
statements[n++] = stat;
}
return a;
}, []);
CHANGED = statements.length != orig;
return statements;
};
if (aborts(stat)) {
has_quit = statements.slice(i + 1);
break;
}
}
statements.length = n;
CHANGED = n != len;
if (has_quit) has_quit.forEach(function(stat) {
extract_declarations_from_unreachable_code(compressor, stat, statements);
});
}
function sequencesize(statements, compressor) {
if (statements.length < 2) return statements;
var seq = [], ret = [];
if (statements.length < 2) return;
var seq = [], n = 0;
function push_seq() {
if (!seq.length) return;
var body = make_sequence(seq[0], seq);
ret.push(make_node(AST_SimpleStatement, body, { body: body }));
statements[n++] = make_node(AST_SimpleStatement, body, { body: body });
seq = [];
};
statements.forEach(function(stat){
}
for (var i = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (stat instanceof AST_SimpleStatement) {
if (seq.length >= compressor.sequences_limit) push_seq();
var body = stat.body;
@@ -1142,18 +1148,18 @@ merge(Compressor.prototype, {
if (body) merge_sequence(seq, body);
} else {
push_seq();
ret.push(stat);
statements[n++] = stat;
}
});
}
push_seq();
ret = sequencesize_2(ret, compressor);
CHANGED = ret.length != statements.length;
return ret;
};
statements.length = n;
sequencesize_2(statements, compressor);
CHANGED = statements.length != len;
}
function sequencesize_2(statements, compressor) {
function cons_seq(right) {
ret.pop();
n--;
var left = prev.body;
if (!(left instanceof AST_Sequence)) {
left = make_node(AST_Sequence, left, {
@@ -1163,8 +1169,9 @@ merge(Compressor.prototype, {
merge_sequence(left.expressions, right);
return left.transform(compressor);
};
var ret = [], prev = null;
statements.forEach(function(stat){
var n = 0, prev;
for (var i = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (prev) {
if (stat instanceof AST_For) {
try {
@@ -1177,7 +1184,7 @@ merge(Compressor.prototype, {
}
else if (!stat.init) {
stat.init = prev.body.drop_side_effect_free(compressor);
ret.pop();
n--;
}
} catch(ex) {
if (ex !== cons_seq) throw ex;
@@ -1199,15 +1206,16 @@ merge(Compressor.prototype, {
stat.expression = cons_seq(stat.expression);
}
}
ret.push(stat);
statements[n++] = stat;
prev = stat instanceof AST_SimpleStatement ? stat : null;
});
return ret;
};
}
statements.length = n;
}
function join_consecutive_vars(statements, compressor) {
var prev = null;
return statements.reduce(function(a, stat){
for (var i = 0, j = -1, len = statements.length; i < len; i++) {
var stat = statements[i];
var prev = statements[j];
if (stat instanceof AST_Definitions && prev && prev.TYPE == stat.TYPE) {
prev.definitions = prev.definitions.concat(stat.definitions);
CHANGED = true;
@@ -1216,35 +1224,19 @@ merge(Compressor.prototype, {
&& prev instanceof AST_Var
&& (!stat.init || stat.init.TYPE == prev.TYPE)) {
CHANGED = true;
a.pop();
if (stat.init) {
stat.init.definitions = prev.definitions.concat(stat.init.definitions);
} else {
stat.init = prev;
}
a.push(stat);
prev = stat;
statements[j] = stat;
}
else {
prev = stat;
a.push(stat);
statements[++j] = stat;
}
return a;
}, []);
};
};
function extract_functions_from_statement_array(statements) {
var funs = [];
for (var i = statements.length - 1; i >= 0; --i) {
var stat = statements[i];
if (stat instanceof AST_Defun) {
statements.splice(i, 1);
funs.unshift(stat);
}
}
return funs;
statements.length = j + 1;
};
}
function extract_declarations_from_unreachable_code(compressor, stat, target) {
@@ -2050,13 +2042,12 @@ merge(Compressor.prototype, {
});
OPT(AST_Block, function(self, compressor){
if (self.body instanceof AST_Node) { return self; }
self.body = tighten_body(self.body, compressor);
if (!(self.body instanceof AST_Node)) tighten_body(self.body, compressor);
return self;
});
OPT(AST_BlockStatement, function(self, compressor){
self.body = tighten_body(self.body, compressor);
tighten_body(self.body, compressor);
switch (self.body.length) {
case 1:
if (!compressor.has_directive("use strict") && compressor.parent() instanceof AST_If
@@ -3057,7 +3048,7 @@ merge(Compressor.prototype, {
});
OPT(AST_Try, function(self, compressor){
self.body = tighten_body(self.body, compressor);
tighten_body(self.body, compressor);
if (self.bcatch && self.bfinally && all(self.bfinally.body, is_empty)) self.bfinally = null;
if (all(self.body, is_empty)) {
var body = [];
@@ -3114,18 +3105,18 @@ merge(Compressor.prototype, {
OPT(AST_Call, function(self, compressor){
var exp = self.expression;
if (compressor.option("reduce_vars") && exp instanceof AST_SymbolRef) {
var fixed = exp.fixed_value();
if (fixed instanceof AST_Function) exp = fixed;
}
var fn = exp;
if (compressor.option("unused")
&& exp instanceof AST_Function
&& !exp.uses_arguments
&& !exp.uses_eval) {
&& (fn instanceof AST_Function
|| compressor.option("reduce_vars")
&& fn instanceof AST_SymbolRef
&& (fn = fn.fixed_value()) instanceof AST_Function)
&& !fn.uses_arguments
&& !fn.uses_eval) {
var pos = 0, last = 0;
for (var i = 0, len = self.args.length; i < len; i++) {
var trim = i >= exp.argnames.length;
if (trim || exp.argnames[i].__unused) {
var trim = i >= fn.argnames.length;
if (trim || fn.argnames[i].__unused) {
var node = self.args[i].drop_side_effect_free(compressor);
if (node) {
self.args[pos++] = node;
@@ -3330,15 +3321,15 @@ merge(Compressor.prototype, {
}
}
}
if (exp instanceof AST_Function && !self.expression.is_generator && !self.expression.async) {
var stat = exp.body[0];
if (compressor.option("inline") && stat instanceof AST_Return) {
var value = stat && stat.value;
if (!value || value.is_constant_expression()) {
var args = self.args.concat(value || make_node(AST_Undefined, self));
return make_sequence(self, args).transform(compressor);
}
var stat = fn instanceof AST_Function && fn.body[0];
if (compressor.option("inline") && stat instanceof AST_Return) {
var value = stat.value;
if (!value || value.is_constant_expression()) {
var args = self.args.concat(value || make_node(AST_Undefined, self));
return make_sequence(self, args).transform(compressor);
}
}
if (exp instanceof AST_Function && !exp.is_generator && !exp.async) {
if (compressor.option("inline")
&& !exp.name
&& exp.body.length == 1
@@ -3361,16 +3352,19 @@ merge(Compressor.prototype, {
if (exp.argnames.length > 0) {
fn.body.push(make_node(AST_Var, self, {
definitions: exp.argnames.map(function(sym, i) {
var arg = self.args[i];
return make_node(AST_VarDef, sym, {
name: sym,
value: self.args[i] || make_node(AST_Undefined, self)
value: arg ? arg.clone(true) : make_node(AST_Undefined, self)
});
})
}));
}
if (self.args.length > exp.argnames.length) {
fn.body.push(make_node(AST_SimpleStatement, self, {
body: make_sequence(self, self.args.slice(exp.argnames.length))
body: make_sequence(self, self.args.slice(exp.argnames.length).map(function(node) {
return node.clone(true);
}))
}));
}
fn.body.push(make_node(AST_Return, self, {
@@ -3381,18 +3375,21 @@ merge(Compressor.prototype, {
if (body.length == 1 && body[0] instanceof AST_Return) {
value = body[0].value;
if (!value) return make_node(AST_Undefined, self);
value.walk(new TreeWalker(function(node) {
var tw = new TreeWalker(function(node) {
if (value === self) return true;
if (node instanceof AST_SymbolRef && matches(node.scope.find_variable(node))
|| node instanceof AST_This && matches(node)) {
if (node instanceof AST_SymbolRef) {
var ref = node.scope.find_variable(node);
if (ref && ref.scope.parent_scope === fn.parent_scope) {
value = self;
return true;
}
}
if (node instanceof AST_This && !tw.find_parent(AST_Scope)) {
value = self;
return true;
}
function matches(ref) {
return ref && ref.scope.parent_scope === fn.parent_scope;
}
}));
});
value.walk(tw);
if (value !== self) value = best_of(compressor, value, self);
} else {
value = self;
@@ -3531,6 +3528,7 @@ merge(Compressor.prototype, {
field = "left";
}
} else if (cdr instanceof AST_Call
&& !(left instanceof AST_PropAccess && cdr.expression.equivalent_to(left))
|| cdr instanceof AST_PropAccess
|| cdr instanceof AST_Unary && !unary_side_effects(cdr.operator)) {
field = "expression";