From: Igor Sysoev Date: Wed, 18 Nov 2015 12:45:35 +0000 (+0300) Subject: Support of escape sequences inside strings: "\uXXXX", "\xXX", X-Git-Tag: 0.1.0~122 X-Git-Url: http://git.kaiwu.me/postgresql/log/contrib/postgres_fdw/static/gitweb.js?a=commitdiff_plain;h=6b9a76d337cd496c52748d9d91b68991e21e8238;p=njs.git Support of escape sequences inside strings: "\uXXXX", "\xXX", "\u{X}, "\r\n", string continuations, etc. --- diff --git a/njs/njs_lexer.c b/njs/njs_lexer.c index 05dcc1a6..583a320c 100644 --- a/njs/njs_lexer.c +++ b/njs/njs_lexer.c @@ -472,33 +472,37 @@ njs_lexer_word(njs_lexer_t *lexer, u_char c) static njs_token_t njs_lexer_string(njs_lexer_t *lexer, u_char quote) { - u_char *p, ch; + u_char *p, c; + nxt_bool_t escape; + escape = 0; lexer->text.data = lexer->start; p = lexer->start; while (p < lexer->end) { - /* TODO: end of line, backslash. */ + c = *p++; - ch = *p++; - - if (ch == '\\') { + if (c == '\\') { if (p == lexer->end) { return NJS_TOKEN_ILLEGAL; } - /* STUB: reallocate. */ - p++; + escape = 1; + continue; } - if (ch == quote) { + if (c == quote) { lexer->start = p; lexer->text.len = (p - 1) - lexer->text.data; - return NJS_TOKEN_STRING; + if (escape == 0) { + return NJS_TOKEN_STRING; + } + + return NJS_TOKEN_ESCAPE_STRING; } } diff --git a/njs/njs_number.c b/njs/njs_number.c index 09a84323..4074625f 100644 --- a/njs/njs_number.c +++ b/njs/njs_number.c @@ -110,6 +110,49 @@ njs_number_parse(const u_char **start, const u_char *end) } +int64_t +njs_hex_number_parse(u_char *p, u_char *end) +{ + int8_t d; + uint32_t n; + + static const int8_t hex[256] + nxt_aligned(32) = + { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }; + + n = 0; + + while (p < end) { + d = hex[*p++]; + + if (nxt_slow_path(d < 0)) { + return -1; + } + + n = (n << 4) + d; + } + + return n; +} + + njs_ret_t njs_number_to_string(njs_vm_t *vm, njs_value_t *string, const njs_value_t *number) diff --git a/njs/njs_number.h b/njs/njs_number.h index 2e7aa78e..d59d2284 100644 --- a/njs/njs_number.h +++ b/njs/njs_number.h @@ -25,6 +25,7 @@ double njs_value_to_number(njs_value_t *value); double njs_number_parse(const u_char **start, const u_char *end); +int64_t njs_hex_number_parse(u_char *p, u_char *end); njs_ret_t njs_number_to_string(njs_vm_t *vm, njs_value_t *string, const njs_value_t *number); njs_ret_t njs_number_function(njs_vm_t *vm, njs_param_t *param); diff --git a/njs/njs_parser.c b/njs/njs_parser.c index 476af93f..dca82cbc 100644 --- a/njs/njs_parser.c +++ b/njs/njs_parser.c @@ -60,6 +60,8 @@ static njs_token_t njs_parser_object(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *obj); static njs_token_t njs_parser_array(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *obj); +static njs_token_t njs_parser_escape_string_create(njs_vm_t *vm, + njs_value_t *value); njs_parser_node_t * @@ -1373,6 +1375,18 @@ njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, break; + case NJS_TOKEN_ESCAPE_STRING: + node->token = NJS_TOKEN_STRING; + + nxt_thread_log_debug("JS: '%V'", &parser->lexer->text); + + ret = njs_parser_escape_string_create(vm, &node->u.value); + if (nxt_slow_path(ret != NJS_TOKEN_STRING)) { + return ret; + } + + break; + case NJS_TOKEN_NUMBER: nxt_thread_log_debug("JS: %f", parser->lexer->number); @@ -1674,6 +1688,168 @@ njs_parser_string_create(njs_vm_t *vm, njs_value_t *value) } +static njs_token_t +njs_parser_escape_string_create(njs_vm_t *vm, njs_value_t *value) +{ + u_char c, *p, *start, *dst, *src, *end, *hex_end; + size_t size, length, hex_length, skip; + int64_t u; + + start = NULL; + dst = NULL; + + for ( ;; ) { + /* + * The loop runs twice: at the first step string size and + * UTF-8 length are evaluated. Then the string is allocated + * and at the second step string content is copied. + */ + size = 0; + length = 0; + + src = vm->parser->lexer->text.data; + end = src + vm->parser->lexer->text.len; + + while (src < end) { + c = *src++; + + if (c == '\\') { + /* + * Testing "src == end" is not required here + * since this has been already tested by lexer. + */ + c = *src++; + + switch (c) { + + case 'u': + skip = 0; + hex_length = 4; + + /* + * A character after "u" can be safely tested here + * because there is always a closing quote at the + * end of string: ...\u". + */ + if (*src == '{') { + hex_length = 0; + src++; + + for (p = src; p < end && *p != '}'; p++) { + hex_length++; + } + + if (hex_length == 0 || hex_length > 6) { + return NJS_TOKEN_ILLEGAL; + } + + skip = 1; + } + + goto hex; + + case 'x': + skip = 0; + hex_length = 2; + goto hex; + + case '0': + c = '\0'; + break; + + 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 'v': + c = '\v'; + break; + + case '\r': + /* + * A character after "\r" can be safely tested here + * because there is always a closing quote at the + * end of string: ...\\r". + */ + if (*src == '\n') { + src++; + } + + continue; + + case '\n': + continue; + + default: + break; + } + } + + size++; + length++; + + if (dst != NULL) { + *dst++ = c; + } + + continue; + + hex: + + hex_end = src + hex_length; + + if (hex_end > end) { + return NJS_TOKEN_ILLEGAL; + } + + u = njs_hex_number_parse(src, hex_end); + if (nxt_slow_path(u < 0)) { + return NJS_TOKEN_ILLEGAL; + } + + src = hex_end + skip; + size += nxt_utf8_size(u); + length++; + + if (dst != NULL) { + dst = nxt_utf8_encode(dst, (uint32_t) u); + } + } + + if (start != NULL) { + if (length > NJS_STRING_MAP_OFFSET && length != size) { + njs_string_offset_map_init(start, size); + } + + return NJS_TOKEN_STRING; + } + + start = njs_string_alloc(vm, value, size, length); + if (nxt_slow_path(start == NULL)) { + return NJS_TOKEN_ERROR; + } + + dst = start; + } +} + + njs_index_t njs_parser_index(njs_parser_t *parser, uint32_t scope) { diff --git a/njs/njs_parser.h b/njs/njs_parser.h index 0faa0589..992f30ae 100644 --- a/njs/njs_parser.h +++ b/njs/njs_parser.h @@ -115,6 +115,7 @@ typedef enum { #define NJS_TOKEN_LAST_CONST NJS_TOKEN_STRING + NJS_TOKEN_ESCAPE_STRING, NJS_TOKEN_NAME, NJS_TOKEN_OBJECT_CREATE, diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c index 7791ca9b..eaeafd82 100644 --- a/njs/test/njs_unit_test.c +++ b/njs/test/njs_unit_test.c @@ -39,11 +39,6 @@ static njs_unit_test_t njs_test[] = nxt_string("1.7976931348623157e+308") }, #endif -#if 0 - { nxt_string("var a = 'a\\'b'"), - nxt_string("a'b") }, -#endif - { nxt_string("+1"), nxt_string("1") }, @@ -1699,6 +1694,50 @@ static njs_unit_test_t njs_test[] = " a = b"), nxt_string("abcdefghijklmnop") }, + /* Escape strings. */ + + { nxt_string("'\\a \\' \\\" \\\\ \\0 \\b \\f \\n \\r \\t \\v'"), + nxt_string("a ' \" \\ \0 \b \f \n \r \t \v") }, + + { nxt_string("'a\\\nb'"), + nxt_string("ab") }, + + { nxt_string("'a\\\rb'"), + nxt_string("ab") }, + + { nxt_string("'a\\\r\nb'"), + nxt_string("ab") }, + + { nxt_string("'\\'"), + nxt_string("SyntaxError") }, + + { nxt_string("'\\u03B1'"), + nxt_string("α") }, + + { nxt_string("'\\u'"), + nxt_string("SyntaxError") }, + + { nxt_string("'\\u03B'"), + nxt_string("SyntaxError") }, + + { nxt_string("'\\u{61}\\u{3B1}\\u{20AC}'"), + nxt_string("aα€") }, + + { nxt_string("'\\u'"), + nxt_string("SyntaxError") }, + + { nxt_string("'\\u{'"), + nxt_string("SyntaxError") }, + + { nxt_string("'\\u{}'"), + nxt_string("SyntaxError") }, + + { nxt_string("'\\u{1234567}'"), + nxt_string("SyntaxError") }, + + { nxt_string("'\\x61'"), + nxt_string("a") }, + { nxt_string("''.length"), nxt_string("0") }, diff --git a/nxt/nxt_utf8.h b/nxt/nxt_utf8.h index 13f42e16..24e53ff5 100644 --- a/nxt/nxt_utf8.h +++ b/nxt/nxt_utf8.h @@ -57,4 +57,8 @@ nxt_utf8_next(const u_char *p, const u_char *end) } +#define nxt_utf8_size(u) \ + ((u < 0x80) ? 1 : ((u < 0x0800) ? 2 : ((u < 0x10000) ? 3 : 4))) + + #endif /* _NXT_UTF8_H_INCLUDED_ */