]> git.kaiwu.me - njs.git/commitdiff
Fixed Array prototype functions according to the specification.
authorAlexander Borisov <alexander.borisov@nginx.com>
Thu, 19 Sep 2019 07:19:00 +0000 (10:19 +0300)
committerAlexander Borisov <alexander.borisov@nginx.com>
Thu, 19 Sep 2019 07:19:00 +0000 (10:19 +0300)
The following fuctions were fixed:
includes, indexOf, lastIndexOf, reduceRight.

src/njs_array.c
src/test/njs_unit_test.c

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