[njs] Introduced initial iterator support.

Dmitry Volyntsev xeioex at nginx.com
Fri Nov 6 13:48:51 UTC 2020


details:   https://hg.nginx.org/njs/rev/1c2313826b2b
branches:  
changeset: 1561:1c2313826b2b
user:      Artem S. Povalyukhin <artem.povaluhin at gmail.com>
date:      Sun Oct 25 18:29:15 2020 +0300
description:
Introduced initial iterator support.

diffstat:

 auto/sources             |    1 +
 src/njs_array.c          |   54 ++++++++
 src/njs_builtin.c        |    9 +
 src/njs_iterator.c       |  299 +++++++++++++++++++++++++++++++++++++++++++++++
 src/njs_iterator.h       |   21 +++
 src/njs_main.h           |    1 +
 src/njs_string.c         |   27 ++++
 src/njs_typed_array.c    |   59 +++++++++
 src/njs_value.h          |    1 +
 src/njs_vm.h             |    4 +-
 src/test/njs_unit_test.c |  132 ++++++++++++++++++++
 11 files changed, 607 insertions(+), 1 deletions(-)

diffs (769 lines):

diff -r 3e7f9e326219 -r 1c2313826b2b auto/sources
--- a/auto/sources	Fri Nov 06 11:41:32 2020 +0000
+++ b/auto/sources	Sun Oct 25 18:29:15 2020 +0300
@@ -58,6 +58,7 @@ NJS_LIB_SRCS=" \
    src/njs_query_string.c \
    src/njs_encoding.c \
    src/njs_buffer.c \
+   src/njs_iterator.c \
 "
 
 NJS_LIB_TEST_SRCS=" \
diff -r 3e7f9e326219 -r 1c2313826b2b src/njs_array.c
--- a/src/njs_array.c	Fri Nov 06 11:41:32 2020 +0000
+++ b/src/njs_array.c	Sun Oct 25 18:29:15 2020 +0300
@@ -3351,6 +3351,24 @@ njs_array_prototype_copy_within(njs_vm_t
 }
 
 
