diff options
author | Mihai Bazon <mihai@bazon.net> | 2012-08-21 12:55:56 +0300 |
---|---|---|
committer | Mihai Bazon <mihai@bazon.net> | 2012-08-21 13:53:16 +0300 |
commit | 7ae1c600a24e2f43feb839c5fd1625f6261751b5 (patch) | |
tree | abe06f041accc5dc0a4d277ae29612f2ee5af080 | |
parent | 92bd53b513c6cf030d96ed627efc50dd1875ba85 (diff) | |
download | tracifyjs-7ae1c600a24e2f43feb839c5fd1625f6261751b5.tar.gz tracifyjs-7ae1c600a24e2f43feb839c5fd1625f6261751b5.zip |
some reorganization
(moved pretty much everything that relates to scope in scope.js, added a
module for NodeJS that can be used with require() and exports everything.)
-rw-r--r-- | lib/ast.js | 20 | ||||
-rw-r--r-- | lib/output.js | 16 | ||||
-rw-r--r-- | lib/scope.js | 53 | ||||
-rwxr-xr-x | tmp/test-node.js | 109 | ||||
-rw-r--r-- | tools/node.js | 37 |
5 files changed, 125 insertions, 110 deletions
@@ -156,16 +156,7 @@ var AST_With = DEFNODE("With", "expression", { /* -----[ scope and functions ]----- */ var AST_Scope = DEFNODE("Scope", null, { - $documentation: "Base class for all statements introducing a lexical scope", - initialize: function() { - this.variables = {}; // map name to AST_SymbolVar (variables defined in this scope; includes functions) - this.functions = {}; // map name to AST_SymbolDefun (functions defined in this scope) - this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement - this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval` - this.parent_scope = null; // the parent scope - this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes - this.cname = -1; // the current index for mangling functions/variables - } + $documentation: "Base class for all statements introducing a lexical scope" }, AST_BlockStatement); var AST_Toplevel = DEFNODE("Toplevel", null, { @@ -174,10 +165,6 @@ var AST_Toplevel = DEFNODE("Toplevel", null, { var AST_Lambda = DEFNODE("Lambda", "name argnames", { $documentation: "Base class for functions", - initialize: function() { - AST_Scope.prototype.initialize.call(this); - this.uses_arguments = false; - }, _walk: function(visitor) { return visitor._visit(this, function(){ if (this.name) this.name._walk(visitor); @@ -479,10 +466,7 @@ var AST_ObjectGetter = DEFNODE("ObjectGetter", null, { var AST_Symbol = DEFNODE("Symbol", "scope name", { }); -var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "references", { - initialize: function() { - this.references = []; - } +var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", null, { }, AST_Symbol); var AST_SymbolVar = DEFNODE("SymbolVar", null, { diff --git a/lib/output.js b/lib/output.js index 0d6518df..bd38f9d5 100644 --- a/lib/output.js +++ b/lib/output.js @@ -4,12 +4,11 @@ function OutputStream(options) { indent_start : 0, indent_level : 4, quote_keys : false, - space_colon : false, + space_colon : true, ascii_only : false, inline_script : false, width : 80, - beautify : true, - scope_style : "negate" + beautify : true }); var indentation = 0; @@ -184,7 +183,7 @@ function OutputStream(options) { function colon() { print(":"); - space(); + if (options.space_colon) space(); }; var stack = []; @@ -204,7 +203,7 @@ function OutputStream(options) { with_block : with_block, with_parens : with_parens, with_square : with_square, - options : function(opt) { return options[opt] }, + option : function(opt) { return options[opt] }, line : function() { return current_line }, col : function() { return current_col }, pos : function() { return current_pos }, @@ -228,7 +227,6 @@ function OutputStream(options) { nodetype.DEFMETHOD("print", function(stream){ var self = this; stream.push_node(self); - //stream.print("«" + self.TYPE + ":" + self.start.line + ":" + self.start.col + "»"); if (self.needs_parens(stream)) { stream.with_parens(function(){ generator(self, stream); @@ -777,10 +775,10 @@ function OutputStream(options) { }); DEFPRINT(AST_ObjectKeyVal, function(self, output){ var key = self.key; - if (output.options("quote_keys")) { + if (output.option("quote_keys")) { output.print_string(key); } else if ((typeof key == "number" - || !output.options("beautify") + || !output.option("beautify") && +key + "" == key) && parseFloat(key) >= 0) { output.print(make_num(key)); @@ -863,7 +861,7 @@ function OutputStream(options) { // self should be AST_New. decide if we want to show parens or not. function no_constructor_parens(self, output) { - return self.args.length == 0 && !output.options("beautify"); + return self.args.length == 0 && !output.option("beautify"); }; function best_of(a) { diff --git a/lib/scope.js b/lib/scope.js index a5719bf3..0ac797e0 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -11,6 +11,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){ var labels = {}; var tw = new TreeWalker(function(node, descend){ if (node instanceof AST_Scope) { + node.init_scope_vars(); var save_scope = node.parent_scope = scope; scope = node; descend(); @@ -31,6 +32,9 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){ delete labels[l.name]; return true; // no descend again } + if (node instanceof AST_SymbolDeclaration) { + node.init_scope_vars(); + } if (node instanceof AST_SymbolLambda) { scope.def_function(node); } @@ -65,15 +69,27 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){ }); this.walk(tw); - // pass 2: find back references and eval/with - var tw = new TreeWalker(function(node){ + // pass 2: find back references and eval + var func = null; + var tw = new TreeWalker(function(node, descend){ + if (node instanceof AST_Lambda) { + var prev_func = func; + func = node; + descend(); + func = prev_func; + return true; + } if (node instanceof AST_SymbolRef) { var sym = node.scope.find_variable(node); node.reference(sym); if (!sym) { if (node.name == "eval") { - for (var s = node.scope; s; s = s.parent_scope) - s.uses_eval = true; + for (var s = node.scope; + s && !s.uses_eval; + s = s.parent_scope) s.uses_eval = true; + } + if (node.name == "arguments") { + func.uses_arguments = true; } } } @@ -81,11 +97,31 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){ this.walk(tw); }); +AST_Scope.DEFMETHOD("init_scope_vars", function(){ + this.variables = {}; // map name to AST_SymbolVar (variables defined in this scope; includes functions) + this.functions = {}; // map name to AST_SymbolDefun (functions defined in this scope) + this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement + this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval` + this.parent_scope = null; // the parent scope + this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes + this.cname = -1; // the current index for mangling functions/variables +}); + +AST_Lambda.DEFMETHOD("init_scope_vars", function(){ + AST_Scope.prototype.init_scope_vars.call(this); + this.uses_arguments = false; +}); + +AST_SymbolDeclaration.DEFMETHOD("init_scope_vars", function(){ + this.references = []; +}); + AST_Toplevel.DEFMETHOD("scope_warnings", function(options){ options = defaults(options, { undeclared : false, // this makes a lot of noise unreferenced : true, assign_to_global : true, + func_arguments : true, eval : true }); var tw = new TreeWalker(function(node){ @@ -132,6 +168,15 @@ AST_Toplevel.DEFMETHOD("scope_warnings", function(options){ col: node.start.col }); } + if (options.func_arguments + && node instanceof AST_Lambda + && node.uses_arguments) { + AST_Node.warn("arguments used in function {name} [{line},{col}]", { + name: node.name ? node.name.name : "anonymous", + line: node.start.line, + col: node.start.col + }); + } }); this.walk(tw); }); diff --git a/tmp/test-node.js b/tmp/test-node.js index b78fccbc..01716ad6 100755 --- a/tmp/test-node.js +++ b/tmp/test-node.js @@ -1,81 +1,32 @@ #! /usr/bin/env node -(function(){ - - var fs = require("fs"); - var vm = require("vm"); - var sys = require("util"); - var path = require("path"); - - function load_global(file) { - file = path.resolve(path.dirname(module.filename), file); - try { - var code = fs.readFileSync(file, "utf8"); - return vm.runInThisContext(code, file); - } catch(ex) { - sys.debug("ERROR in file: " + file + " / " + ex); - process.exit(1); - } - }; - - load_global("../lib/utils.js"); - load_global("../lib/ast.js"); - load_global("../lib/parse.js"); - load_global("../lib/scope.js"); - load_global("../lib/output.js"); - - AST_Node.warn_function = function(txt) { - sys.debug(txt); - }; - - /// - - var filename = process.argv[2]; - var code = fs.readFileSync(filename, "utf8"); - - var ast = time_it("parse", function() { - return parse(code); - }); - var stream = OutputStream({ beautify: true }); - time_it("scope", function(){ - ast.figure_out_scope(); - }); - time_it("mangle", function(){ - ast.mangle_names(); - }); - time_it("generate", function(){ - ast.print(stream); - }); - sys.puts(stream.get()); - - // var w = new TreeWalker(function(node, descend){ - // if (node.start) { - // console.log(node.TYPE + " [" + node.start.line + ":" + node.start.col + "]"); - // } else { - // console.log(node.TYPE + " [NO START]"); - // } - // if (node instanceof AST_Scope) { - // if (node.uses_eval) console.log("!!! uses eval"); - // if (node.uses_with) console.log("!!! uses with"); - // } - // if (node instanceof AST_SymbolDeclaration) { - // console.log("--- declaration " + node.name + (node.global ? " [global]" : "")); - // } - // else if (node instanceof AST_SymbolRef) { - // console.log("--- reference " + node.name + " to " + (node.symbol ? node.symbol.name : "global")); - // if (node.symbol) { - // console.log(" declaration at: " + node.symbol.start.line + ":" + node.symbol.start.col); - // } - // } - // }); - // ast._walk(w); - - ast.scope_warnings(); - - function time_it(name, cont) { - var t1 = new Date().getTime(); - try { return cont(); } - finally { sys.debug("// " + name + ": " + ((new Date().getTime() - t1) / 1000).toFixed(3) + " sec."); } - }; - -})(); +var sys = require("util"); +var fs = require("fs"); + +var UglifyJS = require("../tools/node.js"); + +var filename = process.argv[2]; +var code = fs.readFileSync(filename, "utf8"); + +var ast = time_it("parse", function() { + return UglifyJS.parse(code); +}); +var stream = UglifyJS.OutputStream({ beautify: true }); +time_it("scope", function(){ + ast.figure_out_scope(); +}); +time_it("mangle", function(){ + ast.mangle_names(); +}); +time_it("generate", function(){ + ast.print(stream); +}); +sys.puts(stream.get()); + +ast.scope_warnings(); + +function time_it(name, cont) { + var t1 = new Date().getTime(); + try { return cont(); } + finally { sys.debug("// " + name + ": " + ((new Date().getTime() - t1) / 1000).toFixed(3) + " sec."); } +}; diff --git a/tools/node.js b/tools/node.js new file mode 100644 index 00000000..bb8ed7ae --- /dev/null +++ b/tools/node.js @@ -0,0 +1,37 @@ +var fs = require("fs"); +var vm = require("vm"); +var sys = require("util"); +var path = require("path"); + +var UglifyJS = vm.createContext({}); + +function load_global(file) { + file = path.resolve(path.dirname(module.filename), file); + try { + var code = fs.readFileSync(file, "utf8"); + return vm.runInContext(code, UglifyJS, file); + } catch(ex) { + // XXX: in case of a syntax error, the message is kinda + // useless. (no location information). + sys.debug("ERROR in file: " + file + " / " + ex); + process.exit(1); + } +}; + +load_global("../lib/utils.js"); +load_global("../lib/ast.js"); +load_global("../lib/parse.js"); +load_global("../lib/scope.js"); +load_global("../lib/output.js"); +load_global("../lib/compress.js"); + +UglifyJS.AST_Node.warn_function = function(txt) { + sys.debug(txt); +}; + +// XXX: perhaps we shouldn't export everything but heck, I'm lazy. +for (var i in UglifyJS) { + if (UglifyJS.hasOwnProperty(i)) { + exports[i] = UglifyJS[i]; + } +} |