JS Lexer and Parser should be more informative when they encounter errors
authoroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 21 Apr 2013 23:26:56 +0000 (23:26 +0000)
committeroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 21 Apr 2013 23:26:56 +0000 (23:26 +0000)
https://bugs.webkit.org/show_bug.cgi?id=114924

Reviewed by Filip Pizlo.

Source/JavaScriptCore:

Add new tokens to represent the various ways that parsing and lexing have failed.
This gives us the ability to produce better error messages in some cases,
and to indicate whether or not the failure was due to invalid source, or simply
early termination.

The jsc prompt now makes use of this so that you can write functions that
are more than one line long.

* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::generate):
* jsc.cpp:
(stringFromUTF):
(jscSource):
(runInteractive):
* parser/Lexer.cpp:
(JSC::::parseFourDigitUnicodeHex):
(JSC::::parseIdentifierSlowCase):
(JSC::::parseString):
(JSC::::parseStringSlowCase):
(JSC::::lex):
* parser/Lexer.h:
(UnicodeHexValue):
(JSC::Lexer::UnicodeHexValue::UnicodeHexValue):
(JSC::Lexer::UnicodeHexValue::valueType):
(JSC::Lexer::UnicodeHexValue::isValid):
(JSC::Lexer::UnicodeHexValue::value):
(Lexer):
* parser/Parser.h:
(JSC::Parser::getTokenName):
(JSC::Parser::updateErrorMessageSpecialCase):
(JSC::::parse):
* parser/ParserError.h:
(ParserError):
(JSC::ParserError::ParserError):
* parser/ParserTokens.h:
* runtime/Completion.cpp:
(JSC):
(JSC::checkSyntax):
* runtime/Completion.h:
(JSC):

LayoutTests:

Update test results to cover improved error messages.

* fast/js/kde/parse-expected.txt:
* sputnik/Conformance/07_Lexical_Conventions/7.2_White_Space/S7.2_A5_T1-expected.txt:
* sputnik/Conformance/07_Lexical_Conventions/7.2_White_Space/S7.2_A5_T2-expected.txt:
* sputnik/Conformance/07_Lexical_Conventions/7.2_White_Space/S7.2_A5_T3-expected.txt:
* sputnik/Conformance/07_Lexical_Conventions/7.2_White_Space/S7.2_A5_T4-expected.txt:
* sputnik/Conformance/07_Lexical_Conventions/7.2_White_Space/S7.2_A5_T5-expected.txt:
* sputnik/Conformance/07_Lexical_Conventions/7.3_Line_Terminators/S7.3_A6_T1-expected.txt:
* sputnik/Conformance/07_Lexical_Conventions/7.3_Line_Terminators/S7.3_A6_T2-expected.txt:
* sputnik/Conformance/07_Lexical_Conventions/7.3_Line_Terminators/S7.3_A6_T3-expected.txt:
* sputnik/Conformance/07_Lexical_Conventions/7.3_Line_Terminators/S7.3_A6_T4-expected.txt:
* sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T1-expected.txt:
* sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T10-expected.txt:
* sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T2-expected.txt:
* sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T3-expected.txt:
* sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T4-expected.txt:
* sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T5-expected.txt:
* sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T6-expected.txt:
* sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T7-expected.txt:
* sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T8-expected.txt:
* sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T9-expected.txt:
* sputnik/Conformance/13_Function_Definition/S13_A7_T3-expected.txt:

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

32 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/js/kde/parse-expected.txt
LayoutTests/sputnik/Conformance/07_Lexical_Conventions/7.2_White_Space/S7.2_A5_T1-expected.txt
LayoutTests/sputnik/Conformance/07_Lexical_Conventions/7.2_White_Space/S7.2_A5_T2-expected.txt
LayoutTests/sputnik/Conformance/07_Lexical_Conventions/7.2_White_Space/S7.2_A5_T3-expected.txt
LayoutTests/sputnik/Conformance/07_Lexical_Conventions/7.2_White_Space/S7.2_A5_T4-expected.txt
LayoutTests/sputnik/Conformance/07_Lexical_Conventions/7.2_White_Space/S7.2_A5_T5-expected.txt
LayoutTests/sputnik/Conformance/07_Lexical_Conventions/7.3_Line_Terminators/S7.3_A6_T1-expected.txt
LayoutTests/sputnik/Conformance/07_Lexical_Conventions/7.3_Line_Terminators/S7.3_A6_T2-expected.txt
LayoutTests/sputnik/Conformance/07_Lexical_Conventions/7.3_Line_Terminators/S7.3_A6_T3-expected.txt
LayoutTests/sputnik/Conformance/07_Lexical_Conventions/7.3_Line_Terminators/S7.3_A6_T4-expected.txt
LayoutTests/sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T1-expected.txt
LayoutTests/sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T10-expected.txt
LayoutTests/sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T2-expected.txt
LayoutTests/sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T3-expected.txt
LayoutTests/sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T4-expected.txt
LayoutTests/sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T5-expected.txt
LayoutTests/sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T6-expected.txt
LayoutTests/sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T7-expected.txt
LayoutTests/sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T8-expected.txt
LayoutTests/sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T9-expected.txt
LayoutTests/sputnik/Conformance/13_Function_Definition/S13_A7_T3-expected.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/jsc.cpp
Source/JavaScriptCore/parser/Lexer.cpp
Source/JavaScriptCore/parser/Lexer.h
Source/JavaScriptCore/parser/Parser.h
Source/JavaScriptCore/parser/ParserError.h
Source/JavaScriptCore/parser/ParserTokens.h
Source/JavaScriptCore/runtime/Completion.cpp
Source/JavaScriptCore/runtime/Completion.h

