rewrite handle_if_return
optimizations of if/return/continue seem to be even better now
This commit is contained in:
201
lib/compress.js
201
lib/compress.js
@@ -156,7 +156,7 @@ function Compressor(options, false_by_default) {
|
|||||||
function eliminate_spurious_blocks(statements) {
|
function eliminate_spurious_blocks(statements) {
|
||||||
return statements.reduce(function(a, stat){
|
return statements.reduce(function(a, stat){
|
||||||
if (stat instanceof AST_BlockStatement) {
|
if (stat instanceof AST_BlockStatement) {
|
||||||
a.push.apply(a, stat.body);
|
a.push.apply(a, eliminate_spurious_blocks(stat.body));
|
||||||
} else if (!(stat instanceof AST_EmptyStatement)) {
|
} else if (!(stat instanceof AST_EmptyStatement)) {
|
||||||
a.push(stat);
|
a.push(stat);
|
||||||
}
|
}
|
||||||
@@ -164,6 +164,21 @@ function Compressor(options, false_by_default) {
|
|||||||
}, []);
|
}, []);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function as_statement_array(thing) {
|
||||||
|
if (thing === null) return [];
|
||||||
|
if (thing instanceof AST_BlockStatement) return thing.body;
|
||||||
|
if (thing instanceof AST_EmptyStatement) return [];
|
||||||
|
if (thing instanceof AST_StatementBase) return [ thing ];
|
||||||
|
throw new Error("Can't convert thing to statement array");
|
||||||
|
};
|
||||||
|
|
||||||
|
function is_empty(thing) {
|
||||||
|
if (thing === null) return true;
|
||||||
|
if (thing instanceof AST_EmptyStatement) return true;
|
||||||
|
if (thing instanceof AST_BlockStatement) return thing.body.length == 0;
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
function tighten_body(statements, compressor) {
|
function tighten_body(statements, compressor) {
|
||||||
var CHANGED;
|
var CHANGED;
|
||||||
statements = do_list(statements, compressor, true);
|
statements = do_list(statements, compressor, true);
|
||||||
@@ -181,12 +196,130 @@ function Compressor(options, false_by_default) {
|
|||||||
if (compressor.option("join_vars")) {
|
if (compressor.option("join_vars")) {
|
||||||
statements = join_consecutive_vars(statements, compressor);
|
statements = join_consecutive_vars(statements, compressor);
|
||||||
}
|
}
|
||||||
|
statements = eliminate_spurious_blocks(statements);
|
||||||
} while (CHANGED);
|
} while (CHANGED);
|
||||||
return statements;
|
return statements;
|
||||||
|
|
||||||
|
function handle_if_return(statements, compressor) {
|
||||||
|
var self = compressor.self();
|
||||||
|
var in_lambda = self instanceof AST_Lambda;
|
||||||
|
var last = statements.length - 1;
|
||||||
|
var ret = [];
|
||||||
|
loop: 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):
|
||||||
|
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:
|
||||||
|
if (stat.body instanceof AST_Return) {
|
||||||
|
//---
|
||||||
|
// pretty silly case, but:
|
||||||
|
// if (foo()) return; return; ==> foo(); return;
|
||||||
|
if (((in_lambda && ret.length == 0)
|
||||||
|
|| (ret[0] instanceof AST_Return && !ret[0].value))
|
||||||
|
&& !stat.body.value && !stat.alternative) {
|
||||||
|
CHANGED = true;
|
||||||
|
var cond = make_node(AST_SimpleStatement, stat.condition, {
|
||||||
|
body: stat.condition
|
||||||
|
}).optimize(compressor);
|
||||||
|
ret.unshift(cond);
|
||||||
|
continue loop;
|
||||||
|
}
|
||||||
|
//---
|
||||||
|
// if (foo()) return x; return y; ==> return foo() ? x : y;
|
||||||
|
if (ret[0] instanceof AST_Return && stat.body.value && ret[0].value && !stat.alternative) {
|
||||||
|
CHANGED = true;
|
||||||
|
stat = stat.clone();
|
||||||
|
stat.alternative = ret[0];
|
||||||
|
ret[0] = stat.squeeze(compressor);
|
||||||
|
continue loop;
|
||||||
|
}
|
||||||
|
//---
|
||||||
|
// if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
|
||||||
|
if ((ret.length == 0 || ret[0] instanceof AST_Return) && stat.body.value && !stat.alternative && in_lambda) {
|
||||||
|
CHANGED = true;
|
||||||
|
stat = stat.clone();
|
||||||
|
stat.alternative = ret[0] || make_node(AST_Return, stat, {
|
||||||
|
value: make_node(AST_Undefined, stat)
|
||||||
|
});
|
||||||
|
ret[0] = stat.squeeze(compressor);
|
||||||
|
continue loop;
|
||||||
|
}
|
||||||
|
//---
|
||||||
|
// if (foo()) return; [ else x... ]; y... ==> if (!foo()) { x...; y... }
|
||||||
|
if (!stat.body.value && in_lambda) {
|
||||||
|
CHANGED = true;
|
||||||
|
stat = stat.clone();
|
||||||
|
stat.condition = stat.condition.negate(compressor);
|
||||||
|
stat.body = make_node(AST_BlockStatement, stat, {
|
||||||
|
body: as_statement_array(stat.alternative).concat(ret)
|
||||||
|
});
|
||||||
|
stat.alternative = null;
|
||||||
|
ret = [ stat.squeeze(compressor) ];
|
||||||
|
continue loop;
|
||||||
|
}
|
||||||
|
//---
|
||||||
|
if (ret.length == 1 && in_lambda && ret[0] instanceof AST_SimpleStatement
|
||||||
|
&& (!stat.alternative || stat.alternative instanceof AST_SimpleStatement)) {
|
||||||
|
CHANGED = true;
|
||||||
|
ret.push(make_node(AST_Return, ret[0], {
|
||||||
|
value: make_node(AST_Undefined, ret[0])
|
||||||
|
}).squeeze(compressor));
|
||||||
|
ret = as_statement_array(stat.alternative).concat(ret);
|
||||||
|
ret.unshift(stat);
|
||||||
|
continue loop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ab = aborts(stat.body);
|
||||||
|
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
|
||||||
|
|| (ab instanceof AST_Continue && self === ab.target()))) {
|
||||||
|
CHANGED = true;
|
||||||
|
var body = tighten_body(as_statement_array(stat.body).slice(0, -1), compressor);
|
||||||
|
stat = stat.clone();
|
||||||
|
stat.condition = stat.condition.negate(compressor);
|
||||||
|
stat.body = make_node(AST_BlockStatement, stat, {
|
||||||
|
body: ret
|
||||||
|
});
|
||||||
|
stat.alternative = make_node(AST_BlockStatement, stat, {
|
||||||
|
body: body
|
||||||
|
});
|
||||||
|
ret = [ stat.squeeze(compressor) ];
|
||||||
|
continue loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ab = aborts(stat.alternative);
|
||||||
|
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
|
||||||
|
|| (ab instanceof AST_Continue && self === ab.target()))) {
|
||||||
|
CHANGED = true;
|
||||||
|
stat = stat.clone();
|
||||||
|
stat.body = make_node(AST_BlockStatement, stat.body, {
|
||||||
|
body: tighten_body(as_statement_array(stat.body).concat(ret), compressor)
|
||||||
|
});
|
||||||
|
stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
|
||||||
|
body: tighten_body(as_statement_array(stat.alternative).slice(0, -1), compressor)
|
||||||
|
});
|
||||||
|
ret = [ stat.squeeze(compressor) ];
|
||||||
|
continue loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.unshift(stat);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret.unshift(stat);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
/// XXX: this function is UGLY and kinda wrong.
|
/// XXX: this function is UGLY and kinda wrong.
|
||||||
/// I think it would be cleaner if it operates backwards.
|
/// I think it would be cleaner if it operates backwards.
|
||||||
function handle_if_return(statements, compressor) {
|
function handle_if_return_2(statements, compressor) {
|
||||||
var self = compressor.self();
|
var self = compressor.self();
|
||||||
var in_lambda = self instanceof AST_Lambda;
|
var in_lambda = self instanceof AST_Lambda;
|
||||||
var last = statements.length - 1;
|
var last = statements.length - 1;
|
||||||
@@ -677,12 +810,15 @@ function Compressor(options, false_by_default) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// tell me if a statement aborts
|
// tell me if a statement aborts
|
||||||
|
function aborts(thing) {
|
||||||
|
return thing && thing.aborts();
|
||||||
|
};
|
||||||
(function(def){
|
(function(def){
|
||||||
def(AST_StatementBase, function(){ return null });
|
def(AST_StatementBase, function(){ return null });
|
||||||
def(AST_Jump, function(){ return this });
|
def(AST_Jump, function(){ return this });
|
||||||
def(AST_BlockStatement, function(){
|
def(AST_BlockStatement, function(){
|
||||||
var n = this.body.length;
|
var n = this.body.length;
|
||||||
return n > 0 && this.body[n - 1].aborts();
|
return n > 0 && aborts(this.body[n - 1]);
|
||||||
});
|
});
|
||||||
})(function(node, func){
|
})(function(node, func){
|
||||||
node.DEFMETHOD("aborts", func);
|
node.DEFMETHOD("aborts", func);
|
||||||
@@ -938,6 +1074,7 @@ function Compressor(options, false_by_default) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (is_empty(self.alternative)) self.alternative = null;
|
||||||
var negated = self.condition.negate(compressor);
|
var negated = self.condition.negate(compressor);
|
||||||
var negated_is_best = best_of(self.condition, negated) === negated;
|
var negated_is_best = best_of(self.condition, negated) === negated;
|
||||||
if (self.alternative && negated_is_best) {
|
if (self.alternative && negated_is_best) {
|
||||||
@@ -947,8 +1084,7 @@ function Compressor(options, false_by_default) {
|
|||||||
self.body = self.alternative || new AST_EmptyStatement();
|
self.body = self.alternative || new AST_EmptyStatement();
|
||||||
self.alternative = tmp;
|
self.alternative = tmp;
|
||||||
}
|
}
|
||||||
if (self.body instanceof AST_EmptyStatement
|
if (is_empty(self.body) && is_empty(self.alternative)) {
|
||||||
&& self.alternative instanceof AST_EmptyStatement) {
|
|
||||||
return make_node(AST_SimpleStatement, self.condition, {
|
return make_node(AST_SimpleStatement, self.condition, {
|
||||||
body: self.condition
|
body: self.condition
|
||||||
});
|
});
|
||||||
@@ -963,9 +1099,7 @@ function Compressor(options, false_by_default) {
|
|||||||
}).optimize(compressor)
|
}).optimize(compressor)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if ((!self.alternative
|
if (is_empty(self.alternative) && self.body instanceof AST_SimpleStatement) {
|
||||||
|| self.alternative instanceof AST_EmptyStatement)
|
|
||||||
&& self.body instanceof AST_SimpleStatement) {
|
|
||||||
if (negated_is_best) return make_node(AST_SimpleStatement, self, {
|
if (negated_is_best) return make_node(AST_SimpleStatement, self, {
|
||||||
body: make_node(AST_Binary, self, {
|
body: make_node(AST_Binary, self, {
|
||||||
operator : "||",
|
operator : "||",
|
||||||
@@ -999,7 +1133,7 @@ function Compressor(options, false_by_default) {
|
|||||||
value: make_node(AST_Conditional, self, {
|
value: make_node(AST_Conditional, self, {
|
||||||
condition : self.condition,
|
condition : self.condition,
|
||||||
consequent : self.body.value,
|
consequent : self.body.value,
|
||||||
alternative : self.alternative.value
|
alternative : self.alternative.value || make_node(AST_Undefined, self).optimize(compressor)
|
||||||
}).optimize(compressor)
|
}).optimize(compressor)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1010,11 +1144,10 @@ function Compressor(options, false_by_default) {
|
|||||||
operator: "&&",
|
operator: "&&",
|
||||||
left: self.condition,
|
left: self.condition,
|
||||||
right: self.body.condition
|
right: self.body.condition
|
||||||
});
|
}).optimize(compressor);
|
||||||
self.body = self.body.body;
|
self.body = self.body.body;
|
||||||
}
|
}
|
||||||
var abort = self.body.aborts();
|
if (aborts(self.body)) {
|
||||||
if (abort) {
|
|
||||||
if (self.alternative) {
|
if (self.alternative) {
|
||||||
var alt = self.alternative;
|
var alt = self.alternative;
|
||||||
self.alternative = null;
|
self.alternative = null;
|
||||||
@@ -1023,6 +1156,15 @@ function Compressor(options, false_by_default) {
|
|||||||
}).optimize(compressor);
|
}).optimize(compressor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (aborts(self.alternative)) {
|
||||||
|
var body = self.body;
|
||||||
|
self.body = self.alternative;
|
||||||
|
self.condition = negated_is_best ? negated : self.condition.negate(compressor);
|
||||||
|
self.alternative = null;
|
||||||
|
return make_node(AST_BlockStatement, self, {
|
||||||
|
body: [ self, body ]
|
||||||
|
}).optimize(compressor);
|
||||||
|
}
|
||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1125,7 +1267,7 @@ function Compressor(options, false_by_default) {
|
|||||||
return self.optimize(compressor);
|
return self.optimize(compressor);
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Lambda.DEFMETHOD("optimize", function(compressor){
|
AST_Function.DEFMETHOD("optimize", function(compressor){
|
||||||
if (compressor.option("unused_func")) {
|
if (compressor.option("unused_func")) {
|
||||||
if (this.name && this.name.unreferenced()) {
|
if (this.name && this.name.unreferenced()) {
|
||||||
this.name = null;
|
this.name = null;
|
||||||
@@ -1324,6 +1466,19 @@ function Compressor(options, false_by_default) {
|
|||||||
if (this.operator.length == 2) this.operator += "=";
|
if (this.operator.length == 2) this.operator += "=";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "&&":
|
||||||
|
case "||":
|
||||||
|
if (this.left instanceof AST_UnaryPrefix && this.left.operator == "!"
|
||||||
|
&& this.right instanceof AST_UnaryPrefix && this.right.operator == "!") {
|
||||||
|
this.left = this.left.expression;
|
||||||
|
this.right = this.right.expression;
|
||||||
|
this.operator = this.operator == "&&" ? "||" : "&&";
|
||||||
|
return make_node(AST_UnaryPrefix, this, {
|
||||||
|
operator: "!",
|
||||||
|
expression: this
|
||||||
|
}).optimize(compressor);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (compressor.option("booleans") && compressor.in_boolean_context()) switch (this.operator) {
|
if (compressor.option("booleans") && compressor.in_boolean_context()) switch (this.operator) {
|
||||||
case "&&":
|
case "&&":
|
||||||
@@ -1435,16 +1590,16 @@ function Compressor(options, false_by_default) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
AST_Undefined.DEFMETHOD("optimize", function(compressor){
|
AST_Undefined.DEFMETHOD("optimize", function(compressor){
|
||||||
if (compressor.option("unsafe") && !(compressor.parent() instanceof AST_Array)) {
|
// if (compressor.option("unsafe") && !(compressor.parent() instanceof AST_Array)) {
|
||||||
return make_node(AST_Sub, this, {
|
// return make_node(AST_Sub, this, {
|
||||||
expression: make_node(AST_Array, this, {
|
// expression: make_node(AST_Array, this, {
|
||||||
elements: []
|
// elements: []
|
||||||
}),
|
// }),
|
||||||
property: make_node(AST_Number, this, {
|
// property: make_node(AST_Number, this, {
|
||||||
value: 0
|
// value: 0
|
||||||
})
|
// })
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
return this;
|
return this;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -892,6 +892,7 @@ function OutputStream(options) {
|
|||||||
DEFPRINT(AST_Undefined, function(self, output){
|
DEFPRINT(AST_Undefined, function(self, output){
|
||||||
// XXX: should add more options for this
|
// XXX: should add more options for this
|
||||||
output.print("void 0");
|
output.print("void 0");
|
||||||
|
//output.print("[][0]");
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_This, function(self, output){
|
DEFPRINT(AST_This, function(self, output){
|
||||||
output.print("this");
|
output.print("this");
|
||||||
|
|||||||
14
lib/utils.js
14
lib/utils.js
@@ -109,23 +109,23 @@ function defaults(args, defs) {
|
|||||||
function noop() {};
|
function noop() {};
|
||||||
|
|
||||||
var MAP = (function(){
|
var MAP = (function(){
|
||||||
function MAP(a, f, o) {
|
function MAP(a, f, backwards) {
|
||||||
var ret = [], top = [], i;
|
var ret = [], top = [], i;
|
||||||
function doit() {
|
function doit() {
|
||||||
var val = f.call(o, a[i], i);
|
var val = f(a[i], i);
|
||||||
var is_last = val instanceof Last;
|
var is_last = val instanceof Last;
|
||||||
if (is_last) val = val.v;
|
if (is_last) val = val.v;
|
||||||
if (val instanceof AtTop) {
|
if (val instanceof AtTop) {
|
||||||
val = val.v;
|
val = val.v;
|
||||||
if (val instanceof Splice) {
|
if (val instanceof Splice) {
|
||||||
top.push.apply(top, val.v);
|
top.push.apply(top, backwards ? val.v.slice().reverse() : val.v);
|
||||||
} else {
|
} else {
|
||||||
top.push(val);
|
top.push(val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (val !== skip) {
|
else if (val !== skip) {
|
||||||
if (val instanceof Splice) {
|
if (val instanceof Splice) {
|
||||||
ret.push.apply(ret, val.v);
|
ret.push.apply(ret, backwards ? val.v.slice().reverse() : val.v);
|
||||||
} else {
|
} else {
|
||||||
ret.push(val);
|
ret.push(val);
|
||||||
}
|
}
|
||||||
@@ -133,8 +133,14 @@ var MAP = (function(){
|
|||||||
return is_last;
|
return is_last;
|
||||||
};
|
};
|
||||||
if (a instanceof Array) {
|
if (a instanceof Array) {
|
||||||
|
if (backwards) {
|
||||||
|
for (i = a.length; --i >= 0;) if (doit()) break;
|
||||||
|
ret.reverse();
|
||||||
|
top.reverse();
|
||||||
|
} else {
|
||||||
for (i = 0; i < a.length; ++i) if (doit()) break;
|
for (i = 0; i < a.length; ++i) if (doit()) break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
for (i in a) if (HOP(a, i)) if (doit()) break;
|
for (i in a) if (HOP(a, i)) if (doit()) break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,9 @@ ifs_4: {
|
|||||||
|
|
||||||
ifs_5: {
|
ifs_5: {
|
||||||
options = {
|
options = {
|
||||||
if_return: true
|
if_return: true,
|
||||||
|
conditionals: true,
|
||||||
|
comparations: true,
|
||||||
};
|
};
|
||||||
input: {
|
input: {
|
||||||
function f() {
|
function f() {
|
||||||
@@ -117,7 +119,7 @@ ifs_5: {
|
|||||||
function g() {
|
function g() {
|
||||||
if (!(foo || bar || baz || baa)) {
|
if (!(foo || bar || baz || baa)) {
|
||||||
a();
|
a();
|
||||||
b()
|
b();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user