JSON errors should be informative
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 25 Jul 2011 18:41:40 +0000 (18:41 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 25 Jul 2011 18:41:40 +0000 (18:41 +0000)
https://bugs.webkit.org/show_bug.cgi?id=63339

Source/JavaScriptCore:

Added error messages to the JSON Parser.

Patch by Juan C. Montemayor <jmont@apple.com> on 2011-07-25
Reviewed by Oliver Hunt.

* runtime/JSONObject.cpp:
(JSC::JSONProtoFuncParse):
* runtime/LiteralParser.cpp:
(JSC::LiteralParser::Lexer::lex):
(JSC::LiteralParser::Lexer::lexString):
(JSC::LiteralParser::Lexer::lexNumber):
(JSC::LiteralParser::parse):
* runtime/LiteralParser.h:
(JSC::LiteralParser::getErrorMessage):
(JSC::LiteralParser::Lexer::sawError):
(JSC::LiteralParser::Lexer::getErrorMessage):

LayoutTests:

Updated expected test results.

Patch by Juan C. Montemayor <jmont@apple.com> on 2011-07-25
Reviewed by Oliver Hunt.

* fast/js/JSON-parse-expected.txt:
* platform/chromium/fast/js/JSON-parse-expected.txt: Added.

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

LayoutTests/ChangeLog
LayoutTests/fast/js/JSON-parse-expected.txt
LayoutTests/platform/chromium/fast/js/JSON-parse-expected.txt [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/JSONObject.cpp
Source/JavaScriptCore/runtime/LiteralParser.cpp
Source/JavaScriptCore/runtime/LiteralParser.h

index 24e8650..5b0649f 100644 (file)
@@ -1,3 +1,15 @@
+2011-07-25  Juan C. Montemayor  <jmont@apple.com>
+
+        JSON errors should be informative
+        https://bugs.webkit.org/show_bug.cgi?id=63339
+
+        Updated expected test results.
+
+        Reviewed by Oliver Hunt.
+
+        * fast/js/JSON-parse-expected.txt:
+        * platform/chromium/fast/js/JSON-parse-expected.txt: Added.
+
 2011-07-25  Adrienne Walker  <enne@google.com>
 
         [chromium] Mark media/remove-from-document as another flaky crasher.
index 9362954..92d8fc3 100644 (file)
@@ -5,7 +5,7 @@ PASS tests[i](nativeJSON) threw exception Error: JSON.parse requires at least on
 function (jsonObject) {
         return jsonObject.parse('');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Unexpected EOF.
 function (jsonObject) {
         return jsonObject.parse('1');
     }
@@ -17,11 +17,11 @@ PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
 function (jsonObject) {
         return jsonObject.parse('Infinity');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Unexpected identifier "Infinity?".
 function (jsonObject) {
         return jsonObject.parse('NaN');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Unexpected identifier "NaN".
 function (jsonObject) {
         return jsonObject.parse('null');
     }
@@ -29,7 +29,7 @@ PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
 function (jsonObject) {
         return jsonObject.parse('undefined');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Unexpected identifier "undefined".
 function (jsonObject) {
         return jsonObject.parse('{}');
     }
@@ -37,31 +37,31 @@ PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
 function (jsonObject) {
         return jsonObject.parse('({})');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Unexpected token '('.
 function (jsonObject) {
         return jsonObject.parse('{a}');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Expected '}'.
 function (jsonObject) {
         return jsonObject.parse('{a:}');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Expected '}'.
 function (jsonObject) {
         return jsonObject.parse('{a:5}');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Expected '}'.
 function (jsonObject) {
         return jsonObject.parse('{a:5,}');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Expected '}'.
 function (jsonObject) {
         return jsonObject.parse('{"a"}');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Expected ':' before value in object property definition.
 function (jsonObject) {
         return jsonObject.parse('{"a":}');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Unexpected token '}'.
 function (jsonObject) {
         return jsonObject.parse('{"a":5}');
     }
@@ -73,20 +73,20 @@ PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
 function (jsonObject) {
         return jsonObject.parse('{"a":5,}');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Property name must be a string literal.
 json2.js did not throw for a test we expect to throw.
 function (jsonObject) {
         return jsonObject.parse('{"a":5,,}');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Property name must be a string literal.
 function (jsonObject) {
         return jsonObject.parse('{"a":5,"a",}');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Expected ':'.
 function (jsonObject) {
         return jsonObject.parse('{"a":(5,"a"),}');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Unexpected token '('.
 function (jsonObject) {
         return jsonObject.parse('[]');
     }
@@ -98,7 +98,7 @@ PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
 function (jsonObject) {
         return jsonObject.parse('[1,]');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Unexpected comma at the end of array expression.
 json2.js did not throw for a test we expect to throw.
 function (jsonObject) {
         return jsonObject.parse('[1,2]');
@@ -107,12 +107,12 @@ PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
 function (jsonObject) {
         return jsonObject.parse('[1,2,,]');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Unexpected token ','.
 json2.js did not throw for a test we expect to throw.
 function (jsonObject) {
         return jsonObject.parse('[1,2,,4]');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Unexpected token ','.
 json2.js did not throw for a test we expect to throw.
 function (jsonObject) {
         return jsonObject.parse('""');
@@ -129,15 +129,15 @@ PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
 function (jsonObject) {
         return jsonObject.parse('"a\\"');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Unterminated string.
 function (jsonObject) {
         return jsonObject.parse('"a\\z"');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Invalid escape character z.
 function (jsonObject) {
         return jsonObject.parse('"a\\\z"');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Invalid escape character z.
 function (jsonObject) {
         return jsonObject.parse('"a\\\\z"');
     }
@@ -153,7 +153,7 @@ PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
 function (jsonObject) {
         return jsonObject.parse('"a\nz"');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Unterminated string.
 function (jsonObject) {
         return jsonObject.parse('"a\\nz"');
     }
@@ -161,7 +161,7 @@ PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
 function (jsonObject) {
         return jsonObject.parse('"a\rz"');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Unterminated string.
 function (jsonObject) {
         return jsonObject.parse('"a\\rz"');
     }
@@ -177,7 +177,7 @@ PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
 function (jsonObject) {
         return jsonObject.parse('"a\bz"');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Unterminated string.
 json2.js did not throw for a test we expect to throw.
 function (jsonObject) {
         return jsonObject.parse('"a\\bz"');
@@ -186,7 +186,7 @@ PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
 function (jsonObject) {
         return jsonObject.parse('"a\rz"');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Unterminated string.
 function (jsonObject) {
         return jsonObject.parse('"a\\rz"');
     }
@@ -194,19 +194,19 @@ PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
 function (jsonObject) {
         return jsonObject.parse('"a\\uz"     ');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: "\uz"  " is not a valid unicode escape.
 function (jsonObject) {
         return jsonObject.parse('"a\\u0z"    ');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: "\u0z" " is not a valid unicode escape.
 function (jsonObject) {
         return jsonObject.parse('"a\\u00z"   ');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: "\u00z"" is not a valid unicode escape.
 function (jsonObject) {
         return jsonObject.parse('"a\\u000z"  ');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: "\u000z" is not a valid unicode escape.
 function (jsonObject) {
         return jsonObject.parse('"a\\u0000z" ');
     }
@@ -222,11 +222,11 @@ PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
 function (jsonObject) {
         return jsonObject.parse('"a\\u000Gz" ');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: "\u000G" is not a valid unicode escape.
 function (jsonObject) {
         return jsonObject.parse('"a\\u000gz" ');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: "\u000g" is not a valid unicode escape.
 function (jsonObject) {
         return jsonObject.parse('"a\\u00A0z" ');
     }
@@ -238,11 +238,11 @@ PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
 function (jsonObject) {
         return jsonObject.parse('"a\\u00G0z" ');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: "\u00G0" is not a valid unicode escape.
 function (jsonObject) {
         return jsonObject.parse('"a\\u00g0z" ');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: "\u00g0" is not a valid unicode escape.
 function (jsonObject) {
         return jsonObject.parse('"a\\u0A00z" ');
     }
@@ -254,11 +254,11 @@ PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
 function (jsonObject) {
         return jsonObject.parse('"a\\u0G00z" ');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: "\u0G00" is not a valid unicode escape.
 function (jsonObject) {
         return jsonObject.parse('"a\\u0g00z" ');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: "\u0g00" is not a valid unicode escape.
 function (jsonObject) {
         return jsonObject.parse('"a\\uA000z" ');
     }
@@ -270,71 +270,71 @@ PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
 function (jsonObject) {
         return jsonObject.parse('"a\\uG000z" ');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: "\uG000" is not a valid unicode escape.
 function (jsonObject) {
         return jsonObject.parse('"a\\ug000z" ');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: "\ug000" is not a valid unicode escape.
 function (jsonObject) {
         return jsonObject.parse('00');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Unable to parse JSON string.
 json2.js did not throw for a test we expect to throw.
 function (jsonObject) {
         return jsonObject.parse('01');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Unable to parse JSON string.
 json2.js did not throw for a test we expect to throw.
 function (jsonObject) {
         return jsonObject.parse('0.a');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Invalid digits after decimal point.
 function (jsonObject) {
         return jsonObject.parse('0x0');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Unable to parse JSON string.
 function (jsonObject) {
         return jsonObject.parse('2e1.3');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Unable to parse JSON string.
 function (jsonObject) {
         return jsonObject.parse('2e-+10');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Exponent symbols should be followed by an optional '+' or '-' and then by at least one number.
 function (jsonObject) {
         return jsonObject.parse('2e+-10');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Exponent symbols should be followed by an optional '+' or '-' and then by at least one number.
 function (jsonObject) {
         return jsonObject.parse('2e3e4');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Unable to parse JSON string.
 function (jsonObject) {
         return jsonObject.parse('-01.0');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Unable to parse JSON string.
 function (jsonObject) {
         return jsonObject.parse('-01');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Unable to parse JSON string.
 json2.js did not throw for a test we expect to throw.
 function (jsonObject) {
         return jsonObject.parse('-01.a');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Invalid digits after decimal point.
 function (jsonObject) {
         return jsonObject.parse('1.e1');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Invalid digits after decimal point.
 json2.js did not throw for a test we expect to throw.
 function (jsonObject) {
         return jsonObject.parse('{/* block comments are not allowed */}');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Unrecognized token '/'.
 function (jsonObject) {
         return jsonObject.parse('{// line comments are not allowed \n}');
     }
-PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Unrecognized token '/'.
 function (jsonObject) {
         return jsonObject.parse('true');
     }
diff --git a/LayoutTests/platform/chromium/fast/js/JSON-parse-expected.txt b/LayoutTests/platform/chromium/fast/js/JSON-parse-expected.txt
new file mode 100644 (file)
index 0000000..9362954
--- /dev/null
@@ -0,0 +1,602 @@
+function (jsonObject) {
+        return jsonObject.parse();
+    }
+PASS tests[i](nativeJSON) threw exception Error: JSON.parse requires at least one parameter.
+function (jsonObject) {
+        return jsonObject.parse('');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('1');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('-1');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('Infinity');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('NaN');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('null');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('undefined');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('{}');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('({})');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('{a}');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('{a:}');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('{a:5}');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('{a:5,}');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('{"a"}');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('{"a":}');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('{"a":5}');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('{"__proto__":5}');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('{"a":5,}');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+json2.js did not throw for a test we expect to throw.
+function (jsonObject) {
+        return jsonObject.parse('{"a":5,,}');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('{"a":5,"a",}');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('{"a":(5,"a"),}');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('[]');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('[1]');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('[1,]');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+json2.js did not throw for a test we expect to throw.
+function (jsonObject) {
+        return jsonObject.parse('[1,2]');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('[1,2,,]');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+json2.js did not throw for a test we expect to throw.
+function (jsonObject) {
+        return jsonObject.parse('[1,2,,4]');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+json2.js did not throw for a test we expect to throw.
+function (jsonObject) {
+        return jsonObject.parse('""');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('"\'"');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('"a\"');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('"a\\"');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('"a\\z"');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('"a\\\z"');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('"a\\\\z"');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('"a\tz"');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('"a\\tz"');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('"a\nz"');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('"a\\nz"');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('"a\rz"');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('"a\\rz"');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('"a\/z"');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('"a\\/z"');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('"a\bz"');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+json2.js did not throw for a test we expect to throw.
+function (jsonObject) {
+        return jsonObject.parse('"a\\bz"');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('"a\rz"');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('"a\\rz"');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('"a\\uz"     ');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('"a\\u0z"    ');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('"a\\u00z"   ');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('"a\\u000z"  ');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('"a\\u0000z" ');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('"a\\u000Az" ');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('"a\\u000az" ');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('"a\\u000Gz" ');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('"a\\u000gz" ');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('"a\\u00A0z" ');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('"a\\u00a0z" ');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('"a\\u00G0z" ');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('"a\\u00g0z" ');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('"a\\u0A00z" ');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('"a\\u0a00z" ');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('"a\\u0G00z" ');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('"a\\u0g00z" ');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('"a\\uA000z" ');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('"a\\ua000z" ');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('"a\\uG000z" ');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('"a\\ug000z" ');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('00');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+json2.js did not throw for a test we expect to throw.
+function (jsonObject) {
+        return jsonObject.parse('01');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+json2.js did not throw for a test we expect to throw.
+function (jsonObject) {
+        return jsonObject.parse('0.a');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('0x0');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('2e1.3');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('2e-+10');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('2e+-10');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('2e3e4');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('-01.0');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('-01');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+json2.js did not throw for a test we expect to throw.
+function (jsonObject) {
+        return jsonObject.parse('-01.a');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('1.e1');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+json2.js did not throw for a test we expect to throw.
+function (jsonObject) {
+        return jsonObject.parse('{/* block comments are not allowed */}');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('{// line comments are not allowed \n}');
+    }
+PASS tests[i](nativeJSON) threw exception SyntaxError: Unable to parse JSON string.
+function (jsonObject) {
+        return jsonObject.parse('true');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('false');
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse(JSON.stringify(simpleObject));
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is tests[i].expected
+function (jsonObject) {
+        return jsonObject.parse(JSON.stringify(complexObject));
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse(JSON.stringify(complexObject));
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is tests[i].expected
+function (jsonObject) {
+        return jsonObject.parse(JSON.stringify(simpleObject,null,100));
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is tests[i].expected
+function (jsonObject) {
+        return jsonObject.parse(JSON.stringify(complexObject,null,100));
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse(JSON.stringify(complexObject,null,100));
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is tests[i].expected
+function (jsonObject) {
+        return jsonObject.parse(JSON.stringify(simpleObject,null," "));
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is tests[i].expected
+function (jsonObject) {
+        return jsonObject.parse(JSON.stringify(complexObject,null," "));
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse(JSON.stringify(complexObject,null," "));
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is tests[i].expected
+function (jsonObject) {
+        return jsonObject.parse(JSON.stringify(simpleObject,null,"\t"));
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is tests[i].expected
+function (jsonObject) {
+        return jsonObject.parse(JSON.stringify(complexObject,null,"\t"));
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse(JSON.stringify(complexObject,null,"\t"));
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is tests[i].expected
+function (jsonObject) {
+        return jsonObject.parse(JSON.stringify(simpleObject,null,"\n"));
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is tests[i].expected
+function (jsonObject) {
+        return jsonObject.parse(JSON.stringify(complexObject,null,"\n"));
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is tests[i].expected
+function (jsonObject) {
+        return jsonObject.parse("true", log);
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse("false", log);
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse("null", log);
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse("1", log);
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse("1.5", log);
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('"a string"', log);
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse(JSON.stringify(simpleArray), log);
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse(JSON.stringify(complexArray), log);
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse(JSON.stringify(simpleObject), log);
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse(JSON.stringify(complexObject), log);
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse('{"__proto__":{"a":5}}', log);
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        logOrderString = "";
+        return jsonObject.parse("true", logOrder);
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        logOrderString = "";
+        return jsonObject.parse("false", logOrder);
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        logOrderString = "";
+        return jsonObject.parse("null", logOrder);
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        logOrderString = "";
+        return jsonObject.parse("1", logOrder);
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        logOrderString = "";
+        return jsonObject.parse("1.5", logOrder);
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        logOrderString = "";
+        return jsonObject.parse('"a string"', logOrder);
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        logOrderString = "";
+        return jsonObject.parse(JSON.stringify(simpleArray), logOrder);
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        logOrderString = "";
+        return jsonObject.parse(JSON.stringify(complexArray), logOrder);
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        logOrderString = "";
+        return jsonObject.parse(JSON.stringify(simpleObject), logOrder);
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        logOrderString = "";
+        return jsonObject.parse(JSON.stringify(complexObject), logOrder);
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        logOrderString = "";
+        jsonObject.parse("true", logOrder);
+        return logOrderString;
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        logOrderString = "";
+        jsonObject.parse("false", logOrder);
+        return logOrderString;
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        logOrderString = "";
+        jsonObject.parse("null", logOrder);
+        return logOrderString;
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        logOrderString = "";
+        jsonObject.parse("1", logOrder);
+        return logOrderString;
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        logOrderString = "";
+        jsonObject.parse("1.5", logOrder);
+        return logOrderString;
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        logOrderString = "";
+        jsonObject.parse('"a string"', logOrder);
+        return logOrderString;
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        logOrderString = "";
+        jsonObject.parse(JSON.stringify(simpleArray), logOrder);
+        return logOrderString;
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        logOrderString = "";
+        jsonObject.parse(JSON.stringify(complexArray), logOrder);
+        return logOrderString;
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        logOrderString = "";
+        jsonObject.parse(JSON.stringify(simpleObject), logOrder);
+        return logOrderString;
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        logOrderString = "";
+        jsonObject.parse(JSON.stringify(complexObject), logOrder);
+        return logOrderString;
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        callCount = 0;
+        logOrderString = "";
+        return jsonObject.parse(JSON.stringify(complexArray), throwAfterFifthCall);
+    }
+PASS tests[i](nativeJSON) threw exception from reviver.
+function (jsonObject) {
+        callCount = 0;
+        logOrderString = "";
+        return jsonObject.parse(JSON.stringify(simpleObject), throwAfterFifthCall);
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        callCount = 0;
+        logOrderString = "";
+        return jsonObject.parse(JSON.stringify(complexObject), throwAfterFifthCall);
+    }
+PASS tests[i](nativeJSON) threw exception from reviver.
+function (jsonObject) {
+        callCount = 0;
+        logOrderString = "";
+        try { jsonObject.parse(JSON.stringify(complexArray), throwAfterFifthCall); } catch (e) {}
+        return logOrderString;
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        callCount = 0;
+        logOrderString = "";
+        try { jsonObject.parse(JSON.stringify(simpleObject), throwAfterFifthCall); } catch (e) {}
+        return logOrderString;
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        callCount = 0;
+        logOrderString = "";
+        try { jsonObject.parse(JSON.stringify(complexObject), throwAfterFifthCall); } catch (e) {}
+        return logOrderString;
+    }
+PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
+function (jsonObject) {
+        return jsonObject.parse(JSON.stringify(unicode));
+    }
+PASS tests[i](nativeJSON) is tests[i].unstringifiedExpected
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
index 7137dbd..13eef69 100644 (file)
@@ -1,3 +1,24 @@
+2011-07-25  Juan C. Montemayor  <jmont@apple.com>
+
+        JSON errors should be informative
+        https://bugs.webkit.org/show_bug.cgi?id=63339
+
+        Added error messages to the JSON Parser.
+
+        Reviewed by Oliver Hunt.
+
+        * runtime/JSONObject.cpp:
+        (JSC::JSONProtoFuncParse):
+        * runtime/LiteralParser.cpp:
+        (JSC::LiteralParser::Lexer::lex):
+        (JSC::LiteralParser::Lexer::lexString):
+        (JSC::LiteralParser::Lexer::lexNumber):
+        (JSC::LiteralParser::parse):
+        * runtime/LiteralParser.h:
+        (JSC::LiteralParser::getErrorMessage):
+        (JSC::LiteralParser::Lexer::sawError):
+        (JSC::LiteralParser::Lexer::getErrorMessage):
+
 2011-07-25  Filip Pizlo  <fpizlo@apple.com>
 
         X86-64 assembler emits three instructions instead of two for certain
index 8d261a2..05bde28 100644 (file)
@@ -819,7 +819,7 @@ EncodedJSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState* exec)
     LiteralParser jsonParser(exec, source.characters(), source.length(), LiteralParser::StrictJSON);
     JSValue unfiltered = jsonParser.tryLiteralParse();
     if (!unfiltered)
-        return throwVMError(exec, createSyntaxError(exec, "Unable to parse JSON string"));
+        return throwVMError(exec, createSyntaxError(exec, jsonParser.getErrorMessage()));
     
     if (exec->argumentCount() < 2)
         return JSValue::encode(unfiltered);
index 715caae..c69ef87 100644 (file)
@@ -236,11 +236,14 @@ template <LiteralParser::ParserMode mode> LiteralParser::TokenType LiteralParser
             return TokIdentifier;
         }
         if (*m_ptr == '\'') {
-            if (mode == StrictJSON)
+            if (mode == StrictJSON) {
+                m_lexErrorMessage = "Single quotes (\') are not allowed in JSON";
                 return TokError;
+            }
             return lexString<mode, '\''>(token);
         }
     }
+    m_lexErrorMessage = String::format("Unrecognized token '%c'", *m_ptr).impl();
     return TokError;
 }
 
@@ -274,8 +277,10 @@ template <LiteralParser::ParserMode mode, UChar terminator> inline LiteralParser
             if (builder.isEmpty() && runStart < m_ptr)
                 builder.append(runStart, m_ptr - runStart);
             ++m_ptr;
-            if (m_ptr >= m_end)
+            if (m_ptr >= m_end) {
+                m_lexErrorMessage = "Unterminated string";
                 return TokError;
+            }
             switch (*m_ptr) {
                 case '"':
                     builder.append('"');
@@ -311,11 +316,15 @@ template <LiteralParser::ParserMode mode, UChar terminator> inline LiteralParser
                     break;
 
                 case 'u':
-                    if ((m_end - m_ptr) < 5) // uNNNN == 5 characters
+                    if ((m_end - m_ptr) < 5) { 
+                        m_lexErrorMessage = "\\u must be followed by 4 hex digits";
                         return TokError;
+                    } // uNNNN == 5 characters
                     for (int i = 1; i < 5; i++) {
-                        if (!isASCIIHexDigit(m_ptr[i]))
+                        if (!isASCIIHexDigit(m_ptr[i])) {
+                            m_lexErrorMessage = String::format("\"\\%s\" is not a valid unicode escape", UString(m_ptr, 5).ascii().data()).impl();
                             return TokError;
+                        }
                     }
                     builder.append(JSC::Lexer::convertUnicode(m_ptr[1], m_ptr[2], m_ptr[3], m_ptr[4]));
                     m_ptr += 5;
@@ -327,13 +336,16 @@ template <LiteralParser::ParserMode mode, UChar terminator> inline LiteralParser
                         m_ptr++;
                         break;
                     }
+                    m_lexErrorMessage = String::format("Invalid escape character %c", *m_ptr).impl();
                     return TokError;
             }
         }
     } while ((mode != NonStrictJSON) && m_ptr != runStart && (m_ptr < m_end) && *m_ptr != terminator);
 
-    if (m_ptr >= m_end || *m_ptr != terminator)
+    if (m_ptr >= m_end || *m_ptr != terminator) {
+        m_lexErrorMessage = "Unterminated string";
         return TokError;
+    }
 
     if (builder.isEmpty()) {
         token.stringBuffer = UString();
@@ -376,15 +388,19 @@ LiteralParser::TokenType LiteralParser::Lexer::lexNumber(LiteralParserToken& tok
         // [0-9]*
         while (m_ptr < m_end && isASCIIDigit(*m_ptr))
             ++m_ptr;
-    } else
+    } else {
+        m_lexErrorMessage = "Invalid number";
         return TokError;
+    }
 
     // ('.' [0-9]+)?
     if (m_ptr < m_end && *m_ptr == '.') {
         ++m_ptr;
         // [0-9]+
-        if (m_ptr >= m_end || !isASCIIDigit(*m_ptr))
+        if (m_ptr >= m_end || !isASCIIDigit(*m_ptr)) {
+            m_lexErrorMessage = "Invalid digits after decimal point";
             return TokError;
+        }
 
         ++m_ptr;
         while (m_ptr < m_end && isASCIIDigit(*m_ptr))
@@ -416,8 +432,10 @@ LiteralParser::TokenType LiteralParser::Lexer::lexNumber(LiteralParserToken& tok
             ++m_ptr;
 
         // [0-9]+
-        if (m_ptr >= m_end || !isASCIIDigit(*m_ptr))
+        if (m_ptr >= m_end || !isASCIIDigit(*m_ptr)) {
+            m_lexErrorMessage = "Exponent symbols should be followed by an optional '+' or '-' and then by at least one number";
             return TokError;
+        }
         
         ++m_ptr;
         while (m_ptr < m_end && isASCIIDigit(*m_ptr))
@@ -458,8 +476,10 @@ JSValue LiteralParser::parse(ParserState initialState)
             case DoParseArrayStartExpression: {
                 TokenType lastToken = m_lexer.currentToken().type;
                 if (m_lexer.next() == TokRBracket) {
-                    if (lastToken == TokComma)
+                    if (lastToken == TokComma) {
+                        m_parseErrorMessage = "Unexpected comma at the end of array expression";
                         return JSValue();
+                    }
                     m_lexer.next();
                     lastValue = objectStack.last();
                     objectStack.removeLast();
@@ -475,8 +495,10 @@ JSValue LiteralParser::parse(ParserState initialState)
                 if (m_lexer.currentToken().type == TokComma)
                     goto doParseArrayStartExpression;
 
-                if (m_lexer.currentToken().type != TokRBracket)
+                if (m_lexer.currentToken().type != TokRBracket) {
+                    m_parseErrorMessage = "Expected ']'";
                     return JSValue();
+                }
                 
                 m_lexer.next();
                 lastValue = objectStack.last();
@@ -493,16 +515,20 @@ JSValue LiteralParser::parse(ParserState initialState)
                     Lexer::LiteralParserToken identifierToken = m_lexer.currentToken();
 
                     // Check for colon
-                    if (m_lexer.next() != TokColon)
+                    if (m_lexer.next() != TokColon) {
+                        m_parseErrorMessage = "Expected ':' before value in object property definition";
                         return JSValue();
+                    }
                     
                     m_lexer.next();
                     identifierStack.append(makeIdentifier(identifierToken.stringToken, identifierToken.stringLength));
                     stateStack.append(DoParseObjectEndExpression);
                     goto startParseExpression;
                 }
-                if (type != TokRBrace) 
+                if (type != TokRBrace)  {
+                    m_parseErrorMessage = "Expected '}'";
                     return JSValue();
+                }
                 m_lexer.next();
                 lastValue = objectStack.last();
                 objectStack.removeLast();
@@ -511,13 +537,17 @@ JSValue LiteralParser::parse(ParserState initialState)
             doParseObjectStartExpression:
             case DoParseObjectStartExpression: {
                 TokenType type = m_lexer.next();
-                if (type != TokString && (m_mode == StrictJSON || type != TokIdentifier))
+                if (type != TokString && (m_mode == StrictJSON || type != TokIdentifier)) {
+                    m_parseErrorMessage = "Property name must be a string literal";
                     return JSValue();
+                }
                 Lexer::LiteralParserToken identifierToken = m_lexer.currentToken();
 
                 // Check for colon
-                if (m_lexer.next() != TokColon)
+                if (m_lexer.next() != TokColon) {
+                    m_parseErrorMessage = "Expected ':'";
                     return JSValue();
+                }
 
                 m_lexer.next();
                 identifierStack.append(makeIdentifier(identifierToken.stringToken, identifierToken.stringLength));
@@ -530,8 +560,10 @@ JSValue LiteralParser::parse(ParserState initialState)
                 identifierStack.removeLast();
                 if (m_lexer.currentToken().type == TokComma)
                     goto doParseObjectStartExpression;
-                if (m_lexer.currentToken().type != TokRBrace)
+                if (m_lexer.currentToken().type != TokRBrace) {
+                    m_parseErrorMessage = "Expected '}'";
                     return JSValue();
+                }
                 m_lexer.next();
                 lastValue = objectStack.last();
                 objectStack.removeLast();
@@ -570,9 +602,43 @@ JSValue LiteralParser::parse(ParserState initialState)
                         m_lexer.next();
                         lastValue = jsBoolean(false);
                         break;
-
+                    case TokRBracket:
+                        m_parseErrorMessage = "Unexpected token ']'";
+                        return JSValue();
+                    case TokRBrace:
+                        m_parseErrorMessage = "Unexpected token '}'";
+                        return JSValue();
+                    case TokIdentifier:
+                        m_parseErrorMessage = String::format("Unexpected identifier \"%s\"", UString(m_lexer.currentToken().stringToken).ascii().data()).impl();
+                        return JSValue();
+                    case TokColon:
+                        m_parseErrorMessage = "Unexpected token ':'";
+                        return JSValue();
+                    case TokLParen:
+                        m_parseErrorMessage = "Unexpected token '('";
+                        return JSValue();
+                    case TokRParen:
+                        m_parseErrorMessage = "Unexpected token ')'";
+                        return JSValue();
+                    case TokComma:
+                        m_parseErrorMessage = "Unexpected token ','";
+                        return JSValue();
+                    case TokDot:
+                        m_parseErrorMessage = "Unexpected token '.'";
+                        return JSValue();
+                    case TokAssign:
+                        m_parseErrorMessage = "Unexpected token '='";
+                        return JSValue();
+                    case TokSemi:
+                        m_parseErrorMessage = "Unexpected token ';'";
+                        return JSValue();
+                    case TokEnd:
+                        m_parseErrorMessage = "Unexpected EOF";
+                        return JSValue();
+                    case TokError:
                     default:
                         // Error
+                        m_parseErrorMessage = "Could not parse value expression";
                         return JSValue();
                 }
                 break;
@@ -589,7 +655,51 @@ JSValue LiteralParser::parse(ParserState initialState)
                         stateStack.append(StartParseStatementEndStatement);
                         goto startParseExpression;
                     }
+                    case TokRBracket:
+                        m_parseErrorMessage = "Unexpected token ']'";
+                        return JSValue();
+                    case TokLBrace:
+                        m_parseErrorMessage = "Unexpected token '{'";
+                        return JSValue();
+                    case TokRBrace:
+                        m_parseErrorMessage = "Unexpected token '}'";
+                        return JSValue();
+                    case TokIdentifier:
+                        m_parseErrorMessage = String::format("Unexpected identifier \"%s\"", UString(m_lexer.currentToken().stringToken).ascii().data()).impl();
+                        return JSValue();
+                    case TokColon:
+                        m_parseErrorMessage = "Unexpected token ':'";
+                        return JSValue();
+                    case TokRParen:
+                        m_parseErrorMessage = "Unexpected token ')'";
+                        return JSValue();
+                    case TokComma:
+                        m_parseErrorMessage = "Unexpected token ','";
+                        return JSValue();
+                    case TokTrue:
+                        m_parseErrorMessage = "Unexpected token 'true'";
+                        return JSValue();
+                    case TokFalse:
+                        m_parseErrorMessage = "Unexpected token 'false'";
+                        return JSValue();
+                    case TokNull:
+                        m_parseErrorMessage = "Unexpected token 'null'";
+                        return JSValue();
+                    case TokEnd:
+                        m_parseErrorMessage = "Unexpected EOF";
+                        return JSValue();
+                    case TokDot:
+                        m_parseErrorMessage = "Unexpected token '.'";
+                        return JSValue();
+                    case TokAssign:
+                        m_parseErrorMessage = "Unexpected token '='";
+                        return JSValue();
+                    case TokSemi:
+                        m_parseErrorMessage = "Unexpected token ';'";
+                        return JSValue();
+                    case TokError:
                     default:
+                        m_parseErrorMessage = "Could not parse statement";
                         return JSValue();
                 }
             }
@@ -599,6 +709,7 @@ JSValue LiteralParser::parse(ParserState initialState)
                     return JSValue();
                 if (m_lexer.next() == TokEnd)
                     return lastValue;
+                m_parseErrorMessage = "Unexpected content at end of JSON literal";
                 return JSValue();
             }
             default:
index 85e0311..4eb8918 100644 (file)
@@ -43,6 +43,14 @@ namespace JSC {
         {
         }
         
+        UString getErrorMessage() 
+        { 
+            if (m_parseErrorMessage.isEmpty())
+                m_parseErrorMessage = "Unable to parse JSON string";
+            UString errorMessage = m_lexer.sawError() ?  m_lexer.getErrorMessage() : m_parseErrorMessage;
+            return String::format("JSON Parse error: %s", errorMessage.ascii().data()).impl();
+        }
+        
         JSValue tryLiteralParse()
         {
             m_lexer.next();
@@ -112,7 +120,11 @@ namespace JSC {
                 return m_currentToken;
             }
             
+            bool sawError() { return !m_lexErrorMessage.isEmpty(); }
+            UString getErrorMessage() { return m_lexErrorMessage; }
+            
         private:
+            UString m_lexErrorMessage;
             template <ParserMode mode> TokenType lex(LiteralParserToken&);
             template <ParserMode mode, UChar terminator> ALWAYS_INLINE TokenType lexString(LiteralParserToken&);
             ALWAYS_INLINE TokenType lexNumber(LiteralParserToken&);
@@ -129,6 +141,7 @@ namespace JSC {
         ExecState* m_exec;
         LiteralParser::Lexer m_lexer;
         ParserMode m_mode;
+        UString m_parseErrorMessage;
         static unsigned const MaximumCachableCharacter = 128;
         FixedArray<Identifier, MaximumCachableCharacter> m_shortIdentifiers;
         FixedArray<Identifier, MaximumCachableCharacter> m_recentIdentifiers;