aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMihai Bazon <mihai@bazon.net>2012-09-13 15:20:57 +0300
committerMihai Bazon <mihai@bazon.net>2012-09-13 15:20:57 +0300
commitd72c1d1293487b7e775d0a248f8c9c848b01502c (patch)
treeb46efbd276d40679f1186bcb9a903a28b5dd6a81
parentf5027ec1fc67d6daeb51714eeddd06350cae00d6 (diff)
downloadtracifyjs-d72c1d1293487b7e775d0a248f8c9c848b01502c.tar.gz
tracifyjs-d72c1d1293487b7e775d0a248f8c9c848b01502c.zip
few more optimizations:
- do multiple passes in tighten_body if it was changed - transform if (foo) return x; return y; ==> return foo?x:y - don't optimize !0 as true (use best_of after evaluation of constant expr) With hoist_vars off we now beat UglifyJS v1 on jQuery-1.8.1
-rw-r--r--lib/compress.js237
-rw-r--r--test/compress/conditionals.js2
2 files changed, 135 insertions, 104 deletions
diff --git a/lib/compress.js b/lib/compress.js
index efdc6d83..15eab988 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -67,6 +67,7 @@ function Compressor(options, false_by_default) {
unused_func : !false_by_default,
hoist_funs : !false_by_default,
hoist_vars : !false_by_default,
+ if_return : !false_by_default,
warnings : true
});
@@ -93,6 +94,7 @@ function Compressor(options, false_by_default) {
push_node : function(node) { stack.push(node) },
pop_node : function() { return stack.pop() },
stack : function() { return stack },
+ self : function() { return stack[stack.length - 1] },
parent : function(n) {
return stack[stack.length - 2 - (n || 0)];
},
@@ -122,9 +124,9 @@ function Compressor(options, false_by_default) {
};
function SQUEEZE(nodetype, squeeze) {
- nodetype.DEFMETHOD("squeeze", function(compressor, block, index){
+ nodetype.DEFMETHOD("squeeze", function(compressor){
compressor.push_node(this);
- var new_node = squeeze(this, compressor, block, index);
+ var new_node = squeeze(this, compressor);
compressor.pop_node();
return new_node !== undefined ? new_node : this;
});
@@ -156,92 +158,146 @@ function Compressor(options, false_by_default) {
};
function tighten_body(statements, compressor) {
+ var CHANGED;
statements = do_list(statements, compressor, true);
- if (compressor.option("dead_code")) {
- statements = eliminate_dead_code(statements, compressor);
- }
- if (compressor.option("sequences")) {
- statements = sequencesize(statements, compressor);
- }
+ do {
+ CHANGED = false;
+ if (compressor.option("dead_code")) {
+ statements = eliminate_dead_code(statements, compressor);
+ }
+ if (compressor.option("sequences")) {
+ statements = sequencesize(statements, compressor);
+ }
+ if (compressor.option("if_return")) {
+ statements = handle_if_return(statements, compressor);
+ }
+ } while (CHANGED);
return statements;
- };
- function extract_declarations_from_unreachable_code(compressor, stat, target) {
- warn_dead_code(stat);
- stat.walk(new TreeWalker(function(node){
- if (node instanceof AST_Definitions || node instanceof AST_Defun) {
- compressor.warn("Declarations in unreachable code! [{line},{col}]", node.start);
- if (node instanceof AST_Definitions) {
- node = node.clone();
- node.remove_initializers();
- target.push(node);
+ function handle_if_return(statements, compressor) {
+ var in_lambda = compressor.self() instanceof AST_Lambda;
+ var last = statements.length - 1;
+ return MAP(statements, function(stat, i){
+ if (stat instanceof AST_If
+ && stat.body instanceof AST_Return
+ && !stat.body.value
+ && !stat.alternative
+ && in_lambda) {
+ CHANGED = true;
+ if (i < last) {
+ var rest = statements.slice(i + 1);
+ var cond = stat.condition;
+ while (rest[0] instanceof AST_If
+ && rest[0].body instanceof AST_Return
+ && !rest[0].alternative) {
+ cond = make_node(AST_Binary, rest[0], {
+ operator: "||",
+ left: cond,
+ right: rest[0].condition
+ });
+ rest.shift();
+ }
+ return MAP.last(make_node(AST_If, stat, {
+ condition: cond.negate(compressor),
+ body: make_node(AST_BlockStatement, stat, {
+ body: rest
+ }).optimize(compressor)
+ }).optimize(compressor));
+ } else {
+ return make_node(AST_SimpleStatement, stat, {
+ body: stat.condition
+ }).optimize(compressor);
+ }
}
- else if (node instanceof AST_Defun) {
- target.push(node);
+ if (stat instanceof AST_If
+ && stat.body instanceof AST_Return
+ && stat.body.value
+ && !stat.alternative
+ && i < last
+ && statements[i + 1] instanceof AST_Return
+ && statements[i + 1].value) {
+ CHANGED = true;
+ return MAP.last(make_node(AST_If, stat, {
+ condition: stat.condition,
+ body: stat.body,
+ alternative: statements[i + 1]
+ }).optimize(compressor));
}
- return true; // no point to descend
- }
- if (node instanceof AST_Scope) {
- // also don't descend any other nested scopes
- return true;
- }
- }));
- };
+ return stat;
+ });
+ };
- function eliminate_dead_code(statements, compressor) {
- var has_quit = false;
- return statements.reduce(function(a, stat){
- if (has_quit) {
- if (stat instanceof AST_Defun) {
+ function eliminate_dead_code(statements, compressor) {
+ var has_quit = false;
+ return statements.reduce(function(a, stat){
+ if (has_quit) {
+ extract_declarations_from_unreachable_code(compressor, stat, a);
+ } else {
a.push(stat);
+ if (stat instanceof AST_Jump) {
+ has_quit = true;
+ }
+ }
+ return a;
+ }, []);
+ };
+
+ // XXX: this is destructive -- it modifies tree nodes.
+ function sequencesize(statements) {
+ var prev = null, last = statements.length - 1;
+ if (last) statements = statements.reduce(function(a, cur, i){
+ if (prev instanceof AST_SimpleStatement
+ && cur instanceof AST_SimpleStatement) {
+ CHANGED = true;
+ var seq = make_node(AST_Seq, prev, {
+ first: prev.body,
+ second: cur.body
+ });
+ prev.body = seq;
+ }
+ else if (i == last
+ && cur instanceof AST_Exit && cur.value
+ && a.length > 0
+ && prev instanceof AST_SimpleStatement) {
+ CHANGED = true;
+ var seq = make_node(AST_Seq, prev, {
+ first: prev.body,
+ second: cur.value
+ });
+ cur.value = seq;
+ a.pop();
+ a.push(cur);
+ return a;
}
else {
- extract_declarations_from_unreachable_code(compressor, stat, a);
- };
- } else {
- a.push(stat);
- if (stat instanceof AST_Jump) {
- has_quit = true;
+ a.push(cur);
+ prev = cur;
}
- }
- return a;
- }, []);
+ return a;
+ }, []);
+ return statements;
+ };
+
};
- // XXX: this is destructive -- it modifies tree nodes.
- function sequencesize(statements) {
- var prev = null, last = statements.length - 1;
- if (last) statements = statements.reduce(function(a, cur, i){
- if (prev instanceof AST_SimpleStatement
- && cur instanceof AST_SimpleStatement) {
- var seq = make_node(AST_Seq, prev, {
- first: prev.body,
- second: cur.body
- });
- prev.body = seq;
+ function extract_declarations_from_unreachable_code(compressor, stat, target) {
+ warn_dead_code(stat);
+ stat.walk(new TreeWalker(function(node){
+ if (node instanceof AST_Definitions) {
+ compressor.warn("Declarations in unreachable code! [{line},{col}]", node.start);
+ node = node.clone();
+ node.remove_initializers();
+ target.push(node);
+ return true;
}
- else if (i == last
- && cur instanceof AST_Exit && cur.value
- && a.length > 0
- && prev instanceof AST_SimpleStatement) {
- // it only makes sense to do this transformation
- // if the AST gets to a single statement.
- var seq = make_node(AST_Seq, prev, {
- first: prev.body,
- second: cur.value
- });
- cur.value = seq;
- a.pop();
- a.push(cur);
- return a;
+ if (node instanceof AST_Defun) {
+ target.push(node);
+ return true;
}
- else {
- a.push(cur);
- prev = cur;
+ if (node instanceof AST_Scope) {
+ return true;
}
- return a;
- }, []);
- return statements;
+ }));
};
/* -----[ boolean/negation helpers ]----- */
@@ -337,7 +393,7 @@ function Compressor(options, false_by_default) {
type: typeof val
}));
}
- return [ ast, val ];
+ return [ best_of(ast, this), val ];
} catch(ex) {
if (ex !== def) throw ex;
return [ this ];
@@ -432,8 +488,7 @@ function Compressor(options, false_by_default) {
var self = this.clone();
self.consequent = self.consequent.negate(compressor);
self.alternative = self.alternative.negate(compressor);
- //return best_of(basic_negation(this), self);
- return self;
+ return best_of(basic_negation(this), self);
});
def(AST_Binary, function(compressor){
var self = this.clone(), op = this.operator;
@@ -723,16 +778,16 @@ function Compressor(options, false_by_default) {
return self;
});
- SQUEEZE(AST_If, function(self, compressor, block, index){
+ SQUEEZE(AST_If, function(self, compressor){
self = self.clone();
self.condition = self.condition.squeeze(compressor);
self.body = self.body.squeeze(compressor);
if (self.alternative)
self.alternative = self.alternative.squeeze(compressor);
- return self.optimize(compressor, block, index);
+ return self.optimize(compressor);
});
- AST_If.DEFMETHOD("optimize", function(compressor, block, index){
+ AST_If.DEFMETHOD("optimize", function(compressor){
var self = this;
if (!compressor.option("conditionals")) return self;
// if condition can be statically determined, warn and drop
@@ -827,29 +882,6 @@ function Compressor(options, false_by_default) {
}).optimize(compressor)
});
}
- if (self.body instanceof AST_Return
- && !self.body.value
- && !self.alternative
- && index < block.length - 1) {
- if (compressor.parent() instanceof AST_Lambda) {
- var rest = tighten_body(block.slice(index + 1), compressor);
- var cond = self.condition;
- while (rest[0] instanceof AST_If && rest[0].body instanceof AST_Return && !rest[0].alternative) {
- cond = make_node(AST_Binary, rest[0], {
- operator: "||",
- left: cond,
- right: rest[0].condition
- });
- rest.shift();
- }
- return MAP.last(make_node(AST_If, self, {
- condition: cond.negate(compressor),
- body: make_node(AST_BlockStatement, block[index + 1], {
- body: rest
- }).optimize(compressor)
- }).optimize(compressor));
- }
- }
if (self.body instanceof AST_If
&& !self.body.alternative
&& !self.alternative) {
@@ -1081,7 +1113,6 @@ function Compressor(options, false_by_default) {
});
SQUEEZE(AST_UnaryPrefix, function(self, compressor){
- // need to determine the context before cloning the node
self = self.clone();
self.expression = self.expression.squeeze(compressor);
return self.optimize(compressor);
diff --git a/test/compress/conditionals.js b/test/compress/conditionals.js
index 54c0751d..92e5d61b 100644
--- a/test/compress/conditionals.js
+++ b/test/compress/conditionals.js
@@ -90,7 +90,7 @@ ifs_4: {
ifs_5: {
options = {
- conditionals: true
+ if_return: true
};
input: {
function f() {