Web Inspector: Test RuntimeAgent.parse, detecting if a script parse error is recoverable
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Sep 2015 19:48:54 +0000 (19:48 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Sep 2015 19:48:54 +0000 (19:48 +0000)
https://bugs.webkit.org/show_bug.cgi?id=148790

Patch by Joseph Pecoraro <pecoraro@apple.com> on 2015-09-04
Reviewed by Timothy Hatcher.

Source/JavaScriptCore:

* parser/Lexer.cpp:
(JSC::Lexer<T>::lex):
* parser/Parser.cpp:
(JSC::Parser<LexerType>::printUnexpectedTokenText):
* parser/ParserTokens.h:
More consistently name and treat unterminated numeric literals.

* parser/Parser.h:
(JSC::Parser<LexerType>::parse):
Treat multiline capable unterminated literals as recoverable.

LayoutTests:

* inspector/runtime/parse-expected.txt: Added.
* inspector/runtime/parse.html: Added.

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

LayoutTests/ChangeLog
LayoutTests/inspector/runtime/parse-expected.txt [new file with mode: 0644]
LayoutTests/inspector/runtime/parse.html [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/parser/Lexer.cpp
Source/JavaScriptCore/parser/Parser.cpp
Source/JavaScriptCore/parser/Parser.h
Source/JavaScriptCore/parser/ParserTokens.h

index 26aad84..dd6df3e 100644 (file)
@@ -1,3 +1,13 @@
+2015-09-04  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Test RuntimeAgent.parse, detecting if a script parse error is recoverable
+        https://bugs.webkit.org/show_bug.cgi?id=148790
+
+        Reviewed by Timothy Hatcher.
+
+        * inspector/runtime/parse-expected.txt: Added.
+        * inspector/runtime/parse.html: Added.
+
 2015-09-04  Tim Horton  <timothy_horton@apple.com>
 
         swipe-start-hysteresis-failures is flaky
diff --git a/LayoutTests/inspector/runtime/parse-expected.txt b/LayoutTests/inspector/runtime/parse-expected.txt
new file mode 100644 (file)
index 0000000..0fca402
--- /dev/null
@@ -0,0 +1,474 @@
+Tests for the Runtime.parse command.
+
+
+== Running test suite: Runtime.parse
+-- Running test case: SyntaxErrorType.None
+PASS: Should be SyntaxErrorType None.
+Source: 
+
+PASS: Should be SyntaxErrorType None.
+Source: ;
+
+PASS: Should be SyntaxErrorType None.
+Source: myVariable
+
+PASS: Should be SyntaxErrorType None.
+Source: var x = 1; x
+
+PASS: Should be SyntaxErrorType None.
+Source: [1,2,3]
+
+PASS: Should be SyntaxErrorType None.
+Source: ({})
+
+PASS: Should be SyntaxErrorType None.
+Source: ({prop:true}).x.x.x
+
+PASS: Should be SyntaxErrorType None.
+Source: if(1)1
+
+
+-- Running test case: SyntaxErrorType.UnterminatedLiteral
+PASS: Should be SyntaxErrorType UnterminatedLiteral.
+Source: 0b
+        ^
+Error Message: No binary digits after '0b'
+Range: {"startOffset":0,"endOffset":1}
+
+PASS: Should be SyntaxErrorType UnterminatedLiteral.
+Source: 0o
+        ^
+Error Message: No octal digits after '0o'
+Range: {"startOffset":0,"endOffset":1}
+
+PASS: Should be SyntaxErrorType UnterminatedLiteral.
+Source: 0x
+        ^
+Error Message: No hexadecimal digits after '0x'
+Range: {"startOffset":0,"endOffset":1}
+
+PASS: Should be SyntaxErrorType UnterminatedLiteral.
+Source: 1e
+        ^~
+Error Message: Non-number found after exponent indicator
+Range: {"startOffset":0,"endOffset":2}
+
+PASS: Should be SyntaxErrorType UnterminatedLiteral.
+Source: '
+        ^
+Error Message: Unexpected EOF
+Range: {"startOffset":0,"endOffset":1}
+
+PASS: Should be SyntaxErrorType UnterminatedLiteral.
+Source: "
+        ^
+Error Message: Unexpected EOF
+Range: {"startOffset":0,"endOffset":1}
+
+PASS: Should be SyntaxErrorType UnterminatedLiteral.
+Source: var \u007
+            ^~
+Error Message: Incomplete unicode escape in identifier: '\u'
+Range: {"startOffset":4,"endOffset":6}
+
+
+-- Running test case: SyntaxErrorType.Recoverable
+PASS: Should be SyntaxErrorType Recoverable.
+Source: (
+        ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":0,"endOffset":1}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: {
+        ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":0,"endOffset":1}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: !
+        ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":0,"endOffset":1}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: -
+        ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":0,"endOffset":1}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: +
+        ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":0,"endOffset":1}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: ~
+        ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":0,"endOffset":1}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: 1,
+         ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":1,"endOffset":2}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: var
+        ^~~
+Error Message: Unexpected end of script
+Error Range: {"startOffset":0,"endOffset":3}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: 'use strict'; let
+                      ^~~
+Error Message: Unexpected end of script
+Error Range: {"startOffset":14,"endOffset":17}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: const
+        ^~~~~
+Error Message: Unexpected end of script
+Error Range: {"startOffset":0,"endOffset":5}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: var {x
+             ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":5,"endOffset":6}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: var [x
+             ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":5,"endOffset":6}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: var x,
+             ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":5,"endOffset":6}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: if(1)
+            ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":4,"endOffset":5}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: if(1){
+             ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":5,"endOffset":6}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: if(1){}else
+               ^~~~
+Error Message: Unexpected end of script
+Error Range: {"startOffset":7,"endOffset":11}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: if(1){}else{
+                   ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":11,"endOffset":12}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: while(1)
+               ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":7,"endOffset":8}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: while(1){
+                ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":8,"endOffset":9}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: for(;;)
+              ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":6,"endOffset":7}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: for(;;){
+               ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":7,"endOffset":8}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: ()=>
+          ^~
+Error Message: Unexpected end of script
+Error Range: {"startOffset":2,"endOffset":4}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: ()=>{
+            ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":4,"endOffset":5}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: /*
+        ^~
+Error Message: Multiline comment was not closed properly
+Error Range: {"startOffset":0,"endOffset":2}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: `
+        ^
+Error Message: Unexpected EOF
+Error Range: {"startOffset":0,"endOffset":1}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: `${1}
+            ^
+Error Message: Unexpected EOF
+Error Range: {"startOffset":4,"endOffset":5}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: `${
+        ^~~
+Error Message: Unexpected end of script
+Error Range: {"startOffset":0,"endOffset":3}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: function
+        ^~~~~~~~
+Error Message: Unexpected end of script
+Error Range: {"startOffset":0,"endOffset":8}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: function foo
+                 ^~~
+Error Message: Unexpected end of script
+Error Range: {"startOffset":9,"endOffset":12}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: function foo(
+                    ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":12,"endOffset":13}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: function foo(){
+                      ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":14,"endOffset":15}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: (function() {
+                    ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":12,"endOffset":13}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: (function() {}
+                     ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":13,"endOffset":14}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: (function() {})(
+                       ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":15,"endOffset":16}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: switch(x)
+                ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":8,"endOffset":9}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: switch(x){
+                 ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":9,"endOffset":10}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: switch(x){case
+                  ^~~~
+Error Message: Unexpected end of script
+Error Range: {"startOffset":10,"endOffset":14}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: switch(x){case 1
+                       ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":15,"endOffset":16}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: switch(x){case 1:
+                        ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":16,"endOffset":17}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: class
+        ^~~~~
+Error Message: Unexpected end of script
+Error Range: {"startOffset":0,"endOffset":5}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: class Klass
+              ^~~~~
+Error Message: Unexpected end of script
+Error Range: {"startOffset":6,"endOffset":11}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: class Klass {
+                    ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":12,"endOffset":13}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: var x = {
+                ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":8,"endOffset":9}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: var x = {prop
+                 ^~~~
+Error Message: Unexpected end of script
+Error Range: {"startOffset":9,"endOffset":13}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: var x = {prop:
+                     ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":13,"endOffset":14}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: var x = {prop:123
+                      ^~~
+Error Message: Unexpected end of script
+Error Range: {"startOffset":14,"endOffset":17}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: var x = {prop:123 
+                      ^~~
+Error Message: Unexpected end of script
+Error Range: {"startOffset":14,"endOffset":17}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: var x = {prop:123,
+                         ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":17,"endOffset":18}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: var x = {prop:123, 
+                         ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":17,"endOffset":18}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: var x = {prop:123, 'prop2'
+                           ^~~~~~~
+Error Message: Unexpected end of script
+Error Range: {"startOffset":19,"endOffset":26}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: var x = {prop:123, [12
+                            ^~
+Error Message: Unexpected end of script
+Error Range: {"startOffset":20,"endOffset":22}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: var x = {prop:123, [12]
+                              ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":22,"endOffset":23}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: var x = {prop:123, [12]:
+                               ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":23,"endOffset":24}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: var x = {prop:123, [12]:567
+                                ^~~
+Error Message: Unexpected end of script
+Error Range: {"startOffset":24,"endOffset":27}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: var x = [
+                ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":8,"endOffset":9}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: var x = [1
+                 ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":9,"endOffset":10}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: var x = [1,
+                  ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":10,"endOffset":11}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: var x = [1,[]
+                    ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":12,"endOffset":13}
+
+PASS: Should be SyntaxErrorType Recoverable.
+Source: var x = [1,{}
+                    ^
+Error Message: Unexpected end of script
+Error Range: {"startOffset":12,"endOffset":13}
+
+
+-- Running test case: SyntaxErrorType.Irrecoverable
+PASS: Should be SyntaxErrorType Irrecoverable.
+Source: 1$
+        ^
+Error Message: No identifiers allowed directly after numeric literal
+Error Range: {"startOffset":0,"endOffset":1}
+
+PASS: Should be SyntaxErrorType Irrecoverable.
+Source: var x = {}}
+                  ^
+Error Message: Parser error
+Error Range: {"startOffset":10,"endOffset":11}
+
+PASS: Should be SyntaxErrorType Irrecoverable.
+Source: ({}))
+            ^
+Error Message: Unexpected token ')'. Parse error.
+Error Range: {"startOffset":4,"endOffset":5}
+
+PASS: Should be SyntaxErrorType Irrecoverable.
+Source: var x = =
+                ^
+Error Message: Unexpected token '='
+Error Range: {"startOffset":8,"endOffset":9}
+
+PASS: Should be SyntaxErrorType Irrecoverable.
+Source: const x;
+               ^
+Error Message: Unexpected token ';'. const declared variable 'x' must have an initializer.
+Error Range: {"startOffset":7,"endOffset":8}
+
+PASS: Should be SyntaxErrorType Irrecoverable.
+Source: let x = 1; let x = 2;
+                         ^
+Error Message: Cannot declare a let variable twice: 'x'.
+Error Range: {"startOffset":17,"endOffset":18}
+
+PASS: Should be SyntaxErrorType Irrecoverable.
+Source: if x(1)
+           ^
+Error Message: Unexpected identifier 'x'. Expected '(' to start a 'if' condition.
+Error Range: {"startOffset":3,"endOffset":4}
+
+
diff --git a/LayoutTests/inspector/runtime/parse.html b/LayoutTests/inspector/runtime/parse.html
new file mode 100644 (file)
index 0000000..3a4b127
--- /dev/null
@@ -0,0 +1,192 @@
+<!doctype html>
+<html>
+<head>
+<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script>
+function test()
+{
+    InspectorTest.dumpActivityToSystemConsole = true;
+
+    function outputErrorSourceRange(range) {
+        var str = " ".repeat("Source: ".length);
+        str += " ".repeat(range.startOffset);
+        str += "^";
+        str += "~".repeat(range.endOffset - range.startOffset - 1);
+        InspectorTest.log(str);
+    }
+
+    let suite = InspectorTest.createAsyncSuite("Runtime.parse");
+
+    suite.addTestCase({
+        name: "SyntaxErrorType.None",
+        description: "Test source without an issue.",
+        test: (resolve, reject) => {
+            function testSource(source) {
+                RuntimeAgent.parse(source, (error, result, message, range) => {
+                    InspectorTest.assert(!error, "Should not be a protocol error.");
+                    InspectorTest.expectThat(result === RuntimeAgent.SyntaxErrorType.None, "Should be SyntaxErrorType None.");
+                    InspectorTest.log("Source: " + source);
+                    InspectorTest.assert(!message);
+                    InspectorTest.assert(!range);
+                    InspectorTest.log("");
+                });
+            }
+
+            // Complete programs without syntax errors (could have runtime errors).
+            testSource("");
+            testSource(";");
+            testSource("myVariable");
+            testSource("var x = 1; x");
+            testSource("[1,2,3]");
+            testSource("({})");
+            testSource("({prop:true}).x.x.x");
+            testSource("if(1)1");
+            InspectorBackend.runAfterPendingDispatches(resolve);
+        }
+    });
+
+    suite.addTestCase({
+        name: "SyntaxErrorType.UnterminatedLiteral",
+        description: "Test source with an unterminated literal.",
+        test: (resolve, reject) => {
+            function testSource(source) {
+                RuntimeAgent.parse(source, (error, result, message, range) => {
+                    InspectorTest.assert(!error, "Should not be a protocol error.");
+                    InspectorTest.expectThat(result === RuntimeAgent.SyntaxErrorType.UnterminatedLiteral, "Should be SyntaxErrorType UnterminatedLiteral.");
+                    InspectorTest.log("Source: " + source);
+                    outputErrorSourceRange(range);
+                    InspectorTest.log("Error Message: " + message);
+                    InspectorTest.log("Range: " + JSON.stringify(range));
+                    InspectorTest.log("");
+                });
+            }
+
+            // A literal that is not yet complete, but would be a single token.
+            testSource("0b"); // binary
+            testSource("0o"); // octal
+            testSource("0x"); // hex
+            testSource("1e"); // scientific notation number
+            testSource("'");  // string
+            testSource("\""); // string
+            testSource("var \\u007"); // identifier unicode escape
+            // testSource("/r"); // regexp - Not detected by JavaScriptCore.
+            InspectorBackend.runAfterPendingDispatches(resolve);
+        }
+    });
+
+    suite.addTestCase({
+        name: "SyntaxErrorType.Recoverable",
+        description: "Test source with a recoverable error.",
+        test: (resolve, reject) => {
+            function testSource(source) {
+                RuntimeAgent.parse(source, (error, result, message, range) => {
+                    InspectorTest.assert(!error, "Should not be a protocol error.");
+                    InspectorTest.expectThat(result === RuntimeAgent.SyntaxErrorType.Recoverable, "Should be SyntaxErrorType Recoverable.");
+                    InspectorTest.log("Source: " + source);
+                    outputErrorSourceRange(range);
+                    InspectorTest.log("Error Message: " + message);
+                    InspectorTest.log("Error Range: " + JSON.stringify(range));
+                    InspectorTest.log("");
+                });
+            }
+
+            // Not yet a syntax error, just an incomplete program.
+            testSource("(");
+            testSource("{");
+            testSource("!");
+            testSource("-"); // unary
+            testSource("+"); // unary
+            testSource("~"); // unary
+            testSource("1,");
+            testSource("var");
+            testSource("'use strict'; let"); // When not in strict mode, let is treated as the start of an identifier, not a keyword!
+            testSource("const");
+            testSource("var {x"); // destructuring
+            testSource("var [x"); // destructuring
+            testSource("var x,");
+            testSource("if(1)");
+            testSource("if(1){");
+            testSource("if(1){}else");
+            testSource("if(1){}else{");
+            testSource("while(1)");
+            testSource("while(1){");
+            testSource("for(;;)");
+            testSource("for(;;){");
+            testSource("()=>");  // arrow function
+            testSource("()=>{"); // arrow function
+            testSource("/*");    // comment
+            testSource("`");     // template string
+            testSource("`${1}"); // template string
+            testSource("`${");   // template string
+            testSource("function");
+            testSource("function foo");
+            testSource("function foo(");
+            testSource("function foo(){");
+            testSource("(function() {");
+            testSource("(function() {}");
+            testSource("(function() {})(");
+            testSource("switch(x)");
+            testSource("switch(x){");
+            testSource("switch(x){case");
+            testSource("switch(x){case 1");
+            testSource("switch(x){case 1:");
+            testSource("class");
+            testSource("class Klass");
+            testSource("class Klass {");
+            testSource("var x = {");
+            testSource("var x = {prop");
+            testSource("var x = {prop:");
+            testSource("var x = {prop:123");
+            testSource("var x = {prop:123 ");
+            testSource("var x = {prop:123,");
+            testSource("var x = {prop:123, ");
+            testSource("var x = {prop:123, 'prop2'");
+            testSource("var x = {prop:123, [12");
+            testSource("var x = {prop:123, [12]");
+            testSource("var x = {prop:123, [12]:");
+            testSource("var x = {prop:123, [12]:567");
+            testSource("var x = [");
+            testSource("var x = [1");
+            testSource("var x = [1,");
+            testSource("var x = [1,[]");
+            testSource("var x = [1,{}");
+            InspectorBackend.runAfterPendingDispatches(resolve);
+        }
+    });
+
+    suite.addTestCase({
+        name: "SyntaxErrorType.Irrecoverable",
+        description: "Test source with an irrecoverable issue.",
+        test: (resolve, reject) => {
+            function testSource(source) {
+                RuntimeAgent.parse(source, (error, result, message, range) => {
+                    InspectorTest.assert(!error, "Should not be a protocol error.");
+                    InspectorTest.expectThat(result === RuntimeAgent.SyntaxErrorType.Irrecoverable, "Should be SyntaxErrorType Irrecoverable.");
+                    InspectorTest.log("Source: " + source);
+                    outputErrorSourceRange(range);
+                    InspectorTest.log("Error Message: " + message);
+                    InspectorTest.log("Error Range: " + JSON.stringify(range));
+                    InspectorTest.log("");
+                });
+            }
+
+            // SyntaxErrors. Any typing after the syntax error is detected does not matter.
+            testSource("1$");
+            testSource("var x = {}}");
+            testSource("({}))");
+            testSource("var x = =");
+            testSource("const x;");
+            testSource("let x = 1; let x = 2;");
+            testSource("if x(1)");
+            InspectorBackend.runAfterPendingDispatches(resolve);
+        }
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body onload="runTest()">
+<p>Tests for the Runtime.parse command.</p>
+</body>
+</html>
index 2fffdf8..dd975df 100644 (file)
@@ -1,3 +1,21 @@
+2015-09-04  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Test RuntimeAgent.parse, detecting if a script parse error is recoverable
+        https://bugs.webkit.org/show_bug.cgi?id=148790
+
+        Reviewed by Timothy Hatcher.
+
+        * parser/Lexer.cpp:
+        (JSC::Lexer<T>::lex):
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::printUnexpectedTokenText):
+        * parser/ParserTokens.h:
+        More consistently name and treat unterminated numeric literals.
+
+        * parser/Parser.h:
+        (JSC::Parser<LexerType>::parse):
+        Treat multiline capable unterminated literals as recoverable.
+
 2015-08-25  Basile Clement  <basile_clement@apple.com>
 
         Get rid of FTLValueFormat
index f1fd574..e4492ff 100644 (file)
@@ -2031,7 +2031,7 @@ start:
         if ((m_current | 0x20) == 'x') {
             if (!isASCIIHexDigit(peek(1))) {
                 m_lexErrorMessage = ASCIILiteral("No hexadecimal digits after '0x'");
-                token = INVALID_HEX_NUMBER_ERRORTOK;
+                token = UNTERMINATED_HEX_NUMBER_ERRORTOK;
                 goto returnError;
             }
 
@@ -2041,7 +2041,7 @@ start:
             parseHex(tokenData->doubleValue);
             if (isIdentStart(m_current)) {
                 m_lexErrorMessage = ASCIILiteral("No space between hexadecimal literal and identifier");
-                token = INVALID_HEX_NUMBER_ERRORTOK;
+                token = UNTERMINATED_HEX_NUMBER_ERRORTOK;
                 goto returnError;
             }
             token = tokenTypeForIntegerLikeToken(tokenData->doubleValue);
@@ -2051,7 +2051,7 @@ start:
         if ((m_current | 0x20) == 'b') {
             if (!isASCIIBinaryDigit(peek(1))) {
                 m_lexErrorMessage = ASCIILiteral("No binary digits after '0b'");
-                token = INVALID_BINARY_NUMBER_ERRORTOK;
+                token = UNTERMINATED_BINARY_NUMBER_ERRORTOK;
                 goto returnError;
             }
 
@@ -2061,7 +2061,7 @@ start:
             parseBinary(tokenData->doubleValue);
             if (isIdentStart(m_current)) {
                 m_lexErrorMessage = ASCIILiteral("No space between binary literal and identifier");
-                token = INVALID_BINARY_NUMBER_ERRORTOK;
+                token = UNTERMINATED_BINARY_NUMBER_ERRORTOK;
                 goto returnError;
             }
             token = tokenTypeForIntegerLikeToken(tokenData->doubleValue);
@@ -2072,7 +2072,7 @@ start:
         if ((m_current | 0x20) == 'o') {
             if (!isASCIIOctalDigit(peek(1))) {
                 m_lexErrorMessage = ASCIILiteral("No octal digits after '0o'");
-                token = INVALID_OCTAL_NUMBER_ERRORTOK;
+                token = UNTERMINATED_OCTAL_NUMBER_ERRORTOK;
                 goto returnError;
             }
 
@@ -2082,7 +2082,7 @@ start:
             parseOctal(tokenData->doubleValue);
             if (isIdentStart(m_current)) {
                 m_lexErrorMessage = ASCIILiteral("No space between octal literal and identifier");
-                token = INVALID_OCTAL_NUMBER_ERRORTOK;
+                token = UNTERMINATED_OCTAL_NUMBER_ERRORTOK;
                 goto returnError;
             }
             token = tokenTypeForIntegerLikeToken(tokenData->doubleValue);
@@ -2093,7 +2093,7 @@ start:
         record8('0');
         if (strictMode && isASCIIDigit(m_current)) {
             m_lexErrorMessage = ASCIILiteral("Decimal integer literals with a leading zero are forbidden in strict mode");
-            token = INVALID_OCTAL_NUMBER_ERRORTOK;
+            token = UNTERMINATED_OCTAL_NUMBER_ERRORTOK;
             goto returnError;
         }
         if (isASCIIOctalDigit(m_current)) {
index 7f0501c..08c12c5 100644 (file)
@@ -3661,7 +3661,7 @@ template <typename LexerType> void Parser<LexerType>::printUnexpectedTokenText(W
     case INVALID_NUMERIC_LITERAL_ERRORTOK:
         out.print("Invalid numeric literal: '", getToken(), "'");
         return;
-    case INVALID_OCTAL_NUMBER_ERRORTOK:
+    case UNTERMINATED_OCTAL_NUMBER_ERRORTOK:
         out.print("Invalid use of octal: '", getToken(), "'");
         return;
     case INVALID_STRING_LITERAL_ERRORTOK:
index f697be3..54b82c7 100644 (file)
@@ -1340,8 +1340,13 @@ std::unique_ptr<ParsedNode> Parser<LexerType>::parse(ParserError& error, const I
             ParserError::SyntaxErrorType errorType = ParserError::SyntaxErrorIrrecoverable;
             if (m_token.m_type == EOFTOK)
                 errorType = ParserError::SyntaxErrorRecoverable;
-            else if (m_token.m_type & UnterminatedErrorTokenFlag)
-                errorType = ParserError::SyntaxErrorUnterminatedLiteral;
+            else if (m_token.m_type & UnterminatedErrorTokenFlag) {
+                // Treat multiline capable unterminated literals as recoverable.
+                if (m_token.m_type == UNTERMINATED_MULTILINE_COMMENT_ERRORTOK || m_token.m_type == UNTERMINATED_TEMPLATE_LITERAL_ERRORTOK)
+                    errorType = ParserError::SyntaxErrorRecoverable;
+                else
+                    errorType = ParserError::SyntaxErrorUnterminatedLiteral;
+            }
             
             if (isEvalNode<ParsedNode>())
                 error = ParserError(ParserError::EvalError, errorType, m_token, errMsg, errLine);
index 3dfdc28..42c212d 100644 (file)
@@ -162,13 +162,13 @@ enum JSTokenType {
     INVALID_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK = 3 | ErrorTokenFlag,
     UNTERMINATED_MULTILINE_COMMENT_ERRORTOK = 4 | ErrorTokenFlag | UnterminatedErrorTokenFlag,
     UNTERMINATED_NUMERIC_LITERAL_ERRORTOK = 5 | ErrorTokenFlag | UnterminatedErrorTokenFlag,
-    INVALID_OCTAL_NUMBER_ERRORTOK = 6 | ErrorTokenFlag | UnterminatedErrorTokenFlag,
+    UNTERMINATED_OCTAL_NUMBER_ERRORTOK = 6 | ErrorTokenFlag | UnterminatedErrorTokenFlag,
     INVALID_NUMERIC_LITERAL_ERRORTOK = 7 | ErrorTokenFlag,
     UNTERMINATED_STRING_LITERAL_ERRORTOK = 8 | ErrorTokenFlag | UnterminatedErrorTokenFlag,
     INVALID_STRING_LITERAL_ERRORTOK = 9 | ErrorTokenFlag,
     INVALID_PRIVATE_NAME_ERRORTOK = 10 | ErrorTokenFlag,
-    INVALID_HEX_NUMBER_ERRORTOK = 11 | ErrorTokenFlag,
-    INVALID_BINARY_NUMBER_ERRORTOK = 12 | ErrorTokenFlag,
+    UNTERMINATED_HEX_NUMBER_ERRORTOK = 11 | ErrorTokenFlag | UnterminatedErrorTokenFlag,
+    UNTERMINATED_BINARY_NUMBER_ERRORTOK = 12 | ErrorTokenFlag | UnterminatedErrorTokenFlag,
     UNTERMINATED_TEMPLATE_LITERAL_ERRORTOK = 13 | ErrorTokenFlag | UnterminatedErrorTokenFlag,
     INVALID_TEMPLATE_LITERAL_ERRORTOK = 14 | ErrorTokenFlag,
 };