aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMihai Bazon <mihai@bazon.net>2012-09-07 15:18:32 +0300
committerMihai Bazon <mihai@bazon.net>2012-09-07 15:18:32 +0300
commit919b2733ab52d3072bb4276210fe442bc0ade5fd (patch)
tree662eff0fbadc5c2b31fdda06e764f0f10c94eb6e
parentb77574ea1c25e2630dcde000ae0fa32b01f8311e (diff)
downloadtracifyjs-919b2733ab52d3072bb4276210fe442bc0ade5fd.tar.gz
tracifyjs-919b2733ab52d3072bb4276210fe442bc0ade5fd.zip
always keep declarations found in unreachable code
a few more tests and some cleanups.
-rw-r--r--lib/compress.js159
-rw-r--r--test/compress/conditionals.js74
-rw-r--r--test/compress/dead-code.js30
-rwxr-xr-xtest/run-tests.js6
-rw-r--r--tools/node.js2
5 files changed, 224 insertions, 47 deletions
diff --git a/lib/compress.js b/lib/compress.js
index a7563b98..ea0218ed 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -63,7 +63,7 @@ function Compressor(options, false_by_default) {
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,
@@ -129,15 +129,33 @@ function Compressor(options, 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);
}
@@ -147,15 +165,25 @@ function Compressor(options, false_by_default) {
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) {
@@ -166,24 +194,7 @@ function Compressor(options, false_by_default) {
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);
@@ -552,18 +563,27 @@ function Compressor(options, false_by_default) {
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;
@@ -575,7 +595,34 @@ function Compressor(options, false_by_default) {
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){
@@ -625,10 +672,24 @@ function Compressor(options, false_by_default) {
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
@@ -638,6 +699,12 @@ function Compressor(options, false_by_default) {
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, {
@@ -814,11 +881,15 @@ function Compressor(options, false_by_default) {
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
@@ -828,11 +899,11 @@ function Compressor(options, false_by_default) {
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){
diff --git a/test/compress/conditionals.js b/test/compress/conditionals.js
new file mode 100644
index 00000000..ba5e674d
--- /dev/null
+++ b/test/compress/conditionals.js
@@ -0,0 +1,74 @@
+ifs_1: {
+ options = {
+ conditionals: true
+ };
+ input: {
+ if (foo) bar();
+ if (!foo); else bar();
+ if (foo); else bar();
+ if (foo); else;
+ }
+ expect: {
+ foo&&bar();
+ foo&&bar();
+ foo||bar();
+ foo;
+ }
+}
+
+ifs_2: {
+ options = {
+ conditionals: true
+ };
+ input: {
+ if (foo) {
+ x();
+ } else if (bar) {
+ y();
+ } else if (baz) {
+ z();
+ }
+
+ if (foo) {
+ x();
+ } else if (bar) {
+ y();
+ } else if (baz) {
+ z();
+ } else {
+ t();
+ }
+ }
+ expect: {
+ foo ? x() : bar ? y() : baz && z();
+ foo ? x() : bar ? y() : baz ? z() : t();
+ }
+}
+
+ifs_3_should_warn: {
+ options = {
+ conditionals : true,
+ dead_code : true,
+ evaluate : true,
+ booleans : true
+ };
+ input: {
+ if (x && !(x + "1") && y) { // 1
+ var qq;
+ foo();
+ } else {
+ bar();
+ }
+
+ if (x || !!(x + "1") || y) { // 2
+ foo();
+ } else {
+ var jj;
+ bar();
+ }
+ }
+ expect: {
+ var qq; bar(); // 1
+ var jj; foo(); // 2
+ }
+}
diff --git a/test/compress/dead-code.js b/test/compress/dead-code.js
index e84b4011..0fd066eb 100644
--- a/test/compress/dead-code.js
+++ b/test/compress/dead-code.js
@@ -57,3 +57,33 @@ dead_code_2_should_warn: {
}
}
}
+
+dead_code_constant_boolean_should_warn_more: {
+ options = {
+ dead_code : true,
+ loops : true,
+ booleans : true,
+ conditionals : true,
+ evaluate : true
+ };
+ input: {
+ while (!((foo && bar) || (x + "0"))) {
+ console.log("unreachable");
+ var foo;
+ function bar() {}
+ }
+ for (var x = 10; x && (y || x) && (!typeof x); ++x) {
+ asdf();
+ foo();
+ var moo;
+ }
+ }
+ expect: {
+ var foo;
+ function bar() {}
+ // nothing for the while
+ // as for the for, it should keep:
+ var x = 10;
+ var moo;
+ }
+}
diff --git a/test/run-tests.js b/test/run-tests.js
index b70e2291..158c9d35 100755
--- a/test/run-tests.js
+++ b/test/run-tests.js
@@ -119,8 +119,10 @@ function parse_test(file) {
})
);
var stat = node.body;
- if (stat instanceof U.AST_BlockStatement && stat.body.length == 1)
- stat = stat.body[0];
+ if (stat instanceof U.AST_BlockStatement) {
+ if (stat.body.length == 1) stat = stat.body[0];
+ else if (stat.body.length == 0) stat = new U.AST_EmptyStatement();
+ }
test[node.label.name] = stat;
return true;
}
diff --git a/tools/node.js b/tools/node.js
index be83d171..c8d3bd8c 100644
--- a/tools/node.js
+++ b/tools/node.js
@@ -32,7 +32,7 @@ load_global("../lib/compress.js");
load_global("../lib/sourcemap.js");
UglifyJS.AST_Node.warn_function = function(txt) {
- sys.debug(txt);
+ sys.error("WARN: " + txt);
};
// XXX: perhaps we shouldn't export everything but heck, I'm lazy.