+static njs_int_t
+njs_array_prototype_iterator_obj(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t kind)
+{
+    njs_int_t    ret;
+    njs_value_t  *this;
+
+    this = njs_argument(args, 0);
+
+    ret = njs_value_to_object(vm, this);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    return njs_array_iterator_create(vm, this, &vm->retval, kind);
+}
+
+
 static const njs_object_prop_t  njs_array_prototype_properties[] =
 {
     {
@@ -3386,6 +3404,15 @@ static const njs_object_prop_t  njs_arra
 
     {
         .type = NJS_PROPERTY,
+        .name = njs_string("entries"),
+        .value = njs_native_function2(njs_array_prototype_iterator_obj, 0,
+                                      NJS_ENUM_BOTH),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
         .name = njs_string("every"),
         .value = njs_native_function2(njs_array_prototype_iterator, 1,
                                       njs_array_func(NJS_ARRAY_EVERY)),
@@ -3465,6 +3492,15 @@ static const njs_object_prop_t  njs_arra
 
     {
         .type = NJS_PROPERTY,
+        .name = njs_string("keys"),
+        .value = njs_native_function2(njs_array_prototype_iterator_obj, 0,
+                                      NJS_ENUM_KEYS),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
         .name = njs_string("lastIndexOf"),
         .value = njs_native_function2(njs_array_prototype_reverse_iterator, 1,
                                       NJS_ARRAY_LAST_INDEX_OF),
@@ -3579,6 +3615,24 @@ static const njs_object_prop_t  njs_arra
         .writable = 1,
         .configurable = 1,
     },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("values"),
+        .value = njs_native_function2(njs_array_prototype_iterator_obj, 0,
+                                      NJS_ENUM_VALUES),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_wellknown_symbol(NJS_SYMBOL_ITERATOR),
+        .value = njs_native_function2(njs_array_prototype_iterator_obj, 0,
+                                      NJS_ENUM_VALUES),
+        .writable = 1,
+        .configurable = 1,
+    },
 };
 
 
diff -r 3e7f9e326219 -r 1c2313826b2b src/njs_builtin.c
--- a/src/njs_builtin.c	Fri Nov 06 11:41:32 2020 +0000
+++ b/src/njs_builtin.c	Sun Oct 25 18:29:15 2020 +0300
@@ -85,6 +85,8 @@ static const njs_object_type_init_t *con
 
     /* Hidden types. */
 
+    &njs_iterator_type_init,
+    &njs_array_iterator_type_init,
     &njs_dirent_type_init,
     &njs_hash_type_init,
     &njs_hmac_type_init,
@@ -294,6 +296,10 @@ njs_builtin_objects_create(njs_vm_t *vm)
     constructor = shared->constructors;
 
     for (i = NJS_OBJ_TYPE_OBJECT; i < NJS_OBJ_TYPE_MAX; i++) {
+        if (njs_object_type_init[i]->constructor_props == NULL) {
+            continue;
+        }
+
         constructor[i] = njs_object_type_init[i]->constructor;
         constructor[i].object.shared = 0;
 
@@ -360,6 +366,9 @@ njs_builtin_objects_clone(njs_vm_t *vm, 
         vm->prototypes[i].object.__proto__ = typed_array_prototype;
     }
 
+    vm->prototypes[NJS_OBJ_TYPE_ARRAY_ITERATOR].object.__proto__ =
+                              &vm->prototypes[NJS_OBJ_TYPE_ITERATOR].object;
+
     vm->prototypes[NJS_OBJ_TYPE_BUFFER].object.__proto__ =
                               &vm->prototypes[NJS_OBJ_TYPE_UINT8_ARRAY].object;
 
diff -r 3e7f9e326219 -r 1c2313826b2b src/njs_iterator.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/njs_iterator.c	Sun Oct 25 18:29:15 2020 +0300
@@ -0,0 +1,299 @@
+
+/*
+ * Copyright (C) Artem S. Povalyukhin
+ * Copyright (C) NGINX, Inc.
+ */
+
+
+#include <njs_main.h>
+
+
+struct njs_value_iterator_s {
+    njs_value_t        target;
+    int64_t            next;
+    njs_object_enum_t  kind;
+};
+
+
+typedef struct njs_value_iterator_s  njs_array_iterator_t;
+
+
+static const njs_value_t  string_done = njs_string("done");
+static const njs_value_t  string_value = njs_string("value");
+
+
+njs_int_t
+njs_array_iterator_create(njs_vm_t *vm, const njs_value_t *target,
+    njs_value_t *retval, njs_object_enum_t kind)
+{
+    njs_object_value_t    *ov;
+    njs_array_iterator_t  *it;
+
+    ov = njs_mp_alloc(vm->mem_pool, sizeof(njs_object_value_t));
+    if (njs_slow_path(ov == NULL)) {
+        njs_memory_error(vm);
+        return NJS_ERROR;
+    }
+
+    njs_lvlhsh_init(&ov->object.hash);
+    njs_lvlhsh_init(&ov->object.shared_hash);
+    ov->object.type = NJS_OBJECT_VALUE;
+    ov->object.shared = 0;
+    ov->object.extensible = 1;
+    ov->object.error_data = 0;
+    ov->object.fast_array = 0;
+
+    ov->object.__proto__ =
+        &vm->prototypes[NJS_OBJ_TYPE_ARRAY_ITERATOR].object;
+    ov->object.slots = NULL;
+
+    it = njs_mp_alloc(vm->mem_pool, sizeof(njs_array_iterator_t));
+    if (njs_slow_path(it == NULL)) {
+        njs_memory_error(vm);
+        return NJS_ERROR;
+    }
+
+    /* GC retain it->target */
+    it->target = *target;
+    it->next = 0;
+    it->kind = kind;
+
+    njs_set_data(&ov->value, it, NJS_DATA_TAG_ARRAY_ITERATOR);
+    njs_set_object_value(retval, ov);
+
+    return NJS_OK;
+}
+
+
+njs_int_t
+njs_array_iterator_next(njs_vm_t *vm, njs_value_t *iterator,
+    njs_value_t *retval)
+{
+    int64_t               length;
+    njs_int_t             ret;
+    njs_array_t           *array, *entry;
+    njs_typed_array_t     *tarray;
+    const njs_value_t     *value;
+    njs_array_iterator_t  *it;
+
+    if (njs_slow_path(!njs_is_valid(njs_object_value(iterator)))) {
+        return NJS_DECLINED;
+    }
+
+    it = njs_object_data(iterator);
+    value = &njs_value_undefined;
+
+    if (njs_is_fast_array(&it->target)) {
+        array = njs_array(&it->target);
+        length = array->length;
+
+        if (it->next >= length) {
+            goto release;
+        }
+
+        if (it->kind > NJS_ENUM_KEYS && njs_is_valid(&array->start[it->next])) {
+            value = &array->start[it->next];
+        }
+
+    } else if (njs_is_typed_array(&it->target)) {
+        tarray = njs_typed_array(&it->target);
+
+        if (njs_slow_path(njs_is_detached_buffer(tarray->buffer))) {
+            njs_type_error(vm, "detached buffer");
+            return NJS_ERROR;
+        }
+
+        length = njs_typed_array_length(tarray);
+
+        if (it->next >= length) {
+            goto release;
+        }
+
+        if (it->kind > NJS_ENUM_KEYS) {
+            njs_set_number(retval, njs_typed_array_prop(tarray, it->next));
+            value = retval;
+        }
+
+    } else {
+        ret = njs_object_length(vm, &it->target, &length);
+        if (njs_slow_path(ret == NJS_ERROR)) {
+            return ret;
+        }
+
+        if (it->next >= length) {
+            goto release;
+        }
+
+        if (it->kind > NJS_ENUM_KEYS) {
+            ret = njs_value_property_i64(vm, &it->target, it->next, retval);
+            if (njs_slow_path(ret == NJS_ERROR)) {
+                return ret;
+            }
+
+            value = njs_is_valid(retval) ? retval
+                                         : &njs_value_undefined;
+        }
+    }
+
+    switch (it->kind) {
+    case NJS_ENUM_KEYS:
+        njs_set_number(retval, it->next++);
+        break;
+
+    case NJS_ENUM_VALUES:
+        it->next++;
+        *retval = *value;
+        break;
+
+    case NJS_ENUM_BOTH:
+        entry = njs_array_alloc(vm, 0, 2, 0);
+        if (njs_slow_path(entry == NULL)) {
+            return NJS_ERROR;
+        }
+
+        njs_set_number(&entry->start[0], it->next++);
+        entry->start[1] = *value;
+
+        njs_set_array(retval, entry);
+        break;
+
+    default:
+        njs_internal_error(vm, "invalid enum kind");
+        return NJS_ERROR;
+    }
+
+    return NJS_OK;
+
+release:
+
+    /* GC release it->target */
+    njs_mp_free(vm->mem_pool, it);
+    njs_set_invalid(njs_object_value(iterator));
+
+    return NJS_DECLINED;
+}
+
+
+static njs_int_t
+njs_iterator_prototype_get_this(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused)
+{
+    vm->retval = args[0];
+
+    return NJS_OK;
+}
+
+
+static const njs_object_prop_t  njs_iterator_prototype_properties[] =
+{
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_wellknown_symbol(NJS_SYMBOL_ITERATOR),
+        .value = njs_native_function(njs_iterator_prototype_get_this, 0),
+        .configurable = 1,
+        .writable = 1,
+    },
+};
+
+
+static const njs_object_init_t  njs_iterator_prototype_init = {
+    njs_iterator_prototype_properties,
+    njs_nitems(njs_iterator_prototype_properties),
+};
+
+
+const njs_object_type_init_t  njs_iterator_type_init = {
+    .prototype_props = &njs_iterator_prototype_init,
+    .prototype_value = { .object = { .type = NJS_OBJECT } },
+};
+
+
+static njs_int_t
+njs_array_iterator_prototype_next(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t tag)
+{
+    njs_int_t          ret;
+    njs_bool_t         check;
+    njs_value_t        *this;
+    njs_object_t       *object;
+    njs_object_prop_t  *prop_value, *prop_done;
+
+    this = njs_argument(args, 0);
+
+    check = njs_is_object_value(this)
+            && (njs_is_object_data(this, NJS_DATA_TAG_ARRAY_ITERATOR)
+                || !njs_is_valid(njs_object_value(this)));
+
+    if (njs_slow_path(!check)) {
+        njs_type_error(vm, "Method [Array Iterator].prototype.next"
+                           " called on incompatible receiver");
+        return NJS_ERROR;
+    }
+
+    object = njs_object_alloc(vm);
+    if (njs_slow_path(object == NULL)) {
+        return NJS_ERROR;
+    }
+
+    njs_set_object(&vm->retval, object);
+
+    prop_value = njs_object_property_add(vm, &vm->retval,
+                                         njs_value_arg(&string_value), 0);
+    if (njs_slow_path(prop_value == NULL)) {
+        return NJS_ERROR;
+    }
+
+    prop_done = njs_object_property_add(vm, &vm->retval,
+                                        njs_value_arg(&string_done), 0);
+    if (njs_slow_path(prop_done == NULL)) {
+        return NJS_ERROR;
+    }
+
+    ret = njs_array_iterator_next(vm, this, &prop_value->value);
+    if (njs_slow_path(ret == NJS_ERROR)) {
+        return ret;
+    }
+
+    if (njs_slow_path(ret == NJS_DECLINED)) {
+        njs_set_undefined(&prop_value->value);
+        njs_set_boolean(&prop_done->value, 1);
+
+        return NJS_OK;
+    }
+
+    njs_set_boolean(&prop_done->value, 0);
+
+    return NJS_OK;
+}
+
+
+static const njs_object_prop_t  njs_array_iterator_prototype_properties[] =
+{
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("next"),
+        .value = njs_native_function2(njs_array_iterator_prototype_next, 0,
+                                      NJS_DATA_TAG_ARRAY_ITERATOR),
+        .configurable = 1,
+        .writable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG),
+        .value = njs_string("Array Iterator"),
+        .configurable = 1,
+    },
+};
+
+
+static const njs_object_init_t  njs_array_iterator_prototype_init = {
+    njs_array_iterator_prototype_properties,
+    njs_nitems(njs_array_iterator_prototype_properties),
+};
+
+
+const njs_object_type_init_t  njs_array_iterator_type_init = {
+    .prototype_props = &njs_array_iterator_prototype_init,
+    .prototype_value = { .object = { .type = NJS_OBJECT } },
+};
diff -r 3e7f9e326219 -r 1c2313826b2b src/njs_iterator.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/njs_iterator.h	Sun Oct 25 18:29:15 2020 +0300
@@ -0,0 +1,21 @@
+
+/*
+ * Copyright (C) Artem S. Povalyukhin
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_ITERATOR_H_INCLUDED_
+#define _NJS_ITERATOR_H_INCLUDED_
+
+
+njs_int_t njs_array_iterator_create(njs_vm_t *vm, const njs_value_t *src,
+    njs_value_t *dst, njs_object_enum_t kind);
+
+njs_int_t njs_array_iterator_next(njs_vm_t *vm, njs_value_t *iterator,
+    njs_value_t *retval);
+
+
+extern const njs_object_type_init_t  njs_iterator_type_init;
+extern const njs_object_type_init_t  njs_array_iterator_type_init;
+
+#endif /* _NJS_ITERATOR_H_INCLUDED_ */
diff -r 3e7f9e326219 -r 1c2313826b2b src/njs_main.h
--- a/src/njs_main.h	Fri Nov 06 11:41:32 2020 +0000
+++ b/src/njs_main.h	Sun Oct 25 18:29:15 2020 +0300
@@ -71,6 +71,7 @@
 #include <njs_regexp_pattern.h>
 #include <njs_date.h>
 #include <njs_promise.h>
