Browse Source

Fixed incorrect handling of continue in for-in loops.

Dmitry Panov 3 years ago
parent
commit
5ea1285e6c
2 changed files with 57 additions and 1 deletions
  1. 4 1
      compiler_stmt.go
  2. 53 0
      compiler_test.go

+ 4 - 1
compiler_stmt.go

@@ -624,11 +624,14 @@ func (c *compiler) emitBlockExitCode(label *ast.Identifier, idx file.Idx, isBrea
 		c.throwSyntaxError(int(idx)-1, "Could not find block")
 		c.throwSyntaxError(int(idx)-1, "Could not find block")
 		panic("unreachable")
 		panic("unreachable")
 	}
 	}
+	contForLoop := !isBreak && block.typ == blockLoop
 L:
 L:
 	for b := c.block; b != block; b = b.outer {
 	for b := c.block; b != block; b = b.outer {
 		switch b.typ {
 		switch b.typ {
 		case blockIterScope:
 		case blockIterScope:
-			if !isBreak && b.outer == block {
+			// blockIterScope in 'for' loops is shared across iterations, so
+			// continue should not pop it.
+			if contForLoop && b.outer == block {
 				break L
 				break L
 			}
 			}
 			fallthrough
 			fallthrough

+ 53 - 0
compiler_test.go

@@ -5548,6 +5548,59 @@ func TestThisResolutionWithStackVar(t *testing.T) {
 	testScript(SCRIPT, valueTrue, t)
 	testScript(SCRIPT, valueTrue, t)
 }
 }
 
 
+func TestForInLoopContinue(t *testing.T) {
+	const SCRIPT = `
+	var globalSink;
+	(function() {
+	    const data = [{disabled: true}, {}];
+		function dummy() {}
+	    function f1() {}
+
+	    function f() {
+			dummy(); // move dummy to stash (so that f1 is at index 1)
+	        for (const d of data) {
+	            if (d.disabled) continue;
+	            globalSink = () => d; // move d to stash
+	            f1();
+	        }
+	    }
+
+	    f();
+	})();
+	`
+	testScript(SCRIPT, _undefined, t)
+}
+
+func TestForInLoopContinueOuter(t *testing.T) {
+	const SCRIPT = `
+	var globalSink;
+	(function() {
+	    const data = [{disabled: true}, {}];
+		function dummy1() {}
+	    function f1() {}
+
+	    function f() {
+			dummy1();
+			let counter = 0;
+			OUTER: for (let i = 0; i < 1; i++) {
+		        for (const d of data) {
+		            if (d.disabled) continue OUTER;
+		            globalSink = () => d;
+		        }
+				counter++;
+			}
+			f1();
+			if (counter !== 0) {
+				throw new Error(counter);
+			}
+	    }
+
+	    f();
+	})();
+	`
+	testScript(SCRIPT, _undefined, t)
+}
+
 /*
 /*
 func TestBabel(t *testing.T) {
 func TestBabel(t *testing.T) {
 	src, err := os.ReadFile("babel7.js")
 	src, err := os.ReadFile("babel7.js")