Build label def/refs info when figuring out scope
authorMihai Bazon <mihai.bazon@gmail.com>
Thu, 12 Nov 2015 09:48:06 +0000 (11:48 +0200)
committerMihai Bazon <mihai.bazon@gmail.com>
Thu, 12 Nov 2015 09:48:06 +0000 (11:48 +0200)
Fix #862

lib/scope.js

index 4a3739c..1f0986c 100644 (file)
@@ -92,6 +92,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
     // pass 1: setup scope chaining and handle definitions
     var self = this;
     var scope = self.parent_scope = null;
+    var labels = new Dictionary();
     var defun = null;
     var nesting = 0;
     var tw = new TreeWalker(function(node, descend){
@@ -108,12 +109,25 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
             node.init_scope_vars(nesting);
             var save_scope = node.parent_scope = scope;
             var save_defun = defun;
+            var save_labels = labels;
             defun = scope = node;
+            labels = new Dictionary();
             ++nesting; descend(); --nesting;
             scope = save_scope;
             defun = save_defun;
+            labels = save_labels;
             return true;        // don't descend again in TreeWalker
         }
+        if (node instanceof AST_LabeledStatement) {
+            var l = node.label;
+            if (labels.has(l.name)) {
+                throw new Error(string_template("Label {name} defined twice", l));
+            }
+            labels.set(l.name, l);
+            descend();
+            labels.del(l.name);
+            return true;        // no descend again
+        }
         if (node instanceof AST_With) {
             for (var s = scope; s; s = s.parent_scope)
                 s.uses_with = true;
@@ -122,6 +136,10 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
         if (node instanceof AST_Symbol) {
             node.scope = scope;
         }
+        if (node instanceof AST_Label) {
+            node.thedef = node;
+            node.references = [];
+        }
         if (node instanceof AST_SymbolLambda) {
             defun.def_function(node);
         }
@@ -143,6 +161,15 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
             (options.screw_ie8 ? scope : defun)
                 .def_variable(node);
         }
+        else if (node instanceof AST_LabelRef) {
+            var sym = labels.get(node.name);
+            if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
+                name: node.name,
+                line: node.start.line,
+                col: node.start.col
+            }));
+            node.thedef = sym;
+        }
     });
     self.walk(tw);
 
@@ -157,6 +184,10 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
             func = prev_func;
             return true;
         }
+        if (node instanceof AST_LoopControl && node.label) {
+            node.label.thedef.references.push(node);
+            return true;
+        }
         if (node instanceof AST_SymbolRef) {
             var name = node.name;
             var sym = node.scope.find_variable(name);