102 lines
3.4 KiB
JavaScript
102 lines
3.4 KiB
JavaScript
AST_Scope.DEFMETHOD("figure_out_scope", function(){
|
|
// This does what ast_add_scope did in UglifyJS v1.
|
|
//
|
|
// Part of it could be done at parse time, but it would complicate
|
|
// the parser (and it's already kinda complex). It's also worth
|
|
// having it separated because we might need to call it multiple
|
|
// times on the same tree.
|
|
|
|
// pass 1: setup scope chaining and handle definitions
|
|
var scope = null;
|
|
var tw = new TreeWalker(function(node, descend){
|
|
if (node instanceof AST_Scope) {
|
|
var save_scope = node.parent_scope = scope;
|
|
scope = node;
|
|
descend.call(node);
|
|
scope = save_scope;
|
|
return true; // don't descend again in TreeWalker
|
|
}
|
|
if (node instanceof AST_With) {
|
|
for (var s = scope; s; s = s.parent_scope)
|
|
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) {
|
|
scope.def_function(node);
|
|
}
|
|
else if (node instanceof AST_SymbolDefun) {
|
|
scope.parent_scope.def_function(node);
|
|
}
|
|
else if (node instanceof AST_Label) {
|
|
scope.def_label(node);
|
|
}
|
|
else if (node instanceof AST_SymbolCatch) {
|
|
// XXX: this is wrong according to ECMA-262 (12.4). the
|
|
// `catch` argument name should be visible only inside the
|
|
// catch block. For a quick fix AST_Catch should inherit
|
|
// from AST_Scope.
|
|
scope.def_variable(node);
|
|
}
|
|
else if (node instanceof AST_SymbolRef) {
|
|
node.scope = scope;
|
|
}
|
|
});
|
|
this.walk(tw);
|
|
|
|
// pass 2: find back references and eval/with
|
|
var tw = new TreeWalker(function(node){
|
|
if (node instanceof AST_LabelRef) {
|
|
var sym = node.scope.find_label(node);
|
|
if (!sym) throw new Error("Undefined label " + node.name);
|
|
node.reference(sym);
|
|
}
|
|
else if (node instanceof AST_SymbolRef) {
|
|
var sym = node.scope.find_variable(node);
|
|
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("find_variable", function(name){
|
|
if (name instanceof AST_Symbol) name = name.name;
|
|
return this.variables[name] ||
|
|
(this.name && this.name.name == name && this.name) ||
|
|
(this.parent_scope && this.parent_scope.find_variable(name));
|
|
});
|
|
|
|
AST_Scope.DEFMETHOD("find_label", function(name){
|
|
if (name instanceof AST_Symbol) name = name.name;
|
|
return this.labels[name];
|
|
});
|
|
|
|
AST_Scope.DEFMETHOD("def_function", function(symbol){
|
|
this.def_variable(symbol);
|
|
this.functions[symbol.name] = symbol;
|
|
symbol.scope = this;
|
|
});
|
|
|
|
AST_Scope.DEFMETHOD("def_variable", function(symbol){
|
|
this.variables[symbol.name] = symbol;
|
|
delete this.functions[symbol.name];
|
|
symbol.scope = this;
|
|
});
|
|
|
|
AST_Scope.DEFMETHOD("def_label", function(symbol){
|
|
this.labels[symbol.name] = symbol;
|
|
symbol.scope = this;
|
|
});
|