JSON.parse errors made user-friendly

Part of the improve error messages initiative.

Based on a resource of JSON.parse() errors found at
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/JSON_bad_parse

Previously JSON.parse(NaN) would output:
SyntaxError: Unexpected token N in JSON at position 0
Now the output is:
SyntaxError: "NaN" is not valid JSON

Previously JSON.parse("{a:1}") would output:
SyntaxError: Unexpected token a in JSON at position 1
Now the output is:
SyntaxError: Expected property name or '}' in JSON at position 1

Bug: v8:6551
Change-Id: Ic9fad1fdbd295e1302805b81e6603fc526121960
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3513684
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Commit-Queue: Issack John <issackjohn@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#80567}
This commit is contained in:
Issack John 2022-05-16 08:53:06 -07:00 committed by V8 LUCI CQ
parent b9420be7a2
commit 718f743750
7 changed files with 308 additions and 61 deletions

View File

@ -481,9 +481,40 @@ namespace internal {
T(InvalidUnusedPrivateStaticMethodAccessedByDebugger, \
"Unused static private method '%' cannot be accessed at debug time") \
T(JsonParseUnexpectedEOS, "Unexpected end of JSON input") \
T(JsonParseUnexpectedToken, "Unexpected token % in JSON at position %") \
T(JsonParseUnexpectedTokenNumber, "Unexpected number in JSON at position %") \
T(JsonParseUnexpectedTokenString, "Unexpected string in JSON at position %") \
T(JsonParseUnterminatedString, "Unterminated string in JSON at position %") \
T(JsonParseExpectedPropNameOrRBrace, \
"Expected property name or '}' in JSON at position %") \
T(JsonParseExpectedCommaOrRBrack, \
"Expected ',' or ']' after array element in JSON at position %") \
T(JsonParseExpectedCommaOrRBrace, \
"Expected ',' or '}' after property value in JSON at position " \
"%") \
T(JsonParseExpectedDoubleQuotedPropertyName, \
"Expected double-quoted property name in JSON at position %") \
T(JsonParseExponentPartMissingNumber, \
"Exponent part is missing a number in JSON at position %") \
T(JsonParseExpectedColonAfterPropertyName, \
"Expected ':' after property name in JSON at position %") \
T(JsonParseUnterminatedFractionalNumber, \
"Unterminated fractional number in JSON at position %") \
T(JsonParseUnexpectedNonWhiteSpaceCharacter, \
"Unexpected non-whitespace character after JSON at position " \
"%") \
T(JsonParseBadEscapedCharacter, \
"Bad escaped character in JSON at position %") \
T(JsonParseNoNumberAfterMinusSign, \
"No number after minus sign in JSON at position %") \
T(JsonParseShortString, "\"%\" is not valid JSON") \
T(JsonParseUnexpectedTokenShortString, \
"Unexpected token '%', \"%\" is not valid JSON") \
T(JsonParseUnexpectedTokenSurroundStringWithContext, \
"Unexpected token '%', ...\"%\"... is not valid JSON") \
T(JsonParseUnexpectedTokenEndStringWithContext, \
"Unexpected token '%', ...\"%\" is not valid JSON") \
T(JsonParseUnexpectedTokenStartStringWithContext, \
"Unexpected token '%', \"%\"... is not valid JSON") \
T(LabelRedeclaration, "Label '%' has already been declared") \
T(LabelledFunctionDeclaration, \
"Labelled function declaration not allowed as the body of a control flow " \

View File

@ -240,20 +240,75 @@ JsonParser<Char>::JsonParser(Isolate* isolate, Handle<String> source)
}
template <typename Char>
void JsonParser<Char>::ReportUnexpectedToken(JsonToken token) {
// Some exception (for example stack overflow) is already pending.
if (isolate_->has_pending_exception()) return;
// Parse failed. Current character is the unexpected token.
Factory* factory = this->factory();
MessageTemplate message;
bool JsonParser<Char>::IsSpecialString() {
// The special cases are undefined, NaN, Infinity, and {} being passed to the
// parse method
int offset = original_source_->IsSlicedString()
? SlicedString::cast(*original_source_).offset()
: 0;
int pos = position() - offset;
Handle<Object> arg1 = Handle<Smi>(Smi::FromInt(pos), isolate());
Handle<Object> arg2;
size_t length = original_source_->length();
#define CASES(V) \
V("[object Object]") \
V("undefined") \
V("Infinity") \
V("NaN")
switch (length) {
#define CASE(n) \
case arraysize(n) - 1: \
return CompareCharsEqual(chars_ + offset, n, arraysize(n) - 1);
CASES(CASE)
default:
return false;
}
#undef CASE
#undef CASES
}
template <typename Char>
MessageTemplate JsonParser<Char>::GetErrorMessageWithEllipses(
Handle<Object>& arg, Handle<Object>& arg2, int pos) {
MessageTemplate message;
Factory* factory = this->factory();
arg = factory->LookupSingleCharacterStringFromCode(*cursor_);
int origin_source_length = original_source_->length();
// only provide context for strings with at least
// kMinOriginalSourceLengthForContext charcacters in length
if (origin_source_length >= kMinOriginalSourceLengthForContext) {
int substring_start = 0;
int substring_end = origin_source_length;
if (pos < kMaxContextCharacters) {
message =
MessageTemplate::kJsonParseUnexpectedTokenStartStringWithContext;
// Output the string followed by elipses
substring_end = pos + kMaxContextCharacters;
} else if (pos >= kMaxContextCharacters &&
pos < origin_source_length - kMaxContextCharacters) {
message =
MessageTemplate::kJsonParseUnexpectedTokenSurroundStringWithContext;
// Add context before and after position of bad token surrounded by
// elipses
substring_start = pos - kMaxContextCharacters;
substring_end = pos + kMaxContextCharacters;
} else {
message = MessageTemplate::kJsonParseUnexpectedTokenEndStringWithContext;
// Add ellipses followed by some context before bad token
substring_start = pos - kMaxContextCharacters;
}
arg2 =
factory->NewSubString(original_source_, substring_start, substring_end);
} else {
arg2 = original_source_;
// Output the entire string without ellipses but provide the token which
// was unexpected
message = MessageTemplate::kJsonParseUnexpectedTokenShortString;
}
return message;
}
template <typename Char>
MessageTemplate JsonParser<Char>::LookUpErrorMessageForJsonToken(
JsonToken token, Handle<Object>& arg, Handle<Object>& arg2, int pos) {
MessageTemplate message;
switch (token) {
case JsonToken::EOS:
message = MessageTemplate::kJsonParseUnexpectedEOS;
@ -265,11 +320,36 @@ void JsonParser<Char>::ReportUnexpectedToken(JsonToken token) {
message = MessageTemplate::kJsonParseUnexpectedTokenString;
break;
default:
message = MessageTemplate::kJsonParseUnexpectedToken;
arg2 = arg1;
arg1 = factory->LookupSingleCharacterStringFromCode(*cursor_);
break;
// Output entire string without ellipses and don't provide the token
// that was unexpected because it makes the error messages more confusing
if (IsSpecialString()) {
arg = original_source_;
message = MessageTemplate::kJsonParseShortString;
} else {
message = GetErrorMessageWithEllipses(arg, arg2, pos);
}
}
return message;
}
template <typename Char>
void JsonParser<Char>::ReportUnexpectedToken(
JsonToken token, base::Optional<MessageTemplate> errorMessage) {
// Some exception (for example stack overflow) is already pending.
if (isolate_->has_pending_exception()) return;
// Parse failed. Current character is the unexpected token.
Factory* factory = this->factory();
int offset = original_source_->IsSlicedString()
? SlicedString::cast(*original_source_).offset()
: 0;
int pos = position() - offset;
Handle<Object> arg(Smi::FromInt(pos), isolate());
Handle<Object> arg2;
MessageTemplate message =
errorMessage ? errorMessage.value()
: LookUpErrorMessageForJsonToken(token, arg, arg2, pos);
Handle<Script> script(factory->NewScript(original_source_));
if (isolate()->NeedsSourcePositionsForProfiling()) {
@ -290,7 +370,7 @@ void JsonParser<Char>::ReportUnexpectedToken(JsonToken token) {
// separated source file.
isolate()->debug()->OnCompileError(script);
MessageLocation location(script, pos, pos + 1);
isolate()->ThrowAt(factory->NewSyntaxError(message, arg1, arg2), &location);
isolate()->ThrowAt(factory->NewSyntaxError(message, arg, arg2), &location);
// Move the cursor to the end so we won't be able to proceed parsing.
cursor_ = end_;
@ -325,7 +405,9 @@ JsonParser<Char>::~JsonParser() {
template <typename Char>
MaybeHandle<Object> JsonParser<Char>::ParseJson() {
MaybeHandle<Object> result = ParseJsonValue();
if (!Check(JsonToken::EOS)) ReportUnexpectedToken(peek());
if (!Check(JsonToken::EOS))
ReportUnexpectedToken(
peek(), MessageTemplate::kJsonParseUnexpectedNonWhiteSpaceCharacter);
if (isolate_->has_pending_exception()) return MaybeHandle<Object>();
return result;
}
@ -751,10 +833,12 @@ MaybeHandle<Object> JsonParser<Char>::ParseJsonValue() {
property_stack.size());
// Parse the property key.
ExpectNext(JsonToken::STRING);
ExpectNext(JsonToken::STRING,
MessageTemplate::kJsonParseExpectedPropNameOrRBrace);
property_stack.emplace_back(ScanJsonPropertyKey(&cont));
ExpectNext(JsonToken::COLON);
ExpectNext(JsonToken::COLON,
MessageTemplate::kJsonParseExpectedColonAfterPropertyName);
// Continue to start producing the first property value.
continue;
@ -829,7 +913,9 @@ MaybeHandle<Object> JsonParser<Char>::ParseJsonValue() {
if (V8_LIKELY(Check(JsonToken::COMMA))) {
// Parse the property key.
ExpectNext(JsonToken::STRING);
ExpectNext(
JsonToken::STRING,
MessageTemplate::kJsonParseExpectedDoubleQuotedPropertyName);
property_stack.emplace_back(ScanJsonPropertyKey(&cont));
ExpectNext(JsonToken::COLON);
@ -855,7 +941,8 @@ MaybeHandle<Object> JsonParser<Char>::ParseJsonValue() {
}
value = BuildJsonObject(cont, property_stack, feedback);
property_stack.resize_no_init(cont.index);
Expect(JsonToken::RBRACE);
Expect(JsonToken::RBRACE,
MessageTemplate::kJsonParseExpectedCommaOrRBrace);
// Return the object.
value = cont.scope.CloseAndEscape(value);
@ -874,7 +961,8 @@ MaybeHandle<Object> JsonParser<Char>::ParseJsonValue() {
value = BuildJsonArray(cont, element_stack);
element_stack.resize_no_init(cont.index);
Expect(JsonToken::RBRACK);
Expect(JsonToken::RBRACK,
MessageTemplate::kJsonParseExpectedCommaOrRBrack);
// Return the array.
value = cont.scope.CloseAndEscape(value);
@ -933,7 +1021,9 @@ Handle<Object> JsonParser<Char>::ParseJsonNumber() {
AdvanceToNonDecimal();
if (V8_UNLIKELY(smi_start == cursor_)) {
AllowGarbageCollection allow_before_exception;
ReportUnexpectedCharacter(CurrentCharacter());
ReportUnexpectedToken(
JsonToken::ILLEGAL,
MessageTemplate::kJsonParseNoNumberAfterMinusSign);
return handle(Smi::FromInt(0), isolate_);
}
c = CurrentCharacter();
@ -959,7 +1049,9 @@ Handle<Object> JsonParser<Char>::ParseJsonNumber() {
c = NextCharacter();
if (!IsDecimalDigit(c)) {
AllowGarbageCollection allow_before_exception;
ReportUnexpectedCharacter(c);
ReportUnexpectedToken(
JsonToken::ILLEGAL,
MessageTemplate::kJsonParseUnterminatedFractionalNumber);
return handle(Smi::FromInt(0), isolate_);
}
AdvanceToNonDecimal();
@ -970,7 +1062,9 @@ Handle<Object> JsonParser<Char>::ParseJsonNumber() {
if (c == '-' || c == '+') c = NextCharacter();
if (!IsDecimalDigit(c)) {
AllowGarbageCollection allow_before_exception;
ReportUnexpectedCharacter(c);
ReportUnexpectedToken(
JsonToken::ILLEGAL,
MessageTemplate::kJsonParseExponentPartMissingNumber);
return handle(Smi::FromInt(0), isolate_);
}
AdvanceToNonDecimal();
@ -1137,7 +1231,8 @@ JsonString JsonParser<Char>::ScanJsonString(bool needs_internalization) {
if (V8_UNLIKELY(is_at_end())) {
AllowGarbageCollection allow_before_exception;
ReportUnexpectedCharacter(kEndOfString);
ReportUnexpectedToken(JsonToken::ILLEGAL,
MessageTemplate::kJsonParseUnterminatedString);
break;
}
@ -1188,7 +1283,8 @@ JsonString JsonParser<Char>::ScanJsonString(bool needs_internalization) {
case EscapeKind::kIllegal:
AllowGarbageCollection allow_before_exception;
ReportUnexpectedCharacter(c);
ReportUnexpectedToken(JsonToken::ILLEGAL,
MessageTemplate::kJsonParseBadEscapedCharacter);
return JsonString();
}

View File

@ -211,17 +211,21 @@ class JsonParser final {
advance();
}
void Expect(JsonToken token) {
void Expect(JsonToken token,
base::Optional<MessageTemplate> errorMessage = base::nullopt) {
if (V8_LIKELY(peek() == token)) {
advance();
} else {
ReportUnexpectedToken(peek());
errorMessage ? ReportUnexpectedToken(peek(), errorMessage.value())
: ReportUnexpectedToken(peek());
}
}
void ExpectNext(JsonToken token) {
void ExpectNext(
JsonToken token,
base::Optional<MessageTemplate> errorMessage = base::nullopt) {
SkipWhitespace();
Expect(token);
errorMessage ? Expect(token, errorMessage.value()) : Expect(token);
}
bool Check(JsonToken token) {
@ -301,10 +305,22 @@ class JsonParser final {
const JsonContinuation& cont,
const SmallVector<Handle<Object>>& element_stack);
static const int kMaxContextCharacters = 10;
static const int kMinOriginalSourceLengthForContext =
(kMaxContextCharacters * 2) + 1;
// Mark that a parsing error has happened at the current character.
void ReportUnexpectedCharacter(base::uc32 c);
bool IsSpecialString();
MessageTemplate GetErrorMessageWithEllipses(Handle<Object>& arg,
Handle<Object>& arg2, int pos);
MessageTemplate LookUpErrorMessageForJsonToken(JsonToken token,
Handle<Object>& arg,
Handle<Object>& arg2, int pos);
// Mark that a parsing error has happened at the current token.
void ReportUnexpectedToken(JsonToken token);
void ReportUnexpectedToken(
JsonToken token,
base::Optional<MessageTemplate> errorMessage = base::nullopt);
inline Isolate* isolate() { return isolate_; }
inline Factory* factory() { return isolate_->factory(); }

View File

@ -6,7 +6,7 @@ Test malformed sourceURL magic comment.
columnNumber : 0
exception : {
className : SyntaxError
description : SyntaxError: Unexpected token / in JSON at position 0 at JSON.parse (<anonymous>) at <anonymous>:1:6
description : SyntaxError: Unexpected token '/', "//" is not valid JSON at JSON.parse (<anonymous>) at <anonymous>:1:6
objectId : <objectId>
subtype : error
type : object
@ -18,7 +18,7 @@ Test malformed sourceURL magic comment.
}
result : {
className : SyntaxError
description : SyntaxError: Unexpected token / in JSON at position 0 at JSON.parse (<anonymous>) at <anonymous>:1:6
description : SyntaxError: Unexpected token '/', "//" is not valid JSON at JSON.parse (<anonymous>) at <anonymous>:1:6
objectId : <objectId>
subtype : error
type : object
@ -32,7 +32,7 @@ Test malformed sourceURL magic comment.
columnNumber : 0
exception : {
className : SyntaxError
description : SyntaxError: Unexpected token / in JSON at position 0 at JSON.parse (<anonymous>) at <anonymous>:1:6
description : SyntaxError: Unexpected token '/', "//#" is not valid JSON at JSON.parse (<anonymous>) at <anonymous>:1:6
objectId : <objectId>
subtype : error
type : object
@ -44,7 +44,7 @@ Test malformed sourceURL magic comment.
}
result : {
className : SyntaxError
description : SyntaxError: Unexpected token / in JSON at position 0 at JSON.parse (<anonymous>) at <anonymous>:1:6
description : SyntaxError: Unexpected token '/', "//#" is not valid JSON at JSON.parse (<anonymous>) at <anonymous>:1:6
objectId : <objectId>
subtype : error
type : object
@ -58,7 +58,7 @@ Test malformed sourceURL magic comment.
columnNumber : 0
exception : {
className : SyntaxError
description : SyntaxError: Unexpected token / in JSON at position 0 at JSON.parse (<anonymous>) at <anonymous>:1:6
description : SyntaxError: Unexpected token '/', "//# " is not valid JSON at JSON.parse (<anonymous>) at <anonymous>:1:6
objectId : <objectId>
subtype : error
type : object
@ -70,7 +70,7 @@ Test malformed sourceURL magic comment.
}
result : {
className : SyntaxError
description : SyntaxError: Unexpected token / in JSON at position 0 at JSON.parse (<anonymous>) at <anonymous>:1:6
description : SyntaxError: Unexpected token '/', "//# " is not valid JSON at JSON.parse (<anonymous>) at <anonymous>:1:6
objectId : <objectId>
subtype : error
type : object
@ -84,7 +84,7 @@ Test malformed sourceURL magic comment.
columnNumber : 0
exception : {
className : SyntaxError
description : SyntaxError: Unexpected token / in JSON at position 0 at JSON.parse (<anonymous>) at <anonymous>:1:6
description : SyntaxError: Unexpected token '/', "//# sourceURL" is not valid JSON at JSON.parse (<anonymous>) at <anonymous>:1:6
objectId : <objectId>
subtype : error
type : object
@ -96,7 +96,7 @@ Test malformed sourceURL magic comment.
}
result : {
className : SyntaxError
description : SyntaxError: Unexpected token / in JSON at position 0 at JSON.parse (<anonymous>) at <anonymous>:1:6
description : SyntaxError: Unexpected token '/', "//# sourceURL" is not valid JSON at JSON.parse (<anonymous>) at <anonymous>:1:6
objectId : <objectId>
subtype : error
type : object
@ -110,7 +110,7 @@ Test malformed sourceURL magic comment.
columnNumber : 0
exception : {
className : SyntaxError
description : SyntaxError: Unexpected token / in JSON at position 0 at JSON.parse (<anonymous>) at <anonymous>:1:6
description : SyntaxError: Unexpected token '/', "//# sourceURL=" is not valid JSON at JSON.parse (<anonymous>) at <anonymous>:1:6
objectId : <objectId>
subtype : error
type : object
@ -122,7 +122,7 @@ Test malformed sourceURL magic comment.
}
result : {
className : SyntaxError
description : SyntaxError: Unexpected token / in JSON at position 0 at JSON.parse (<anonymous>) at <anonymous>:1:6
description : SyntaxError: Unexpected token '/', "//# sourceURL=" is not valid JSON at JSON.parse (<anonymous>) at <anonymous>:1:6
objectId : <objectId>
subtype : error
type : object
@ -136,7 +136,7 @@ Test malformed sourceURL magic comment.
columnNumber : 0
exception : {
className : SyntaxError
description : SyntaxError: Unexpected token / in JSON at position 0 at JSON.parse (<anonymous>) at <anonymous>:1:6
description : SyntaxError: Unexpected token '/', "//# sourceURL="" is not valid JSON at JSON.parse (<anonymous>) at <anonymous>:1:6
objectId : <objectId>
subtype : error
type : object
@ -148,10 +148,10 @@ Test malformed sourceURL magic comment.
}
result : {
className : SyntaxError
description : SyntaxError: Unexpected token / in JSON at position 0 at JSON.parse (<anonymous>) at <anonymous>:1:6
description : SyntaxError: Unexpected token '/', "//# sourceURL="" is not valid JSON at JSON.parse (<anonymous>) at <anonymous>:1:6
objectId : <objectId>
subtype : error
type : object
}
}
}
}

View File

@ -1,4 +1,4 @@
undefined:1: SyntaxError: Unexpected token / in JSON at position 0
undefined:1: SyntaxError: Unexpected token '/', "// Copyrig"... is not valid JSON
// Copyright 2021 the V8 project authors. All rights reserved.
^
SyntaxError: Unexpected token / in JSON at position 0
SyntaxError: Unexpected token '/', "// Copyrig"... is not valid JSON

View File

@ -12,19 +12,19 @@ function TryParse(s, message) {
}
var s = `{"a\\\\b `;
TryParse(s, "Unexpected end of JSON input");
TryParse(s, "Unterminated string in JSON at position 7");
var s = `{"a\\\\\u03A9 `;
TryParse(s, "Unexpected end of JSON input");
TryParse(s, "Unterminated string in JSON at position 7");
var s = `{"ab `;
TryParse(s, "Unexpected end of JSON input");
TryParse(s, "Unterminated string in JSON at position 5");
var s = `{"a\u03A9 `;
TryParse(s, "Unexpected end of JSON input");
TryParse(s, "Unterminated string in JSON at position 5");
var s = `{"a\nb":"b"}`;
TryParse(s, "Unexpected token \n in JSON at position 3");
TryParse(s, "Unexpected token '\n', \"{\"a\nb\":\"b\"}\" is not valid JSON");
var s = `{"a\nb":"b\u03A9"}`;
TryParse(s, "Unexpected token \n in JSON at position 3");
TryParse(s, "Unexpected token '\n', \"{\"a\nb\":\"b\u03A9\"}\" is not valid JSON");

View File

@ -474,25 +474,129 @@ test(function() {
eval("'\n'");
}, "Invalid or unexpected token", SyntaxError);
//kJsonParseUnexpectedEOS
// kJsonParseUnterminatedString
test(function() {
JSON.parse('{"a" : "}')
}, "Unterminated string in JSON at position 9", SyntaxError);
// kJsonParseExpectedPropNameOrRBrace
test(function() {
JSON.parse("{")
}, "Unexpected end of JSON input", SyntaxError);
}, "Expected property name or '}' in JSON at position 1", SyntaxError);
// kJsonParseUnexpectedTokenAt
// kJsonParseExpectedDoubleQuotedPropertyName
test(function() {
JSON.parse('{"foo" : 1, }');
}, "Expected double-quoted property name in JSON at position 12", SyntaxError);
// kJsonParseExpectedCommaNameOrRBrack
test(function() {
JSON.parse("{'foo': 1}");
}, "Expected property name or '}' in JSON at position 1", SyntaxError);
// kJsonParseExpectedCommaNameOrRBrace
test(function() {
JSON.parse('[1, 2, 3, 4');
}, "Expected ',' or ']' after array element in JSON at position 11", SyntaxError);
test(function() {
JSON.parse('[1, 2, 3, 4g');
}, "Expected ',' or ']' after array element in JSON at position 11", SyntaxError);
// kJsonParseExponentPartMissingNumber
test(function() {
JSON.parse('[1e]');
}, "Exponent part is missing a number in JSON at position 3", SyntaxError);
// kJsonParseExpectedColonAfterPropertyName
test(function() {
JSON.parse('{"a"}');
}, "Expected ':' after property name in JSON at position 4", SyntaxError);
// kJsonParseUnterminatedFractionNumber
test(function() {
JSON.parse('{"a": 0.bs}');
}, "Unterminated fractional number in JSON at position 8", SyntaxError);
// kJsonParseUnexpectedNonWhiteSpaceCharacter
test(function() {
JSON.parse('{"a": 3}a');
}, "Unexpected non-whitespace character after JSON at position 8", SyntaxError);
// kJsonParseBadEscapedCharacter
test(function() {
JSON.parse('{"b" : "\\a"}');
}, "Bad escaped character in JSON at position 9", SyntaxError);
// kJsonParseNoNumberAfterMinusSign
test(function() {
JSON.parse('-');
}, "No number after minus sign in JSON at position 1", SyntaxError);
// kJsonParseUnexpectedTokenShortString
test(function () {
JSON.parse(NaN)
}, "\"NaN\" is not valid JSON", SyntaxError);
// kJsonParseUnexpectedTokenShortString
test(function() {
JSON.parse("/")
}, "Unexpected token / in JSON at position 0", SyntaxError);
}, "Unexpected token '/', \"/\" is not valid JSON", SyntaxError);
// kJsonParseUnexpectedTokenNumberAt
// kJsonParseUnexpectedTokenShortString
test(function () {
JSON.parse(undefined)
}, "\"undefined\" is not valid JSON", SyntaxError);
// kJsonParseUnexpectedTokenShortString
test(function () {
JSON.parse(Infinity)
}, "\"Infinity\" is not valid JSON", SyntaxError);
// kJsonParseUnexpectedTokenShortString
test(function () {
JSON.parse('Bad string')
}, "Unexpected token 'B', \"Bad string\" is not valid JSON", SyntaxError);
// kJsonParseUnexpectedTokenShortString
test(function () {
JSON.parse({})
}, "\"[object Object]\" is not valid JSON", SyntaxError);
// kJsonParseExpectedPropNameOrRBrace
test(function() {
JSON.parse("{ 1")
}, "Unexpected number in JSON at position 2", SyntaxError);
}, "Expected property name or '}' in JSON at position 2", SyntaxError);
// kJsonParseUnexpectedTokenStringAt
// kJsonParseUnexpectedNonWhiteSpaceCharacter
test(function() {
JSON.parse('"""')
}, "Unexpected string in JSON at position 2", SyntaxError);
}, "Unexpected non-whitespace character after JSON at position 2", SyntaxError);
// kJsonParseUnexpectedTokenStringShortString
test(function() {
JSON.parse('[1, 2,]');
}, "Unexpected token ']', \"[1, 2,]\" is not valid JSON", SyntaxError);
// kJsonParseUnexpectedTokenStringShortString
test(function() {
JSON.parse('[1, 2, 3, 4, ]');
}, "Unexpected token ']', \"[1, 2, 3, 4, ]\" is not valid JSON", SyntaxError);
// kJsonParseUnexpectedTokenStringSurroundWithContext
test(function() {
JSON.parse('[1, 2, 3, 4, 5, , 7, 8, 9, 10]');
}, "Unexpected token ',', ...\" 3, 4, 5, , 7, 8, 9,\"... is not valid JSON", SyntaxError);
// kJsonParseUnexpectedTokenStringStartWithContext
test(function() {
JSON.parse('[, 2, 3, 4, 5, 6, 7, 8, 9, 10]');
}, "Unexpected token ',', \"[, 2, 3, 4,\"... is not valid JSON", SyntaxError);
// kJsonParseUnexpectedTokenStringEndWithContext
test(function() {
JSON.parse('[1, 2, 3, 4, 5, 6, 7, ]');
}, "Unexpected token ']', ...\" 5, 6, 7, ]\" is not valid JSON", SyntaxError);
// kMalformedRegExp
test(function() {