+#include <njs_iterator.h>
 
 #include <njs_math.h>
 #include <njs_json.h>
diff -r 3e7f9e326219 -r 1c2313826b2b src/njs_string.c
--- a/src/njs_string.c	Fri Nov 06 11:41:32 2020 +0000
+++ b/src/njs_string.c	Sun Oct 25 18:29:15 2020 +0300
@@ -3845,6 +3845,24 @@ njs_string_prototype_replace(njs_vm_t *v
 }
 
 
+static njs_int_t
+njs_string_prototype_iterator_obj(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t kind)
+{
+    njs_int_t    ret;
+    njs_value_t  *this;
+
+    this = njs_argument(args, 0);
+
+    ret = njs_string_object_validate(vm, this);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    return njs_array_iterator_create(vm, this, &vm->retval, kind);
+}
+
+
 double
 njs_string_to_number(const njs_value_t *value, njs_bool_t parse_float)
 {
@@ -4327,6 +4345,15 @@ static const njs_object_prop_t  njs_stri
         .writable = 1,
         .configurable = 1,
     },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_wellknown_symbol(NJS_SYMBOL_ITERATOR),
+        .value = njs_native_function2(njs_string_prototype_iterator_obj, 0,
+                                      NJS_ENUM_VALUES),
+        .writable = 1,
+        .configurable = 1,
+    },
 };
 
 
