]> git.kaiwu.me - njs.git/commitdiff
Introduced TypedArray objects.
authorDmitry Volyntsev <xeioex@nginx.com>
Wed, 25 Dec 2019 12:59:01 +0000 (15:59 +0300)
committerDmitry Volyntsev <xeioex@nginx.com>
Wed, 25 Dec 2019 12:59:01 +0000 (15:59 +0300)
Added the global constructors:
  Uint8Array, Int8Array, Uint8ClampedArray, Uint16Array, Int16Array,
  Uint32Array, Int32Array, Float32Array, Float64Array.

Implemented:
    - TypedArray(),
    - TypedArray(length),
    - TypedArray(buffer, [start, [end]]),
    - TypedArray(object),
    - TypedArray(typedarray),
    - TypedArray.BYTES_PER_ELEMENT.

    - %TypedArray%.prototype.constructor
    - get %TypedArray%.prototype.buffer
    - get %TypedArray%.prototype.byteLength
    - get %TypedArray%.prototype.byteOffset
    - get %TypedArray%.prototype.length
    - %TypedArray%.prototype[@@toStringTag]
    - %TypedArray%.prototype.set(array [, start])
    - %TypedArray%.prototype.slice([start, [end]])
    - %TypedArray%.prototype.toString()
    - %TypedArray%.prototype.join(separator)
    - %TypedArray%.prototype.fill().

In collaboration with Tiago Natel de Moura.

This closes #264 issue on Github.

23 files changed:
auto/sources
src/njs.h
src/njs_array.c
src/njs_array.h
src/njs_array_buffer.c
src/njs_builtin.c
src/njs_json.c
src/njs_main.h
src/njs_number.c
src/njs_number.h
src/njs_object.c
src/njs_object_hash.h
src/njs_object_prop.c
src/njs_string.c
src/njs_typed_array.c [new file with mode: 0644]
src/njs_typed_array.h [new file with mode: 0644]
src/njs_value.c
src/njs_value.h
src/njs_value_conversion.h
src/njs_vm.h
src/njs_vmcode.c
src/test/njs_benchmark.c
src/test/njs_unit_test.c

index d7b39ece8e8c9e7d398609bbc7ea59f90ddb8f66..dbad78c6555e8a04892430aaa28fb1e2fdf9bd06 100644 (file)
@@ -53,6 +53,7 @@ NJS_LIB_SRCS=" \
    src/njs_generator.c \
    src/njs_disassembler.c \
    src/njs_array_buffer.c \
+   src/njs_typed_array.c \
    src/njs_promise.c \
 "
 
index 9421af0cda425c9ad85e19e15dbf0996f6d79165..0012325df9be1fe3a86db3e54a112169e77ac9ee 100644 (file)
--- a/src/njs.h
+++ b/src/njs.h
@@ -274,7 +274,7 @@ NJS_EXPORT njs_int_t njs_vm_value_string(njs_vm_t *vm, njs_str_t *dst,
 NJS_EXPORT njs_int_t njs_vm_retval_string(njs_vm_t *vm, njs_str_t *dst);
 
 NJS_EXPORT njs_int_t njs_vm_value_dump(njs_vm_t *vm, njs_str_t *dst,
-    const njs_value_t *value, njs_uint_t console, njs_uint_t indent);
+    njs_value_t *value, njs_uint_t console, njs_uint_t indent);
 NJS_EXPORT njs_int_t njs_vm_retval_dump(njs_vm_t *vm, njs_str_t *dst,
     njs_uint_t indent);
 
index e6016e8cf966b78e7a515223575d5b5010977609..75e94a52309a7c010040519de6942d6fc9e2b94a 100644 (file)
@@ -1089,7 +1089,7 @@ njs_array_prototype_reverse(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 }
 
 
-static njs_int_t
+njs_int_t
 njs_array_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused)
 {
index 1fe184728352ccc41ba702a3643c04a1f66365c0..92f5f5b4cde07634b7db55916f71802e14f8fbb6 100644 (file)
@@ -21,6 +21,8 @@ njs_int_t njs_array_string_add(njs_vm_t *vm, njs_array_t *array,
     const u_char *start, size_t size, size_t length);
 njs_int_t njs_array_expand(njs_vm_t *vm, njs_array_t *array, uint32_t prepend,
     uint32_t append);
+njs_int_t njs_array_prototype_to_string(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused);
 
 
 extern const njs_object_init_t  njs_array_instance_init;
index 6b200f31f3ec06fb7679bf729d2e83957794fbbd..c04bc6f98c4601b8fd7619f500efe89c7705a7f6 100644 (file)
@@ -59,7 +59,7 @@ static njs_int_t
 njs_array_buffer_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused)
 {
-    uint32_t            size;
+    uint64_t            size;
     njs_int_t           ret;
     njs_value_t         *value;
     njs_array_buffer_t  *array;
@@ -98,6 +98,16 @@ njs_array_buffer_get_this(njs_vm_t *vm, njs_value_t *args,
 }
 
 
+static njs_int_t
+njs_array_buffer_is_view(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t unused)
+{
+    njs_set_boolean(&vm->retval, njs_is_typed_array(njs_arg(args, nargs, 1)));
+
+    return NJS_OK;
+}
+
+
 static const njs_object_prop_t  njs_array_buffer_constructor_properties[] =
 {
     /* ArrayBuffer.name == "ArrayBuffer". */
@@ -134,6 +144,15 @@ static const njs_object_prop_t  njs_array_buffer_constructor_properties[] =
         .configurable = 1,
         .enumerable = 0,
     },
+
+    /* ArrayBuffer.isView(new Uint8Array()) === true */
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("isView"),
+        .value = njs_native_function(njs_array_buffer_is_view, 1),
+        .writable = 1,
+        .configurable = 1,
+    },
 };
 
 
index 4825a5810beea846348384035ad4a2461d086a25..e544dc1630b3dfe1453e4ab4620429224eccb82e 100644 (file)
@@ -58,7 +58,6 @@ static const njs_object_type_init_t *const
 
     &njs_obj_type_init,
     &njs_array_type_init,
-    &njs_array_buffer_type_init,
     &njs_boolean_type_init,
     &njs_number_type_init,
     &njs_symbol_type_init,
@@ -67,14 +66,27 @@ static const njs_object_type_init_t *const
     &njs_regexp_type_init,
     &njs_date_type_init,
     &njs_promise_type_init,
+    &njs_array_buffer_type_init,
 
     /* Hidden types. */
 
     &njs_hash_type_init,
     &njs_hmac_type_init,
+    &njs_typed_array_type_init,
 
-    /* Error types. */
+    /* TypedArray types. */
 
+    &njs_typed_array_u8_type_init,
+    &njs_typed_array_u8clamped_type_init,
+    &njs_typed_array_i8_type_init,
+    &njs_typed_array_u16_type_init,
+    &njs_typed_array_i16_type_init,
+    &njs_typed_array_u32_type_init,
+    &njs_typed_array_i32_type_init,
+    &njs_typed_array_f32_type_init,
+    &njs_typed_array_f64_type_init,
+
+    /* Error types. */
     &njs_error_type_init,
     &njs_eval_error_type_init,
     &njs_internal_error_type_init,
