[njs] Introduced TypedArray objects.

Dmitry Volyntsev xeioex at nginx.com
Wed Dec 25 15:14:32 UTC 2019


details:   https://hg.nginx.org/njs/rev/1c3c593cc3fd
branches:  
changeset: 1289:1c3c593cc3fd
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Wed Dec 25 15:59:01 2019 +0300
description:
Introduced TypedArray objects.

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.

diffstat:

 auto/sources               |     1 +
 src/njs.h                  |     2 +-
 src/njs_array.c            |     2 +-
 src/njs_array.h            |     2 +
 src/njs_array_buffer.c     |    21 +-
 src/njs_builtin.c          |   139 +++-
 src/njs_json.c             |    84 +-
 src/njs_main.h             |     1 +
 src/njs_number.c           |    15 +-
 src/njs_number.h           |    13 +-
 src/njs_object.c           |    65 +-
 src/njs_object_hash.h      |   139 +++
 src/njs_object_prop.c      |    30 +
 src/njs_string.c           |    83 +-
 src/njs_typed_array.c      |  1751 ++++++++++++++++++++++++++++++++++++++++++++
 src/njs_typed_array.h      |   163 ++++
 src/njs_value.c            |    94 ++-
 src/njs_value.h            |    78 +-
 src/njs_value_conversion.h |     4 +-
 src/njs_vm.h               |    24 +-
 src/njs_vmcode.c           |    10 +-
 src/test/njs_benchmark.c   |    20 +
 src/test/njs_unit_test.c   |   605 +++++++++++++++-
 23 files changed, 3239 insertions(+), 107 deletions(-)

diffs (truncated from 4042 to 1000 lines):

diff -r 99f9008e1b17 -r 1c3c593cc3fd auto/sources
--- a/auto/sources	Tue Dec 17 18:22:50 2019 +0300
+++ b/auto/sources	Wed Dec 25 15:59:01 2019 +0300
@@ -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 \
 "
 
diff -r 99f9008e1b17 -r 1c3c593cc3fd src/njs.h
--- a/src/njs.h	Tue Dec 17 18:22:50 2019 +0300
+++ b/src/njs.h	Wed Dec 25 15:59:01 2019 +0300
@@ -274,7 +274,7 @@ NJS_EXPORT njs_int_t njs_vm_value_string
 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);
 
diff -r 99f9008e1b17 -r 1c3c593cc3fd src/njs_array.c
--- a/src/njs_array.c	Tue Dec 17 18:22:50 2019 +0300
+++ b/src/njs_array.c	Wed Dec 25 15:59:01 2019 +0300
@@ -1089,7 +1089,7 @@ njs_array_prototype_reverse(njs_vm_t *vm
 }
 
 
-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)
 {
diff -r 99f9008e1b17 -r 1c3c593cc3fd src/njs_array.h
--- a/src/njs_array.h	Tue Dec 17 18:22:50 2019 +0300
+++ b/src/njs_array.h	Wed Dec 25 15:59:01 2019 +0300
@@ -21,6 +21,8 @@ njs_int_t njs_array_string_add(njs_vm_t 
     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;
diff -r 99f9008e1b17 -r 1c3c593cc3fd src/njs_array_buffer.c
--- a/src/njs_array_buffer.c	Tue Dec 17 18:22:50 2019 +0300
+++ b/src/njs_array_buffer.c	Wed Dec 25 15:59:01 2019 +0300
@@ -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, 
 }
 
 
+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_arra
         .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,
+    },
 };
 
 
diff -r 99f9008e1b17 -r 1c3c593cc3fd src/njs_builtin.c
--- a/src/njs_builtin.c	Tue Dec 17 18:22:50 2019 +0300
+++ b/src/njs_builtin.c	Wed Dec 25 15:59:01 2019 +0300
@@ -58,7 +58,6 @@ static const njs_object_type_init_t *con
 
     &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 *con
     &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,
