UglifyJS/lib/scope.js

569 lines
21 KiB
JavaScript
Raw Normal View History

2012-08-22 18:28:59 +00:00
/***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2
-------------------------------- (C) ---------------------------------
Author: Mihai Bazon
<mihai.bazon@gmail.com>
http://mihai.bazon.net/blog
Distributed under the BSD license:
2012-08-27 08:01:27 +00:00
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
2012-08-22 18:28:59 +00:00
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AS IS AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
***********************************************************************/
2012-10-02 09:45:31 +00:00
"use strict";
function SymbolDef(scope, index, orig) {
this.name = orig.name;
this.orig = [ orig ];
this.scope = scope;
this.references = [];
this.global = false;
this.mangled_name = null;
this.undeclared = false;
this.constant = false;
this.index = index;
};
SymbolDef.prototype = {
unmangleable: function(options) {
return (this.global && !(options && options.toplevel))
|| this.undeclared
2012-11-08 10:31:28 +00:00
|| (!(options && options.eval) && (this.scope.uses_eval || this.scope.uses_with));
},
mangle: function(options) {
if (!this.mangled_name && !this.unmangleable(options)) {
var s = this.scope;
2014-04-16 16:43:40 +00:00
if (!options.screw_ie8 && this.orig[0] instanceof Cola.AST_SymbolLambda)
s = s.parent_scope;
this.mangled_name = s.next_mangled(options, this);
}
}
};
2014-04-16 16:43:40 +00:00
Cola.AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
options = Cola.defaults(options, {
screw_ie8: false
});
2012-08-19 19:46:00 +00:00
// pass 1: setup scope chaining and handle definitions
var self = this;
var scope = self.parent_scope = null;
var defun = null;
var nesting = 0;
2014-04-16 16:43:40 +00:00
var tw = new Cola.TreeWalker(function(node, descend){
if (options.screw_ie8 && node instanceof Cola.AST_Catch) {
var save_scope = scope;
2014-04-16 16:43:40 +00:00
scope = new Cola.AST_Scope(node);
scope.init_scope_vars(nesting);
scope.parent_scope = save_scope;
descend();
scope = save_scope;
return true;
}
2014-04-16 16:43:40 +00:00
if (node instanceof Cola.AST_Scope) {
node.init_scope_vars(nesting);
var save_scope = node.parent_scope = scope;
var save_defun = defun;
defun = scope = node;
++nesting; descend(); --nesting;
scope = save_scope;
defun = save_defun;
2014-04-16 16:43:40 +00:00
return true; // don't descend again in Cola.TreeWalker
}
2014-04-16 16:43:40 +00:00
if (node instanceof Cola.AST_Directive) {
2012-09-18 10:21:09 +00:00
node.scope = scope;
2014-04-16 16:43:40 +00:00
Cola.push_uniq(scope.directives, node.value);
2012-09-18 10:21:09 +00:00
return true;
}
2014-04-16 16:43:40 +00:00
if (node instanceof Cola.AST_With) {
for (var s = scope; s; s = s.parent_scope)
s.uses_with = true;
return;
}
2014-04-16 16:43:40 +00:00
if (node instanceof Cola.AST_Symbol) {
node.scope = scope;
}
2014-04-16 16:43:40 +00:00
if (node instanceof Cola.AST_SymbolLambda) {
defun.def_function(node);
}
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_SymbolDefun) {
2012-08-20 14:19:30 +00:00
// Careful here, the scope where this should be defined is
// the parent scope. The reason is that we enter a new
2014-04-16 16:43:40 +00:00
// scope when we encounter the Cola.AST_Defun node (which is
// instanceof Cola.AST_Scope) but we get to the symbol a bit
2012-08-20 14:19:30 +00:00
// later.
(node.scope = defun.parent_scope).def_function(node);
}
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_SymbolVar
|| node instanceof Cola.AST_SymbolConst) {
var def = defun.def_variable(node);
2014-04-16 16:43:40 +00:00
def.constant = node instanceof Cola.AST_SymbolConst;
2013-02-06 09:15:31 +00:00
def.init = tw.parent().value;
2012-08-20 14:19:30 +00:00
}
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_SymbolCatch) {
(options.screw_ie8 ? scope : defun)
.def_variable(node);
}
});
self.walk(tw);
2012-08-19 19:46:00 +00:00
// pass 2: find back references and eval
var func = null;
2014-04-16 16:43:40 +00:00
var globals = self.globals = new Cola.Dictionary();
var tw = new Cola.TreeWalker(function(node, descend){
if (node instanceof Cola.AST_Lambda) {
var prev_func = func;
func = node;
descend();
func = prev_func;
return true;
}
2014-04-16 16:43:40 +00:00
if (node instanceof Cola.AST_SymbolRef) {
var name = node.name;
var sym = node.scope.find_variable(name);
if (!sym) {
var g;
if (globals.has(name)) {
g = globals.get(name);
} else {
g = new SymbolDef(self, globals.size(), node);
g.undeclared = true;
2013-05-22 10:08:19 +00:00
g.global = true;
globals.set(name, g);
}
node.thedef = g;
2014-04-16 16:43:40 +00:00
if (name == "eval" && tw.parent() instanceof Cola.AST_Call) {
for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope)
s.uses_eval = true;
}
if (func && name == "arguments") {
func.uses_arguments = true;
}
} else {
node.thedef = sym;
}
node.reference();
2012-09-28 08:12:47 +00:00
return true;
}
});
self.walk(tw);
});
2012-08-19 19:46:00 +00:00
2014-04-16 16:43:40 +00:00
Cola.AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
2012-09-18 10:21:09 +00:00
this.directives = []; // contains the directives defined in this scope, i.e. "use strict"
2014-04-16 16:43:40 +00:00
this.variables = new Cola.Dictionary(); // map name to Cola.AST_SymbolVar (variables defined in this scope; includes functions)
this.functions = new Cola.Dictionary(); // map name to Cola.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
this.nesting = nesting; // the nesting level of this scope (0 means toplevel)
});
2014-04-16 16:43:40 +00:00
Cola.AST_Scope.DEFMETHOD("strict", function(){
2012-09-28 08:12:47 +00:00
return this.has_directive("use strict");
});
2014-04-16 16:43:40 +00:00
Cola.AST_Lambda.DEFMETHOD("init_scope_vars", function(){
Cola.AST_Scope.prototype.init_scope_vars.apply(this, arguments);
this.uses_arguments = false;
});
2014-04-16 16:43:40 +00:00
Cola.AST_SymbolRef.DEFMETHOD("reference", function() {
var def = this.definition();
def.references.push(this);
2012-10-04 05:49:18 +00:00
var s = this.scope;
while (s) {
2014-04-16 16:43:40 +00:00
Cola.push_uniq(s.enclosed, def);
2012-10-11 07:28:48 +00:00
if (s === def.scope) break;
s = s.parent_scope;
}
this.frame = this.scope.nesting - def.scope.nesting;
});
2014-04-16 16:43:40 +00:00
Cola.AST_Scope.DEFMETHOD("find_variable", function(name){
if (name instanceof Cola.AST_Symbol) name = name.name;
return this.variables.get(name)
|| (this.parent_scope && this.parent_scope.find_variable(name));
});
2012-08-19 19:46:00 +00:00
2014-04-16 16:43:40 +00:00
Cola.AST_Scope.DEFMETHOD("has_directive", function(value){
2012-09-18 10:21:09 +00:00
return this.parent_scope && this.parent_scope.has_directive(value)
|| (this.directives.indexOf(value) >= 0 ? this : null);
});
2014-04-16 16:43:40 +00:00
Cola.AST_Scope.DEFMETHOD("def_function", function(symbol){
this.functions.set(symbol.name, this.def_variable(symbol));
});
2012-08-19 19:46:00 +00:00
2014-04-16 16:43:40 +00:00
Cola.AST_Scope.DEFMETHOD("def_variable", function(symbol){
var def;
if (!this.variables.has(symbol.name)) {
def = new SymbolDef(this, this.variables.size(), symbol);
this.variables.set(symbol.name, def);
def.global = !this.parent_scope;
} else {
def = this.variables.get(symbol.name);
def.orig.push(symbol);
}
return symbol.thedef = def;
});
2012-08-19 19:46:00 +00:00
2014-04-16 16:43:40 +00:00
Cola.AST_Scope.DEFMETHOD("next_mangled", function(options){
var ext = this.enclosed;
out: while (true) {
var m = base54(++this.cname);
2014-04-17 10:10:18 +00:00
if (!Cola.is_identifier(m, options.is_js)) continue; // skip over "do"
2013-12-16 18:37:09 +00:00
// https://github.com/mishoo/UglifyJS2/issues/242 -- do not
// shadow a name excepted from mangling.
if (options.except.indexOf(m) >= 0) continue;
// 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 = ext.length; --i >= 0;) {
2012-08-20 14:19:30 +00:00
var sym = ext[i];
var name = sym.mangled_name || (sym.unmangleable(options) && sym.name);
2012-08-20 14:19:30 +00:00
if (m == name) continue out;
}
return m;
}
});
2014-04-16 16:43:40 +00:00
Cola.AST_Function.DEFMETHOD("next_mangled", function(options, def){
// #179, #326
2013-10-29 13:53:54 +00:00
// in Safari strict mode, something like (function x(x){...}) is a syntax error;
// a function expression's argument cannot shadow the function expression's name
2014-04-16 16:43:40 +00:00
var tricky_def = def.orig[0] instanceof Cola.AST_SymbolFunarg && this.name && this.name.definition();
while (true) {
2014-04-16 16:43:40 +00:00
var name = Cola.AST_Lambda.prototype.next_mangled.call(this, options, def);
if (!(tricky_def && tricky_def.mangled_name == name))
return name;
}
});
2014-04-16 16:43:40 +00:00
Cola.AST_Scope.DEFMETHOD("references", function(sym){
if (sym instanceof Cola.AST_Symbol) sym = sym.definition();
2012-09-28 08:12:47 +00:00
return this.enclosed.indexOf(sym) < 0 ? null : sym;
});
2014-04-16 16:43:40 +00:00
Cola.AST_Symbol.DEFMETHOD("unmangleable", function(options){
return this.definition().unmangleable(options);
});
// property accessors are not mangleable
2014-04-16 16:43:40 +00:00
Cola.AST_SymbolAccessor.DEFMETHOD("unmangleable", function(){
return true;
});
// labels are always mangleable
2014-04-16 16:43:40 +00:00
Cola.AST_Label.DEFMETHOD("unmangleable", function(){
return false;
});
2014-04-16 16:43:40 +00:00
Cola.AST_Symbol.DEFMETHOD("unreferenced", function(){
2012-09-19 09:27:38 +00:00
return this.definition().references.length == 0
&& !(this.scope.uses_eval || this.scope.uses_with);
2012-08-20 14:19:30 +00:00
});
2014-04-16 16:43:40 +00:00
Cola.AST_Symbol.DEFMETHOD("undeclared", function(){
return this.definition().undeclared;
});
2014-04-16 16:43:40 +00:00
Cola.AST_LabelRef.DEFMETHOD("undeclared", function(){
2012-10-02 10:20:07 +00:00
return false;
});
2014-04-16 16:43:40 +00:00
Cola.AST_Label.DEFMETHOD("undeclared", function(){
2012-10-02 10:20:07 +00:00
return false;
});
2014-04-16 16:43:40 +00:00
Cola.AST_Symbol.DEFMETHOD("definition", function(){
return this.thedef;
2012-08-21 08:53:19 +00:00
});
2014-04-16 16:43:40 +00:00
Cola.AST_Symbol.DEFMETHOD("global", function(){
return this.definition().global;
2012-08-21 08:53:19 +00:00
});
2014-04-16 16:43:40 +00:00
Cola.AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
return Cola.defaults(options, {
except : [],
eval : false,
sort : false,
toplevel : false,
2014-04-17 10:10:18 +00:00
screw_ie8 : false,
is_js : false
});
2012-11-08 10:31:28 +00:00
});
2014-04-16 16:43:40 +00:00
Cola.AST_Toplevel.DEFMETHOD("mangle_names", function(options){
2012-11-08 10:31:28 +00:00
options = this._default_mangler_options(options);
// We only need to mangle declaration nodes. Special logic wired
// into the code generator will display the mangled name if it's
2014-04-16 16:43:40 +00:00
// present (and for Cola.AST_SymbolRef-s it'll use the mangled name of
// the Cola.AST_SymbolDeclaration that it points to).
var lname = -1;
var to_mangle = [];
2014-04-16 16:43:40 +00:00
var tw = new Cola.TreeWalker(function(node, descend){
if (node instanceof Cola.AST_LabeledStatement) {
// lname is incremented when we get to the Cola.AST_Label
var save_nesting = lname;
descend();
lname = save_nesting;
2014-04-16 16:43:40 +00:00
return true; // don't descend again in Cola.TreeWalker
}
2014-04-16 16:43:40 +00:00
if (node instanceof Cola.AST_Scope) {
2013-01-02 10:39:00 +00:00
var p = tw.parent(), a = [];
node.variables.each(function(symbol){
if (options.except.indexOf(symbol.name) < 0) {
2013-01-02 10:39:00 +00:00
a.push(symbol);
}
});
2013-01-02 10:39:00 +00:00
if (options.sort) a.sort(function(a, b){
return b.references.length - a.references.length;
});
to_mangle.push.apply(to_mangle, a);
return;
}
2014-04-16 16:43:40 +00:00
if (node instanceof Cola.AST_Label) {
var name;
2014-04-17 10:10:18 +00:00
do name = base54(++lname); while (!Cola.is_identifier(name, options.is_js));
node.mangled_name = name;
return true;
2012-08-20 14:19:30 +00:00
}
2014-04-16 16:43:40 +00:00
if (options.screw_ie8 && node instanceof Cola.AST_SymbolCatch) {
to_mangle.push(node.definition());
return;
}
2012-08-20 14:19:30 +00:00
});
this.walk(tw);
to_mangle.forEach(function(def){ def.mangle(options) });
2012-08-20 14:19:30 +00:00
});
2014-04-16 16:43:40 +00:00
Cola.AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
2012-11-08 10:31:28 +00:00
options = this._default_mangler_options(options);
2014-04-16 16:43:40 +00:00
var tw = new Cola.TreeWalker(function(node){
if (node instanceof Cola.AST_Constant)
base54.consider(node.print_to_string());
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_Return)
base54.consider("return");
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_Throw)
base54.consider("throw");
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_Continue)
base54.consider("continue");
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_Break)
base54.consider("break");
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_Debugger)
base54.consider("debugger");
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_Directive)
base54.consider(node.value);
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_While)
base54.consider("while");
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_Do)
2012-09-18 11:17:23 +00:00
base54.consider("do while");
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_If) {
base54.consider("if");
if (node.alternative) base54.consider("else");
}
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_Var)
base54.consider("var");
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_Const)
base54.consider("const");
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_Lambda)
base54.consider("function");
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_For)
base54.consider("for");
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_ForIn)
2012-09-18 11:17:23 +00:00
base54.consider("for in");
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_Switch)
base54.consider("switch");
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_Case)
base54.consider("case");
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_Default)
base54.consider("default");
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_With)
base54.consider("with");
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_ObjectSetter)
base54.consider("set" + node.key);
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_ObjectGetter)
base54.consider("get" + node.key);
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_ObjectKeyVal)
base54.consider(node.key);
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_New)
base54.consider("new");
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_This)
base54.consider("this");
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_Try)
base54.consider("try");
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_Catch)
base54.consider("catch");
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_Finally)
base54.consider("finally");
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_Symbol && node.unmangleable(options))
base54.consider(node.name);
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_Unary || node instanceof Cola.AST_Binary)
base54.consider(node.operator);
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_Dot)
base54.consider(node.property);
});
this.walk(tw);
base54.sort();
});
var base54 = (function() {
var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789";
var chars, frequency;
function reset() {
frequency = Object.create(null);
chars = string.split("").map(function(ch){ return ch.charCodeAt(0) });
chars.forEach(function(ch){ frequency[ch] = 0 });
}
base54.consider = function(str){
for (var i = str.length; --i >= 0;) {
var code = str.charCodeAt(i);
if (code in frequency) ++frequency[code];
}
};
base54.sort = function() {
2014-04-16 16:43:40 +00:00
chars = Cola.mergeSort(chars, function(a, b){
if (Cola.is_digit(a) && !Cola.is_digit(b)) return 1;
if (Cola.is_digit(b) && !Cola.is_digit(a)) return -1;
return frequency[b] - frequency[a];
});
};
base54.reset = reset;
reset();
base54.get = function(){ return chars };
base54.freq = function(){ return frequency };
function base54(num) {
var ret = "", base = 54;
do {
ret += String.fromCharCode(chars[num % base]);
num = Math.floor(num / base);
base = 64;
} while (num > 0);
return ret;
};
return base54;
})();
2014-04-16 16:43:40 +00:00
Cola.AST_Toplevel.DEFMETHOD("scope_warnings", function(options){
options = Cola.defaults(options, {
undeclared : false, // this makes a lot of noise
unreferenced : true,
assign_to_global : true,
func_arguments : true,
nested_defuns : true,
eval : true
});
2014-04-16 16:43:40 +00:00
var tw = new Cola.TreeWalker(function(node){
if (options.undeclared
2014-04-16 16:43:40 +00:00
&& node instanceof Cola.AST_SymbolRef
&& node.undeclared())
{
// XXX: this also warns about JS standard names,
// i.e. Object, Array, parseInt etc. Should add a list of
// exceptions.
2014-04-16 16:43:40 +00:00
Cola.AST_Node.warn("Undeclared symbol: {name} [{file}:{line},{col}]", {
name: node.name,
file: node.start.file,
line: node.start.line,
col: node.start.col
});
}
if (options.assign_to_global)
{
var sym = null;
2014-04-16 16:43:40 +00:00
if (node instanceof Cola.AST_Assign && node.left instanceof Cola.AST_SymbolRef)
sym = node.left;
2014-04-16 16:43:40 +00:00
else if (node instanceof Cola.AST_ForIn && node.init instanceof Cola.AST_SymbolRef)
sym = node.init;
if (sym
&& (sym.undeclared()
|| (sym.global() && sym.scope !== sym.definition().scope))) {
2014-04-16 16:43:40 +00:00
Cola.AST_Node.warn("{msg}: {name} [{file}:{line},{col}]", {
msg: sym.undeclared() ? "Accidental global?" : "Assignment to global",
name: sym.name,
file: sym.start.file,
line: sym.start.line,
col: sym.start.col
});
}
}
if (options.eval
2014-04-16 16:43:40 +00:00
&& node instanceof Cola.AST_SymbolRef
&& node.undeclared()
&& node.name == "eval") {
2014-04-16 16:43:40 +00:00
Cola.AST_Node.warn("Eval is used [{file}:{line},{col}]", node.start);
}
if (options.unreferenced
2014-04-16 16:43:40 +00:00
&& (node instanceof Cola.AST_SymbolDeclaration || node instanceof Cola.AST_Label)
&& node.unreferenced()) {
2014-04-16 16:43:40 +00:00
Cola.AST_Node.warn("{type} {name} is declared but not referenced [{file}:{line},{col}]", {
type: node instanceof Cola.AST_Label ? "Label" : "Symbol",
name: node.name,
file: node.start.file,
line: node.start.line,
col: node.start.col
});
}
if (options.func_arguments
2014-04-16 16:43:40 +00:00
&& node instanceof Cola.AST_Lambda
&& node.uses_arguments) {
2014-04-16 16:43:40 +00:00
Cola.AST_Node.warn("arguments used in function {name} [{file}:{line},{col}]", {
name: node.name ? node.name.name : "anonymous",
file: node.start.file,
line: node.start.line,
col: node.start.col
});
}
if (options.nested_defuns
2014-04-16 16:43:40 +00:00
&& node instanceof Cola.AST_Defun
&& !(tw.parent() instanceof Cola.AST_Scope)) {
Cola.AST_Node.warn("Function {name} declared in nested statement \"{type}\" [{file}:{line},{col}]", {
name: node.name.name,
type: tw.parent().TYPE,
file: node.start.file,
line: node.start.line,
col: node.start.col
});
}
});
this.walk(tw);
});