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