comparations : !false_by_default,
evaluate : !false_by_default,
booleans : !false_by_default,
- dwloops : !false_by_default,
+ loops : !false_by_default,
hoist_funs : !false_by_default,
hoist_vars : !false_by_default,
});
};
- function do_list(array, compressor) {
+ function do_list(array, compressor, splice_blocks) {
return MAP(array, function(node){
- return node.squeeze(compressor);
+ node = node.squeeze(compressor);
+ if (splice_blocks) {
+ if (node instanceof AST_BlockStatement) {
+ return MAP.splice(eliminate_spurious_blocks(node.body));
+ }
+ if (node instanceof AST_EmptyStatement)
+ return MAP.skip;
+ }
+ return node;
});
};
+ function eliminate_spurious_blocks(statements) {
+ return statements.reduce(function(a, stat){
+ if (stat instanceof AST_BlockStatement) {
+ a.push.apply(a, stat.body);
+ } else if (!(stat instanceof AST_EmptyStatement)) {
+ a.push(stat);
+ }
+ return a;
+ }, []);
+ };
+
function tighten_body(statements, compressor) {
- statements = do_list(statements, compressor);
- statements = eliminate_spurious_blocks(statements);
+ statements = do_list(statements, compressor, true);
if (compressor.option("dead_code")) {
statements = eliminate_dead_code(statements, compressor);
}
return statements;
};
- function eliminate_spurious_blocks(statements) {
- return statements.reduce(function(a, stat){
- if (stat instanceof AST_BlockStatement) {
- a.push.apply(a, stat.body);
- } else if (!(stat instanceof AST_EmptyStatement)) {
- a.push(stat);
+ function extract_declarations_from_unreachable_code(compressor, stat, target) {
+ 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);
+ }
+ else if (node instanceof AST_Defun) {
+ target.push(node);
+ }
+ return true; // no point to descend
}
- return a;
- }, []);
+ if (node instanceof AST_Scope) {
+ // also don't descend any other nested scopes
+ return true;
+ }
+ }));
};
function eliminate_dead_code(statements, compressor) {
a.push(stat);
}
else {
- 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();
- a.push(node);
- }
- else if (node instanceof AST_Defun) {
- a.push(node);
- }
- return true; // no point to descend
- }
- if (node instanceof AST_Scope) {
- // also don't descend any other nested scopes
- return true;
- }
- }));
+ extract_declarations_from_unreachable_code(compressor, stat, a);
};
} else {
a.push(stat);
return self.optimize(compressor);
});
+ function warn_dead_code(node) {
+ AST_Node.warn("Dropping unreachable code [{line},{col}]", node.start);
+ };
+
AST_DWLoop.DEFMETHOD("optimize", function(compressor){
var self = this;
- if (!compressor.option("dwloops")) return self;
var cond = self.condition.evaluate(compressor);
+ self.condition = cond[0];
+ if (!compressor.option("loops")) return self;
if (cond.length == 2) {
if (cond[1]) {
return make_node(AST_For, self, {
body: self.body
});
} else if (self instanceof AST_While) {
- AST_Node.warn("Unreachable code [{line},{col}]", self.start);
- return make_node(AST_EmptyStatement, self);
+ if (compressor.option("dead_code")) {
+ warn_dead_code(self);
+ var a = [];
+ extract_declarations_from_unreachable_code(compressor, self.body, a);
+ return make_node(AST_BlockStatement, self, { body: a });
+ }
}
}
return self;
if (self.condition) self.condition = self.condition.squeeze(compressor);
if (self.step) self.step = self.step.squeeze(compressor);
self.body = self.body.squeeze(compressor);
- return self;
+ return self.optimize(compressor);
+ });
+
+ AST_For.DEFMETHOD("optimize", function(compressor){
+ var cond = this.condition;
+ if (cond) {
+ cond = cond.evaluate(compressor);
+ this.condition = cond[0];
+ }
+ if (!compressor.option("loops")) return this;
+ if (this.condition) {
+ var cond = this.condition.evaluate(compressor);
+ if (cond.length == 2 && !cond[1]) {
+ if (compressor.option("dead_code")) {
+ warn_dead_code(this.body);
+ var a = [];
+ if (this.init instanceof AST_Statement) a.push(this.init);
+ else if (this.init) a.push(make_node(AST_SimpleStatement, this.init, {
+ body: this.init
+ }));
+ extract_declarations_from_unreachable_code(compressor, this.body, a);
+ return make_node(AST_BlockStatement, this, {
+ body: a
+ });
+ }
+ }
+ }
+ return this;
});
SQUEEZE(AST_ForIn, function(self, compressor){
if (cond.length == 2) {
if (cond[1]) {
AST_Node.warn("Condition always true [{line},{col}]", self.condition.start);
- return self.body;
+ if (compressor.option("dead_code")) {
+ var a = [];
+ if (self.alternative) {
+ warn_dead_code(self.alternative);
+ extract_declarations_from_unreachable_code(compressor, self.alternative, a);
+ }
+ a.push(self.body);
+ return make_node(AST_BlockStatement, self, { body: a });
+ }
} else {
AST_Node.warn("Condition always false [{line},{col}]", self.condition.start);
- return self.alternative || make_node(AST_EmptyStatement, self);
+ if (compressor.option("dead_code")) {
+ warn_dead_code(self.body);
+ var a = [];
+ extract_declarations_from_unreachable_code(compressor, self.body, a);
+ if (self.alternative) a.push(self.alternative);
+ return make_node(AST_BlockStatement, self, { body: a });
+ }
}
}
if (self.condition instanceof AST_UnaryPrefix
self.body = self.alternative || make_node(AST_EmptyStatement, self);
self.alternative = tmp;
}
+ if (self.body instanceof AST_EmptyStatement
+ && self.alternative instanceof AST_EmptyStatement) {
+ return make_node(AST_SimpleStatement, self.condition, {
+ body: self.condition
+ });
+ }
if (self.body instanceof AST_SimpleStatement
&& self.alternative instanceof AST_SimpleStatement) {
return make_node(AST_SimpleStatement, self, {
SQUEEZE(AST_UnaryPrefix, function(self, compressor){
// need to determine the context before cloning the node
- var bool = compressor.in_boolean_context();
self = self.clone();
- var e = self.expression = self.expression.squeeze(compressor);
- if (compressor.option("booleans") && bool) {
- switch (self.operator) {
+ self.expression = self.expression.squeeze(compressor);
+ return self.optimize(compressor);
+ });
+
+ AST_UnaryPrefix.DEFMETHOD("optimize", function(compressor){
+ if (compressor.option("booleans") && compressor.in_boolean_context()) {
+ var e = this.expression;
+ switch (this.operator) {
case "!":
if (e instanceof AST_UnaryPrefix && e.operator == "!") {
// !!foo ==> foo, if we're in boolean context
case "typeof":
// typeof always returns a non-empty string, thus it's
// always true in booleans
- AST_Node.warn("Boolean expression always true [{line},{col}]", self.start);
- return make_node(AST_True, self).optimize(compressor);
+ AST_Node.warn("Boolean expression always true [{line},{col}]", this.start);
+ return make_node(AST_True, this).optimize(compressor);
}
}
- return self.evaluate(compressor)[0];
+ return this.evaluate(compressor)[0];
});
SQUEEZE(AST_Binary, function(self, compressor){