[njs] Object.values() method.

Valentin Bartenev vbart at nginx.com
Mon Dec 3 19:07:22 UTC 2018


details:   https://hg.nginx.org/njs/rev/1af8f40573f2
branches:  
changeset: 682:1af8f40573f2
user:      Valentin Bartenev <vbart at nginx.com>
date:      Mon Dec 03 19:20:58 2018 +0300
description:
Object.values() method.

diffstat:

 njs/njs_json.c           |    4 +-
 njs/njs_object.c         |  169 +++++++++++++++++++++++++++++++++++++++-------
 njs/njs_object.h         |    9 ++-
 njs/test/njs_unit_test.c |   38 ++++++++++
 4 files changed, 191 insertions(+), 29 deletions(-)

diffs (347 lines):

diff -r 4a543ed58c95 -r 1af8f40573f2 njs/njs_json.c
--- a/njs/njs_json.c	Sat Dec 01 22:32:33 2018 +0300
+++ b/njs/njs_json.c	Mon Dec 03 19:20:58 2018 +0300
@@ -1095,7 +1095,7 @@ njs_json_push_parse_state(njs_vm_t *vm, 
         } else {
             state->type = NJS_JSON_OBJECT_START;
             state->prop_value = NULL;
-            state->keys = njs_object_keys_array(vm, value);
+            state->keys = njs_object_enumerate(vm, value, NJS_ENUM_KEYS);
             if (state->keys == NULL) {
                 return NULL;
             }
@@ -1659,7 +1659,7 @@ njs_json_push_stringify_state(njs_vm_t *
                 state->keys = njs_extern_keys_array(vm, value->external.proto);
 
             } else {
-                state->keys = njs_object_keys_array(vm, value);
+                state->keys = njs_object_enumerate(vm, value, NJS_ENUM_KEYS);
             }
 
             if (state->keys == NULL) {
diff -r 4a543ed58c95 -r 1af8f40573f2 njs/njs_object.c
--- a/njs/njs_object.c	Sat Dec 01 22:32:33 2018 +0300
+++ b/njs/njs_object.c	Mon Dec 03 19:20:58 2018 +0300
@@ -872,7 +872,7 @@ njs_object_keys(njs_vm_t *vm, njs_value_
         return NXT_ERROR;
     }
 
-    keys = njs_object_keys_array(vm, value);
+    keys = njs_object_enumerate(vm, value, NJS_ENUM_KEYS);
     if (keys == NULL) {
         return NXT_ERROR;
     }
@@ -885,20 +885,52 @@ njs_object_keys(njs_vm_t *vm, njs_value_
 }
 
 
+static njs_ret_t
+njs_object_values(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+ {
+    njs_array_t        *array;
+    const njs_value_t  *value;
+
+    value = njs_arg(args, nargs, 1);
+
+    if (njs_is_null_or_void(value)) {
+        njs_type_error(vm, "cannot convert %s argument to object",
+                       njs_type_string(value->type));
+
+        return NXT_ERROR;
+    }
+
+    array = njs_object_enumerate(vm, value, NJS_ENUM_VALUES);
+    if (array == NULL) {
+        return NXT_ERROR;
+    }
+
+    vm->retval.data.u.array = array;
+    vm->retval.type = NJS_ARRAY;
+    vm->retval.data.truth = 1;
+
+    return NXT_OK;
+}
+
+
 njs_array_t *
-njs_object_keys_array(njs_vm_t *vm, const njs_value_t *value)
+njs_object_enumerate(njs_vm_t *vm, const njs_value_t *value,
+    njs_object_enum_t kind)
 {
-    uint32_t           i, n, length, keys_length, properties;
-    njs_value_t        *string;
-    njs_array_t        *keys, *array;
+    u_char             *dst;
+    uint32_t           i, length, size, items_length, properties;
+    njs_value_t        *string, *item;
+    njs_array_t        *items, *array;
     nxt_lvlhsh_t       *hash;
+    const u_char       *src, *end;
     njs_object_prop_t  *prop;
     njs_string_prop_t  string_prop;
     nxt_lvlhsh_each_t  lhe;
 
     array = NULL;
     length = 0;
-    keys_length = 0;
+    items_length = 0;
 
     switch (value->type) {
     case NJS_ARRAY:
@@ -907,7 +939,7 @@ njs_object_keys_array(njs_vm_t *vm, cons
 
         for (i = 0; i < length; i++) {
             if (njs_is_valid(&array->start[i])) {
-                keys_length++;
+                items_length++;
             }
         }
 
@@ -923,7 +955,7 @@ njs_object_keys_array(njs_vm_t *vm, cons
         }
 
         length = njs_string_prop(&string_prop, string);
-        keys_length += length;
+        items_length += length;
         break;
 
     default:
@@ -950,46 +982,123 @@ njs_object_keys_array(njs_vm_t *vm, cons
             }
         }
 
-        keys_length += properties;
+        items_length += properties;
     }
 
-    keys = njs_array_alloc(vm, keys_length, NJS_ARRAY_SPARE);
-    if (nxt_slow_path(keys == NULL)) {
+    items = njs_array_alloc(vm, items_length, NJS_ARRAY_SPARE);
+    if (nxt_slow_path(items == NULL)) {
         return NULL;
     }
 
-    n = 0;
+    item = items->start;
 
     if (array != NULL) {
-        for (i = 0; i < length; i++) {
-            if (njs_is_valid(&array->start[i])) {
-                njs_uint32_to_string(&keys->start[n++], i);
+
+        switch (kind) {
+        case NJS_ENUM_KEYS:
+            for (i = 0; i < length; i++) {
+                if (njs_is_valid(&array->start[i])) {
+                    njs_uint32_to_string(item++, i);
+                }
             }
+
+            break;
+
+        case NJS_ENUM_VALUES:
+            for (i = 0; i < length; i++) {
+                if (njs_is_valid(&array->start[i])) {
+                    /* GC: retain. */
+                    *item++ = array->start[i];
+                }
+            }
+
+            break;
         }
 
-    } else {
-        for (i = 0; i < length; i++) {
-            njs_uint32_to_string(&keys->start[n++], i);
+    } else if (length != 0) {
+
+        switch (kind) {
+        case NJS_ENUM_KEYS:
+            for (i = 0; i < length; i++) {
+                njs_uint32_to_string(item++, i);
+            }
+
+            break;
+
+        case NJS_ENUM_VALUES:
+            if (string_prop.size == (size_t) length) {
+                /* Byte or ASCII string. */
+
+                for (i = 0; i < length; i++) {
+                    dst = njs_string_short_start(item);
+                    dst[0] = string_prop.start[i];
+
+                    njs_string_short_set(item, 1, 1);
+
+                    item++;
+                }
+
+            } else {
+                /* UTF-8 string. */
+
+                src = string_prop.start;
+                end = src + string_prop.size;
+
+                do {
+                    dst = njs_string_short_start(item);
+                    dst = nxt_utf8_copy(dst, &src, end);
+                    size = dst - njs_string_short_start(value);
+
+                    njs_string_short_set(item, size, 1);
+
+                    item++;
+
+                } while (src != end);
+            }
+
+            break;
         }
     }
 
     if (nxt_fast_path(properties != 0)) {
         nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
 
-        for ( ;; ) {
-            prop = nxt_lvlhsh_each(hash, &lhe);
-
-            if (prop == NULL) {
-                break;
+        switch (kind) {
+
+        case NJS_ENUM_KEYS:
+            for ( ;; ) {
+                prop = nxt_lvlhsh_each(hash, &lhe);
+
+                if (prop == NULL) {
+                    break;
+                }
+
+                if (prop->type != NJS_WHITEOUT && prop->enumerable) {
+                    njs_string_copy(item++, &prop->name);
+                }
             }
 
-            if (prop->type != NJS_WHITEOUT && prop->enumerable) {
-                njs_string_copy(&keys->start[n++], &prop->name);
+            break;
+
+        case NJS_ENUM_VALUES:
+            for ( ;; ) {
+                prop = nxt_lvlhsh_each(hash, &lhe);
+
+                if (prop == NULL) {
+                    break;
+                }
+
+                if (prop->type != NJS_WHITEOUT && prop->enumerable) {
+                    /* GC: retain. */
+                    *item++ = prop->value;
+                }
             }
+
+            break;
         }
     }
 
-    return keys;
+    return items;
 }
 
 
@@ -1853,6 +1962,14 @@ static const njs_object_prop_t  njs_obje
                                      NJS_SKIP_ARG, NJS_OBJECT_ARG),
     },
 
+    /* ES8: Object.values(). */
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("values"),
+        .value = njs_native_function(njs_object_values, 0,
+                                     NJS_SKIP_ARG, NJS_OBJECT_ARG),
+    },
+
     /* Object.defineProperty(). */
     {
         .type = NJS_METHOD,
diff -r 4a543ed58c95 -r 1af8f40573f2 njs/njs_object.h
--- a/njs/njs_object.h	Sat Dec 01 22:32:33 2018 +0300
+++ b/njs/njs_object.h	Mon Dec 03 19:20:58 2018 +0300
@@ -17,6 +17,12 @@ typedef enum {
 } njs_object_property_type_t;
 
 
+typedef enum {
+    NJS_ENUM_KEYS = 0,
+    NJS_ENUM_VALUES,
+} njs_object_enum_t;
+
+
 /*
  * Attributes are generally used as Boolean values.
  * The UNSET value is used internally only by njs_define_property().
@@ -79,7 +85,8 @@ njs_object_t *njs_object_alloc(njs_vm_t 
 njs_object_t *njs_object_value_copy(njs_vm_t *vm, njs_value_t *value);
 njs_object_t *njs_object_value_alloc(njs_vm_t *vm, const njs_value_t *value,
     nxt_uint_t type);
-njs_array_t *njs_object_keys_array(njs_vm_t *vm, const njs_value_t *value);
+njs_array_t *njs_object_enumerate(njs_vm_t *vm, const njs_value_t *value,
+    njs_object_enum_t kind);
 njs_ret_t njs_value_property(njs_vm_t *vm, njs_value_t *value,
     const njs_value_t *property, njs_value_t *retval);
 njs_object_prop_t *njs_object_property(njs_vm_t *vm, const njs_object_t *obj,
diff -r 4a543ed58c95 -r 1af8f40573f2 njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c	Sat Dec 01 22:32:33 2018 +0300
+++ b/njs/test/njs_unit_test.c	Mon Dec 03 19:20:58 2018 +0300
@@ -7421,6 +7421,30 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("Object.keys(true)"),
       nxt_string("") },
 
+    { nxt_string("var o = {a:3, b:2, c:1}; Object.values(o)"),
+      nxt_string("3,2,1") },
+
+    { nxt_string("Object.values('s')"),
+      nxt_string("s") },
+
+    { nxt_string("Object.values('абв abc')"),
+      nxt_string("а,б,в, ,a,b,c") },
+
+    { nxt_string("var s = new String('abc'); s.three = 3; Object.values(s)"),
+      nxt_string("a,b,c,3") },
+
+    { nxt_string("var a = [,,5,,4,,,3,2]; a.one = 1; Object.values(a)"),
+      nxt_string("5,4,3,2,1") },
+
+    { nxt_string("Object.values([{}, null, false, NaN, function() {}])"),
+      nxt_string("[object Object],,false,NaN,[object Function]") },
+
+    { nxt_string("Object.values(1)"),
+      nxt_string("") },
+
+    { nxt_string("Object.values()"),
+      nxt_string("TypeError: cannot convert undefined argument to object") },
+
     { nxt_string("var o = {}; Object.defineProperty(o, 'a', {}); o.a"),
       nxt_string("undefined") },
 
@@ -7444,6 +7468,20 @@ static njs_unit_test_t  njs_test[] =
                  "Object.keys(o)"),
       nxt_string("a,c,b") },
 
+    { nxt_string("var o = {a:1, c:2}; Object.defineProperty(o, 'b', {});"
+                 "Object.values(o)"),
+      nxt_string("1,2") },
+
+    { nxt_string("var o = {a:1, c:2};"
+                 "Object.defineProperty(o, 'b', {enumerable:false, value:3});"
+                 "Object.values(o)"),
+      nxt_string("1,2") },
+
+    { nxt_string("var o = {a:1, c:3};"
+                 "Object.defineProperty(o, 'b', {enumerable:true, value:2});"
+                 "Object.values(o)"),
+      nxt_string("1,3,2") },
+
     { nxt_string("var o = {}; Object.defineProperty(o, 'a', {}); o.a = 1"),
       nxt_string("TypeError: Cannot assign to read-only property 'a' of object") },
 


More information about the nginx-devel mailing list