mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-30 07:14:39 +08:00
Report JSON.parse syntax errors with V8's messages and positions (#2129)
This commit is contained in:
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/7993-json-parse-error-messages.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -23,8 +23,8 @@
|
||||
* Ownership: the checked-dynamic tree owns its children (strings, items, entry keys+values);
|
||||
* releasing the root releases the tree recursively. scr_json_parse borrows
|
||||
* the text and returns +1 — or THROWS a catchable string shaped like Node's
|
||||
* V8 messages ("Unexpected end of JSON input"; approximate fidelity, see
|
||||
* SEMANTICS.md) and returns NULL. scr_dyn_obj_get returns a BORROWED
|
||||
* V8 messages ("Unexpected end of JSON input", "... in JSON at position
|
||||
* <n> (line <l> column <c>)") and returns NULL. scr_dyn_obj_get returns a BORROWED
|
||||
* member (the compiler's builders retain what they extract themselves).
|
||||
*/
|
||||
|
||||
|
||||
+165
-41
@@ -9,10 +9,10 @@
|
||||
* arrays, objects (later duplicate keys win, like JS), and the
|
||||
* four JSON whitespace characters. Syntax errors THROW catchable
|
||||
* SyntaxError instances (the depth cap a RangeError, like V8's) whose
|
||||
* messages are shaped like Node's V8 texts ("Unexpected end of JSON
|
||||
* input", "Unexpected token 'x', \"...\" is not valid JSON") —
|
||||
* APPROXIMATE message fidelity by design (SEMANTICS.md; e.name is exact,
|
||||
* e.message is ours), pinned by the runtime C tests.
|
||||
* messages are Node's V8 texts ("Unexpected end of JSON input",
|
||||
* "Unexpected token 'x', \"...\" is not valid JSON", "... in JSON at
|
||||
* position <utf-16 offset> (line <l> column <c>)"), reported where V8
|
||||
* reports them.
|
||||
* - scr_dyn_check_fail is the shared failure path of the compiler-emitted
|
||||
* dynCheck builders: a TypeError instance carrying "expected <want> at
|
||||
* <path>, got <kind>", thrown through the exception cell.
|
||||
@@ -11115,26 +11115,142 @@ static void scr_json_throw(const ScrJsonP *p, const char *msg) {
|
||||
scr_throw_error_msg(SCR_ERR_SYNTAX, msg, strlen(msg));
|
||||
}
|
||||
|
||||
/* V8's location of a JSON error at byte `pos`: the UTF-16 offset (the
|
||||
* text is WTF-8; a four-byte sequence is two units), and the 1-based line
|
||||
* and column, where \r, \n and \r\n end a line. */
|
||||
static void scr_json_location(const ScrJsonP *p, size_t pos, size_t *unit, size_t *line,
|
||||
size_t *column) {
|
||||
const unsigned char *s = (const unsigned char *)p->s;
|
||||
size_t units = 0, ln = 1, line_start = 0;
|
||||
for (size_t i = 0; i < pos; i++) {
|
||||
unsigned char c = s[i];
|
||||
if ((c & 0xC0) == 0x80) continue;
|
||||
if (c == '\r' && i + 1 < pos && s[i + 1] == '\n') {
|
||||
i++;
|
||||
units++;
|
||||
c = '\n';
|
||||
}
|
||||
units += c >= 0xF0 ? 2 : 1;
|
||||
if (c == '\r' || c == '\n') {
|
||||
ln++;
|
||||
line_start = units;
|
||||
}
|
||||
}
|
||||
*unit = units;
|
||||
*line = ln;
|
||||
*column = 1 + units - line_start;
|
||||
}
|
||||
|
||||
/* Errors stay out of line so the scanners that report them keep their
|
||||
* registers for the common path. */
|
||||
static __attribute__((noinline, cold)) void scr_json_throw_pos(const ScrJsonP *p, const char *what,
|
||||
size_t pos) {
|
||||
* registers for the common path. V8's positional messages:
|
||||
* "<what> in JSON at position <unit> (line <l> column <c>)". */
|
||||
static __attribute__((noinline, cold)) void scr_json_throw_located(const ScrJsonP *p,
|
||||
const char *what,
|
||||
const char *where, size_t pos) {
|
||||
if (p->quiet) return;
|
||||
char buf[128];
|
||||
int n = snprintf(buf, sizeof buf, "%s in JSON at position %zu", what, pos);
|
||||
size_t unit, line, column;
|
||||
scr_json_location(p, pos, &unit, &line, &column);
|
||||
char buf[192];
|
||||
int n = snprintf(buf, sizeof buf, "%s%s at position %zu (line %zu column %zu)", what, where, unit,
|
||||
line, column);
|
||||
scr_throw_error_msg(SCR_ERR_SYNTAX, buf, (size_t)n);
|
||||
}
|
||||
|
||||
/* V8-flavored bad-token message with a short snippet of the input around
|
||||
* the offending character. Approximate fidelity (documented). */
|
||||
static void scr_json_throw_token(const ScrJsonP *p) {
|
||||
static __attribute__((noinline, cold)) void scr_json_throw_pos(const ScrJsonP *p, const char *what,
|
||||
size_t pos) {
|
||||
scr_json_throw_located(p, what, " in JSON", pos);
|
||||
}
|
||||
|
||||
/* Appends the text's UTF-16 units [from, to) as WTF-8: a surrogate pair the
|
||||
* range cuts contributes its half as a lone surrogate, as V8's substring. */
|
||||
static size_t scr_json_unit_slice(const ScrJsonP *p, size_t from, size_t to, char *out) {
|
||||
const unsigned char *s = (const unsigned char *)p->s;
|
||||
size_t n = 0, unit = 0, i = 0;
|
||||
while (i < p->len && unit < to) {
|
||||
unsigned char c = s[i];
|
||||
size_t width = c < 0x80 ? 1 : c < 0xE0 ? 2 : c < 0xF0 ? 3 : 4;
|
||||
if (i + width > p->len) width = p->len - i;
|
||||
if (width == 4) {
|
||||
uint32_t cp = ((uint32_t)(c & 0x07) << 18) | ((uint32_t)(s[i + 1] & 0x3F) << 12) |
|
||||
((uint32_t)(s[i + 2] & 0x3F) << 6) | (uint32_t)(s[i + 3] & 0x3F);
|
||||
bool high = unit >= from && unit < to, low = unit + 1 >= from && unit + 1 < to;
|
||||
if (high && low) {
|
||||
memcpy(out + n, s + i, 4);
|
||||
n += 4;
|
||||
} else if (high) {
|
||||
n += scr_wtf8_put(out + n, 0xD800 + ((cp - 0x10000) >> 10));
|
||||
} else if (low) {
|
||||
n += scr_wtf8_put(out + n, 0xDC00 + ((cp - 0x10000) & 0x3FF));
|
||||
}
|
||||
unit += 2;
|
||||
} else {
|
||||
if (unit >= from) {
|
||||
memcpy(out + n, s + i, width);
|
||||
n += width;
|
||||
}
|
||||
unit++;
|
||||
}
|
||||
i += width;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/* V8's report of an unexpected token at byte `pos`: the end of the input,
|
||||
* a string or a number, or any other character, named with the source (a
|
||||
* short one whole, a longer one as up to ten units on each side), except
|
||||
* that the sources "NaN", "Infinity", "undefined" and "[object Object]"
|
||||
* name only themselves. */
|
||||
static __attribute__((noinline, cold)) void scr_json_throw_unexpected(const ScrJsonP *p,
|
||||
size_t pos) {
|
||||
if (p->quiet) return;
|
||||
size_t start = p->pos > 8 ? p->pos - 8 : 0;
|
||||
size_t take = p->len - start < 16 ? p->len - start : 16;
|
||||
char buf[192];
|
||||
int n = snprintf(buf, sizeof buf, "Unexpected token '%c', %s\"%.*s\"%s is not valid JSON",
|
||||
p->s[p->pos], start > 0 ? "..." : "", (int)take, p->s + start,
|
||||
start + take < p->len ? "..." : "");
|
||||
if (pos >= p->len) {
|
||||
scr_json_throw(p, "Unexpected end of JSON input");
|
||||
return;
|
||||
}
|
||||
char c = p->s[pos];
|
||||
if (c == '"') {
|
||||
scr_json_throw_pos(p, "Unexpected string", pos);
|
||||
return;
|
||||
}
|
||||
if (c == '-' || (c >= '0' && c <= '9')) {
|
||||
scr_json_throw_pos(p, "Unexpected number", pos);
|
||||
return;
|
||||
}
|
||||
static const char *const special[] = {"NaN", "Infinity", "undefined", "[object Object]"};
|
||||
char buf[256];
|
||||
int n;
|
||||
for (size_t k = 0; k < sizeof special / sizeof special[0]; k++) {
|
||||
if (p->len == strlen(special[k]) && memcmp(p->s, special[k], p->len) == 0) {
|
||||
n = snprintf(buf, sizeof buf, "\"%s\" is not valid JSON", special[k]);
|
||||
scr_throw_error_msg(SCR_ERR_SYNTAX, buf, (size_t)n);
|
||||
return;
|
||||
}
|
||||
}
|
||||
size_t unit, line, column, total;
|
||||
scr_json_location(p, pos, &unit, &line, &column);
|
||||
scr_json_location(p, p->len, &total, &line, &column);
|
||||
char token[8];
|
||||
size_t token_len = scr_json_unit_slice(p, unit, unit + 1, token);
|
||||
enum { CONTEXT = 10 };
|
||||
size_t from = 0, to = total;
|
||||
const char *before = "", *after = "";
|
||||
if (total >= 2 * CONTEXT + 1) {
|
||||
if (unit < CONTEXT) {
|
||||
to = unit + CONTEXT;
|
||||
after = "...";
|
||||
} else if (unit < total - CONTEXT) {
|
||||
from = unit - CONTEXT;
|
||||
to = unit + CONTEXT;
|
||||
before = after = "...";
|
||||
} else {
|
||||
from = unit - CONTEXT;
|
||||
before = "...";
|
||||
}
|
||||
}
|
||||
char source[4 * 2 * CONTEXT + 8];
|
||||
size_t source_len = scr_json_unit_slice(p, from, to, source);
|
||||
n = snprintf(buf, sizeof buf, "Unexpected token '%.*s', %s\"%.*s\"%s is not valid JSON",
|
||||
(int)token_len, token, before, (int)source_len, source, after);
|
||||
scr_throw_error_msg(SCR_ERR_SYNTAX, buf, (size_t)n);
|
||||
}
|
||||
|
||||
@@ -11159,12 +11275,13 @@ static void scr_json_put_cp(ScrJsonBuf *b, uint32_t cp) {
|
||||
|
||||
/* Four hex digits at pos, or -1 (throws). */
|
||||
static int32_t scr_json_hex4(ScrJsonP *p) {
|
||||
if (p->len - p->pos < 4) {
|
||||
scr_json_throw(p, "Unexpected end of JSON input");
|
||||
return -1;
|
||||
}
|
||||
uint32_t v = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (p->pos + (size_t)i >= p->len) {
|
||||
/* V8 reports the escape at the end of the input. */
|
||||
scr_json_throw_pos(p, "Bad Unicode escape", p->len);
|
||||
return -1;
|
||||
}
|
||||
char c = p->s[p->pos + (size_t)i];
|
||||
uint32_t digit;
|
||||
if (c >= '0' && c <= '9') digit = (uint32_t)(c - '0');
|
||||
@@ -11205,7 +11322,7 @@ static int scr_json_string_span(ScrJsonP *p, const char **span,
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
scr_json_throw_pos(p, "Unterminated string", p->pos);
|
||||
scr_json_throw_pos(p, "Unterminated string", p->len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -11221,7 +11338,7 @@ static ScrStr *scr_json_string_slow(ScrJsonP *p, size_t prefix) {
|
||||
for (;;) {
|
||||
if (p->pos >= p->len) {
|
||||
scr_jb_dispose(&b);
|
||||
scr_json_throw_pos(p, "Unterminated string", open);
|
||||
scr_json_throw_pos(p, "Unterminated string", p->len);
|
||||
return NULL;
|
||||
}
|
||||
unsigned char c = (unsigned char)p->s[p->pos];
|
||||
@@ -11325,6 +11442,12 @@ static bool scr_json_number_value(ScrJsonP *p, double *out) {
|
||||
}
|
||||
if (s[pos] == '0') {
|
||||
pos++;
|
||||
/* A leading zero followed by a digit: V8's unexpected number there. */
|
||||
if (pos < len && s[pos] >= '0' && s[pos] <= '9') {
|
||||
p->pos = pos;
|
||||
scr_json_throw_pos(p, "Unexpected number", pos);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
while (pos < len && s[pos] >= '0' && s[pos] <= '9') {
|
||||
if (ndig < 15) {
|
||||
@@ -11405,7 +11528,10 @@ static bool scr_json_lit(ScrJsonP *p, const char *word, size_t n) {
|
||||
p->pos += n;
|
||||
return true;
|
||||
}
|
||||
scr_json_throw_token(p);
|
||||
/* V8 names the first character that differs, or the end of the input. */
|
||||
size_t i = 1;
|
||||
while (p->pos + i < p->len && i < n && p->s[p->pos + i] == word[i]) i++;
|
||||
scr_json_throw_unexpected(p, p->pos + i);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -11465,7 +11591,7 @@ static ScrDyn *scr_json_array(ScrJsonP *p) {
|
||||
scr_json_ws(p);
|
||||
if (p->pos >= p->len) {
|
||||
scr_dyn_release(arr);
|
||||
scr_json_throw(p, "Unexpected end of JSON input");
|
||||
scr_json_throw_pos(p, "Expected ',' or ']' after array element", p->pos);
|
||||
return NULL;
|
||||
}
|
||||
if (p->s[p->pos] == ',') {
|
||||
@@ -11595,18 +11721,20 @@ static ScrDyn *scr_json_object(ScrJsonP *p) {
|
||||
}
|
||||
uint32_t shape = 0; /* the empty layout */
|
||||
bool shaped = true; /* obj holds exactly the keys on shape's path */
|
||||
bool first = true;
|
||||
for (;;) {
|
||||
scr_json_ws(p);
|
||||
if (p->pos >= p->len) {
|
||||
if (p->pos >= p->len || p->s[p->pos] != '"') {
|
||||
/* V8: the first member may also close the object; after a comma
|
||||
* only a property name may follow. */
|
||||
scr_dyn_release(obj);
|
||||
scr_json_throw(p, "Unexpected end of JSON input");
|
||||
return NULL;
|
||||
}
|
||||
if (p->s[p->pos] != '"') {
|
||||
scr_dyn_release(obj);
|
||||
scr_json_throw_pos(p, "Expected property name or '}'", p->pos);
|
||||
scr_json_throw_pos(p,
|
||||
first ? "Expected property name or '}'"
|
||||
: "Expected double-quoted property name",
|
||||
p->pos);
|
||||
return NULL;
|
||||
}
|
||||
first = false;
|
||||
/* Keep the key borrowed until insertion determines whether it already
|
||||
* exists or can reuse a recycled slot. Escaped keys own their decoded
|
||||
* string until the value has been parsed and inserted. */
|
||||
@@ -11644,8 +11772,7 @@ static ScrDyn *scr_json_object(ScrJsonP *p) {
|
||||
if (p->pos >= p->len || p->s[p->pos] != ':') {
|
||||
scr_str_release(decoded_key);
|
||||
scr_dyn_release(obj);
|
||||
if (p->pos >= p->len) scr_json_throw(p, "Unexpected end of JSON input");
|
||||
else scr_json_throw_pos(p, "Expected ':' after property name", p->pos);
|
||||
scr_json_throw_pos(p, "Expected ':' after property name", p->pos);
|
||||
return NULL;
|
||||
}
|
||||
p->pos++; /* ':' */
|
||||
@@ -11672,7 +11799,7 @@ static ScrDyn *scr_json_object(ScrJsonP *p) {
|
||||
scr_json_ws(p);
|
||||
if (p->pos >= p->len) {
|
||||
scr_dyn_release(obj);
|
||||
scr_json_throw(p, "Unexpected end of JSON input");
|
||||
scr_json_throw_pos(p, "Expected ',' or '}' after property value", p->pos);
|
||||
return NULL;
|
||||
}
|
||||
if (p->s[p->pos] == ',') {
|
||||
@@ -11730,7 +11857,7 @@ static ScrDyn *scr_json_value(ScrJsonP *p) {
|
||||
return scr_dyn_new_null();
|
||||
}
|
||||
if (c == '-' || (c >= '0' && c <= '9')) return scr_json_number(p);
|
||||
scr_json_throw_token(p);
|
||||
scr_json_throw_unexpected(p, p->pos);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -11750,10 +11877,7 @@ ScrDyn *scr_json_parse(ScrStr *text) {
|
||||
scr_json_ws(&p);
|
||||
if (p.pos < p.len) {
|
||||
scr_dyn_release(d);
|
||||
char buf[96];
|
||||
int n = snprintf(buf, sizeof buf,
|
||||
"Unexpected non-whitespace character after JSON at position %zu", p.pos);
|
||||
scr_throw_error_msg(SCR_ERR_SYNTAX, buf, (size_t)n);
|
||||
scr_json_throw_located(&p, "Unexpected non-whitespace character after JSON", "", p.pos);
|
||||
return NULL;
|
||||
}
|
||||
return d;
|
||||
|
||||
@@ -53,9 +53,8 @@ beforeAll(async () => {
|
||||
// The C side parses/inspects dyn values as checks and routes each expected
|
||||
// failure through scr_exc_print_uncaught. Parse failures are SyntaxError
|
||||
// INSTANCES (the depth cap a RangeError, dynCheck failures TypeErrors), so
|
||||
// every uncaught line renders "name: message" — e.name is Node-exact while
|
||||
// the V8-flavored message wording stays approximate (SEMANTICS.md): these
|
||||
// lines pin OUR rendering, not Node's.
|
||||
// every uncaught line renders "name: message"; the JSON syntax error
|
||||
// messages are Node's (V8's wording, positions and line/column).
|
||||
test("json runtime: parser, dyn, dynCheck failure path, stringify buffer", async () => {
|
||||
const { stdout, stderr } = await execFileAsync(bin);
|
||||
expect(stdout).toMatch(/^(\d+)\/\1 checks passed\n$/);
|
||||
@@ -63,21 +62,21 @@ test("json runtime: parser, dyn, dynCheck failure path, stringify buffer", async
|
||||
[
|
||||
`Uncaught SyntaxError: Unexpected end of JSON input`,
|
||||
`Uncaught SyntaxError: Unexpected end of JSON input`,
|
||||
`Uncaught SyntaxError: Expected property name or '}' in JSON at position 1`,
|
||||
`Uncaught SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2)`,
|
||||
`Uncaught SyntaxError: Unexpected end of JSON input`,
|
||||
`Uncaught SyntaxError: Expected ',' or ']' after array element in JSON at position 3 (line 1 column 4)`,
|
||||
`Uncaught SyntaxError: Expected ':' after property name in JSON at position 4 (line 1 column 5)`,
|
||||
`Uncaught SyntaxError: Expected double-quoted property name in JSON at position 7 (line 1 column 8)`,
|
||||
`Uncaught SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2)`,
|
||||
`Uncaught SyntaxError: Unterminated string in JSON at position 13 (line 1 column 14)`,
|
||||
`Uncaught SyntaxError: Bad escaped character in JSON at position 6 (line 1 column 7)`,
|
||||
`Uncaught SyntaxError: Bad control character in string literal in JSON at position 6 (line 1 column 7)`,
|
||||
`Uncaught SyntaxError: Bad Unicode escape in JSON at position 5 (line 1 column 6)`,
|
||||
`Uncaught SyntaxError: No number after minus sign in JSON at position 1 (line 1 column 2)`,
|
||||
`Uncaught SyntaxError: Unterminated fractional number in JSON at position 2 (line 1 column 3)`,
|
||||
`Uncaught SyntaxError: Exponent part is missing a number in JSON at position 3 (line 1 column 4)`,
|
||||
`Uncaught SyntaxError: Unexpected non-whitespace character after JSON at position 2 (line 1 column 3)`,
|
||||
`Uncaught SyntaxError: Unexpected end of JSON input`,
|
||||
`Uncaught SyntaxError: Expected ',' or ']' after array element in JSON at position 3`,
|
||||
`Uncaught SyntaxError: Expected ':' after property name in JSON at position 4`,
|
||||
`Uncaught SyntaxError: Expected property name or '}' in JSON at position 7`,
|
||||
`Uncaught SyntaxError: Expected property name or '}' in JSON at position 1`,
|
||||
`Uncaught SyntaxError: Unterminated string in JSON at position 0`,
|
||||
`Uncaught SyntaxError: Bad escaped character in JSON at position 6`,
|
||||
`Uncaught SyntaxError: Bad control character in string literal in JSON at position 6`,
|
||||
`Uncaught SyntaxError: Bad Unicode escape in JSON at position 5`,
|
||||
`Uncaught SyntaxError: No number after minus sign in JSON at position 1`,
|
||||
`Uncaught SyntaxError: Unterminated fractional number in JSON at position 2`,
|
||||
`Uncaught SyntaxError: Exponent part is missing a number in JSON at position 3`,
|
||||
`Uncaught SyntaxError: Unexpected non-whitespace character after JSON at position 2`,
|
||||
`Uncaught SyntaxError: Unexpected token 'n', "nul" is not valid JSON`,
|
||||
`Uncaught RangeError: Maximum call stack size exceeded`,
|
||||
`Uncaught TypeError: expected number at $.items[2].price, got string`,
|
||||
`Uncaught TypeError: expected object at $, got undefined`,
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
// JSON.parse syntax errors are CATCHABLE, exactly like Node's SyntaxError.
|
||||
// The message text itself is unobservable here (the supported catch form is
|
||||
// bindingless) and its V8-flavored shape is deliberately approximate —
|
||||
// exact-message assertions live in the runtime C tests (test_json.c), per
|
||||
// SEMANTICS.md. This program only proves the throw/catch/recover behavior
|
||||
// matches Node for every syntax-error class.
|
||||
// This program proves the throw/catch/recover behavior matches Node for
|
||||
// every syntax-error class; 7993-json-parse-error-messages pins the
|
||||
// messages themselves.
|
||||
|
||||
function attempt(raw: string, label: string): void {
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// JSON.parse syntax error messages, exactly as Node's V8 writes them: the
|
||||
// expected token at the end of the input inside objects and arrays, the
|
||||
// position as a UTF-16 offset with its line and column (\n, \r and \r\n
|
||||
// end lines), the unexpected character named with the source (whole when
|
||||
// short, ten units of context on each side when longer), and the special
|
||||
// sources that name only themselves.
|
||||
const inputs: string[] = [
|
||||
"", " ", "[", "[{", "{", '{"a"', '{"a":', '{"a":1', '{"a":1,', '{"a":1,}', "{,", "{1:2}", "{'a':1}",
|
||||
'{"a" 1}', '{"a":1 "b":2}', "[1", "[1,", "[1,]", "[1 2]", "[,1]", '"abc', '"a\\', '"\\u12', '"\\u12x4"',
|
||||
'"\\u12"', '"\\x"', "tru", "nul", "fals", "t1", 'n"', "-", "-x", "1.", "1.e", "1e", "1e+", "01", "-01",
|
||||
"[01]", "+1", "1 2", "{} x", "[]]", "\u0001", '"\u0001"', '{"a":[1,{"b":', '[{"a":1},', '{"a":{"b":{}',
|
||||
"[[[", " [ \n { \n", "\n\n{", "\r\n\r\n{", "\r\r{", '{"a":tru}', "NaN", "Infinity", "undefined",
|
||||
"[object Object]", " NaN", "-Infinity", "[-]",
|
||||
// Positions count UTF-16 units: non-ASCII text before the error.
|
||||
'{"é":1 x}', '["😀" 1]', '{"😀😀":', '"é\u0001"', "é",
|
||||
// Long sources: the unexpected character with its context.
|
||||
'{"name":"some long value","other":x}', '[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,]',
|
||||
'x{"name":"some long value","other":1}', '{"name":"some long value","other":1}x', '{"a":"0123456789abcdef"x}',
|
||||
'["😀😀😀😀😀😀", x, "😀😀😀😀😀😀"]', '["😀😀😀😀😀😀",x]', '{"k":[true,false,null,"s",1.5e3,-2] ]',
|
||||
'\n\n {\n "a": [1, 2,\n 3 4]\n }',
|
||||
];
|
||||
for (const text of inputs) {
|
||||
try {
|
||||
JSON.parse(text);
|
||||
console.log(JSON.stringify(text), "parsed");
|
||||
} catch (e) {
|
||||
const err = e as Error;
|
||||
console.log(JSON.stringify(text), err.name, JSON.stringify(err.message));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{ "plain": 3600 }
|
||||
@@ -191,11 +191,11 @@ for (const text of inputs) {
|
||||
expect(r.stderr).toBe("");
|
||||
expect(r.stdout.split("\n")).toEqual([
|
||||
"SyntaxError: Unexpected end of JSON input (same)",
|
||||
"SyntaxError: Expected ',' or '}' after property value in JSON at position 19 (same)",
|
||||
"SyntaxError: Bad escaped character in JSON at position 11 (same)",
|
||||
"SyntaxError: Unexpected non-whitespace character after JSON at position 22 (same)",
|
||||
'SyntaxError: Unexpected token \']\', ...","x":[1,],"qty":"... is not valid JSON (same)',
|
||||
"SyntaxError: Bad Unicode escape in JSON at position 21 (same)",
|
||||
"SyntaxError: Unexpected number in JSON at position 19 (line 1 column 20) (same)",
|
||||
"SyntaxError: Bad escaped character in JSON at position 11 (line 1 column 12) (same)",
|
||||
"SyntaxError: Unexpected non-whitespace character after JSON at position 22 (line 1 column 23) (same)",
|
||||
'SyntaxError: Unexpected token \']\', ..."a","x":[1,],"qty":1}"... is not valid JSON (same)',
|
||||
"SyntaxError: Bad Unicode escape in JSON at position 21 (line 1 column 22) (same)",
|
||||
"RangeError: Maximum call stack size exceeded (same)",
|
||||
"TypeError: expected number at $[0].qty, got undefined (plain ok)",
|
||||
"TypeError: expected number at $[1].qty, got boolean (plain ok)",
|
||||
@@ -268,7 +268,7 @@ try {
|
||||
"err TypeError: expected number at $.id, got undefined",
|
||||
"err TypeError: expected number at $.id, got null",
|
||||
"err TypeError: expected object at $, got array",
|
||||
"err SyntaxError: Unexpected end of JSON input",
|
||||
"err SyntaxError: Expected ',' or '}' after property value in JSON at position 39 (line 1 column 40)",
|
||||
"ok Infinity \uFFFD 0 ",
|
||||
"ok 7 reordered 0 ",
|
||||
"err expected number at $[1].qty, got string",
|
||||
|
||||
Reference in New Issue
Block a user