[njs] Introduced memory-efficient arrays.

Dmitry Volyntsev xeioex at nginx.com
Tue Feb 4 17:35:28 UTC 2020


details:   https://hg.nginx.org/njs/rev/ccfa84cea2b3
branches:  
changeset: 1316:ccfa84cea2b3
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Tue Feb 04 20:35:02 2020 +0300
description:
Introduced memory-efficient arrays.

1) If "length" of ordinary array exceeds 32768 it is converted to
to array-like object.

2) Array.prototype.concat() is rewritten according to the spec.
3) Array.prototype.slice() is rewritten according to the spec.

diffstat:

 src/njs_array.c          |  997 +++++++++++++++++++++++++++++++++-------------
 src/njs_array.h          |   20 +-
 src/njs_array_buffer.c   |    2 +
 src/njs_chb.h            |    2 +-
 src/njs_clang.h          |    3 +
 src/njs_crypto.c         |    2 +
 src/njs_date.c           |    2 +
 src/njs_error.c          |    2 +
 src/njs_json.c           |  362 ++++++++++------
 src/njs_module.c         |    2 +
 src/njs_object.c         |   23 +-
 src/njs_object.h         |    2 +
 src/njs_object_prop.c    |   87 +++-
 src/njs_promise.c        |    3 +-
 src/njs_regexp.c         |    2 +
 src/njs_typed_array.c    |    1 -
 src/njs_value.c          |  125 ++++-
 src/njs_value.h          |    8 +
 src/test/njs_benchmark.c |    6 +-
 src/test/njs_unit_test.c |  280 ++++++++++++-
 20 files changed, 1415 insertions(+), 516 deletions(-)

diffs (truncated from 3354 to 1000 lines):

diff -r f5afb325896f -r ccfa84cea2b3 src/njs_array.c
--- a/src/njs_array.c	Tue Feb 04 20:35:00 2020 +0300
+++ b/src/njs_array.c	Tue Feb 04 20:35:02 2020 +0300
@@ -8,7 +8,7 @@
 #include <njs_main.h>
 
 
