From 220dc95c0d212ad4f3b97fcd937c4370ae5345dd Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sun, 4 Oct 2020 23:56:52 +0100 Subject: [PATCH] clean up scope-related variables (#4179) --- lib/ast.js | 6 +++--- lib/compress.js | 4 ++-- lib/scope.js | 34 ++++++++++++++++++++++++---------- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/lib/ast.js b/lib/ast.js index ba4e6fde..08b4b65c 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -251,9 +251,10 @@ var AST_Block = DEFNODE("Block", "body", { }, }, 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", $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", functions: "[Object/S] like `variables`, but only lists function declarations", parent_scope: "[AST_Scope?/S] link to the parent scope", @@ -443,10 +444,9 @@ var AST_With = DEFNODE("With", "expression", { /* -----[ 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", $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", }, diff --git a/lib/compress.js b/lib/compress.js index 63645a5e..cb8b0caf 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -6778,7 +6778,7 @@ merge(Compressor.prototype, { if (self.args.length == 0) return make_node(AST_Function, self, { argnames: [], body: [] - }).init_scope_vars(exp.scope); + }).init_vars(exp.scope); if (all(self.args, function(x) { return x instanceof AST_String; })) { @@ -9071,7 +9071,7 @@ merge(Compressor.prototype, { self.expression = make_node(AST_Function, self.expression, { argnames: [], body: [] - }).init_scope_vars(exp.scope); + }).init_vars(exp.scope); break; case "Number": self.expression = make_node(AST_Number, self.expression, { diff --git a/lib/scope.js b/lib/scope.js index 86fd3957..209ac235 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -127,7 +127,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) { return true; } 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; } if (node instanceof AST_Symbol) { @@ -154,7 +159,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) { } function walk_scope(descend) { - node.init_scope_vars(scope); + node.init_vars(scope); var save_defun = defun; var save_scope = scope; if (node instanceof AST_Scope) defun = node; @@ -197,9 +202,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) { if (name == "eval") { var parent = tw.parent(); 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; - } + } while (s = s.parent_scope); } else if (sym.undeclared) { 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.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.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) 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); }); - -AST_Lambda.DEFMETHOD("init_scope_vars", function(parent_scope) { +AST_Lambda.DEFMETHOD("init_vars", function(parent_scope) { init_scope_vars(this, parent_scope); this.uses_arguments = false; this.def_variable(new AST_SymbolFunarg({ -- 2.34.1