+
+    /* 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, 
 {
     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, 
 
     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, 
 
     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;
@@ -1111,6 +1144,96 @@ static const njs_object_prop_t  njs_glob
 
     {
         .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"),
         .value = njs_prop_handler2(njs_top_level_constructor,
                                    NJS_OBJ_TYPE_BOOLEAN, NJS_BOOLEAN_HASH),
diff -r 99f9008e1b17 -r 1c3c593cc3fd src/njs_json.c
--- a/src/njs_json.c	Tue Dec 17 18:22:50 2019 +0300
+++ b/src/njs_json.c	Wed Dec 25 15:59:01 2019 +0300
@@ -1087,12 +1087,15 @@ njs_json_pop_stringify_state(njs_json_st
 }
 
 
-#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
 
 
 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 *str
 
         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_va
 
 
 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_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_
 
     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_
         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_
                 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_
                 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_
                 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_
 
         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_
 
             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_
 
             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;
diff -r 99f9008e1b17 -r 1c3c593cc3fd src/njs_main.h
--- a/src/njs_main.h	Tue Dec 17 18:22:50 2019 +0300
+++ b/src/njs_main.h	Wed Dec 25 15:59:01 2019 +0300
@@ -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>
diff -r 99f9008e1b17 -r 1c3c593cc3fd src/njs_number.c
--- a/src/njs_number.c	Tue Dec 17 18:22:50 2019 +0300
+++ b/src/njs_number.c	Wed Dec 25 15:59:01 2019 +0300
@@ -20,19 +20,16 @@ static njs_int_t njs_number_to_string_ra
     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 *valu
         }
     }
 
-    if ((uint32_t) num == num) {
-        return (uint32_t) num;
-    }
-
-    return NJS_ARRAY_INVALID_INDEX;
+    return NAN;
 }
 
 
diff -r 99f9008e1b17 -r 1c3c593cc3fd src/njs_number.h
--- a/src/njs_number.h	Tue Dec 17 18:22:50 2019 +0300
+++ b/src/njs_number.h	Wed Dec 25 15:59:01 2019 +0300
@@ -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_
     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)
 {
diff -r 99f9008e1b17 -r 1c3c593cc3fd src/njs_object.c
--- a/src/njs_object.c	Tue Dec 17 18:22:50 2019 +0300
+++ b/src/njs_object.c	Wed Dec 25 15:59:01 2019 +0300
@@ -20,6 +20,8 @@ static uint32_t njs_object_own_enumerate
     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 nj
             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 
                                              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;
 
@@ -798,6 +810,54 @@ njs_object_enumerate_array(njs_vm_t *vm,
 
 
 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_obj
                                      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 
         &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);
diff -r 99f9008e1b17 -r 1c3c593cc3fd src/njs_object_hash.h
--- a/src/njs_object_hash.h	Tue Dec 17 18:22:50 2019 +0300
+++ b/src/njs_object_hash.h	Wed Dec 25 15:59:01 2019 +0300
@@ -587,4 +587,143 @@
         '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_ */
diff -r 99f9008e1b17 -r 1c3c593cc3fd src/njs_object_prop.c
--- a/src/njs_object_prop.c	Tue Dec 17 18:22:50 2019 +0300
+++ b/src/njs_object_prop.c	Wed Dec 25 15:59:01 2019 +0300
@@ -164,6 +164,16 @@ njs_object_prop_define(njs_vm_t *vm, njs
             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
 
         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",
diff -r 99f9008e1b17 -r 1c3c593cc3fd src/njs_string.c
--- a/src/njs_string.c	Tue Dec 17 18:22:50 2019 +0300
+++ b/src/njs_string.c	Wed Dec 25 15:59:01 2019 +0300
@@ -4342,32 +4342,85 @@ njs_string_to_number(const njs_value_t *
 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;
-    }
-
-    if (size == 0) {
+        start = value->long_string.data->start;
+    }
+
+    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;
+        }
+    }
+
+    num = njs_strtod(&p, end);
+    if (p != end) {
         return NAN;
     }
 
-    if (*p == '0' && size > 1) {
-        return NAN;
-    }
-
-    end = p + size;
-    num = njs_number_dec_parse(&p, end);
-
-    if (p != end) {
+    num = minus ? -num : num;
+
+    len = njs_dtoa(num, (char *) buf);
+    if (size != len || memcmp(start, buf, size) != 0) {
         return NAN;
     }
 
diff -r 99f9008e1b17 -r 1c3c593cc3fd src/njs_typed_array.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/njs_typed_array.c	Wed Dec 25 15:59:01 2019 +0300
@@ -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;
+


More information about the nginx-devel mailing list