-#define NJS_ARRAY_LARGE_OBJECT_LENGTH  4096
+#define njs_fast_object(_sz)           ((_sz) <= NJS_ARRAY_FAST_OBJECT_LENGTH)
 
 
 typedef struct {
@@ -29,35 +29,36 @@ typedef njs_int_t (*njs_array_iterator_h
 
 static njs_int_t njs_array_prototype_slice_copy(njs_vm_t *vm,
     njs_value_t *this, int64_t start, int64_t length);
-static njs_value_t *njs_array_copy(njs_value_t *dst, njs_value_t *src);
-static njs_array_t *njs_object_indexes(njs_vm_t *vm, njs_value_t *object);
 
 
 njs_array_t *
 njs_array_alloc(njs_vm_t *vm, uint64_t length, uint32_t spare)
 {
     uint64_t     size;
+    njs_int_t    ret;
     njs_array_t  *array;
+    njs_value_t  value;
 
     if (njs_slow_path(length > UINT32_MAX)) {
         goto overflow;
     }
 
-    size = length + spare;
-
-    if (njs_slow_path(size > NJS_ARRAY_MAX_LENGTH)) {
-        goto memory_error;
-    }
-
     array = njs_mp_alloc(vm->mem_pool, sizeof(njs_array_t));
     if (njs_slow_path(array == NULL)) {
         goto memory_error;
     }
 
-    array->data = njs_mp_align(vm->mem_pool, sizeof(njs_value_t),
-                               size * sizeof(njs_value_t));
-    if (njs_slow_path(array->data == NULL)) {
-        goto memory_error;
+    size = length + spare;
+
+    if (size <= NJS_ARRAY_LARGE_OBJECT_LENGTH) {
+        array->data = njs_mp_align(vm->mem_pool, sizeof(njs_value_t),
+                                   size * sizeof(njs_value_t));
+        if (njs_slow_path(array->data == NULL)) {
+            goto memory_error;
+        }
+
+    } else {
+        array->data = NULL;
     }
 
     array->start = array->data;
@@ -67,9 +68,23 @@ njs_array_alloc(njs_vm_t *vm, uint64_t l
     array->object.type = NJS_ARRAY;
     array->object.shared = 0;
     array->object.extensible = 1;
-    array->object.fast_array = 1;
-    array->size = size;
-    array->length = length;
+    array->object.error_data = 0;
+    array->object.fast_array = (array->data != NULL);
+
+    if (njs_fast_path(array->object.fast_array)) {
+        array->size = size;
+        array->length = length;
+
+    } else {
+        array->size = 0;
+        array->length = 0;
+
+        njs_set_array(&value, array);
+        ret = njs_array_length_redefine(vm, &value, length);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NULL;
+        }
+    }
 
     return array;
 
@@ -88,6 +103,130 @@ overflow:
 
 
 njs_int_t
+njs_array_convert_to_slow_array(njs_vm_t *vm, njs_array_t *array)
+{
+    uint32_t           i, length;
+    njs_value_t        index, value;
+    njs_object_prop_t  *prop;
+
+    njs_set_array(&value, array);
+    array->object.fast_array = 0;
+
+    length = array->length;
+
+    for (i = 0; i < length; i++) {
+        njs_uint32_to_string(&index, i);
+
+        if (njs_is_valid(&array->start[i])) {
+            prop = njs_object_property_add(vm, &value, &index, 0);
+            if (njs_slow_path(prop == NULL)) {
+                return NJS_ERROR;
+            }
+
+            prop->value = array->start[i];
+        }
+    }
+
+    /* GC: release value. */
+
+    njs_mp_free(vm->mem_pool, array->start);
+    array->start = NULL;
+
+    return NJS_OK;
+}
+
+
+njs_int_t
+njs_array_length_redefine(njs_vm_t *vm, njs_value_t *value, uint32_t length)
+{
+    njs_object_prop_t  *prop;
+
+    static const njs_value_t  string_length = njs_string("length");
+
+    if (njs_slow_path(!njs_is_array(value))) {
+        njs_internal_error(vm, "njs_array_length_redefine() "
+                           "applied to non-array");
+        return NJS_ERROR;
+    }
+
+    prop = njs_object_property_add(vm, value, njs_value_arg(&string_length), 1);
+    if (njs_slow_path(prop == NULL)) {
+        njs_internal_error(vm, "njs_array_length_redefine() "
+                           "cannot redefine \"length\"");
+        return NJS_ERROR;
+    }
+
+    prop->enumerable = 0;
+    prop->configurable = 0;
+
+    njs_value_number_set(&prop->value, length);
+
+    return NJS_OK;
+}
+
+
+njs_int_t
+njs_array_length_set(njs_vm_t *vm, njs_value_t *value,
+    njs_object_prop_t *prev, njs_value_t *setval)
+{
+    double        num, idx;
+    uint32_t      i, length, prev_length;
+    njs_int_t     ret;
+    njs_array_t   *array, *keys;
+
+    array = njs_object_proto_lookup(njs_object(value), NJS_ARRAY, njs_array_t);
+    if (njs_slow_path(array == NULL)) {
+        return NJS_DECLINED;
+    }
+
+    ret = njs_value_to_number(vm, setval, &num);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    length = njs_number_to_length(num);
+    if ((double) length != num) {
+        njs_range_error(vm, "Invalid array length");
+        return NJS_ERROR;
+    }
+
+    ret = njs_value_to_length(vm, &prev->value, &prev_length);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    if (length < prev_length) {
+        keys = njs_array_indices(vm, value);
+        if (njs_slow_path(keys == NULL)) {
+            return NJS_ERROR;
+        }
+
+        if (keys->length != 0) {
+            i = keys->length - 1;
+
+            do {
+                idx = njs_string_to_index(&keys->start[i]);
+                if (idx >= length) {
+                    ret = njs_value_property_delete(vm, value, &keys->start[i],
+                                                    NULL);
+                    if (njs_slow_path(ret == NJS_ERROR)) {
+                        return ret;
+                    }
+                }
+            } while (i-- != 0);
+        }
+    }
+
+    ret = njs_array_length_redefine(vm, value, length);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    return NJS_OK;
+}
+
+
+njs_int_t
 njs_array_add(njs_vm_t *vm, njs_array_t *array, njs_value_t *value)
 {
     njs_int_t  ret;
@@ -206,24 +345,25 @@ njs_array_constructor(njs_vm_t *vm, njs_
 
     if (njs_fast_path(array != NULL)) {
 
-        value = array->start;
-
-        if (args == NULL) {
-            while (size != 0) {
-                njs_set_invalid(value);
-                value++;
-                size--;
-            }
-
-        } else {
-            while (size != 0) {
-                njs_retain(args);
-                *value++ = *args++;
-                size--;
+        if (array->object.fast_array) {
+            value = array->start;
+
+            if (args == NULL) {
+                while (size != 0) {
+                    njs_set_invalid(value);
+                    value++;
+                    size--;
+                }
+
+            } else {
+                while (size != 0) {
+                    njs_retain(args);
+                    *value++ = *args++;
+                    size--;
+                }
             }
         }
 
-
         njs_set_array(&vm->retval, array);
 
         return NJS_OK;
@@ -253,26 +393,28 @@ njs_array_is_array(njs_vm_t *vm, njs_val
 
 
 static njs_int_t
-njs_array_of(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, njs_index_t unused)
+njs_array_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t unused)
 {
-        uint32_t     length, i;
-        njs_array_t  *array;
-
-        length = nargs > 1 ? nargs - 1 : 0;
-
-        array = njs_array_alloc(vm, length, NJS_ARRAY_SPARE);
-        if (njs_slow_path(array == NULL)) {
-            return NJS_ERROR;
-        }
-
-        njs_set_array(&vm->retval, array);
-
+    uint32_t     length, i;
+    njs_array_t  *array;
+
+    length = nargs > 1 ? nargs - 1 : 0;
+
+    array = njs_array_alloc(vm, length, NJS_ARRAY_SPARE);
+    if (njs_slow_path(array == NULL)) {
+        return NJS_ERROR;
+    }
+
+    njs_set_array(&vm->retval, array);
+
+    if (array->object.fast_array) {
         for (i = 0; i < length; i++) {
             array->start[i] = args[i + 1];
         }
-
-        return NJS_OK;
+    }
+
+    return NJS_OK;
 }
 
 
@@ -358,18 +500,12 @@ njs_array_length(njs_vm_t *vm,njs_object
         return NJS_DECLINED;
     }
 
-    if (njs_slow_path(!njs_is_number(setval))) {
-        ret = njs_value_to_number(vm, setval, &num);
-        if (njs_slow_path(ret != NJS_OK)) {
-            return ret;
-        }
-
-    } else {
-        num = njs_number(setval);
+    ret = njs_value_to_number(vm, setval, &num);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
     }
 
     length = njs_number_to_length(num);
-
     if ((double) length != num) {
         njs_range_error(vm, "Invalid array length");
         return NJS_ERROR;
@@ -377,35 +513,46 @@ njs_array_length(njs_vm_t *vm,njs_object
 
     array = (njs_array_t *) proto;
 
-    size = (int64_t) length - array->length;
-
-    if (size > 0) {
-        ret = njs_array_expand(vm, array, 0, size);
-        if (njs_slow_path(ret != NJS_OK)) {
-            return NJS_ERROR;
+    if (njs_fast_path(array->object.fast_array)) {
+        if (njs_fast_path(length <= NJS_ARRAY_LARGE_OBJECT_LENGTH)) {
+            size = (int64_t) length - array->length;
+
+            if (size > 0) {
+                ret = njs_array_expand(vm, array, 0, size);
+                if (njs_slow_path(ret != NJS_OK)) {
+                    return NJS_ERROR;
+                }
+
+                val = &array->start[array->length];
+
+                do {
+                    njs_set_invalid(val);
+                    val++;
+                    size--;
+                } while (size != 0);
+            }
+
+            array->length = length;
+
+            *retval = *setval;
+            return NJS_OK;
         }
 
-        val = &array->start[array->length];
-
-        do {
-            njs_set_invalid(val);
-            val++;
-            size--;
-        } while (size != 0);
+        ret = njs_array_convert_to_slow_array(vm, array);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return ret;
+        }
     }
 
-    array->length = length;
+    prop->type = NJS_PROPERTY;
+    njs_set_number(&prop->value, length);
 
     *retval = *setval;
+
     return NJS_OK;
 }
 
 
-/*
- * Array.slice(start[, end]).
- * JavaScript 1.2, ECMAScript 3.
- */
-
 static njs_int_t
 njs_array_prototype_slice(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused)
@@ -413,16 +560,16 @@ njs_array_prototype_slice(njs_vm_t *vm, 
     int64_t      start, end, length;
     uint32_t     object_length;
     njs_int_t    ret;
-    njs_value_t  *value;
-
-    value = njs_arg(args, nargs, 0);
-
-    ret = njs_value_to_object(vm, value);
+    njs_value_t  *this;
+
+    this = njs_argument(args, 0);
+
+    ret = njs_value_to_object(vm, this);
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
 
-    ret = njs_object_length(vm, value, &object_length);
+    ret = njs_object_length(vm, this, &object_length);
     if (njs_slow_path(ret == NJS_ERROR)) {
         return ret;
     }
@@ -474,7 +621,7 @@ njs_array_prototype_slice(njs_vm_t *vm, 
         }
     }
 
-    return njs_array_prototype_slice_copy(vm, value, start, length);
+    return njs_array_prototype_slice_copy(vm, this, start, length);
 }
 
 
@@ -486,8 +633,8 @@ njs_array_prototype_slice_copy(njs_vm_t 
     u_char             *dst;
     uint32_t           n;
     njs_int_t          ret;
-    njs_array_t        *array;
-    njs_value_t        *value, name;
+    njs_array_t        *array, *keys;
+    njs_value_t        *value, index, retval, array_value;
     const u_char       *src, *end;
     njs_slice_prop_t   string_slice;
     njs_string_prop_t  string;
@@ -497,17 +644,37 @@ njs_array_prototype_slice_copy(njs_vm_t 
         return NJS_ERROR;
     }
 
-    njs_set_array(&vm->retval, array);
-
-    if (length != 0) {
-        n = 0;
-
-        if (njs_fast_path(njs_is_array(this))) {
+    if (njs_slow_path(length == 0)) {
+        goto done;
+    }
+
+    n = 0;
+
+    if (njs_fast_path(array->object.fast_array)) {
+        if (njs_fast_path(njs_is_fast_array(this))) {
             value = njs_array_start(this);
 
             do {
-                /* GC: retain long string and object in values[start]. */
-                array->start[n++] = value[start++];
+                if (njs_fast_path(njs_is_valid(&value[start]))) {
+                    array->start[n++] = value[start++];
+
+                } else {
+
+                    /* src value may be in Array.prototype object. */
+
+                    njs_uint32_to_string(&index, start++);
+
+                    value = &array->start[n++];
+                    ret = njs_value_property(vm, this, &index, value);
+                    if (njs_slow_path(ret == NJS_ERROR)) {
+                        return NJS_ERROR;
+                    }
+
+                    if (ret != NJS_OK) {
+                        njs_set_invalid(value);
+                    }
+                }
+
                 length--;
             } while (length != 0);
 
@@ -553,10 +720,10 @@ njs_array_prototype_slice_copy(njs_vm_t 
         } else if (njs_is_object(this)) {
 
             do {
-                njs_uint32_to_string(&name, start++);
+                njs_uint32_to_string(&index, start++);
 
                 value = &array->start[n++];
-                ret = njs_value_property(vm, this, &name, value);
+                ret = njs_value_property(vm, this, &index, value);
 
                 if (ret != NJS_OK) {
                     njs_set_invalid(value);
@@ -576,8 +743,56 @@ njs_array_prototype_slice_copy(njs_vm_t 
                 length--;
             } while (length != 0);
         }
+
+        goto done;
     }
 
+    njs_set_array(&array_value, array);
+
+    if (njs_fast_object(length)) {
+        do {
+            njs_uint32_to_string(&index, start++);
+
+            ret = njs_value_property(vm, this, &index, &retval);
+            if (njs_slow_path(ret == NJS_ERROR)) {
+                return NJS_ERROR;
+            }
+
+            if (ret == NJS_OK) {
+                ret = njs_value_property_set(vm, &array_value, &index, &retval);
+                if (njs_slow_path(ret == NJS_ERROR)) {
+                    return ret;
+                }
+            }
+
+            length--;
+        } while (length != 0);
+
+        goto done;
+    }
+
+    keys = njs_array_indices(vm, this);
+    if (njs_slow_path(keys == NULL)) {
+        return NJS_ERROR;
+    }
+
+    for (n = 0; n < keys->length; n++) {
+        ret = njs_value_property(vm, this, &keys->start[n], &retval);
+        if (njs_slow_path(ret == NJS_ERROR)) {
+            return ret;
+        }
+
+        ret = njs_value_property_set(vm, &array_value, &keys->start[n],
+                                     &retval);
+        if (njs_slow_path(ret == NJS_ERROR)) {
+            return ret;
+        }
+    }
+
+done:
+
+    njs_set_array(&vm->retval, array);
+
     return NJS_OK;
 }
 
@@ -590,18 +805,18 @@ njs_array_prototype_push(njs_vm_t *vm, n
     njs_int_t    ret;
     njs_uint_t   i;
     njs_array_t  *array;
-    njs_value_t  *value, index;
-
-    value = njs_arg(args, nargs, 0);
+    njs_value_t  *this, index;
+
     length = 0;
-
-    ret = njs_value_to_object(vm, value);
+    this = njs_argument(args, 0);
+
+    ret = njs_value_to_object(vm, this);
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
 
-    if (njs_is_array(&args[0])) {
-        array = njs_array(&args[0]);
+    if (njs_is_fast_array(this)) {
+        array = njs_array(this);
 
         if (nargs != 0) {
             ret = njs_array_expand(vm, array, 0, nargs);
@@ -620,7 +835,7 @@ njs_array_prototype_push(njs_vm_t *vm, n
         return NJS_OK;
     }
 
-    ret = njs_object_length(vm, value, &length);
+    ret = njs_object_length(vm, this, &length);
     if (njs_slow_path(ret == NJS_ERROR)) {
         return ret;
     }
@@ -628,13 +843,13 @@ njs_array_prototype_push(njs_vm_t *vm, n
     for (i = 1; i < nargs; i++) {
         njs_uint32_to_string(&index, length++);
 
-        ret = njs_value_property_set(vm, value, &index, &args[i]);
+        ret = njs_value_property_set(vm, this, &index, &args[i]);
         if (njs_slow_path(ret == NJS_ERROR)) {
             return ret;
         }
     }
 
-    ret = njs_object_length_set(vm, value, length);
+    ret = njs_object_length_set(vm, this, length);
     if (njs_slow_path(ret == NJS_ERROR)) {
         return ret;
     }
@@ -652,19 +867,19 @@ njs_array_prototype_pop(njs_vm_t *vm, nj
     uint32_t     length;
     njs_int_t    ret;
     njs_array_t  *array;
-    njs_value_t  *value, *entry, index;
-
-    value = njs_arg(args, nargs, 0);
-
-    ret = njs_value_to_object(vm, value);
+    njs_value_t  *this, *entry, index;
+
+    this = njs_argument(args, 0);
+
+    ret = njs_value_to_object(vm, this);
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
 
     njs_set_undefined(&vm->retval);
 
-    if (njs_is_array(&args[0])) {
-        array = njs_array(&args[0]);
+    if (njs_is_fast_array(this)) {
+        array = njs_array(this);
 
         if (array->length != 0) {
             array->length--;
@@ -678,7 +893,7 @@ njs_array_prototype_pop(njs_vm_t *vm, nj
         return NJS_OK;
     }
 
-    ret = njs_object_length(vm, value, &length);
+    ret = njs_object_length(vm, this, &length);
     if (njs_slow_path(ret == NJS_ERROR)) {
         return ret;
     }
@@ -686,13 +901,13 @@ njs_array_prototype_pop(njs_vm_t *vm, nj
     if (length != 0) {
         njs_uint32_to_string(&index, --length);
 
-        ret = njs_value_property_delete(vm, value, &index, &vm->retval);
+        ret = njs_value_property_delete(vm, this, &index, &vm->retval);
         if (njs_slow_path(ret == NJS_ERROR)) {
             return ret;
         }
     }
 
-    ret = njs_object_length_set(vm, value, length);
+    ret = njs_object_length_set(vm, this, length);
     if (njs_slow_path(ret == NJS_ERROR)) {
         return ret;
     }
@@ -710,19 +925,19 @@ njs_array_prototype_unshift(njs_vm_t *vm
     njs_int_t    ret;
     njs_uint_t   n;
     njs_array_t  *array, *keys;
-    njs_value_t  *value, entry, index;
-
-    value = njs_arg(args, nargs, 0);
+    njs_value_t  *this, entry, index;
+
+    this = njs_argument(args, 0);
     length = 0;
     n = nargs - 1;
 
-    ret = njs_value_to_object(vm, value);
+    ret = njs_value_to_object(vm, this);
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
 
-    if (njs_is_array(value)) {
-        array = njs_array(value);
+    if (njs_is_fast_array(this)) {
+        array = njs_array(this);
 
         if (array->length > (UINT32_MAX - n)) {
             njs_type_error(vm, "Invalid length");
@@ -751,7 +966,7 @@ njs_array_prototype_unshift(njs_vm_t *vm
         return NJS_OK;
     }
 
-    ret = njs_object_length(vm, value, &length);
+    ret = njs_object_length(vm, this, &length);
     if (njs_slow_path(ret == NJS_ERROR)) {
         return ret;
     }
@@ -765,8 +980,8 @@ njs_array_prototype_unshift(njs_vm_t *vm
         return NJS_ERROR;
     }
 
-    if (length > NJS_ARRAY_LARGE_OBJECT_LENGTH) {
-        keys = njs_object_indexes(vm, value);
+    if (!njs_fast_object(length)) {
+        keys = njs_array_indices(vm, this);
         if (njs_slow_path(keys == NULL)) {
             return NJS_ERROR;
         }
@@ -774,7 +989,7 @@ njs_array_prototype_unshift(njs_vm_t *vm
         from = keys->length;
 
         while (from > 0) {
-            ret = njs_value_property_delete(vm, value, &keys->start[--from],
+            ret = njs_value_property_delete(vm, this, &keys->start[--from],
                                             &entry);
             if (njs_slow_path(ret == NJS_ERROR)) {
                 return ret;
@@ -785,7 +1000,7 @@ njs_array_prototype_unshift(njs_vm_t *vm
 
                 njs_uint32_to_string(&index, (uint32_t) idx + nargs - 1);
 
-                ret = njs_value_property_set(vm, value, &index, &entry);
+                ret = njs_value_property_set(vm, this, &index, &entry);
                 if (njs_slow_path(ret == NJS_ERROR)) {
                     return ret;
                 }
@@ -804,7 +1019,7 @@ njs_array_prototype_unshift(njs_vm_t *vm
     while (from > 0) {
         njs_uint32_to_string(&index, --from);
 
-        ret = njs_value_property_delete(vm, value, &index, &entry);
+        ret = njs_value_property_delete(vm, this, &index, &entry);
         if (njs_slow_path(ret == NJS_ERROR)) {
             return ret;
         }
@@ -814,7 +1029,7 @@ njs_array_prototype_unshift(njs_vm_t *vm
         if (ret == NJS_OK) {
             njs_uint32_to_string(&index, to);
 
-            ret = njs_value_property_set(vm, value, &index, &entry);
+            ret = njs_value_property_set(vm, this, &index, &entry);
             if (njs_slow_path(ret == NJS_ERROR)) {
                 return ret;
             }
@@ -826,7 +1041,7 @@ copy:
     for (n = 1; n < nargs; n++) {
         njs_uint32_to_string(&index, n - 1);
 
-        ret = njs_value_property_set(vm, value, &index, &args[n]);
+        ret = njs_value_property_set(vm, this, &index, &args[n]);
         if (njs_slow_path(ret == NJS_ERROR)) {
             return ret;
         }
@@ -834,7 +1049,7 @@ copy:
 
 done:
 
-    ret = njs_object_length_set(vm, value, length);
+    ret = njs_object_length_set(vm, this, length);
     if (njs_slow_path(ret == NJS_ERROR)) {
         return ret;
     }
@@ -852,20 +1067,20 @@ njs_array_prototype_shift(njs_vm_t *vm, 
     uint32_t     i, length;
     njs_int_t    ret;
     njs_array_t  *array;
-    njs_value_t  *value, *item, entry, index;
-
-    value = njs_arg(args, nargs, 0);
+    njs_value_t  *this, *item, entry, index;
+
+    this = njs_argument(args, 0);
     length = 0;
 
-    ret = njs_value_to_object(vm, value);
+    ret = njs_value_to_object(vm, this);
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
 
     njs_set_undefined(&vm->retval);
 
-    if (njs_is_array(&args[0])) {
-        array = njs_array(&args[0]);
+    if (njs_is_fast_array(this)) {
+        array = njs_array(this);
 
         if (array->length != 0) {
             array->length--;
@@ -881,7 +1096,7 @@ njs_array_prototype_shift(njs_vm_t *vm, 
         return NJS_OK;
     }
 
-    ret = njs_object_length(vm, value, &length);
+    ret = njs_object_length(vm, this, &length);
     if (njs_slow_path(ret == NJS_ERROR)) {
         return ret;
     }
@@ -892,7 +1107,7 @@ njs_array_prototype_shift(njs_vm_t *vm, 
 
     njs_uint32_to_string(&index, 0);
 
-    ret = njs_value_property_delete(vm, value, &index, &vm->retval);
+    ret = njs_value_property_delete(vm, this, &index, &vm->retval);
     if (njs_slow_path(ret == NJS_ERROR)) {
         return ret;
     }
@@ -900,7 +1115,7 @@ njs_array_prototype_shift(njs_vm_t *vm, 
     for (i = 1; i < length; i++) {
         njs_uint32_to_string(&index, i);
 
-        ret = njs_value_property_delete(vm, value, &index, &entry);
+        ret = njs_value_property_delete(vm, this, &index, &entry);
         if (njs_slow_path(ret == NJS_ERROR)) {
             return ret;
         }
@@ -908,7 +1123,7 @@ njs_array_prototype_shift(njs_vm_t *vm, 
         if (ret == NJS_OK) {
             njs_uint32_to_string(&index, i - 1);
 
-            ret = njs_value_property_set(vm, value, &index, &entry);
+            ret = njs_value_property_set(vm, this, &index, &entry);
             if (njs_slow_path(ret == NJS_ERROR)) {
                 return ret;
             }
@@ -919,7 +1134,7 @@ njs_array_prototype_shift(njs_vm_t *vm, 
 
 done:
 
-    ret = njs_object_length_set(vm, value, length);
+    ret = njs_object_length_set(vm, this, length);
     if (njs_slow_path(ret == NJS_ERROR)) {
         return ret;
     }
@@ -935,35 +1150,33 @@ njs_array_prototype_splice(njs_vm_t *vm,
     int64_t      n, start, length, items, delta, delete;
     njs_int_t    ret;
     njs_uint_t   i;
-    njs_value_t  *value;
+    njs_value_t  *this;
     njs_array_t  *array, *deleted;
 
-    value = njs_arg(args, nargs, 0);
-
-    ret = njs_value_to_object(vm, value);
+    this = njs_argument(args, 0);
+
+    ret = njs_value_to_object(vm, this);
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
 
+    if (njs_slow_path(!njs_is_fast_array(this))) {
+        njs_internal_error(vm, "splice() is not implemented yet for objects");
+        return NJS_ERROR;
+    }
+
     array = NULL;
     start = 0;
     delete = 0;
 
-    if (njs_is_array(value)) {
-        array = njs_array(value);
+    if (njs_is_fast_array(this)) {
+        array = njs_array(this);
         length = array->length;
 
         if (nargs > 1) {
-            value = njs_argument(args, 1);
-
-            if (njs_slow_path(!njs_is_number(value))) {
-                ret = njs_value_to_integer(vm, value, &start);
-                if (njs_slow_path(ret != NJS_OK)) {
-                    return ret;
-                }
-
-            } else {
-                start = njs_number_to_integer(njs_number(value));
+            ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &start);
+            if (njs_slow_path(ret != NJS_OK)) {
+                return ret;
             }
 
             if (start < 0) {
@@ -980,16 +1193,9 @@ njs_array_prototype_splice(njs_vm_t *vm,
             delete = length - start;
 
             if (nargs > 2) {
-                value = njs_argument(args, 2);
-
-                if (njs_slow_path(!njs_is_number(value))) {
-                    ret = njs_value_to_integer(vm, value, &n);
-                    if (njs_slow_path(ret != NJS_OK)) {
-                        return ret;
-                    }
-
-                } else {
-                    n = njs_number_to_integer(njs_number(value));
+                ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &n);
+                if (njs_slow_path(ret != NJS_OK)) {
+                    return ret;
                 }
 
                 if (n < 0) {
@@ -1007,6 +1213,11 @@ njs_array_prototype_splice(njs_vm_t *vm,
         return NJS_ERROR;
     }
 
+    if (njs_slow_path(!deleted->object.fast_array)) {
+        njs_internal_error(vm, "deleted is not a fast_array");
+        return NJS_ERROR;
+    }
+
     if (array != NULL && (delete >= 0 || nargs > 3)) {
 
         /* Move deleted items to a new array to return. */
@@ -1055,19 +1266,35 @@ static njs_int_t
 njs_array_prototype_reverse(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused)
 {
+    uint32_t     length;
     njs_int_t    ret;
-    njs_uint_t   i, n, length;
+    njs_uint_t   i, n;
     njs_value_t  value, *this;
     njs_array_t  *array;
 
-    this = njs_arg(args, nargs, 0);
+    this = njs_argument(args, 0);
 
     ret = njs_value_to_object(vm, this);
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
 
-    if (njs_is_array(this)) {
+    ret = njs_object_length(vm, this, &length);
+    if (njs_slow_path(ret == NJS_ERROR)) {
+        return ret;
+    }
+
+    if (njs_slow_path(length == 0)) {
+        vm->retval = *this;
+        return NJS_OK;
+    }
+
+    if (njs_slow_path(!njs_is_fast_array(this))) {
+        njs_internal_error(vm, "reverse() is not implemented yet for objects");
+        return NJS_ERROR;
+    }
+
+    if (njs_is_fast_array(this)) {
         array = njs_array(this);
         length = array->length;
 
@@ -1080,10 +1307,6 @@ njs_array_prototype_reverse(njs_vm_t *vm
         }
 
         njs_set_array(&vm->retval, array);
-
-    } else {
-        /* STUB */
-        vm->retval = *this;
     }
 
     return NJS_OK;
@@ -1126,15 +1349,18 @@ njs_array_prototype_join(njs_vm_t *vm, n
     u_char             *p, *last;
     size_t             size;
     ssize_t            length;
+    uint32_t           len;
     njs_int_t          ret;
     njs_chb_t          chain;
     njs_utf8_t         utf8;
     njs_uint_t         i;
     njs_array_t        *array;
-    njs_value_t        *value;
+    njs_value_t        *value, *this, index, entry;
     njs_string_prop_t  separator, string;
 
-    ret = njs_value_to_object(vm, &args[0]);
+    this = njs_argument(args, 0);
+
+    ret = njs_value_to_object(vm, this);
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
@@ -1155,20 +1381,47 @@ njs_array_prototype_join(njs_vm_t *vm, n
 
     (void) njs_string_prop(&separator, value);
 
-    if (!njs_is_array(&args[0]) || njs_array_len(&args[0]) == 0) {
+    if (njs_slow_path(!njs_is_object(this))) {
         vm->retval = njs_string_empty;
         return NJS_OK;
     }
 
-    array = njs_array(&args[0]);
+    length = 0;
+    array = NULL;
+    utf8 = njs_is_byte_string(&separator) ? NJS_STRING_BYTE : NJS_STRING_UTF8;
+
+    if (njs_is_fast_array(this)) {
+        array = njs_array(this);
+        len = array->length;
+
+    } else {


More information about the nginx-devel mailing list