From: Mihai Bazon Date: Wed, 5 Sep 2012 10:43:34 +0000 (+0300) Subject: support for hoisting declarations X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=0503513dcc9e4dc9f01ffc894245076de9f48cb0;p=UglifyJS.git support for hoisting declarations and finally it seems we beat v1 in terms of compression --- diff --git a/lib/compress.js b/lib/compress.js index 71b20edb..3d2f5404 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -64,7 +64,8 @@ function Compressor(options, false_by_default) { evaluate : !false_by_default, booleans : !false_by_default, dwloops : !false_by_default, - hoist : !false_by_default, + hoist_funs : !false_by_default, + hoist_vars : !false_by_default, warnings : true }); @@ -102,7 +103,7 @@ function Compressor(options, false_by_default) { }; }; -(function(){ +(function(undefined){ AST_Node.DEFMETHOD("squeeze", function(){ return this; @@ -124,7 +125,7 @@ function Compressor(options, false_by_default) { compressor.push_node(this); var new_node = squeeze(this, compressor); compressor.pop_node(); - return new_node || this; + return new_node !== undefined ? new_node : this; }); }; @@ -148,12 +149,9 @@ function Compressor(options, false_by_default) { function eliminate_spurious_blocks(statements) { return statements.reduce(function(a, stat){ - if (stat.TYPE == "BlockStatement") { - // XXX: no instanceof here because we would catch - // AST_Lambda-s and other blocks too. perhaps we - // should refine the hierarchy. + if (stat instanceof AST_BlockStatement) { a.push.apply(a, stat.body); - } else { + } else if (!(stat instanceof AST_EmptyStatement)) { a.push(stat); } return a; @@ -494,7 +492,49 @@ function Compressor(options, false_by_default) { }); AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){ - if (compressor.option("hoist")) { + var hoist_funs = compressor.option("hoist_funs"); + var hoist_vars = compressor.option("hoist_vars"); + if (hoist_funs || hoist_vars) { + var self = this; + var hoisted = []; + var defuns = {}; + var vars = {}, vars_found = 0; + var tw = new TreeWalker(function(node){ + if (node !== self) { + if (node instanceof AST_Defun && hoist_funs) { + hoisted.push(node.clone()); + node.hoisted = true; + defuns[node.name.name] = true; + } + if (node instanceof AST_Var && hoist_vars) { + node.definitions.forEach(function(def){ + vars[def.name.name] = def; + ++vars_found; + }); + node.hoisted = true; + } + if (node instanceof AST_Scope) + return true; + } + }); + self.walk(tw); + if (vars_found > 0) { + if (self instanceof AST_Lambda && !self.uses_arguments) { + for (var i in vars) if (HOP(vars, i)) { + self.argnames.push(vars[i].name); + } + } else { + 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); } }); @@ -665,13 +705,49 @@ function Compressor(options, false_by_default) { AST_Definitions.DEFMETHOD("remove_initializers", function(){ this.definitions = this.definitions.map(function(def){ - var def = def.clone(); + def = def.clone(); def.value = null; return def; }); }); + AST_Definitions.DEFMETHOD("to_assignments", function(){ + var assignments = this.definitions.reduce(function(a, def){ + if (def.value) { + a.push(make_node(AST_Assign, def, { + operator : "=", + left : def.name, + right : def.value + })); + } + return a; + }, []); + if (assignments.length == 0) return null; + return (function seq(list){ + var first = list[0]; + if (list.length == 1) return first; + return make_node(AST_Seq, first, { + first: first, + second: seq(list.slice(1)) + }); + })(assignments); + }); + SQUEEZE(AST_Definitions, function(self, compressor){ + 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; @@ -684,10 +760,11 @@ 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.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); return self; });