index 71127a3..f04950c 100644 (file)
@@ -1,3 +1,34 @@
+2013-04-21  Oliver Hunt  <oliver@apple.com>
+
+        JS Lexer and Parser should be more informative when they encounter errors
+        https://bugs.webkit.org/show_bug.cgi?id=114924
+
+        Reviewed by Filip Pizlo.
+
+        Update test results to cover improved error messages.
+
+        * fast/js/kde/parse-expected.txt:
+        * sputnik/Conformance/07_Lexical_Conventions/7.2_White_Space/S7.2_A5_T1-expected.txt:
+        * sputnik/Conformance/07_Lexical_Conventions/7.2_White_Space/S7.2_A5_T2-expected.txt:
+        * sputnik/Conformance/07_Lexical_Conventions/7.2_White_Space/S7.2_A5_T3-expected.txt:
+        * sputnik/Conformance/07_Lexical_Conventions/7.2_White_Space/S7.2_A5_T4-expected.txt:
+        * sputnik/Conformance/07_Lexical_Conventions/7.2_White_Space/S7.2_A5_T5-expected.txt:
+        * sputnik/Conformance/07_Lexical_Conventions/7.3_Line_Terminators/S7.3_A6_T1-expected.txt:
+        * sputnik/Conformance/07_Lexical_Conventions/7.3_Line_Terminators/S7.3_A6_T2-expected.txt:
+        * sputnik/Conformance/07_Lexical_Conventions/7.3_Line_Terminators/S7.3_A6_T3-expected.txt:
+        * sputnik/Conformance/07_Lexical_Conventions/7.3_Line_Terminators/S7.3_A6_T4-expected.txt:
+        * sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T1-expected.txt:
+        * sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T10-expected.txt:
+        * sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T2-expected.txt:
+        * sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T3-expected.txt:
+        * sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T4-expected.txt:
+        * sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T5-expected.txt:
+        * sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T6-expected.txt:
+        * sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T7-expected.txt:
+        * sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T8-expected.txt:
+        * sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T9-expected.txt:
+        * sputnik/Conformance/13_Function_Definition/S13_A7_T3-expected.txt:
+
 2013-04-21  Christophe Dumez  <ch.dumez@sisa.samsung.com>
 
         Regression(r148672): fast/events/touch/frame-hover-update.html fails
index 1dc18ac..df6933b 100644 (file)
@@ -20,9 +20,9 @@ PASS var f÷; threw exception SyntaxError: Invalid character '\u0247'.
 PASS var \u0061 = 102; a is 102
 PASS var f\u0030 = 103; f0 is 103
 PASS var \u00E9\u0100\u02AF\u0388\u18A8 = 104; \u00E9\u0100\u02AF\u0388\u18A8; is 104
-PASS var f\u00F7; threw exception SyntaxError: Unrecognized token 'f\u00F7'.
-PASS var \u0030; threw exception SyntaxError: Unrecognized token '\u0030'.
-PASS var test = { }; test.i= 0; test.i\u002b= 1; test.i; threw exception SyntaxError: Unrecognized token 'i\u002b'.
+PASS var f\u00F7; threw exception SyntaxError: Invalid unicode escape in identifier: 'f\u00F7'.
+PASS var \u0030; threw exception SyntaxError: Invalid unicode escape in identifier: '\u0030'.
+PASS var test = { }; test.i= 0; test.i\u002b= 1; test.i; threw exception SyntaxError: Invalid unicode escape in identifier: 'i\u002b'.
 PASS var test = { }; test.i= 0; test.i+= 1; test.i; is 1
 PASS successfullyParsed is true
 
index 1e44031..aefbbc2 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: line 76: SyntaxError: Unrecognized token '\u0009'
+CONSOLE MESSAGE: line 76: SyntaxError: Invalid unicode escape in identifier: '\u0009'
 S7.2_A5_T1
 
 PASS Expected parsing failure
index f63469a..680787d 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: line 76: SyntaxError: Unrecognized token '\u000B'
+CONSOLE MESSAGE: line 76: SyntaxError: Invalid unicode escape in identifier: '\u000B'
 S7.2_A5_T2
 
 PASS Expected parsing failure
index c75699e..6201941 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: line 76: SyntaxError: Unrecognized token '\u000C'
+CONSOLE MESSAGE: line 76: SyntaxError: Invalid unicode escape in identifier: '\u000C'
 S7.2_A5_T3
 
 PASS Expected parsing failure
index 0419f72..8bcc3a9 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: line 76: SyntaxError: Unrecognized token '\u0020'
+CONSOLE MESSAGE: line 76: SyntaxError: Invalid unicode escape in identifier: '\u0020'
 S7.2_A5_T4
 
 PASS Expected parsing failure
index 5588f92..aff3961 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: line 76: SyntaxError: Unrecognized token '\u00A0'
+CONSOLE MESSAGE: line 76: SyntaxError: Invalid unicode escape in identifier: '\u00A0'
 S7.2_A5_T5
 
 PASS Expected parsing failure
index f044558..5c4d0e4 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: line 76: SyntaxError: Unrecognized token '\u000A'
+CONSOLE MESSAGE: line 76: SyntaxError: Invalid unicode escape in identifier: '\u000A'
 S7.3_A6_T1
 
 PASS Expected parsing failure
