Class body ending with a semicolon throws a SyntaxError
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 27 Apr 2015 04:20:32 +0000 (04:20 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 27 Apr 2015 04:20:32 +0000 (04:20 +0000)
https://bugs.webkit.org/show_bug.cgi?id=144244

Reviewed by Darin Adler.

Source/JavaScriptCore:

The bug was caused by parseClass's inner loop for method definitions not moving onto the next iteration
it encounters a semicolon. As a result, we always expected a method to appear after a semicolon. Fixed
it by continue'ing when it encounters a semicolon.

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

LayoutTests:

Added a regression test for having a semicolon inside the class definition.

* js/class-syntax-semicolon-expected.txt: Added.
* js/class-syntax-semicolon.html: Added.
* js/script-tests/class-syntax-semicolon.js: Added.

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

LayoutTests/ChangeLog
LayoutTests/js/class-syntax-semicolon-expected.txt [new file with mode: 0644]
LayoutTests/js/class-syntax-semicolon.html [new file with mode: 0644]
LayoutTests/js/script-tests/class-syntax-semicolon.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/parser/Parser.cpp

index 3d1148f018d56e882ec7c57f49fb9580783745b0..e792bbf5c176f026336fc0f1f5bb809c0278c451 100644 (file)
@@ -1,3 +1,16 @@
+2015-04-26  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Class body ending with a semicolon throws a SyntaxError
+        https://bugs.webkit.org/show_bug.cgi?id=144244
+
+        Reviewed by Darin Adler.
+
+        Added a regression test for having a semicolon inside the class definition.
+
+        * js/class-syntax-semicolon-expected.txt: Added.
+        * js/class-syntax-semicolon.html: Added.
+        * js/script-tests/class-syntax-semicolon.js: Added.
+
 2015-04-26  Ryosuke Niwa  <rniwa@webkit.org>
 
         Getter or setter method named "prototype" or "constrcutor" should throw SyntaxError
diff --git a/LayoutTests/js/class-syntax-semicolon-expected.txt b/LayoutTests/js/class-syntax-semicolon-expected.txt
new file mode 100644 (file)
index 0000000..8715474
--- /dev/null
@@ -0,0 +1,66 @@
+Tests for ES6 class syntax containing semicolon in the class body
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS class A { foo;() { } } threw exception SyntaxError: Unexpected token ';'. Expected an opening '(' before a method's parameter list..
+PASS class A { foo() ; { } } threw exception SyntaxError: Unexpected token ';'. Expected an opening '{' at the start of a method body..
+PASS class A { get ; foo() { } } threw exception SyntaxError: Unexpected token ';'.
+PASS class A { get foo;() { } } threw exception SyntaxError: Unexpected token ';'. Expected a parameter list for getter definition..
+PASS class A { get foo() ; { } } threw exception SyntaxError: Unexpected token ';'. Expected an opening '{' at the start of a getter body..
+PASS class A { set ; foo(x) { } } threw exception SyntaxError: Unexpected token ';'.
+PASS class A { set foo;(x) { } } threw exception SyntaxError: Unexpected token ';'. Expected a parameter list for setter definition..
+PASS class A { set foo(x) ; { } } threw exception SyntaxError: Unexpected token ';'. Expected an opening '{' at the start of a setter body..
+PASS class A { ; } did not throw exception.
+PASS class A { foo() { } ; } did not throw exception.
+PASS class A { get foo() { } ; } did not throw exception.
+PASS class A { set foo(x) { } ; } did not throw exception.
+PASS class A { static foo() { } ; } did not throw exception.
+PASS class A { static get foo() { } ; } did not throw exception.
+PASS class A { static set foo(x) { } ; } did not throw exception.
+PASS class A { ; foo() { } } did not throw exception.
+PASS class A { ; get foo() { } } did not throw exception.
+PASS class A { ; set foo(x) { } } did not throw exception.
+PASS class A { ; static foo() { } } did not throw exception.
+PASS class A { ; static get foo() { } } did not throw exception.
+PASS class A { ; static set foo(x) { } } did not throw exception.
+PASS class A { foo() { } ; foo() {} } did not throw exception.
+PASS class A { foo() { } ; get foo() {} } did not throw exception.
+PASS class A { foo() { } ; set foo(x) {} } did not throw exception.
+PASS class A { foo() { } ; static foo() {} } did not throw exception.
+PASS class A { foo() { } ; static get foo() {} } did not throw exception.
+PASS class A { foo() { } ; static set foo(x) {} } did not throw exception.
+PASS class A { get foo() { } ; foo() {} } did not throw exception.
+PASS class A { get foo() { } ; get foo() {} } did not throw exception.
+PASS class A { get foo() { } ; set foo(x) {} } did not throw exception.
+PASS class A { get foo() { } ; static foo() {} } did not throw exception.
+PASS class A { get foo() { } ; static get foo() {} } did not throw exception.
+PASS class A { get foo() { } ; static set foo(x) {} } did not throw exception.
+PASS class A { set foo(x) { } ; foo() {} } did not throw exception.
+PASS class A { set foo(x) { } ; get foo() {} } did not throw exception.
+PASS class A { set foo(x) { } ; set foo(x) {} } did not throw exception.
+PASS class A { set foo(x) { } ; static foo() {} } did not throw exception.
+PASS class A { set foo(x) { } ; static get foo() {} } did not throw exception.
+PASS class A { set foo(x) { } ; static set foo(x) {} } did not throw exception.
+PASS class A { static foo() { } ; foo() {} } did not throw exception.
+PASS class A { static foo() { } ; get foo() {} } did not throw exception.
+PASS class A { static foo() { } ; set foo(x) {} } did not throw exception.
+PASS class A { static foo() { } ; static foo() {} } did not throw exception.
+PASS class A { static foo() { } ; static get foo() {} } did not throw exception.
+PASS class A { static foo() { } ; static set foo(x) {} } did not throw exception.
+PASS class A { static get foo() { } ; foo() {} } did not throw exception.
+PASS class A { static get foo() { } ; get foo() {} } did not throw exception.
+PASS class A { static get foo() { } ; set foo(x) {} } did not throw exception.
+PASS class A { static get foo() { } ; static foo() {} } did not throw exception.
+PASS class A { static get foo() { } ; static get foo() {} } did not throw exception.
+PASS class A { static get foo() { } ; static set foo(x) {} } did not throw exception.
+PASS class A { static set foo(x) { } ; foo() {} } did not throw exception.
+PASS class A { static set foo(x) { } ; get foo() {} } did not throw exception.
+PASS class A { static set foo(x) { } ; set foo(x) {} } did not throw exception.
+PASS class A { static set foo(x) { } ; static foo() {} } did not throw exception.
+PASS class A { static set foo(x) { } ; static get foo() {} } did not throw exception.
+PASS class A { static set foo(x) { } ; static set foo(x) {} } did not throw exception.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/class-syntax-semicolon.html b/LayoutTests/js/class-syntax-semicolon.html
new file mode 100644 (file)
index 0000000..b4d0e2d
--- /dev/null
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../resources/js-test-pre.js"></script>
+<script src="script-tests/class-syntax-semicolon.js"></script>
+<script src="../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/script-tests/class-syntax-semicolon.js b/LayoutTests/js/script-tests/class-syntax-semicolon.js
new file mode 100644 (file)
index 0000000..6123cf6
--- /dev/null
@@ -0,0 +1,70 @@
+
+description('Tests for ES6 class syntax containing semicolon in the class body');
+
+shouldThrow("class A { foo;() { } }", "'SyntaxError: Unexpected token \\';\\'. Expected an opening \\'(\\' before a method\\'s parameter list.'");
+shouldThrow("class A { foo() ; { } }", "'SyntaxError: Unexpected token \\\';\\'. Expected an opening \\'{\\' at the start of a method body.'");
+shouldThrow("class A { get ; foo() { } }", "'SyntaxError: Unexpected token \\';\\''");
+shouldThrow("class A { get foo;() { } }", "'SyntaxError: Unexpected token \\\';\\'. Expected a parameter list for getter definition.'");
+shouldThrow("class A { get foo() ; { } }", "'SyntaxError: Unexpected token \\\';\\'. Expected an opening \\'{\\' at the start of a getter body.'");
+shouldThrow("class A { set ; foo(x) { } }", "'SyntaxError: Unexpected token \\';\\''");
+shouldThrow("class A { set foo;(x) { } }", "'SyntaxError: Unexpected token \\\';\\'. Expected a parameter list for setter definition.'");
+shouldThrow("class A { set foo(x) ; { } }", "'SyntaxError: Unexpected token \\\';\\'. Expected an opening \\'{\\' at the start of a setter body.'");
+
+shouldNotThrow("class A { ; }");
+shouldNotThrow("class A { foo() { } ; }");
+shouldNotThrow("class A { get foo() { } ; }");
+shouldNotThrow("class A { set foo(x) { } ; }");
+shouldNotThrow("class A { static foo() { } ; }");
+shouldNotThrow("class A { static get foo() { } ; }");
+shouldNotThrow("class A { static set foo(x) { } ; }");
+
+shouldNotThrow("class A { ; foo() { } }");
+shouldNotThrow("class A { ; get foo() { } }");
+shouldNotThrow("class A { ; set foo(x) { } }");
+shouldNotThrow("class A { ; static foo() { } }");
+shouldNotThrow("class A { ; static get foo() { } }");
+shouldNotThrow("class A { ; static set foo(x) { } }");
+
+shouldNotThrow("class A { foo() { } ; foo() {} }");
+shouldNotThrow("class A { foo() { } ; get foo() {} }");
+shouldNotThrow("class A { foo() { } ; set foo(x) {} }");
+shouldNotThrow("class A { foo() { } ; static foo() {} }");
+shouldNotThrow("class A { foo() { } ; static get foo() {} }");
+shouldNotThrow("class A { foo() { } ; static set foo(x) {} }");
+
+shouldNotThrow("class A { get foo() { } ; foo() {} }");
+shouldNotThrow("class A { get foo() { } ; get foo() {} }");
+shouldNotThrow("class A { get foo() { } ; set foo(x) {} }");
+shouldNotThrow("class A { get foo() { } ; static foo() {} }");
+shouldNotThrow("class A { get foo() { } ; static get foo() {} }");
+shouldNotThrow("class A { get foo() { } ; static set foo(x) {} }");
+
+shouldNotThrow("class A { set foo(x) { } ; foo() {} }");
+shouldNotThrow("class A { set foo(x) { } ; get foo() {} }");
+shouldNotThrow("class A { set foo(x) { } ; set foo(x) {} }");
+shouldNotThrow("class A { set foo(x) { } ; static foo() {} }");
+shouldNotThrow("class A { set foo(x) { } ; static get foo() {} }");
+shouldNotThrow("class A { set foo(x) { } ; static set foo(x) {} }");
+
+shouldNotThrow("class A { static foo() { } ; foo() {} }");
+shouldNotThrow("class A { static foo() { } ; get foo() {} }");
+shouldNotThrow("class A { static foo() { } ; set foo(x) {} }");
+shouldNotThrow("class A { static foo() { } ; static foo() {} }");
+shouldNotThrow("class A { static foo() { } ; static get foo() {} }");
+shouldNotThrow("class A { static foo() { } ; static set foo(x) {} }");
+
+shouldNotThrow("class A { static get foo() { } ; foo() {} }");
+shouldNotThrow("class A { static get foo() { } ; get foo() {} }");
+shouldNotThrow("class A { static get foo() { } ; set foo(x) {} }");
+shouldNotThrow("class A { static get foo() { } ; static foo() {} }");
+shouldNotThrow("class A { static get foo() { } ; static get foo() {} }");
+shouldNotThrow("class A { static get foo() { } ; static set foo(x) {} }");
+
+shouldNotThrow("class A { static set foo(x) { } ; foo() {} }");
+shouldNotThrow("class A { static set foo(x) { } ; get foo() {} }");
+shouldNotThrow("class A { static set foo(x) { } ; set foo(x) {} }");
+shouldNotThrow("class A { static set foo(x) { } ; static foo() {} }");
+shouldNotThrow("class A { static set foo(x) { } ; static get foo() {} }");
+shouldNotThrow("class A { static set foo(x) { } ; static set foo(x) {} }");
+
+var successfullyParsed = true;
index e6377eb8d139f9ecc2e168db5c0c65f31ace56b4..5f1bb2033ebf6756eb106efb4371b21450f5a8b8 100644 (file)
@@ -1,3 +1,17 @@
+2015-04-26  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Class body ending with a semicolon throws a SyntaxError
+        https://bugs.webkit.org/show_bug.cgi?id=144244
+
+        Reviewed by Darin Adler.
+
+        The bug was caused by parseClass's inner loop for method definitions not moving onto the next iteration
+        it encounters a semicolon. As a result, we always expected a method to appear after a semicolon. Fixed
+        it by continue'ing when it encounters a semicolon.
+
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::parseClass):
+
 2015-04-26  Ryosuke Niwa  <rniwa@webkit.org>
 
         Getter or setter method named "prototype" or "constrcutor" should throw SyntaxError
index 2163dbd8720c9fef8f290931474845dc2b51b125..c7088e71928cc2be5879609bb30908c1e5834559 100644 (file)
@@ -1537,8 +1537,10 @@ template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(T
     TreePropertyList instanceMethodsTail = 0;
     TreePropertyList staticMethodsTail = 0;
     while (!match(CLOSEBRACE)) {
-        if (match(SEMICOLON))
+        if (match(SEMICOLON)) {
             next();
+            continue;
+        }
 
         JSTokenLocation methodLocation(tokenLocation());
         unsigned methodStart = tokenStart();