From 4201577dd7c75c1d1b1bddf5c153373275b8e977 Mon Sep 17 00:00:00 2001 From: Mihai Bazon Date: Tue, 25 Sep 2012 15:15:47 +0300 Subject: [PATCH] started some refactoring (WIP) -- moving squeezer to TreeTransformer --- lib/compress.js | 226 +++++++++++++++++++++++++++--------------------- 1 file changed, 126 insertions(+), 100 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 47053c40..ec16dc2b 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -146,31 +146,12 @@ function Compressor(options, false_by_default) { }); }; - function do_list(array, compressor, splice_blocks) { - return MAP(array, function(node, i){ - node = node.squeeze(compressor, array, i); - 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 do_list(array, compressor) { + return MAP(array, function(node){ + return node.squeeze(compressor); }); }; - function eliminate_spurious_blocks(statements) { - return statements.reduce(function(a, stat){ - if (stat instanceof AST_BlockStatement) { - a.push.apply(a, eliminate_spurious_blocks(stat.body)); - } else if (!(stat instanceof AST_EmptyStatement)) { - a.push(stat); - } - return a; - }, []); - }; - function as_statement_array(thing) { if (thing === null) return []; if (thing instanceof AST_BlockStatement) return thing.body; @@ -188,9 +169,9 @@ function Compressor(options, false_by_default) { function tighten_body(statements, compressor) { var CHANGED; - statements = do_list(statements, compressor, true); do { CHANGED = false; + statements = eliminate_spurious_blocks(statements); if (compressor.option("dead_code")) { statements = eliminate_dead_code(statements, compressor); } @@ -203,10 +184,23 @@ function Compressor(options, false_by_default) { if (compressor.option("join_vars")) { statements = join_consecutive_vars(statements, compressor); } - statements = eliminate_spurious_blocks(statements); } while (CHANGED); return statements; + function eliminate_spurious_blocks(statements) { + return statements.reduce(function(a, stat){ + if (stat instanceof AST_BlockStatement) { + CHANGED = true; + a.push.apply(a, eliminate_spurious_blocks(stat.body)); + } else if (stat instanceof AST_EmptyStatement) { + CHANGED = true; + } else { + a.push(stat); + } + return a; + }, []); + }; + function handle_if_return(statements, compressor) { var self = compressor.self(); var in_lambda = self instanceof AST_Lambda; @@ -289,7 +283,7 @@ function Compressor(options, false_by_default) { 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); + var body = as_statement_array(stat.body).slice(0, -1); stat = stat.clone(); stat.condition = stat.condition.negate(compressor); stat.body = make_node(AST_BlockStatement, stat, { @@ -308,10 +302,10 @@ function Compressor(options, false_by_default) { CHANGED = true; stat = stat.clone(); stat.body = make_node(AST_BlockStatement, stat.body, { - body: tighten_body(as_statement_array(stat.body).concat(ret), compressor) + body: as_statement_array(stat.body).concat(ret) }); stat.alternative = make_node(AST_BlockStatement, stat.alternative, { - body: tighten_body(as_statement_array(stat.alternative).slice(0, -1), compressor) + body: as_statement_array(stat.alternative).slice(0, -1) }); ret = [ stat.squeeze(compressor) ]; continue loop; @@ -331,6 +325,7 @@ function Compressor(options, false_by_default) { var has_quit = false; return statements.reduce(function(a, stat){ if (has_quit) { + CHANGED = true; extract_declarations_from_unreachable_code(compressor, stat, a); } else { a.push(stat); @@ -772,7 +767,7 @@ function Compressor(options, false_by_default) { }); AST_Directive.DEFMETHOD("optimize", function(compressor){ - if (this.hoisted || this.scope.has_directive(this.value) !== this.scope) { + if (this.scope.has_directive(this.value) !== this.scope) { return make_node(AST_EmptyStatement, this); } return this; @@ -806,11 +801,12 @@ function Compressor(options, false_by_default) { SQUEEZE(AST_BlockStatement, function(self, compressor){ self = self.clone(); - self.body = tighten_body(self.body, compressor); + self.body = do_list(self.body, compressor); return self.optimize(compressor); }); AST_BlockStatement.DEFMETHOD("optimize", function(compressor){ + this.body = tighten_body(this.body, compressor); switch (this.body.length) { case 1: return this.body[0]; case 0: return make_node(AST_EmptyStatement, this); @@ -820,15 +816,24 @@ function Compressor(options, false_by_default) { SQUEEZE(AST_Block, function(self, compressor){ self = self.clone(); - self.body = tighten_body(self.body, compressor); - return self; + self.body = do_list(self.body, compressor); + return self.optimize(compressor); + }); + + AST_Block.DEFMETHOD("optimize", function(compressor){ + this.body = tighten_body(this.body, compressor); + return this; }); SQUEEZE(AST_Scope, function(self, compressor){ - self = self.clone(); - self.hoist_declarations(compressor); - self.body = tighten_body(self.body, compressor); - return self; + self = self.clone().hoist_declarations(compressor); + self.body = do_list(self.body, compressor); + return self.optimize(compressor); + }); + + AST_Scope.DEFMETHOD("optimize", function(compressor){ + this.body = tighten_body(this.body, compressor); + return this; }); AST_Scope.DEFMETHOD("drop_unused", function(compressor){ @@ -937,48 +942,70 @@ function Compressor(options, false_by_default) { AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){ var hoist_funs = compressor.option("hoist_funs"); var hoist_vars = compressor.option("hoist_vars"); - this.drop_unused(compressor); + var self = this; + self.drop_unused(compressor); if (hoist_funs || hoist_vars) { - var self = this; + var dirs = []; var hoisted = []; - var defuns = {}; - var vars = {}, vars_found = 0, vardecl = []; - var tw = new TreeWalker(function(node){ - if (node !== self) { - if (node instanceof AST_Directive && (hoist_funs || hoist_vars) && !node.hoisted) { - hoisted.unshift(node.clone()); - node.hoisted = true; - } - if (node instanceof AST_Defun && hoist_funs && !node.hoisted) { - hoisted.push(node.clone()); - node.hoisted = true; - defuns[node.name.name] = true; - } - if (node instanceof AST_Var && hoist_vars && !node.hoisted) { - node.definitions.forEach(function(def){ - vars[def.name.name] = def; - ++vars_found; - }); - vardecl.push(node); + var vars = {}, vars_found = 0, var_decl = 0; + // let's count var_decl first, we seem to waste a lot of + // space if we hoist `var` when there's only one. + self.walk(new TreeWalker(function(node){ + if (node instanceof AST_Scope && node !== self) + return true; + if (node instanceof AST_Var) { + ++var_decl; + return true; + } + })); + hoist_vars = hoist_vars && var_decl > 1; + var tt = new TreeTransformer( + function before(node) { + if (node !== self) { + if (node instanceof AST_Directive) { + dirs.push(node); + return make_node(AST_EmptyStatement, node); + } + if (node instanceof AST_Defun && hoist_funs) { + hoisted.push(node); + return make_node(AST_EmptyStatement, node); + } + if (node instanceof AST_Var && hoist_vars) { + node.definitions.forEach(function(def){ + vars[def.name.name] = def; + ++vars_found; + }); + var seq = node.to_assignments(); + var p = tt.parent(); + if (p instanceof AST_ForIn && p.init === node) { + if (seq == null) return node.definitions[0].name; + return seq; + } + if (p instanceof AST_For && p.init === node) { + return seq; + } + if (!seq) return make_node(AST_EmptyStatement, node); + return make_node(AST_SimpleStatement, node, { + body: seq + }); + } + if (node instanceof AST_Scope) + return node; // to avoid descending in nested scopes } - if (node instanceof AST_Scope) - return true; } - }); - self.walk(tw); - if (vars_found > 0 && vardecl.length > 1) { - vardecl.forEach(function(v){ v.hoisted = true }); - var node = make_node(AST_Var, self, { - definitions: Object.keys(vars).map(function(name){ - var def = vars[name].clone(); - def.value = null; - return def; - }) - }); - hoisted.unshift(node); - } - self.body = hoisted.concat(self.body); + ); + self = self.transform(tt); + + if (vars_found > 0) hoisted.unshift(make_node(AST_Var, self, { + definitions: Object.keys(vars).map(function(name){ + var def = vars[name].clone(); + def.value = null; + return def; + }) + })); + self.body = dirs.concat(hoisted, self.body); } + return self; }); SQUEEZE(AST_SimpleStatement, function(self, compressor){ @@ -1260,16 +1287,26 @@ function Compressor(options, false_by_default) { SQUEEZE(AST_Case, function(self, compressor){ self = self.clone(); self.expression = self.expression.squeeze(compressor); - self.body = tighten_body(self.body, compressor); - return self; + self.body = do_list(self.body, compressor); + return self.optimize(compressor); + }); + + AST_Case.DEFMETHOD("optimize", function(compressor){ + this.body = tighten_body(this.body, compressor); + return this; }); SQUEEZE(AST_Try, function(self, compressor){ self = self.clone(); - self.body = tighten_body(self.body, compressor); + self.body = do_list(self.body, compressor); if (self.bcatch) self.bcatch = self.bcatch.squeeze(compressor); if (self.bfinally) self.bfinally = self.bfinally.squeeze(compressor); - return self; + return self.optimize(compressor); + }); + + AST_Try.DEFMETHOD("optimize", function(compressor){ + this.body = tighten_body(this.body, compressor); + return this; }); AST_Definitions.DEFMETHOD("remove_initializers", function(){ @@ -1304,25 +1341,15 @@ function Compressor(options, false_by_default) { }); SQUEEZE(AST_Definitions, function(self, compressor){ - if (self.definitions.length == 0) - return make_node(AST_EmptyStatement, self); - if (self.hoisted) { - var seq = self.to_assignments(); - var p = compressor.parent(); - if (seq) seq = seq.squeeze(compressor); - if (p instanceof AST_ForIn && p.init === self) { - if (seq == null) return self.definitions[0].name; //XXX: is this fine? - return seq; - } - if (p instanceof AST_For && p.init === self) { - return seq; - } - if (!seq) return make_node(AST_EmptyStatement, self); - return make_node(AST_SimpleStatement, self, { body: seq }); - } self = self.clone(); self.definitions = do_list(self.definitions, compressor); - return self; + return self.optimize(compressor); + }); + + AST_Definitions.DEFMETHOD("optimize", function(compressor){ + if (this.definitions.length == 0) + return make_node(AST_EmptyStatement, this); + return this; }); SQUEEZE(AST_VarDef, function(self, compressor){ @@ -1332,22 +1359,21 @@ function Compressor(options, false_by_default) { }); SQUEEZE(AST_Lambda, function(self, compressor){ - if (self.hoisted) return make_node(AST_EmptyStatement, self); - self = self.clone(); + self = self.clone().hoist_declarations(compressor); if (self.name) self.name = self.name.squeeze(compressor); self.argnames = do_list(self.argnames, compressor); - self.hoist_declarations(compressor); - self.body = tighten_body(self.body, compressor); + self.body = do_list(self.body, compressor); return self.optimize(compressor); }); AST_Function.DEFMETHOD("optimize", function(compressor){ + var self = AST_Lambda.prototype.optimize.call(this, compressor); if (compressor.option("unused")) { - if (this.name && this.name.unreferenced()) { - this.name = null; + if (self.name && self.name.unreferenced()) { + self.name = null; } } - return this; + return self; }); SQUEEZE(AST_Call, function(self, compressor){ -- 2.34.1