]> git.kaiwu.me - njs.git/commitdiff
Refactored iterator Array methods.
authorDmitry Volyntsev <xeioex@nginx.com>
Tue, 18 Aug 2020 11:52:17 +0000 (11:52 +0000)
committerDmitry Volyntsev <xeioex@nginx.com>
Tue, 18 Aug 2020 11:52:17 +0000 (11:52 +0000)
src/njs_array.c
src/test/njs_unit_test.c

index 6f2559b0c34d8069caf6afeb7aff5e89c3112586..ddffee187550069dfcbe26652796be419e6678b6 100644 (file)
@@ -8,7 +8,45 @@
 #include <njs_main.h>
 
 
-#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 = &prop;
-                }
+    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(&copy);
     }
 
-    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, &copy, 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(&copy);
+    }
 
-            return 1;
-        }
+    ret = njs_array_iterator_call(vm, args, &copy, 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, &copy);
             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(&copy);
+    ret = njs_value_to_object(vm, iargs.value);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
     }
 
-    ret = njs_array_iterator_call(vm, args, &copy, 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(&copy);
-    }
+    case NJS_ARRAY_FOR_EACH:
+        handler = njs_array_handler_for_each;
+        break;
 
-    ret = njs_array_iterator_call(vm, args, &copy, 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 = &prop;
+                }
+
+                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,
     },
index f191bf82d3d8621505f66fdae0b1439f5accf8b0..a020d16a38b5a0b6354b7721af0439ef4e5a18dd 100644 (file)
@@ -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)});"