diff options
author | Mihai Bazon <mihai@bazon.net> | 2012-09-23 12:47:34 +0300 |
---|---|---|
committer | Mihai Bazon <mihai@bazon.net> | 2012-09-23 12:47:34 +0300 |
commit | a83b28503f4673aeb744b45306500584a6480e29 (patch) | |
tree | 52c34c1e72d32531dd3e1a6c7a1eb935a70c23ec /lib/compress.js | |
parent | 76d88b59dcd28b836b2cdaf7908bfd5acaa88ebc (diff) | |
download | tracifyjs-a83b28503f4673aeb744b45306500584a6480e29.tar.gz tracifyjs-a83b28503f4673aeb744b45306500584a6480e29.zip |
properly drop mutually-referring declarations that are not otherwise
referenced and have no side effects
Diffstat (limited to 'lib/compress.js')
-rw-r--r-- | lib/compress.js | 144 |
1 files changed, 95 insertions, 49 deletions
diff --git a/lib/compress.js b/lib/compress.js index 6813925e..a93b39b9 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -64,8 +64,7 @@ function Compressor(options, false_by_default) { evaluate : !false_by_default, booleans : !false_by_default, loops : !false_by_default, - unused_func : !false_by_default, - unused_vars : !false_by_default, + unused : !false_by_default, hoist_funs : !false_by_default, hoist_vars : !false_by_default, if_return : !false_by_default, @@ -816,50 +815,113 @@ function Compressor(options, false_by_default) { return self; }); - AST_Scope.DEFMETHOD("drop_unused_vars", function(compressor){ - if (compressor.option("unused_vars")) { - var self = this; - var tw = new TreeWalker(function(node){ + AST_Scope.DEFMETHOD("drop_unused", function(compressor){ + var self = this; + if (compressor.option("unused") + && !(self instanceof AST_Toplevel) + && !self.uses_eval + ) { + var in_use = []; + // pass 1: find out which symbols are directly used in + // this scope (not in nested scopes). + var scope = this; + var tw = new TreeWalker(function(node, descend){ if (node !== self) { - if (node instanceof AST_Scope) + if (node instanceof AST_Defun) { return true; // don't go in nested scopes - if (node instanceof AST_Definitions) { - if (!(tw.parent() instanceof AST_ForIn)) { - var a = node.definitions; - for (var i = a.length; --i >= 0;) { - var def = a[i]; - var sym = def.name; - if (sym.unreferenced()) { - var warn = { - name: sym.name, - file: sym.start.file, - line: sym.start.line, - col: sym.start.col - }; - if (def.value && def.value.has_side_effects()) { - compressor.warn("Side effects in initialization of unreferenced variable {name} [{file}:{line},{col}]", warn); - } else { - compressor.warn("Dropping unreferenced variable {name} [{file}:{line},{col}]", warn); - a.splice(i, 1); - } - } + } + if (node instanceof AST_Definitions && scope === self) { + node.definitions.forEach(function(def){ + if (def.value && def.value.has_side_effects()) { + def.value.walk(tw); } - } + }); return true; } - if (!(node instanceof AST_Statement)) { - return true; // pointless to visit expressions + if (node instanceof AST_SymbolRef && !(node instanceof AST_LabelRef)) { + push_uniq(in_use, node.definition()); + return true; + } + if (node instanceof AST_Scope) { + var save_scope = scope; + scope = node; + descend(); + scope = save_scope; + return true; } } }); - this.walk(tw); + self.walk(tw); + // pass 2: for every used symbol we need to walk its + // initialization code to figure out if it uses other + // symbols (that may not be in_use). + for (var i = 0; i < in_use.length; ++i) { + in_use[i].orig.forEach(function(decl){ + // undeclared globals will be instanceof AST_SymbolRef + if (decl instanceof AST_SymbolDeclaration) { + decl.init.forEach(function(init){ + var tw = new TreeWalker(function(node){ + if (node instanceof AST_SymbolRef + && node.definition().scope.$self === self.$self) { + push_uniq(in_use, node.definition()); + } + }); + init.walk(tw); + }); + } + }); + } + // pass 3: we should drop declarations not in_use + var tt = new TreeTransformer( + function before(node, descend) { + if (node instanceof AST_Defun && node !== self) { + if (!member(node.name.definition(), in_use)) { + compressor.warn("Dropping unused function {name} [{file}:{line},{col}]", { + name : node.name.name, + file : node.name.start.file, + line : node.name.start.line, + col : node.name.start.col + }); + return make_node(AST_EmptyStatement, node); + } + return node; + } + if (node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn)) { + var def = node.definitions.filter(function(def){ + if (member(def.name.definition(), in_use)) return true; + var w = { + name : def.name.name, + file : def.name.start.file, + line : def.name.start.line, + col : def.name.start.col + }; + if (def.value && def.value.has_side_effects()) { + compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w); + return true; + } + compressor.warn("Dropping unused variable {name} [{file}:{line},{col}]", w); + return false; + }); + if (def.length == 0) { + return make_node(AST_EmptyStatement, node); + } + if (def.length != node.definitions.length) { + node.definitions = def; + return node; + } + } + if (node instanceof AST_Scope && node !== self) + return node; + } + ); + self.transform(tt); } }); AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){ var hoist_funs = compressor.option("hoist_funs"); var hoist_vars = compressor.option("hoist_vars"); - this.drop_unused_vars(compressor); + this.drop_unused(compressor); if (hoist_funs || hoist_vars) { var self = this; var hoisted = []; @@ -1264,7 +1326,7 @@ function Compressor(options, false_by_default) { }); AST_Function.DEFMETHOD("optimize", function(compressor){ - if (compressor.option("unused_func")) { + if (compressor.option("unused")) { if (this.name && this.name.unreferenced()) { this.name = null; } @@ -1272,22 +1334,6 @@ function Compressor(options, false_by_default) { return this; }); - AST_Defun.DEFMETHOD("optimize", function(compressor){ - if (compressor.option("unused_func")) { - if (this.name.unreferenced() - && !(this.parent_scope instanceof AST_Toplevel)) { - compressor.warn("Dropping unused function {name} [{file}:{line},{col}]", { - name: this.name.name, - file: this.start.file, - line: this.start.line, - col: this.start.col - }); - return make_node(AST_EmptyStatement, this); - } - } - return this; - }); - SQUEEZE(AST_Call, function(self, compressor){ self = self.clone(); self.expression = self.expression.squeeze(compressor); |