From 1a2ebf4718c6c8a7785351ffc5b9b356038076db Mon Sep 17 00:00:00 2001 From: Dmitry Volyntsev Date: Tue, 18 Aug 2020 11:52:17 +0000 Subject: [PATCH] Refactored iterator Array methods. --- src/njs_array.c | 1869 +++++++++++++++++--------------------- src/test/njs_unit_test.c | 78 +- 2 files changed, 855 insertions(+), 1092 deletions(-) diff --git a/src/njs_array.c b/src/njs_array.c index 6f2559b0..ddffee18 100644 --- a/src/njs_array.c +++ b/src/njs_array.c @@ -8,7 +8,45 @@ #include -#define njs_fast_object(_sz) ((_sz) <= NJS_ARRAY_FAST_OBJECT_LENGTH) +#define njs_fast_object(_sz) ((_sz) <= NJS_ARRAY_FAST_OBJECT_LENGTH) + + +#define njs_array_func(type) \ + ((type << 1) | NJS_ARRAY_FUNC) + + +#define njs_array_arg(type) \ + ((type << 1) | NJS_ARRAY_ARG) + + +#define njs_array_type(magic) (magic >> 1) +#define njs_array_arg1(magic) (magic & 0x1) + + +typedef enum { + NJS_ARRAY_EVERY = 0, + NJS_ARRAY_SOME, + NJS_ARRAY_INCLUDES, + NJS_ARRAY_INDEX_OF, + NJS_ARRAY_FOR_EACH, + NJS_ARRAY_FIND, + NJS_ARRAY_FIND_INDEX, + NJS_ARRAY_REDUCE, + NJS_ARRAY_FILTER, + NJS_ARRAY_MAP, +} njs_array_iterator_fun_t; + + +typedef enum { + NJS_ARRAY_LAST_INDEX_OF = 0, + NJS_ARRAY_REDUCE_RIGHT, +} njs_array_reverse_iterator_fun_t; + + +typedef enum { + NJS_ARRAY_FUNC = 0, + NJS_ARRAY_ARG +} njs_array_iterator_arg_t; typedef struct { @@ -1769,211 +1807,324 @@ njs_array_indices(njs_vm_t *vm, njs_value_t *object) njs_inline njs_int_t -njs_array_object_handler(njs_vm_t *vm, njs_array_iterator_handler_t handler, - njs_array_iterator_args_t *args, njs_value_t *key, int64_t i) +njs_is_concat_spreadable(njs_vm_t *vm, njs_value_t *value) { njs_int_t ret; - njs_value_t prop, *entry; + njs_value_t retval; - if (key != NULL) { - ret = njs_value_property(vm, args->value, key, &prop); - if (njs_slow_path(ret == NJS_ERROR)) { - return ret; - } + static const njs_value_t key = + njs_wellknown_symbol(NJS_SYMBOL_IS_CONCAT_SPREADABLE); - } else { - ret = njs_value_property_i64(vm, args->value, i, &prop); - if (njs_slow_path(ret == NJS_ERROR)) { - return ret; - } + if (njs_slow_path(!njs_is_object(value))) { + return NJS_DECLINED; } - entry = (ret == NJS_OK) ? &prop : njs_value_arg(&njs_value_invalid); - - ret = handler(vm, args, entry, i); - if (njs_slow_path(ret != NJS_OK)) { - if (ret > 0) { - return NJS_DECLINED; - } - + ret = njs_value_property(vm, value, njs_value_arg(&key), &retval); + if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } - return ret; + if (njs_is_defined(&retval)) { + return njs_bool(&retval) ? NJS_OK : NJS_DECLINED; + } + + return njs_is_array(value) ? NJS_OK : NJS_DECLINED; } -njs_inline njs_int_t -njs_array_iterator(njs_vm_t *vm, njs_array_iterator_args_t *args, - njs_array_iterator_handler_t handler) +static njs_int_t +njs_array_prototype_concat(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) { - double idx; - int64_t length, i, from, to; - njs_int_t ret; - njs_array_t *array, *keys; - njs_value_t *value, *entry, prop, character, string_obj; - njs_object_t *object; - const u_char *p, *end, *pos; - njs_string_prop_t string_prop; - - value = args->value; - from = args->from; - to = args->to; - - if (njs_is_array(value)) { - array = njs_array(value); + double idx; + int64_t k, len, length; + njs_int_t ret; + njs_uint_t i; + njs_value_t this, retval, *value, *e; + njs_array_t *array, *keys; - for (; from < to; from++) { - if (njs_slow_path(!array->object.fast_array)) { - goto process_object; - } + ret = njs_value_to_object(vm, &args[0]); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } - if (njs_fast_path(from < array->length - && njs_is_valid(&array->start[from]))) - { - ret = handler(vm, args, &array->start[from], from); + /* TODO: ArraySpeciesCreate(). */ - } else { - entry = njs_value_arg(&njs_value_invalid); - ret = njs_value_property_i64(vm, value, from, &prop); - if (njs_slow_path(ret != NJS_DECLINED)) { - if (ret == NJS_ERROR) { - return NJS_ERROR; - } + array = njs_array_alloc(vm, 0, 0, NJS_ARRAY_SPARE); + if (njs_slow_path(array == NULL)) { + return NJS_ERROR; + } - entry = ∝ - } + njs_set_array(&this, array); - ret = handler(vm, args, entry, from); - } + len = 0; + length = 0; - if (njs_slow_path(ret != NJS_OK)) { - if (ret > 0) { - return NJS_DECLINED; - } + for (i = 0; i < nargs; i++) { + e = njs_argument(args, i); - return NJS_ERROR; - } + ret = njs_is_concat_spreadable(vm, e); + if (njs_slow_path(ret == NJS_ERROR)) { + return NJS_ERROR; } - return NJS_OK; - } - - if (njs_is_string(value) || njs_is_object_string(value)) { + if (ret == NJS_OK) { + ret = njs_object_length(vm, e, &len); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } - if (njs_is_string(value)) { - object = njs_object_value_alloc(vm, value, NJS_STRING); - if (njs_slow_path(object == NULL)) { + if (njs_slow_path((length + len) > NJS_MAX_LENGTH)) { + njs_type_error(vm, "Invalid length"); return NJS_ERROR; } - njs_set_type_object(&string_obj, object, NJS_OBJECT_STRING); + if (njs_is_fast_array(&this) && njs_is_fast_array(e) + && (length + len) <= NJS_ARRAY_LARGE_OBJECT_LENGTH) + { + for (k = 0; k < len; k++, length++) { + value = &njs_array_start(e)[k]; - args->value = &string_obj; - } - else { - value = njs_object_value(value); - } + if (njs_slow_path(!njs_is_valid(value))) { + ret = njs_value_property_i64(vm, e, k, &retval); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } - length = njs_string_prop(&string_prop, value); + if (ret == NJS_DECLINED) { + njs_set_invalid(&retval); + } - p = string_prop.start; - end = p + string_prop.size; + value = &retval; + } - if ((size_t) length == string_prop.size) { - /* Byte or ASCII string. */ + ret = njs_array_add(vm, array, value); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + } - for (i = from; i < to; i++) { - /* This cannot fail. */ - (void) njs_string_new(vm, &character, p + i, 1, 1); + continue; + } - ret = handler(vm, args, &character, i); - if (njs_slow_path(ret != NJS_OK)) { - if (ret > 0) { - return NJS_DECLINED; + if (njs_fast_object(len)) { + for (k = 0; k < len; k++, length++) { + ret = njs_value_property_i64(vm, e, k, &retval); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; } - return NJS_ERROR; + if (ret != NJS_OK) { + continue; + } + + ret = njs_value_property_i64_set(vm, &this, length, + &retval); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } } + + continue; } - } else { - /* UTF-8 string. */ + keys = njs_array_indices(vm, e); + if (njs_slow_path(keys == NULL)) { + return NJS_ERROR; + } - for (i = from; i < to; i++) { - pos = njs_utf8_next(p, end); + for (k = 0; k < keys->length; k++) { + ret = njs_value_property(vm, e, &keys->start[k], &retval); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } - /* This cannot fail. */ - (void) njs_string_new(vm, &character, p, pos - p, 1); + idx = njs_string_to_index(&keys->start[k]) + length; - ret = handler(vm, args, &character, i); - if (njs_slow_path(ret != NJS_OK)) { - if (ret > 0) { - return NJS_DECLINED; + if (ret == NJS_OK) { + ret = njs_value_property_i64_set(vm, &this, idx, &retval); + if (njs_slow_path(ret == NJS_ERROR)) { + njs_array_destroy(vm, keys); + return ret; } - - return NJS_ERROR; } - - p = pos; } - } - return NJS_OK; - } + njs_array_destroy(vm, keys); - if (!njs_is_object(value)) { - return NJS_OK; - } + length += len; -process_object: + continue; + } - if (!njs_fast_object(to - from)) { - keys = njs_array_indices(vm, value); - if (njs_slow_path(keys == NULL)) { + if (njs_slow_path((length + len) >= NJS_MAX_LENGTH)) { + njs_type_error(vm, "Invalid length"); return NJS_ERROR; } - for (i = 0; i < keys->length; i++) { - idx = njs_string_to_index(&keys->start[i]); - - if (idx < from || idx >= to) { - continue; + if (njs_is_fast_array(&this)) { + ret = njs_array_add(vm, array, e); + if (njs_slow_path(ret != NJS_OK)) { + return ret; } - ret = njs_array_object_handler(vm, handler, args, &keys->start[i], - idx); - if (njs_slow_path(ret != NJS_OK)) { - njs_array_destroy(vm, keys); + } else { + ret = njs_value_property_i64_set(vm, &this, length, e); + if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } - njs_array_destroy(vm, keys); - - return NJS_OK; + length++; } - for (i = from; i < to; i++) { - ret = njs_array_object_handler(vm, handler, args, NULL, i); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } + ret = njs_object_length_set(vm, &this, length); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; } + vm->retval = this; + return NJS_OK; } -njs_inline njs_int_t -njs_array_reverse_iterator(njs_vm_t *vm, njs_array_iterator_args_t *args, +static njs_int_t +njs_array_prototype_fill(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + int64_t i, length, start, end; + njs_int_t ret; + njs_array_t *array; + njs_value_t *this, *value; + + this = njs_argument(args, 0); + + ret = njs_value_to_object(vm, this); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + array = NULL; + + if (njs_is_fast_array(this)) { + array = njs_array(this); + length = array->length; + + } else { + ret = njs_object_length(vm, this, &length); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + } + + ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &start); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + start = (start < 0) ? njs_max(length + start, 0) : njs_min(start, length); + + if (njs_is_undefined(njs_arg(args, nargs, 3))) { + end = length; + + } else { + ret = njs_value_to_integer(vm, njs_arg(args, nargs, 3), &end); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + } + + end = (end < 0) ? njs_max(length + end, 0) : njs_min(end, length); + + value = njs_arg(args, nargs, 1); + + if (array != NULL) { + for (i = start; i < end; i++) { + array->start[i] = *value; + } + + vm->retval = *this; + + return NJS_OK; + } + + value = njs_arg(args, nargs, 1); + + while (start < end) { + ret = njs_value_property_i64_set(vm, this, start++, value); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + } + + vm->retval = *this; + + return NJS_OK; +} + + +njs_inline njs_int_t +njs_array_iterator_call(njs_vm_t *vm, njs_array_iterator_args_t *args, + const njs_value_t *entry, uint32_t n) +{ + njs_value_t arguments[3]; + + /* GC: array elt, array */ + + arguments[0] = *entry; + njs_set_number(&arguments[1], n); + arguments[2] = *args->value; + + return njs_function_call(vm, args->function, args->argument, arguments, 3, + &vm->retval); +} + + +static njs_int_t +njs_array_object_handler(njs_vm_t *vm, njs_array_iterator_handler_t handler, + njs_array_iterator_args_t *args, njs_value_t *key, int64_t i) +{ + njs_int_t ret; + njs_value_t prop, *entry; + + if (key != NULL) { + ret = njs_value_property(vm, args->value, key, &prop); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + } else { + ret = njs_value_property_i64(vm, args->value, i, &prop); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + } + + entry = (ret == NJS_OK) ? &prop : njs_value_arg(&njs_value_invalid); + + ret = handler(vm, args, entry, i); + if (njs_slow_path(ret != NJS_OK)) { + if (ret == NJS_DONE) { + return NJS_DONE; + } + + return NJS_ERROR; + } + + return ret; +} + + +njs_inline njs_int_t +njs_array_iterator(njs_vm_t *vm, njs_array_iterator_args_t *args, njs_array_iterator_handler_t handler) { double idx; - int64_t i, from, to, length; + int64_t length, i, from, to; njs_int_t ret; njs_array_t *array, *keys; - njs_value_t *entry, *value, prop, character, string_obj; + njs_value_t *value, *entry, prop, character, string_obj; njs_object_t *object; const u_char *p, *end, *pos; njs_string_prop_t string_prop; @@ -1985,9 +2136,7 @@ njs_array_reverse_iterator(njs_vm_t *vm, njs_array_iterator_args_t *args, if (njs_is_array(value)) { array = njs_array(value); - from += 1; - - while (from-- > to) { + for (; from < to; from++) { if (njs_slow_path(!array->object.fast_array)) { goto process_object; } @@ -2012,8 +2161,8 @@ njs_array_reverse_iterator(njs_vm_t *vm, njs_array_iterator_args_t *args, } if (njs_slow_path(ret != NJS_OK)) { - if (ret > 0) { - return NJS_DECLINED; + if (ret == NJS_DONE) { + return NJS_DONE; } return NJS_ERROR; @@ -2040,49 +2189,40 @@ njs_array_reverse_iterator(njs_vm_t *vm, njs_array_iterator_args_t *args, } length = njs_string_prop(&string_prop, value); - end = string_prop.start + string_prop.size; + + p = string_prop.start; + end = p + string_prop.size; if ((size_t) length == string_prop.size) { /* Byte or ASCII string. */ - p = string_prop.start + from; - - i = from + 1; - - while (i-- > to) { + for (i = from; i < to; i++) { /* This cannot fail. */ - (void) njs_string_new(vm, &character, p, 1, 1); + (void) njs_string_new(vm, &character, p + i, 1, 1); ret = handler(vm, args, &character, i); if (njs_slow_path(ret != NJS_OK)) { - if (ret > 0) { - return NJS_DECLINED; + if (ret == NJS_DONE) { + return NJS_DONE; } return NJS_ERROR; } - - p--; } } else { /* UTF-8 string. */ - p = njs_string_offset(string_prop.start, end, from); - p = njs_utf8_next(p, end); - - i = from + 1; - - while (i-- > to) { - pos = njs_utf8_prev(p); + for (i = from; i < to; i++) { + pos = njs_utf8_next(p, end); /* This cannot fail. */ - (void) njs_string_new(vm, &character, pos, p - pos , 1); + (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; + if (ret == NJS_DONE) { + return NJS_DONE; } return NJS_ERROR; @@ -2101,18 +2241,16 @@ njs_array_reverse_iterator(njs_vm_t *vm, njs_array_iterator_args_t *args, process_object: - if (!njs_fast_object(from - to)) { + if (!njs_fast_object(to - from)) { keys = njs_array_indices(vm, value); if (njs_slow_path(keys == NULL)) { return NJS_ERROR; } - i = keys->length; - - while (i > 0) { - idx = njs_string_to_index(&keys->start[--i]); + for (i = 0; i < keys->length; i++) { + idx = njs_string_to_index(&keys->start[i]); - if (idx < to || idx > from) { + if (idx < from || idx >= to) { continue; } @@ -2129,9 +2267,7 @@ process_object: return NJS_OK; } - i = from + 1; - - while (i-- > to) { + for (i = from; i < to; i++) { ret = njs_array_object_handler(vm, handler, args, NULL, i); if (njs_slow_path(ret != NJS_OK)) { return ret; @@ -2142,522 +2278,82 @@ process_object: } -njs_inline njs_int_t -njs_is_concat_spreadable(njs_vm_t *vm, njs_value_t *value) +static njs_int_t +njs_array_handler_every(njs_vm_t *vm, njs_array_iterator_args_t *args, + njs_value_t *entry, int64_t n) { - njs_int_t ret; - njs_value_t retval; + njs_int_t ret; - static const njs_value_t key = - njs_wellknown_symbol(NJS_SYMBOL_IS_CONCAT_SPREADABLE); + if (njs_is_valid(entry)) { + ret = njs_array_iterator_call(vm, args, entry, n); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } - if (njs_slow_path(!njs_is_object(value))) { - return NJS_DECLINED; + if (!njs_is_true(&vm->retval)) { + vm->retval = njs_value_false; + return NJS_DONE; + } } - ret = njs_value_property(vm, value, njs_value_arg(&key), &retval); - if (njs_slow_path(ret == NJS_ERROR)) { - return NJS_ERROR; - } + return NJS_OK; +} - if (njs_is_defined(&retval)) { - return njs_bool(&retval) ? NJS_OK : NJS_DECLINED; + +static njs_int_t +njs_array_handler_some(njs_vm_t *vm, njs_array_iterator_args_t *args, + njs_value_t *entry, int64_t n) +{ + njs_int_t ret; + + if (njs_is_valid(entry)) { + ret = njs_array_iterator_call(vm, args, entry, n); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + if (njs_is_true(&vm->retval)) { + vm->retval = njs_value_true; + return NJS_DONE; + } } - return njs_is_array(value) ? NJS_OK : NJS_DECLINED; + 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) +njs_array_handler_includes(njs_vm_t *vm, njs_array_iterator_args_t *args, + njs_value_t *entry, int64_t n) { - double idx; - int64_t k, len, length; - njs_int_t ret; - njs_uint_t i; - njs_value_t this, retval, *value, *e; - njs_array_t *array, *keys; - - ret = njs_value_to_object(vm, &args[0]); - if (njs_slow_path(ret != NJS_OK)) { - return ret; + if (!njs_is_valid(entry)) { + entry = njs_value_arg(&njs_value_undefined); } - /* TODO: ArraySpeciesCreate(). */ + if (njs_values_same_zero(args->argument, entry)) { + njs_set_true(&vm->retval); - array = njs_array_alloc(vm, 0, 0, NJS_ARRAY_SPARE); - if (njs_slow_path(array == NULL)) { - return NJS_ERROR; + return NJS_DONE; } - njs_set_array(&this, array); + return NJS_OK; +} - len = 0; - length = 0; - for (i = 0; i < nargs; i++) { - e = njs_argument(args, i); +static njs_int_t +njs_array_handler_index_of(njs_vm_t *vm, njs_array_iterator_args_t *args, + njs_value_t *entry, int64_t n) +{ + if (njs_values_strict_equal(args->argument, entry)) { + njs_set_number(&vm->retval, n); - ret = njs_is_concat_spreadable(vm, e); - if (njs_slow_path(ret == NJS_ERROR)) { - return NJS_ERROR; - } - - if (ret == NJS_OK) { - ret = njs_object_length(vm, e, &len); - if (njs_slow_path(ret == NJS_ERROR)) { - return ret; - } - - if (njs_slow_path((length + len) > NJS_MAX_LENGTH)) { - njs_type_error(vm, "Invalid length"); - return NJS_ERROR; - } - - if (njs_is_fast_array(&this) && njs_is_fast_array(e) - && (length + len) <= NJS_ARRAY_LARGE_OBJECT_LENGTH) - { - for (k = 0; k < len; k++, length++) { - value = &njs_array_start(e)[k]; - - if (njs_slow_path(!njs_is_valid(value))) { - ret = njs_value_property_i64(vm, e, k, &retval); - if (njs_slow_path(ret == NJS_ERROR)) { - return ret; - } - - if (ret == NJS_DECLINED) { - njs_set_invalid(&retval); - } - - value = &retval; - } - - ret = njs_array_add(vm, array, value); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_ERROR; - } - } - - continue; - } - - if (njs_fast_object(len)) { - for (k = 0; k < len; k++, length++) { - ret = njs_value_property_i64(vm, e, k, &retval); - if (njs_slow_path(ret == NJS_ERROR)) { - return ret; - } - - if (ret != NJS_OK) { - continue; - } - - ret = njs_value_property_i64_set(vm, &this, length, - &retval); - if (njs_slow_path(ret == NJS_ERROR)) { - return ret; - } - } - - continue; - } - - keys = njs_array_indices(vm, e); - if (njs_slow_path(keys == NULL)) { - return NJS_ERROR; - } - - for (k = 0; k < keys->length; k++) { - ret = njs_value_property(vm, e, &keys->start[k], &retval); - if (njs_slow_path(ret == NJS_ERROR)) { - return ret; - } - - idx = njs_string_to_index(&keys->start[k]) + length; - - if (ret == NJS_OK) { - ret = njs_value_property_i64_set(vm, &this, idx, &retval); - if (njs_slow_path(ret == NJS_ERROR)) { - njs_array_destroy(vm, keys); - return ret; - } - } - } - - njs_array_destroy(vm, keys); - - length += len; - - continue; - } - - if (njs_slow_path((length + len) >= NJS_MAX_LENGTH)) { - njs_type_error(vm, "Invalid length"); - return NJS_ERROR; - } - - if (njs_is_fast_array(&this)) { - ret = njs_array_add(vm, array, e); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - } else { - ret = njs_value_property_i64_set(vm, &this, length, e); - if (njs_slow_path(ret == NJS_ERROR)) { - return ret; - } - } - - length++; - } - - ret = njs_object_length_set(vm, &this, length); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_ERROR; - } - - vm->retval = this; - - return NJS_OK; -} - - -static njs_int_t -njs_array_handler_index_of(njs_vm_t *vm, njs_array_iterator_args_t *args, - njs_value_t *entry, int64_t n) -{ - if (njs_values_strict_equal(args->argument, entry)) { - njs_set_number(&vm->retval, n); - - return 1; + return NJS_DONE; } 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, length; - njs_int_t ret; - njs_array_iterator_args_t iargs; - - iargs.value = njs_argument(args, 0); - - ret = njs_value_to_object(vm, iargs.value); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - iargs.argument = njs_arg(args, nargs, 1); - - ret = njs_value_length(vm, iargs.value, &length); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &from); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - if (length == 0 || from >= (int64_t) length) { - goto not_found; - } - - if (from < 0) { - from = length + from; - - if (from < 0) { - from = 0; - } - } - - iargs.from = from; - iargs.to = length; - - ret = njs_array_iterator(vm, &iargs, njs_array_handler_index_of); - if (njs_fast_path(ret != NJS_OK)) { - return (ret == NJS_DECLINED) ? NJS_OK : NJS_ERROR; - } - -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, length; - njs_int_t ret; - njs_array_iterator_args_t iargs; - - iargs.value = njs_argument(args, 0); - - ret = njs_value_to_object(vm, iargs.value); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - 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) { - ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &from); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - } 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_OK)) { - return (ret == NJS_DECLINED) ? NJS_OK : NJS_ERROR; - } - -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, int64_t n) -{ - if (!njs_is_valid(entry)) { - entry = njs_value_arg(&njs_value_undefined); - } - - if (njs_values_same_zero(args->argument, 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, length; - njs_int_t ret; - njs_array_iterator_args_t iargs; - - iargs.value = njs_argument(args, 0); - - ret = njs_value_to_object(vm, iargs.value); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - 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; - } - - ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &from); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - if (from < 0) { - from += length; - - if (from < 0) { - from = 0; - } - } - - iargs.from = from; - iargs.to = length; - - ret = njs_array_iterator(vm, &iargs, njs_array_handler_includes); - if (njs_fast_path(ret != NJS_OK)) { - return (ret == NJS_DECLINED) ? NJS_OK : NJS_ERROR; - } - -not_found: - - njs_set_false(&vm->retval); - - return NJS_OK; -} - - -static njs_int_t -njs_array_prototype_fill(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused) -{ - int64_t i, length, start, end; - njs_int_t ret; - njs_array_t *array; - njs_value_t *this, *value; - - this = njs_argument(args, 0); - - ret = njs_value_to_object(vm, this); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - array = NULL; - - if (njs_is_fast_array(this)) { - array = njs_array(this); - length = array->length; - - } else { - ret = njs_object_length(vm, this, &length); - if (njs_slow_path(ret == NJS_ERROR)) { - return ret; - } - } - - ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &start); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - start = (start < 0) ? njs_max(length + start, 0) : njs_min(start, length); - - if (njs_is_undefined(njs_arg(args, nargs, 3))) { - end = length; - - } else { - ret = njs_value_to_integer(vm, njs_arg(args, nargs, 3), &end); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - } - - end = (end < 0) ? njs_max(length + end, 0) : njs_min(end, length); - - value = njs_arg(args, nargs, 1); - - if (array != NULL) { - for (i = start; i < end; i++) { - array->start[i] = *value; - } - - vm->retval = *this; - - return NJS_OK; - } - - value = njs_arg(args, nargs, 1); - - while (start < end) { - ret = njs_value_property_i64_set(vm, this, start++, value); - if (njs_slow_path(ret == NJS_ERROR)) { - return ret; - } - } - - vm->retval = *this; - - return NJS_OK; -} - - -njs_inline njs_int_t -njs_array_iterator_call(njs_vm_t *vm, njs_array_iterator_args_t *args, - const njs_value_t *entry, uint32_t n) -{ - njs_value_t arguments[3]; - - /* GC: array elt, array */ - - arguments[0] = *entry; - njs_set_number(&arguments[1], n); - arguments[2] = *args->value; - - return njs_function_call(vm, args->function, args->argument, arguments, 3, - &vm->retval); -} - - -njs_inline njs_int_t -njs_array_validate_args(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_array_iterator_args_t *iargs) -{ - njs_int_t ret; - - iargs->value = njs_argument(args, 0); - - ret = njs_value_to_object(vm, iargs->value); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - ret = njs_value_length(vm, iargs->value, &iargs->to); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - if (njs_slow_path(!njs_is_function(njs_arg(args, nargs, 1)))) { - goto failed; - } - - iargs->from = 0; - iargs->function = njs_function(njs_argument(args, 1)); - iargs->argument = njs_arg(args, nargs, 2); - - return NJS_OK; - -failed: - - njs_type_error(vm, "unexpected iterator arguments"); - - return NJS_ERROR; -} - - static njs_int_t njs_array_handler_for_each(njs_vm_t *vm, njs_array_iterator_args_t *args, njs_value_t *entry, int64_t n) @@ -2671,70 +2367,28 @@ njs_array_handler_for_each(njs_vm_t *vm, njs_array_iterator_args_t *args, static njs_int_t -njs_array_prototype_for_each(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused) -{ - njs_int_t ret; - njs_array_iterator_args_t iargs; - - ret = njs_array_validate_args(vm, args, nargs, &iargs); - 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; - } - - njs_set_undefined(&vm->retval); - - return NJS_OK; -} - - -static njs_int_t -njs_array_handler_some(njs_vm_t *vm, njs_array_iterator_args_t *args, +njs_array_handler_find(njs_vm_t *vm, njs_array_iterator_args_t *args, njs_value_t *entry, int64_t n) { - njs_int_t ret; + njs_int_t ret; + njs_value_t copy; if (njs_is_valid(entry)) { - ret = njs_array_iterator_call(vm, args, entry, n); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - if (njs_is_true(&vm->retval)) { - vm->retval = njs_value_true; + copy = *entry; - return 1; - } + } else { + njs_set_undefined(©); } - return NJS_OK; -} - - -static njs_int_t -njs_array_prototype_some(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused) -{ - njs_int_t ret; - njs_array_iterator_args_t iargs; - - ret = njs_array_validate_args(vm, args, nargs, &iargs); + ret = njs_array_iterator_call(vm, args, ©, n); 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; - } + if (njs_is_true(&vm->retval)) { + vm->retval = copy; - if (ret != NJS_DECLINED) { - vm->retval = njs_value_false; + return NJS_DONE; } return NJS_OK; @@ -2742,22 +2396,28 @@ njs_array_prototype_some(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, static njs_int_t -njs_array_handler_every(njs_vm_t *vm, njs_array_iterator_args_t *args, +njs_array_handler_find_index(njs_vm_t *vm, njs_array_iterator_args_t *args, njs_value_t *entry, int64_t n) { - njs_int_t ret; + njs_int_t ret; + njs_value_t copy; if (njs_is_valid(entry)) { - ret = njs_array_iterator_call(vm, args, entry, n); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } + copy = *entry; - if (!njs_is_true(&vm->retval)) { - vm->retval = njs_value_false; + } else { + njs_set_undefined(©); + } - return 1; - } + ret = njs_array_iterator_call(vm, args, ©, n); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + if (njs_is_true(&vm->retval)) { + njs_set_number(&vm->retval, n); + + return NJS_DONE; } return NJS_OK; @@ -2765,24 +2425,31 @@ njs_array_handler_every(njs_vm_t *vm, njs_array_iterator_args_t *args, static njs_int_t -njs_array_prototype_every(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused) +njs_array_handler_reduce(njs_vm_t *vm, njs_array_iterator_args_t *args, + njs_value_t *entry, int64_t n) { - njs_int_t ret; - njs_array_iterator_args_t iargs; + njs_int_t ret; + njs_value_t arguments[5]; - ret = njs_array_validate_args(vm, args, nargs, &iargs); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } + if (njs_is_valid(entry)) { + if (!njs_is_valid(args->argument)) { + *(args->argument) = *entry; + return NJS_OK; + } - ret = njs_array_iterator(vm, &iargs, njs_array_handler_every); - if (njs_slow_path(ret == NJS_ERROR)) { - return ret; - } + /* GC: array elt, array */ - if (ret != NJS_DECLINED) { - vm->retval = njs_value_true; + njs_set_undefined(&arguments[0]); + arguments[1] = *args->argument; + arguments[2] = *entry; + njs_set_number(&arguments[3], n); + arguments[4] = *args->value; + + ret = njs_function_apply(vm, args->function, arguments, 5, + args->argument); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } } return NJS_OK; @@ -2805,7 +2472,6 @@ njs_array_handler_filter(njs_vm_t *vm, njs_array_iterator_args_t *args, } if (njs_is_true(&vm->retval)) { - ret = njs_array_add(vm, args->array, ©); if (njs_slow_path(ret != NJS_OK)) { return ret; @@ -2818,279 +2484,394 @@ njs_array_handler_filter(njs_vm_t *vm, njs_array_iterator_args_t *args, static njs_int_t -njs_array_prototype_filter(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused) +njs_array_handler_map(njs_vm_t *vm, njs_array_iterator_args_t *args, + njs_value_t *entry, int64_t n) { - njs_int_t ret; - njs_array_iterator_args_t iargs; + njs_int_t ret; + njs_array_t *retval; + njs_value_t this; - ret = njs_array_validate_args(vm, args, nargs, &iargs); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } + retval = args->array; - iargs.array = njs_array_alloc(vm, 0, 0, NJS_ARRAY_SPARE); - if (njs_slow_path(iargs.array == NULL)) { - return NJS_ERROR; + if (retval->object.fast_array) { + njs_set_invalid(&retval->start[n]); } - ret = njs_array_iterator(vm, &iargs, njs_array_handler_filter); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } + if (njs_is_valid(entry)) { + ret = njs_array_iterator_call(vm, args, entry, n); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + if (njs_is_valid(&vm->retval)) { + if (retval->object.fast_array) { + retval->start[n] = vm->retval; + + } else { + njs_set_array(&this, retval); - njs_set_array(&vm->retval, iargs.array); + ret = njs_value_property_i64_set(vm, &this, n, &vm->retval); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + } + } + } return NJS_OK; } static njs_int_t -njs_array_handler_find(njs_vm_t *vm, njs_array_iterator_args_t *args, - njs_value_t *entry, int64_t n) +njs_array_prototype_iterator(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t magic) { - njs_int_t ret; - njs_value_t copy; + int64_t i, length; + njs_int_t ret; + njs_array_t *array; + njs_value_t accumulator; + njs_array_iterator_args_t iargs; + njs_array_iterator_handler_t handler; - if (njs_is_valid(entry)) { - copy = *entry; + iargs.value = njs_argument(args, 0); - } else { - njs_set_undefined(©); + ret = njs_value_to_object(vm, iargs.value); + if (njs_slow_path(ret != NJS_OK)) { + return ret; } - ret = njs_array_iterator_call(vm, args, ©, n); + ret = njs_value_length(vm, iargs.value, &iargs.to); if (njs_slow_path(ret != NJS_OK)) { return ret; } - if (njs_is_true(&vm->retval)) { - vm->retval = copy; + iargs.from = 0; + + if (njs_array_arg1(magic) == NJS_ARRAY_FUNC) { + if (njs_slow_path(!njs_is_function(njs_arg(args, nargs, 1)))) { + njs_type_error(vm, "callback argument is not callable"); + return NJS_ERROR; + } + + iargs.function = njs_function(njs_argument(args, 1)); + iargs.argument = njs_arg(args, nargs, 2); - return 1; + } else { + iargs.argument = njs_arg(args, nargs, 1); } - return NJS_OK; -} + switch (njs_array_type(magic)) { + case NJS_ARRAY_EVERY: + handler = njs_array_handler_every; + break; + case NJS_ARRAY_SOME: + handler = njs_array_handler_some; + break; -static njs_int_t -njs_array_prototype_find(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused) -{ - njs_int_t ret; - njs_array_iterator_args_t iargs; + case NJS_ARRAY_INCLUDES: + case NJS_ARRAY_INDEX_OF: + switch (njs_array_type(magic)) { + case NJS_ARRAY_INCLUDES: + handler = njs_array_handler_includes; - ret = njs_array_validate_args(vm, args, nargs, &iargs); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } + if (iargs.to == 0) { + goto done; + } - ret = njs_array_iterator(vm, &iargs, njs_array_handler_find); - if (njs_slow_path(ret == NJS_ERROR)) { - return ret; - } + break; - if (ret != NJS_DECLINED) { - vm->retval = njs_value_undefined; - } + default: + handler = njs_array_handler_index_of; + } - return NJS_OK; -} + ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &iargs.from); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + if (iargs.from < 0) { + iargs.from += iargs.to; -static njs_int_t -njs_array_handler_find_index(njs_vm_t *vm, njs_array_iterator_args_t *args, - njs_value_t *entry, int64_t n) -{ - njs_int_t ret; - njs_value_t copy; + if (iargs.from < 0) { + iargs.from = 0; + } + } - if (njs_is_valid(entry)) { - copy = *entry; + break; - } else { - njs_set_undefined(©); - } + case NJS_ARRAY_FOR_EACH: + handler = njs_array_handler_for_each; + break; - ret = njs_array_iterator_call(vm, args, ©, n); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } + case NJS_ARRAY_FIND: + handler = njs_array_handler_find; + break; - if (njs_is_true(&vm->retval)) { - njs_set_number(&vm->retval, n); + case NJS_ARRAY_FIND_INDEX: + handler = njs_array_handler_find_index; + break; - return 1; - } + case NJS_ARRAY_REDUCE: + handler = njs_array_handler_reduce; - return NJS_OK; -} + njs_set_invalid(&accumulator); + if (nargs > 2) { + accumulator = *iargs.argument; + } -static njs_int_t -njs_array_prototype_find_index(njs_vm_t *vm, njs_value_t *args, - njs_uint_t nargs, njs_index_t unused) -{ - njs_int_t ret; - njs_array_iterator_args_t iargs; + iargs.argument = &accumulator; + break; - ret = njs_array_validate_args(vm, args, nargs, &iargs); - if (njs_slow_path(ret != NJS_OK)) { - return ret; + case NJS_ARRAY_FILTER: + case NJS_ARRAY_MAP: + default: + if (njs_array_type(magic) == NJS_ARRAY_FILTER) { + length = 0; + handler = njs_array_handler_filter; + + } else { + length = iargs.to; + handler = njs_array_handler_map; + } + + array = njs_array_alloc(vm, 0, length, NJS_ARRAY_SPARE); + if (njs_slow_path(array == NULL)) { + return NJS_ERROR; + } + + if (array->object.fast_array) { + for (i = 0; i < length; i++) { + njs_set_invalid(&array->start[i]); + } + } + + iargs.array = array; + + break; } - ret = njs_array_iterator(vm, &iargs, njs_array_handler_find_index); + ret = njs_array_iterator(vm, &iargs, handler); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } - if (ret != NJS_DECLINED) { + if (ret == NJS_DONE) { + return NJS_OK; + } + +done: + + /* Default values. */ + + switch (njs_array_type(magic)) { + case NJS_ARRAY_EVERY: + vm->retval = njs_value_true; + break; + + case NJS_ARRAY_SOME: + case NJS_ARRAY_INCLUDES: + vm->retval = njs_value_false; + break; + + case NJS_ARRAY_INDEX_OF: + case NJS_ARRAY_FIND_INDEX: njs_set_number(&vm->retval, -1); + break; + + case NJS_ARRAY_FOR_EACH: + case NJS_ARRAY_FIND: + njs_set_undefined(&vm->retval); + break; + + case NJS_ARRAY_REDUCE: + if (!njs_is_valid(&accumulator)) { + njs_type_error(vm, "Reduce of empty object with no initial value"); + return NJS_ERROR; + } + + vm->retval = accumulator; + break; + + case NJS_ARRAY_FILTER: + case NJS_ARRAY_MAP: + default: + njs_set_array(&vm->retval, iargs.array); } return NJS_OK; } -static njs_int_t -njs_array_handler_map(njs_vm_t *vm, njs_array_iterator_args_t *args, - njs_value_t *entry, int64_t n) +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 ret; - njs_array_t *retval; - njs_value_t this; + double idx; + int64_t i, from, to, length; + njs_int_t ret; + njs_array_t *array, *keys; + njs_value_t *entry, *value, prop, character, string_obj; + njs_object_t *object; + const u_char *p, *end, *pos; + njs_string_prop_t string_prop; - retval = args->array; + value = args->value; + from = args->from; + to = args->to; - if (retval->object.fast_array) { - njs_set_invalid(&retval->start[n]); - } + if (njs_is_array(value)) { + array = njs_array(value); - if (njs_is_valid(entry)) { - ret = njs_array_iterator_call(vm, args, entry, n); - if (njs_slow_path(ret != NJS_OK)) { - return ret; + from += 1; + + while (from-- > to) { + if (njs_slow_path(!array->object.fast_array)) { + goto process_object; + } + + if (njs_fast_path(from < array->length + && njs_is_valid(&array->start[from]))) + { + ret = handler(vm, args, &array->start[from], from); + + } else { + entry = njs_value_arg(&njs_value_invalid); + ret = njs_value_property_i64(vm, value, from, &prop); + if (njs_slow_path(ret != NJS_DECLINED)) { + if (ret == NJS_ERROR) { + return NJS_ERROR; + } + + entry = ∝ + } + + ret = handler(vm, args, entry, from); + } + + if (njs_slow_path(ret != NJS_OK)) { + if (ret == NJS_DONE) { + return NJS_DONE; + } + + return NJS_ERROR; + } } - if (njs_is_valid(&vm->retval)) { - if (retval->object.fast_array) { - retval->start[n] = vm->retval; + 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; + } - } else { - njs_set_array(&this, retval); + njs_set_type_object(&string_obj, object, NJS_OBJECT_STRING); - ret = njs_value_property_i64_set(vm, &this, n, &vm->retval); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - } + args->value = &string_obj; + } + else { + value = njs_object_value(value); } - } - return NJS_OK; -} + length = njs_string_prop(&string_prop, value); + end = string_prop.start + string_prop.size; + if ((size_t) length == string_prop.size) { + /* Byte or ASCII string. */ -static njs_int_t -njs_array_prototype_map(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused) -{ - int64_t i, length; - njs_int_t ret; - njs_array_t *array; - njs_value_t *this; - njs_array_iterator_args_t iargs; + p = string_prop.start + from; - this = njs_argument(args, 0); + i = from + 1; - ret = njs_value_to_object(vm, this); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } + while (i-- > to) { + /* This cannot fail. */ + (void) njs_string_new(vm, &character, p, 1, 1); - ret = njs_value_length(vm, this, &length); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } + ret = handler(vm, args, &character, i); + if (njs_slow_path(ret != NJS_OK)) { + if (ret == NJS_DONE) { + return NJS_DONE; + } - if (njs_slow_path(!njs_is_function(njs_arg(args, nargs, 1)))) { - goto unexpected_args; - } + return NJS_ERROR; + } - iargs.array = njs_array_alloc(vm, 0, length, 0); - if (njs_slow_path(iargs.array == NULL)) { - return NJS_ERROR; - } + p--; + } - if (njs_slow_path(length == 0)) { - goto done; - } + } else { + /* UTF-8 string. */ - iargs.from = 0; - iargs.to = length; - iargs.value = this; - iargs.function = njs_function(njs_argument(args, 1)); - iargs.argument = njs_arg(args, nargs, 2); + p = njs_string_offset(string_prop.start, end, from); + p = njs_utf8_next(p, end); - if (iargs.array->object.fast_array) { - array = iargs.array; + i = from + 1; - for (i = 0; i < length; i++) { - njs_set_invalid(&array->start[i]); - } - } + while (i-- > to) { + pos = njs_utf8_prev(p); - ret = njs_array_iterator(vm, &iargs, njs_array_handler_map); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } + /* This cannot fail. */ + (void) njs_string_new(vm, &character, pos, p - pos , 1); -done: + ret = handler(vm, args, &character, i); + if (njs_slow_path(ret != NJS_OK)) { + if (ret == NJS_DONE) { + return NJS_DONE; + } - njs_set_array(&vm->retval, iargs.array); + return NJS_ERROR; + } - return NJS_OK; + p = pos; + } + } -unexpected_args: + return NJS_OK; + } - njs_type_error(vm, "unexpected iterator arguments"); + if (!njs_is_object(value)) { + return NJS_OK; + } - return NJS_ERROR; -} +process_object: + if (!njs_fast_object(from - to)) { + keys = njs_array_indices(vm, value); + if (njs_slow_path(keys == NULL)) { + return NJS_ERROR; + } -njs_inline njs_int_t -njs_array_iterator_reduce(njs_vm_t *vm, njs_array_iterator_args_t *args, - njs_value_t *entry, int64_t n) -{ - njs_value_t arguments[5]; + i = keys->length; - /* GC: array elt, array */ + while (i > 0) { + idx = njs_string_to_index(&keys->start[--i]); - njs_set_undefined(&arguments[0]); - arguments[1] = *args->argument; - arguments[2] = *entry; - njs_set_number(&arguments[3], n); - arguments[4] = *args->value; + if (idx < to || idx > from) { + continue; + } - return njs_function_apply(vm, args->function, arguments, 5, args->argument); -} + ret = njs_array_object_handler(vm, handler, args, &keys->start[i], + idx); + if (njs_slow_path(ret != NJS_OK)) { + njs_array_destroy(vm, keys); + return ret; + } + } + njs_array_destroy(vm, keys); -static njs_int_t -njs_array_handler_reduce(njs_vm_t *vm, njs_array_iterator_args_t *args, - njs_value_t *entry, int64_t n) -{ - njs_int_t ret; + return NJS_OK; + } - if (njs_is_valid(entry)) { - if (!njs_is_valid(args->argument)) { - *(args->argument) = *entry; - return NJS_OK; - } + i = from + 1; - ret = njs_array_iterator_reduce(vm, args, entry, n); + while (i-- > to) { + ret = njs_array_object_handler(vm, handler, args, NULL, i); if (njs_slow_path(ret != NJS_OK)) { return ret; } @@ -3101,112 +2882,112 @@ njs_array_handler_reduce(njs_vm_t *vm, njs_array_iterator_args_t *args, static njs_int_t -njs_array_prototype_reduce(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused) +njs_array_prototype_reverse_iterator(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t type) { - njs_int_t ret; - njs_value_t accumulator; - njs_array_iterator_args_t iargs; + int64_t from, length; + njs_int_t ret; + njs_value_t accumulator; + njs_array_iterator_args_t iargs; + njs_array_iterator_handler_t handler; + + iargs.value = njs_argument(args, 0); - ret = njs_array_validate_args(vm, args, nargs, &iargs); + ret = njs_value_to_object(vm, iargs.value); if (njs_slow_path(ret != NJS_OK)) { return ret; } - njs_set_invalid(&accumulator); - - if (nargs > 2) { - accumulator = *iargs.argument; - } - - iargs.argument = &accumulator; + iargs.argument = njs_arg(args, nargs, 1); - ret = njs_array_iterator(vm, &iargs, njs_array_handler_reduce); + ret = njs_value_length(vm, iargs.value, &length); if (njs_slow_path(ret != NJS_OK)) { return ret; } - if (!njs_is_valid(&accumulator)) { - njs_type_error(vm, "Reduce of empty object with no initial value"); - return NJS_ERROR; - } + switch (type) { + case NJS_ARRAY_LAST_INDEX_OF: + handler = njs_array_handler_index_of; - vm->retval = accumulator; + if (length == 0) { + goto done; + } - return NJS_OK; -} + if (nargs > 2) { + ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &from); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + } else { + from = length - 1; + } -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) -{ - njs_int_t ret; - njs_value_t accumulator; - njs_array_iterator_args_t iargs; + if (from >= 0) { + from = njs_min(from, length - 1); - iargs.value = njs_argument(args, 0); + } else if (from < 0) { + from += length; + } - ret = njs_value_to_object(vm, iargs.value); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } + break; - ret = njs_value_length(vm, iargs.value, &iargs.from); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } + case NJS_ARRAY_REDUCE_RIGHT: + default: + handler = njs_array_handler_reduce; - if (njs_slow_path(!njs_is_function(njs_arg(args, nargs, 1)))) { - goto unexpected_args; - } + if (njs_slow_path(!njs_is_function(njs_arg(args, nargs, 1)))) { + njs_type_error(vm, "callback argument is not callable"); + return NJS_ERROR; + } - njs_set_invalid(&accumulator); + njs_set_invalid(&accumulator); - iargs.to = 0; - iargs.function = njs_function(njs_argument(args, 1)); - iargs.argument = &accumulator; + iargs.function = njs_function(njs_argument(args, 1)); + iargs.argument = &accumulator; - if (nargs > 2) { - accumulator = *njs_argument(args, 2); - } + if (nargs > 2) { + accumulator = *njs_argument(args, 2); - if (iargs.from == 0) { - if (nargs < 3) { - goto failed; + } else if (length == 0) { + goto done; } - goto done; + from = length - 1; + break; } - iargs.from--; + iargs.from = from; + iargs.to = 0; - ret = njs_array_reverse_iterator(vm, &iargs, njs_array_handler_reduce); - if (njs_slow_path(ret != NJS_OK)) { - return ret; + ret = njs_array_reverse_iterator(vm, &iargs, handler); + if (njs_fast_path(ret == NJS_ERROR)) { + return NJS_ERROR; } - if (!njs_is_valid(&accumulator)) { - goto failed; + if (ret == NJS_DONE) { + return NJS_OK; } done: - vm->retval = accumulator; - - return NJS_OK; - -failed: - - njs_type_error(vm, "Reduce of empty object with no initial value"); - - return NJS_ERROR; + switch (type) { + case NJS_ARRAY_LAST_INDEX_OF: + njs_set_number(&vm->retval, -1); + break; -unexpected_args: + case NJS_ARRAY_REDUCE_RIGHT: + default: + if (!njs_is_valid(&accumulator)) { + njs_type_error(vm, "Reduce of empty object with no initial value"); + return NJS_ERROR; + } - njs_type_error(vm, "unexpected iterator arguments"); + vm->retval = accumulator; + break; + } - return NJS_ERROR; + return NJS_OK; } @@ -3589,88 +3370,95 @@ static const njs_object_prop_t njs_array_prototype_properties[] = { .type = NJS_PROPERTY, - .name = njs_string("slice"), - .value = njs_native_function(njs_array_prototype_slice, 2), + .name = njs_string("concat"), + .value = njs_native_function(njs_array_prototype_concat, 1), .writable = 1, .configurable = 1, }, { .type = NJS_PROPERTY, - .name = njs_string("push"), - .value = njs_native_function(njs_array_prototype_push, 1), + .name = njs_string("copyWithin"), + .value = njs_native_function(njs_array_prototype_copy_within, 2), .writable = 1, .configurable = 1, }, { .type = NJS_PROPERTY, - .name = njs_string("pop"), - .value = njs_native_function(njs_array_prototype_pop, 0), + .name = njs_string("every"), + .value = njs_native_function2(njs_array_prototype_iterator, 1, + njs_array_func(NJS_ARRAY_EVERY)), .writable = 1, .configurable = 1, }, { .type = NJS_PROPERTY, - .name = njs_string("unshift"), - .value = njs_native_function(njs_array_prototype_unshift, 1), + .name = njs_string("fill"), + .value = njs_native_function(njs_array_prototype_fill, 1), .writable = 1, .configurable = 1, }, { .type = NJS_PROPERTY, - .name = njs_string("shift"), - .value = njs_native_function(njs_array_prototype_shift, 0), + .name = njs_string("filter"), + .value = njs_native_function2(njs_array_prototype_iterator, 1, + njs_array_func(NJS_ARRAY_FILTER)), .writable = 1, .configurable = 1, }, { .type = NJS_PROPERTY, - .name = njs_string("splice"), - .value = njs_native_function(njs_array_prototype_splice, 2), + .name = njs_string("find"), + .value = njs_native_function2(njs_array_prototype_iterator, 1, + njs_array_func(NJS_ARRAY_FIND)), .writable = 1, .configurable = 1, }, { .type = NJS_PROPERTY, - .name = njs_string("reverse"), - .value = njs_native_function(njs_array_prototype_reverse, 0), + .name = njs_string("findIndex"), + .value = njs_native_function2(njs_array_prototype_iterator, 1, + njs_array_func(NJS_ARRAY_FIND_INDEX)), .writable = 1, .configurable = 1, }, { .type = NJS_PROPERTY, - .name = njs_string("toString"), - .value = njs_native_function(njs_array_prototype_to_string, 0), + .name = njs_string("forEach"), + .value = njs_native_function2(njs_array_prototype_iterator, 1, + njs_array_func(NJS_ARRAY_FOR_EACH)), .writable = 1, .configurable = 1, }, { .type = NJS_PROPERTY, - .name = njs_string("join"), - .value = njs_native_function(njs_array_prototype_join, 1), + .name = njs_string("includes"), + .value = njs_native_function2(njs_array_prototype_iterator, 1, + njs_array_arg(NJS_ARRAY_INCLUDES)), .writable = 1, .configurable = 1, }, { .type = NJS_PROPERTY, - .name = njs_string("concat"), - .value = njs_native_function(njs_array_prototype_concat, 1), + .name = njs_string("indexOf"), + .value = njs_native_function2(njs_array_prototype_iterator, 1, + njs_array_arg(NJS_ARRAY_INDEX_OF)), .writable = 1, .configurable = 1, }, { .type = NJS_PROPERTY, - .name = njs_string("indexOf"), - .value = njs_native_function(njs_array_prototype_index_of, 1), + .name = njs_string("join"), + .value = njs_native_function(njs_array_prototype_join, 1), .writable = 1, .configurable = 1, }, @@ -3678,111 +3466,116 @@ static const njs_object_prop_t njs_array_prototype_properties[] = { .type = NJS_PROPERTY, .name = njs_string("lastIndexOf"), - .value = njs_native_function(njs_array_prototype_last_index_of, 1), + .value = njs_native_function2(njs_array_prototype_reverse_iterator, 1, + NJS_ARRAY_LAST_INDEX_OF), .writable = 1, .configurable = 1, }, { .type = NJS_PROPERTY, - .name = njs_string("includes"), - .value = njs_native_function(njs_array_prototype_includes, 1), + .name = njs_string("map"), + .value = njs_native_function2(njs_array_prototype_iterator, 1, + njs_array_func(NJS_ARRAY_MAP)), .writable = 1, .configurable = 1, }, { .type = NJS_PROPERTY, - .name = njs_string("forEach"), - .value = njs_native_function(njs_array_prototype_for_each, 1), + .name = njs_string("pop"), + .value = njs_native_function(njs_array_prototype_pop, 0), .writable = 1, .configurable = 1, }, { .type = NJS_PROPERTY, - .name = njs_string("some"), - .value = njs_native_function(njs_array_prototype_some, 1), + .name = njs_string("push"), + .value = njs_native_function(njs_array_prototype_push, 1), .writable = 1, .configurable = 1, }, { .type = NJS_PROPERTY, - .name = njs_string("every"), - .value = njs_native_function(njs_array_prototype_every, 1), + .name = njs_string("reduce"), + .value = njs_native_function2(njs_array_prototype_iterator, 1, + njs_array_func(NJS_ARRAY_REDUCE)), .writable = 1, .configurable = 1, }, { .type = NJS_PROPERTY, - .name = njs_string("fill"), - .value = njs_native_function(njs_array_prototype_fill, 1), + .name = njs_string("reduceRight"), + .value = njs_native_function2(njs_array_prototype_reverse_iterator, 1, + NJS_ARRAY_REDUCE_RIGHT), .writable = 1, .configurable = 1, }, { .type = NJS_PROPERTY, - .name = njs_string("filter"), - .value = njs_native_function(njs_array_prototype_filter, 1), + .name = njs_string("reverse"), + .value = njs_native_function(njs_array_prototype_reverse, 0), .writable = 1, .configurable = 1, }, { .type = NJS_PROPERTY, - .name = njs_string("find"), - .value = njs_native_function(njs_array_prototype_find, 1), + .name = njs_string("shift"), + .value = njs_native_function(njs_array_prototype_shift, 0), .writable = 1, .configurable = 1, }, { .type = NJS_PROPERTY, - .name = njs_string("findIndex"), - .value = njs_native_function(njs_array_prototype_find_index, 1), + .name = njs_string("slice"), + .value = njs_native_function(njs_array_prototype_slice, 2), .writable = 1, .configurable = 1, }, { .type = NJS_PROPERTY, - .name = njs_string("map"), - .value = njs_native_function(njs_array_prototype_map, 1), + .name = njs_string("some"), + .value = njs_native_function2(njs_array_prototype_iterator, 1, + njs_array_func(NJS_ARRAY_SOME)), .writable = 1, .configurable = 1, }, { .type = NJS_PROPERTY, - .name = njs_string("reduce"), - .value = njs_native_function(njs_array_prototype_reduce, 1), + .name = njs_string("sort"), + .value = njs_native_function(njs_array_prototype_sort, 1), .writable = 1, .configurable = 1, }, { .type = NJS_PROPERTY, - .name = njs_string("reduceRight"), - .value = njs_native_function(njs_array_prototype_reduce_right, 1), + .name = njs_string("splice"), + .value = njs_native_function(njs_array_prototype_splice, 2), .writable = 1, .configurable = 1, }, { .type = NJS_PROPERTY, - .name = njs_string("sort"), - .value = njs_native_function(njs_array_prototype_sort, 1), + .name = njs_string("toString"), + .value = njs_native_function(njs_array_prototype_to_string, 0), .writable = 1, .configurable = 1, }, { .type = NJS_PROPERTY, - .name = njs_string("copyWithin"), - .value = njs_native_function(njs_array_prototype_copy_within, 2), + .name = njs_string("unshift"), + .value = njs_native_function(njs_array_prototype_unshift, 1), .writable = 1, .configurable = 1, }, diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index f191bf82..a020d16a 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -4900,6 +4900,9 @@ static njs_unit_test_t njs_test[] = { njs_str("var a = [1,2,3,4,3,4]; a.lastIndexOf(3, -10)"), njs_str("-1") }, + { njs_str("[1,2,3].lastIndexOf(1, -5.3)"), + njs_str("-1") }, + { njs_str("[1,2,1].lastIndexOf(2,undefined)"), njs_str("-1") }, @@ -4964,6 +4967,9 @@ static njs_unit_test_t njs_test[] = { njs_str("Array.prototype.lastIndexOf.call({199:true, 200:'200.59', length:200}, '200.59')"), njs_str("-1") }, + { njs_str("Array.prototype.lastIndexOf.call({0:'undefined', length:0}, 'undefined')"), + njs_str("-1") }, + { njs_str("[''].lastIndexOf.call('00000000000000000000000000000а00')"), njs_str("-1") }, @@ -5083,25 +5089,19 @@ static njs_unit_test_t njs_test[] = "Array.prototype.forEach.call(s, function (a, b, c) {t = typeof c;}); [t, typeof s];"), njs_str("object,string") }, - { njs_str("var i = 0; var o = {get length() {i++}};" - "try {Array.prototype.forEach.call(o);}" - "catch (e) {i += '; ' + e} i"), - njs_str("1; TypeError: unexpected iterator arguments") }, - - { njs_str("var a = [];" - "a.some(function(v, i, a) { return v > 1 })"), + { njs_str("[].some(function(v) { return v > 1 })"), njs_str("false") }, - { njs_str("var a = [1,2,3];" - "a.some(function(v, i, a) { return v > 1 })"), + { njs_str("[11].some(function(v) { return 5 })"), njs_str("true") }, - { njs_str("var a = [1,2,3];" - "a.some(function(v, i, a) { return v > 2 })"), + { njs_str("[1,2,3].some(function(v) { return v > 1 })"), njs_str("true") }, - { njs_str("var a = [1,2,3];" - "a.some(function(v, i, a) { return v > 3 })"), + { njs_str("[1,2,3].some(function(v) { return v > 2 })"), + njs_str("true") }, + + { njs_str("[1,2,3].some(function(v) { return v > 3 })"), njs_str("false") }, { njs_str("var o = {0: 'a', 1: 'b', 2: 'c', 'length': { valueOf: function() { return 3 }}};" @@ -5118,25 +5118,20 @@ static njs_unit_test_t njs_test[] = "var r = Array.prototype.some.call(o, function(v, i, a) { return v === 'd' }); r"), njs_str("true") }, - { njs_str("var i = 0; var o = {get length() {i++}};" - "try {Array.prototype.some.call(o);}" - "catch (e) {i += '; ' + e} i"), - njs_str("1; TypeError: unexpected iterator arguments") }, + { njs_str("[].every(function(v) { return v > 1 })"), + njs_str("true") }, - { njs_str("var a = [];" - "a.every(function(v, i, a) { return v > 1 })"), + { njs_str("var accessed = false;" + "[1].every((v) => {accessed = true; return 0; }) === false && accessed === true"), njs_str("true") }, - { njs_str("var a = [3,2,1];" - "a.every(function(v, i, a) { return v > 3 })"), + { njs_str("[3,2,1].every(function(v) { return v > 3 })"), njs_str("false") }, - { njs_str("var a = [3,2,1];" - "a.every(function(v, i, a) { return v > 2 })"), + { njs_str("[3,2,1].every(function(v) { return v > 2 })"), njs_str("false") }, - { njs_str("var a = [3,2,1];" - "a.every(function(v, i, a) { return v > 0 })"), + { njs_str("[3,2,1].every(function(v) { return v > 0 })"), njs_str("true") }, { njs_str("var o = {0: 'c', 1: 'b', 2: 'c', 'length': { valueOf() { return 3 }}};" @@ -5147,11 +5142,6 @@ static njs_unit_test_t njs_test[] = "var r = Array.prototype.every.call(o, function(el, i, arr) {return el == 'c'}); r"), njs_str("true") }, - { njs_str("var i = 0; var o = {get length() {i++}};" - "try {Array.prototype.every.call(o);}" - "catch (e) {i += '; ' + e} i"), - njs_str("1; TypeError: unexpected iterator arguments") }, - { njs_str("var obj = new Date(); obj.length = 1; obj[0] = 1;" "Array.prototype.every.call(obj, (val,idx,obj)=>!(obj instanceof Date))"), njs_str("false") }, @@ -5989,11 +5979,6 @@ static njs_unit_test_t njs_test[] = "var r = Array.prototype.filter.call(o, function(el, i, arr) { return el == 'c' }); r"), njs_str("c,c") }, - { njs_str("var i = 0; var o = {get length() {i++}};" - "try {Array.prototype.filter.call(o);}" - "catch (e) {i += '; ' + e} i"), - njs_str("1; TypeError: unexpected iterator arguments") }, - { njs_str("var a = [];" "a.find(function(v, i, a) { return v > 1 })"), njs_str("undefined") }, @@ -6052,9 +6037,9 @@ static njs_unit_test_t njs_test[] = njs_str("d") }, { njs_str("var i = 0; var o = {get length() {i++}};" - "try {Array.prototype.find.call(o);}" - "catch (e) {i += '; ' + e} i"), - njs_str("1; TypeError: unexpected iterator arguments") }, + "try {Array.prototype.filter.call(o);}" + "catch (e) {i += '; ' + e}; i"), + njs_str("1; TypeError: callback argument is not callable") }, { njs_str("var callz = 0, res = [], arr = 'abc'.split('');" "void arr.find((k) => { if (0 == callz++) { arr.splice(1,1); } res.push(k) });" @@ -6115,11 +6100,6 @@ static njs_unit_test_t njs_test[] = "var r = Array.prototype.findIndex.call(o, function(el, i, arr) { return el == 'd' }); r"), njs_str("3") }, - { njs_str("var i = 0; var o = {get length() {i++}};" - "try {Array.prototype.findIndex.call(o);}" - "catch (e) {i += '; ' + e} i"), - njs_str("1; TypeError: unexpected iterator arguments") }, - { njs_str("var callz = 0, res = [], arr = 'abc'.split('');" "void arr.findIndex((k) => { if (0 == callz++) { arr.splice(1,1); } res.push(k) });" "res.join(',')"), @@ -6177,11 +6157,6 @@ static njs_unit_test_t njs_test[] = "var res = Array.prototype.map.call(obj, callbackfn); typeof res[8000]"), njs_str("undefined") }, - { njs_str("var i = 0; var o = {get length() {i++}};" - "try {Array.prototype.map.call(o);}" - "catch (e) {i += '; ' + e} i"), - njs_str("1; TypeError: unexpected iterator arguments") }, - { njs_str("var a = [1,2,3]; Object.defineProperty(a, '1', {enumerable:false});" "a.map(v=>v)"), njs_str("1,2,3") }, @@ -6249,11 +6224,6 @@ static njs_unit_test_t njs_test[] = "var r = Array.prototype.reduce.call(o, (a, b) => a + b); r"), njs_str("abcd") }, - { njs_str("var i = 0; var o = {get length() {i++}};" - "try {Array.prototype.reduce.call(o);}" - "catch (e) {i += '; ' + e} i"), - njs_str("1; TypeError: unexpected iterator arguments") }, - { njs_str("var a = [];" "a.reduceRight(function(p, v, i, a) { return p + v })"), njs_str("TypeError: Reduce of empty object with no initial value") }, @@ -6305,7 +6275,7 @@ static njs_unit_test_t njs_test[] = { njs_str("var i = 0; var o = {get length() {i++}};" "try {Array.prototype.reduceRight.call(o);}" "catch (e) {i += '; ' + e} i"), - njs_str("1; TypeError: unexpected iterator arguments") }, + njs_str("1; TypeError: callback argument is not callable") }, { njs_str("var m = [];" "[''].reduceRight.call('00000000000000000000000000000а00', (p, v, i, a) => {m.push(v)});" -- 2.47.3