@@ -303,8 +315,9 @@ njs_builtin_objects_clone(njs_vm_t *vm, njs_value_t *global)
 {
     size_t        size;
     njs_uint_t    i;
-    njs_object_t  *object_prototype, *function_prototype, *error_prototype,
-                  *error_constructor;
+    njs_object_t  *object_prototype, *function_prototype,
+                  *typed_array_prototype, *error_prototype,
+                  *typed_array_ctor, *error_ctor;
 
     /*
      * Copy both prototypes and constructors arrays by one memcpy()
@@ -317,11 +330,21 @@ njs_builtin_objects_clone(njs_vm_t *vm, njs_value_t *global)
 
     object_prototype = &vm->prototypes[NJS_OBJ_TYPE_OBJECT].object;
 
-    for (i = NJS_OBJ_TYPE_ARRAY; i < NJS_OBJ_TYPE_EVAL_ERROR; i++) {
+    for (i = NJS_OBJ_TYPE_ARRAY; i < NJS_OBJ_TYPE_NORMAL_MAX; i++) {
         vm->prototypes[i].object.__proto__ = object_prototype;
     }
 
+    typed_array_prototype = &vm->prototypes[NJS_OBJ_TYPE_TYPED_ARRAY].object;
+
+    for (i = NJS_OBJ_TYPE_TYPED_ARRAY_MIN;
+         i < NJS_OBJ_TYPE_TYPED_ARRAY_MAX;
+         i++)
+    {
+        vm->prototypes[i].object.__proto__ = typed_array_prototype;
+    }
+
     error_prototype = &vm->prototypes[NJS_OBJ_TYPE_ERROR].object;
+    error_prototype->__proto__ = object_prototype;
 
     for (i = NJS_OBJ_TYPE_EVAL_ERROR; i < NJS_OBJ_TYPE_MAX; i++) {
         vm->prototypes[i].object.__proto__ = error_prototype;
@@ -329,14 +352,24 @@ njs_builtin_objects_clone(njs_vm_t *vm, njs_value_t *global)
 
     function_prototype = &vm->prototypes[NJS_OBJ_TYPE_FUNCTION].object;
 
-    for (i = NJS_OBJ_TYPE_OBJECT; i < NJS_OBJ_TYPE_EVAL_ERROR; i++) {
+    for (i = NJS_OBJ_TYPE_OBJECT; i < NJS_OBJ_TYPE_NORMAL_MAX; i++) {
         vm->constructors[i].object.__proto__ = function_prototype;
     }
 
-    error_constructor = &vm->constructors[NJS_OBJ_TYPE_ERROR].object;
+    typed_array_ctor = &vm->constructors[NJS_OBJ_TYPE_TYPED_ARRAY].object;
+
+    for (i = NJS_OBJ_TYPE_TYPED_ARRAY_MIN;
+         i < NJS_OBJ_TYPE_TYPED_ARRAY_MAX;
+         i++)
+    {
+        vm->constructors[i].object.__proto__ = typed_array_ctor;
+    }
+
+    error_ctor = &vm->constructors[NJS_OBJ_TYPE_ERROR].object;
+    error_ctor->__proto__ = function_prototype;
 
     for (i = NJS_OBJ_TYPE_EVAL_ERROR; i < NJS_OBJ_TYPE_MAX; i++) {
-        vm->constructors[i].object.__proto__ = error_constructor;
+        vm->constructors[i].object.__proto__ = error_ctor;
     }
 
     vm->global_object.__proto__ = object_prototype;
@@ -1109,6 +1142,96 @@ static const njs_object_prop_t  njs_global_this_object_properties[] =
         .configurable = 1,
     },
 
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("Uint8Array"),
+        .value = njs_prop_handler2(njs_top_level_constructor,
+                                   NJS_OBJ_TYPE_UINT8_ARRAY,
+                                   NJS_UINT8ARRAY_HASH),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("Uint16Array"),
+        .value = njs_prop_handler2(njs_top_level_constructor,
+                                   NJS_OBJ_TYPE_UINT16_ARRAY,
+                                   NJS_UINT16ARRAY_HASH),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("Uint32Array"),
+        .value = njs_prop_handler2(njs_top_level_constructor,
+                                   NJS_OBJ_TYPE_UINT32_ARRAY,
+                                   NJS_UINT32ARRAY_HASH),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("Int8Array"),
+        .value = njs_prop_handler2(njs_top_level_constructor,
+                                   NJS_OBJ_TYPE_INT8_ARRAY,
+                                   NJS_INT8ARRAY_HASH),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("Int16Array"),
+        .value = njs_prop_handler2(njs_top_level_constructor,
+                                   NJS_OBJ_TYPE_INT16_ARRAY,
+                                   NJS_INT16ARRAY_HASH),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("Int32Array"),
+        .value = njs_prop_handler2(njs_top_level_constructor,
+                                   NJS_OBJ_TYPE_INT32_ARRAY,
+                                   NJS_INT32ARRAY_HASH),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("Float32Array"),
+        .value = njs_prop_handler2(njs_top_level_constructor,
+                                   NJS_OBJ_TYPE_FLOAT32_ARRAY,
+                                   NJS_FLOAT32ARRAY_HASH),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("Float64Array"),
+        .value = njs_prop_handler2(njs_top_level_constructor,
+                                   NJS_OBJ_TYPE_FLOAT64_ARRAY,
+                                   NJS_FLOAT64ARRAY_HASH),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_long_string("Uint8ClampedArray"),
+        .value = njs_prop_handler2(njs_top_level_constructor,
+                                   NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY,
+                                   NJS_UINT8CLAMPEDARRAY_HASH),
+        .writable = 1,
+        .configurable = 1,
+    },
+
     {
         .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("Boolean"),
index f8e10eeb9c8634a56ce211505515f93e24d8446f..595a2b900253a8e9b57c83ea80a5120457c353e8 100644 (file)
@@ -1087,12 +1087,15 @@ njs_json_pop_stringify_state(njs_json_stringify_t *stringify)
 }
 
 
-#define njs_json_is_object(value)                                             \
-    (((value)->type == NJS_OBJECT)                                            \
-     || ((value)->type == NJS_OBJECT_SYMBOL)                                  \
-     || ((value)->type == NJS_EXTERNAL)                                       \
-     || ((value)->type == NJS_ARRAY)                                          \
-     || ((value)->type >= NJS_REGEXP))
+njs_inline njs_bool_t
+njs_json_is_object(const njs_value_t *value)
+{
+    return (((value)->type == NJS_OBJECT)
+             || ((value)->type == NJS_ARRAY)
+             || ((value)->type == NJS_OBJECT_SYMBOL)
+             || ((value)->type == NJS_EXTERNAL)
+             || ((value)->type >= NJS_REGEXP));
+}
 
 
 #define njs_json_stringify_indent(times)                                      \
@@ -1717,12 +1720,14 @@ const njs_object_init_t  njs_json_object_init = {
 
 
 static njs_int_t
-njs_dump_value(njs_json_stringify_t *stringify, njs_chb_t *chain,
-    const njs_value_t *value, njs_uint_t console)
+njs_dump_terminal(njs_json_stringify_t *stringify, njs_chb_t *chain,
+    njs_value_t *value, njs_uint_t console)
 {
-    njs_str_t    str;
-    njs_int_t    ret;
-    njs_value_t  str_val;
+    njs_str_t          str;
+    njs_int_t          ret;
+    njs_value_t        str_val, tag;
+    njs_typed_array_t  *array;
+    njs_string_prop_t  string;
 
     njs_int_t   (*to_string)(njs_vm_t *, njs_value_t *, const njs_value_t *);
 
@@ -1836,6 +1841,27 @@ njs_dump_value(njs_json_stringify_t *stringify, njs_chb_t *chain,
 
         break;
 
+    case NJS_TYPED_ARRAY:
+        array = njs_typed_array(value);
+        ret = njs_object_string_tag(stringify->vm, value, &tag);
+        if (njs_slow_path(ret == NJS_ERROR)) {
+            return ret;
+        }
+
+        if (ret == NJS_OK) {
+            (void) njs_string_prop(&string, &tag);
+            njs_chb_append(chain, string.start, string.size);
+            njs_chb_append_literal(chain, " ");
+        }
+
+        njs_chb_append_literal(chain, "[");
+
+        (void) njs_typed_array_to_chain(stringify->vm, chain, array, NULL);
+
+        njs_chb_append_literal(chain, "]");
+
+        break;
+
     case NJS_NUMBER:
         if (njs_slow_path(njs_number(value) == 0.0
                           && signbit(njs_number(value))))
@@ -1899,13 +1925,11 @@ njs_dump_is_external_object(const njs_value_t *value)
 
 
 njs_inline njs_bool_t
-njs_dump_is_object(const njs_value_t *value)
+njs_dump_is_recursive(const njs_value_t *value)
 {
     return (value->type == NJS_OBJECT && !njs_object(value)->error_data)
            || (value->type == NJS_ARRAY)
-           || (value->type == NJS_ARRAY_BUFFER)
-           || (value->type == NJS_PROMISE)
-           || (value->type == NJS_OBJECT_VALUE)
+           || (value->type >= NJS_OBJECT_SPECIAL_MAX)
            || njs_dump_is_external_object(value);
 }
 
@@ -1916,7 +1940,7 @@ static const njs_value_t  string_get_set = njs_long_string("[Getter/Setter]");
 
 
 njs_int_t
-njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, const njs_value_t *value,
+njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, njs_value_t *value,
     njs_uint_t console, njs_uint_t indent)
 {
     njs_int_t             i;
@@ -1934,13 +1958,11 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, const njs_value_t *value,
 
     stringify->vm = vm;
     stringify->depth = 0;
-    njs_set_undefined(&stringify->replacer);
-    stringify->keys_type = NJS_ENUM_STRING | NJS_ENUM_SYMBOL;
 
     njs_chb_init(&chain, vm->mem_pool);
 
-    if (!njs_dump_is_object(value)) {
-        ret = njs_dump_value(stringify, &chain, value, console);
+    if (!njs_dump_is_recursive(value)) {
+        ret = njs_dump_terminal(stringify, &chain, value, console);
         if (njs_slow_path(ret != NJS_OK)) {
             goto memory_error;
         }
@@ -1948,6 +1970,8 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, const njs_value_t *value,
         goto done;
     }
 
+    njs_set_undefined(&stringify->replacer);
+    stringify->keys_type = NJS_ENUM_STRING | NJS_ENUM_SYMBOL;
     indent = njs_min(indent, 10);
     stringify->space.length = indent;
     stringify->space.start = stringify->space_buf;
@@ -2028,6 +2052,7 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, const njs_value_t *value,
                 if (njs_is_defined(&prop->getter)) {
                     if (njs_is_defined(&prop->setter)) {
                         val = njs_value_arg(&string_get_set);
+
                     } else {
                         val = njs_value_arg(&string_get);
                     }
@@ -2050,7 +2075,7 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, const njs_value_t *value,
                 njs_chb_append_literal(&chain, " ");
             }
 
-            if (njs_dump_is_object(val)) {
+            if (njs_dump_is_recursive(val)) {
                 state = njs_json_push_stringify_state(vm, stringify, val);
                 if (njs_slow_path(state == NULL)) {
                     goto exception;
@@ -2059,7 +2084,7 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, const njs_value_t *value,
                 break;
             }
 
-            ret = njs_dump_value(stringify, &chain, val, console);
+            ret = njs_dump_terminal(stringify, &chain, val, console);
             if (njs_slow_path(ret != NJS_OK)) {
                 if (ret == NJS_DECLINED) {
                     goto exception;
@@ -2072,6 +2097,17 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, const njs_value_t *value,
 
         case NJS_JSON_ARRAY:
             if (state->index == 0) {
+                ret = njs_object_string_tag(vm, &state->value, &tag);
+                if (njs_slow_path(ret == NJS_ERROR)) {
+                    return ret;
+                }
+
+                if (ret == NJS_OK) {
+                    (void) njs_string_prop(&string, &tag);
+                    njs_chb_append(&chain, string.start, string.size);
+                    njs_chb_append_literal(&chain, " ");
+                }
+
                 njs_chb_append_literal(&chain, "[");
                 njs_json_stringify_indent(stringify->depth + 1);
             }
@@ -2095,7 +2131,7 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, const njs_value_t *value,
 
             val = &njs_array_start(&state->value)[state->index++];
 
-            if (njs_dump_is_object(val)) {
+            if (njs_dump_is_recursive(val)) {
                 state = njs_json_push_stringify_state(vm, stringify, val);
                 if (njs_slow_path(state == NULL)) {
                     goto exception;
@@ -2106,7 +2142,7 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, const njs_value_t *value,
 
             state->written = 1;
 
-            ret = njs_dump_value(stringify, &chain, val, console);
+            ret = njs_dump_terminal(stringify, &chain, val, console);
             if (njs_slow_path(ret != NJS_OK)) {
                 if (ret == NJS_DECLINED) {
                     goto exception;
index 6bc39614006ecc24bd573e7685ebb031b9478e00..51e8229d855486b3a10a40a825fbfef4aed8792b 100644 (file)
@@ -61,6 +61,7 @@
 #include <njs_object_hash.h>
 #include <njs_array.h>
 #include <njs_array_buffer.h>
+#include <njs_typed_array.h>
 #include <njs_function.h>
 #include <njs_regexp.h>
 #include <njs_regexp_pattern.h>
index 8b4476c182a52873c79582e951d03e26738b4025..3d0f00223d8f408415a1e26e5658457e2df7d946 100644 (file)
@@ -20,19 +20,16 @@ static njs_int_t njs_number_to_string_radix(njs_vm_t *vm, njs_value_t *string,
     double number, uint32_t radix);
 
 
-uint32_t
+double
 njs_key_to_index(const njs_value_t *value)
 {
-    double       num;
     njs_array_t  *array;
 
-    num = NAN;
-
     if (njs_fast_path(njs_is_numeric(value))) {
-        num = njs_number(value);
+        return njs_number(value);
 
     } else if (njs_is_string(value)) {
-        num = njs_string_to_index(value);
+        return njs_string_to_index(value);
 
     } else if (njs_is_array(value)) {
 
@@ -52,11 +49,7 @@ njs_key_to_index(const njs_value_t *value)
         }
     }
 
-    if ((uint32_t) num == num) {
-        return (uint32_t) num;
-    }
-
-    return NJS_ARRAY_INVALID_INDEX;
+    return NAN;
 }
 
 
index f73bf7c0bfdc2291456794458727ffedb46462aa..b8348144573c3f0a936faca0370ab8bbdb3fbde9 100644 (file)
@@ -8,7 +8,7 @@
 #define _NJS_NUMBER_H_INCLUDED_
 
 
-uint32_t njs_key_to_index(const njs_value_t *value);
+double njs_key_to_index(const njs_value_t *value);
 double njs_number_dec_parse(const u_char **start, const u_char *end);
 uint64_t njs_number_oct_parse(const u_char **start, const u_char *end);
 uint64_t njs_number_bin_parse(const u_char **start, const u_char *end);
@@ -29,6 +29,17 @@ njs_int_t njs_number_parse_float(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_index_t unused);
 
 
+njs_inline njs_bool_t
+njs_number_is_integer_index(double num, const njs_value_t *value)
+{
+    uint32_t  u32;
+
+    u32 = num;
+
+    return (u32 == num && u32 != 0xffffffff)
+            && !(njs_is_string(value) && num == 0 && signbit(num));
+}
+
 njs_inline int64_t
 njs_number_to_int64(double num)
 {
index 41fa88f8a7c7e9d35f0c4e9e4ca631d646aba077..65a57e8e7caed392959fcca49aa964f10e8c1eae 100644 (file)
@@ -20,6 +20,8 @@ static uint32_t njs_object_own_enumerate_object_length(
     njs_object_enum_type_t type, njs_bool_t all);
 static njs_int_t njs_object_enumerate_array(njs_vm_t *vm,
     const njs_array_t *array, njs_array_t *items, njs_object_enum_t kind);
+static njs_int_t njs_object_enumerate_typed_array(njs_vm_t *vm,
+    const njs_typed_array_t *array, njs_array_t *items, njs_object_enum_t kind);
 static njs_int_t njs_object_enumerate_string(njs_vm_t *vm,
     const njs_value_t *value, njs_array_t *items, njs_object_enum_t kind);
 static njs_int_t njs_object_enumerate_object(njs_vm_t *vm,
@@ -462,6 +464,10 @@ njs_object_own_enumerate_length(const njs_object_t *object,
             length += njs_object_enumerate_array_length(object);
             break;
 
+        case NJS_TYPED_ARRAY:
+            length += njs_typed_array_length((njs_typed_array_t *) object);
+            break;
+
         case NJS_OBJECT_STRING:
             length += njs_object_enumerate_string_length(object);
             break;
@@ -531,6 +537,12 @@ njs_object_own_enumerate_value(njs_vm_t *vm, const njs_object_t *object,
                                              kind);
             break;
 
+        case NJS_TYPED_ARRAY:
+            ret = njs_object_enumerate_typed_array(vm,
+                                                   (njs_typed_array_t *) object,
+                                                   items, kind);
+            break;
+
         case NJS_OBJECT_STRING:
             obj_val = (njs_object_value_t *) object;
 
@@ -797,6 +809,54 @@ njs_object_enumerate_array(njs_vm_t *vm, const njs_array_t *array,
 }
 
 
+static njs_int_t
+njs_object_enumerate_typed_array(njs_vm_t *vm, const njs_typed_array_t *array,
+    njs_array_t *items, njs_object_enum_t kind)
+{
+    uint32_t     i, length;
+    njs_value_t  *item;
+    njs_array_t  *entry;
+
+    item = items->start;
+    length = njs_typed_array_length(array);
+
+    switch (kind) {
+    case NJS_ENUM_KEYS:
+        for (i = 0; i < length; i++) {
+            njs_uint32_to_string(item++, i);
+        }
+
+        break;
+
+    case NJS_ENUM_VALUES:
+        for (i = 0; i < length; i++) {
+            njs_set_number(item++, njs_typed_array_get(array, i));
+        }
+
+        break;
+
+    case NJS_ENUM_BOTH:
+        for (i = 0; i < length; i++) {
+            entry = njs_array_alloc(vm, 2, 0);
+            if (njs_slow_path(entry == NULL)) {
+                return NJS_ERROR;
+            }
+
+            njs_uint32_to_string(&entry->start[0], i);
+            njs_set_number(&entry->start[1], njs_typed_array_get(array, i));
+
+            njs_set_array(item++, entry);
+        }
+
+        break;
+    }
+
+    items->start = item;
+
+    return NJS_OK;
+}
+
+
 static njs_int_t
 njs_object_enumerate_string(njs_vm_t *vm, const njs_value_t *value,
     njs_array_t *items, njs_object_enum_t kind)
@@ -2356,8 +2416,6 @@ static const njs_value_t  njs_object_object_string =
                                      njs_long_string("[object Object]");
 static const njs_value_t  njs_object_array_string =
                                      njs_string("[object Array]");
-static const njs_value_t  njs_object_array_buffer_string =
-                                     njs_long_string("[object ArrayBuffer]");
 static const njs_value_t  njs_object_function_string =
                                      njs_long_string("[object Function]");
 static const njs_value_t  njs_object_regexp_string =
@@ -2411,7 +2469,8 @@ njs_object_prototype_to_string(njs_vm_t *vm, njs_value_t *args,
         &njs_object_date_string,
         &njs_object_object_string,
         &njs_object_object_string,
-        &njs_object_array_buffer_string,
+        &njs_object_object_string,
+        &njs_object_object_string,
     };
 
     value = njs_argument(args, 0);
index 1d845dfae9159293c17863d5addf2207f86a36dc..2392bed28defa4e0edf6a4c27c80b3fe604860a8 100644 (file)
         'A'), 'r'), 'r'), 'a'), 'y'), 'B'), 'u'), 'f'), 'f'), 'e'), 'r')
 
 
+#define NJS_UINT8ARRAY_HASH                                                   \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(NJS_DJB_HASH_INIT,                                       \
+        'U'), 'i'), 'n'), 't'), '8'), 'A'), 'r'), 'r'), 'a'), 'y')
+
+
+#define NJS_UINT16ARRAY_HASH                                                  \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(NJS_DJB_HASH_INIT,                                       \
+        'U'), 'i'), 'n'), 't'), '1'), '6'), 'A'), 'r'), 'r'), 'a'), 'y')
+
+
+#define NJS_UINT32ARRAY_HASH                                                  \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(NJS_DJB_HASH_INIT,                                       \
+        'U'), 'i'), 'n'), 't'), '3'), '2'), 'A'), 'r'), 'r'), 'a'), 'y')
+
+
+#define NJS_INT8ARRAY_HASH                                                    \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(NJS_DJB_HASH_INIT,                                       \
+        'I'), 'n'), 't'), '8'), 'A'), 'r'), 'r'), 'a'), 'y')
+
+
+#define NJS_INT16ARRAY_HASH                                                   \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(NJS_DJB_HASH_INIT,                                       \
+        'I'), 'n'), 't'), '1'), '6'), 'A'), 'r'), 'r'), 'a'), 'y')
+
+
+#define NJS_INT32ARRAY_HASH                                                   \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(NJS_DJB_HASH_INIT,                                       \
+        'I'), 'n'), 't'), '3'), '2'), 'A'), 'r'), 'r'), 'a'), 'y')
+
+
+#define NJS_FLOAT32ARRAY_HASH                                                 \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(NJS_DJB_HASH_INIT,                                       \
+        'F'), 'l'), 'o'), 'a'), 't'), '3'), '2'), 'A'), 'r'), 'r'), 'a'), 'y')
+
+
+#define NJS_FLOAT64ARRAY_HASH                                                 \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(NJS_DJB_HASH_INIT,                                       \
+        'F'), 'l'), 'o'), 'a'), 't'), '6'), '4'), 'A'), 'r'), 'r'), 'a'), 'y')
+
+
+#define NJS_UINT8CLAMPEDARRAY_HASH                                            \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(NJS_DJB_HASH_INIT,                                       \
+        'U'), 'i'), 'n'), 't'), '8'), 'C'), 'l'), 'a'), 'm'), 'p'), 'e'),     \
+        'd'), 'A'), 'r'), 'r'), 'a'), 'y')
+
+
 #endif /* _NJS_OBJECT_HASH_H_INCLUDED_ */
index 0a8aa960cd8b25aa7d9a7013f528dd1e36c2b651..c486ece17d275f7dbdaf4f9e6d1544c88ccfd65d 100644 (file)
@@ -164,6 +164,16 @@ njs_object_prop_define(njs_vm_t *vm, njs_value_t *object,
             return NJS_ERROR;
         }
 
+        if (njs_slow_path(njs_is_typed_array(object)
+                          && njs_is_string(name)))
+        {
+            /* Integer-Indexed Exotic Objects [[DefineOwnProperty]]. */
+            if (!isnan(njs_string_to_index(name))) {
+                njs_type_error(vm, "Invalid typed array index");
+                return NJS_ERROR;
+            }
+        }
+
         /* 6.2.5.6 CompletePropertyDescriptor */
 
         if (njs_is_accessor_descriptor(prop)) {
@@ -234,6 +244,26 @@ njs_object_prop_define(njs_vm_t *vm, njs_value_t *object,
 
         return NJS_OK;
 
+    case NJS_PROPERTY_TYPED_ARRAY_REF:
+        if (njs_is_accessor_descriptor(prop)) {
+            goto exception;
+        }
+
+        if (prop->configurable == NJS_ATTRIBUTE_TRUE ||
+            prop->enumerable == NJS_ATTRIBUTE_FALSE ||
+            prop->writable == NJS_ATTRIBUTE_FALSE)
+        {
+            goto exception;
+        }
+
+        if (njs_is_valid(&prop->value)) {
+            return njs_typed_array_set_value(vm, njs_typed_array(&prev->value),
+                                             prev->value.data.magic32,
+                                             &prop->value);
+        }
+
+        return NJS_OK;
+
     default:
         njs_internal_error(vm, "unexpected property type \"%s\" "
                            "while defining property",
index 19eeda3332d6a4748b61eecab03083e05f524199..f234f882707e1e811f1900b1a576b99d965daba3 100644 (file)
@@ -4342,32 +4342,85 @@ njs_string_to_number(const njs_value_t *value, njs_bool_t parse_float)
 double
 njs_string_to_index(const njs_value_t *value)
 {
+    size_t        size, len;
     double        num;
-    size_t        size;
-    const u_char  *p, *end;
+    njs_bool_t    minus;
+    const u_char  *p, *start, *end;
+    u_char        buf[128];
 
     size = value->short_string.size;
 
     if (size != NJS_STRING_LONG) {
-        p = value->short_string.start;
+        start = value->short_string.start;
 
     } else {
         size = value->long_string.size;
-        p = value->long_string.data->start;
+        start = value->long_string.data->start;
     }
 
-    if (size == 0) {
-        return NAN;
+    p = start;
+    end = p + size;
+    minus = 0;
+
+    if (size > 1) {
+        switch (p[0]) {
+        case '0':
+            if (size != 1) {
+                return NAN;
+            }
+
+            /* Fall through. */
+
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+            break;
+
+        case '-':
+            if (size == 2 && p[1] == '0') {
+                return -0.0;
+            }
+
+            if (size == njs_length("-Infinity")
+                && memcmp(&p[1], "Infinity", njs_length("Infinity")) == 0)
+            {
+                return -INFINITY;
+            }
+
+            p++;
+            minus = 1;
+
+            break;
+
+        case 'I':
+            if (size == njs_length("Infinity")
+                && memcmp(p, "Infinity", njs_length("Infinity")) == 0)
+            {
+                return INFINITY;
+            }
+
+            /* Fall through. */
+
+        default:
+            return NAN;
+        }
     }
 
-    if (*p == '0' && size > 1) {
+    num = njs_strtod(&p, end);
+    if (p != end) {
         return NAN;
     }
 
-    end = p + size;
-    num = njs_number_dec_parse(&p, end);
+    num = minus ? -num : num;
 
-    if (p != end) {
+    len = njs_dtoa(num, (char *) buf);
+    if (size != len || memcmp(start, buf, size) != 0) {
         return NAN;
     }
 
diff --git a/src/njs_typed_array.c b/src/njs_typed_array.c
new file mode 100644 (file)
index 0000000..6c35ea1
--- /dev/null
@@ -0,0 +1,1751 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+
+#include <njs_main.h>
+
+
+static njs_int_t
+njs_typed_array_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t magic)
+{
+    double              num;
+    uint32_t            i, length, element_size;
+    uint64_t            size, offset;
+    njs_int_t           ret;
+    njs_value_t         *value, index, prop;
+    njs_array_t         *src_array;
+    njs_object_type_t   type;
+    njs_typed_array_t   *array, *src_tarray;
+    njs_array_buffer_t  *buffer;
+
+    size = 0;
+    offset = 0;
+
+    buffer = NULL;
+    src_array = NULL;
+    src_tarray = NULL;
+
+    type = magic;
+    element_size = njs_typed_array_element_size(type);
+
+    if (!vm->top_frame->ctor) {
+        njs_type_error(vm, "Constructor of TypedArray requires 'new'");
+        return NJS_ERROR;
+    }
+
+    value = njs_arg(args, nargs, 1);
+
+    if (njs_is_array_buffer(value)) {
+        buffer = njs_array_buffer(value);
+
+        ret = njs_value_to_index(vm, njs_arg(args, nargs, 2), &offset);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+        if (njs_slow_path((offset % element_size) != 0)) {
+            njs_range_error(vm, "start offset must be multiple of %uD",
+                            element_size);
+            return NJS_ERROR;
+        }
+
+        if (!njs_is_undefined(njs_arg(args, nargs, 3))) {
+            ret = njs_value_to_index(vm, njs_argument(args, 3), &size);
+            if (njs_slow_path(ret != NJS_OK)) {
+                return NJS_ERROR;
+            }
+
+            size *= element_size;
+
+            if (njs_slow_path((offset + size) > buffer->size)) {
+                njs_range_error(vm, "Invalid typed array length: %uD", size);
+                return NJS_ERROR;
+            }
+
+        } else {
+            if (njs_slow_path((buffer->size % element_size) != 0)) {
+                njs_range_error(vm, "byteLength of buffer must be "
+                                "multiple of %uD", element_size);
+                return NJS_ERROR;
+            }
+
+            if (offset > buffer->size) {
+                njs_range_error(vm, "byteOffset %uL is outside the bound of "
+                                "the buffer", offset);
+                return NJS_ERROR;
+            }
+
+            size = buffer->size - offset;
+        }
+
+    } else if (njs_is_typed_array(value)) {
+        src_tarray = njs_typed_array(value);
+        size = njs_typed_array_length(src_tarray) * element_size;
+
+    } else if (njs_is_object(value)) {
+        if (njs_is_array(value) && njs_object_hash_is_empty(value)) {
+            src_array = njs_array(value);
+            length = src_array->length;
+
+        } else {
+            ret = njs_object_length(vm, value, &length);
+            if (njs_slow_path(ret == NJS_ERROR)) {
+                return ret;
+            }
+        }
+
+        size = (uint64_t) length * element_size;
+
+    } else {
+        ret = njs_value_to_index(vm, value, &size);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+        size *= element_size;
+    }
+
+    if (buffer == NULL) {
+        buffer = njs_array_buffer_alloc(vm, size);
+        if (njs_slow_path(buffer == NULL)) {
+            return NJS_ERROR;
+        }
+    }
+
+    array = njs_mp_zalloc(vm->mem_pool, sizeof(njs_typed_array_t));
+    if (njs_slow_path(array == NULL)) {
+        goto memory_error;
+    }
+
+    array->buffer = buffer;
+    array->offset = offset / element_size;
+    array->byte_length = size;
+    array->type = type;
+
+    if (src_tarray != NULL) {
+        if (type != src_tarray->type) {
+            length = njs_typed_array_length(src_tarray);
+            for (i = 0; i < length; i++) {
+                njs_typed_array_set(array, i,
+                                    njs_typed_array_get(src_tarray, i));
+            }
+
+        } else {
+            memcpy(&buffer->u.u8[0], &src_tarray->buffer->u.u8[0], size);
+        }
+
+    } else if (src_array != NULL) {
+        for (i = 0; i < length; i++) {
+            ret = njs_value_to_number(vm, &src_array->start[i], &num);
+            if (njs_slow_path(ret == NJS_ERROR)) {
+                return NJS_ERROR;
+            }
+
+            if (ret == NJS_OK) {
+                njs_typed_array_set(array, i, num);
+            }
+        }
+
+    } else if (!njs_is_array_buffer(value) && njs_is_object(value)) {
+        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 NJS_ERROR;
+            }
+
+            num = NAN;
+
+            if (ret == NJS_OK) {
+                ret = njs_value_to_number(vm, &prop, &num);
+                if (njs_slow_path(ret == NJS_ERROR)) {
+                    return NJS_ERROR;
+                }
+            }
+
+            njs_typed_array_set(array, i, num);
+        }
+    }
+
+    njs_lvlhsh_init(&array->object.hash);
+    njs_lvlhsh_init(&array->object.shared_hash);
+    array->object.__proto__ = &vm->prototypes[type].object;
+    array->object.type = NJS_TYPED_ARRAY;
+    array->object.shared = 0;
+    array->object.extensible = 1;
+
+    njs_set_typed_array(&vm->retval, array);
+
+    return NJS_OK;
+
+memory_error:
+
+    njs_memory_error(vm);
+
+    return NJS_ERROR;
+}
+
+
+static njs_int_t
+njs_typed_array_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_value_t  njs_typed_array_uint8_tag = njs_string("Uint8Array");
+static const njs_value_t  njs_typed_array_uint8_clamped_tag =
+                                        njs_long_string("Uint8ClampedArray");
+static const njs_value_t  njs_typed_array_int8_tag = njs_string("Int8Array");
+static const njs_value_t  njs_typed_array_uint16_tag =
+                                                    njs_string("Uint16Array");
+static const njs_value_t  njs_typed_array_int16_tag = njs_string("Int16Array");
+static const njs_value_t  njs_typed_array_uint32_tag =
+                                                    njs_string("Uint32Array");
+static const njs_value_t  njs_typed_array_int32_tag = njs_string("Int32Array");
+static const njs_value_t  njs_typed_array_float32_tag =
+                                                    njs_string("Float32Array");
+static const njs_value_t  njs_typed_array_float64_tag =
+                                                    njs_string("Float64Array");
+
+static njs_int_t
+njs_typed_array_get_string_tag(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused)
+{
+    njs_value_t  *this;
+
+    static const njs_value_t  *tags[NJS_OBJ_TYPE_TYPED_ARRAY_SIZE] = {
+        &njs_typed_array_uint8_tag,
+        &njs_typed_array_uint8_clamped_tag,
+        &njs_typed_array_int8_tag,
+        &njs_typed_array_uint16_tag,
+        &njs_typed_array_int16_tag,
+        &njs_typed_array_uint32_tag,
+        &njs_typed_array_int32_tag,
+        &njs_typed_array_float32_tag,
+        &njs_typed_array_float64_tag,
+    };
+
+    this = njs_argument(args, 0);
+
+    if (!njs_is_typed_array(this)) {
+        njs_set_undefined(&vm->retval);
+        return NJS_OK;
+    }
+
+    vm->retval = *tags[njs_typed_array_index(njs_typed_array(this)->type)];
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_typed_array_prototype_length(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused)
+{
+    njs_value_t        *this;
+    njs_typed_array_t  *array;
+
+    this = njs_argument(args, 0);
+    if (!njs_is_typed_array(this)) {
+        njs_type_error(vm, "Method TypedArray.prototype.length called "
+                       "on incompatible receiver");
+        return NJS_ERROR;
+    }
+
+    array = njs_typed_array(this);
+
+    njs_set_number(&vm->retval, njs_typed_array_length(array));
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_typed_array_prototype_buffer(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused)
+{
+    njs_value_t        *this;
+    njs_typed_array_t  *array;
+
+    this = njs_argument(args, 0);
+    if (!njs_is_typed_array(this)) {
+        njs_type_error(vm, "Method TypedArray.prototype.buffer called "
+                       "on incompatible receiver");
+        return NJS_ERROR;
+    }
+
+    array = njs_typed_array(this);
+
+    njs_set_array_buffer(&vm->retval, njs_typed_array_buffer(array));
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_typed_array_prototype_byte_length(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused)
+{
+    njs_value_t        *this;
+    njs_typed_array_t  *array;
+
+    this = njs_argument(args, 0);
+    if (!njs_is_typed_array(this)) {
+        njs_type_error(vm, "Method TypedArray.prototype.byteLength called "
+                       "on incompatible receiver");
+        return NJS_ERROR;
+    }
+
+    array = njs_typed_array(this);
+
+    njs_set_number(&vm->retval, array->byte_length);
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_typed_array_prototype_byte_offset(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused)
+{
+    njs_value_t        *this;
+    njs_typed_array_t  *array;
+
+    this = njs_argument(args, 0);
+    if (!njs_is_typed_array(this)) {
+        njs_type_error(vm, "Method TypedArray.prototype.byteOffset called "
+                       "on incompatible receiver");
+        return NJS_ERROR;
+    }
+
+    array = njs_typed_array(this);
+
+    njs_set_number(&vm->retval, array->offset
+                                * njs_typed_array_element_size(array->type));
+
+    return NJS_OK;
+}
+
+
+njs_int_t
+njs_typed_array_set_value(njs_vm_t *vm, njs_typed_array_t *array,
+    uint32_t index, njs_value_t *setval)
+{
+    double     num;
+    njs_int_t  ret;
+
+    ret = njs_value_to_number(vm, setval, &num);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    njs_typed_array_set(array, index, num);
+
+    njs_set_number(setval, num);
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_typed_array_prototype_set(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused)
+{
+    double             num;
+    uint32_t           i, length, src_length;
+    int64_t            offset;
+    njs_int_t          ret;
+    njs_value_t        *this, *src, *value, index, prop;
+    njs_array_t        *array;
+    njs_typed_array_t  *self, *src_tarray;
+
+    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;
+    }
+
+    self = njs_typed_array(this);
+    src = njs_arg(args, nargs, 1);
+    value = njs_arg(args, nargs, 2);
+
+    ret = njs_value_to_integer(vm, value, &offset);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
+
+    if (njs_slow_path(offset < 0)) {
+        njs_range_error(vm, "offset is out of bounds");
+        return NJS_ERROR;
+    }
+
+    length = njs_typed_array_length(self);
+
+    if (njs_is_typed_array(src)) {
+        src_tarray = njs_typed_array(src);
+        src_length = njs_typed_array_length(src_tarray);
+
+        if (njs_slow_path((src_length > length)
+                          || (offset > length - src_length)))
+        {
+            njs_range_error(vm, "source is too large");
+            return NJS_ERROR;
+        }
+
+        length = njs_min(njs_typed_array_length(src_tarray), length - offset);
+
+        for (i = 0; i < length; i++) {
+            njs_typed_array_set(self, offset + i,
+                                njs_typed_array_get(src_tarray, i));
+        }
+
+    } else {
+        if (njs_is_array(src) && njs_object_hash_is_empty(src)) {
+            array = njs_array(src);
+            src_length = array->length;
+
+            if (njs_slow_path((src_length > length)
+                              || (offset > length - src_length)))
+            {
+                njs_range_error(vm, "source is too large");
+                return NJS_ERROR;
+            }
+
+            length = njs_min(array->length, length - offset);
+
+            for (i = 0; i < length; i++) {
+                ret = njs_value_to_number(vm, &array->start[i], &num);
+                if (ret == NJS_OK) {
+                    njs_typed_array_set(self, offset + i, num);
+                }
+            }
+
+            goto done;
+        }
+
+        ret = njs_value_to_object(vm, src);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return ret;
+        }
+
+        ret = njs_object_length(vm, src, &src_length);
+        if (njs_slow_path(ret == NJS_ERROR)) {
+            return ret;
+        }
+
+        if (njs_slow_path((src_length > length)
+                          || (offset > length - src_length)))
+        {
+            njs_range_error(vm, "source is too large");
+            return NJS_ERROR;
+        }
+
+        length = njs_min(src_length, length - offset);
+
+        for (i = 0; i < length; i++) {
+            njs_uint32_to_string(&index, i);
+
+            ret = njs_value_property(vm, src, &index, &prop);
+            if (njs_slow_path(ret == NJS_ERROR)) {
+                return NJS_ERROR;
+            }
+
+            num = NAN;
+
+            if (ret == NJS_OK) {
+                ret = njs_value_to_number(vm, &prop, &num);
+                if (njs_slow_path(ret == NJS_ERROR)) {
+                    return NJS_ERROR;
+                }
+            }
+
+            njs_typed_array_set(self, offset + i, num);
+        }
+    }
+
+done:
+
+    njs_set_undefined(&vm->retval);
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_typed_array_prototype_fill(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused)
+{
+    float               f32;
+    int8_t              i8;
+    double              num;
+    int16_t             i16;
+    int32_t             i32;
+    uint8_t             u8;
+    int64_t             start, end;
+    uint32_t            i, length;
+    njs_int_t           ret;
+    njs_value_t         *this, *setval, lvalue;
+    njs_typed_array_t   *array;
+    njs_array_buffer_t  *buffer;
+
+    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);
+    length = njs_typed_array_length(array);
+
+    setval = njs_lvalue_arg(&lvalue, args, nargs, 1);
+    ret = njs_value_to_number(vm, setval, &num);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
+
+    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);
+
+    njs_set_typed_array(&vm->retval, array);
+
+    buffer = array->buffer;
+
+    switch (array->type) {
+    case NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY:
+        if (num < 0) {
+            num = 0;
+
+        } else if (num > 255) {
+            num = 255;
+        }
+
+        u8 = lrint(num);
+
+        for (i = start; i < end; i++) {
+            buffer->u.u8[i] = u8;
+        }
+
+        break;
+
+    case NJS_OBJ_TYPE_UINT8_ARRAY:
+    case NJS_OBJ_TYPE_INT8_ARRAY:
+        i8 = njs_number_to_int32(num);
+
+        for (i = start; i < end; i++) {
+            buffer->u.u8[i] = i8;
+        }
+
+        break;
+
+    case NJS_OBJ_TYPE_UINT16_ARRAY:
+    case NJS_OBJ_TYPE_INT16_ARRAY:
+        i16 = njs_number_to_int32(num);
+
+        for (i = start; i < end; i++) {
+            buffer->u.u16[i] = i16;
+        }
+
+        break;
+
+    case NJS_OBJ_TYPE_UINT32_ARRAY:
+    case NJS_OBJ_TYPE_INT32_ARRAY:
+        i32 = njs_number_to_int32(num);
+
+        for (i = start; i < end; i++) {
+            buffer->u.u32[i] = i32;
+        }
+
+        break;
+
+    case NJS_OBJ_TYPE_FLOAT32_ARRAY:
+        f32 = num;
+
+        for (i = start; i < end; i++) {
+            buffer->u.f32[i] = f32;
+        }
+
+        break;
+
+    default:
+
+        /* NJS_OBJ_TYPE_FLOAT64_ARRAY. */
+
+        for (i = start; i < end; i++) {
+            buffer->u.f64[i] = num;
+        }
+       }
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_typed_array_species_create(njs_vm_t *vm, njs_value_t *exemplar,
+    int64_t count, njs_value_t *retval)
+{
+    njs_int_t          ret;
+    njs_value_t        this, constructor, argument;
+    njs_object_t       *object;
+    njs_typed_array_t  *array;
+
+    array = njs_typed_array(exemplar);
+
+    njs_set_function(&constructor, &vm->constructors[array->type]);
+
+    ret = njs_value_species_constructor(vm, exemplar, &constructor,
+                                        &constructor);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    object = njs_function_new_object(vm, &constructor);
+    if (njs_slow_path(object == NULL)) {
+        return NJS_ERROR;
+    }
+
+    njs_set_object(&this, object);
+    njs_set_number(&argument, count);
+
+    ret = njs_function_call2(vm, njs_function(&constructor), &this,
+                             &argument, 1, retval, 1);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
+
+    if (!njs_is_typed_array(retval)) {
+        njs_type_error(vm, "Derived TypedArray constructor "
+                       "returned not a typed array");
+        return NJS_ERROR;
+    }
+
+    array = njs_typed_array(retval);
+    if (njs_typed_array_length(array) < count) {
+        njs_type_error(vm, "Derived TypedArray constructor too small array");
+        return NJS_ERROR;
+    }
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_typed_array_prototype_slice(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused)
+{
+    int64_t             start, end, count;
+    uint32_t            i, element_size, length;
+    njs_int_t           ret;
+    njs_value_t         *this, *value;
+    njs_typed_array_t   *array, *new_array;
+    njs_array_buffer_t  *buffer, *new_buffer;
+
+    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);
+    length = njs_typed_array_length(array);
+    buffer = njs_typed_array_buffer(array);
+
+    ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &start);
+    if (njs_slow_path(ret != NJS_OK)) {
+        njs_range_error(vm, "invalid start");
+        return NJS_ERROR;
+    }
+
+    start = (start < 0) ? njs_max(length + start, 0) : njs_min(start, length);
+
+    value = njs_arg(args, nargs, 2);
+
+    if (njs_is_undefined(value)) {
+        end = length;
+
+    } else {
+        ret = njs_value_to_integer(vm, value, &end);
+        if (njs_slow_path(ret != NJS_OK)) {
+            njs_range_error(vm, "invalid end");
+            return NJS_ERROR;
+        }
+    }
+
+    end = (end < 0) ? njs_max(length + end, 0) : njs_min(end, length);
+
+    count = njs_max(end - start, 0);
+
+    ret = njs_typed_array_species_create(vm, this, count, &vm->retval);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    new_array = njs_typed_array(&vm->retval);
+    new_buffer = njs_typed_array_buffer(new_array);
+    element_size = njs_typed_array_element_size(array->type);
+
+    if (njs_fast_path(array->type == new_array->type)) {
+        start = start * element_size;
+        count = count * element_size;
+
+        memcpy(&new_buffer->u.u8[0], &buffer->u.u8[start], count);
+
+    } else {
+        for (i = 0; i < count; i++) {
+            njs_typed_array_set(new_array, i,
+                                njs_typed_array_get(array, i + start));
+        }
+    }
+
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_typed_array_prototype_copy_within(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused)
+{
+    int64_t             length, to, from, final, count;
+    uint32_t            element_size;
+    njs_int_t           ret;
+    njs_value_t         *this, *value;
+    njs_typed_array_t   *array;
+    njs_array_buffer_t  *buffer;
+
+    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);
+    length = njs_typed_array_length(array);
+
+    value = njs_arg(args, nargs, 1);
+
+    ret = njs_value_to_integer(vm, value, &to);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
+
+    to = to < 0 ? njs_max(to + length, 0) : njs_min(to, length);
+
+    value = njs_arg(args, nargs, 2);
+
+    ret = njs_value_to_integer(vm, value, &from);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    from = from < 0 ? njs_max(from + length, 0) : njs_min(from, length);
+
+    value = njs_arg(args, nargs, 3);
+
+    if (njs_is_undefined(value)) {
+        final = length;
+
+    } else {
+        ret = njs_value_to_integer(vm, value, &final);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+    }
+
+    final = (final < 0) ? njs_max(final + length, 0) : njs_min(final, length);
+
+    njs_set_typed_array(&vm->retval, array);
+
+    count = njs_min(final - from, length - to);
+
+    if (count <= 0) {
+        return NJS_OK;
+    }
+
+    buffer = njs_typed_array_buffer(array);
+    element_size = njs_typed_array_element_size(array->type);
+
+    to = to * element_size;
+    from = from * element_size;
+    count = count * element_size;
+
+    memmove(&buffer->u.u8[to], &buffer->u.u8[from], count);
+
+    return NJS_OK;
+}
+
+
+njs_int_t
+njs_typed_array_to_chain(njs_vm_t *vm, njs_chb_t *chain,
+    njs_typed_array_t *array, njs_value_t *sep)
+{
+    size_t             size, length, arr_length;
+    uint32_t           i;
+    njs_string_prop_t  separator;
+
+    if (sep == NULL) {
+        sep = njs_value_arg(&njs_string_comma);
+    }
+
+    (void) njs_string_prop(&separator, sep);
+
+    arr_length = njs_typed_array_length(array);
+
+    if (arr_length == 0) {
+        return 0;
+    }
+
+    for (i = 0; i < arr_length; i++) {
+        njs_number_to_chain(vm, chain, njs_typed_array_get(array, i));
+        njs_chb_append(chain, separator.start, separator.size);
+    }
+
+    njs_chb_drop(chain, separator.size);
+
+    size = njs_chb_size(chain);
+
+    if (njs_utf8_length(separator.start, separator.size) >= 0) {
+        length = size - (separator.size - separator.length) * (arr_length - 1);
+
+    } else {
+        length = 0;
+    }
+
+    return length;
+}
+
+
+static njs_int_t
+njs_typed_array_prototype_join(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused)
+{
+    u_char             *p;
+    size_t             size, length, arr_length;
+    njs_int_t          ret;
+    njs_chb_t          chain;
+    njs_value_t        *this, *separator;
+    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);
+    arr_length = njs_typed_array_length(array);
+
+    separator = njs_arg(args, nargs, 1);
+
+    if (njs_slow_path(!njs_is_string(separator))) {
+        if (njs_is_undefined(separator)) {
+            separator = njs_value_arg(&njs_string_comma);
+
+        } else {
+            ret = njs_value_to_string(vm, separator, separator);
+            if (njs_slow_path(ret != NJS_OK)) {
+                return ret;
+            }
+        }
+    }
+
+    if (arr_length == 0) {
+        vm->retval = njs_string_empty;
+        return NJS_OK;
+    }
+
+    njs_chb_init(&chain, vm->mem_pool);
+
+    length = njs_typed_array_to_chain(vm, &chain, array, separator);
+    size = njs_chb_size(&chain);
+
+    p = njs_string_alloc(vm, &vm->retval, size, length);
+    if (njs_slow_path(p == NULL)) {
+        return NJS_ERROR;
+    }
+
+    njs_chb_join_to(&chain, p);
+    njs_chb_destroy(&chain);
+
+    return NJS_OK;
+}
+
+
+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)
+{
+    njs_type_error(vm, "Abstract class TypedArray not directly constructable");
+
+    return NJS_ERROR;
+}
+
+
+static const njs_object_prop_t  njs_typed_array_constructor_props[] =
+{
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("name"),
+        .value = njs_string("TypedArray"),
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("length"),
+        .value = njs_value(NJS_NUMBER, 0, 0),
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("prototype"),
+        .value = njs_prop_handler(njs_object_prototype_create),
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_wellknown_symbol(NJS_SYMBOL_SPECIES),
+        .value = njs_value(NJS_INVALID, 1, NAN),
+        .getter = njs_native_function(njs_typed_array_get_this, 0),
+        .setter = njs_value(NJS_UNDEFINED, 0, NAN),
+        .writable = NJS_ATTRIBUTE_UNSET,
+        .configurable = 1,
+        .enumerable = 0,
+    },
+};
+
+
+static const njs_object_init_t  njs_typed_array_constructor_init = {
+    njs_typed_array_constructor_props,
+    njs_nitems(njs_typed_array_constructor_props),
+};
+
+
+static const njs_object_prop_t  njs_typed_array_prototype_properties[] =
+{
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG),
+        .value = njs_value(NJS_INVALID, 1, NAN),
+        .getter = njs_native_function(njs_typed_array_get_string_tag,
+                                      0),
+        .setter = njs_value(NJS_UNDEFINED, 0, NAN),
+        .writable = NJS_ATTRIBUTE_UNSET,
+        .configurable = 1,
+        .enumerable = 0,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("constructor"),
+        .value = njs_prop_handler(njs_object_prototype_create_constructor),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("buffer"),
+        .value = njs_value(NJS_INVALID, 1, NAN),
+        .getter = njs_native_function(njs_typed_array_prototype_buffer, 0),
+        .setter = njs_value(NJS_UNDEFINED, 0, NAN),
+        .writable = NJS_ATTRIBUTE_UNSET,
+        .configurable = 1,
+        .enumerable = 0,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("byteLength"),
+        .value = njs_value(NJS_INVALID, 1, NAN),
+        .getter = njs_native_function(njs_typed_array_prototype_byte_length, 0),
+        .setter = njs_value(NJS_UNDEFINED, 0, NAN),
+        .writable = NJS_ATTRIBUTE_UNSET,
+        .configurable = 1,
+        .enumerable = 0,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("byteOffset"),
+        .value = njs_value(NJS_INVALID, 1, NAN),
+        .getter = njs_native_function(njs_typed_array_prototype_byte_offset, 0),
+        .setter = njs_value(NJS_UNDEFINED, 0, NAN),
+        .writable = NJS_ATTRIBUTE_UNSET,
+        .configurable = 1,
+        .enumerable = 0,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("length"),
+        .value = njs_value(NJS_INVALID, 1, NAN),
+        .getter = njs_native_function(njs_typed_array_prototype_length, 0),
+        .setter = njs_value(NJS_UNDEFINED, 0, NAN),
+        .writable = NJS_ATTRIBUTE_UNSET,
+        .configurable = 1,
+        .enumerable = 0,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("set"),
+        .value = njs_native_function(njs_typed_array_prototype_set, 2),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("slice"),
+        .value = njs_native_function(njs_typed_array_prototype_slice, 2),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("copyWithin"),
+        .value = njs_native_function(njs_typed_array_prototype_copy_within, 2),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("fill"),
+        .value = njs_native_function(njs_typed_array_prototype_fill, 1),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("join"),
+        .value = njs_native_function(njs_typed_array_prototype_join, 1),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("toString"),
+        .value = njs_native_function(njs_array_prototype_to_string, 0),
+        .writable = 1,
+        .configurable = 1,
+    },
+};
+
+
+static const njs_object_init_t  njs_typed_array_prototype_init = {
+    njs_typed_array_prototype_properties,
+    njs_nitems(njs_typed_array_prototype_properties),
+};
+
+
+const njs_object_type_init_t  njs_typed_array_type_init = {
+    .constructor = njs_native_ctor(njs_typed_array_constructor_intrinsic, 0, 0),
+    .prototype_props = &njs_typed_array_prototype_init,
+    .constructor_props = &njs_typed_array_constructor_init,
+    .prototype_value = { .object = { .type = NJS_OBJECT } },
+};
+
+
+static const njs_object_prop_t  njs_typed_array_u8_constructor_props[] =
+{
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("name"),
+        .value = njs_string("Uint8Array"),
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("length"),
+        .value = njs_value(NJS_NUMBER, 1, 3.0),
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("prototype"),
+        .value = njs_prop_handler(njs_object_prototype_create),
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_long_string("BYTES_PER_ELEMENT"),
+        .value = njs_value(NJS_NUMBER, 1, 1),
+        .configurable = 0,
+        .enumerable = 0,
+        .writable = 0,
+    },
+};
+
+
+static const njs_object_init_t  njs_typed_array_u8_constructor_init = {
+    njs_typed_array_u8_constructor_props,
+    njs_nitems(njs_typed_array_u8_constructor_props),
+};
+
+
+static const njs_object_prop_t  njs_typed_array_u8_prototype_properties[] =
+{
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("constructor"),
+        .value = njs_prop_handler(njs_object_prototype_create_constructor),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_long_string("BYTES_PER_ELEMENT"),
+        .value = njs_value(NJS_NUMBER, 1, 1),
+        .configurable = 0,
+        .enumerable = 0,
+        .writable = 0,
+    },
+};
+
+
+static const njs_object_init_t  njs_typed_array_u8_prototype_init = {
+    njs_typed_array_u8_prototype_properties,
+    njs_nitems(njs_typed_array_u8_prototype_properties),
+};
+
+
+const njs_object_type_init_t  njs_typed_array_u8_type_init = {
+    .constructor = njs_native_ctor(njs_typed_array_constructor, 3,
+                                   NJS_OBJ_TYPE_UINT8_ARRAY),
+    .prototype_props = &njs_typed_array_u8_prototype_init,
+    .constructor_props = &njs_typed_array_u8_constructor_init,
+    .prototype_value = { .object = { .type = NJS_OBJECT } },
+};
+
+
+static const njs_object_prop_t  njs_typed_array_u8c_constructor_props[] =
+{
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("name"),
+        .value = njs_long_string("Uint8ClampedArray"),
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("length"),
+        .value = njs_value(NJS_NUMBER, 1, 3.0),
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("prototype"),
+        .value = njs_prop_handler(njs_object_prototype_create),
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_long_string("BYTES_PER_ELEMENT"),
+        .value = njs_value(NJS_NUMBER, 1, 1),
+        .configurable = 0,
+        .enumerable = 0,
+        .writable = 0,
+    },
+};
+
+
+static const njs_object_init_t  njs_typed_array_u8c_constructor_init = {
+    njs_typed_array_u8c_constructor_props,
+    njs_nitems(njs_typed_array_u8c_constructor_props),
+};
+
+
+static const njs_object_prop_t  njs_typed_array_u8c_prototype_properties[] =
+{
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("constructor"),
+        .value = njs_prop_handler(njs_object_prototype_create_constructor),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_long_string("BYTES_PER_ELEMENT"),
+        .value = njs_value(NJS_NUMBER, 1, 1),
+        .configurable = 0,
+        .enumerable = 0,
+        .writable = 0,
+    },
+};
+
+
+static const njs_object_init_t  njs_typed_array_u8c_prototype_init = {
+    njs_typed_array_u8c_prototype_properties,
+    njs_nitems(njs_typed_array_u8c_prototype_properties),
+};
+
+
+const njs_object_type_init_t  njs_typed_array_u8clamped_type_init = {
+    .constructor = njs_native_ctor(njs_typed_array_constructor, 3,
+                                   NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY),
+    .prototype_props = &njs_typed_array_u8c_prototype_init,
+    .constructor_props = &njs_typed_array_u8c_constructor_init,
+    .prototype_value = { .object = { .type = NJS_OBJECT } },
+};
+
+
+static const njs_object_prop_t  njs_typed_array_i8_constructor_props[] =
+{
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("name"),
+        .value = njs_string("Int8Array"),
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("length"),
+        .value = njs_value(NJS_NUMBER, 1, 3.0),
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("prototype"),
+        .value = njs_prop_handler(njs_object_prototype_create),
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_long_string("BYTES_PER_ELEMENT"),
+        .value = njs_value(NJS_NUMBER, 1, 1),
+        .configurable = 0,
+        .enumerable = 0,
+        .writable = 0,
+    },
+};
+
+
+static const njs_object_init_t  njs_typed_array_i8_constructor_init = {
+    njs_typed_array_i8_constructor_props,
+    njs_nitems(njs_typed_array_i8_constructor_props),
+};
+
+
+static const njs_object_prop_t  njs_typed_array_i8_prototype_properties[] =
+{
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("constructor"),
+        .value = njs_prop_handler(njs_object_prototype_create_constructor),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_long_string("BYTES_PER_ELEMENT"),
+        .value = njs_value(NJS_NUMBER, 1, 1),
+        .configurable = 0,
+        .enumerable = 0,
+        .writable = 0,
+    },
+};
+
+
+static const njs_object_init_t  njs_typed_array_i8_prototype_init = {
+    njs_typed_array_i8_prototype_properties,
+    njs_nitems(njs_typed_array_i8_prototype_properties),
+};
+
+
+const njs_object_type_init_t  njs_typed_array_i8_type_init = {
+    .constructor = njs_native_ctor(njs_typed_array_constructor, 3,
+                                   NJS_OBJ_TYPE_INT8_ARRAY),
+    .prototype_props = &njs_typed_array_i8_prototype_init,
+    .constructor_props = &njs_typed_array_i8_constructor_init,
+    .prototype_value = { .object = { .type = NJS_OBJECT } },
+};
+
+
+static const njs_object_prop_t  njs_typed_array_u16_constructor_props[] =
+{
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("name"),
+        .value = njs_string("Uint16Array"),
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("length"),
+        .value = njs_value(NJS_NUMBER, 1, 3.0),
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("prototype"),
+        .value = njs_prop_handler(njs_object_prototype_create),
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_long_string("BYTES_PER_ELEMENT"),
+        .value = njs_value(NJS_NUMBER, 1, 2),
+        .configurable = 0,
+        .enumerable = 0,
+        .writable = 0,
+    },
+};
+
+
+static const njs_object_init_t  njs_typed_array_u16_constructor_init = {
+    njs_typed_array_u16_constructor_props,
+    njs_nitems(njs_typed_array_u16_constructor_props),
+};
+
+
+static const njs_object_prop_t  njs_typed_array_u16_prototype_properties[] =
+{
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("constructor"),
+        .value = njs_prop_handler(njs_object_prototype_create_constructor),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_long_string("BYTES_PER_ELEMENT"),
+        .value = njs_value(NJS_NUMBER, 1, 2),
+        .configurable = 0,
+        .enumerable = 0,
+        .writable = 0,
+    },
+};
+
+
+static const njs_object_init_t  njs_typed_array_u16_prototype_init = {
+    njs_typed_array_u16_prototype_properties,
+    njs_nitems(njs_typed_array_u16_prototype_properties),
+};
+
+
+const njs_object_type_init_t  njs_typed_array_u16_type_init = {
+    .constructor = njs_native_ctor(njs_typed_array_constructor, 3,
+                                   NJS_OBJ_TYPE_UINT16_ARRAY),
+    .prototype_props = &njs_typed_array_u16_prototype_init,
+    .constructor_props = &njs_typed_array_u16_constructor_init,
+    .prototype_value = { .object = { .type = NJS_OBJECT } },
+};
+
+
+static const njs_object_prop_t  njs_typed_array_i16_constructor_props[] =
+{
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("name"),
+        .value = njs_string("Int16Array"),
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("length"),
+        .value = njs_value(NJS_NUMBER, 1, 3.0),
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("prototype"),
+        .value = njs_prop_handler(njs_object_prototype_create),
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_long_string("BYTES_PER_ELEMENT"),
+        .value = njs_value(NJS_NUMBER, 1, 2),
+        .configurable = 0,
+        .enumerable = 0,
+        .writable = 0,
+    },
+};
+
+
+static const njs_object_init_t  njs_typed_array_i16_constructor_init = {
+    njs_typed_array_i16_constructor_props,
+    njs_nitems(njs_typed_array_i16_constructor_props),
+};
+
+
+static const njs_object_prop_t  njs_typed_array_i16_prototype_properties[] =
+{
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("constructor"),
+        .value = njs_prop_handler(njs_object_prototype_create_constructor),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_long_string("BYTES_PER_ELEMENT"),
+        .value = njs_value(NJS_NUMBER, 1, 2),
+        .configurable = 0,
+        .enumerable = 0,
+        .writable = 0,
+    },
+};
+
+
+static const njs_object_init_t  njs_typed_array_i16_prototype_init = {
+    njs_typed_array_i16_prototype_properties,
+    njs_nitems(njs_typed_array_i16_prototype_properties),
+};
+
+
+const njs_object_type_init_t  njs_typed_array_i16_type_init = {
+    .constructor = njs_native_ctor(njs_typed_array_constructor, 3,
+                                   NJS_OBJ_TYPE_INT16_ARRAY),
+    .prototype_props = &njs_typed_array_i16_prototype_init,
+    .constructor_props = &njs_typed_array_i16_constructor_init,
+    .prototype_value = { .object = { .type = NJS_OBJECT } },
+};
+
+
+static const njs_object_prop_t  njs_typed_array_u32_constructor_props[] =
+{
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("name"),
+        .value = njs_string("Uint32Array"),
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("length"),
+        .value = njs_value(NJS_NUMBER, 1, 3.0),
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("prototype"),
+        .value = njs_prop_handler(njs_object_prototype_create),
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_long_string("BYTES_PER_ELEMENT"),
+        .value = njs_value(NJS_NUMBER, 1, 4),
+        .configurable = 0,
+        .enumerable = 0,
+        .writable = 0,
+    },
+};
+
+
+static const njs_object_init_t  njs_typed_array_u32_constructor_init = {
+    njs_typed_array_u32_constructor_props,
+    njs_nitems(njs_typed_array_u32_constructor_props),
+};
+
+
+static const njs_object_prop_t  njs_typed_array_u32_prototype_properties[] =
+{
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("constructor"),
+        .value = njs_prop_handler(njs_object_prototype_create_constructor),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_long_string("BYTES_PER_ELEMENT"),
+        .value = njs_value(NJS_NUMBER, 1, 4),
+        .configurable = 0,
+        .enumerable = 0,
+        .writable = 0,
+    },
+};
+
+
+static const njs_object_init_t  njs_typed_array_u32_prototype_init = {
+    njs_typed_array_u32_prototype_properties,
+    njs_nitems(njs_typed_array_u32_prototype_properties),
+};
+
+
+const njs_object_type_init_t  njs_typed_array_u32_type_init = {
+    .constructor = njs_native_ctor(njs_typed_array_constructor, 3,
+                                   NJS_OBJ_TYPE_UINT32_ARRAY),
+    .prototype_props = &njs_typed_array_u32_prototype_init,
+    .constructor_props = &njs_typed_array_u32_constructor_init,
+    .prototype_value = { .object = { .type = NJS_OBJECT } },
+};
+
+
+static const njs_object_prop_t  njs_typed_array_i32_constructor_props[] =
+{
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("name"),
+        .value = njs_string("Int32Array"),
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("length"),
+        .value = njs_value(NJS_NUMBER, 1, 3.0),
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("prototype"),
+        .value = njs_prop_handler(njs_object_prototype_create),
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_long_string("BYTES_PER_ELEMENT"),
+        .value = njs_value(NJS_NUMBER, 1, 4),
+        .configurable = 0,
+        .enumerable = 0,
+        .writable = 0,
+    },
+};
+
+
+static const njs_object_init_t  njs_typed_array_i32_constructor_init = {
+    njs_typed_array_i32_constructor_props,
+    njs_nitems(njs_typed_array_i32_constructor_props),
+};
+
+
+static const njs_object_prop_t  njs_typed_array_i32_prototype_properties[] =
+{
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("constructor"),
+        .value = njs_prop_handler(njs_object_prototype_create_constructor),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_long_string("BYTES_PER_ELEMENT"),
+        .value = njs_value(NJS_NUMBER, 1, 4),
+        .configurable = 0,
+        .enumerable = 0,
+        .writable = 0,
+    },
+};
+
+
+static const njs_object_init_t  njs_typed_array_i32_prototype_init = {
+    njs_typed_array_i32_prototype_properties,
+    njs_nitems(njs_typed_array_i32_prototype_properties),
+};
+
+
+const njs_object_type_init_t  njs_typed_array_i32_type_init = {
+    .constructor = njs_native_ctor(njs_typed_array_constructor, 3,
+                                   NJS_OBJ_TYPE_INT32_ARRAY),
+    .prototype_props = &njs_typed_array_i32_prototype_init,
+    .constructor_props = &njs_typed_array_i32_constructor_init,
+    .prototype_value = { .object = { .type = NJS_OBJECT } },
+};
+
+
+static const njs_object_prop_t  njs_typed_array_f32_constructor_props[] =
+{
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("name"),
+        .value = njs_string("Float32Array"),
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("length"),
+        .value = njs_value(NJS_NUMBER, 1, 3.0),
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("prototype"),
+        .value = njs_prop_handler(njs_object_prototype_create),
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_long_string("BYTES_PER_ELEMENT"),
+        .value = njs_value(NJS_NUMBER, 1, 4),
+        .configurable = 0,
+        .enumerable = 0,
+        .writable = 0,
+    },
+};
+
+
+static const njs_object_init_t  njs_typed_array_f32_constructor_init = {
+    njs_typed_array_f32_constructor_props,
+    njs_nitems(njs_typed_array_f32_constructor_props),
+};
+
+
+static const njs_object_prop_t  njs_typed_array_f32_prototype_properties[] =
+{
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("constructor"),
+        .value = njs_prop_handler(njs_object_prototype_create_constructor),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_long_string("BYTES_PER_ELEMENT"),
+        .value = njs_value(NJS_NUMBER, 1, 4),
+        .configurable = 0,
+        .enumerable = 0,
+        .writable = 0,
+    },
+};
+
+
+static const njs_object_init_t  njs_typed_array_f32_prototype_init = {
+    njs_typed_array_f32_prototype_properties,
+    njs_nitems(njs_typed_array_f32_prototype_properties),
+};
+
+
+const njs_object_type_init_t  njs_typed_array_f32_type_init = {
+    .constructor = njs_native_ctor(njs_typed_array_constructor, 3,
+                                   NJS_OBJ_TYPE_FLOAT32_ARRAY),
+    .prototype_props = &njs_typed_array_f32_prototype_init,
+    .constructor_props = &njs_typed_array_f32_constructor_init,
+    .prototype_value = { .object = { .type = NJS_OBJECT } },
+};
+
+
+static const njs_object_prop_t  njs_typed_array_f64_constructor_props[] =
+{
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("name"),
+        .value = njs_string("Float64Array"),
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("length"),
+        .value = njs_value(NJS_NUMBER, 1, 3.0),
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("prototype"),
+        .value = njs_prop_handler(njs_object_prototype_create),
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_long_string("BYTES_PER_ELEMENT"),
+        .value = njs_value(NJS_NUMBER, 1, 8),
+        .configurable = 0,
+        .enumerable = 0,
+        .writable = 0,
+    },
+};
+
+
+static const njs_object_init_t  njs_typed_array_f64_constructor_init = {
+    njs_typed_array_f64_constructor_props,
+    njs_nitems(njs_typed_array_f64_constructor_props),
+};
+
+
+static const njs_object_prop_t  njs_typed_array_f64_prototype_properties[] =
+{
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("constructor"),
+        .value = njs_prop_handler(njs_object_prototype_create_constructor),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_long_string("BYTES_PER_ELEMENT"),
+        .value = njs_value(NJS_NUMBER, 1, 8),
+        .configurable = 0,
+        .enumerable = 0,
+        .writable = 0,
+    },
+};
+
+
+static const njs_object_init_t  njs_typed_array_f64_prototype_init = {
+    njs_typed_array_f64_prototype_properties,
+    njs_nitems(njs_typed_array_f64_prototype_properties),
+};
+
+
+const njs_object_type_init_t  njs_typed_array_f64_type_init = {
+    .constructor = njs_native_ctor(njs_typed_array_constructor, 3,
+                                   NJS_OBJ_TYPE_FLOAT64_ARRAY),
+    .prototype_props = &njs_typed_array_f64_prototype_init,
+    .constructor_props = &njs_typed_array_f64_constructor_init,
+    .prototype_value = { .object = { .type = NJS_OBJECT } },
+};
diff --git a/src/njs_typed_array.h b/src/njs_typed_array.h
new file mode 100644 (file)
index 0000000..daed168
--- /dev/null
@@ -0,0 +1,163 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_TYPED_ARRAY_H_INCLUDED_
+#define _NJS_TYPED_ARRAY_H_INCLUDED_
+
+
+njs_int_t njs_typed_array_set_value(njs_vm_t *vm, njs_typed_array_t *array,
+    uint32_t index, njs_value_t *setval);
+njs_int_t njs_typed_array_to_chain(njs_vm_t *vm, njs_chb_t *chain,
+    njs_typed_array_t *array, njs_value_t *sep);
+
+
+njs_inline unsigned
+njs_typed_array_element_size(njs_object_type_t type)
+{
+    switch (type) {
+    case NJS_OBJ_TYPE_UINT8_ARRAY:
+    case NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY:
+    case NJS_OBJ_TYPE_INT8_ARRAY:
+        return 1;
+
+    case NJS_OBJ_TYPE_UINT16_ARRAY:
+    case NJS_OBJ_TYPE_INT16_ARRAY:
+        return 2;
+
+    case NJS_OBJ_TYPE_UINT32_ARRAY:
+    case NJS_OBJ_TYPE_INT32_ARRAY:
+    case NJS_OBJ_TYPE_FLOAT32_ARRAY:
+        return 4;
+
+    default:
+
+        /* NJS_OBJ_TYPE_FLOAT64_ARRAY. */
+
+        return 8;
+    }
+}
+
+
+njs_inline uint32_t
+njs_typed_array_length(const njs_typed_array_t *array)
+{
+    return array->byte_length / njs_typed_array_element_size(array->type);
+}
+
+
+njs_inline double
+njs_typed_array_get(const njs_typed_array_t *array, uint32_t index)
+{
+    njs_array_buffer_t  *buffer;
+
+    index += array->offset;
+
+    buffer = array->buffer;
+
+    switch (array->type) {
+    case NJS_OBJ_TYPE_UINT8_ARRAY:
+    case NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY:
+        return buffer->u.u8[index];
+
+    case NJS_OBJ_TYPE_INT8_ARRAY:
+        return buffer->u.i8[index];
+
+    case NJS_OBJ_TYPE_UINT16_ARRAY:
+        return buffer->u.u16[index];
+
+    case NJS_OBJ_TYPE_INT16_ARRAY:
+        return buffer->u.i16[index];
+
+    case NJS_OBJ_TYPE_UINT32_ARRAY:
+        return buffer->u.u32[index];
+
+    case NJS_OBJ_TYPE_INT32_ARRAY:
+        return buffer->u.i32[index];
+
+    case NJS_OBJ_TYPE_FLOAT32_ARRAY:
+        return buffer->u.f32[index];
+
+    default:
+
+        /* NJS_OBJ_TYPE_FLOAT64_ARRAY. */
+
+        return buffer->u.f64[index];
+    }
+
+    return NAN;
+}
+
+
+njs_inline void
+njs_typed_array_set(njs_typed_array_t *array, uint32_t index, double v)
+{
+    int8_t              i8;
+    int16_t             i16;
+    int32_t             i32;
+    njs_array_buffer_t  *buffer;
+
+    index += array->offset;
+
+    buffer = array->buffer;
+
+    switch (array->type) {
+    case NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY:
+        if (v < 0) {
+            v = 0;
+        }
+
+        if (v > 255) {
+            v = 255;
+        }
+
+        buffer->u.u8[index] = lrint(v);
+
+        break;
+
+    case NJS_OBJ_TYPE_UINT8_ARRAY:
+    case NJS_OBJ_TYPE_INT8_ARRAY:
+        i8 = njs_number_to_int32(v);
+        buffer->u.u8[index] = i8;
+        break;
+
+    case NJS_OBJ_TYPE_UINT16_ARRAY:
+    case NJS_OBJ_TYPE_INT16_ARRAY:
+        i16 = njs_number_to_int32(v);
+        buffer->u.u16[index] = i16;
+        break;
+
+    case NJS_OBJ_TYPE_UINT32_ARRAY:
+    case NJS_OBJ_TYPE_INT32_ARRAY:
+        i32 = njs_number_to_int32(v);
+        buffer->u.u32[index] = i32;
+        break;
+
+    case NJS_OBJ_TYPE_FLOAT32_ARRAY:
+        buffer->u.f32[index] = v;
+        break;
+
+    default:
+
+        /* NJS_OBJ_TYPE_FLOAT64_ARRAY. */
+
+        buffer->u.f64[index] = v;
+    }
+}
+
+
+extern const njs_object_type_init_t  njs_typed_array_type_init;
+extern const njs_object_type_init_t  njs_typed_array_u8_type_init;
+extern const njs_object_type_init_t  njs_typed_array_u8clamped_type_init;
+extern const njs_object_type_init_t  njs_typed_array_i8_type_init;
+extern const njs_object_type_init_t  njs_typed_array_u16_type_init;
+extern const njs_object_type_init_t  njs_typed_array_i16_type_init;
+extern const njs_object_type_init_t  njs_typed_array_u32_type_init;
+extern const njs_object_type_init_t  njs_typed_array_i32_type_init;
+extern const njs_object_type_init_t  njs_typed_array_f32_type_init;
+extern const njs_object_type_init_t  njs_typed_array_f64_type_init;
+
+
+#endif /* _NJS_TYPED_ARRAY_H_INCLUDED_ */
index e015c213e7e97b33692862c57a193efcbed73cf3..aff4dd984c604f596a1736bad543cee485bad171 100644 (file)
@@ -13,6 +13,8 @@ static njs_int_t njs_object_property_query(njs_vm_t *vm,
     const njs_value_t *key);
 static njs_int_t njs_array_property_query(njs_vm_t *vm,
     njs_property_query_t *pq, njs_array_t *array, uint32_t index);
