aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMihai Bazon <mihai@bazon.net>2012-08-21 12:55:56 +0300
committerMihai Bazon <mihai@bazon.net>2012-08-21 13:53:16 +0300
commit7ae1c600a24e2f43feb839c5fd1625f6261751b5 (patch)
treeabe06f041accc5dc0a4d277ae29612f2ee5af080
parent92bd53b513c6cf030d96ed627efc50dd1875ba85 (diff)
downloadtracifyjs-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.js20
-rw-r--r--lib/output.js16
-rw-r--r--lib/scope.js53
-rwxr-xr-xtmp/test-node.js109
-rw-r--r--tools/node.js37
5 files changed, 125 insertions, 110 deletions
diff --git a/lib/ast.js b/lib/ast.js
index c38b3277..bb2a07c0 100644
--- a/lib/ast.js
+++ b/lib/ast.js
@@ -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];
+ }
+}