[njs] Added generic implementation of Array.prototype.fill().

Alexander Borisov alexander.borisov at nginx.com
Tue May 28 17:45:26 UTC 2019


details:   https://hg.nginx.org/njs/rev/8e7e7ba29c71
branches:  
changeset: 990:8e7e7ba29c71
user:      Alexander Borisov <alexander.borisov at nginx.com>
date:      Thu May 23 18:03:46 2019 +0300
description:
Added generic implementation of Array.prototype.fill().

According to ES6: 22.1.3.6.

This closes #72 issue on GitHub.

diffstat:

 njs/njs_array.c          |  152 +++++++++++++++++++++++++++++++++-------------
 njs/test/njs_unit_test.c |   14 ++++
 2 files changed, 121 insertions(+), 45 deletions(-)

diffs (219 lines):

diff -r 77e4b95109d4 -r 8e7e7ba29c71 njs/njs_array.c
--- a/njs/njs_array.c	Tue May 28 17:04:40 2019 +0300
+++ b/njs/njs_array.c	Thu May 23 18:03:46 2019 +0300
@@ -23,6 +23,16 @@ typedef struct {
 
 
 typedef struct {
+    union {
+        njs_continuation_t  cont;
+        u_char              padding[NJS_CONTINUATION_SIZE];
+    } u;
+
+    njs_value_t             length;
+} njs_array_fill_t;
+
+
+typedef struct {
     njs_continuation_t      cont;
     njs_value_t             *values;
     uint32_t                max;
@@ -88,6 +98,8 @@ static njs_ret_t njs_array_prototype_sli
 static njs_ret_t njs_array_prototype_join_continuation(njs_vm_t *vm,
     njs_value_t *args, nxt_uint_t nargs, njs_index_t unused);
 static njs_value_t *njs_array_copy(njs_value_t *dst, njs_value_t *src);
+static njs_ret_t njs_array_prototype_fill_continuation(njs_vm_t *vm,
+    njs_value_t *args, nxt_uint_t nargs, njs_index_t unused);
 static njs_ret_t njs_array_prototype_for_each_continuation(njs_vm_t *vm,
     njs_value_t *args, nxt_uint_t nargs, njs_index_t unused);
 static njs_ret_t njs_array_prototype_some_continuation(njs_vm_t *vm,
@@ -1395,62 +1407,111 @@ static njs_ret_t
 njs_array_prototype_fill(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
     njs_index_t unused)
 {
+    njs_ret_t         ret;
+    njs_value_t       *this;
+    njs_array_fill_t  *fill;
+
+    static const njs_value_t  njs_string_length = njs_string("length");
+
+    this = (njs_value_t *) njs_arg(args, nargs, 0);
+
+    if (njs_is_primitive(this)) {
+        if (njs_is_null_or_undefined(this)) {
+            njs_type_error(vm, "\"this\" argument cannot be "
+                               "undefined or null value");
+            return NJS_ERROR;
+        }
+
+    } else if (!njs_is_array(this)) {
+        fill = njs_vm_continuation(vm);
+        fill->u.cont.function = njs_array_prototype_fill_continuation;
+
+        ret = njs_value_property(vm, this, &njs_string_length, &fill->length);
+        if (nxt_slow_path(ret == NXT_ERROR || ret == NJS_TRAP)) {
+            return ret;
+        }
+    }
+
+    return njs_array_prototype_fill_continuation(vm, args, nargs, unused);
+}
+
+
+static njs_ret_t
+njs_array_prototype_fill_continuation(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    njs_ret_t          ret;
     nxt_int_t          i, start, end, length;
     njs_array_t        *array;
-    const njs_value_t  *value;
-
-    vm->retval = args[0];
-
-    if (!njs_is_array(&args[0])) {
-        return NXT_OK;
-    }
-
-    array = args[0].data.u.array;
-    length = array->length;
-
-    if (length == 0) {
+    njs_value_t        name;
+    njs_object_t       *object;
+    njs_array_fill_t   *fill;
+    const njs_value_t  *this, *value;
+
+    array = NULL;
+
+    this = njs_arg(args, nargs, 0);
+
+    if (njs_is_primitive(this)) {
+        object = njs_object_value_alloc(vm, this, this->type);
+        if (nxt_slow_path(object == NULL)) {
+            return NXT_ERROR;
+        }
+
+        vm->retval.data.u.object = object;
+        vm->retval.type = object->type;
+        vm->retval.data.truth = 1;
+
         return NXT_OK;
     }
 
-    start = 0;
-    end = length;
-
-    if (nargs > 2) {
-        start = args[2].data.u.number;
-
-        if (start > length) {
-            start = length;
-        }
-
-        if (start < 0) {
-            start += length;
-
-            if (start < 0) {
-                start = 0;
-            }
+    if (njs_is_array(this)) {
+        array = this->data.u.array;
+        length = array->length;
+
+    } else {
+        fill = njs_vm_continuation(vm);
+
+        if (nxt_slow_path(!njs_is_primitive(&fill->length))) {
+            njs_vm_trap_value(vm, &fill->length);
+            return njs_trap(vm, NJS_TRAP_NUMBER_ARG);
         }
 
-        if (nargs > 3) {
-            end = args[3].data.u.number;
-
-            if (end > length) {
-                end = length;
-            }
-
-            if (end < 0) {
-                end += length;
-
-                if (end < 0) {
-                    end = 0;
-                }
-            }
-        }
+        length = njs_primitive_value_to_length(&fill->length);
     }
 
+    start = njs_primitive_value_to_integer(njs_arg(args, nargs, 2));
+    start = (start < 0) ? nxt_max(length + start, 0) : nxt_min(start, length);
+
+    if (njs_is_undefined(njs_arg(args, nargs, 3))) {
+        end = length;
+
+    } else {
+        end = njs_primitive_value_to_integer(njs_arg(args, nargs, 3));
+    }
+
+    end = (end < 0) ? nxt_max(length + end, 0) : nxt_min(end, length);
+
     value = njs_arg(args, nargs, 1);
 
+    vm->retval = *this;
+
+    if (array != NULL) {
+        for (i = start; i < end; i++) {
+            array->start[i] = *value;
+        }
+
+        return NXT_OK;
+    }
+
     for (i = start; i < end; i++) {
-        array->start[i] = *value;
+        njs_uint32_to_string(&name, i);
+
+        ret = njs_value_property_set(vm, &vm->retval, &name,
+                                     (njs_value_t *) value);
+        if (nxt_slow_path(ret == NXT_ERROR)) {
+            return ret;
+        }
     }
 
     return NXT_OK;
@@ -2395,7 +2456,8 @@ static const njs_object_prop_t  njs_arra
     {
         .type = NJS_METHOD,
         .name = njs_string("fill"),
-        .value = njs_native_function(njs_array_prototype_fill, 0,
+        .value = njs_native_function(njs_array_prototype_fill,
+                     njs_continuation_size(njs_array_fill_t),
                      NJS_OBJECT_ARG, NJS_SKIP_ARG, NJS_NUMBER_ARG,
                      NJS_NUMBER_ARG),
         .writable = 1,
diff -r 77e4b95109d4 -r 8e7e7ba29c71 njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c	Tue May 28 17:04:40 2019 +0300
+++ b/njs/test/njs_unit_test.c	Thu May 23 18:03:46 2019 +0300
@@ -3995,6 +3995,20 @@ static njs_unit_test_t  njs_test[] =
                                  "{ return a + (x === undefined); }, 0)"),
       nxt_string("3") },
 
+    { nxt_string("var a = Array.prototype.fill.apply("
+                 "Object({length: 40}), [\"a\", 1, 20]); Object.values(a)"),
+      nxt_string("a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,40,a,a,a,a") },
+
+    { nxt_string("var a = Array.prototype.fill.apply({length: "
+                 "{ valueOf: function() { return 40 }}}, [\"a\", 1, 20]);"
+                 "Object.values(a)"),
+      nxt_string("a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,[object Object],a,a,a,a") },
+
+    { nxt_string("[NaN, false, ''].map("
+                 "(x) => Array.prototype.fill.call(x)"
+                 ").every((x) => typeof x == 'object')"),
+      nxt_string("true") },
+
     { nxt_string("var a = [];"
                  "a.filter(function(v, i, a) { return v > 1 })"),
       nxt_string("") },


More information about the nginx-devel mailing list