+static njs_int_t njs_typed_array_property_query(njs_vm_t *vm,
+    njs_property_query_t *pq, njs_typed_array_t *array, uint32_t index);
 static njs_int_t njs_string_property_query(njs_vm_t *vm,
     njs_property_query_t *pq, njs_value_t *object, uint32_t index);
 static njs_int_t njs_external_property_query(njs_vm_t *vm,
@@ -339,7 +341,10 @@ njs_type_string(njs_value_type_t type)
         return "array";
 
     case NJS_ARRAY_BUFFER:
-        return "object arraybuffer";
+        return "array buffer";
+
+    case NJS_TYPED_ARRAY:
+        return "typed array";
 
     case NJS_OBJECT_BOOLEAN:
         return "object boolean";
@@ -500,7 +505,8 @@ njs_value_is_function(const njs_value_t *value)
  *     in NJS_PROPERTY_QUERY_GET
  *       prop->type is NJS_PROPERTY or NJS_PROPERTY_HANDLER.
  *     in NJS_PROPERTY_QUERY_SET, NJS_PROPERTY_QUERY_DELETE
- *       prop->type is NJS_PROPERTY, NJS_PROPERTY_REF or
+ *       prop->type is NJS_PROPERTY, NJS_PROPERTY_REF,
+ *       NJS_PROPERTY_TYPED_ARRAY_REF or
  *       NJS_PROPERTY_HANDLER.
  *   NJS_DECLINED         property was not found in object,
  *     if pq->lhq.value != NULL it contains retval of type
@@ -515,6 +521,7 @@ njs_int_t
 njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *value,
     njs_value_t *key)
 {
+    double          num;
     uint32_t        index;
     njs_int_t       ret;
     njs_object_t    *obj;
@@ -541,10 +548,9 @@ njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *value,
 
     case NJS_STRING:
         if (njs_fast_path(!njs_is_null_or_undefined_or_boolean(key))) {
-            index = njs_key_to_index(key);
-
-            if (njs_fast_path(index < NJS_STRING_MAX_LENGTH)) {
-                return njs_string_property_query(vm, pq, value, index);
+            num = njs_key_to_index(key);
+            if (njs_fast_path(njs_number_is_integer_index(num, key))) {
+                return njs_string_property_query(vm, pq, value, num);
             }
         }
 
@@ -554,6 +560,7 @@ njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *value,
     case NJS_OBJECT:
     case NJS_ARRAY:
     case NJS_ARRAY_BUFFER:
+    case NJS_TYPED_ARRAY:
     case NJS_OBJECT_BOOLEAN:
     case NJS_OBJECT_NUMBER:
     case NJS_OBJECT_SYMBOL:
@@ -624,12 +631,13 @@ static njs_int_t
 njs_object_property_query(njs_vm_t *vm, njs_property_query_t *pq,
     njs_object_t *object, const njs_value_t *key)
 {
-    uint32_t            index;
+    double              num;
     njs_int_t           ret;
     njs_bool_t          own;
     njs_array_t         *array;
     njs_object_t        *proto;
     njs_object_prop_t   *prop;
+    njs_typed_array_t   *tarray;
     njs_object_value_t  *ov;
 
     pq->lhq.proto = &njs_object_hash_proto;
@@ -645,19 +653,32 @@ njs_object_property_query(njs_vm_t *vm, njs_property_query_t *pq,
         if (!njs_is_null_or_undefined_or_boolean(key)) {
             switch (proto->type) {
             case NJS_ARRAY:
-                index = njs_key_to_index(key);
-                if (njs_fast_path(index < NJS_ARRAY_MAX_INDEX)) {
+                num = njs_key_to_index(key);
+                if (njs_fast_path(njs_number_is_integer_index(num, key))) {
                     array = (njs_array_t *) proto;
-                    return njs_array_property_query(vm, pq, array, index);
+                    return njs_array_property_query(vm, pq, array, num);
+                }
+
+                break;
+
+            case NJS_TYPED_ARRAY:
+                num = njs_key_to_index(key);
+                if (njs_fast_path(njs_number_is_integer_index(num, key))) {
+                    tarray = (njs_typed_array_t *) proto;
+                    return njs_typed_array_property_query(vm, pq, tarray, num);
+                }
+
+                if (!isnan(num)) {
+                    return NJS_DECLINED;
                 }
 
                 break;
 
             case NJS_OBJECT_STRING:
-                index = njs_key_to_index(key);
-                if (njs_fast_path(index < NJS_STRING_MAX_LENGTH)) {
+                num = njs_key_to_index(key);
+                if (njs_fast_path(njs_number_is_integer_index(num, key))) {
                     ov = (njs_object_value_t *) proto;
-                    ret = njs_string_property_query(vm, pq, &ov->value, index);
+                    ret = njs_string_property_query(vm, pq, &ov->value, num);
 
                     if (njs_fast_path(ret != NJS_DECLINED)) {
                         return ret;
@@ -774,6 +795,38 @@ njs_array_property_query(njs_vm_t *vm, njs_property_query_t *pq,
 }
 
 
+static njs_int_t
+njs_typed_array_property_query(njs_vm_t *vm, njs_property_query_t *pq,
+    njs_typed_array_t *array, uint32_t index)
+{
+    njs_object_prop_t  *prop;
+
+    if (index >= njs_typed_array_length(array)) {
+        return NJS_DECLINED;
+    }
+
+    prop = &pq->scratch;
+
+    if (pq->query == NJS_PROPERTY_QUERY_GET) {
+        njs_set_number(&prop->value, njs_typed_array_get(array, index));
+        prop->type = NJS_PROPERTY;
+
+    } else {
+        prop->value.data.u.typed_array = array;
+        prop->value.data.magic32 = index;
+        prop->type = NJS_PROPERTY_TYPED_ARRAY_REF;
+    }
+
+    prop->writable = 1;
+    prop->enumerable = 1;
+    prop->configurable = 0;
+
+    pq->lhq.value = prop;
+
+    return NJS_OK;
+}
+
+
 static njs_int_t
 njs_string_property_query(njs_vm_t *vm, njs_property_query_t *pq,
     njs_value_t *object, uint32_t index)
@@ -1098,6 +1151,12 @@ njs_value_property_set(njs_vm_t *vm, njs_value_t *value, njs_value_t *key,
                 *prop->value.data.u.value = *setval;
                 return NJS_OK;
 
+            case NJS_PROPERTY_TYPED_ARRAY_REF:
+                return njs_typed_array_set_value(vm,
+                                                 njs_typed_array(&prop->value),
+                                                 prop->value.data.magic32,
+                                                 setval);
+
             default:
                 njs_internal_error(vm, "unexpected property type \"%s\" "
                                    "while setting",
@@ -1124,6 +1183,15 @@ njs_value_property_set(njs_vm_t *vm, njs_value_t *value, njs_value_t *key,
             goto found;
         }
 
+        if (njs_slow_path(pq.own && njs_is_typed_array(value)
+                          && njs_is_string(key)))
+        {
+            /* Integer-Indexed Exotic Objects [[DefineOwnProperty]]. */
+            if (!isnan(njs_string_to_index(key))) {
+                return NJS_OK;
+            }
+        }
+
         break;
 
     case NJS_ERROR:
index 28ed7640059e10d53fd71529aa595ce0c5597a22..daf08534aa12d4be60d9b838c08c176351bf9950 100644 (file)
  */
 
 typedef enum {
-    NJS_NULL                  = 0x00,
-    NJS_UNDEFINED             = 0x01,
+    NJS_NULL,
+    NJS_UNDEFINED,
 
     /* The order of the above type is used in njs_is_null_or_undefined(). */
 
-    NJS_BOOLEAN               = 0x02,
+    NJS_BOOLEAN,
     /*
      * The order of the above type is used in
      * njs_is_null_or_undefined_or_boolean().
      */
-    NJS_NUMBER                = 0x03,
+    NJS_NUMBER,
     /*
      * The order of the above type is used in njs_is_numeric().
      * Booleans, null and void values can be used in mathematical operations:
@@ -32,16 +32,16 @@ typedef enum {
      *   a numeric value of the null and false values is zero,
      *   a numeric value of the void value is NaN.
      */
-    NJS_SYMBOL                = 0x04,
+    NJS_SYMBOL,
 
-    NJS_STRING                = 0x05,
+    NJS_STRING,
 
     /* The order of the above type is used in njs_is_primitive(). */
 
-    NJS_DATA                  = 0x06,
+    NJS_DATA,
 
     /* The type is external code. */
-    NJS_EXTERNAL              = 0x07,
+    NJS_EXTERNAL,
 
     /*
      * The invalid value type is used:
@@ -49,7 +49,7 @@ typedef enum {
      *   to detect non-declared explicitly or implicitly variables,
      *   for native property getters.
      */
-    NJS_INVALID               = 0x08,
+    NJS_INVALID,
 
     /*
      * The object types are >= NJS_OBJECT, this is used in njs_is_object().
@@ -59,17 +59,22 @@ typedef enum {
      * is used in vm->prototypes and vm->constructors arrays.
      */
     NJS_OBJECT                = 0x10,
-    NJS_ARRAY                 = 0x11,
-    NJS_OBJECT_BOOLEAN        = 0x12,
-    NJS_OBJECT_NUMBER         = 0x13,
-    NJS_OBJECT_SYMBOL         = 0x14,
-    NJS_OBJECT_STRING         = 0x15,
-    NJS_FUNCTION              = 0x16,
-    NJS_REGEXP                = 0x17,
-    NJS_DATE                  = 0x18,
-    NJS_PROMISE               = 0x19,
-    NJS_OBJECT_VALUE          = 0x1A,
-    NJS_ARRAY_BUFFER          = 0x1B,
+    NJS_ARRAY,
+#define NJS_OBJECT_WRAPPER_MIN  (NJS_OBJECT_BOOLEAN)
+    NJS_OBJECT_BOOLEAN,
+    NJS_OBJECT_NUMBER,
+    NJS_OBJECT_SYMBOL,
+    NJS_OBJECT_STRING,
+#define NJS_OBJECT_WRAPPER_MAX  (NJS_OBJECT_STRING + 1)
+#define NJS_OBJECT_SPECIAL_MIN  (NJS_FUNCTION)
+    NJS_FUNCTION,
+    NJS_REGEXP,
+    NJS_DATE,
+    NJS_TYPED_ARRAY,
+#define NJS_OBJECT_SPECIAL_MAX  (NJS_TYPED_ARRAY + 1)
+    NJS_PROMISE,
+    NJS_OBJECT_VALUE,
+    NJS_ARRAY_BUFFER,
     NJS_VALUE_TYPE_MAX
 } njs_value_type_t;
 
@@ -82,6 +87,7 @@ typedef struct njs_function_lambda_s  njs_function_lambda_t;
 typedef struct njs_regexp_pattern_s   njs_regexp_pattern_t;
 typedef struct njs_array_s            njs_array_t;
 typedef struct njs_array_buffer_s     njs_array_buffer_t;
+typedef struct njs_typed_array_s      njs_typed_array_t;
 typedef struct njs_regexp_s           njs_regexp_t;
 typedef struct njs_date_s             njs_date_t;
 typedef struct njs_object_value_s     njs_promise_t;
@@ -143,6 +149,7 @@ union njs_value_s {
             njs_object_t              *object;
             njs_array_t               *array;
             njs_array_buffer_t        *array_buffer;
+            njs_typed_array_t         *typed_array;
             njs_object_value_t        *object_value;
             njs_function_t            *function;
             njs_function_lambda_t     *lambda;
@@ -249,6 +256,15 @@ struct njs_array_buffer_s {
 };
 
 
+struct njs_typed_array_s {
+    njs_object_t                      object;
+    njs_array_buffer_t                *buffer;
+    size_t                            offset; // byte_offset / element_size
+    size_t                            byte_length;
+    uint8_t                           type;
+};
+
+
 typedef struct {
     union {
         uint32_t                      count;
@@ -345,6 +361,7 @@ typedef enum {
 typedef enum {
     NJS_PROPERTY = 0,
     NJS_PROPERTY_REF,
+    NJS_PROPERTY_TYPED_ARRAY_REF,
     NJS_PROPERTY_HANDLER,
     NJS_WHITEOUT,
 } njs_object_prop_type_t;
@@ -634,6 +651,10 @@ typedef struct {
     ((value)->type == NJS_ARRAY_BUFFER)
 
 
+#define njs_is_typed_array(value)                                             \
+    ((value)->type == NJS_TYPED_ARRAY)
+
+
 #define njs_is_function(value)                                                \
     ((value)->type == NJS_FUNCTION)
 
@@ -710,6 +731,14 @@ typedef struct {
     ((value)->data.u.array_buffer)
 
 
+#define njs_typed_array(value)                                                \
+    ((value)->data.u.typed_array)
+
+
+#define njs_typed_array_buffer(value)                                         \
+    ((value)->buffer)
+
+
 #define njs_array_start(value)                                                \
     ((value)->data.u.array->start)
 
@@ -870,6 +899,15 @@ njs_set_array_buffer(njs_value_t *value, njs_array_buffer_t *array)
 }
 
 
+njs_inline void
+njs_set_typed_array(njs_value_t *value, njs_typed_array_t *array)
+{
+    value->data.u.typed_array = array;
+    value->type = NJS_TYPED_ARRAY;
+    value->data.truth = 1;
+}
+
+
 njs_inline void
 njs_set_function(njs_value_t *value, njs_function_t *function)
 {
index 581a29c6542a2c2bc02aa33f551d5c5ad9f0c15e..d684578119d859fa434ddeb94a4b9d192ca85892 100644 (file)
@@ -97,7 +97,7 @@ njs_value_to_length(njs_vm_t *vm, njs_value_t *value, uint32_t *dst)
 
 
 njs_inline njs_int_t
-njs_value_to_index(njs_vm_t *vm, njs_value_t *value, uint32_t *dst)
+njs_value_to_index(njs_vm_t *vm, njs_value_t *value, uint64_t *dst)
 {
     int64_t    integer_index;
     njs_int_t  ret;
@@ -116,7 +116,7 @@ njs_value_to_index(njs_vm_t *vm, njs_value_t *value, uint32_t *dst)
             return NJS_ERROR;
         }
 
-        *dst = (uint32_t) integer_index;
+        *dst = integer_index;
     }
 
     return NJS_OK;
index 5167ff805f5a2818dac1efa26422150719002e96..623a2176baba3f3da3b6831186fdf17c21766635 100644 (file)
@@ -82,7 +82,6 @@ typedef enum {
 typedef enum {
     NJS_OBJ_TYPE_OBJECT = 0,
     NJS_OBJ_TYPE_ARRAY,
-    NJS_OBJ_TYPE_ARRAY_BUFFER,
     NJS_OBJ_TYPE_BOOLEAN,
     NJS_OBJ_TYPE_NUMBER,
     NJS_OBJ_TYPE_SYMBOL,
@@ -91,10 +90,29 @@ typedef enum {
     NJS_OBJ_TYPE_REGEXP,
     NJS_OBJ_TYPE_DATE,
     NJS_OBJ_TYPE_PROMISE,
+    NJS_OBJ_TYPE_ARRAY_BUFFER,
+
     NJS_OBJ_TYPE_CRYPTO_HASH,
 #define NJS_OBJ_TYPE_HIDDEN_MIN    (NJS_OBJ_TYPE_CRYPTO_HASH)
     NJS_OBJ_TYPE_CRYPTO_HMAC,
-#define NJS_OBJ_TYPE_HIDDEN_MAX    (NJS_OBJ_TYPE_CRYPTO_HMAC + 1)
+    NJS_OBJ_TYPE_TYPED_ARRAY,
+#define NJS_OBJ_TYPE_HIDDEN_MAX    (NJS_OBJ_TYPE_TYPED_ARRAY + 1)
+#define NJS_OBJ_TYPE_NORMAL_MAX    (NJS_OBJ_TYPE_HIDDEN_MAX)
+
+#define NJS_OBJ_TYPE_TYPED_ARRAY_MIN    (NJS_OBJ_TYPE_UINT8_ARRAY)
+#define njs_typed_array_index(type)     (type - NJS_OBJ_TYPE_TYPED_ARRAY_MIN)
+    NJS_OBJ_TYPE_UINT8_ARRAY,
+    NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY,
+    NJS_OBJ_TYPE_INT8_ARRAY,
+    NJS_OBJ_TYPE_UINT16_ARRAY,
+    NJS_OBJ_TYPE_INT16_ARRAY,
+    NJS_OBJ_TYPE_UINT32_ARRAY,
+    NJS_OBJ_TYPE_INT32_ARRAY,
+    NJS_OBJ_TYPE_FLOAT32_ARRAY,
+    NJS_OBJ_TYPE_FLOAT64_ARRAY,
+#define NJS_OBJ_TYPE_TYPED_ARRAY_MAX    (NJS_OBJ_TYPE_FLOAT64_ARRAY + 1)
+#define NJS_OBJ_TYPE_TYPED_ARRAY_SIZE   (NJS_OBJ_TYPE_TYPED_ARRAY_MAX         \
+                                         - NJS_OBJ_TYPE_TYPED_ARRAY_MIN)
     NJS_OBJ_TYPE_ERROR,
     NJS_OBJ_TYPE_EVAL_ERROR,
     NJS_OBJ_TYPE_INTERNAL_ERROR,
@@ -104,7 +122,7 @@ typedef enum {
     NJS_OBJ_TYPE_TYPE_ERROR,
     NJS_OBJ_TYPE_URI_ERROR,
     NJS_OBJ_TYPE_MEMORY_ERROR,
-#define NJS_OBJ_TYPE_MAX      (NJS_OBJ_TYPE_MEMORY_ERROR + 1)
+    NJS_OBJ_TYPE_MAX,
 } njs_object_type_t;
 
 
index b5de9966fab1e4ce871b799ff5f6bb3e97ca3ca6..ff0e8a27270d0a669b957f7091214f36c88f7477 100644 (file)
@@ -1141,22 +1141,24 @@ static njs_jump_off_t
 njs_vmcode_property_init(njs_vm_t *vm, njs_value_t *value, njs_value_t *key,
     njs_value_t *init)
 {
+    double              num;
     uint32_t            index, size;
+    njs_int_t           ret;
     njs_array_t         *array;
     njs_value_t         *val, name;
-    njs_jump_off_t      ret;
     njs_object_prop_t   *prop;
     njs_lvlhsh_query_t  lhq;
 
     switch (value->type) {
     case NJS_ARRAY:
-        index = njs_key_to_index(key);
-        if (njs_slow_path(index == NJS_ARRAY_INVALID_INDEX)) {
+        num = njs_key_to_index(key);
+        if (njs_slow_path(!njs_number_is_integer_index(num, key))) {
             njs_internal_error(vm,
                                "invalid index while property initialization");
             return NJS_ERROR;
         }
 
+        index = (uint32_t) num;
         array = value->data.u.array;
 
         if (index >= array->length) {
@@ -1420,6 +1422,8 @@ njs_vmcode_typeof(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
         &njs_string_object,
         &njs_string_object,
         &njs_string_object,
+        &njs_string_object,
+        &njs_string_object,
     };
 
     vm->retval = *types[value->type];
index b85f66f6b8f5f08070d471ca8142669f4b27c408..d04fa9895aca837b5f87a9ae3d30e14a09778621 100644 (file)
@@ -149,9 +149,20 @@ main(int argc, char **argv)
     static njs_str_t while_loop = njs_str(
         "var i = 0; while (i < 100000000) { i++ }; i");
 
+    static njs_str_t  typed_array_10M = njs_str(
+        "var arr = new Uint8Array(10000000); var count = 0, length = arr.length;"
+        "arr.fill(2);"
+        "for (var i = 0; i < length; i++) { count += arr[i]; } count");
+
+    static njs_str_t  array_10M = njs_str(
+        "var arr = new Array(10000000); var count = 0, length = arr.length;"
+        "arr.fill(2);"
+        "for (var i = 0; i < length; i++) { count += arr[i]; } count");
+
     static njs_str_t  fibo_result = njs_str("3524578");
     static njs_str_t  json_result = njs_str("123");
     static njs_str_t  loop_result = njs_str("100000000");
+    static njs_str_t  sum_result  = njs_str("20000000");
 
 
     if (argc > 1) {
@@ -188,9 +199,18 @@ main(int argc, char **argv)
         case 'u':
             return njs_unit_test_benchmark(&fibo_utf8, &fibo_result,
                                            "fibobench utf8 strings", 1);
+
+        case 't':
+            return njs_unit_test_benchmark(&typed_array_10M, &sum_result,
+                                           "typed_array_10M", 1);
+
+        case 'A':
+            return njs_unit_test_benchmark(&array_10M, &sum_result,
+                                           "array_10M", 1);
         }
     }
 
     njs_printf("unknown agrument\n");
+
     return EXIT_FAILURE;
 }
index 80a92a4e1a1461919afffb8a2253f73c4a4b41a5..14264d20af2582092c9b2dd5b6174005a7371661 100644 (file)
@@ -348,7 +348,7 @@ static njs_unit_test_t  njs_test[] =
                  "(1).X()"),
       njs_str("123") },
 
-    /* Indexes. */
+    /* Indices. */
 
     { njs_str("var a = []; a[-1] = 2; a[-1] == a['-1']"),
       njs_str("true") },
@@ -359,6 +359,19 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("var a = []; a[NaN] = 2; a[NaN] == a['NaN']"),
       njs_str("true") },
 
+#define NJS_NOT_CANONICAL_INDICES "['+0', '-0', '1.', '0.', '0.0', '4294967295', " \
+                                  " '4294967296', '-1', '1.1', '9223372036854775808']"
+
+    { njs_str("var a = [1,2]; "
+              NJS_NOT_CANONICAL_INDICES
+               ".every(v=>(a[v] === undefined))"),
+      njs_str("true") },
+
+    { njs_str("var a = [1,2]; "
+              NJS_NOT_CANONICAL_INDICES
+               ".every(v=>{a[v] = 'a'; return a[v] === 'a'})"),
+      njs_str("true") },
+
     /* Number.toString(radix) method. */
 
     { njs_str("0..toString(2)"),
@@ -4825,6 +4838,512 @@ static njs_unit_test_t  njs_test[] =
               ".map(pr=>buffer.slice(pr[0], pr[1]).byteLength)"),
       njs_str("8,0,0,15,0,0") },
 
+    { njs_str("[false,NaN,1]"
+              ".map(v=>(new Uint8Array(v)).length)"),
+      njs_str("0,0,1") },
+
+#define NJS_TYPED_ARRAY_LIST "[Uint8Array,Uint8ClampedArray,Int8Array,"     \
+                             " Uint16Array,Int16Array,Uint32Array,"         \
+                             " Int32Array, Float32Array,Float64Array]"
+
+#define NJS_INT_TYPED_ARRAY_LIST   "[Uint8Array,Uint8ClampedArray,Int8Array," \
+                                   " Uint16Array,Int16Array,Uint32Array,"     \
+                                   " Int32Array]"
+
+#define NJS_FLOAT_TYPED_ARRAY_LIST "[Float32Array,Float64Array]"
+
+    { njs_str("var TypedArray = Object.getPrototypeOf(Uint8Array);"
+              "[TypedArray.name, TypedArray.length]"),
+      njs_str("TypedArray,0") },
+
+    { njs_str("Object.getPrototypeOf(Uint8Array)()"),
+      njs_str("TypeError: Abstract class TypedArray not directly constructable") },
+
+    { njs_str("var TypedArray = Object.getPrototypeOf(Uint8Array);"
+              NJS_TYPED_ARRAY_LIST
+              ".every(v=>Object.getPrototypeOf(v) === TypedArray)"),
+      njs_str("true") },
+
+    { njs_str("var TypedArray = Object.getPrototypeOf(Uint8Array);"
+              NJS_TYPED_ARRAY_LIST
+              ".every(v=>Object.getPrototypeOf(v.prototype) === TypedArray.prototype)"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v.prototype.constructor(0); "
+              "           return njs.dump(a) === `${v.name} []`})"),
+      njs_str("true") },
+
+    { njs_str("var global = this;"
+              NJS_TYPED_ARRAY_LIST
+              ".every(v=>ArrayBuffer.isView(new global[v.name]))"),
+      njs_str("true") },
+
+    { njs_str("var global = this;"
+              NJS_TYPED_ARRAY_LIST
+              ".every(v=>global[v.name][Symbol.species].name === v.name)"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v({length:2, '0':1, '1':2}); "
+              "           return a[0] == 1 && a[1] == 2 && a.length == 2})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{try{ new v({length:Math.pow(2,53)}) } "
+              "           catch(e) {return e.name == 'InternalError' || e.name == 'RangeError'}})"),
+      njs_str("true") },
+
+    { njs_str(NJS_FLOAT_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v({length:5, 0: null, 2:42, 3:'7', 4:NaN, 5:Symbol('1')}); "
+              "           return njs.dump(a) === `${v.name} [0,NaN,42,7,NaN]`})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var o = {length:2, '0':1}; Object.defineProperty(o, '1', {get(){throw 'Oops'}});"
+              "           try {new v(o)} catch (e) { return e == 'Oops'}})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v(3); Object.defineProperty(a, '1', {value:1});"
+              "           return njs.dump(a) === `${v.name} [0,1,0]`})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([1,1,1]); Object.defineProperty(a, '1', {});"
+              "           return njs.dump(a) === `${v.name} [1,1,1]`})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{try {var a = new v([1,1]); Object.defineProperty(a, '1', {configurable:true})} "
+              "           catch (e) { return e.message == 'Cannot redefine property: \"1\"'}})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{try {var a = new v([1,1]); Object.defineProperty(a, '1', {enumerable:false})} "
+              "           catch (e) { return e.message == 'Cannot redefine property: \"1\"'}})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{try {var a = new v([1,1]); Object.defineProperty(a, '1', {writable:false})} "
+              "           catch (e) { return e.message == 'Cannot redefine property: \"1\"'}})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{try {var a = new v([1,1]); Object.defineProperty(a, '1', {get(){return 22}})} "
+              "           catch (e) { return e.message == 'Cannot redefine property: \"1\"'}})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v(3);"
+              "           return [a.hasOwnProperty('1'), a.hasOwnProperty('4')].toString() === 'true,false'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var descs = Object.getOwnPropertyNames(v).sort().toString(); "
+              "           return descs === 'BYTES_PER_ELEMENT,length,name,prototype'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".map(v=>{var a = new ArrayBuffer(8); return (new v(a).length)})"),
+      njs_str("8,8,8,4,4,2,2,2,1") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".map(v=>{var a = new v(1); a[0]--; return a[0]})"),
+      njs_str("255,0,-1,65535,-1,4294967295,-1,-1,-1") },
+
+    { njs_str(NJS_NOT_CANONICAL_INDICES
+              ".every(v=>{var a = new Uint8Array([1,2]); return a[v] === undefined})"),
+      njs_str("true") },
+
+    { njs_str(NJS_NOT_CANONICAL_INDICES
+              ".map(v=>{var a = new Uint8Array([1,2]); a[v] = 'a'; return a[v]})"),
+      njs_str("a,,a,a,a,,,,,a") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{v.prototype[10] = 'foo'; return (new v(16))[10] === 0})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{v.prototype[20] = 'foo'; return (new v(16))[20] === undefined})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{v.prototype.foo = 'bar'; return (new v(16)).foo === 'bar'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{v.prototype[-1] = 'foo'; return (new v(8))[-1] === undefined})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".map(v=>v.BYTES_PER_ELEMENT)"),
+      njs_str("1,1,1,2,2,4,4,4,8") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>v.length === 3)"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v(1); return --a[0] == -1})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v(4); a.fill(42); return (a[0] === 42 && a.length == 4)})"),
+      njs_str("true") },
+
+    { njs_str(NJS_INT_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v(1); a.fill({}); return a[0] === 0})"),
+      njs_str("true") },
+
+    { njs_str(NJS_FLOAT_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v(1); a.fill({}); return isNaN(a[0])})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([1,2,3]); a.fill({valueOf(){return 12}}, 1,2); "
+              "       return (a[0] === 1 && a[1] === 12 && a[2] === 3 && a.length == 3)})"),
+      njs_str("true") },
+
+    { njs_str(NJS_INT_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([1,2,3]); a.fill('qq', 1, 2); "
+              "       return (a[0] === 1 && a[1] === 0 && a[2] === 3 && a.length == 3)})"),
+      njs_str("true") },
+
+    { njs_str(NJS_FLOAT_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([1,2,3]); a.fill('qq', 1, 2); "
+              "       return (a[0] === 1 && isNaN(a[1]) && a[2] === 3 && a.length == 3)})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([1,2,3]); var d = Object.getOwnPropertyDescriptors(a)[1];"
+              "       return (d.value === 2 && d.writable && d.enumerable && !d.configurable)})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([1,2,3]); return Object.keys(a).toString() === '0,1,2'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([1,2,3]); return Object.values(a).toString() === '1,2,3'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([1,2,3]); return Object.entries(a).toString() === '0,1,1,2,2,3'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{try {delete (new v(1))[0]} catch (e) { return e.name == 'TypeError'}})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{return delete (new v(1))[-1]; })"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v(8), b = new v(a.buffer); a[0] = 42; return b[0] === 42})"),
+      njs_str("true") },
+
+    { njs_str(NJS_INT_TYPED_ARRAY_LIST
+              ".map(v=>{var init = new Uint8Array([1,2,3]);"
+              "     try { return new v(init.buffer, 0, 2)} catch (e) {return e.name}})"),
+      njs_str("1,2,1,2,1,2,RangeError,RangeError,RangeError,RangeError") },
+
+    { njs_str(NJS_INT_TYPED_ARRAY_LIST
+              ".map(v=>{var init = new Uint8Array([1,2,3]);"
+              "     try { return new v(init.buffer, 1)} catch (e) {return e.name}})"),
+      njs_str("2,3,2,3,2,3,RangeError,RangeError,RangeError,RangeError") },
+
+    { njs_str(NJS_INT_TYPED_ARRAY_LIST
+              ".map(v=>{var init = new Uint32Array([0xaabbccdd]);"
+              "     try { return new v(init.buffer, 0, 2)} catch (e) {return e.name}})"),
+      njs_str("221,204,221,204,-35,-52,52445,43707,-13091,-21829,RangeError,RangeError") },
+
+    { njs_str(NJS_INT_TYPED_ARRAY_LIST
+              ".map(v=>{var init = new Uint32Array([0xaabbccdd]);"
+              "     try { return new v(init.buffer, 1, 2)} catch (e) {return e.name}})"),
+      njs_str("204,187,204,187,-52,-69,RangeError,RangeError,RangeError,RangeError") },
+
+    { njs_str(NJS_INT_TYPED_ARRAY_LIST
+              ".map(v=>{var init = new Uint32Array([0xaabbccdd,0xdeadbeef]);"
+              "     try { return new v(init.buffer, 0, 2)} catch (e) {return e.name}})"),
+      njs_str("221,204,221,204,-35,-52,52445,43707,-13091,-21829,2864434397,3735928559,-1430532899,-559038737") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var buffer1 = new ArrayBuffer(8 * v.BYTES_PER_ELEMENT);"
+              "           var ta1 = new v(buffer1);"
+              "           var ta2 = new v(ta1.buffer, 4 * v.BYTES_PER_ELEMENT); "
+              "           ta1[5] = 100; ta1[7] = 101;"
+              "           return ta2.toString() === '0,100,0,101'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([42,11]); return a.toString() === '42,11'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v(3); a[1] = 42; return a.toString() === '0,42,0'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v(0); return a.toString() === ''})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([42,11]); return a.join('|') === '42|11'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([42,11]); return a.join('α').length === 5})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{return njs.dump(new v()) === `${v.name} []`})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{return njs.dump(new v([42,11])) === `${v.name} [42,11]`})"),
+      njs_str("true") },
+
+    { njs_str("var a = new Uint8Array(8); var b = new Uint32Array(a.buffer);"
+              "a[0] = 0xff; a[1] = 0xff; a[2] = 0xff; a[3] = 0xff; b[0];"),
+      njs_str("4294967295") },
+
+    { njs_str("[1,300,-100]"
+              ".map(v=>{var a = new Uint8Array(1); a[0] = v; return a[0];})"),
+      njs_str("1,44,156") },
+
+    { njs_str("[1,300,-100]"
+              ".map(v=>{var a = new Uint8ClampedArray(1); a[0] = v; return a[0];})"),
+      njs_str("1,255,0") },
+
+    { njs_str("[1,300,-100]"
+              ".map(v=>{var a = new Uint8ClampedArray(1); a.set([v], 0); return a[0];})"),
+      njs_str("1,255,0") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var init = [1,2,3,4]; var a = new v(4);"
+              "           a.set(init); return a.toString() === '1,2,3,4'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var init = {length:4, 0:1,1:2,2:3,3:4}; var a = new v(4);"
+              "           a.set(init); return a.toString() === '1,2,3,4'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var init = new v([1,2,3,4]); var a = new v(init);"
+              "           return a.toString() === '1,2,3,4'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var init = new v([1,2,3,4]); var a = new v(4);"
+              "           a.set(init); return a.toString() === '1,2,3,4'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var init = {length:2, 0:1,1:2}; var a = new v(4);"
+              "           a.set(init,2); return a.toString() === '0,0,1,2'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_INT_TYPED_ARRAY_LIST
+              ".every(v=>{var init = {length:4, 0:1,1:2,3:4}; var a = new v(4);"
+              "           a.set(init); return a.toString() === '1,2,0,4'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_FLOAT_TYPED_ARRAY_LIST
+              ".every(v=>{var init = {length:4, 0:1,1:2,3:4}; var a = new v(4);"
+              "           a.set(init); return a.toString() === '1,2,NaN,4'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var init = [1,2,3]; var a = new v(4);"
+              "           a.set(init); return a.toString() === '1,2,3,0'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var init = new v([1,2,3]); var a = new v(4);"
+              "           a.set(init); return a.toString() === '1,2,3,0'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var init = [1,2,3,4]; var a = new v(4);"
+              "           a.set(init, 0); return a.toString() === '1,2,3,4'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var init = [1,2]; var a = new v(4);"
+              "           a.set(init, 2); return a.toString() === '0,0,1,2'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var init = new v([1,2]); var a = new v(4);"
+              "           a.set(init,2); return a.toString() === '0,0,1,2'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var init = [1,2,3,4]; var a = new v(4);"
+              "           try {a.set(init,2)} catch (e) {return e.name == 'RangeError'};})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var init = [1,2,3,4]; var a = new v(4);"
+              "           try {a.set(init,Infinity)} catch (e) {return e.name == 'RangeError'};})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var init = [1,2,3,4,5]; var a = new v(4);"
+              "           try {a.set(init)} catch (e) {return e.name == 'RangeError'};})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var init = {length:5}; var a = new v(4);"
+              "           try {a.set(init)} catch (e) {return e.name == 'RangeError'};})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var init = {length:3}; var a = new v(4);"
+              "           try {a.set(init,2)} catch (e) {return e.name == 'RangeError'};})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var init = {length:3}; var a = new v(4);"
+              "           try {a.set(init,Infinity)} catch (e) {return e.name == 'RangeError'};})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var init = new v([1,2,3,4,5]); var a = new v(4);"
+              "           try {a.set(init)} catch (e) {return e.name == 'RangeError'};})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var init = new v([1,2,3]); var a = new v(4);"
+              "           try {a.set(init,2)} catch (e) {return e.name == 'RangeError'};})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var init = new v([1,2,3]); var a = new v(4);"
+              "           try {a.set(init,Infinity)} catch (e) {return e.name == 'RangeError'};})"),
+      njs_str("true") },
+
+    { njs_str("[-1,-1.00001,-Infinity]"
+              ".every(v=>{ try {(new Uint8Array(10)).set([], v)} catch (ee) {return ee.name === 'RangeError'}})"),
+      njs_str("true") },
+
+    { njs_str("[0.1,2.5,{},{'1': '10'},[1000]]"
+              ".map(v=>{var a = new Uint8Array(1); a.set([v], 0); return a[0]})"),
+      njs_str("0,2,0,0,232") },
+
+    { njs_str("[1.0, -1234.0]"
+              ".map(v=>{var a = new Float32Array(1); a[0] = v; var b = new Uint8Array(a.buffer);"
+              "         return (b[0] << 24 | b[1] << 16| b[2] <<8 | b[3]).toString(16).padStart(8, '0');})"),
+      njs_str("0000803f,00409ac4") },
+
+    { njs_str("var a = new ArrayBuffer(0); a.slice(0, 0).byteLength"),
+      njs_str("0") },
+
+    { njs_str("var a = new ArrayBuffer(10); a.slice(1).byteLength"),
+      njs_str("9") },
+
+    { njs_str("var a = new ArrayBuffer(10); a.slice(1,2).byteLength"),
+      njs_str("1") },
+
+    { njs_str("var a = new ArrayBuffer(10); a.slice(0,-1).byteLength"),
+      njs_str("9") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".map(v=>{var buffer = new ArrayBuffer(8); var view = new v(buffer);"
+              "         view[0] = 511; return new Uint8Array(buffer.slice(0,4))})"),
+      njs_str("255,0,0,0,255,0,0,0,255,0,0,0,255,1,0,0,255,1,0,0,255,1,0,0,255,1,0,0,0,128,255,67,0,0,0,0") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".map(v=>{var buffer = new ArrayBuffer(8); var view = new v(buffer);"
+              "         view[view.length - 1] = 511; return new Uint8Array(buffer.slice(4))})"),
+      njs_str("0,0,0,255,0,0,0,255,0,0,0,255,0,0,255,1,0,0,255,1,255,1,0,0,255,1,0,0,0,128,255,67,0,240,127,64") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".map(v=>{var buffer = new ArrayBuffer(8); var view = new v(buffer);"
+              "         view[0] = 511; return new Uint8Array(buffer.slice(0,-4))})"),
+      njs_str("255,0,0,0,255,0,0,0,255,0,0,0,255,1,0,0,255,1,0,0,255,1,0,0,255,1,0,0,0,128,255,67,0,0,0,0") },
+
+    { njs_str("var a = new Uint8Array(10); var b = a.slice(1); b.length"),
+      njs_str("9") },
+
+    { njs_str("var a = new Uint8Array(10); var b = a.slice(0,9); b.length"),
+      njs_str("9") },
+
+    { njs_str("var a = new Uint8Array(10); var b = a.slice(9,10); b.length"),
+      njs_str("1") },
+
+    { njs_str("var a = new Uint8Array(10); var b = a.slice(); b.length"),
+      njs_str("10") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([42]); "
+              "           var r = a.slice();"
+              "           return  njs.dump(r) === `${v.name} [42]`;})"),
+      njs_str("true") },
+
+    { njs_str(NJS_INT_TYPED_ARRAY_LIST
+              ".map(v=>{var init = new Uint8Array([1,2,3,4,5,6,7,8]); var view = new v(init.buffer);"
+              "         return view.slice(0,2)})"),
+      njs_str("1,2,1,2,1,2,513,1027,513,1027,67305985,134678021,67305985,134678021") },
+
+    { njs_str(NJS_INT_TYPED_ARRAY_LIST
+              ".map(v=>{var init = new Uint8Array([1,2,3,4,5,6,7,8]); var view = new v(init.buffer);"
+              "         return view.slice(0,-2)})"),
+      njs_str("1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,513,1027,513,1027,,") },
+
+    { njs_str("var other = new Uint8Array([0xff,0xff,0xff,0xff]);"
+              NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([42]); "
+              "           a.constructor = {[Symbol.species]: function () {return other;}}; "
+              "           var r = a.slice(0,0);"
+              "           return r == other && r.length == 4 && r[0] == 0xff;})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([1,2,3]); "
+              "           a.constructor = {[Symbol.species]: function () {return new v([0xff,0xee]);}}; "
+              "           try {a.slice(0)} catch(e) {return e.name == 'TypeError'}})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([1,2,3]); "
+              "           a.constructor = {[Symbol.species]: function () {return new Date();}}; "
+              "           try {a.slice(0)} catch(e) {return e.name == 'TypeError'}})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([1,2,3,4]); a.copyWithin(2); "
+              "           return a.toString() === '1,2,1,2'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([1,2,3,4]); a.copyWithin(2,1); "
+              "           return a.toString() === '1,2,2,3'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([1,2,3,4]); a.copyWithin(2,1,2); "
+              "           return a.toString() === '1,2,2,4'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([1,2,3,4]); a.copyWithin(-1,1,2); "
+              "           return a.toString() === '1,2,3,2'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([1,2,3,4]); a.copyWithin(-1,-4,2); "
+              "           return a.toString() === '1,2,3,1'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([1,2,3,4]); a.copyWithin(-1,-2); "
+              "           return a.toString() === '1,2,3,3'})"),
+      njs_str("true") },
+
 #if NJS_HAVE_LARGE_STACK
     { njs_str("var o = Object({length: 3});"
                  "Object.defineProperty(o, '0', {set: function(v){this[0] = 2 * v}});"
@@ -17079,6 +17598,88 @@ done:
 }
 
 
+static njs_int_t
+njs_string_to_index_test(njs_vm_t *vm, njs_opts_t *opts, njs_stat_t *stat)
+{
+    njs_str_t   s, string;
+    njs_int_t   ret;
+    njs_bool_t  success, is_integer_index;
+    njs_uint_t  i;
+
+    static const struct {
+        njs_value_t  value;
+        njs_str_t    expected;
+        njs_bool_t   is_integer_index;
+    } tests[] = {
+        { njs_string(" 1"), njs_str("NaN"), 0 },
+        { njs_string(""), njs_str("NaN"), 0 },
+        { njs_string("+0"), njs_str("NaN"), 0 },
+        { njs_string("-"), njs_str("NaN"), 0 },
+
+        { njs_string("-0"), njs_str("-0"), 0 },
+        { njs_value(NJS_NUMBER, 0, -0.0), njs_str("-0"), 1 },
+
+        { njs_string("-1"), njs_str("-1"), 0 },
+        { njs_string("0"), njs_str("0"), 1 },
+        { njs_string("0."), njs_str("NaN"), 0 },
+        { njs_string("0.0"), njs_str("NaN"), 0 },
+        { njs_string("0x1"), njs_str("NaN"), 0 },
+        { njs_string("1 "), njs_str("NaN"), 0 },
+        { njs_string("1"), njs_str("1"), 1 },
+        { njs_string("1."), njs_str("NaN"), 0 },
+        { njs_string("1.1"), njs_str("1.1"), 0 },
+        { njs_string("100"), njs_str("100"), 1 },
+        { njs_string("1a"), njs_str("NaN"), 0 },
+        { njs_string("1e+19"), njs_str("NaN"), 0 },
+        { njs_string("1e+22"), njs_str("1e+22"), 0 },
+        { njs_string("1e22"), njs_str("NaN"), 0 },
+        { njs_string("4294967296"), njs_str("4294967296"), 0 },
+    };
+
+    for (i = 0; i < njs_nitems(tests); i++) {
+        if (njs_is_string(&tests[i].value)) {
+            njs_set_number(&vm->retval, njs_string_to_index(&tests[i].value));
+
+            ret = njs_vm_retval_dump(vm, &s, 0);
+            if (ret != NJS_OK) {
+                njs_printf("njs_string_to_index_test: "
+                           "njs_vm_retval_dump() failed\n");
+                return NJS_ERROR;
+            }
+
+            success = njs_strstr_eq(&tests[i].expected, &s);
+
+            if (!success) {
+                njs_string_get(&tests[i].value, &string);
+                njs_printf("njs_string_to_index_test(\"%V\"):\n"
+                           "expected: \"%V\"\n     got: \"%V\"\n",
+                           &string, &tests[i].expected, &s);
+
+                stat->failed++;
+                continue;
+            }
+        }
+
+        is_integer_index = njs_number_is_integer_index(njs_number(&vm->retval),
+                                                       &tests[i].value);
+
+        if (tests[i].is_integer_index != is_integer_index) {
+            njs_string_get(&tests[i].value, &string);
+            njs_printf("njs_string_to_index_test2(\"%V\"):\n"
+                       "expected: %b\n     got: %b\n",
+                       &string, tests[i].is_integer_index, is_integer_index);
+
+            stat->failed++;
+            continue;
+        }
+
+        stat->passed++;
+    }
+
+    return NJS_OK;
+}
+
+
 static njs_int_t
 njs_api_test(njs_opts_t *opts, njs_stat_t *stat)
 {
@@ -17100,6 +17701,8 @@ njs_api_test(njs_opts_t *opts, njs_stat_t *stat)
           njs_str("njs_file_dirname_test") },
         { njs_chb_test,
           njs_str("njs_chb_test") },
+        { njs_string_to_index_test,
+          njs_str("njs_string_to_index_test") },
     };
 
     vm = NULL;