clean up scope-related variables (#4179)
authorAlex Lam S.L <alexlamsl@gmail.com>
Sun, 4 Oct 2020 22:56:52 +0000 (23:56 +0100)
committerGitHub <noreply@github.com>
Sun, 4 Oct 2020 22:56:52 +0000 (06:56 +0800)
lib/ast.js
lib/compress.js
lib/scope.js

index ba4e6fd..08b4b65 100644 (file)
@@ -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",
     },
index 63645a5..cb8b0ca 100644 (file)
@@ -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, {
index 86fd395..209ac23 100644 (file)
@@ -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({