We incorrectly throw a syntax error when declaring a top level for-loop iteration...
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 22 May 2017 05:47:33 +0000 (05:47 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 22 May 2017 05:47:33 +0000 (05:47 +0000)
https://bugs.webkit.org/show_bug.cgi?id=171041
<rdar://problem/32082516>

Reviewed by Yusuke Suzuki.

JSTests:

* stress/lexical-scoping-for-loop.js: Added.
(assert):
(test1):
(test2):
(test3):
(test4):
(test5):
(test6):
(let.test7):
(let.test8):
(let.test9):
(let.test10):
(let.test11):
(let.test12):

Source/JavaScriptCore:

We were treating a for-loop variable declaration potentially as a top
level statement, e.g, in a program like this:
```
function foo() {
    for (let variable of expr) { }
}
```
But we should not be. This had the consequence of making this type of program
throw a syntax error:
```
function foo(arg) {
    for (let arg of expr) { }
}
```
even though it should not. The fix is simple, we just need to increment the
statement depth before parsing anything inside the for loop.

* parser/Parser.cpp:
(JSC::Parser<LexerType>::parseForStatement):

LayoutTests:

* js/parser-syntax-check-expected.txt:
* js/script-tests/parser-syntax-check.js:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@217200 268f45cc-cd09-0410-ab3c-d52691b4dbfc

JSTests/ChangeLog
JSTests/stress/lexical-scoping-for-loop.js [new file with mode: 0644]
LayoutTests/ChangeLog
LayoutTests/js/parser-syntax-check-expected.txt
LayoutTests/js/script-tests/parser-syntax-check.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/parser/Parser.cpp

index 1fee493..9dffa51 100644 (file)
@@ -1,3 +1,26 @@
+2017-05-21  Saam Barati  <sbarati@apple.com>
+
+        We incorrectly throw a syntax error when declaring a top level for-loop iteration variable the same as a parameter
+        https://bugs.webkit.org/show_bug.cgi?id=171041
+        <rdar://problem/32082516>
+
+        Reviewed by Yusuke Suzuki.
+
+        * stress/lexical-scoping-for-loop.js: Added.
+        (assert):
+        (test1):
+        (test2):
+        (test3):
+        (test4):
+        (test5):
+        (test6):
+        (let.test7):
+        (let.test8):
+        (let.test9):
+        (let.test10):
+        (let.test11):
+        (let.test12):
+
 2017-05-19  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [JSC] Make get_by_val & string "499" to number 499
diff --git a/JSTests/stress/lexical-scoping-for-loop.js b/JSTests/stress/lexical-scoping-for-loop.js
new file mode 100644 (file)
index 0000000..1e61e31
--- /dev/null
@@ -0,0 +1,61 @@
+'use strict';
+
+function assert(b) {
+    if (!b)
+        throw new Error("Bad");
+}
+
+function test1(x) {
+    for (let x = 20; x < 30; ++x) { }
+    return x;
+}
+function test2(x) {
+    for (let x of [1,2,3]) { }
+    return x;
+}
+function test3(x) {
+    for (let x in {}) { }
+    return x;
+}
+function test4(x) {
+    let i = 0;
+    for (const x = 20; i < 1; ++i) { }
+    return x;
+}
+function test5(x) {
+    for (const x of [1, 2, 3]) { }
+    return x;
+}
+function test6(x) {
+    for (const x in {}) { }
+    return x;
+}
+
+let test7 = (x) => {
+    for (let x = 20; x < 30; ++x) { }
+    return x;
+}
+let test8 = (x) => {
+    for (let x of [1,2,3]) { }
+    return x;
+}
+let test9 = (x) => {
+    for (let x in {}) { }
+    return x;
+}
+let test10 = (x) => {
+    let i = 0;
+    for (const x = 20; i < 1; ++i) { }
+    return x;
+}
+let test11 = (x) => {
+    for (const x of [1, 2, 3]) { }
+    return x;
+}
+let test12 = (x) => {
+    for (const x in {}) { }
+    return x;
+}
+
+for (let test of [test1, test2, test3, test4, test5, test7, test8, test9, test10, test11, test12])
+    assert(test("foo") === "foo");
index e241ec6..0212281 100644 (file)
@@ -1,3 +1,14 @@
+2017-05-21  Saam Barati  <sbarati@apple.com>
+
+        We incorrectly throw a syntax error when declaring a top level for-loop iteration variable the same as a parameter
+        https://bugs.webkit.org/show_bug.cgi?id=171041
+        <rdar://problem/32082516>
+
+        Reviewed by Yusuke Suzuki.
+
+        * js/parser-syntax-check-expected.txt:
+        * js/script-tests/parser-syntax-check.js:
+
 2017-05-21  Antti Koivisto  <antti@apple.com>
 
         matchMedia('print').addListener() fires in WK1 but never in WK2 when printing (breaks printing Google maps, QuickLooks)
index 0cb130e..f9d7dd5 100644 (file)
@@ -556,6 +556,54 @@ PASS Invalid: "for (const {i} = 20 in b) { }". Produced the following syntax err
 PASS Invalid: "function f() { for (const {i} = 20 in b) { } }". Produced the following syntax error: "SyntaxError: Cannot assign to the loop variable inside a for-in loop header."
 PASS Invalid: "for (let {i} = 20 in b) { }". Produced the following syntax error: "SyntaxError: Cannot assign to the loop variable inside a for-in loop header."
 PASS Invalid: "function f() { for (let {i} = 20 in b) { } }". Produced the following syntax error: "SyntaxError: Cannot assign to the loop variable inside a for-in loop header."
+PASS Valid:   "function x(i) { for (let i in {}) { } }"
+PASS Valid:   "function f() { function x(i) { for (let i in {}) { } } }"
+PASS Valid:   "function x(i) { for (let i of []) { } }"
+PASS Valid:   "function f() { function x(i) { for (let i of []) { } } }"
+PASS Valid:   "function x(i) { for (let i of []) { } }"
+PASS Valid:   "function f() { function x(i) { for (let i of []) { } } }"
+PASS Valid:   "function x(i) { for (let i; false; ) { } }"
+PASS Valid:   "function f() { function x(i) { for (let i; false; ) { } } }"
+PASS Valid:   "let f = (i) => { for (let i in {}) { } }"
+PASS Valid:   "function f() { let f = (i) => { for (let i in {}) { } } }"
+PASS Valid:   "let f = (i) => { for (let i of []) { } }"
+PASS Valid:   "function f() { let f = (i) => { for (let i of []) { } } }"
+PASS Valid:   "let f = (i) => { for (let i of []) { } }"
+PASS Valid:   "function f() { let f = (i) => { for (let i of []) { } } }"
+PASS Valid:   "let f = (i) => { for (let i; false; ) { } }"
+PASS Valid:   "function f() { let f = (i) => { for (let i; false; ) { } } }"
+PASS Valid:   "function* x(i) { for (let i in {}) { } }"
+PASS Valid:   "function f() { function* x(i) { for (let i in {}) { } } }"
+PASS Valid:   "function* x(i) { for (let i of []) { } }"
+PASS Valid:   "function f() { function* x(i) { for (let i of []) { } } }"
+PASS Valid:   "function* x(i) { for (let i of []) { } }"
+PASS Valid:   "function f() { function* x(i) { for (let i of []) { } } }"
+PASS Valid:   "function* x(i) { for (let i; false; ) { } }"
+PASS Valid:   "function f() { function* x(i) { for (let i; false; ) { } } }"
+PASS Valid:   "function x(i) { for (const i in {}) { } }"
+PASS Valid:   "function f() { function x(i) { for (const i in {}) { } } }"
+PASS Valid:   "function x(i) { for (const i of []) { } }"
+PASS Valid:   "function f() { function x(i) { for (const i of []) { } } }"
+PASS Valid:   "function x(i) { for (const i of []) { } }"
+PASS Valid:   "function f() { function x(i) { for (const i of []) { } } }"
+PASS Valid:   "function x(i) { for (const i = 20; false; ) { } }"
+PASS Valid:   "function f() { function x(i) { for (const i = 20; false; ) { } } }"
+PASS Valid:   "let f = (i) => { for (const i in {}) { } }"
+PASS Valid:   "function f() { let f = (i) => { for (const i in {}) { } } }"
+PASS Valid:   "let f = (i) => { for (const i of []) { } }"
+PASS Valid:   "function f() { let f = (i) => { for (const i of []) { } } }"
+PASS Valid:   "let f = (i) => { for (const i of []) { } }"
+PASS Valid:   "function f() { let f = (i) => { for (const i of []) { } } }"
+PASS Valid:   "let f = (i) => { for (const i = 20; false; ) { } }"
+PASS Valid:   "function f() { let f = (i) => { for (const i = 20; false; ) { } } }"
+PASS Valid:   "function* x(i) { for (const i in {}) { } }"
+PASS Valid:   "function f() { function* x(i) { for (const i in {}) { } } }"
+PASS Valid:   "function* x(i) { for (const i of []) { } }"
+PASS Valid:   "function f() { function* x(i) { for (const i of []) { } } }"
+PASS Valid:   "function* x(i) { for (const i of []) { } }"
+PASS Valid:   "function f() { function* x(i) { for (const i of []) { } } }"
+PASS Valid:   "function* x(i) { for (const i = 20; false; ) { } }"
+PASS Valid:   "function f() { function* x(i) { for (const i = 20; false; ) { } } }"
 try statement
 PASS Invalid: "try { break } catch(e) {}". Produced the following syntax error: "SyntaxError: 'break' is only valid inside a switch or loop statement."
 PASS Invalid: "function f() { try { break } catch(e) {} }". Produced the following syntax error: "SyntaxError: 'break' is only valid inside a switch or loop statement."
index ec27fc2..a3103a3 100644 (file)
@@ -371,6 +371,30 @@ invalid("for (let i = 20 in b) { }");
 invalid("for (const i = 20 in b) { }");
 invalid("for (const {i} = 20 in b) { }");
 invalid("for (let {i} = 20 in b) { }");
+valid("function x(i) { for (let i in {}) { } }");
+valid("function x(i) { for (let i of []) { } }");
+valid("function x(i) { for (let i of []) { } }");
+valid("function x(i) { for (let i; false; ) { } }");
+valid("let f = (i) => { for (let i in {}) { } }");
+valid("let f = (i) => { for (let i of []) { } }");
+valid("let f = (i) => { for (let i of []) { } }");
+valid("let f = (i) => { for (let i; false; ) { } }");
+valid("function* x(i) { for (let i in {}) { } }");
+valid("function* x(i) { for (let i of []) { } }");
+valid("function* x(i) { for (let i of []) { } }");
+valid("function* x(i) { for (let i; false; ) { } }");
+valid("function x(i) { for (const i in {}) { } }");
+valid("function x(i) { for (const i of []) { } }");
+valid("function x(i) { for (const i of []) { } }");
+valid("function x(i) { for (const i = 20; false; ) { } }");
+valid("let f = (i) => { for (const i in {}) { } }");
+valid("let f = (i) => { for (const i of []) { } }");
+valid("let f = (i) => { for (const i of []) { } }");
+valid("let f = (i) => { for (const i = 20; false; ) { } }");
+valid("function* x(i) { for (const i in {}) { } }");
+valid("function* x(i) { for (const i of []) { } }");
+valid("function* x(i) { for (const i of []) { } }");
+valid("function* x(i) { for (const i = 20; false; ) { } }");
 
 debug  ("try statement");
 
index aa475c2..1670920 100644 (file)
@@ -1,3 +1,31 @@
+2017-05-21  Saam Barati  <sbarati@apple.com>
+
+        We incorrectly throw a syntax error when declaring a top level for-loop iteration variable the same as a parameter
+        https://bugs.webkit.org/show_bug.cgi?id=171041
+        <rdar://problem/32082516>
+
+        Reviewed by Yusuke Suzuki.
+
+        We were treating a for-loop variable declaration potentially as a top
+        level statement, e.g, in a program like this:
+        ```
+        function foo() {
+            for (let variable of expr) { }
+        }
+        ```
+        But we should not be. This had the consequence of making this type of program
+        throw a syntax error:
+        ```
+        function foo(arg) {
+            for (let arg of expr) { }
+        }
+        ```
+        even though it should not. The fix is simple, we just need to increment the
+        statement depth before parsing anything inside the for loop.
+
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::parseForStatement):
+
 2017-05-19  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [JSC] Make get_by_val & string "499" to number 499
index 86647d9..9b45da7 100644 (file)
@@ -1133,6 +1133,10 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseForStatement(
     JSTokenLocation location(tokenLocation());
     int startLine = tokenLine();
     next();
+
+    DepthManager statementDepth(&m_statementDepth);
+    m_statementDepth++;
+
     handleProductionOrFail(OPENPAREN, "(", "start", "for-loop header");
     int nonLHSCount = m_parserState.nonLHSCount;
     int declarations = 0;