in-place if_return

This commit is contained in:
alexlamsl 2017-06-16 17:34:02 +08:00
parent 2de466331a
commit 0ff3c24ad5

View File

@ -673,7 +673,7 @@ merge(Compressor.prototype, {
eliminate_dead_code(statements, compressor); eliminate_dead_code(statements, compressor);
} }
if (compressor.option("if_return")) { if (compressor.option("if_return")) {
statements = handle_if_return(statements, compressor); handle_if_return(statements, compressor);
} }
if (compressor.sequences_limit > 0) { if (compressor.sequences_limit > 0) {
sequencesize(statements, compressor); sequencesize(statements, compressor);
@ -916,35 +916,34 @@ merge(Compressor.prototype, {
var self = compressor.self(); var self = compressor.self();
var multiple_if_returns = has_multiple_if_returns(statements); var multiple_if_returns = has_multiple_if_returns(statements);
var in_lambda = self instanceof AST_Lambda; var in_lambda = self instanceof AST_Lambda;
var ret = []; // Optimized statements, build from tail to front for (var i = statements.length; --i >= 0;) {
loop: for (var i = statements.length; --i >= 0;) {
var stat = statements[i]; var stat = statements[i];
switch (true) { var next = statements[i + 1];
case (in_lambda && stat instanceof AST_Return && !stat.value && ret.length == 0):
if (in_lambda && stat instanceof AST_Return && !stat.value && !next) {
CHANGED = true; CHANGED = true;
// note, ret.length is probably always zero statements.length--;
// because we drop unreachable code before this continue;
// step. nevertheless, it's good to check. }
continue loop;
case stat instanceof AST_If: if (stat instanceof AST_If) {
var ab = aborts(stat.body); var ab = aborts(stat.body);
if (can_merge_flow(ab)) { if (can_merge_flow(ab)) {
if (ab.label) { if (ab.label) {
remove(ab.label.thedef.references, ab); remove(ab.label.thedef.references, ab);
} }
CHANGED = true; CHANGED = true;
var funs = extract_functions_from_statement_array(ret);
var body = as_statement_array_with_return(stat.body, ab);
stat = stat.clone(); stat = stat.clone();
stat.condition = stat.condition.negate(compressor); stat.condition = stat.condition.negate(compressor);
var body = as_statement_array_with_return(stat.body, ab);
stat.body = make_node(AST_BlockStatement, stat, { 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, { stat.alternative = make_node(AST_BlockStatement, stat, {
body: body body: body
}); });
ret = [ stat.transform(compressor) ].concat(funs); statements[i] = stat.transform(compressor);
continue loop; continue;
} }
var ab = aborts(stat.alternative); var ab = aborts(stat.alternative);
@ -953,81 +952,73 @@ merge(Compressor.prototype, {
remove(ab.label.thedef.references, ab); remove(ab.label.thedef.references, ab);
} }
CHANGED = true; CHANGED = true;
var funs = extract_functions_from_statement_array(ret);
stat = stat.clone(); stat = stat.clone();
stat.body = make_node(AST_BlockStatement, stat.body, { 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); var body = as_statement_array_with_return(stat.alternative, ab);
stat.alternative = make_node(AST_BlockStatement, stat.alternative, { stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
body: body body: body
}); });
ret = [ stat.transform(compressor) ].concat(funs); statements[i] = stat.transform(compressor);
continue loop; continue;
} }
}
if (stat.body instanceof AST_Return) { if (stat instanceof AST_If && stat.body instanceof AST_Return) {
var value = stat.body.value; var value = stat.body.value;
//--- //---
// pretty silly case, but: // pretty silly case, but:
// if (foo()) return; return; ==> foo(); return; // if (foo()) return; return; ==> foo(); return;
if ((in_lambda && ret.length == 0 || ret[0] instanceof AST_Return && !ret[0].value) if (!value && !stat.alternative
&& !value && !stat.alternative) { && (in_lambda && !next || next instanceof AST_Return && !next.value)) {
CHANGED = true; CHANGED = true;
var cond = make_node(AST_SimpleStatement, stat.condition, { statements[i] = make_node(AST_SimpleStatement, stat.condition, {
body: stat.condition body: stat.condition
}); });
ret.unshift(cond); continue;
continue loop; }
} //---
//--- // if (foo()) return x; return y; ==> return foo() ? x : y;
// if (foo()) return x; return y; ==> return foo() ? x : y; if (value && !stat.alternative && next instanceof AST_Return && next.value) {
if (ret[0] instanceof AST_Return && value && ret[0].value && !stat.alternative) { CHANGED = true;
CHANGED = true; stat = stat.clone();
stat = stat.clone(); stat.alternative = next;
stat.alternative = ret[0]; statements[i] = stat.transform(compressor);
ret[0] = stat.transform(compressor); statements.length = i + 1;
continue loop; continue;
} }
//--- //---
// if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined; // if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
if (multiple_if_returns && (ret.length == 0 || ret[0] instanceof AST_Return) if (multiple_if_returns && in_lambda && value && !stat.alternative
&& value && !stat.alternative && in_lambda) { && (!next || next instanceof AST_Return)) {
CHANGED = true; CHANGED = true;
stat = stat.clone(); stat = stat.clone();
stat.alternative = ret[0] || make_node(AST_Return, stat, { stat.alternative = next || make_node(AST_Return, stat, {
value: null value: null
}); });
ret[0] = stat.transform(compressor); statements[i] = stat.transform(compressor);
continue loop; statements.length = i + 1;
} continue;
//--- }
// if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e; //---
// // 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 // if sequences is not enabled, this can lead to an endless loop (issue #866).
// the example code. // however, with sequences on this helps producing slightly better output for
if (compressor.option("sequences") // the example code.
&& i > 0 && statements[i - 1] instanceof AST_If && statements[i - 1].body instanceof AST_Return var prev = statements[i - 1];
&& ret.length == 1 && in_lambda && ret[0] instanceof AST_SimpleStatement if (compressor.option("sequences") && in_lambda && !stat.alternative
&& !stat.alternative) { && prev instanceof AST_If && prev.body instanceof AST_Return
CHANGED = true; && i + 2 == statements.length && next instanceof AST_SimpleStatement) {
ret.push(make_node(AST_Return, ret[0], { CHANGED = true;
value: null statements.push(make_node(AST_Return, next, {
}).transform(compressor)); value: null
ret.unshift(stat); }).transform(compressor));
continue loop; continue;
}
} }
ret.unshift(stat);
break;
default:
ret.unshift(stat);
break;
} }
} }
return ret;
function has_multiple_if_returns(statements) { function has_multiple_if_returns(statements) {
var n = 0; var n = 0;
@ -1052,6 +1043,18 @@ merge(Compressor.prototype, {
|| ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === 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) { function as_statement_array_with_return(node, ab) {
var body = as_statement_array(node).slice(0, -1); var body = as_statement_array(node).slice(0, -1);
if (ab.value) { if (ab.value) {
@ -1061,7 +1064,7 @@ merge(Compressor.prototype, {
} }
return body; return body;
} }
}; }
function eliminate_dead_code(statements, compressor) { function eliminate_dead_code(statements, compressor) {
var has_quit; var has_quit;
@ -1205,18 +1208,6 @@ merge(Compressor.prototype, {
}; };
} }
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;
}
function extract_declarations_from_unreachable_code(compressor, stat, target) { function extract_declarations_from_unreachable_code(compressor, stat, target) {
if (!(stat instanceof AST_Defun)) { if (!(stat instanceof AST_Defun)) {
compressor.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start); compressor.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);