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.)
This commit is contained in:
20
lib/ast.js
20
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, {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
53
lib/scope.js
53
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);
|
||||
});
|
||||
|
||||
@@ -1,42 +1,17 @@
|
||||
#! /usr/bin/env node
|
||||
|
||||
(function(){
|
||||
|
||||
var fs = require("fs");
|
||||
var vm = require("vm");
|
||||
var sys = require("util");
|
||||
var path = require("path");
|
||||
var fs = require("fs");
|
||||
|
||||
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 UglifyJS = require("../tools/node.js");
|
||||
|
||||
var filename = process.argv[2];
|
||||
var code = fs.readFileSync(filename, "utf8");
|
||||
|
||||
var ast = time_it("parse", function() {
|
||||
return parse(code);
|
||||
return UglifyJS.parse(code);
|
||||
});
|
||||
var stream = OutputStream({ beautify: true });
|
||||
var stream = UglifyJS.OutputStream({ beautify: true });
|
||||
time_it("scope", function(){
|
||||
ast.figure_out_scope();
|
||||
});
|
||||
@@ -48,28 +23,6 @@
|
||||
});
|
||||
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) {
|
||||
@@ -77,5 +30,3 @@
|
||||
try { return cont(); }
|
||||
finally { sys.debug("// " + name + ": " + ((new Date().getTime() - t1) / 1000).toFixed(3) + " sec."); }
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
37
tools/node.js
Normal file
37
tools/node.js
Normal file
@@ -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];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user