retrofit catch as block-scoped (#4165)
This commit is contained in:
41
lib/ast.js
41
lib/ast.js
@@ -412,33 +412,46 @@ var AST_With = DEFNODE("With", "expression", {
|
|||||||
|
|
||||||
/* -----[ scope and functions ]----- */
|
/* -----[ scope and functions ]----- */
|
||||||
|
|
||||||
var AST_Scope = DEFNODE("Scope", "cname enclosed uses_eval uses_with parent_scope functions variables make_def", {
|
var AST_BlockScope = DEFNODE("BlockScope", "enclosed functions make_def parent_scope variables", {
|
||||||
$documentation: "Base class for all statements introducing a lexical scope",
|
$documentation: "Base class for all statements introducing a lexical scope",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
cname: "[integer/S] current index for mangling variables (used internally by the mangler)",
|
|
||||||
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
|
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
|
||||||
uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
|
|
||||||
uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
|
|
||||||
parent_scope: "[AST_Scope?/S] link to the parent scope",
|
|
||||||
functions: "[Object/S] like `variables`, but only lists function declarations",
|
functions: "[Object/S] like `variables`, but only lists function declarations",
|
||||||
|
parent_scope: "[AST_Scope?/S] link to the parent scope",
|
||||||
variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
|
variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
|
||||||
},
|
},
|
||||||
clone: function(deep) {
|
clone: function(deep) {
|
||||||
var node = this._clone(deep);
|
var node = this._clone(deep);
|
||||||
if (this.variables) node.variables = this.variables.clone();
|
|
||||||
if (this.functions) node.functions = this.functions.clone();
|
|
||||||
if (this.enclosed) node.enclosed = this.enclosed.slice();
|
if (this.enclosed) node.enclosed = this.enclosed.slice();
|
||||||
|
if (this.functions) node.functions = this.functions.clone();
|
||||||
|
if (this.variables) node.variables = this.variables.clone();
|
||||||
return node;
|
return node;
|
||||||
},
|
},
|
||||||
|
pinned: function() {
|
||||||
|
return this.resolve().pinned();
|
||||||
|
},
|
||||||
|
resolve: function() {
|
||||||
|
return this.parent_scope.resolve();
|
||||||
|
},
|
||||||
|
_validate: function() {
|
||||||
|
if (this.parent_scope == null) return;
|
||||||
|
if (!(this.parent_scope instanceof AST_BlockScope)) throw new Error("parent_scope must be AST_BlockScope");
|
||||||
|
if (!(this.resolve() instanceof AST_Scope)) throw new Error("must be contained within AST_Scope");
|
||||||
|
},
|
||||||
|
}, AST_Block);
|
||||||
|
|
||||||
|
var AST_Scope = DEFNODE("Scope", "cname uses_eval uses_with", {
|
||||||
|
$documentation: "Base class for all statements introducing a lexical scope",
|
||||||
|
$propdoc: {
|
||||||
|
cname: "[integer/S] current index for mangling variables (used internally by the mangler)",
|
||||||
|
uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
|
||||||
|
uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
|
||||||
|
},
|
||||||
pinned: function() {
|
pinned: function() {
|
||||||
return this.uses_eval || this.uses_with;
|
return this.uses_eval || this.uses_with;
|
||||||
},
|
},
|
||||||
_validate: function() {
|
resolve: return_this,
|
||||||
if (this.parent_scope != null) {
|
}, AST_BlockScope);
|
||||||
if (!(this.parent_scope instanceof AST_Scope)) throw new Error("parent_scope must be AST_Scope");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}, AST_Block);
|
|
||||||
|
|
||||||
var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
||||||
$documentation: "The toplevel scope",
|
$documentation: "The toplevel scope",
|
||||||
@@ -703,7 +716,7 @@ var AST_Catch = DEFNODE("Catch", "argname", {
|
|||||||
_validate: function() {
|
_validate: function() {
|
||||||
if (!(this.argname instanceof AST_SymbolCatch)) throw new Error("argname must be AST_SymbolCatch");
|
if (!(this.argname instanceof AST_SymbolCatch)) throw new Error("argname must be AST_SymbolCatch");
|
||||||
},
|
},
|
||||||
}, AST_Block);
|
}, AST_BlockScope);
|
||||||
|
|
||||||
var AST_Finally = DEFNODE("Finally", null, {
|
var AST_Finally = DEFNODE("Finally", null, {
|
||||||
$documentation: "A `finally` node; only makes sense as part of a `try` statement"
|
$documentation: "A `finally` node; only makes sense as part of a `try` statement"
|
||||||
|
|||||||
@@ -5401,7 +5401,7 @@ merge(Compressor.prototype, {
|
|||||||
process_boolean_returns(this, compressor);
|
process_boolean_returns(this, compressor);
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("var_names", function() {
|
AST_BlockScope.DEFMETHOD("var_names", function() {
|
||||||
var var_names = this._var_names;
|
var var_names = this._var_names;
|
||||||
if (!var_names) {
|
if (!var_names) {
|
||||||
this._var_names = var_names = Object.create(null);
|
this._var_names = var_names = Object.create(null);
|
||||||
|
|||||||
35
lib/scope.js
35
lib/scope.js
@@ -100,19 +100,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
|||||||
var next_def_id = 0;
|
var next_def_id = 0;
|
||||||
var scope = self.parent_scope = null;
|
var scope = self.parent_scope = null;
|
||||||
var tw = new TreeWalker(function(node, descend) {
|
var tw = new TreeWalker(function(node, descend) {
|
||||||
if (node instanceof AST_Catch) {
|
if (node instanceof AST_BlockScope) {
|
||||||
var save_scope = scope;
|
|
||||||
scope = new AST_Scope(node);
|
|
||||||
scope.init_scope_vars(save_scope);
|
|
||||||
descend();
|
|
||||||
scope = save_scope;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (node instanceof AST_Scope) {
|
|
||||||
node.init_scope_vars(scope);
|
node.init_scope_vars(scope);
|
||||||
var save_scope = scope;
|
|
||||||
var save_defun = defun;
|
var save_defun = defun;
|
||||||
defun = scope = node;
|
var save_scope = scope;
|
||||||
|
if (node instanceof AST_Scope) defun = node;
|
||||||
|
scope = node;
|
||||||
descend();
|
descend();
|
||||||
scope = save_scope;
|
scope = save_scope;
|
||||||
defun = save_defun;
|
defun = save_defun;
|
||||||
@@ -267,7 +260,7 @@ function init_scope_vars(scope, parent) {
|
|||||||
if (parent) scope.make_def = parent.make_def; // top-level tracking of SymbolDef instances
|
if (parent) scope.make_def = parent.make_def; // top-level tracking of SymbolDef instances
|
||||||
}
|
}
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope) {
|
AST_BlockScope.DEFMETHOD("init_scope_vars", function(parent_scope) {
|
||||||
init_scope_vars(this, parent_scope);
|
init_scope_vars(this, parent_scope);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -300,20 +293,20 @@ AST_Symbol.DEFMETHOD("reference", function(options) {
|
|||||||
this.mark_enclosed(options);
|
this.mark_enclosed(options);
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("find_variable", function(name) {
|
AST_BlockScope.DEFMETHOD("find_variable", function(name) {
|
||||||
if (name instanceof AST_Symbol) name = name.name;
|
if (name instanceof AST_Symbol) name = name.name;
|
||||||
return this.variables.get(name)
|
return this.variables.get(name)
|
||||||
|| (this.parent_scope && this.parent_scope.find_variable(name));
|
|| (this.parent_scope && this.parent_scope.find_variable(name));
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("def_function", function(symbol, init) {
|
AST_BlockScope.DEFMETHOD("def_function", function(symbol, init) {
|
||||||
var def = this.def_variable(symbol, init);
|
var def = this.def_variable(symbol, init);
|
||||||
if (!def.init || def.init instanceof AST_Defun) def.init = init;
|
if (!def.init || def.init instanceof AST_Defun) def.init = init;
|
||||||
this.functions.set(symbol.name, def);
|
this.functions.set(symbol.name, def);
|
||||||
return def;
|
return def;
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
|
AST_BlockScope.DEFMETHOD("def_variable", function(symbol, init) {
|
||||||
var def = this.variables.get(symbol.name);
|
var def = this.variables.get(symbol.name);
|
||||||
if (def) {
|
if (def) {
|
||||||
def.orig.push(symbol);
|
def.orig.push(symbol);
|
||||||
@@ -326,12 +319,6 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
|
|||||||
return symbol.thedef = def;
|
return symbol.thedef = def;
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Lambda.DEFMETHOD("resolve", return_this);
|
|
||||||
AST_Scope.DEFMETHOD("resolve", function() {
|
|
||||||
return this.parent_scope.resolve();
|
|
||||||
});
|
|
||||||
AST_Toplevel.DEFMETHOD("resolve", return_this);
|
|
||||||
|
|
||||||
function names_in_use(scope, options) {
|
function names_in_use(scope, options) {
|
||||||
var names = scope.names_in_use;
|
var names = scope.names_in_use;
|
||||||
if (!names) {
|
if (!names) {
|
||||||
@@ -495,8 +482,7 @@ AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) {
|
|||||||
options.reserved.forEach(to_avoid);
|
options.reserved.forEach(to_avoid);
|
||||||
this.globals.each(add_def);
|
this.globals.each(add_def);
|
||||||
this.walk(new TreeWalker(function(node) {
|
this.walk(new TreeWalker(function(node) {
|
||||||
if (node instanceof AST_Scope) node.variables.each(add_def);
|
if (node instanceof AST_BlockScope) node.variables.each(add_def);
|
||||||
if (node instanceof AST_SymbolCatch) add_def(node.definition());
|
|
||||||
}));
|
}));
|
||||||
return avoid;
|
return avoid;
|
||||||
|
|
||||||
@@ -520,8 +506,7 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
|
|||||||
var cname = 0;
|
var cname = 0;
|
||||||
this.globals.each(rename);
|
this.globals.each(rename);
|
||||||
this.walk(new TreeWalker(function(node) {
|
this.walk(new TreeWalker(function(node) {
|
||||||
if (node instanceof AST_Scope) node.variables.each(rename);
|
if (node instanceof AST_BlockScope) node.variables.each(rename);
|
||||||
if (node instanceof AST_SymbolCatch) rename(node.definition());
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function next_name() {
|
function next_name() {
|
||||||
|
|||||||
Reference in New Issue
Block a user