index ee636dc..4287b37 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: line 76: SyntaxError: Unrecognized token '\u000D'
+CONSOLE MESSAGE: line 76: SyntaxError: Invalid unicode escape in identifier: '\u000D'
 S7.3_A6_T2
 
 PASS Expected parsing failure
index 4aff5dd..cd13020 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: line 76: SyntaxError: Unrecognized token '\u2028'
+CONSOLE MESSAGE: line 76: SyntaxError: Invalid unicode escape in identifier: '\u2028'
 S7.3_A6_T3
 
 PASS Expected parsing failure
index 62a01e4..c730296 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: line 76: SyntaxError: Unrecognized token '\u2029'
+CONSOLE MESSAGE: line 76: SyntaxError: Invalid unicode escape in identifier: '\u2029'
 S7.3_A6_T4
 
 PASS Expected parsing failure
index e98e2d6..1a41070 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: line 76: SyntaxError: Unrecognized token '\u007B'
+CONSOLE MESSAGE: line 76: SyntaxError: Invalid unicode escape in identifier: '\u007B'
 S7.7_A2_T1
 
 PASS Expected parsing failure
index 9484771..0d39343 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: line 76: SyntaxError: Unrecognized token '\u002F'
+CONSOLE MESSAGE: line 76: SyntaxError: Invalid unicode escape in identifier: '\u002F'
 S7.7_A2_T10
 
 PASS Expected parsing failure
index 1200f37..972e4f5 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: line 76: SyntaxError: Unrecognized token '\u0028'
+CONSOLE MESSAGE: line 76: SyntaxError: Invalid unicode escape in identifier: '\u0028'
 S7.7_A2_T2
 
 PASS Expected parsing failure
index 5e9351e..e716548 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: line 76: SyntaxError: Unrecognized token '\u005B'
+CONSOLE MESSAGE: line 76: SyntaxError: Invalid unicode escape in identifier: '\u005B'
 S7.7_A2_T3
 
 PASS Expected parsing failure
index 0104b43..a372e5f 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: line 76: SyntaxError: Unrecognized token '\u003B'
+CONSOLE MESSAGE: line 76: SyntaxError: Invalid unicode escape in identifier: '\u003B'
 S7.7_A2_T4
 
 PASS Expected parsing failure
index 2d78e91..db6628a 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: line 77: SyntaxError: Unrecognized token '\u002E'
+CONSOLE MESSAGE: line 77: SyntaxError: Invalid unicode escape in identifier: '\u002E'
 S7.7_A2_T5
 
 PASS Expected parsing failure
index 839ac6e..3782d4a 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: line 76: SyntaxError: Unrecognized token '\u002C'
+CONSOLE MESSAGE: line 76: SyntaxError: Invalid unicode escape in identifier: '\u002C'
 S7.7_A2_T6
 
 PASS Expected parsing failure
index a78c76b..8653d5b 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: line 76: SyntaxError: Unrecognized token '\u002B'
+CONSOLE MESSAGE: line 76: SyntaxError: Invalid unicode escape in identifier: '\u002B'
 S7.7_A2_T7
 
 PASS Expected parsing failure
index 0f118c3..2bb8e47 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: line 76: SyntaxError: Unrecognized token '\u002D'
+CONSOLE MESSAGE: line 76: SyntaxError: Invalid unicode escape in identifier: '\u002D'
 S7.7_A2_T8
 
 PASS Expected parsing failure
index ab792d9..26c2575 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: line 76: SyntaxError: Unrecognized token '\u002A'
+CONSOLE MESSAGE: line 76: SyntaxError: Invalid unicode escape in identifier: '\u002A'
 S7.7_A2_T9
 
 PASS Expected parsing failure
index 0ace65f..d51bcb8 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: line 76: SyntaxError: Unrecognized token '\'
+CONSOLE MESSAGE: line 76: SyntaxError: Invalid escape in identifier: '\'
 S13_A7_T3
 
 PASS Expected parsing failure
index c10b065..af8123d 100644 (file)
@@ -1,3 +1,51 @@
+2013-04-21  Oliver Hunt  <oliver@apple.com>
+
+        JS Lexer and Parser should be more informative when they encounter errors
+        https://bugs.webkit.org/show_bug.cgi?id=114924
+
+        Reviewed by Filip Pizlo.
+
+        Add new tokens to represent the various ways that parsing and lexing have failed.
+        This gives us the ability to produce better error messages in some cases,
+        and to indicate whether or not the failure was due to invalid source, or simply
+        early termination.
+
+        The jsc prompt now makes use of this so that you can write functions that
+        are more than one line long.
+
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::generate):
+        * jsc.cpp:
+        (stringFromUTF):
+        (jscSource):
+        (runInteractive):
+        * parser/Lexer.cpp:
+        (JSC::::parseFourDigitUnicodeHex):
+        (JSC::::parseIdentifierSlowCase):
+        (JSC::::parseString):
+        (JSC::::parseStringSlowCase):
+        (JSC::::lex):
+        * parser/Lexer.h:
+        (UnicodeHexValue):
+        (JSC::Lexer::UnicodeHexValue::UnicodeHexValue):
+        (JSC::Lexer::UnicodeHexValue::valueType):
+        (JSC::Lexer::UnicodeHexValue::isValid):
+        (JSC::Lexer::UnicodeHexValue::value):
+        (Lexer):
+        * parser/Parser.h:
+        (JSC::Parser::getTokenName):
+        (JSC::Parser::updateErrorMessageSpecialCase):
+        (JSC::::parse):
+        * parser/ParserError.h:
+        (ParserError):
+        (JSC::ParserError::ParserError):
+        * parser/ParserTokens.h:
+        * runtime/Completion.cpp:
+        (JSC):
+        (JSC::checkSyntax):
+        * runtime/Completion.h:
+        (JSC):
+
 2013-04-21  Mark Lam  <mark.lam@apple.com>
 
         Refactor identical inline functions in JSVALUE64 and JSVALUE32_64 sections