diff -r 3e7f9e326219 -r 1c2313826b2b src/njs_typed_array.c
--- a/src/njs_typed_array.c	Fri Nov 06 11:41:32 2020 +0000
+++ b/src/njs_typed_array.c	Sun Oct 25 18:29:15 2020 +0300
@@ -2184,6 +2184,29 @@ njs_typed_array_prototype_join(njs_vm_t 
 
 
 static njs_int_t
+njs_typed_array_prototype_iterator_obj(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t kind)
+{
+    njs_value_t        *this;
+    njs_typed_array_t  *array;
+
+    this = njs_argument(args, 0);
+    if (njs_slow_path(!njs_is_typed_array(this))) {
+        njs_type_error(vm, "this is not a typed array");
+        return NJS_ERROR;
+    }
+
+    array = njs_typed_array(this);
+    if (njs_slow_path(njs_is_detached_buffer(array->buffer))) {
+        njs_type_error(vm, "detached buffer");
+        return NJS_ERROR;
+    }
+
+    return njs_array_iterator_create(vm, this, &vm->retval, kind);
+}
+
+
+static njs_int_t
 njs_typed_array_constructor_intrinsic(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_index_t unused)
 {
@@ -2326,6 +2349,15 @@ static const njs_object_prop_t  njs_type
 
     {
         .type = NJS_PROPERTY,
+        .name = njs_string("entries"),
+        .value = njs_native_function2(njs_typed_array_prototype_iterator_obj, 0,
+                                      NJS_ENUM_BOTH),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
         .name = njs_string("every"),
         .value = njs_native_function2(njs_typed_array_prototype_iterator, 1,
                                       NJS_ARRAY_EVERY),
@@ -2403,6 +2435,15 @@ static const njs_object_prop_t  njs_type
 
     {
         .type = NJS_PROPERTY,
+        .name = njs_string("keys"),
+        .value = njs_native_function2(njs_typed_array_prototype_iterator_obj, 0,
+                                      NJS_ENUM_KEYS),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
         .name = njs_string("lastIndexOf"),
         .value = njs_native_function2(njs_typed_array_prototype_index_of, 1, 2),
         .writable = 1,
@@ -2490,6 +2531,24 @@ static const njs_object_prop_t  njs_type
         .writable = 1,
         .configurable = 1,
     },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("values"),
+        .value = njs_native_function2(njs_typed_array_prototype_iterator_obj, 0,
+                                      NJS_ENUM_VALUES),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_wellknown_symbol(NJS_SYMBOL_ITERATOR),
+        .value = njs_native_function2(njs_typed_array_prototype_iterator_obj, 0,
+                                      NJS_ENUM_VALUES),
+        .writable = 1,
+        .configurable = 1,
+    },
 };
 
 
diff -r 3e7f9e326219 -r 1c2313826b2b src/njs_value.h
--- a/src/njs_value.h	Fri Nov 06 11:41:32 2020 +0000
+++ b/src/njs_value.h	Sun Oct 25 18:29:15 2020 +0300
@@ -84,6 +84,7 @@ typedef enum {
     NJS_DATA_TAG_CRYPTO_HMAC,
     NJS_DATA_TAG_TEXT_ENCODER,
     NJS_DATA_TAG_TEXT_DECODER,
+    NJS_DATA_TAG_ARRAY_ITERATOR,
     NJS_DATA_TAG_MAX
 } njs_data_tag_t;
 
diff -r 3e7f9e326219 -r 1c2313826b2b src/njs_vm.h
--- a/src/njs_vm.h	Fri Nov 06 11:41:32 2020 +0000
+++ b/src/njs_vm.h	Sun Oct 25 18:29:15 2020 +0300
@@ -89,7 +89,9 @@ typedef enum {
     NJS_OBJ_TYPE_TEXT_ENCODER,
     NJS_OBJ_TYPE_BUFFER,
 
-#define NJS_OBJ_TYPE_HIDDEN_MIN    (NJS_OBJ_TYPE_FS_DIRENT)
+#define NJS_OBJ_TYPE_HIDDEN_MIN    (NJS_OBJ_TYPE_ITERATOR)
+    NJS_OBJ_TYPE_ITERATOR,
+    NJS_OBJ_TYPE_ARRAY_ITERATOR,
     NJS_OBJ_TYPE_FS_DIRENT,
     NJS_OBJ_TYPE_CRYPTO_HASH,
     NJS_OBJ_TYPE_CRYPTO_HMAC,
diff -r 3e7f9e326219 -r 1c2313826b2b src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c	Fri Nov 06 11:41:32 2020 +0000
+++ b/src/test/njs_unit_test.c	Sun Oct 25 18:29:15 2020 +0300
@@ -6864,6 +6864,138 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("[1,2].sort(1)"),
       njs_str("TypeError: comparefn must be callable or undefined") },
 
+    /*
+      Array.prototype.keys()
+      Array.prototype.values()
+      Array.prototype.entries()
+    */
+
+    { njs_str("['keys', 'values', 'entries', Symbol.iterator]"
+              ".every((x) => typeof Array.prototype[x] == 'function')"),
+      njs_str("true") },
+
+    { njs_str("['keys', 'values', 'entries', Symbol.iterator]"
+              ".every((x) => Array.prototype[x].length === 0)"),
+      njs_str("true") },
+
+#if 0
+    { njs_str("Array.prototype[Symbol.iterator] === Array.prototype.values"),
+      njs_str("true") },
+#endif
+
+    { njs_str("['keys', 'values', 'entries', Symbol.iterator]"
+              ".every((x) => typeof [][x]() == 'object')"),
+      njs_str("true") },
+
+    { njs_str("['keys', 'values', 'entries', Symbol.iterator]"
+              ".every((x) => typeof [][x]().next == 'function')"),
+      njs_str("true") },
+
+    { njs_str("var i = [1,2,3].keys();"
+              "[i.next(), i.next(), i.next(), i.next()].map((x) => x.value)"),
+      njs_str("0,1,2,") },
+
+    { njs_str("var i = [1,2,3].values();"
+              "[i.next(), i.next(), i.next(), i.next()].map((x) => x.value)"),
+      njs_str("1,2,3,") },
+
+    { njs_str("var a = [], i = a.values();"
+              "a.push(1); a.push(2); a.push(3);"
+              "[i.next(), i.next(), i.next(), i.next()].map((x) => x.value)"),
+      njs_str("1,2,3,") },
+
+    { njs_str("var a = [], i = a.values(); i.next();"
+              "a.push(1); a.push(2); a.push(3);"
+              "[i.next(), i.next(), i.next(), i.next()].map((x) => x.value)"),
+      njs_str(",,,") },
+
+    { njs_str("var i = [1,2,3].entries();"
+              "[i.next(), i.next(), i.next(), i.next()].map((x) => x.value)"),
+      njs_str("0,1,1,2,2,3,") },
+
+    { njs_str("var i = Array.prototype.keys.call('abc');"
+              "[i.next(), i.next(), i.next(), i.next()].map((x) => x.done)"),
+      njs_str("false,false,false,true") },
+
+    { njs_str("var i = Array.prototype.values.call('abc');"
+              "[i.next(), i.next(), i.next(), i.next()].map((x) => x.value)"),
+      njs_str("a,b,c,") },
+
+    { njs_str("var x = [true, 1, Symbol()];"
+              "x.map((x) => Array.prototype.keys.call(x).next()).every((x) => x.done)"),
+      njs_str("true") },
+
+    { njs_str("var x = [true, 1, Symbol()];"
+              "x.forEach((x) => Object.getPrototypeOf(Object(x)).length = 1);"
+              "x.map((x) => Array.prototype.keys.call(x).next()).every((x) => !x.done)"),
+      njs_str("true") },
+
+    /*
+      TypedArray.prototype.keys()
+      TypedArray.prototype.values()
+      TypedArray.prototype.entries()
+    */
+
+    { njs_str("['keys', 'values', 'entries', Symbol.iterator]"
+              ".every((x) => typeof Buffer.prototype[x] == 'function')"),
+      njs_str("true") },
+
+    { njs_str("var i = Buffer.from([1,2,3]).keys();"
+              "[i.next(), i.next(), i.next(), i.next()].map((x) => x.value)"),
+      njs_str("0,1,2,") },
+
+    { njs_str("var i = Buffer.from([1,2,3]).values();"
+              "[i.next(), i.next(), i.next(), i.next()].map((x) => x.value)"),
+      njs_str("1,2,3,") },
+
+    { njs_str("var i = Buffer.from([1,2,3]).entries();"
+              "[i.next(), i.next(), i.next(), i.next()].map((x) => x.value)"),
+      njs_str("0,1,1,2,2,3,") },
+
+    { njs_str("[true, 1, Symbol(), 'test', [], { length: 1 }]"
+              ".map((x) => { try { Buffer.prototype.keys.call(x); return x; } catch (e) { return e; } })"
+              ".every((x) => x instanceof TypeError)"),
+      njs_str("true") },
+
+    /* %IteratorPrototype% */
+
+    { njs_str("var x = Object.getPrototypeOf(Object.getPrototypeOf([].keys()));"
+              "typeof x[Symbol.iterator] == 'function'"),
+      njs_str("true") },
+
+    { njs_str("var x = Object.getPrototypeOf(Object.getPrototypeOf([].keys()));"
+              "x[Symbol.iterator]() === x"),
+      njs_str("true") },
+
+    /* %ArrayIteratorPrototype% */
+
+    { njs_str("var x = Object.getPrototypeOf([].keys());"
+              "typeof x.next == 'function'"),
+      njs_str("true") },
+
+    { njs_str("var x = Object.getPrototypeOf([].keys());"
+              "x[Symbol.toStringTag] == 'Array Iterator'"),
+      njs_str("true") },
+
+    /* %StringIteratorPrototype% */
+
+    { njs_str("typeof String.prototype[Symbol.iterator] == 'function'"),
+      njs_str("true") },
+
+    { njs_str("var x = Object.getPrototypeOf(''[Symbol.iterator]());"
+              "typeof x.next == 'function'"),
+      njs_str("true") },
+
+#if 0
+    { njs_str("var x = Object.getPrototypeOf(''[Symbol.iterator]());"
+              "x[Symbol.toStringTag] == 'String Iterator'"),
+      njs_str("true") },
+#else
+    { njs_str("var x = Object.getPrototypeOf(''[Symbol.iterator]());"
+              "x[Symbol.toStringTag] == 'Array Iterator'"),
+      njs_str("true") },
+#endif
+
     /* Template literal. */
 
     { njs_str("`"),


More information about the nginx-devel mailing list