diff options
Diffstat (limited to 'lib/scope.js')
-rw-r--r-- | lib/scope.js | 123 |
1 files changed, 114 insertions, 9 deletions
diff --git a/lib/scope.js b/lib/scope.js index 4c0aa105..d16ac8a0 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -21,18 +21,20 @@ AST_Scope.DEFMETHOD("figure_out_scope", function(){ s.uses_with = true; return; } - if (node instanceof AST_SymbolDeclaration && !scope.parent_scope) { - node.global = true; - } - if (node instanceof AST_SymbolVar) { - scope.def_variable(node); - } - else if (node instanceof AST_SymbolLambda) { + if (node instanceof AST_SymbolLambda) { scope.def_function(node); } else if (node instanceof AST_SymbolDefun) { + // Careful here, the scope where this should be defined is + // the parent scope. The reason is that we enter a new + // scope when we encounter the AST_Defun node (which is + // instanceof AST_Scope) but we get to the symbol a bit + // later. scope.parent_scope.def_function(node); } + else if (node instanceof AST_SymbolVar) { + scope.def_variable(node); + } else if (node instanceof AST_Label) { scope.def_label(node); } @@ -58,19 +60,72 @@ AST_Scope.DEFMETHOD("figure_out_scope", function(){ } else if (node instanceof AST_SymbolRef) { var sym = node.scope.find_variable(node); + node.reference(sym); if (!sym) { if (node.name == "eval") { for (var s = scope; s; s = s.parent_scope) s.uses_eval = true; } - } else { - node.reference(sym); } } }); this.walk(tw); }); +AST_Scope.DEFMETHOD("scope_warnings", function(options){ + options = defaults(options, { + undeclared : false, + assign_to_global : true + }); + var tw = new TreeWalker(function(node){ + if (options.undeclared + && node instanceof AST_SymbolRef + && node.undeclared) + { + // XXX: this also warns about JS standard names, + // i.e. Object, Array, parseInt etc. Should add a list of + // exceptions. + AST_Node.warn("Undeclared symbol: {name} [{line},{col}]", { + name: node.name, + line: node.start.line, + 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))) + { + 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 + }); + } + }); + this.walk(tw); +}); + +AST_SymbolRef.DEFMETHOD("reference", function(symbol) { + if (symbol) { + this.symbol = symbol; + var origin = symbol.scope; + symbol.references.push(this); + for (var s = this.scope; s; s = s.parent_scope) { + push_uniq(s.enclosed, symbol); + if (s === origin) break; + } + } else { + this.undeclared = true; + for (var s = this.scope; s; s = s.parent_scope) { + push_uniq(s.enclosed, this); + } + } +}); + AST_Scope.DEFMETHOD("find_variable", function(name){ if (name instanceof AST_Symbol) name = name.name; return this.variables[name] || @@ -90,6 +145,7 @@ AST_Scope.DEFMETHOD("def_function", function(symbol){ }); AST_Scope.DEFMETHOD("def_variable", function(symbol){ + symbol.global = !this.parent_scope; this.variables[symbol.name] = symbol; delete this.functions[symbol.name]; symbol.scope = this; @@ -99,3 +155,52 @@ AST_Scope.DEFMETHOD("def_label", function(symbol){ this.labels[symbol.name] = symbol; symbol.scope = this; }); + +AST_Scope.DEFMETHOD("next_mangled", function(for_label){ + var ext = this.enclosed, n = ext.length; + out: for (;;) { + var m = base54(for_label + ? (++this.lname) + : (++this.cname)); + + if (!is_identifier(m)) continue; // skip over "do" + + // labels are easy, since they can't be referenced from nested + // scopes. XXX: not sure that will be the case when the `let` + // keyword is to be supported. + if (for_label) return m; + + // if it's for functions or variables, we must ensure that the + // mangled name does not shadow a name from some parent scope + // that is referenced in this or in inner scopes. + for (var i = n; --i >= 0;) { + var sym = ext[i]; + var name = sym.mangled_name || sym.name; + if (m == name) continue out; + } + + return m; + } +}); + +AST_SymbolDeclaration.DEFMETHOD("mangle", function(){ + if (!this.global) + this.mangled_name = this.scope.next_mangled(false); +}); + +AST_Label.DEFMETHOD("mangle", function(){ + this.mangled_name = this.scope.next_mangled(true); +}); + +AST_Scope.DEFMETHOD("mangle_names", function(){ + var tw = new TreeWalker(function(node){ + // We only need to mangle declarations. Special logic wired + // into the code generator will display the mangled name if + // it's present (and for AST_SymbolRef-s it'll use the mangled + // name of the AST_SymbolDeclaration that it points to). + if (node instanceof AST_SymbolDeclaration) { + node.mangle(); + } + }); + this.walk(tw); +}); |