From: Alexander Borisov Date: Thu, 19 Sep 2019 07:19:00 +0000 (+0300) Subject: Fixed Array prototype functions according to the specification. X-Git-Url: http://git.kaiwu.me/postgresql/log/contrib/postgres_fdw/static/gitweb.js?a=commitdiff_plain;h=61c38e0103f937acc35f941c5f374e2b93849b76;p=njs.git Fixed Array prototype functions according to the specification. The following fuctions were fixed: includes, indexOf, lastIndexOf, reduceRight. --- diff --git a/src/njs_array.c b/src/njs_array.c index 05630a91..61c1e554 100644 --- a/src/njs_array.c +++ b/src/njs_array.c @@ -9,11 +9,14 @@ typedef struct { - njs_function_t *function; - njs_value_t *this_arg; - njs_value_t *value; + njs_function_t *function; + njs_value_t *argument; + njs_value_t *value; + + njs_array_t *array; - njs_array_t *array; + uint32_t from; + uint32_t to; } njs_array_iterator_args_t; @@ -1009,260 +1012,546 @@ njs_array_prototype_join(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, } -static njs_int_t -njs_array_prototype_concat(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused) +njs_inline njs_int_t +njs_array_iterator(njs_vm_t *vm, njs_array_iterator_args_t *args, + njs_array_iterator_handler_t handler) { - uint64_t length; - njs_uint_t i; - njs_value_t *value; - njs_array_t *array; - - length = 0; + uint32_t length, i, from, to; + njs_int_t ret; + njs_value_t *entry, *value, character, index, string_obj, prop; + njs_object_t *object; + const u_char *p, *end, *pos; + njs_string_prop_t string_prop; - for (i = 0; i < nargs; i++) { - if (njs_is_array(&args[i])) { - length += njs_array_len(&args[i]); + value = args->value; + from = args->from; + to = args->to; - } else { - length++; + if (njs_is_array(value)) { + if (njs_slow_path(!njs_object_hash_is_empty(value))) { + goto process_object; } - } - - array = njs_array_alloc(vm, length, NJS_ARRAY_SPARE); - if (njs_slow_path(array == NULL)) { - return NJS_ERROR; - } - njs_set_array(&vm->retval, array); + for (i = from; i < to; i++) { + entry = &njs_array_start(value)[i]; - value = array->start; + ret = handler(vm, args, entry, i); + if (njs_slow_path(ret != NJS_OK)) { + if (ret > 0) { + return NJS_DECLINED; + } - for (i = 0; i < nargs; i++) { - value = njs_array_copy(value, &args[i]); - } + return NJS_ERROR; + } - return NJS_OK; -} + to = njs_min(to, njs_array_len(value)); + } + return NJS_OK; + } -static njs_value_t * -njs_array_copy(njs_value_t *dst, njs_value_t *src) -{ - njs_uint_t n; + if (njs_is_string(value) || njs_is_object_string(value)) { - n = 1; + if (njs_is_string(value)) { + object = njs_object_value_alloc(vm, value, NJS_STRING); + if (njs_slow_path(object == NULL)) { + return NJS_ERROR; + } - if (njs_is_array(src)) { - n = njs_array_len(src); - src = njs_array_start(src); - } + njs_set_type_object(&string_obj, object, NJS_OBJECT_STRING); - while (n != 0) { - /* GC: njs_retain src */ - *dst++ = *src++; - n--; - } + args->value = &string_obj; + } + else { + value = njs_object_value(value); + } - return dst; -} + length = (uint32_t) njs_string_prop(&string_prop, value); + p = string_prop.start; + end = p + string_prop.size; -static njs_int_t -njs_array_prototype_index_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused) -{ - njs_int_t i, index, length; - njs_value_t *value, *start; - njs_array_t *array; + if (length == string_prop.size) { + /* Byte or ASCII string. */ - index = -1; + for (i = from; i < to; i++) { + /* This cannot fail. */ + (void) njs_string_new(vm, &character, p + i, 1, 1); - if (nargs < 2 || !njs_is_array(&args[0])) { - goto done; - } + ret = handler(vm, args, &character, i); + if (njs_slow_path(ret != NJS_OK)) { + if (ret > 0) { + return NJS_DECLINED; + } - array = njs_array(&args[0]); - length = array->length; + return NJS_ERROR; + } + } - if (length == 0) { - goto done; - } + } else { + /* UTF-8 string. */ - i = 0; + for (i = from; i < to; i++) { + pos = njs_utf8_next(p, end); - if (nargs > 2) { - i = njs_number(&args[2]); + /* This cannot fail. */ + (void) njs_string_new(vm, &character, p, pos - p, 1); - if (i >= length) { - goto done; - } + ret = handler(vm, args, &character, i); + if (njs_slow_path(ret != NJS_OK)) { + if (ret > 0) { + return NJS_DECLINED; + } - if (i < 0) { - i += length; + return NJS_ERROR; + } - if (i < 0) { - i = 0; + p = pos; } } + + return NJS_OK; } - value = &args[1]; - start = array->start; + if (!njs_is_object(value)) { + return NJS_OK; + } - do { - if (njs_values_strict_equal(value, &start[i])) { - index = i; - break; - } +process_object: - i++; + for (i = from; i < to; i++) { + njs_uint32_to_string(&index, i); - } while (i < length); + ret = njs_value_property(vm, value, &index, &prop); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } -done: + if (ret != NJS_DECLINED) { + ret = handler(vm, args, &prop, i); + if (njs_slow_path(ret != NJS_OK)) { + if (ret > 0) { + return NJS_DECLINED; + } - njs_set_number(&vm->retval, index); + return NJS_ERROR; + } + } + } return NJS_OK; } -static njs_int_t -njs_array_prototype_last_index_of(njs_vm_t *vm, njs_value_t *args, - njs_uint_t nargs, njs_index_t unused) +njs_inline njs_int_t +njs_array_reverse_iterator(njs_vm_t *vm, njs_array_iterator_args_t *args, + njs_array_iterator_handler_t handler) { - njs_int_t k, n, index, length; - njs_value_t *start; - njs_array_t *array; - njs_value_t *this, *value; + uint32_t i, from, to, length; + njs_int_t ret; + njs_value_t *entry, *value, character, index, string_obj, prop; + njs_object_t *object; + const u_char *p, *end, *pos; + njs_string_prop_t string_prop; - index = -1; + value = args->value; + from = args->from; + to = args->to; - this = njs_arg(args, nargs, 0); + if (njs_is_array(value)) { + if (njs_slow_path(!njs_object_hash_is_empty(value))) { + goto process_object; + } - if (!njs_is_array(this)) { - goto done; - } + i = from + 1; - array = njs_array(this); - length = array->length; + while (i-- > to) { + entry = &njs_array_start(value)[i]; - if (length == 0) { - goto done; - } + ret = handler(vm, args, entry, i); + if (njs_slow_path(ret != NJS_OK)) { + if (ret > 0) { + return NJS_DECLINED; + } - if (nargs > 2) { - n = njs_primitive_value_to_integer(njs_argument(args, 2)); + return NJS_ERROR; + } + } - } else { - n = length - 1; + return NJS_OK; } - if (n >= 0) { - k = njs_min(n, length - 1); - - } else { - k = n + length; + if (njs_is_string(value) || njs_is_object_string(value)) { - if (k < 0) { - goto done; - } - } + if (njs_is_string(value)) { + object = njs_object_value_alloc(vm, value, NJS_STRING); + if (njs_slow_path(object == NULL)) { + return NJS_ERROR; + } - value = njs_arg(args, nargs, 1); - start = array->start; + njs_set_type_object(&string_obj, object, NJS_OBJECT_STRING); - do { - if (njs_values_strict_equal(value, &start[k])) { - index = k; - break; + args->value = &string_obj; + } + else { + value = njs_object_value(value); } - k--; + length = (uint32_t) njs_string_prop(&string_prop, value); + end = string_prop.start + string_prop.size; - } while (k >= 0); + if (length == string_prop.size) { + /* Byte or ASCII string. */ -done: + p = string_prop.start + from; - njs_set_number(&vm->retval, index); + i = from + 1; - return NJS_OK; -} + while (i-- > to) { + /* This cannot fail. */ + (void) njs_string_new(vm, &character, p, 1, 1); + ret = handler(vm, args, &character, i); + if (njs_slow_path(ret != NJS_OK)) { + if (ret > 0) { + return NJS_DECLINED; + } -static njs_int_t -njs_array_prototype_includes(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused) -{ - njs_int_t i, length; - njs_value_t *value, *start; - njs_array_t *array; - const njs_value_t *retval; + return NJS_ERROR; + } - retval = &njs_value_false; + p--; + } - if (nargs < 2 || !njs_is_array(&args[0])) { - goto done; - } + } else { + /* UTF-8 string. */ - array = njs_array(&args[0]); - length = array->length; + p = njs_string_offset(string_prop.start, end, from + 1); - if (length == 0) { - goto done; - } + i = from + 1; - i = 0; + while (i-- > to) { + pos = njs_utf8_prev(p); - if (nargs > 2) { - i = njs_number(&args[2]); + /* This cannot fail. */ + (void) njs_string_new(vm, &character, pos, p - pos , 1); - if (i >= length) { - goto done; - } + ret = handler(vm, args, &character, i); + if (njs_slow_path(ret != NJS_OK)) { + if (ret > 0) { + return NJS_DECLINED; + } - if (i < 0) { - i += length; + return NJS_ERROR; + } - if (i < 0) { - i = 0; + p = pos; } } + + return NJS_OK; } - start = array->start; - value = &args[1]; + if (!njs_is_object(value)) { + return NJS_OK; + } - if (njs_is_number(value) && isnan(njs_number(value))) { +process_object: - do { - value = &start[i]; + i = from + 1; - if (njs_is_number(value) && isnan(njs_number(value))) { - retval = &njs_value_true; - break; - } + while (i-- > to) { + njs_uint32_to_string(&index, i); - i++; + ret = njs_value_property(vm, value, &index, &prop); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } - } while (i < length); + if (ret != NJS_DECLINED) { + ret = handler(vm, args, &prop, i); + if (njs_slow_path(ret != NJS_OK)) { + if (ret > 0) { + return NJS_DECLINED; + } - } else { - do { - if (njs_values_strict_equal(value, &start[i])) { - retval = &njs_value_true; - break; + return NJS_ERROR; } + } + } + + return NJS_OK; +} + + +static njs_int_t +njs_array_prototype_concat(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + uint64_t length; + njs_uint_t i; + njs_value_t *value; + njs_array_t *array; - i++; + length = 0; - } while (i < length); + for (i = 0; i < nargs; i++) { + if (njs_is_array(&args[i])) { + length += njs_array_len(&args[i]); + + } else { + length++; + } } -done: + array = njs_array_alloc(vm, length, NJS_ARRAY_SPARE); + if (njs_slow_path(array == NULL)) { + return NJS_ERROR; + } - vm->retval = *retval; + njs_set_array(&vm->retval, array); + + value = array->start; + + for (i = 0; i < nargs; i++) { + value = njs_array_copy(value, &args[i]); + } + + return NJS_OK; +} + + +static njs_value_t * +njs_array_copy(njs_value_t *dst, njs_value_t *src) +{ + njs_uint_t n; + + n = 1; + + if (njs_is_array(src)) { + n = njs_array_len(src); + src = njs_array_start(src); + } + + while (n != 0) { + /* GC: njs_retain src */ + *dst++ = *src++; + n--; + } + + return dst; +} + + +static njs_int_t +njs_array_handler_index_of(njs_vm_t *vm, njs_array_iterator_args_t *args, + njs_value_t *entry, uint32_t n) +{ + if (njs_values_strict_equal(args->argument, entry)) { + njs_set_number(&vm->retval, n); + + return 1; + } + + return NJS_OK; +} + + +static njs_int_t +njs_array_prototype_index_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + int64_t from; + uint32_t length; + njs_int_t ret; + njs_array_iterator_args_t iargs; + + if (njs_slow_path(njs_is_null_or_undefined(njs_arg(args, nargs, 0)))) { + njs_type_error(vm, "unexpected iterator arguments"); + return NJS_ERROR; + } + + iargs.value = njs_argument(args, 0); + iargs.argument = njs_arg(args, nargs, 1); + + ret = njs_value_length(vm, iargs.value, &length); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + from = njs_primitive_value_to_integer(njs_arg(args, nargs, 2)); + + if (length == 0 || from >= (int64_t) length) { + goto not_found; + } + + if (from < 0) { + from = length + from; + + if (from < 0) { + from = 0; + } + } + + iargs.from = (uint32_t) from; + iargs.to = length; + + ret = njs_array_iterator(vm, &iargs, njs_array_handler_index_of); + if (njs_fast_path(ret == NJS_DECLINED)) { + return NJS_OK; + } + +not_found: + + njs_set_number(&vm->retval, -1); + + return ret; +} + + +static njs_int_t +njs_array_prototype_last_index_of(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t unused) +{ + int64_t from; + uint32_t length; + njs_int_t ret; + njs_array_iterator_args_t iargs; + + if (njs_slow_path(njs_is_null_or_undefined(njs_arg(args, nargs, 0)))) { + njs_type_error(vm, "unexpected iterator arguments"); + return NJS_ERROR; + } + + iargs.value = njs_argument(args, 0); + iargs.argument = njs_arg(args, nargs, 1); + + ret = njs_value_length(vm, iargs.value, &length); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + if (length == 0) { + goto not_found; + } + + if (nargs > 2) { + from = njs_primitive_value_to_integer(njs_arg(args, nargs, 2)); + + } else { + from = length - 1; + } + + if (from >= 0) { + from = njs_min(from, length - 1); + + } else if (from < 0) { + from += length; + + if (from <= 0) { + goto not_found; + } + } + + iargs.from = from; + iargs.to = 0; + + ret = njs_array_reverse_iterator(vm, &iargs, njs_array_handler_index_of); + if (njs_fast_path(ret == NJS_DECLINED)) { + return NJS_OK; + } + +not_found: + + njs_set_number(&vm->retval, -1); + + return ret; +} + + +static njs_int_t +njs_array_handler_includes(njs_vm_t *vm, njs_array_iterator_args_t *args, + njs_value_t *entry, uint32_t n) +{ + if (njs_values_strict_equal(args->argument, entry)) { + njs_set_true(&vm->retval); + + return 1; + } + + return NJS_OK; +} + + +static njs_int_t +njs_array_handler_includes_nan(njs_vm_t *vm, njs_array_iterator_args_t *args, + njs_value_t *entry, uint32_t n) +{ + if (njs_is_numeric(entry) && isnan(njs_number(entry))) { + njs_set_true(&vm->retval); + + return 1; + } + + return NJS_OK; +} + + +static njs_int_t +njs_array_prototype_includes(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + int64_t from; + uint32_t length; + njs_int_t ret; + njs_array_iterator_args_t iargs; + + if (njs_slow_path(njs_is_null_or_undefined(njs_arg(args, nargs, 0)))) { + njs_type_error(vm, "unexpected iterator arguments"); + return NJS_ERROR; + } + + iargs.value = njs_argument(args, 0); + iargs.argument = njs_arg(args, nargs, 1); + + ret = njs_value_length(vm, iargs.value, &length); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + if (length == 0) { + goto not_found; + } + + from = njs_primitive_value_to_integer(njs_arg(args, nargs, 2)); + + if (from < 0) { + from += length; + + if (from < 0) { + from = 0; + } + } + + iargs.from = (uint32_t) from; + iargs.to = length; + + if (njs_is_numeric(iargs.argument) && isnan(njs_number(iargs.argument))) { + ret = njs_array_iterator(vm, &iargs, njs_array_handler_includes_nan); + if (njs_fast_path(ret == NJS_DECLINED)) { + return NJS_OK; + } + + } else { + ret = njs_array_iterator(vm, &iargs, njs_array_handler_includes); + if (njs_fast_path(ret == NJS_DECLINED)) { + return NJS_OK; + } + } + +not_found: + + njs_set_false(&vm->retval); return NJS_OK; } @@ -1375,149 +1664,11 @@ njs_array_iterator_call(njs_vm_t *vm, njs_array_iterator_args_t *args, njs_set_number(&arguments[1], n); arguments[2] = *args->value; - return njs_function_call(vm, args->function, args->this_arg, arguments, 3, + return njs_function_call(vm, args->function, args->argument, arguments, 3, &vm->retval); } -njs_inline njs_int_t -njs_array_iterator(njs_vm_t *vm, njs_array_iterator_args_t *args, - njs_array_iterator_handler_t handler, uint32_t length) -{ - uint32_t i; - njs_int_t ret; - njs_value_t *entry, *value, character, index, string_obj, prop; - njs_object_t *object; - const u_char *p, *end, *pos; - njs_string_prop_t string_prop; - - value = args->value; - - if (njs_is_array(value)) { - if (njs_slow_path(!njs_object_hash_is_empty(value))) { - goto process_object; - } - - length = njs_array_len(value); - - for (i = 0; i < length; i++) { - entry = &njs_array_start(value)[i]; - - ret = handler(vm, args, entry, i); - if (njs_slow_path(ret != NJS_OK)) { - if (ret > 0) { - return NJS_DECLINED; - } - - return NJS_ERROR; - } - - length = njs_min(length, njs_array_len(value)); - } - - return NJS_OK; - } - - if (njs_is_string(value) || njs_is_object_string(value)) { - - if (njs_is_string(value)) { - object = njs_object_value_alloc(vm, value, NJS_STRING); - if (njs_slow_path(object == NULL)) { - return NJS_ERROR; - } - - njs_set_type_object(&string_obj, object, NJS_OBJECT_STRING); - - args->value = &string_obj; - } - else { - value = njs_object_value(value); - } - - length = (uint32_t) njs_string_prop(&string_prop, value); - - p = string_prop.start; - end = p + string_prop.size; - - if (length == string_prop.size) { - /* Byte or ASCII string. */ - - for (i = 0; i < length; i++) { - /* This cannot fail. */ - (void) njs_string_new(vm, &character, p++, 1, 1); - - ret = handler(vm, args, &character, i); - if (njs_slow_path(ret != NJS_OK)) { - if (ret > 0) { - return NJS_DECLINED; - } - - return NJS_ERROR; - } - } - - } else { - /* UTF-8 string. */ - - for (i = 0; i < length; i++) { - pos = njs_utf8_next(p, end); - - /* This cannot fail. */ - (void) njs_string_new(vm, &character, p, pos - p, 1); - - ret = handler(vm, args, &character, i); - if (njs_slow_path(ret != NJS_OK)) { - if (ret > 0) { - return NJS_DECLINED; - } - - return NJS_ERROR; - } - - p = pos; - } - } - - return NJS_OK; - } - - if (!njs_is_object(value)) { - return NJS_OK; - } - -process_object: - - if (length > NJS_ARRAY_MAX_LENGTH) { - ret = njs_object_length(vm, value, &length); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_ERROR; - } - } - - for (i = 0; i < length; i++) { - njs_uint32_to_string(&index, i); - - ret = njs_value_property(vm, value, &index, &prop); - if (njs_slow_path(ret == NJS_ERROR)) { - return ret; - } - - if (ret != NJS_DECLINED) { - ret = handler(vm, args, &prop, i); - if (njs_slow_path(ret != NJS_OK)) { - if (ret > 0) { - return NJS_DECLINED; - } - - return NJS_ERROR; - } - } - } - - return NJS_OK; -} - - static njs_int_t njs_array_handler_for_each(njs_vm_t *vm, njs_array_iterator_args_t *args, njs_value_t *entry, uint32_t n) @@ -1546,10 +1697,16 @@ njs_array_prototype_for_each(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, iargs.value = njs_argument(args, 0); iargs.function = njs_function(&args[1]); - iargs.this_arg = njs_arg(args, nargs, 2); + iargs.argument = njs_arg(args, nargs, 2); - ret = njs_array_iterator(vm, &iargs, njs_array_handler_for_each, - NJS_ARRAY_MAX_LENGTH + 1); + iargs.from = 0; + + ret = njs_value_length(vm, iargs.value, &iargs.to); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + ret = njs_array_iterator(vm, &iargs, njs_array_handler_for_each); if (njs_slow_path(ret != NJS_OK)) { return ret; } @@ -1599,10 +1756,16 @@ njs_array_prototype_some(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, iargs.value = njs_argument(args, 0); iargs.function = njs_function(&args[1]); - iargs.this_arg = njs_arg(args, nargs, 2); + iargs.argument = njs_arg(args, nargs, 2); + + iargs.from = 0; - ret = njs_array_iterator(vm, &iargs, njs_array_handler_some, - NJS_ARRAY_MAX_LENGTH + 1); + ret = njs_value_length(vm, iargs.value, &iargs.to); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + ret = njs_array_iterator(vm, &iargs, njs_array_handler_some); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } @@ -1654,10 +1817,16 @@ njs_array_prototype_every(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, iargs.value = njs_argument(args, 0); iargs.function = njs_function(&args[1]); - iargs.this_arg = njs_arg(args, nargs, 2); + iargs.argument = njs_arg(args, nargs, 2); - ret = njs_array_iterator(vm, &iargs, njs_array_handler_every, - NJS_ARRAY_MAX_LENGTH + 1); + iargs.from = 0; + + ret = njs_value_length(vm, iargs.value, &iargs.to); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + ret = njs_array_iterator(vm, &iargs, njs_array_handler_every); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } @@ -1714,15 +1883,21 @@ njs_array_prototype_filter(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, iargs.value = njs_argument(args, 0); iargs.function = njs_function(&args[1]); - iargs.this_arg = njs_arg(args, nargs, 2); + iargs.argument = njs_arg(args, nargs, 2); + + iargs.from = 0; + + ret = njs_value_length(vm, iargs.value, &iargs.to); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } iargs.array = njs_array_alloc(vm, 0, NJS_ARRAY_SPARE); if (njs_slow_path(iargs.array == NULL)) { return NJS_ERROR; } - ret = njs_array_iterator(vm, &iargs, njs_array_handler_filter, - NJS_ARRAY_MAX_LENGTH + 1); + ret = njs_array_iterator(vm, &iargs, njs_array_handler_filter); if (njs_slow_path(ret != NJS_OK)) { return ret; } @@ -1778,10 +1953,16 @@ njs_array_prototype_find(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, iargs.value = njs_argument(args, 0); iargs.function = njs_function(&args[1]); - iargs.this_arg = njs_arg(args, nargs, 2); + iargs.argument = njs_arg(args, nargs, 2); + + iargs.from = 0; - ret = njs_array_iterator(vm, &iargs, njs_array_handler_find, - NJS_ARRAY_MAX_LENGTH + 1); + ret = njs_value_length(vm, iargs.value, &iargs.to); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + ret = njs_array_iterator(vm, &iargs, njs_array_handler_find); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } @@ -1839,10 +2020,16 @@ njs_array_prototype_find_index(njs_vm_t *vm, njs_value_t *args, iargs.value = njs_argument(args, 0); iargs.function = njs_function(&args[1]); - iargs.this_arg = njs_arg(args, nargs, 2); + iargs.argument = njs_arg(args, nargs, 2); + + iargs.from = 0; - ret = njs_array_iterator(vm, &iargs, njs_array_handler_find_index, - NJS_ARRAY_MAX_LENGTH + 1); + ret = njs_value_length(vm, iargs.value, &iargs.to); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + ret = njs_array_iterator(vm, &iargs, njs_array_handler_find_index); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } @@ -1899,7 +2086,7 @@ njs_array_prototype_map(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, iargs.value = njs_argument(args, 0); iargs.function = njs_function(&args[1]); - iargs.this_arg = njs_arg(args, nargs, 2); + iargs.argument = njs_arg(args, nargs, 2); ret = njs_value_length(vm, iargs.value, &length); if (njs_slow_path(ret != NJS_OK)) { @@ -1912,7 +2099,10 @@ njs_array_prototype_map(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, } if (length > 0) { - ret = njs_array_iterator(vm, &iargs, njs_array_handler_map, length); + iargs.from = 0; + iargs.to = length; + + ret = njs_array_iterator(vm, &iargs, njs_array_handler_map); if (njs_slow_path(ret != NJS_OK)) { return ret; } @@ -1943,12 +2133,12 @@ njs_array_iterator_reduce(njs_vm_t *vm, njs_array_iterator_args_t *args, /* GC: array elt, array */ njs_set_undefined(&arguments[0]); - arguments[1] = *args->this_arg; + arguments[1] = *args->argument; arguments[2] = *entry; njs_set_number(&arguments[3], n); arguments[4] = *args->value; - return njs_function_apply(vm, args->function, arguments, 5, args->this_arg); + return njs_function_apply(vm, args->function, arguments, 5, args->argument); } @@ -1960,8 +2150,8 @@ njs_array_handler_reduce(njs_vm_t *vm, njs_array_iterator_args_t *args, if (njs_is_valid(entry)) { - if (!njs_is_valid(args->this_arg)) { - *(args->this_arg) = *entry; + if (!njs_is_valid(args->argument)) { + *(args->argument) = *entry; return NJS_OK; } @@ -1998,16 +2188,22 @@ njs_array_prototype_reduce(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, iargs.value = njs_argument(args, 0); iargs.function = njs_function(&args[1]); - iargs.this_arg = &accumulator; + iargs.argument = &accumulator; + + iargs.from = 0; - ret = njs_array_iterator(vm, &iargs, njs_array_handler_reduce, - NJS_ARRAY_MAX_LENGTH + 1); + ret = njs_value_length(vm, iargs.value, &iargs.to); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + ret = njs_array_iterator(vm, &iargs, njs_array_handler_reduce); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (!njs_is_valid(&accumulator)) { - njs_type_error(vm, "invalid index"); + njs_type_error(vm, "Reduce of empty object with no initial value"); return NJS_ERROR; } @@ -2021,57 +2217,63 @@ static njs_int_t njs_array_prototype_reduce_right(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - int32_t i; - uint32_t length; njs_int_t ret; - njs_value_t accumulator, *value, *entry; - njs_array_iterator_args_t iter; + njs_value_t accumulator; + njs_array_iterator_args_t iargs; - if (nargs < 2 || !njs_is_array(&args[0]) || !njs_is_function(&args[1])) { + if (njs_is_null_or_undefined(njs_arg(args, nargs, 0)) + || !njs_is_function(njs_arg(args, nargs, 1))) + { njs_type_error(vm, "unexpected iterator arguments"); return NJS_ERROR; } - value = &args[0]; - length = njs_array_len(value); - njs_set_invalid(&accumulator); - if (nargs > 2) { - accumulator = args[2]; - } + iargs.value = njs_argument(args, 0); + iargs.function = njs_function(&args[1]); + iargs.argument = &accumulator; + iargs.to = 0; - iter.value = value; - iter.function = njs_function(&args[1]); - iter.this_arg = &accumulator; + ret = njs_value_length(vm, iargs.value, &iargs.from); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } - for (i = length - 1; i >= 0; i--) { - entry = &njs_array_start(value)[i]; + if (nargs > 2) { + accumulator = *njs_argument(args, 2); + } - if (njs_is_valid(entry)) { + if (iargs.from == 0) { + if (nargs < 3) { + goto failed; + } - if (!njs_is_valid(&accumulator)) { - accumulator = njs_array_start(value)[i]; - continue; - } + goto done; + } - ret = njs_array_iterator_reduce(vm, &iter, entry, i); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - } + iargs.from--; - length = njs_min(length, njs_array_len(value)); + ret = njs_array_reverse_iterator(vm, &iargs, njs_array_handler_reduce); + if (njs_slow_path(ret != NJS_OK)) { + return ret; } if (!njs_is_valid(&accumulator)) { - njs_type_error(vm, "invalid index"); - return NJS_ERROR; + goto failed; } +done: + vm->retval = accumulator; return NJS_OK; + +failed: + + njs_type_error(vm, "Reduce of empty object with no initial value"); + + return NJS_ERROR; } diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index a348ba18..dd8f5149 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -4020,6 +4020,16 @@ static njs_unit_test_t njs_test[] = { njs_str("[].indexOf.bind(0)(0, 0)"), njs_str("-1") }, + { njs_str("var o = 'abcd';" + "Array.prototype.indexOf.call(o, 'c')"), + njs_str("2") }, + + { njs_str("var o = {0: 'a', 1: 'b', 2: 'c'};" + "Object.defineProperty(o, 'length', {get: () => 4});" + "Object.defineProperty(o, '3', {get: () => 'd'});" + "Array.prototype.indexOf.call(o, 'd')"), + njs_str("3") }, + { njs_str("[].lastIndexOf(1, -1)"), njs_str("-1") }, @@ -4065,6 +4075,36 @@ static njs_unit_test_t njs_test[] = { njs_str("[1,2,1].lastIndexOf(1)"), njs_str("2") }, + { njs_str("var o = 'addc';" + "Array.prototype.lastIndexOf.call(o, 'd')"), + njs_str("2") }, + + { njs_str("var o = 'dddd';" + "Array.prototype.lastIndexOf.call(o, 'd')"), + njs_str("3") }, + + { njs_str("var o = 'dabc';" + "Array.prototype.lastIndexOf.call(o, 'd')"), + njs_str("0") }, + + { njs_str("var o = 'АБВГ';" + "Array.prototype.lastIndexOf.call(o, 'Г')"), + njs_str("3") }, + + { njs_str("var o = 'ГВБА';" + "Array.prototype.lastIndexOf.call(o, 'Г')"), + njs_str("0") }, + + { njs_str("var o = 'ВГБА';" + "Array.prototype.lastIndexOf.call(o, 'Г')"), + njs_str("1") }, + + { njs_str("var o = {0: 'a', 1: 'd', 2: 'd'};" + "Object.defineProperty(o, 'length', {get: () => 4});" + "Object.defineProperty(o, '3', {get: () => 'd'});" + "Array.prototype.lastIndexOf.call(o, 'd')"), + njs_str("3") }, + { njs_str("[1,2,3,4].includes()"), njs_str("false") }, @@ -4095,6 +4135,12 @@ static njs_unit_test_t njs_test[] = { njs_str("[].includes.bind(0)(0, 0)"), njs_str("false") }, + { njs_str("var o = {0: 'a', 1: 'b', 2: 'c'};" + "Object.defineProperty(o, 'length', {get: () => 4});" + "Object.defineProperty(o, '3', {get: () => 'd'});" + "Array.prototype.includes.call(o, 'd')"), + njs_str("true") }, + { njs_str("var a = []; var s = { sum: 0 };" "a.forEach(function(v, i, a) { this.sum += v }, s); s.sum"), njs_str("0") }, @@ -4511,7 +4557,7 @@ static njs_unit_test_t njs_test[] = { njs_str("var a = [];" "a.reduce(function(p, v, i, a) { return p + v })"), - njs_str("TypeError: invalid index") }, + njs_str("TypeError: Reduce of empty object with no initial value") }, { njs_str("var a = [];" "a.reduce(function(p, v, i, a) { return p + v }, 10)"), @@ -4519,7 +4565,7 @@ static njs_unit_test_t njs_test[] = { njs_str("var a = [,,];" "a.reduce(function(p, v, i, a) { return p + v })"), - njs_str("TypeError: invalid index") }, + njs_str("TypeError: Reduce of empty object with no initial value") }, { njs_str("var a = [,,];" "a.reduce(function(p, v, i, a) { return p + v }, 10)"), @@ -4564,7 +4610,7 @@ static njs_unit_test_t njs_test[] = { njs_str("var a = [];" "a.reduceRight(function(p, v, i, a) { return p + v })"), - njs_str("TypeError: invalid index") }, + njs_str("TypeError: Reduce of empty object with no initial value") }, { njs_str("var a = [];" "a.reduceRight(function(p, v, i, a) { return p + v }, 10)"), @@ -4572,7 +4618,7 @@ static njs_unit_test_t njs_test[] = { njs_str("var a = [,,];" "a.reduceRight(function(p, v, i, a) { return p + v })"), - njs_str("TypeError: invalid index") }, + njs_str("TypeError: Reduce of empty object with no initial value") }, { njs_str("var a = [,,];" "a.reduceRight(function(p, v, i, a) { return p + v }, 10)"), @@ -4604,6 +4650,12 @@ static njs_unit_test_t njs_test[] = " { a.shift(); return p + v }, 10)"), njs_str("19") }, + { njs_str("var o = {0: 'a', 1: 'b', 2: 'c'};" + "Object.defineProperty(o, 'length', {get: () => 4});" + "Object.defineProperty(o, '3', {get: () => 'd'});" + "Array.prototype.reduceRight.call(o, (p, v) => p + v)"), + njs_str("dcba") }, + { njs_str("var a = ['1','2','3','4','5','6']; a.sort()"), njs_str("1,2,3,4,5,6") },