[njs] Fixed Array.prototype.map() for a object with nonexistent values.

Alexander Borisov alexander.borisov at nginx.com
Tue Oct 8 05:16:53 UTC 2019


details:   https://hg.nginx.org/njs/rev/b63351157159
branches:  
changeset: 1175:b63351157159
user:      Alexander Borisov <alexander.borisov at nginx.com>
date:      Mon Sep 30 11:41:00 2019 +0300
description:
Fixed Array.prototype.map() for a object with nonexistent values.

Previously nonexistent values in the object were skipped and not added
to the result.

diffstat:

 src/njs_array.c          |  104 +++++++++++++++++++++-------------------------
 src/test/njs_unit_test.c |   10 ++++
 2 files changed, 57 insertions(+), 57 deletions(-)

diffs (198 lines):

diff -r c9061ff75070 -r b63351157159 src/njs_array.c
--- a/src/njs_array.c	Mon Oct 07 18:16:47 2019 +0300
+++ b/src/njs_array.c	Mon Sep 30 11:41:00 2019 +0300
@@ -1300,6 +1300,34 @@ njs_object_indexes(njs_vm_t *vm, njs_val
 
 
 njs_inline njs_int_t
+njs_array_object_handler(njs_vm_t *vm, njs_array_iterator_handler_t handler,
+    njs_array_iterator_args_t *args, njs_value_t *key, uint32_t i)
+{
+    njs_int_t    ret;
+    njs_value_t  prop, *entry;
+
+    ret = njs_value_property(vm, args->value, key, &prop);
+    if (njs_slow_path(ret == NJS_ERROR)) {
+        return ret;
+    }
+
+    entry = (ret == NJS_OK) ? &prop : njs_value_arg(&njs_value_invalid);
+
+    ret = handler(vm, args, entry, i);
+
+    if (njs_slow_path(ret != NJS_OK)) {
+        if (ret > 0) {
+            return NJS_DECLINED;
+        }
+
+        return NJS_ERROR;
+    }
+
+    return ret;
+}
+
+
+njs_inline njs_int_t
 njs_array_iterator(njs_vm_t *vm, njs_array_iterator_args_t *args,
     njs_array_iterator_handler_t handler)
 {
@@ -1307,7 +1335,7 @@ njs_array_iterator(njs_vm_t *vm, njs_arr
     uint32_t           length, i, from, to;
     njs_int_t          ret;
     njs_array_t        *keys;
-    njs_value_t        *entry, *value, character, index, string_obj, prop;
+    njs_value_t        *entry, *value, character, index, string_obj;
     njs_object_t       *object;
     const u_char       *p, *end, *pos;
     njs_string_prop_t  string_prop;
@@ -1421,21 +1449,11 @@ process_object:
                 continue;
             }
 
-            ret = njs_value_property(vm, value, &keys->start[i], &prop);
-            if (njs_slow_path(ret == NJS_ERROR)) {
+            ret = njs_array_object_handler(vm, handler, args, &keys->start[i],
+                                           i);
+            if (njs_slow_path(ret != NJS_OK)) {
                 return ret;
             }
-
-            if (ret != NJS_DECLINED) {
-                ret = handler(vm, args, &prop, i);
-                if (njs_slow_path(ret != NJS_OK)) {
-                    if (ret > 0) {
-                        return NJS_DECLINED;
-                    }
-
-                    return NJS_ERROR;
-                }
-            }
         }
 
         return NJS_OK;
@@ -1444,21 +1462,10 @@ process_object:
     for (i = from; i < to; i++) {
         njs_uint32_to_string(&index, i);
 
-        ret = njs_value_property(vm, value, &index, &prop);
-        if (njs_slow_path(ret == NJS_ERROR)) {
+        ret = njs_array_object_handler(vm, handler, args, &index, i);
+        if (njs_slow_path(ret != NJS_OK)) {
             return ret;
         }
-
-        if (ret != NJS_DECLINED) {
-            ret = handler(vm, args, &prop, i);
-            if (njs_slow_path(ret != NJS_OK)) {
-                if (ret > 0) {
-                    return NJS_DECLINED;
-                }
-
-                return NJS_ERROR;
-            }
-        }
     }
 
     return NJS_OK;
@@ -1473,7 +1480,7 @@ njs_array_reverse_iterator(njs_vm_t *vm,
     uint32_t           i, from, to, length;
     njs_int_t          ret;
     njs_array_t        *keys;
-    njs_value_t        *entry, *value, character, index, string_obj, prop;
+    njs_value_t        *entry, *value, character, index, string_obj;
     njs_object_t       *object;
     const u_char       *p, *end, *pos;
     njs_string_prop_t  string_prop;
@@ -1597,21 +1604,11 @@ process_object:
                 continue;
             }
 
-            ret = njs_value_property(vm, value, &keys->start[i], &prop);
-            if (njs_slow_path(ret == NJS_ERROR)) {
+            ret = njs_array_object_handler(vm, handler, args, &keys->start[i],
+                                           idx);
+            if (njs_slow_path(ret != NJS_OK)) {
                 return ret;
             }
-
-            if (ret != NJS_DECLINED) {
-                ret = handler(vm, args, &prop, idx);
-                if (njs_slow_path(ret != NJS_OK)) {
-                    if (ret > 0) {
-                        return NJS_DECLINED;
-                    }
-
-                    return NJS_ERROR;
-                }
-            }
         }
 
         return NJS_OK;
@@ -1622,21 +1619,10 @@ process_object:
     while (i-- > to) {
         njs_uint32_to_string(&index, i);
 
-        ret = njs_value_property(vm, value, &index, &prop);
-        if (njs_slow_path(ret == NJS_ERROR)) {
+        ret = njs_array_object_handler(vm, handler, args, &index, i);
+        if (njs_slow_path(ret != NJS_OK)) {
             return ret;
         }
-
-        if (ret != NJS_DECLINED) {
-            ret = handler(vm, args, &prop, i);
-            if (njs_slow_path(ret != NJS_OK)) {
-                if (ret > 0) {
-                    return NJS_DECLINED;
-                }
-
-                return NJS_ERROR;
-            }
-        }
     }
 
     return NJS_OK;
@@ -2457,6 +2443,12 @@ njs_array_prototype_map(njs_vm_t *vm, nj
         return NJS_ERROR;
     }
 
+    if (length > NJS_ARRAY_LARGE_OBJECT_LENGTH) {
+        for (i = 0; i < length; i++) {
+            njs_set_invalid(&iargs.array->start[i]);
+        }
+    }
+
     if (length > 0) {
         iargs.from = 0;
         iargs.to = length;
@@ -2466,9 +2458,7 @@ njs_array_prototype_map(njs_vm_t *vm, nj
             return ret;
         }
 
-        if (njs_is_array(&args[0])
-            && njs_object_hash_is_empty(&args[0]))
-        {
+        if (njs_is_array(&args[0]) && njs_object_hash_is_empty(&args[0])) {
             array = iargs.array;
 
             for (i = njs_array_len(&args[0]); i < length; i++) {
diff -r c9061ff75070 -r b63351157159 src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c	Mon Oct 07 18:16:47 2019 +0300
+++ b/src/test/njs_unit_test.c	Mon Sep 30 11:41:00 2019 +0300
@@ -4799,6 +4799,16 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("Array.prototype.map.call('abcdef', (val, idx, obj) => {return val === 100})"),
       njs_str("false,false,false,false,false,false") },
 
+    { njs_str("function callbackfn(val, idx, obj) {return idx === 1 && typeof val === 'undefined';}"
+              "var obj = {2: 2, length: 10};"
+              "var res = Array.prototype.map.call(obj, callbackfn); typeof res[7]"),
+      njs_str("undefined") },
+
+    { njs_str("function callbackfn(val, idx, obj) {return idx === 1 && typeof val === 'undefined';}"
+              "var obj = {2: 2, length: 9000};"
+              "var res = Array.prototype.map.call(obj, callbackfn); typeof res[8000]"),
+      njs_str("undefined") },
+
     { njs_str("var a = [];"
                  "a.reduce(function(p, v, i, a) { return p + v })"),
       njs_str("TypeError: Reduce of empty object with no initial value") },


More information about the nginx-devel mailing list