From: Mihai Bazon Date: Tue, 21 Aug 2012 12:45:05 +0000 (+0300) Subject: cleaned up some mess and started the actual compressor X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=ffe58a9961ced012b40c16b93f9fe2370293431b;p=UglifyJS.git cleaned up some mess and started the actual compressor --- diff --git a/lib/ast.js b/lib/ast.js index bb2a07c0..1b75bd64 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -35,6 +35,9 @@ var AST_Token = DEFNODE("Token", "type value line col pos endpos nlb comments_be }, null); var AST_Node = DEFNODE("Node", "start end", { + clone: function() { + return new this.CTOR(this); + }, $documentation: "Base class of all AST nodes", _walk: function(visitor) { return visitor._visit(this); @@ -44,9 +47,10 @@ var AST_Node = DEFNODE("Node", "start end", { } }, null); -AST_Node.warn_function = noop; +AST_Node.warn_function = null; AST_Node.warn = function(txt, props) { - AST_Node.warn_function(string_template(txt, props)); + if (AST_Node.warn_function) + AST_Node.warn_function(string_template(txt, props)); }; var AST_Debugger = DEFNODE("Debugger", null, { @@ -86,10 +90,9 @@ var AST_BlockStatement = DEFNODE("BlockStatement", null, { $documentation: "A block statement.", _walk: function(visitor) { return visitor._visit(this, function(){ - var a = this.body, i = 0, n = a.length; - while (i < n) { - a[i++]._walk(visitor); - } + this.body.forEach(function(stat){ + stat._walk(visitor); + }); }); } }, AST_Statement); @@ -135,9 +138,8 @@ var AST_ForIn = DEFNODE("ForIn", "init name object", { $documentation: "A `for ... in` statement", _walk: function(visitor) { return visitor._visit(this, function(){ - if (this.init) this.init._walk(visitor); - else if (this.name) this.name._walk(visitor); - if (this.object) this.object._walk(visitor); + this.init._walk(visitor); + this.object._walk(visitor); this.body._walk(visitor); }); } @@ -295,7 +297,7 @@ var AST_Try = DEFNODE("Try", "btry bcatch bfinally", { // IE which simply introduces the name in the surrounding scope. If // we ever want to fix this then AST_Catch should inherit from // AST_Scope. -var AST_Catch = DEFNODE("Catch", "argname", { +var AST_Catch = DEFNODE("Catch", "argname body", { $documentation: "A `catch` node; only makes sense as part of a `try` statement", _walk: function(visitor) { return visitor._visit(this, function(){ @@ -303,11 +305,16 @@ var AST_Catch = DEFNODE("Catch", "argname", { this.body._walk(visitor); }); } -}, AST_BlockStatement); +}); -var AST_Finally = DEFNODE("Finally", null, { - $documentation: "A `finally` node; only makes sense as part of a `try` statement" -}, AST_BlockStatement); +var AST_Finally = DEFNODE("Finally", "body", { + $documentation: "A `finally` node; only makes sense as part of a `try` statement", + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.body._walk(visitor); + }); + } +}); /* -----[ VAR/CONST ]----- */ diff --git a/lib/compress.js b/lib/compress.js new file mode 100644 index 00000000..025c262d --- /dev/null +++ b/lib/compress.js @@ -0,0 +1,249 @@ +// The layout of the compressor follows the code generator (see +// output.js). Basically each node will have a "squeeze" method +// that will apply all known compression rules for that node, and +// return a new node (or the original node if there was no +// compression). We can't quite use the TreeWalker for this +// because it's too simplistic. + +// The Compressor object is for storing the options and for +// maintaining various internal state that might be useful for +// squeezing nodes. + +function Compressor(options) { + options = defaults(options, { + sequences : true, + dead_code : true, + keep_comps : true, + drop_debugger : true, + unsafe : true + }); + var stack = []; + return { + option : function(key) { return options[key] }, + push_node : function(node) { stack.push(node) }, + pop_node : function() { return stack.pop() }, + stack : function() { return stack }, + parent : function(n) { + return stack[stack.length - 2 - (n || 0)]; + } + }; +}; + +(function(){ + + AST_Node.DEFMETHOD("squeeze", function(){ + return this; + }); + + AST_Token.DEFMETHOD("squeeze", function(){ + return this; + }); + + function SQUEEZE(nodetype, squeeze) { + nodetype.DEFMETHOD("squeeze", function(compressor){ + compressor.push_node(this); + var new_node = squeeze(this, compressor); + compressor.pop_node(); + return new_node || this; + }); + }; + + function do_list(array, compressor) { + return MAP(array, function(node){ + if (node instanceof Array) { + sys.debug(node.map(function(node){ + return node.TYPE; + }).join("\n")); + } + return node.squeeze(compressor); + }); + }; + + SQUEEZE(AST_Debugger, function(self, compressor){ + if (compressor.option("drop_debugger")) + return new AST_EmptyStatement(self); + }); + + SQUEEZE(AST_LabeledStatement, function(self, compressor){ + self = self.clone(); + self.statement = self.statement.squeeze(compressor); + return self.label.references.length == 0 ? self.statement : self; + }); + + SQUEEZE(AST_Statement, function(self, compressor){ + self = self.clone(); + self.body = self.body.squeeze(compressor); + return self; + }); + + SQUEEZE(AST_BlockStatement, function(self, compressor){ + self = self.clone(); + self.body = do_list(self.body, compressor); + return self; + }); + + SQUEEZE(AST_EmptyStatement, function(self, compressor){ + if (compressor.parent() instanceof AST_BlockStatement) + return MAP.skip; + }); + + SQUEEZE(AST_DWLoop, function(self, compressor){ + self = self.clone(); + self.condition = self.condition.squeeze(compressor); + self.body = self.body.squeeze(compressor); + return self; + }); + + SQUEEZE(AST_For, function(self, compressor){ + self = self.clone(); + if (self.init) self.init = self.init.squeeze(compressor); + 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; + }); + + SQUEEZE(AST_ForIn, function(self, compressor){ + self = self.clone(); + self.init = self.init.squeeze(compressor); + self.object = self.object.squeeze(compressor); + self.body = self.body.squeeze(compressor); + return self; + }); + + SQUEEZE(AST_With, function(self, compressor){ + self = self.clone(); + self.expression = self.expression.squeeze(compressor); + self.body = self.body.squeeze(compressor); + }); + + SQUEEZE(AST_Exit, function(self, compressor){ + self = self.clone(); + if (self.value) self.value = self.value.squeeze(compressor); + return self; + }); + + SQUEEZE(AST_LoopControl, function(self, compressor){ + self = self.clone(); + if (self.label) self.label = self.label.squeeze(compressor); + return self; + }); + + SQUEEZE(AST_If, function(self, compressor){ + self = self.clone(); + self.condition = self.condition.squeeze(compressor); + self.consequent = self.consequent.squeeze(compressor); + if (self.alternative) + self.alternative = self.alternative.squeeze(compressor); + return self; + }); + + SQUEEZE(AST_Switch, function(self, compressor){ + self = self.clone(); + self.expression = self.expression.squeeze(compressor); + self.body = self.body.squeeze(compressor); + return self; + }); + + SQUEEZE(AST_Case, function(self, compressor){ + self = self.clone(); + self.expression = self.expression.squeeze(compressor); + self.body = do_list(self.body, compressor); + return self; + }); + + SQUEEZE(AST_Try, function(self, compressor){ + self = self.clone(); + self.btry = self.btry.squeeze(compressor); + if (self.bcatch) self.bcatch = self.bcatch.squeeze(compressor); + if (self.bfinally) self.bfinally = self.bfinally.squeeze(compressor); + return self; + }); + + SQUEEZE(AST_Definitions, function(self, compressor){ + self = self.clone(); + self.definitions = do_list(self.definitions, compressor); + return self; + }); + + SQUEEZE(AST_VarDef, function(self, compressor){ + self = self.clone(); + if (self.value) self.value = self.value.squeeze(compressor); + return self; + }); + + SQUEEZE(AST_Lambda, function(self, compressor){ + self = self.clone(); + if (self.name) self.name = self.name.squeeze(compressor); + self.argnames = do_list(self.argnames, compressor); + self.body = self.body.squeeze(compressor); + return self; + }); + + SQUEEZE(AST_Call, function(self, compressor){ + self = self.clone(); + self.expression = self.expression.squeeze(compressor); + self.args = do_list(self.args, compressor); + return self; + }); + + SQUEEZE(AST_Seq, function(self, compressor){ + self = self.clone(); + self.first = self.first.squeeze(compressor); + self.second = self.second.squeeze(compressor); + return self; + }); + + SQUEEZE(AST_Dot, function(self, compressor){ + self = self.clone(); + self.expression = self.expression.squeeze(compressor); + return self; + }); + + SQUEEZE(AST_Sub, function(self, compressor){ + self = self.clone(); + self.expression = self.expression.squeeze(compressor); + self.property = self.property.squeeze(compressor); + return self; + }); + + SQUEEZE(AST_Unary, function(self, compressor){ + self = self.clone(); + self.expression = self.expression.squeeze(compressor); + return self; + }); + + SQUEEZE(AST_Binary, function(self, compressor){ + self = self.clone(); + self.left = self.left.squeeze(compressor); + self.right = self.right.squeeze(compressor); + return self; + }); + + SQUEEZE(AST_Conditional, function(self, compressor){ + self = self.clone(); + self.condition = self.condition.squeeze(compressor); + self.consequent = self.consequent.squeeze(compressor); + self.alternative = self.alternative.squeeze(compressor); + return self; + }); + + SQUEEZE(AST_Array, function(self, compressor){ + self = self.clone(); + self.elements = do_list(self.elements, compressor); + return self; + }); + + SQUEEZE(AST_Object, function(self, compressor){ + self = self.clone(); + self.properties = do_list(self.properties, compressor); + return self; + }); + + SQUEEZE(AST_ObjectProperty, function(self, compressor){ + self = self.clone(); + self.value = self.value.squeeze(compressor); + return self; + }); + +})(); diff --git a/lib/output.js b/lib/output.js index bd38f9d5..61524239 100644 --- a/lib/output.js +++ b/lib/output.js @@ -444,11 +444,7 @@ function OutputStream(options) { output.print("for"); output.space(); output.with_parens(function(){ - if (self.init) { - self.init.print(output); - } else { - self.name.print(output); - } + self.init.print(output); output.space(); output.print("in"); output.space(); diff --git a/lib/parse.js b/lib/parse.js index 78b8bb7c..7f144295 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -746,7 +746,7 @@ function parse($TEXT, exigent_mode) { case "punc": switch (S.token.value) { case "{": - return new AST_BlockStatement({ body: block_() }); + return block_(); case "[": case "(": return simple_statement(); @@ -942,7 +942,7 @@ function parse($TEXT, exigent_mode) { --S.in_function; S.in_loop = loop; S.labels = labels; - return new AST_BlockStatement({ body: a }); + return a; })() }); }; @@ -961,19 +961,25 @@ function parse($TEXT, exigent_mode) { }; function block_() { + var start = S.token; expect("{"); var a = []; while (!is("punc", "}")) { if (is("eof")) unexpected(); a.push(statement()); } + var end = S.token; next(); - return a; + return new AST_BlockStatement({ + start : start, + body : a, + end : end + }); }; var switch_block_ = embed_tokens(curry(in_loop, function(){ expect("{"); - var a = [], cur = null, branch = null; + var a = [], cur = null, branch = null, start = S.token; while (!is("punc", "}")) { if (is("eof")) unexpected(); if (is("keyword", "case")) { @@ -1002,16 +1008,17 @@ function parse($TEXT, exigent_mode) { } } if (branch) branch.end = prev(); + var end = S.token; next(); - return new AST_SwitchBlock({ body: a }); + return new AST_SwitchBlock({ + start : start, + body : a, + end : end + }); })); function try_() { - var body = new AST_BlockStatement({ - start : S.token, - body : block_(), - end : prev() - }), bcatch = null, bfinally = null; + var body = block_(), bcatch = null, bfinally = null; if (is("keyword", "catch")) { var start = S.token; next(); @@ -1021,11 +1028,7 @@ function parse($TEXT, exigent_mode) { bcatch = new AST_Catch({ start : start, argname : name, - body : new AST_BlockStatement({ - start : S.token, - body : block_(), - end : prev() - }), + body : block_(), end : prev() }); } @@ -1034,11 +1037,7 @@ function parse($TEXT, exigent_mode) { next(); bfinally = new AST_Finally({ start : start, - body : new AST_BlockStatement({ - start : S.token, - body : block_(), - end : prev() - }), + body : block_(), end : prev() }); } diff --git a/lib/scope.js b/lib/scope.js index 0ac797e0..05585fe0 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -138,19 +138,24 @@ AST_Toplevel.DEFMETHOD("scope_warnings", function(options){ col: node.start.col }); } - if (options.assign_to_global - && node instanceof AST_Assign - && node.left instanceof AST_SymbolRef - && (node.left.undeclared - || (node.left.symbol.global - && node.left.scope !== node.left.symbol.scope))) + if (options.assign_to_global) { - AST_Node.warn("{msg}: {name} [{line},{col}]", { - msg: node.left.undeclared ? "Accidental global?" : "Assignment to global", - name: node.left.name, - line: node.start.line, - col: node.start.col - }); + var sym = null; + if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef) + sym = node.left; + else if (node instanceof AST_ForIn && node.init instanceof AST_SymbolRef) + sym = node.init; + if (sym + && (sym.undeclared + || (sym.symbol.global + && sym.scope !== sym.symbol.scope))) { + AST_Node.warn("{msg}: {name} [{line},{col}]", { + msg: sym.undeclared ? "Accidental global?" : "Assignment to global", + name: sym.name, + line: sym.start.line, + col: sym.start.col + }); + } } if (options.eval && node instanceof AST_SymbolRef diff --git a/tmp/test-node.js b/tmp/test-node.js index 01716ad6..8b1251bb 100755 --- a/tmp/test-node.js +++ b/tmp/test-node.js @@ -18,6 +18,11 @@ time_it("scope", function(){ time_it("mangle", function(){ ast.mangle_names(); }); +time_it("compress", function(){ + var compressor = new UglifyJS.Compressor({ + }); + ast = ast.squeeze(compressor); +}); time_it("generate", function(){ ast.print(stream); }); diff --git a/tools/node.js b/tools/node.js index bb8ed7ae..b533e419 100644 --- a/tools/node.js +++ b/tools/node.js @@ -3,7 +3,9 @@ var vm = require("vm"); var sys = require("util"); var path = require("path"); -var UglifyJS = vm.createContext({}); +var UglifyJS = vm.createContext({ + sys: sys +}); function load_global(file) { file = path.resolve(path.dirname(module.filename), file);