From 770be1b6b75fb00e8a3d7a1fc496d3f3389dc7a2 Mon Sep 17 00:00:00 2001 From: Igor Sysoev Date: Tue, 30 Aug 2016 12:02:31 +0300 Subject: [PATCH] encodeURI() and encodeURIComponent() functions. --- njs/njs_builtin.c | 16 +++-- njs/njs_generator.c | 2 + njs/njs_lexer_keyword.c | 2 + njs/njs_parser.c | 2 + njs/njs_parser.h | 2 + njs/njs_string.c | 133 +++++++++++++++++++++++++++++++++++++++ njs/njs_string.h | 4 ++ njs/njs_vm.h | 20 +++--- njs/test/njs_unit_test.c | 12 ++++ 9 files changed, 178 insertions(+), 15 deletions(-) diff --git a/njs/njs_builtin.c b/njs/njs_builtin.c index e38379eb..16870f63 100644 --- a/njs/njs_builtin.c +++ b/njs/njs_builtin.c @@ -82,12 +82,14 @@ njs_builtin_objects_create(njs_vm_t *vm) }; static const njs_object_init_t *function_init[] = { - &njs_eval_function_init, - NULL, - NULL, - NULL, - NULL, - NULL, + &njs_eval_function_init, /* eval */ + NULL, /* toString */ + NULL, /* isNaN */ + NULL, /* isFinite */ + NULL, /* parseInt */ + NULL, /* parseFloat */ + NULL, /* encodeURI */ + NULL, /* encodeURIComponent */ }; static const njs_function_init_t native_functions[] = { @@ -99,6 +101,8 @@ njs_builtin_objects_create(njs_vm_t *vm) { njs_number_parse_int, { NJS_SKIP_ARG, NJS_STRING_ARG, NJS_INTEGER_ARG } }, { njs_number_parse_float, { NJS_SKIP_ARG, NJS_STRING_ARG } }, + { njs_string_encode_uri, { NJS_SKIP_ARG, NJS_STRING_ARG } }, + { njs_string_encode_uri_component, { NJS_SKIP_ARG, NJS_STRING_ARG } }, }; static const njs_object_prop_t null_proto_property = { diff --git a/njs/njs_generator.c b/njs/njs_generator.c index 5f8fa52c..5f210bc0 100644 --- a/njs/njs_generator.c +++ b/njs/njs_generator.c @@ -300,6 +300,8 @@ njs_generator(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node) case NJS_TOKEN_IS_FINITE: case NJS_TOKEN_PARSE_INT: case NJS_TOKEN_PARSE_FLOAT: + case NJS_TOKEN_ENCODE_URI: + case NJS_TOKEN_ENCODE_URI_COMPONENT: return njs_generate_builtin_object(vm, parser, node); case NJS_TOKEN_FUNCTION: diff --git a/njs/njs_lexer_keyword.c b/njs/njs_lexer_keyword.c index d6d39fd8..f58d557e 100644 --- a/njs/njs_lexer_keyword.c +++ b/njs/njs_lexer_keyword.c @@ -93,6 +93,8 @@ static const njs_keyword_t njs_keywords[] = { { nxt_string("isFinite"), NJS_TOKEN_IS_FINITE, 0 }, { nxt_string("parseInt"), NJS_TOKEN_PARSE_INT, 0 }, { nxt_string("parseFloat"), NJS_TOKEN_PARSE_FLOAT, 0 }, + { nxt_string("encodeURI"), NJS_TOKEN_ENCODE_URI, 0 }, + { nxt_string("encodeURIComponent"), NJS_TOKEN_ENCODE_URI_COMPONENT, 0 }, /* Reserved words. */ diff --git a/njs/njs_parser.c b/njs/njs_parser.c index c5635f66..96c5f0d6 100644 --- a/njs/njs_parser.c +++ b/njs/njs_parser.c @@ -1671,6 +1671,8 @@ njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token) case NJS_TOKEN_IS_FINITE: case NJS_TOKEN_PARSE_INT: case NJS_TOKEN_PARSE_FLOAT: + case NJS_TOKEN_ENCODE_URI: + case NJS_TOKEN_ENCODE_URI_COMPONENT: return njs_parser_builtin_function(vm, parser, node); default: diff --git a/njs/njs_parser.h b/njs/njs_parser.h index 312dfa64..2b93688f 100644 --- a/njs/njs_parser.h +++ b/njs/njs_parser.h @@ -181,6 +181,8 @@ typedef enum { NJS_TOKEN_IS_FINITE, NJS_TOKEN_PARSE_INT, NJS_TOKEN_PARSE_FLOAT, + NJS_TOKEN_ENCODE_URI, + NJS_TOKEN_ENCODE_URI_COMPONENT, NJS_TOKEN_RESERVED, } njs_token_t; diff --git a/njs/njs_string.c b/njs/njs_string.c index 5efec849..82737bb7 100644 --- a/njs/njs_string.c +++ b/njs/njs_string.c @@ -45,6 +45,8 @@ static njs_ret_t njs_string_match_multiple(njs_vm_t *vm, njs_value_t *args, njs_regexp_pattern_t *pattern); static njs_ret_t njs_string_split_part_add(njs_vm_t *vm, njs_array_t *array, u_char *start, size_t size, nxt_uint_t utf8); +static njs_ret_t njs_string_encode(njs_vm_t *vm, njs_value_t *value, + const uint32_t *escape); njs_ret_t @@ -2105,6 +2107,137 @@ const njs_object_init_t njs_string_prototype_init = { }; +/* + * encodeURI(string) + */ + +njs_ret_t +njs_string_encode_uri(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + static const uint32_t escape[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x50000025, /* 0101 0000 0000 0000 0000 0000 0010 0101 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x78000000, /* 0111 1000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + if (nargs > 1) { + return njs_string_encode(vm, &args[1], escape); + } + + vm->retval = njs_string_void; + + return NXT_OK; +} + + +/* + * encodeURIComponent(string) + */ + +njs_ret_t +njs_string_encode_uri_component(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused) +{ + static const uint32_t escape[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0xfc00987d, /* 1111 1100 0000 0000 1001 1000 0111 1101 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x78000001, /* 0111 1000 0000 0000 0000 0000 0000 0001 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + if (nargs > 1) { + return njs_string_encode(vm, &args[1], escape); + } + + vm->retval = njs_string_void; + + return NXT_OK; +} + + +static njs_ret_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_string_prop_t string; + static const u_char hex[16] = "0123456789ABCDEF"; + + nxt_prefetch(escape); + + (void) njs_string_prop(&string, value); + + src = string.start; + n = 0; + + for (size = string.size; size != 0; size--) { + byte = *src++; + + if ((escape[byte >> 5] & ((uint32_t) 1 << (byte & 0x1f))) != 0) { + n += 2; + } + } + + if (n == 0) { + /* GC: retain src. */ + vm->retval = *value; + return NXT_OK; + } + + size = string.size + n; + + dst = njs_string_alloc(vm, &vm->retval, size, size); + if (nxt_slow_path(dst == NULL)) { + return NXT_ERROR; + } + + size = string.size; + src = string.start; + + do { + byte = *src++; + + if ((escape[byte >> 5] & ((uint32_t) 1 << (byte & 0x1f))) != 0) { + *dst++ = '%'; + *dst++ = hex[byte >> 4]; + *dst++ = hex[byte & 0xf]; + + } else { + *dst++ = byte; + } + + size--; + + } while (size != 0); + + return NXT_OK; +} + + static nxt_int_t njs_values_hash_test(nxt_lvlhsh_query_t *lhq, void *data) { diff --git a/njs/njs_string.h b/njs/njs_string.h index e651c705..45e1d441 100644 --- a/njs/njs_string.h +++ b/njs/njs_string.h @@ -104,6 +104,10 @@ nxt_noinline uint32_t njs_string_index(njs_string_prop_t *string, njs_ret_t njs_primitive_value_to_string(njs_vm_t *vm, njs_value_t *dst, const njs_value_t *src); double njs_string_to_number(njs_value_t *value, nxt_bool_t exact); +njs_ret_t njs_string_encode_uri(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused); +njs_ret_t njs_string_encode_uri_component(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused); njs_index_t njs_value_index(njs_vm_t *vm, njs_parser_t *parser, const njs_value_t *src); diff --git a/njs/njs_vm.h b/njs/njs_vm.h index 79369145..e2ff1143 100644 --- a/njs/njs_vm.h +++ b/njs/njs_vm.h @@ -668,7 +668,7 @@ typedef enum { enum njs_prototypes_e { - NJS_PROTOTYPE_OBJECT = 0, + NJS_PROTOTYPE_OBJECT = 0, NJS_PROTOTYPE_ARRAY, NJS_PROTOTYPE_BOOLEAN, NJS_PROTOTYPE_NUMBER, @@ -698,19 +698,21 @@ enum njs_constructor_e { enum njs_object_e { - NJS_OBJECT_MATH = 0, + NJS_OBJECT_MATH = 0, #define NJS_OBJECT_MAX (NJS_OBJECT_MATH + 1) }; enum njs_function_e { - NJS_FUNCTION_EVAL = 0, - NJS_FUNCTION_TO_STRING = 1, - NJS_FUNCTION_IS_NAN = 2, - NJS_FUNCTION_IS_FINITE = 3, - NJS_FUNCTION_PARSE_INT = 4, - NJS_FUNCTION_PARSE_FLOAT = 5, -#define NJS_FUNCTION_MAX (NJS_FUNCTION_PARSE_FLOAT + 1) + NJS_FUNCTION_EVAL = 0, + NJS_FUNCTION_TO_STRING, + NJS_FUNCTION_IS_NAN, + NJS_FUNCTION_IS_FINITE, + NJS_FUNCTION_PARSE_INT, + NJS_FUNCTION_PARSE_FLOAT, + NJS_FUNCTION_STRING_ENCODE_URI, + NJS_FUNCTION_STRING_ENCODE_URI_COMPONENT, +#define NJS_FUNCTION_MAX (NJS_FUNCTION_STRING_ENCODE_URI_COMPONENT + 1) }; diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c index 2fbed344..2ca4250e 100644 --- a/njs/test/njs_unit_test.c +++ b/njs/test/njs_unit_test.c @@ -3297,6 +3297,18 @@ static njs_unit_test_t njs_test[] = { nxt_string("'0123456789'.split('').reverse().join('')"), nxt_string("9876543210") }, + { nxt_string("encodeURI()"), + nxt_string("undefined")}, + + { nxt_string("encodeURI('012абв')"), + nxt_string("012%D0%B0%D0%B1%D0%B2")}, + + { nxt_string("encodeURI('~}|{`_^]\\\\[@?>=<;:/.-,+*)(\\\'&%$#\"! ')"), + nxt_string("~%7D%7C%7B%60_%5E%5D%5C%5B@?%3E=%3C;:/.-,+*)('&%25$#%22!%20")}, + + { nxt_string("encodeURIComponent('~}|{`_^]\\\\[@?>=<;:/.-,+*)(\\\'&%$#\"! ')"), + nxt_string("~%7D%7C%7B%60_%5E%5D%5C%5B%40%3F%3E%3D%3C%3B%3A%2F.-%2C%2B*)('%26%25%24%23%22!%20")}, + /* Functions. */ { nxt_string("return"), -- 2.47.3