From: Dmitry Volyntsev Date: Tue, 18 Feb 2020 15:55:50 +0000 (+0300) Subject: Fixed encodeURI() and decodeURI() according to the spec. X-Git-Url: http://www.kaiwu.me/postgresql/commit/?a=commitdiff_plain;h=bdc79b20b04634b6855cff506bc168f0896f1298;p=njs.git Fixed encodeURI() and decodeURI() according to the spec. --- diff --git a/src/njs_builtin.c b/src/njs_builtin.c index 2804b7d8..56272e2e 100644 --- a/src/njs_builtin.c +++ b/src/njs_builtin.c @@ -1018,7 +1018,7 @@ static const njs_object_prop_t njs_global_this_object_properties[] = { .type = NJS_PROPERTY, .name = njs_string("encodeURI"), - .value = njs_native_function(njs_string_encode_uri, 1), + .value = njs_native_function2(njs_string_encode_uri, 1, 0), .writable = 1, .configurable = 1, }, @@ -1026,7 +1026,7 @@ static const njs_object_prop_t njs_global_this_object_properties[] = { .type = NJS_PROPERTY, .name = njs_long_string("encodeURIComponent"), - .value = njs_native_function(njs_string_encode_uri_component, 1), + .value = njs_native_function2(njs_string_encode_uri, 1, 1), .writable = 1, .configurable = 1, }, @@ -1034,7 +1034,7 @@ static const njs_object_prop_t njs_global_this_object_properties[] = { .type = NJS_PROPERTY, .name = njs_string("decodeURI"), - .value = njs_native_function(njs_string_decode_uri, 1), + .value = njs_native_function2(njs_string_decode_uri, 1, 0), .writable = 1, .configurable = 1, }, @@ -1042,7 +1042,7 @@ static const njs_object_prop_t njs_global_this_object_properties[] = { .type = NJS_PROPERTY, .name = njs_long_string("decodeURIComponent"), - .value = njs_native_function(njs_string_decode_uri_component, 1), + .value = njs_native_function2(njs_string_decode_uri, 1, 1), .writable = 1, .configurable = 1, }, diff --git a/src/njs_json.c b/src/njs_json.c index 7fabba50..52c52b55 100644 --- a/src/njs_json.c +++ b/src/njs_json.c @@ -684,9 +684,7 @@ njs_json_parse_string(njs_json_parse_ctx_t *ctx, njs_value_t *value, utf = njs_json_unicode(p); p += 4; - if (utf >= 0xd800 && utf <= 0xdfff) { - - /* Surrogate pair. */ + if (njs_surrogate_any(utf)) { if (utf > 0xdbff || p[0] != '\\' || p[1] != 'u') { s = njs_utf8_encode(s, NJS_UTF8_REPLACEMENT); @@ -698,10 +696,10 @@ njs_json_parse_string(njs_json_parse_ctx_t *ctx, njs_value_t *value, utf_low = njs_json_unicode(p); p += 4; - if (njs_fast_path(utf_low >= 0xdc00 && utf_low <= 0xdfff)) { + if (njs_fast_path(njs_surrogate_trailing(utf_low))) { utf = njs_string_surrogate_pair(utf, utf_low); - } else if (utf_low >= 0xd800 && utf_low <= 0xdbff) { + } else if (njs_surrogate_leading(utf_low)) { utf = NJS_UTF8_REPLACEMENT; s = njs_utf8_encode(s, NJS_UTF8_REPLACEMENT); diff --git a/src/njs_parser_terminal.c b/src/njs_parser_terminal.c index a8ca3f30..e1aeecbb 100644 --- a/src/njs_parser_terminal.c +++ b/src/njs_parser_terminal.c @@ -1111,10 +1111,10 @@ njs_parser_escape_string_create(njs_vm_t *vm, njs_parser_t *parser, } if (cp_pair != 0) { - if (njs_fast_path(cp >= 0xdc00 && cp <= 0xdfff)) { + if (njs_fast_path(njs_surrogate_trailing(cp))) { cp = njs_string_surrogate_pair(cp_pair, cp); - } else if (njs_slow_path(cp >= 0xd800 && cp <= 0xdbff)) { + } else if (njs_slow_path(njs_surrogate_leading(cp))) { cp = NJS_UTF8_REPLACEMENT; dst = njs_utf8_encode(dst, (uint32_t) cp); @@ -1125,7 +1125,7 @@ njs_parser_escape_string_create(njs_vm_t *vm, njs_parser_t *parser, cp_pair = 0; - } else if (cp >= 0xd800 && cp <= 0xdfff) { + } else if (njs_surrogate_any(cp)) { if (cp <= 0xdbff && src[0] == '\\' && src[1] == 'u') { cp_pair = cp; continue; @@ -1256,10 +1256,10 @@ njs_parser_escape_string_calc_length(njs_vm_t *vm, njs_parser_t *parser, } if (cp_pair != 0) { - if (njs_fast_path(cp >= 0xdc00 && cp <= 0xdfff)) { + if (njs_fast_path(njs_surrogate_trailing(cp))) { cp = njs_string_surrogate_pair(cp_pair, cp); - } else if (njs_slow_path(cp >= 0xd800 && cp <= 0xdbff)) { + } else if (njs_slow_path(njs_surrogate_leading(cp))) { cp = NJS_UTF8_REPLACEMENT; size += njs_utf8_size(cp); @@ -1272,7 +1272,7 @@ njs_parser_escape_string_calc_length(njs_vm_t *vm, njs_parser_t *parser, cp_pair = 0; - } else if (cp >= 0xd800 && cp <= 0xdfff) { + } else if (njs_surrogate_any(cp)) { if (cp <= 0xdbff && src[0] == '\\' && src[1] == 'u') { cp_pair = cp; continue; diff --git a/src/njs_string.c b/src/njs_string.c index 8850a0b8..297d614b 100644 --- a/src/njs_string.c +++ b/src/njs_string.c @@ -91,10 +91,6 @@ static njs_int_t njs_string_replace_substitute(njs_vm_t *vm, static njs_int_t njs_string_replace_join(njs_vm_t *vm, njs_string_replace_t *r); static void njs_string_replacement_copy(njs_string_replace_part_t *string, const njs_value_t *value); -static njs_int_t njs_string_encode(njs_vm_t *vm, njs_value_t *value, - const uint32_t *escape); -static njs_int_t njs_string_decode(njs_vm_t *vm, njs_value_t *value, - const uint32_t *reserve); #define njs_base64_encoded_length(len) (((len + 2) / 3) * 4) @@ -4698,14 +4694,55 @@ const njs_object_init_t njs_string_instance_init = { }; +njs_inline njs_bool_t +njs_need_escape(const uint32_t *escape, uint32_t byte) +{ + return ((escape[byte >> 5] & ((uint32_t) 1 << (byte & 0x1f))) != 0); +} + + +njs_inline u_char * +njs_string_encode(const uint32_t *escape, size_t size, const u_char *src, + u_char *dst) +{ + uint8_t byte; + static const u_char hex[16] = "0123456789ABCDEF"; + + do { + byte = *src++; + + if (njs_need_escape(escape, byte)) { + *dst++ = '%'; + *dst++ = hex[byte >> 4]; + *dst++ = hex[byte & 0xf]; + + } else { + *dst++ = byte; + } + + size--; + + } while (size != 0); + + return dst; +} + + njs_int_t njs_string_encode_uri(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused) + njs_index_t component) { - njs_int_t ret; - njs_value_t *value; + u_char byte, *dst; + uint64_t size; + uint32_t cp, cp_low; + njs_int_t ret; + njs_value_t *value; + const u_char *src, *end; + const uint32_t *escape; + njs_string_prop_t string; + u_char encode[4]; - static const uint32_t escape[] = { + static const uint32_t escape_uri[] = { 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ @@ -4723,33 +4760,7 @@ njs_string_encode_uri(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ }; - if (nargs < 2) { - njs_set_undefined(&vm->retval); - - return NJS_OK; - } - - value = njs_argument(args, 1); - - if (!njs_is_string(value)) { - ret = njs_value_to_string(vm, value, value); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - } - - return njs_string_encode(vm, value, escape); -} - - -njs_int_t -njs_string_encode_uri_component(njs_vm_t *vm, njs_value_t *args, - njs_uint_t nargs, njs_index_t unused) -{ - njs_int_t ret; - njs_value_t *value; - - static const uint32_t escape[] = { + static const uint32_t escape_uri_component[] = { 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ @@ -4768,91 +4779,172 @@ njs_string_encode_uri_component(njs_vm_t *vm, njs_value_t *args, }; if (nargs < 2) { - njs_set_undefined(&vm->retval); - + vm->retval = njs_string_undefined; return NJS_OK; } value = njs_argument(args, 1); + ret = njs_value_to_string(vm, value, value); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } - if (!njs_is_string(value)) { - ret = njs_value_to_string(vm, value, value); - if (njs_slow_path(ret != NJS_OK)) { - return ret; + escape = (component) ? escape_uri_component : escape_uri; + + njs_prefetch(escape); + + (void) njs_string_prop(&string, value); + + size = 0; + src = string.start; + end = src + string.size; + + if (string.length == 0 || string.length == string.size) { + /* Byte or ASCII string. */ + + while (src < end) { + byte = *src++; + size += njs_need_escape(escape, byte) ? 3 : 1; } - } - return njs_string_encode(vm, value, escape); -} + } else { + /* UTF-8 string. */ + while (src < end) { + cp = njs_utf8_decode(&src, end); -static njs_int_t -njs_string_encode(njs_vm_t *vm, njs_value_t *value, const uint32_t *escape) -{ - u_char byte, *src, *dst; - size_t n, size; - njs_str_t string; - static const u_char hex[16] = "0123456789ABCDEF"; + if (cp < 0x80 && !njs_need_escape(escape, cp)) { + size++; + continue; + } - njs_prefetch(escape); + if (njs_slow_path(njs_surrogate_any(cp))) { + if (src == end) { + goto uri_error; + } - njs_string_get(value, &string); + if (njs_surrogate_leading(cp)) { + cp_low = njs_utf8_decode(&src, end); - src = string.start; - n = 0; + if (njs_slow_path(!njs_surrogate_trailing(cp_low))) { + goto uri_error; + } - for (size = string.length; size != 0; size--) { - byte = *src++; + cp = njs_string_surrogate_pair(cp, cp_low); + size += njs_utf8_size(cp) * 3; + continue; + } + + goto uri_error; + } - if ((escape[byte >> 5] & ((uint32_t) 1 << (byte & 0x1f))) != 0) { - n += 2; + size += njs_utf8_size(cp) * 3; } } - if (n == 0) { + if (size == 0) { /* GC: retain src. */ vm->retval = *value; return NJS_OK; } - size = string.length + n; - dst = njs_string_alloc(vm, &vm->retval, size, size); if (njs_slow_path(dst == NULL)) { return NJS_ERROR; } - size = string.length; src = string.start; - do { - byte = *src++; + if (string.length == 0 || string.length == string.size) { + /* Byte or ASCII string. */ + (void) njs_string_encode(escape, string.size, src, dst); + return NJS_OK; + } - if ((escape[byte >> 5] & ((uint32_t) 1 << (byte & 0x1f))) != 0) { - *dst++ = '%'; - *dst++ = hex[byte >> 4]; - *dst++ = hex[byte & 0xf]; + /* UTF-8 string. */ - } else { - *dst++ = byte; + while (src < end) { + cp = njs_utf8_decode(&src, end); + + if (njs_slow_path(njs_surrogate_leading(cp))) { + cp_low = njs_utf8_decode(&src, end); + cp = njs_string_surrogate_pair(cp, cp_low); } - size--; + njs_utf8_encode(encode, cp); - } while (size != 0); + dst = njs_string_encode(escape, njs_utf8_size(cp), encode, dst); + } return NJS_OK; + +uri_error: + + njs_uri_error(vm, "malformed URI"); + + return NJS_ERROR; +} + + +njs_inline uint32_t +njs_string_decode_uri_cp(const int8_t *hex, const u_char **start, + const u_char *end, njs_bool_t expect_percent) +{ + int8_t d0, d1; + uint32_t cp; + const u_char *p; + + cp = njs_utf8_decode(start, end); + if (njs_fast_path(cp != '%')) { + return expect_percent ? 0xFFFFFFFF: cp; + } + + p = *start; + + if (njs_slow_path((p + 1) >= end)) { + return 0xFFFFFFFF; + } + + d0 = hex[*p++]; + if (njs_slow_path(d0 < 0)) { + return 0xFFFFFFFF; + } + + d1 = hex[*p++]; + if (njs_slow_path(d1 < 0)) { + return 0xFFFFFFFF; + } + + *start += 2; + return (d0 << 4) + d1; +} + + +njs_inline njs_bool_t +njs_reserved(const uint32_t *reserve, uint32_t byte) +{ + return ((reserve[byte >> 5] & ((uint32_t) 1 << (byte & 0x1f))) != 0); } njs_int_t njs_string_decode_uri(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused) + njs_index_t component) { - njs_int_t ret; - njs_value_t *value; + u_char *dst; + int64_t size, length; + uint32_t cp; + njs_int_t ret; + njs_chb_t chain; + njs_uint_t i, n; + njs_bool_t percent; + njs_value_t *value; + const u_char *src, *p, *end; + const uint32_t *reserve; + njs_string_prop_t string; + u_char encode[4]; - static const uint32_t reserve[] = { + static const uint32_t reserve_uri[] = { 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ @@ -4870,33 +4962,7 @@ njs_string_decode_uri(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ }; - if (nargs < 2) { - njs_set_undefined(&vm->retval); - - return NJS_OK; - } - - value = njs_argument(args, 1); - - if (njs_slow_path(!njs_is_string(value))) { - ret = njs_value_to_string(vm, value, value); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - } - - return njs_string_decode(vm, value, reserve); -} - - -njs_int_t -njs_string_decode_uri_component(njs_vm_t *vm, njs_value_t *args, - njs_uint_t nargs, njs_index_t unused) -{ - njs_int_t ret; - njs_value_t *value; - - static const uint32_t reserve[] = { + static const uint32_t reserve_uri_component[] = { 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ @@ -4914,35 +4980,6 @@ njs_string_decode_uri_component(njs_vm_t *vm, njs_value_t *args, 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ }; - if (nargs < 2) { - njs_set_undefined(&vm->retval); - - return NJS_OK; - } - - value = njs_argument(args, 1); - - if (njs_slow_path(!njs_is_string(value))) { - ret = njs_value_to_string(vm, value, value); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - } - - return njs_string_decode(vm, &args[1], reserve); -} - - -static njs_int_t -njs_string_decode(njs_vm_t *vm, njs_value_t *value, const uint32_t *reserve) -{ - int8_t d0, d1; - u_char byte, *start, *src, *dst; - size_t n; - ssize_t size, length; - njs_str_t string; - njs_bool_t utf8; - static const int8_t hex[256] njs_aligned(32) = { @@ -4964,104 +5001,126 @@ njs_string_decode(njs_vm_t *vm, njs_value_t *value, const uint32_t *reserve) -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }; - njs_prefetch(&hex['0']); - njs_prefetch(reserve); - - njs_string_get(value, &string); + if (nargs < 2) { + vm->retval = njs_string_undefined; + return NJS_OK; + } - src = string.start; - n = 0; + value = njs_argument(args, 1); + ret = njs_value_to_string(vm, value, value); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } - for (size = string.length; size != 0; size--) { - byte = *src++; + reserve = component ? reserve_uri_component : reserve_uri; - if (byte == '%') { - size -= 2; + njs_prefetch(reserve); + njs_prefetch(&hex['0']); - if (size <= 0) { - goto uri_error; - } + (void) njs_string_prop(&string, value); - d0 = hex[*src++]; - if (d0 < 0) { - goto uri_error; - } + length = 0; + src = string.start; + end = string.start + string.size; - d1 = hex[*src++]; - if (d1 < 0) { - goto uri_error; - } + njs_chb_init(&chain, vm->mem_pool); - byte = (d0 << 4) + d1; + while (src < end) { + percent = (src[0] == '%'); + cp = njs_string_decode_uri_cp(hex, &src, end, 0); + if (njs_slow_path(cp == 0xFFFFFFFF)) { + goto uri_error; + } - if ((reserve[byte >> 5] & ((uint32_t) 1 << (byte & 0x1f))) == 0) { - n += 2; + if (!percent) { + length += 1; + dst = njs_chb_reserve(&chain, 4); + if (dst != NULL) { + njs_utf8_encode(dst, cp); + njs_chb_written(&chain, njs_utf8_size(cp)); } - } - } - if (n == 0) { - /* GC: retain src. */ - vm->retval = *value; - return NJS_OK; - } + continue; + } - n = string.length - n; + if (cp < 0x80) { + if (njs_reserved(reserve, cp)) { + length += 3; + njs_chb_append(&chain, &src[-3], 3); - start = njs_string_alloc(vm, &vm->retval, n, n); - if (njs_slow_path(start == NULL)) { - return NJS_ERROR; - } + } else { + length += 1; + dst = njs_chb_reserve(&chain, 1); + if (dst != NULL) { + *dst = cp; + njs_chb_written(&chain, 1); + } + } - utf8 = 0; - dst = start; - size = string.length; - src = string.start; + continue; + } - do { - byte = *src++; + n = 1; - if (byte == '%') { - size -= 2; + do { + n++; + } while (((cp << n) & 0x80)); - d0 = hex[*src++]; - d1 = hex[*src++]; - byte = (d0 << 4) + d1; + if (njs_slow_path(n > 4)) { + goto uri_error; + } - utf8 |= (byte >= 0x80); + encode[0] = cp; - if ((reserve[byte >> 5] & ((uint32_t) 1 << (byte & 0x1f))) != 0) { - *dst++ = '%'; - *dst++ = src[-2]; - byte = src[-1]; + for (i = 1; i < n; i++) { + cp = njs_string_decode_uri_cp(hex, &src, end, 1); + if (njs_slow_path(cp == 0xFFFFFFFF)) { + goto uri_error; } + + encode[i] = cp; } - *dst++ = byte; - size--; + p = encode; + cp = njs_utf8_decode(&p, p + n); + if (njs_slow_path(cp == 0xFFFFFFFF)) { + goto uri_error; + } - } while (size != 0); + dst = njs_chb_reserve(&chain, 4); + if (dst != NULL) { + njs_utf8_encode(dst, cp); + njs_chb_written(&chain, njs_utf8_size(cp)); + } - if (utf8) { - length = njs_utf8_length(start, n); + length += 1; + } - if (length < 0) { - length = 0; - } + size = njs_chb_size(&chain); + if (njs_slow_path(size < 0)) { + njs_memory_error(vm); + return NJS_ERROR; + } - if (vm->retval.short_string.size != NJS_STRING_LONG) { - vm->retval.short_string.length = length; + if (size == 0) { + /* GC: retain src. */ + vm->retval = *value; + return NJS_OK; + } - } else { - vm->retval.long_string.data->length = length; - } + dst = njs_string_alloc(vm, &vm->retval, size, length); + if (njs_slow_path(dst == NULL)) { + return NJS_ERROR; } + njs_chb_join_to(&chain, dst); + njs_chb_destroy(&chain); + return NJS_OK; uri_error: - njs_uri_error(vm, NULL); + njs_uri_error(vm, "malformed URI"); return NJS_ERROR; } diff --git a/src/njs_string.h b/src/njs_string.h index 32204009..c78510ca 100644 --- a/src/njs_string.h +++ b/src/njs_string.h @@ -26,6 +26,12 @@ /* The maximum signed int32_t. */ #define NJS_STRING_MAX_LENGTH 0x7fffffff +#define njs_surrogate_leading(cp) ((cp) >= 0xd800 && (cp) <= 0xdbff) + +#define njs_surrogate_trailing(cp) ((cp) >= 0xdc00 && (cp) <= 0xdfff) + +#define njs_surrogate_any(cp) ((cp) >= 0xd800 && (cp) <= 0xdfff) + /* Converting surrogate pair to code point. */ #define njs_string_surrogate_pair(high, low) \ (0x10000 + ((high - 0xd800) << 10) + (low - 0xdc00)) @@ -184,13 +190,9 @@ void njs_string_offset_map_init(const u_char *start, size_t size); double njs_string_to_index(const njs_value_t *value); const char *njs_string_to_c_string(njs_vm_t *vm, njs_value_t *value); njs_int_t njs_string_encode_uri(njs_vm_t *vm, njs_value_t *args, - njs_uint_t nargs, njs_index_t unused); -njs_int_t njs_string_encode_uri_component(njs_vm_t *vm, njs_value_t *args, - njs_uint_t nargs, njs_index_t unused); + njs_uint_t nargs, njs_index_t component); njs_int_t njs_string_decode_uri(njs_vm_t *vm, njs_value_t *args, - njs_uint_t nargs, njs_index_t unused); -njs_int_t njs_string_decode_uri_component(njs_vm_t *vm, njs_value_t *args, - njs_uint_t nargs, njs_index_t unused); + njs_uint_t nargs, njs_index_t component); njs_index_t njs_value_index(njs_vm_t *vm, const njs_value_t *src, njs_uint_t runtime); diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index 4f9557a6..b6361921 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -7979,12 +7979,20 @@ static njs_unit_test_t njs_test[] = { njs_str("encodeURI.length"), njs_str("1")}, - { njs_str("encodeURI()"), - njs_str("undefined")}, - { njs_str("encodeURI('012абв')"), njs_str("012%D0%B0%D0%B1%D0%B2")}, + { njs_str("[" + " String.fromCharCode(0xD800)," + " String.fromCharCode(0xD800) + 'a'," + " String.fromCharCode(0xDC00)," + " String.fromCharCode(0xDC00) + 'a'," + "].every(v=>{try { encodeURI(v)} catch(e) {return e.name == 'URIError'}})"), + njs_str("true")}, + + { njs_str("encodeURI(String.fromCharCode(0xD800)+String.fromCharCode(0xDC00))"), + njs_str("%F0%90%80%80")}, + { njs_str("encodeURI('~}|{`_^]\\\\[@?>=<;:/.-,+*)(\\\'&%$#\"! ')"), njs_str("~%7D%7C%7B%60_%5E%5D%5C%5B@?%3E=%3C;:/.-,+*)('&%25$#%22!%20")}, @@ -8003,18 +8011,6 @@ static njs_unit_test_t njs_test[] = { njs_str("decodeURI.length"), njs_str("1")}, - { njs_str("decodeURI()"), - njs_str("undefined")}, - - { njs_str("decodeURI('%QQ')"), - njs_str("URIError")}, - - { njs_str("decodeURI('%')"), - njs_str("URIError")}, - - { njs_str("decodeURI('%0')"), - njs_str("URIError")}, - { njs_str("decodeURI('%00')"), njs_str("\0")}, @@ -8039,8 +8035,32 @@ static njs_unit_test_t njs_test[] = { njs_str("decodeURI('%D0%B0%D0%B1%D0%B2').length"), njs_str("3")}, - { njs_str("decodeURI('%80%81%82').length"), - njs_str("3")}, + { njs_str("[" + " '%'," + " '%0'," + " '%QQ'," + " '%C0%10'," + " '%DC%C7'," + " '%80%81%82'," + " '%EF%5C%A0'," + " '%EF%A0%5E'," + " '%E0%EF%A0'," + " '%E0%A0%EF'," + " '%FF%A2%95%BB'," + "].every(v=>{try { decodeURI(v)} catch(e) {return e.name == 'URIError'}})"), + njs_str("true")}, + + { njs_str("[" + " 'abc'," + " 'αβγ'," + " '𝟘𝟙𝟚𝟛'," + " String.fromCodePoint(0x20000)," + "].every(v=>decodeURI(encodeURI(v)) === v)"), + njs_str("true")}, + + { njs_str("[encodeURI, encodeURIComponent, decodeURI, decodeURIComponent]" + ".every(v=>{var r = v(); return (typeof r === 'string') && r === 'undefined';})"), + njs_str("true")}, /* Functions. */