clean up scope-related variables (#4179)

This commit is contained in:
Alex Lam S.L
2020-10-04 23:56:52 +01:00
committed by GitHub
parent 8f0521d51d
commit 220dc95c0d
3 changed files with 29 additions and 15 deletions

View File

@@ -251,9 +251,10 @@ var AST_Block = DEFNODE("Block", "body", {
}, },
}, AST_Statement); }, AST_Statement);
var AST_BlockScope = DEFNODE("BlockScope", "enclosed functions make_def parent_scope variables", { var AST_BlockScope = DEFNODE("BlockScope", "cname 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",
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", parent_scope: "[AST_Scope?/S] link to the parent scope",
@@ -443,10 +444,9 @@ var AST_With = DEFNODE("With", "expression", {
/* -----[ scope and functions ]----- */ /* -----[ scope and functions ]----- */
var AST_Scope = DEFNODE("Scope", "cname uses_eval uses_with", { var AST_Scope = DEFNODE("Scope", "uses_eval uses_with", {
$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)",
uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`", 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", uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
}, },

View File

@@ -6778,7 +6778,7 @@ merge(Compressor.prototype, {
if (self.args.length == 0) return make_node(AST_Function, self, { if (self.args.length == 0) return make_node(AST_Function, self, {
argnames: [], argnames: [],
body: [] body: []
}).init_scope_vars(exp.scope); }).init_vars(exp.scope);
if (all(self.args, function(x) { if (all(self.args, function(x) {
return x instanceof AST_String; return x instanceof AST_String;
})) { })) {
@@ -9071,7 +9071,7 @@ merge(Compressor.prototype, {
self.expression = make_node(AST_Function, self.expression, { self.expression = make_node(AST_Function, self.expression, {
argnames: [], argnames: [],
body: [] body: []
}).init_scope_vars(exp.scope); }).init_vars(exp.scope);
break; break;
case "Number": case "Number":
self.expression = make_node(AST_Number, self.expression, { self.expression = make_node(AST_Number, self.expression, {

View File

@@ -127,7 +127,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
return true; return true;
} }
if (node instanceof AST_With) { if (node instanceof AST_With) {
for (var s = scope; s; s = s.parent_scope) s.uses_with = true; var s = scope;
do {
s = s.resolve();
if (s.uses_with) break;
s.uses_with = true;
} while (s = s.parent_scope);
return; return;
} }
if (node instanceof AST_Symbol) { if (node instanceof AST_Symbol) {
@@ -154,7 +159,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
} }
function walk_scope(descend) { function walk_scope(descend) {
node.init_scope_vars(scope); node.init_vars(scope);
var save_defun = defun; var save_defun = defun;
var save_scope = scope; var save_scope = scope;
if (node instanceof AST_Scope) defun = node; if (node instanceof AST_Scope) defun = node;
@@ -197,9 +202,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
if (name == "eval") { if (name == "eval") {
var parent = tw.parent(); var parent = tw.parent();
if (parent.TYPE == "Call" && parent.expression === node) { if (parent.TYPE == "Call" && parent.expression === node) {
for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) { var s = node.scope;
do {
s = s.resolve();
if (s.uses_eval) break;
s.uses_eval = true; s.uses_eval = true;
} } while (s = s.parent_scope);
} else if (sym.undeclared) { } else if (sym.undeclared) {
self.uses_eval = true; self.uses_eval = true;
} }
@@ -281,22 +289,28 @@ AST_Toplevel.DEFMETHOD("def_global", function(node) {
} }
}); });
function init_scope_vars(scope, parent) { function init_block_vars(scope, parent) {
scope.cname = -1; // the current index for mangling functions/variables scope.cname = -1; // the current index for mangling functions/variables
scope.enclosed = []; // variables from this or outer scope(s) that are referenced from this or inner scopes scope.enclosed = []; // variables from this or outer scope(s) that are referenced from this or inner scopes
scope.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`
scope.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
scope.parent_scope = parent; // the parent scope (null if this is the top level) scope.parent_scope = parent; // the parent scope (null if this is the top level)
scope.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope) scope.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
scope.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions) scope.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
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_BlockScope.DEFMETHOD("init_scope_vars", function(parent_scope) { function init_scope_vars(scope, parent) {
init_block_vars(scope, parent);
scope.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`
scope.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
}
AST_BlockScope.DEFMETHOD("init_vars", function(parent_scope) {
init_block_vars(this, parent_scope);
});
AST_Scope.DEFMETHOD("init_vars", function(parent_scope) {
init_scope_vars(this, parent_scope); init_scope_vars(this, parent_scope);
}); });
AST_Lambda.DEFMETHOD("init_vars", function(parent_scope) {
AST_Lambda.DEFMETHOD("init_scope_vars", function(parent_scope) {
init_scope_vars(this, parent_scope); init_scope_vars(this, parent_scope);
this.uses_arguments = false; this.uses_arguments = false;
this.def_variable(new AST_SymbolFunarg({ this.def_variable(new AST_SymbolFunarg({