[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