index 3172029..cf62db7 100644 (file)
@@ -130,8 +130,8 @@ ParserError BytecodeGenerator::generate()
     m_codeBlock->shrinkToFit();
 
     if (m_expressionTooDeep)
-        return ParserError::OutOfMemory;
-    return ParserError::ErrorNone;
+        return ParserError(ParserError::OutOfMemory);
+    return ParserError(ParserError::ErrorNone);
 }
 
 bool BytecodeGenerator::addVar(const Identifier& ident, bool isConstant, RegisterID*& r0)
index d1d2bcb..2030e16 100644 (file)
@@ -146,7 +146,7 @@ public:
     void parseArguments(int, char**);
 };
 
-static const char interactivePrompt[] = "> ";
+static const char interactivePrompt[] = ">>> ";
 
 class StopWatch {
 public:
@@ -268,23 +268,28 @@ GlobalObject::GlobalObject(VM& vm, Structure* structure)
 {
 }
 
-static inline SourceCode jscSource(const char* utf8, const String& filename)
+static inline String stringFromUTF(const char* utf8)
 {
     // Find the the first non-ascii character, or nul.
     const char* pos = utf8;
     while (*pos > 0)
         pos++;
     size_t asciiLength = pos - utf8;
-
+    
     // Fast case - string is all ascii.
     if (!*pos)
-        return makeSource(String(utf8, asciiLength), filename);
-
+        return String(utf8, asciiLength);
+    
     // Slow case - contains non-ascii characters, use fromUTF8WithLatin1Fallback.
     ASSERT(*pos < 0);
     ASSERT(strlen(utf8) == asciiLength + strlen(pos));
-    String source = String::fromUTF8WithLatin1Fallback(utf8, asciiLength + strlen(pos));
-    return makeSource(source.impl(), filename);
+    return String::fromUTF8WithLatin1Fallback(utf8, asciiLength + strlen(pos));
+}
+
+static inline SourceCode jscSource(const char* utf8, const String& filename)
+{
+    String str = stringFromUTF(utf8);
+    return makeSource(str, filename);
 }
 
 EncodedJSValue JSC_HOST_CALL functionPrint(ExecState* exec)
@@ -607,17 +612,33 @@ static bool runWithScripts(GlobalObject* globalObject, const Vector<Script>& scr
 static void runInteractive(GlobalObject* globalObject)
 {
     String interpreterName("Interpreter");
-
-    while (true) {
+    
+    bool shouldQuit = false;
+    while (!shouldQuit) {
 #if HAVE(READLINE) && !RUNNING_FROM_XCODE
-        char* line = readline(interactivePrompt);
-        if (!line)
-            break;
-        if (line[0])
-            add_history(line);
+        ParserError error;
+        String source;
+        do {
+            error = ParserError();
+            char* line = readline(source.isEmpty() ? interactivePrompt : "... ");
+            source = source + line;
+            source = source + '\n';
+            checkSyntax(globalObject->globalExec(), makeSource(source, interpreterName), error);
+            shouldQuit = !line;
+            if (!line || !line[0])
+                break;
+            if (line[0])
+                add_history(line);
+        } while (error.m_syntaxErrorType == ParserError::SyntaxErrorRecoverable);
+        
+        if (error.m_type != ParserError::ErrorNone) {
+            printf("%s:%d\n", error.m_message.utf8().data(), error.m_line);
+            continue;
+        }
+        
+        
         JSValue evaluationException;
-        JSValue returnValue = evaluate(globalObject->globalExec(), jscSource(line, interpreterName), JSValue(), &evaluationException);
-        free(line);
+        JSValue returnValue = evaluate(globalObject->globalExec(), makeSource(source, interpreterName), JSValue(), &evaluationException);
 #else
         printf("%s", interactivePrompt);
         Vector<char, 256> line;
index ca63151..12361a4 100644 (file)
@@ -596,21 +596,21 @@ ALWAYS_INLINE T Lexer<T>::peek(int offset) const
 }
 
 template <typename T>
-int Lexer<T>::parseFourDigitUnicodeHex()
+typename Lexer<T>::UnicodeHexValue Lexer<T>::parseFourDigitUnicodeHex()
 {
     T char1 = peek(1);
     T char2 = peek(2);
     T char3 = peek(3);
 
     if (UNLIKELY(!isASCIIHexDigit(m_current) || !isASCIIHexDigit(char1) || !isASCIIHexDigit(char2) || !isASCIIHexDigit(char3)))
-        return -1;
+        return UnicodeHexValue((m_code + 4) >= m_codeEnd ? UnicodeHexValue::IncompleteHex : UnicodeHexValue::InvalidHex);
 
     int result = convertUnicode(m_current, char1, char2, char3);
     shift();
     shift();
     shift();
     shift();
-    return result;
+    return UnicodeHexValue(result);
 }
 
 template <typename T>
@@ -883,14 +883,14 @@ template <bool shouldCreateIdentifier> JSTokenType Lexer<T>::parseIdentifierSlow
             m_buffer16.append(identifierStart, currentCharacter() - identifierStart);
         shift();
         if (UNLIKELY(m_current != 'u'))
-            return ERRORTOK;
+            return atEnd() ? UNTERMINATED_IDENTIFIER_ESCAPE_ERRORTOK : INVALID_IDENTIFIER_ESCAPE_ERRORTOK;
         shift();
-        int character = parseFourDigitUnicodeHex();
-        if (UNLIKELY(character == -1))
-            return ERRORTOK;
-        UChar ucharacter = static_cast<UChar>(character);
+        UnicodeHexValue character = parseFourDigitUnicodeHex();
+        if (UNLIKELY(!character.isValid()))
+            return character.valueType() == UnicodeHexValue::IncompleteHex ? UNTERMINATED_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK : INVALID_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK;
+        UChar ucharacter = static_cast<UChar>(character.value());
         if (UNLIKELY(m_buffer16.size() ? !isIdentPart(ucharacter) : !isIdentStart(ucharacter)))
-            return ERRORTOK;
+            return INVALID_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK;
         if (shouldCreateIdentifier)
             record16(ucharacter);
         identifierStart = currentCharacter();
@@ -941,7 +941,7 @@ static ALWAYS_INLINE bool characterRequiresParseStringSlowCase(UChar character)
 }
 
 template <typename T>
-template <bool shouldBuildStrings> ALWAYS_INLINE bool Lexer<T>::parseString(JSTokenData* tokenData, bool strictMode)
+template <bool shouldBuildStrings> ALWAYS_INLINE typename Lexer<T>::StringParseResult Lexer<T>::parseString(JSTokenData* tokenData, bool strictMode)
 {
     int startingOffset = currentOffset();
     int startingLineNumber = lineNumber();
@@ -969,7 +969,7 @@ template <bool shouldBuildStrings> ALWAYS_INLINE bool Lexer<T>::parseString(JSTo
                 shift();
                 if (!isASCIIHexDigit(m_current) || !isASCIIHexDigit(peek(1))) {
                     m_lexErrorMessage = "\\x can only be followed by a hex character sequence";
-                    return false;
+                    return (atEnd() || (isASCIIHexDigit(m_current) && (m_code + 1 == m_codeEnd))) ? StringUnterminated : StringCannotBeParsed;
                 }
                 T prev = m_current;
                 shift();
@@ -1004,11 +1004,11 @@ template <bool shouldBuildStrings> ALWAYS_INLINE bool Lexer<T>::parseString(JSTo
     } else
         tokenData->ident = 0;
 
-    return true;
+    return StringParsedSuccessfully;
 }
 
 template <typename T>
-template <bool shouldBuildStrings> bool Lexer<T>::parseStringSlowCase(JSTokenData* tokenData, bool strictMode)
+template <bool shouldBuildStrings> typename Lexer<T>::StringParseResult Lexer<T>::parseStringSlowCase(JSTokenData* tokenData, bool strictMode)
 {
     T stringQuoteCharacter = m_current;
     shift();
@@ -1034,7 +1034,7 @@ template <bool shouldBuildStrings> bool Lexer<T>::parseStringSlowCase(JSTokenDat
                 shift();
                 if (!isASCIIHexDigit(m_current) || !isASCIIHexDigit(peek(1))) {
                     m_lexErrorMessage = "\\x can only be followed by a hex character sequence";
-                    return false;
+                    return StringCannotBeParsed;
                 }
                 T prev = m_current;
                 shift();
@@ -1043,16 +1043,16 @@ template <bool shouldBuildStrings> bool Lexer<T>::parseStringSlowCase(JSTokenDat
                 shift();
             } else if (m_current == 'u') {
                 shift();
-                int character = parseFourDigitUnicodeHex();
-                if (character != -1) {
+                UnicodeHexValue character = parseFourDigitUnicodeHex();
+                if (character.isValid()) {
                     if (shouldBuildStrings)
-                        record16(character);
+                        record16(character.value());
                 } else if (m_current == stringQuoteCharacter) {
                     if (shouldBuildStrings)
                         record16('u');
                 } else {
                     m_lexErrorMessage = "\\u can only be followed by a Unicode character sequence";
-                    return false;
+                    return character.valueType() == UnicodeHexValue::IncompleteHex ? StringUnterminated : StringCannotBeParsed;
                 }
             } else if (strictMode && isASCIIDigit(m_current)) {
                 // The only valid numeric escape in strict mode is '\0', and this must not be followed by a decimal digit.
@@ -1060,7 +1060,7 @@ template <bool shouldBuildStrings> bool Lexer<T>::parseStringSlowCase(JSTokenDat
                 shift();
                 if (character1 != '0' || isASCIIDigit(m_current)) {
                     m_lexErrorMessage = "The only valid numeric escape in strict mode is '\\0'";
-                    return false;
+                    return StringCannotBeParsed;
                 }
                 if (shouldBuildStrings)
                     record16(0);
@@ -1090,7 +1090,7 @@ template <bool shouldBuildStrings> bool Lexer<T>::parseStringSlowCase(JSTokenDat
                 shift();
             } else {
                 m_lexErrorMessage = "Unterminated string constant";
-                return false;
+                return StringUnterminated;
             }
 
             stringStart = currentCharacter();
@@ -1103,7 +1103,7 @@ template <bool shouldBuildStrings> bool Lexer<T>::parseStringSlowCase(JSTokenDat
             // New-line or end of input is not allowed
             if (atEnd() || isLineTerminator(m_current)) {
                 m_lexErrorMessage = "Unexpected EOF";
-                return false;
+                return atEnd() ? StringUnterminated : StringCannotBeParsed;
             }
             // Anything else is just a normal character
         }
@@ -1118,7 +1118,7 @@ template <bool shouldBuildStrings> bool Lexer<T>::parseStringSlowCase(JSTokenDat
         tokenData->ident = 0;
 
     m_buffer16.resize(0);
-    return true;
+    return StringParsedSuccessfully;
 }
 
 template <typename T>
@@ -1462,6 +1462,7 @@ start:
             if (parseMultilineComment())
                 goto start;
             m_lexErrorMessage = "Multiline comment was not closed properly";
+            token = UNTERMINATED_MULTILINE_COMMENT_ERRORTOK;
             goto returnError;
         }
         if (m_current == '=') {
@@ -1581,6 +1582,7 @@ start:
                 if (parseOctal(tokenData->doubleValue)) {
                     if (strictMode) {
                         m_lexErrorMessage = "Octal escapes are forbidden in strict mode";
+                        token = INVALID_OCTAL_NUMBER_ERRORTOK;
                         goto returnError;
                     }
                     token = NUMBER;
@@ -1599,6 +1601,7 @@ inNumberAfterDecimalPoint:
                 if ((m_current | 0x20) == 'e') {
                     if (!parseNumberAfterExponentIndicator()) {
                         m_lexErrorMessage = "Non-number found after exponent indicator";
+                        token = atEnd() ? UNTERMINATED_NUMERIC_LITERAL_ERRORTOK : INVALID_NUMERIC_LITERAL_ERRORTOK;
                         goto returnError;
                     }
                 }
@@ -1611,17 +1614,24 @@ inNumberAfterDecimalPoint:
         // No identifiers allowed directly after numeric literal, e.g. "3in" is bad.
         if (UNLIKELY(isIdentStart(m_current))) {
             m_lexErrorMessage = "At least one digit must occur after a decimal point";
+            token = atEnd() ? UNTERMINATED_NUMERIC_LITERAL_ERRORTOK : INVALID_NUMERIC_LITERAL_ERRORTOK;
             goto returnError;
         }
         m_buffer8.resize(0);
         break;
     case CharacterQuote:
         if (lexerFlags & LexerFlagsDontBuildStrings) {
-            if (UNLIKELY(!parseString<false>(tokenData, strictMode)))
+            StringParseResult result = parseString<false>(tokenData, strictMode);
+            if (UNLIKELY(result != StringParsedSuccessfully)) {
+                token = result == StringUnterminated ? UNTERMINATED_STRING_LITERAL_ERRORTOK : INVALID_STRING_LITERAL_ERRORTOK;
                 goto returnError;
+            }
         } else {
-            if (UNLIKELY(!parseString<true>(tokenData, strictMode)))
+            StringParseResult result = parseString<true>(tokenData, strictMode);
+            if (UNLIKELY(result != StringParsedSuccessfully)) {
+                token = result == StringUnterminated ? UNTERMINATED_STRING_LITERAL_ERRORTOK : INVALID_STRING_LITERAL_ERRORTOK;
                 goto returnError;
+            }
         }
         shift();
         token = STRING;
@@ -1643,10 +1653,12 @@ inNumberAfterDecimalPoint:
         goto start;
     case CharacterInvalid:
         m_lexErrorMessage = invalidCharacterMessage();
+        token = ERRORTOK;
         goto returnError;
     default:
         RELEASE_ASSERT_NOT_REACHED();
         m_lexErrorMessage = "Internal Error";
+        token = ERRORTOK;
         goto returnError;
     }
 
@@ -1678,7 +1690,8 @@ returnError:
     m_error = true;
     tokenLocation->line = m_lineNumber;
     tokenLocation->endOffset = currentOffset();
-    return ERRORTOK;
+    RELEASE_ASSERT(token & ErrorTokenFlag);
+    return token;
 }
 
 template <typename T>
index ffc7a56..5cc2a4c 100644 (file)
@@ -134,7 +134,36 @@ private:
     ALWAYS_INLINE void shift();
     ALWAYS_INLINE bool atEnd() const;
     ALWAYS_INLINE T peek(int offset) const;
-    int parseFourDigitUnicodeHex();
+    struct UnicodeHexValue {
+        
+        enum ValueType { ValidHex, IncompleteHex, InvalidHex };
+        
+        explicit UnicodeHexValue(int value)
+            : m_value(value)
+        {
+        }
+        explicit UnicodeHexValue(ValueType type)
+            : m_value(type == IncompleteHex ? -2 : -1)
+        {
+        }
+
+        ValueType valueType() const
+        {
+            if (m_value >= 0)
+                return ValidHex;
+            return m_value == -2 ? IncompleteHex : InvalidHex;
+        }
+        bool isValid() const { return m_value >= 0; }
+        int value() const
+        {
+            ASSERT(m_value >= 0);
+            return m_value;
+        }
+        
+    private:
+        int m_value;
+    };
+    UnicodeHexValue parseFourDigitUnicodeHex();
     void shiftLineTerminator();
 
     String invalidCharacterMessage() const;
@@ -157,8 +186,13 @@ private:
     template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType parseKeyword(JSTokenData*);
     template <bool shouldBuildIdentifiers> ALWAYS_INLINE JSTokenType parseIdentifier(JSTokenData*, unsigned lexerFlags, bool strictMode);
     template <bool shouldBuildIdentifiers> NEVER_INLINE JSTokenType parseIdentifierSlowCase(JSTokenData*, unsigned lexerFlags, bool strictMode);
-    template <bool shouldBuildStrings> ALWAYS_INLINE bool parseString(JSTokenData*, bool strictMode);
-    template <bool shouldBuildStrings> NEVER_INLINE bool parseStringSlowCase(JSTokenData*, bool strictMode);
+    enum StringParseResult {
+        StringParsedSuccessfully,
+        StringUnterminated,
+        StringCannotBeParsed
+    };
+    template <bool shouldBuildStrings> ALWAYS_INLINE StringParseResult parseString(JSTokenData*, bool strictMode);
+    template <bool shouldBuildStrings> NEVER_INLINE StringParseResult parseStringSlowCase(JSTokenData*, bool strictMode);
     ALWAYS_INLINE void parseHex(double& returnValue);
     ALWAYS_INLINE bool parseOctal(double& returnValue);
     ALWAYS_INLINE bool parseDecimal(double& returnValue);
index 2a89783..69cb196 100644 (file)
@@ -705,9 +705,19 @@ private:
         case RESERVED: 
         case NUMBER:
         case IDENT: 
-        case STRING: 
+        case STRING:
+        case UNTERMINATED_IDENTIFIER_ESCAPE_ERRORTOK:
+        case UNTERMINATED_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK:
+        case UNTERMINATED_MULTILINE_COMMENT_ERRORTOK:
+        case UNTERMINATED_NUMERIC_LITERAL_ERRORTOK:
+        case UNTERMINATED_STRING_LITERAL_ERRORTOK:
+        case INVALID_IDENTIFIER_ESCAPE_ERRORTOK:
+        case INVALID_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK:
+        case INVALID_NUMERIC_LITERAL_ERRORTOK:
+        case INVALID_OCTAL_NUMBER_ERRORTOK:
+        case INVALID_STRING_LITERAL_ERRORTOK:
         case ERRORTOK:
-        case EOFTOK: 
+        case EOFTOK:
             return 0;
         case LastUntaggedToken: 
             break;
@@ -734,7 +744,36 @@ private:
         case STRING: 
             m_errorMessage = "Unexpected string " + getToken();
             return;
-        case ERRORTOK: 
+            
+        case UNTERMINATED_IDENTIFIER_ESCAPE_ERRORTOK:
+        case UNTERMINATED_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK:
+            m_errorMessage = "Incomplete unicode escape in identifier: '" + getToken() + '\'';
+            return;
+        case UNTERMINATED_MULTILINE_COMMENT_ERRORTOK:
+            m_errorMessage = "Unterminated multiline comment";
+            return;
+        case UNTERMINATED_NUMERIC_LITERAL_ERRORTOK:
+            m_errorMessage = "Unterminated numeric literal '" + getToken() + '\'';
+            return;
+        case UNTERMINATED_STRING_LITERAL_ERRORTOK:
+            m_errorMessage = "Unterminated string literal '" + getToken() + '\'';
+            return;
+        case INVALID_IDENTIFIER_ESCAPE_ERRORTOK:
+            m_errorMessage = "Invalid escape in identifier: '" + getToken() + '\'';
+            return;
+        case INVALID_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK:
+            m_errorMessage = "Invalid unicode escape in identifier: '" + getToken() + '\'';
+            return;
+        case INVALID_NUMERIC_LITERAL_ERRORTOK:
+            m_errorMessage = "Invalid numeric literal: '" + getToken() + '\'';
+            return;
+        case INVALID_OCTAL_NUMBER_ERRORTOK:
+            m_errorMessage = "Invalid use of octal: '" + getToken() + '\'';
+                return;
+        case INVALID_STRING_LITERAL_ERRORTOK:
+            m_errorMessage = "Invalid string literal: '" + getToken() + '\'';
+            return;
+        case ERRORTOK:
             m_errorMessage = "Unrecognized token '" + getToken() + '\'';
             return;
         case EOFTOK:  
@@ -994,11 +1033,19 @@ PassRefPtr<ParsedNode> Parser<LexerType>::parse(ParserError& error)
         // code we assume that it was a syntax error since running out of stack is much less
         // likely, and we are currently unable to distinguish between the two cases.
         if (isFunctionBodyNode(static_cast<ParsedNode*>(0)) || m_hasStackOverflow)
-            error = ParserError::StackOverflow;
-        else if (isEvalNode<ParsedNode>())
-            error = ParserError(ParserError::EvalError, errMsg, errLine);
-        else
-            error = ParserError(ParserError::SyntaxError, errMsg, errLine);
+            error = ParserError(ParserError::StackOverflow, ParserError::SyntaxErrorNone, m_token);
+        else {
+            ParserError::SyntaxErrorType errorType = ParserError::SyntaxErrorIrrecoverable;
+            if (m_token.m_type == EOFTOK)
+                errorType = ParserError::SyntaxErrorRecoverable;
+            else if (m_token.m_type & UnterminatedErrorTokenFlag)
+                errorType = ParserError::SyntaxErrorUnterminatedLiteral;
+            
+            if (isEvalNode<ParsedNode>())
+                error = ParserError(ParserError::EvalError, errorType, m_token, errMsg, errLine);
+            else
+                error = ParserError(ParserError::SyntaxError, errorType, m_token, errMsg, errLine);
+        }
     }
 
     m_arena->reset();
index d0a349f..1ec356e 100644 (file)
 
 #include "Error.h"
 #include "ExceptionHelpers.h"
+#include "ParserTokens.h"
 #include <wtf/text/WTFString.h>
 
 namespace JSC {
 
 struct ParserError {
-    enum ErrorType { ErrorNone, StackOverflow, SyntaxError, EvalError, OutOfMemory } m_type;
+    enum SyntaxErrorType {
+        SyntaxErrorNone,
+        SyntaxErrorIrrecoverable,
+        SyntaxErrorUnterminatedLiteral,
+        SyntaxErrorRecoverable
+    };
+
+    enum ErrorType {
+        ErrorNone,
+        StackOverflow,
+        EvalError,
+        OutOfMemory,
+        SyntaxError
+    };
+
+    ErrorType m_type;
+    SyntaxErrorType m_syntaxErrorType;
+    JSToken m_token;
     String m_message;
     int m_line;
     ParserError()
@@ -41,15 +59,26 @@ struct ParserError {
         , m_line(-1)
     {
     }
+    
+    explicit ParserError(ErrorType type)
+        : m_type(type)
+        , m_syntaxErrorType(SyntaxErrorNone)
+        , m_line(-1)
+    {
+    }
 
-    ParserError(ErrorType type)
+    ParserError(ErrorType type, SyntaxErrorType syntaxError, JSToken token)
         : m_type(type)
+        , m_syntaxErrorType(syntaxError)
+        , m_token(token)
         , m_line(-1)
     {
     }
 
-    ParserError(ErrorType type, String msg, int line)
+    ParserError(ErrorType type, SyntaxErrorType syntaxError, JSToken token, String msg, int line)
         : m_type(type)
+        , m_syntaxErrorType(syntaxError)
+        , m_token(token)
         , m_message(msg)
         , m_line(line)
     {
@@ -72,6 +101,7 @@ struct ParserError {
         CRASH();
         return createOutOfMemoryError(globalObject); // Appease Qt bot
     }
+#undef GET_ERROR_CODE
 };
 
 } // namespace JSC
index cea5159..0a335fd 100644 (file)
@@ -38,6 +38,8 @@ enum {
     BinaryOpTokenPrecedenceShift = 8,
     BinaryOpTokenAllowsInPrecedenceAdditionalShift = 4,
     BinaryOpTokenPrecedenceMask = 15 << BinaryOpTokenPrecedenceShift,
+    ErrorTokenFlag = 1 << (BinaryOpTokenAllowsInPrecedenceAdditionalShift + BinaryOpTokenPrecedenceShift + 7),
+    UnterminatedErrorTokenFlag = ErrorTokenFlag << 1
 };
 
 #define BINARY_OP_PRECEDENCE(prec) (((prec) << BinaryOpTokenPrecedenceShift) | ((prec) << (BinaryOpTokenPrecedenceShift + BinaryOpTokenAllowsInPrecedenceAdditionalShift)))
@@ -85,7 +87,6 @@ enum JSTokenType {
     SEMICOLON,
     COLON,
     DOT,
-    ERRORTOK,
     EOFTOK,
     EQUAL,
     PLUSEQUAL,
@@ -133,7 +134,18 @@ enum JSTokenType {
     MINUS = 19 | BINARY_OP_PRECEDENCE(9) | UnaryOpTokenFlag,
     TIMES = 20 | BINARY_OP_PRECEDENCE(10),
     DIVIDE = 21 | BINARY_OP_PRECEDENCE(10),
-    MOD = 22 | BINARY_OP_PRECEDENCE(10)
+    MOD = 22 | BINARY_OP_PRECEDENCE(10),
+    ERRORTOK = 0 | ErrorTokenFlag,
+    UNTERMINATED_IDENTIFIER_ESCAPE_ERRORTOK = 0 | ErrorTokenFlag | UnterminatedErrorTokenFlag,
+    INVALID_IDENTIFIER_ESCAPE_ERRORTOK = 1 | ErrorTokenFlag,
+    UNTERMINATED_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK = 2 | ErrorTokenFlag | UnterminatedErrorTokenFlag,
+    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,
+    INVALID_NUMERIC_LITERAL_ERRORTOK = 7 | ErrorTokenFlag,
+    UNTERMINATED_STRING_LITERAL_ERRORTOK = 8 | ErrorTokenFlag | UnterminatedErrorTokenFlag,
+    INVALID_STRING_LITERAL_ERRORTOK = 9 | ErrorTokenFlag,
 };
 
 union JSTokenData {
index 2f0a96a..8d6a5b0 100644 (file)
@@ -51,6 +51,15 @@ bool checkSyntax(ExecState* exec, const SourceCode& source, JSValue* returnedExc
 
     return true;
 }
+    
+bool checkSyntax(ExecState* exec, const SourceCode& source, ParserError& error)
+{
+    JSLockHolder lock(exec);
+    RELEASE_ASSERT(exec->vm().identifierTable == wtfThreadData().currentIdentifierTable());
+    VM* vm = &exec->vm();
+    RefPtr<ProgramNode> programNode = parse<ProgramNode>(vm, source, 0, Identifier(), JSParseNormal, JSParseProgramCode, error);
+    return programNode;
+}
 
 JSValue evaluate(ExecState* exec, const SourceCode& source, JSValue thisValue, JSValue* returnedException)
 {
index 5cc2d32..33a954d 100644 (file)
 #include "JSCJSValue.h"
 
 namespace JSC {
-
+    
+    struct ParserError;
     class ExecState;
     class JSScope;
     class SourceCode;
 
+    JS_EXPORT_PRIVATE bool checkSyntax(ExecState*, const SourceCode&, ParserError&);
     JS_EXPORT_PRIVATE bool checkSyntax(ExecState*, const SourceCode&, JSValue* exception = 0);
     JS_EXPORT_PRIVATE JSValue evaluate(ExecState*, const SourceCode&, JSValue thisValue = JSValue(), JSValue* exception = 0);