mirror of
https://github.com/bellard/quickjs.git
synced 2025-05-29 01:49:18 +08:00
improved JSON parser conformity (chqrlie) (#250)
This commit is contained in:
parent
aaa9cea6a8
commit
9bce51eefd
2
TODO
2
TODO
@ -62,5 +62,5 @@ Optimization ideas:
|
|||||||
Test262o: 0/11262 errors, 463 excluded
|
Test262o: 0/11262 errors, 463 excluded
|
||||||
Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch)
|
Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch)
|
||||||
|
|
||||||
Result: 60/79202 errors, 1610 excluded, 6738 skipped
|
Result: 58/79202 errors, 1610 excluded, 6738 skipped
|
||||||
Test262 commit: 27622d764767dcb3778784884022c2c7de5769b8
|
Test262 commit: 27622d764767dcb3778784884022c2c7de5769b8
|
||||||
|
171
quickjs.c
171
quickjs.c
@ -21031,11 +21031,6 @@ static __exception int js_parse_string(JSParseState *s, int sep,
|
|||||||
goto invalid_char;
|
goto invalid_char;
|
||||||
c = *p;
|
c = *p;
|
||||||
if (c < 0x20) {
|
if (c < 0x20) {
|
||||||
if (!s->cur_func) {
|
|
||||||
if (do_throw)
|
|
||||||
js_parse_error_pos(s, p, "invalid character in a JSON string");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
if (sep == '`') {
|
if (sep == '`') {
|
||||||
if (c == '\r') {
|
if (c == '\r') {
|
||||||
if (p[1] == '\n')
|
if (p[1] == '\n')
|
||||||
@ -21081,8 +21076,6 @@ static __exception int js_parse_string(JSParseState *s, int sep,
|
|||||||
continue;
|
continue;
|
||||||
default:
|
default:
|
||||||
if (c >= '0' && c <= '9') {
|
if (c >= '0' && c <= '9') {
|
||||||
if (!s->cur_func)
|
|
||||||
goto invalid_escape; /* JSON case */
|
|
||||||
if (!(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`')
|
if (!(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`')
|
||||||
goto parse_escape;
|
goto parse_escape;
|
||||||
if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) {
|
if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) {
|
||||||
@ -21851,6 +21844,150 @@ static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c)
|
|||||||
return atom;
|
return atom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int json_parse_string(JSParseState *s, const uint8_t **pp, int sep)
|
||||||
|
{
|
||||||
|
const uint8_t *p, *p_next;
|
||||||
|
int i;
|
||||||
|
uint32_t c;
|
||||||
|
StringBuffer b_s, *b = &b_s;
|
||||||
|
|
||||||
|
if (string_buffer_init(s->ctx, b, 32))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
p = *pp;
|
||||||
|
for(;;) {
|
||||||
|
if (p >= s->buf_end) {
|
||||||
|
goto end_of_input;
|
||||||
|
}
|
||||||
|
c = *p++;
|
||||||
|
if (c == sep)
|
||||||
|
break;
|
||||||
|
if (c < 0x20) {
|
||||||
|
js_parse_error_pos(s, p - 1, "Bad control character in string literal");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (c == '\\') {
|
||||||
|
c = *p++;
|
||||||
|
switch(c) {
|
||||||
|
case 'b': c = '\b'; break;
|
||||||
|
case 'f': c = '\f'; break;
|
||||||
|
case 'n': c = '\n'; break;
|
||||||
|
case 'r': c = '\r'; break;
|
||||||
|
case 't': c = '\t'; break;
|
||||||
|
case '\\': break;
|
||||||
|
case '/': break;
|
||||||
|
case 'u':
|
||||||
|
c = 0;
|
||||||
|
for(i = 0; i < 4; i++) {
|
||||||
|
int h = from_hex(*p++);
|
||||||
|
if (h < 0) {
|
||||||
|
js_parse_error_pos(s, p - 1, "Bad Unicode escape");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
c = (c << 4) | h;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (c == sep)
|
||||||
|
break;
|
||||||
|
if (p > s->buf_end)
|
||||||
|
goto end_of_input;
|
||||||
|
js_parse_error_pos(s, p - 1, "Bad escaped character");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
if (c >= 0x80) {
|
||||||
|
c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
|
||||||
|
if (c > 0x10FFFF) {
|
||||||
|
js_parse_error_pos(s, p - 1, "Bad UTF-8 sequence");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
p = p_next;
|
||||||
|
}
|
||||||
|
if (string_buffer_putc(b, c))
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
s->token.val = TOK_STRING;
|
||||||
|
s->token.u.str.sep = sep;
|
||||||
|
s->token.u.str.str = string_buffer_end(b);
|
||||||
|
*pp = p;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
end_of_input:
|
||||||
|
js_parse_error(s, "Unexpected end of JSON input");
|
||||||
|
fail:
|
||||||
|
string_buffer_free(b);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int json_parse_number(JSParseState *s, const uint8_t **pp)
|
||||||
|
{
|
||||||
|
const uint8_t *p = *pp;
|
||||||
|
const uint8_t *p_start = p;
|
||||||
|
int radix;
|
||||||
|
double d;
|
||||||
|
JSATODTempMem atod_mem;
|
||||||
|
|
||||||
|
if (*p == '+' || *p == '-')
|
||||||
|
p++;
|
||||||
|
|
||||||
|
if (!is_digit(*p))
|
||||||
|
return js_parse_error_pos(s, p, "Unexpected token '%c'", *p_start);
|
||||||
|
|
||||||
|
if (p[0] == '0') {
|
||||||
|
if (s->ext_json) {
|
||||||
|
/* also accepts base 16, 8 and 2 prefix for integers */
|
||||||
|
radix = 10;
|
||||||
|
if (p[1] == 'x' || p[1] == 'X') {
|
||||||
|
p += 2;
|
||||||
|
radix = 16;
|
||||||
|
} else if ((p[1] == 'o' || p[1] == 'O')) {
|
||||||
|
p += 2;
|
||||||
|
radix = 8;
|
||||||
|
} else if ((p[1] == 'b' || p[1] == 'B')) {
|
||||||
|
p += 2;
|
||||||
|
radix = 2;
|
||||||
|
}
|
||||||
|
if (radix != 10) {
|
||||||
|
/* prefix is present */
|
||||||
|
if (to_digit(*p) >= radix)
|
||||||
|
return js_parse_error_pos(s, p, "Unexpected token '%c'", *p);
|
||||||
|
d = js_atod((const char *)p_start, (const char **)&p, 0,
|
||||||
|
JS_ATOD_INT_ONLY | JS_ATOD_ACCEPT_BIN_OCT, &atod_mem);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_digit(p[1]))
|
||||||
|
return js_parse_error_pos(s, p, "Unexpected number");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (is_digit(*p))
|
||||||
|
p++;
|
||||||
|
|
||||||
|
if (*p == '.') {
|
||||||
|
p++;
|
||||||
|
if (!is_digit(*p))
|
||||||
|
return js_parse_error_pos(s, p, "Unterminated fractional number");
|
||||||
|
while (is_digit(*p))
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
if (*p == 'e' || *p == 'E') {
|
||||||
|
p++;
|
||||||
|
if (*p == '+' || *p == '-')
|
||||||
|
p++;
|
||||||
|
if (!is_digit(*p))
|
||||||
|
return js_parse_error_pos(s, p, "Exponent part is missing a number");
|
||||||
|
while (is_digit(*p))
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
d = js_atod((const char *)p_start, NULL, 10, 0, &atod_mem);
|
||||||
|
done:
|
||||||
|
s->token.val = TOK_NUMBER;
|
||||||
|
s->token.u.num.val = JS_NewFloat64(s->ctx, d);
|
||||||
|
*pp = p;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static __exception int json_next_token(JSParseState *s)
|
static __exception int json_next_token(JSParseState *s)
|
||||||
{
|
{
|
||||||
const uint8_t *p;
|
const uint8_t *p;
|
||||||
@ -21882,7 +22019,8 @@ static __exception int json_next_token(JSParseState *s)
|
|||||||
}
|
}
|
||||||
/* fall through */
|
/* fall through */
|
||||||
case '\"':
|
case '\"':
|
||||||
if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p))
|
p++;
|
||||||
|
if (json_parse_string(s, &p, c))
|
||||||
goto fail;
|
goto fail;
|
||||||
break;
|
break;
|
||||||
case '\r': /* accept DOS and MAC newline sequences */
|
case '\r': /* accept DOS and MAC newline sequences */
|
||||||
@ -21999,23 +22137,8 @@ static __exception int json_next_token(JSParseState *s)
|
|||||||
case '9':
|
case '9':
|
||||||
/* number */
|
/* number */
|
||||||
parse_number:
|
parse_number:
|
||||||
{
|
if (json_parse_number(s, &p))
|
||||||
JSValue ret;
|
|
||||||
int flags, radix;
|
|
||||||
if (!s->ext_json) {
|
|
||||||
flags = 0;
|
|
||||||
radix = 10;
|
|
||||||
} else {
|
|
||||||
flags = ATOD_ACCEPT_BIN_OCT;
|
|
||||||
radix = 0;
|
|
||||||
}
|
|
||||||
ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix,
|
|
||||||
flags);
|
|
||||||
if (JS_IsException(ret))
|
|
||||||
goto fail;
|
goto fail;
|
||||||
s->token.val = TOK_NUMBER;
|
|
||||||
s->token.u.num.val = ret;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (c >= 128) {
|
if (c >= 128) {
|
||||||
|
@ -12,8 +12,6 @@ test262/test/staging/sm/Function/function-toString-builtin.js:14: Test262Error:
|
|||||||
}' Expected SameValue(«null», «null») to be false
|
}' Expected SameValue(«null», «null») to be false
|
||||||
test262/test/staging/sm/Function/implicit-this-in-parameter-expression.js:13: Test262Error: Expected SameValue(«[object Object]», «undefined») to be true
|
test262/test/staging/sm/Function/implicit-this-in-parameter-expression.js:13: Test262Error: Expected SameValue(«[object Object]», «undefined») to be true
|
||||||
test262/test/staging/sm/Function/invalid-parameter-list.js:35: Error: Assertion failed: expected exception SyntaxError, no exception thrown
|
test262/test/staging/sm/Function/invalid-parameter-list.js:35: Error: Assertion failed: expected exception SyntaxError, no exception thrown
|
||||||
test262/test/staging/sm/JSON/parse-number-syntax.js:39: Test262Error: parsing string <1.> threw a non-SyntaxError exception: Test262Error: string <1.> shouldn't have parsed as JSON Expected SameValue(«false», «true») to be true Expected SameValue(«true», «false») to be true
|
|
||||||
test262/test/staging/sm/JSON/parse-syntax-errors-02.js:51: Test262Error: parsing string <["Illegal backslash escape: \x15"]> threw a non-SyntaxError exception: Test262Error: string <["Illegal backslash escape: \x15"]> shouldn't have parsed as JSON Expected SameValue(«false», «true») to be true Expected SameValue(«true», «false») to be true
|
|
||||||
test262/test/staging/sm/Math/cbrt-approx.js:26: Error: got 1.39561242508609, expected a number near 1.3956124250860895 (relative error: 2)
|
test262/test/staging/sm/Math/cbrt-approx.js:26: Error: got 1.39561242508609, expected a number near 1.3956124250860895 (relative error: 2)
|
||||||
test262/test/staging/sm/RegExp/constructor-ordering-2.js:15: Test262Error: Expected SameValue(«false», «true») to be true
|
test262/test/staging/sm/RegExp/constructor-ordering-2.js:15: Test262Error: Expected SameValue(«false», «true») to be true
|
||||||
test262/test/staging/sm/RegExp/regress-613820-1.js:13: Test262Error: Expected SameValue(«"aaa"», «"aa"») to be true
|
test262/test/staging/sm/RegExp/regress-613820-1.js:13: Test262Error: Expected SameValue(«"aaa"», «"aa"») to be true
|
||||||
|
@ -596,7 +596,7 @@ function test_json()
|
|||||||
]
|
]
|
||||||
]`);
|
]`);
|
||||||
|
|
||||||
assert_json_error('\n" @\\x"');
|
assert_json_error('\n" \\@x"');
|
||||||
assert_json_error('\n{ "a": @x }"');
|
assert_json_error('\n{ "a": @x }"');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ function test_ext_json()
|
|||||||
"y":true, // also a comment
|
"y":true, // also a comment
|
||||||
z2:null, // unquoted property names
|
z2:null, // unquoted property names
|
||||||
"a":[+1,0o10,0xa0,], // plus prefix, octal, hexadecimal
|
"a":[+1,0o10,0xa0,], // plus prefix, octal, hexadecimal
|
||||||
"s":"str",} // trailing comma in objects and arrays
|
"s":'str',} // trailing comma in objects and arrays, single quoted string
|
||||||
`;
|
`;
|
||||||
obj = std.parseExtJSON(input);
|
obj = std.parseExtJSON(input);
|
||||||
assert(JSON.stringify(obj), expected);
|
assert(JSON.stringify(obj), expected);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user