[njs] Added Symbol support for builtin operations.

Dmitry Volyntsev xeioex at nginx.com
Thu Nov 21 17:57:34 UTC 2019


details:   https://hg.nginx.org/njs/rev/10a19a2e1d4f
branches:  
changeset: 1248:10a19a2e1d4f
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Thu Nov 21 20:56:06 2019 +0300
description:
Added Symbol support for builtin operations.

1) object property get/set, object literals.
2) Added Object.getOwnPropertySymbols().
3) Extended to support Symbol: Object.getOwnPropertyNames(),
   Object.keys(), Object.defineProperty(), Object.defineProperties(),
   Object.getOwnPropertyDescriptor(),
   Object.getOwnPropertyDescriptors().

diffstat:

 src/njs_array.c          |    7 +-
 src/njs_date.c           |    4 +-
 src/njs_error.c          |    2 +-
 src/njs_function.c       |    2 +-
 src/njs_json.c           |   16 +-
 src/njs_object.c         |  339 ++++++++++++++++++++++++++++------------------
 src/njs_object.h         |  170 ++++++++++++++++++++---
 src/njs_object_prop.c    |   19 ++-
 src/njs_value.c          |   38 +++-
 src/njs_value.h          |   14 +-
 src/njs_vmcode.c         |   14 +-
 src/test/njs_unit_test.c |   49 ++++++
 12 files changed, 482 insertions(+), 192 deletions(-)

diffs (truncated from 1329 to 1000 lines):

diff -r 263c75a40999 -r 10a19a2e1d4f src/njs_array.c
--- a/src/njs_array.c	Tue Nov 19 20:45:22 2019 +0300
+++ b/src/njs_array.c	Thu Nov 21 20:56:06 2019 +0300
@@ -1106,8 +1106,10 @@ njs_array_prototype_to_string(njs_vm_t *
     njs_value_t         value;
     njs_lvlhsh_query_t  lhq;
 
+    static const njs_value_t  join_string = njs_string("join");
+
     if (njs_is_object(&args[0])) {
-        njs_object_property_init(&lhq, "join", NJS_JOIN_HASH);
+        njs_object_property_init(&lhq, &join_string, NJS_JOIN_HASH);
 
         ret = njs_object_property(vm, &args[0], &lhq, &value);
 
@@ -1322,7 +1324,8 @@ njs_object_indexes(njs_vm_t *vm, njs_val
     uint32_t     i;
     njs_array_t  *keys;
 
-    keys = njs_value_own_enumerate(vm, object, NJS_ENUM_KEYS, 0);
+    keys = njs_value_own_enumerate(vm, object, NJS_ENUM_KEYS, NJS_ENUM_STRING,
+                                   0);
     if (njs_slow_path(keys == NULL)) {
         return NULL;
     }
diff -r 263c75a40999 -r 10a19a2e1d4f src/njs_date.c
--- a/src/njs_date.c	Tue Nov 19 20:45:22 2019 +0300
+++ b/src/njs_date.c	Thu Nov 21 20:56:06 2019 +0300
@@ -1403,8 +1403,10 @@ njs_date_prototype_to_json(njs_vm_t *vm,
     njs_value_t         value;
     njs_lvlhsh_query_t  lhq;
 
+    static const njs_value_t  to_iso_string = njs_string("toISOString");
+
     if (njs_is_object(&args[0])) {
-        njs_object_property_init(&lhq, "toISOString", NJS_TO_ISO_STRING_HASH);
+        njs_object_property_init(&lhq, &to_iso_string, NJS_TO_ISO_STRING_HASH);
 
         ret = njs_object_property(vm, &args[0], &lhq, &value);
 
diff -r 263c75a40999 -r 10a19a2e1d4f src/njs_error.c
--- a/src/njs_error.c	Tue Nov 19 20:45:22 2019 +0300
+++ b/src/njs_error.c	Thu Nov 21 20:56:06 2019 +0300
@@ -630,7 +630,7 @@ njs_error_to_string(njs_vm_t *vm, njs_va
 
     static const njs_value_t  default_name = njs_string("Error");
 
-    njs_object_property_init(&lhq, "name", NJS_NAME_HASH);
+    njs_object_property_init(&lhq, &njs_string_name, NJS_NAME_HASH);
 
     ret = njs_object_property(vm, error, &lhq, &value1);
 
diff -r 263c75a40999 -r 10a19a2e1d4f src/njs_function.c
--- a/src/njs_function.c	Tue Nov 19 20:45:22 2019 +0300
+++ b/src/njs_function.c	Thu Nov 21 20:56:06 2019 +0300
@@ -1160,7 +1160,7 @@ njs_function_prototype_bind(njs_vm_t *vm
 
     function->u.bound_target = njs_function(&args[0]);
 
-    njs_object_property_init(&lhq, "name", NJS_NAME_HASH);
+    njs_object_property_init(&lhq, &njs_string_name, NJS_NAME_HASH);
 
     ret = njs_object_property(vm, &args[0], &lhq, &name);
     if (njs_slow_path(ret == NJS_ERROR)) {
diff -r 263c75a40999 -r 10a19a2e1d4f src/njs_json.c
--- a/src/njs_json.c	Tue Nov 19 20:45:22 2019 +0300
+++ b/src/njs_json.c	Thu Nov 21 20:56:06 2019 +0300
@@ -68,6 +68,7 @@ typedef struct {
     njs_value_t                replacer;
     njs_str_t                  space;
     u_char                     space_buf[16];
+    njs_object_enum_type_t     keys_type;
 } njs_json_stringify_t;
 
 
@@ -227,6 +228,7 @@ njs_json_stringify(njs_vm_t *vm, njs_val
     stringify->depth = 0;
     stringify->nodes = NULL;
     stringify->last = NULL;
+    stringify->keys_type = NJS_ENUM_STRING;
 
     replacer = njs_arg(args, nargs, 2);
 
@@ -867,7 +869,8 @@ njs_json_push_parse_state(njs_vm_t *vm, 
     } else {
         state->type = NJS_JSON_OBJECT;
         state->prop = NULL;
-        state->keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, 0);
+        state->keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS,
+                                              NJS_ENUM_STRING, 0);
         if (state->keys == NULL) {
             return NULL;
         }
@@ -1094,7 +1097,7 @@ njs_json_push_stringify_state(njs_vm_t *
 
             } else {
                 state->keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS,
-                                                      0);
+                                                      stringify->keys_type, 0);
             }
 
             if (njs_slow_path(state->keys == NULL)) {
@@ -1362,7 +1365,9 @@ njs_object_to_json_function(njs_vm_t *vm
     njs_value_t         retval;
     njs_lvlhsh_query_t  lhq;
 
-    njs_object_property_init(&lhq, "toJSON", NJS_TO_JSON_HASH);
+    static const njs_value_t  to_json_string = njs_string("toJSON");
+
+    njs_object_property_init(&lhq, &to_json_string, NJS_TO_JSON_HASH);
 
     ret = njs_object_property(vm, value, &lhq, &retval);
 
@@ -2179,6 +2184,7 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_
     stringify->nodes = NULL;
     stringify->last = NULL;
     njs_set_undefined(&stringify->replacer);
+    stringify->keys_type = NJS_ENUM_STRING | NJS_ENUM_SYMBOL;
 
     if (!njs_dump_is_object(value)) {
         ret = njs_dump_value(stringify, value, console);
@@ -2221,8 +2227,7 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_
             }
 
             key = &state->keys->start[state->index++];
-            njs_string_get(key, &lhq.key);
-            lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length);
+            njs_object_property_key_set(&lhq, key, 0);
 
             if (njs_is_external(&state->value)) {
                 lhq.proto = &njs_extern_hash_proto;
@@ -2277,6 +2282,7 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_
             }
 
             state->written = 1;
+            njs_key_string_get(vm, key, &lhq.key);
             njs_json_stringify_append((char *) lhq.key.start, lhq.key.length);
             njs_json_stringify_append(":", 1);
             if (stringify->space.length != 0) {
diff -r 263c75a40999 -r 10a19a2e1d4f src/njs_object.c
--- a/src/njs_object.c	Tue Nov 19 20:45:22 2019 +0300
+++ b/src/njs_object.c	Thu Nov 21 20:56:06 2019 +0300
@@ -14,19 +14,20 @@ static njs_object_prop_t *njs_object_exi
 static uint32_t njs_object_enumerate_array_length(const njs_object_t *object);
 static uint32_t njs_object_enumerate_string_length(const njs_object_t *object);
 static uint32_t njs_object_enumerate_object_length(const njs_object_t *object,
-    njs_bool_t all);
+    njs_object_enum_type_t type, njs_bool_t all);
 static uint32_t njs_object_own_enumerate_object_length(
-    const njs_object_t *object, const njs_object_t *parent, njs_bool_t all);
+    const njs_object_t *object, const njs_object_t *parent,
+    njs_object_enum_type_t type, njs_bool_t all);
 static njs_int_t njs_object_enumerate_array(njs_vm_t *vm,
     const njs_array_t *array, njs_array_t *items, njs_object_enum_t kind);
 static njs_int_t njs_object_enumerate_string(njs_vm_t *vm,
     const njs_value_t *value, njs_array_t *items, njs_object_enum_t kind);
 static njs_int_t njs_object_enumerate_object(njs_vm_t *vm,
     const njs_object_t *object, njs_array_t *items, njs_object_enum_t kind,
-    njs_bool_t all);
+    njs_object_enum_type_t type, njs_bool_t all);
 static njs_int_t njs_object_own_enumerate_object(njs_vm_t *vm,
     const njs_object_t *object, const njs_object_t *parent, njs_array_t *items,
-    njs_object_enum_t kind, njs_bool_t all);
+    njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all);
 
 
 njs_object_t *
@@ -128,8 +129,9 @@ njs_object_hash_create(njs_vm_t *vm, njs
     lhq.pool = vm->mem_pool;
 
     while (n != 0) {
-        njs_string_get(&prop->name, &lhq.key);
-        lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length);
+
+        njs_object_property_key_set(&lhq, &prop->name, 0);
+
         lhq.value = (void *) prop;
 
         ret = njs_lvlhsh_insert(hash, &lhq);
@@ -161,25 +163,34 @@ njs_object_hash_test(njs_lvlhsh_query_t 
 {
     size_t             size;
     u_char             *start;
+    njs_value_t        *name;
     njs_object_prop_t  *prop;
 
     prop = data;
-
-    size = prop->name.short_string.size;
+    name = &prop->name;
+
+    if (njs_slow_path(njs_is_symbol(name))) {
+        return ((njs_symbol_key(name) == lhq->key_hash)
+                && lhq->key.length == 0) ? NJS_OK : NJS_DECLINED;
+    }
+
+    /* string. */
+
+    size = name->short_string.size;
 
     if (size != NJS_STRING_LONG) {
         if (lhq->key.length != size) {
             return NJS_DECLINED;
         }
 
-        start = prop->name.short_string.start;
+        start = name->short_string.start;
 
     } else {
-        if (lhq->key.length != prop->name.long_string.size) {
+        if (lhq->key.length != name->long_string.size) {
             return NJS_DECLINED;
         }
 
-        start = prop->name.long_string.data->start;
+        start = name->long_string.data->start;
     }
 
     if (memcmp(start, lhq->key.start, lhq->key.length) == 0) {
@@ -293,7 +304,8 @@ njs_object_keys(njs_vm_t *vm, njs_value_
         return NJS_ERROR;
     }
 
-    keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, 0);
+    keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS,
+                                   NJS_ENUM_STRING, 0);
     if (keys == NULL) {
         return NJS_ERROR;
     }
@@ -320,7 +332,8 @@ njs_object_values(njs_vm_t *vm, njs_valu
         return NJS_ERROR;
     }
 
-    array = njs_value_own_enumerate(vm, value, NJS_ENUM_VALUES, 0);
+    array = njs_value_own_enumerate(vm, value, NJS_ENUM_VALUES,
+                                    NJS_ENUM_STRING, 0);
     if (array == NULL) {
         return NJS_ERROR;
     }
@@ -347,7 +360,8 @@ njs_object_entries(njs_vm_t *vm, njs_val
         return NJS_ERROR;
     }
 
-    array = njs_value_own_enumerate(vm, value, NJS_ENUM_BOTH, 0);
+    array = njs_value_own_enumerate(vm, value, NJS_ENUM_BOTH,
+                                    NJS_ENUM_STRING, 0);
     if (array == NULL) {
         return NJS_ERROR;
     }
@@ -396,23 +410,26 @@ next:
 
 
 njs_inline uint32_t
-njs_object_enumerate_length(const njs_object_t *object, njs_bool_t all)
+njs_object_enumerate_length(const njs_object_t *object,
+    njs_object_enum_type_t type, njs_bool_t all)
 {
     uint32_t  length;
 
-    length = njs_object_enumerate_object_length(object, all);
-
-    switch (object->type) {
-    case NJS_ARRAY:
-        length += njs_object_enumerate_array_length(object);
-        break;
-
-    case NJS_OBJECT_STRING:
-        length += njs_object_enumerate_string_length(object);
-        break;
-
-    default:
-        break;
+    length = njs_object_enumerate_object_length(object, type, all);
+
+    if (type & NJS_STRING) {
+        switch (object->type) {
+        case NJS_ARRAY:
+            length += njs_object_enumerate_array_length(object);
+            break;
+
+        case NJS_OBJECT_STRING:
+            length += njs_object_enumerate_string_length(object);
+            break;
+
+        default:
+            break;
+        }
     }
 
     return length;
@@ -421,23 +438,25 @@ njs_object_enumerate_length(const njs_ob
 
 njs_inline uint32_t
 njs_object_own_enumerate_length(const njs_object_t *object,
-    const njs_object_t *parent, njs_bool_t all)
+    const njs_object_t *parent, njs_object_enum_type_t type, njs_bool_t all)
 {
     uint32_t  length;
 
-    length = njs_object_own_enumerate_object_length(object, parent, all);
-
-    switch (object->type) {
-    case NJS_ARRAY:
-        length += njs_object_enumerate_array_length(object);
-        break;
-
-    case NJS_OBJECT_STRING:
-        length += njs_object_enumerate_string_length(object);
-        break;
-
-    default:
-        break;
+    length = njs_object_own_enumerate_object_length(object, parent, type, all);
+
+    if (type & NJS_ENUM_STRING) {
+        switch (object->type) {
+        case NJS_ARRAY:
+            length += njs_object_enumerate_array_length(object);
+            break;
+
+        case NJS_OBJECT_STRING:
+            length += njs_object_enumerate_string_length(object);
+            break;
+
+        default:
+            break;
+        }
     }
 
     return length;
@@ -446,34 +465,37 @@ njs_object_own_enumerate_length(const nj
 
 njs_inline njs_int_t
 njs_object_enumerate_value(njs_vm_t *vm, const njs_object_t *object,
-    njs_array_t *items, njs_object_enum_t kind, njs_bool_t all)
+    njs_array_t *items, njs_object_enum_t kind, njs_object_enum_type_t type,
+    njs_bool_t all)
 {
     njs_int_t           ret;
     njs_object_value_t  *obj_val;
 
-    switch (object->type) {
-    case NJS_ARRAY:
-        ret = njs_object_enumerate_array(vm, (njs_array_t *) object, items,
-                                         kind);
-        break;
-
-    case NJS_OBJECT_STRING:
-        obj_val = (njs_object_value_t *) object;
-
-        ret = njs_object_enumerate_string(vm, &obj_val->value, items, kind);
-        break;
-
-    default:
-        goto object;
-    }
-
-    if (njs_slow_path(ret != NJS_OK)) {
-        return NJS_ERROR;
+    if (type & NJS_ENUM_STRING) {
+        switch (object->type) {
+        case NJS_ARRAY:
+            ret = njs_object_enumerate_array(vm, (njs_array_t *) object, items,
+                                             kind);
+            break;
+
+        case NJS_OBJECT_STRING:
+            obj_val = (njs_object_value_t *) object;
+
+            ret = njs_object_enumerate_string(vm, &obj_val->value, items, kind);
+            break;
+
+        default:
+            goto object;
+        }
+
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
     }
 
 object:
 
-    ret = njs_object_enumerate_object(vm, object, items, kind, all);
+    ret = njs_object_enumerate_object(vm, object, items, kind, type, all);
     if (njs_slow_path(ret != NJS_OK)) {
         return NJS_ERROR;
     }
@@ -485,34 +507,37 @@ object:
 njs_inline njs_int_t
 njs_object_own_enumerate_value(njs_vm_t *vm, const njs_object_t *object,
     const njs_object_t *parent, njs_array_t *items, njs_object_enum_t kind,
-    njs_bool_t all)
+    njs_object_enum_type_t type, njs_bool_t all)
 {
     njs_int_t           ret;
     njs_object_value_t  *obj_val;
 
-    switch (object->type) {
-    case NJS_ARRAY:
-        ret = njs_object_enumerate_array(vm, (njs_array_t *) object, items,
-                                         kind);
-        break;
-
-    case NJS_OBJECT_STRING:
-        obj_val = (njs_object_value_t *) object;
-
-        ret = njs_object_enumerate_string(vm, &obj_val->value, items, kind);
-        break;
-
-    default:
-        goto object;
-    }
-
-    if (njs_slow_path(ret != NJS_OK)) {
-        return NJS_ERROR;
+    if (type & NJS_ENUM_STRING) {
+        switch (object->type) {
+        case NJS_ARRAY:
+            ret = njs_object_enumerate_array(vm, (njs_array_t *) object, items,
+                                             kind);
+            break;
+
+        case NJS_OBJECT_STRING:
+            obj_val = (njs_object_value_t *) object;
+
+            ret = njs_object_enumerate_string(vm, &obj_val->value, items, kind);
+            break;
+
+        default:
+            goto object;
+        }
+
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
     }
 
 object:
 
-    ret = njs_object_own_enumerate_object(vm, object, parent, items, kind, all);
+    ret = njs_object_own_enumerate_object(vm, object, parent, items, kind,
+                                          type, all);
     if (njs_slow_path(ret != NJS_OK)) {
         return NJS_ERROR;
     }
@@ -523,20 +548,20 @@ object:
 
 njs_array_t *
 njs_object_enumerate(njs_vm_t *vm, const njs_object_t *object,
-    njs_object_enum_t kind, njs_bool_t all)
+    njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all)
 {
     uint32_t     length;
     njs_int_t    ret;
     njs_array_t  *items;
 
-    length = njs_object_enumerate_length(object, all);
+    length = njs_object_enumerate_length(object, type, all);
 
     items = njs_array_alloc(vm, length, NJS_ARRAY_SPARE);
     if (njs_slow_path(items == NULL)) {
         return NULL;
     }
 
-    ret = njs_object_enumerate_value(vm, object, items, kind, all);
+    ret = njs_object_enumerate_value(vm, object, items, kind, type, all);
     if (njs_slow_path(ret != NJS_OK)) {
         return NULL;
     }
@@ -549,20 +574,21 @@ njs_object_enumerate(njs_vm_t *vm, const
 
 njs_array_t *
 njs_object_own_enumerate(njs_vm_t *vm, const njs_object_t *object,
-    njs_object_enum_t kind, njs_bool_t all)
+    njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all)
 {
     uint32_t     length;
     njs_int_t    ret;
     njs_array_t  *items;
 
-    length = njs_object_own_enumerate_length(object, object, all);
+    length = njs_object_own_enumerate_length(object, object, type, all);
 
     items = njs_array_alloc(vm, length, NJS_ARRAY_SPARE);
     if (njs_slow_path(items == NULL)) {
         return NULL;
     }
 
-    ret = njs_object_own_enumerate_value(vm, object, object, items, kind, all);
+    ret = njs_object_own_enumerate_value(vm, object, object, items, kind, type,
+                                         all);
     if (njs_slow_path(ret != NJS_OK)) {
         return NULL;
     }
@@ -604,17 +630,18 @@ njs_object_enumerate_string_length(const
 
 
 static uint32_t
-njs_object_enumerate_object_length(const njs_object_t *object, njs_bool_t all)
+njs_object_enumerate_object_length(const njs_object_t *object,
+    njs_object_enum_type_t type, njs_bool_t all)
 {
     uint32_t            length;
     const njs_object_t  *proto;
 
-    length = njs_object_own_enumerate_object_length(object, object, all);
+    length = njs_object_own_enumerate_object_length(object, object, type, all);
 
     proto = object->__proto__;
 
     while (proto != NULL) {
-        length += njs_object_own_enumerate_length(proto, object, all);
+        length += njs_object_own_enumerate_length(proto, object, type, all);
         proto = proto->__proto__;
     }
 
@@ -622,9 +649,17 @@ njs_object_enumerate_object_length(const
 }
 
 
+njs_inline njs_bool_t
+njs_is_enumerable(const njs_value_t *value, njs_object_enum_type_t type)
+{
+    return (njs_is_string(value) && (type & NJS_ENUM_STRING))
+           || (njs_is_symbol(value) && (type & NJS_ENUM_SYMBOL));
+}
+
+
 static uint32_t
 njs_object_own_enumerate_object_length(const njs_object_t *object,
-    const njs_object_t *parent, njs_bool_t all)
+    const njs_object_t *parent, njs_object_enum_type_t type, njs_bool_t all)
 {
     uint32_t            length;
     njs_int_t           ret;
@@ -645,8 +680,11 @@ njs_object_own_enumerate_object_length(c
             break;
         }
 
-        lhq.key_hash = lhe.key_hash;
-        njs_string_get(&prop->name, &lhq.key);
+        if (!njs_is_enumerable(&prop->name, type)) {
+            continue;
+        }
+
+        njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash);
 
         ext_prop = njs_object_exist_in_proto(parent, object, &lhq);
 
@@ -667,10 +705,12 @@ njs_object_own_enumerate_object_length(c
             break;
         }
 
-        lhq.key_hash = lhe.key_hash;
-        njs_string_get(&prop->name, &lhq.key);
-
-        lhq.proto = &njs_object_hash_proto;
+        if (!njs_is_enumerable(&prop->name, type)) {
+            continue;
+        }
+
+        njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash);
+
         ret = njs_lvlhsh_find(&object->hash, &lhq);
 
         if (ret != NJS_OK) {
@@ -866,12 +906,14 @@ njs_object_enumerate_string(njs_vm_t *vm
 
 static njs_int_t
 njs_object_enumerate_object(njs_vm_t *vm, const njs_object_t *object,
-    njs_array_t *items, njs_object_enum_t kind, njs_bool_t all)
+    njs_array_t *items, njs_object_enum_t kind, njs_object_enum_type_t type,
+    njs_bool_t all)
 {
     njs_int_t           ret;
     const njs_object_t  *proto;
 
-    ret = njs_object_own_enumerate_object(vm, object, object, items, kind, all);
+    ret = njs_object_own_enumerate_object(vm, object, object, items, kind,
+                                          type, all);
     if (njs_slow_path(ret != NJS_OK)) {
         return NJS_ERROR;
     }
@@ -880,7 +922,7 @@ njs_object_enumerate_object(njs_vm_t *vm
 
     while (proto != NULL) {
         ret = njs_object_own_enumerate_value(vm, proto, object, items, kind,
-                                             all);
+                                             type, all);
         if (njs_slow_path(ret != NJS_OK)) {
             return NJS_ERROR;
         }
@@ -895,7 +937,7 @@ njs_object_enumerate_object(njs_vm_t *vm
 static njs_int_t
 njs_object_own_enumerate_object(njs_vm_t *vm, const njs_object_t *object,
     const njs_object_t *parent, njs_array_t *items, njs_object_enum_t kind,
-    njs_bool_t all)
+    njs_object_enum_type_t type, njs_bool_t all)
 {
     njs_int_t           ret;
     njs_value_t         *item;
@@ -910,6 +952,8 @@ njs_object_own_enumerate_object(njs_vm_t
     item = items->start;
     hash = &object->hash;
 
+    lhq.proto = &njs_object_hash_proto;
+
     switch (kind) {
     case NJS_ENUM_KEYS:
         for ( ;; ) {
@@ -919,8 +963,11 @@ njs_object_own_enumerate_object(njs_vm_t
                 break;
             }
 
-            lhq.key_hash = lhe.key_hash;
-            njs_string_get(&prop->name, &lhq.key);
+            if (!njs_is_enumerable(&prop->name, type)) {
+                continue;
+            }
+
+            njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash);
 
             ext_prop = njs_object_exist_in_proto(parent, object, &lhq);
 
@@ -941,10 +988,12 @@ njs_object_own_enumerate_object(njs_vm_t
                 break;
             }
 
-            lhq.key_hash = lhe.key_hash;
-            njs_string_get(&prop->name, &lhq.key);
-
-            lhq.proto = &njs_object_hash_proto;
+            if (!njs_is_enumerable(&prop->name, type)) {
+                continue;
+            }
+
+            njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash);
+
             ret = njs_lvlhsh_find(&object->hash, &lhq);
 
             if (ret != NJS_OK) {
@@ -966,8 +1015,11 @@ njs_object_own_enumerate_object(njs_vm_t
                 break;
             }
 
-            lhq.key_hash = lhe.key_hash;
-            njs_string_get(&prop->name, &lhq.key);
+            if (!njs_is_enumerable(&prop->name, type)) {
+                continue;
+            }
+
+            njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash);
 
             ext_prop = njs_object_exist_in_proto(parent, object, &lhq);
 
@@ -989,10 +1041,12 @@ njs_object_own_enumerate_object(njs_vm_t
                 break;
             }
 
-            lhq.key_hash = lhe.key_hash;
-            njs_string_get(&prop->name, &lhq.key);
-
-            lhq.proto = &njs_object_hash_proto;
+            if (!njs_is_enumerable(&prop->name, type)) {
+                continue;
+            }
+
+            njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash);
+
             ret = njs_lvlhsh_find(&object->hash, &lhq);
 
             if (ret != NJS_OK) {
@@ -1014,8 +1068,11 @@ njs_object_own_enumerate_object(njs_vm_t
                 break;
             }
 
-            lhq.key_hash = lhe.key_hash;
-            njs_string_get(&prop->name, &lhq.key);
+            if (!njs_is_enumerable(&prop->name, type)) {
+                continue;
+            }
+
+            njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash);
 
             ext_prop = njs_object_exist_in_proto(parent, object, &lhq);
 
@@ -1048,10 +1105,12 @@ njs_object_own_enumerate_object(njs_vm_t
                 break;
             }
 
-            lhq.key_hash = lhe.key_hash;
-            njs_string_get(&prop->name, &lhq.key);
-
-            lhq.proto = &njs_object_hash_proto;
+            if (!njs_is_enumerable(&prop->name, type)) {
+                continue;
+            }
+
+            njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash);
+
             ret = njs_lvlhsh_find(&object->hash, &lhq);
 
             if (ret != NJS_OK && (prop->enumerable || all)) {
@@ -1194,13 +1253,6 @@ njs_object_define_property(njs_vm_t *vm,
 
     name = njs_lvalue_arg(&lvalue, args, nargs, 2);
 
-    if (njs_slow_path(!njs_is_string(name))) {
-        ret = njs_value_to_string(vm, name, name);
-        if (njs_slow_path(ret != NJS_OK)) {
-            return ret;
-        }
-    }
-
     ret = njs_object_prop_define(vm, value, name, desc,
                                  NJS_OBJECT_PROP_DESCRIPTOR);
     if (njs_slow_path(ret != NJS_OK)) {
@@ -1274,7 +1326,7 @@ static njs_int_t
 njs_object_get_own_property_descriptor(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_index_t unused)
 {
-    njs_value_t  *value, *property;
+    njs_value_t  lvalue, *value, *property;
 
     value = njs_arg(args, nargs, 1);
 
@@ -1284,7 +1336,7 @@ njs_object_get_own_property_descriptor(n
         return NJS_ERROR;
     }
 
-    property = njs_arg(args, nargs, 2);
+    property = njs_lvalue_arg(&lvalue, args, nargs, 2);
 
     return njs_object_prop_descriptor(vm, &vm->retval, value, property);
 }
@@ -1311,7 +1363,8 @@ njs_object_get_own_property_descriptors(
         return NJS_ERROR;
     }
 
-    names = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, 1);
+    names = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS,
+                                    NJS_ENUM_STRING | NJS_ENUM_SYMBOL, 1);
     if (njs_slow_path(names == NULL)) {
         return NJS_ERROR;
     }
@@ -1339,8 +1392,7 @@ njs_object_get_own_property_descriptors(
             return NJS_ERROR;
         }
 
-        njs_string_get(key, &lhq.key);
-        lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length);
+        njs_object_property_key_set(&lhq, key, 0);
         lhq.value = pr;
 
         ret = njs_lvlhsh_insert(&descriptors->hash, &lhq);
@@ -1357,8 +1409,8 @@ njs_object_get_own_property_descriptors(
 
 
 static njs_int_t
-njs_object_get_own_property_names(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, njs_index_t unused)
+njs_object_get_own_property(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t type)
 {
     njs_array_t  *names;
     njs_value_t  *value;
@@ -1372,7 +1424,8 @@ njs_object_get_own_property_names(njs_vm
         return NJS_ERROR;
     }
 
-    names = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, 1);
+    names = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS,
+                                    type, 1);
     if (names == NULL) {
         return NJS_ERROR;
     }
@@ -1669,7 +1722,8 @@ njs_object_assign(njs_vm_t *vm, njs_valu
     for (i = 2; i < nargs; i++) {
         source = &args[i];
 
-        names = njs_value_own_enumerate(vm, source, NJS_ENUM_KEYS, 1);
+        names = njs_value_own_enumerate(vm, source, NJS_ENUM_KEYS,
+                                        NJS_ENUM_STRING | NJS_ENUM_SYMBOL, 1);
         if (njs_slow_path(names == NULL)) {
             return NJS_ERROR;
         }
@@ -1913,7 +1967,18 @@ static const njs_object_prop_t  njs_obje
     {
         .type = NJS_PROPERTY,
         .name = njs_long_string("getOwnPropertyNames"),
-        .value = njs_native_function(njs_object_get_own_property_names, 1),
+        .value = njs_native_function2(njs_object_get_own_property, 1,
+                                      NJS_ENUM_STRING),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    /* Object.getOwnPropertySymbols(). */
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_long_string("getOwnPropertySymbols"),
+        .value = njs_native_function2(njs_object_get_own_property, 1,
+                                      NJS_ENUM_SYMBOL),
         .writable = 1,
         .configurable = 1,
     },
diff -r 263c75a40999 -r 10a19a2e1d4f src/njs_object.h
--- a/src/njs_object.h	Tue Nov 19 20:45:22 2019 +0300
+++ b/src/njs_object.h	Thu Nov 21 20:56:06 2019 +0300
@@ -8,27 +8,6 @@
 #define _NJS_OBJECT_H_INCLUDED_
 
 
-#define njs_is_data_descriptor(prop)                                          \
-    ((prop)->writable != NJS_ATTRIBUTE_UNSET || njs_is_valid(&(prop)->value))
-
-
-#define njs_is_accessor_descriptor(prop)                                      \
-    (njs_is_function_or_undefined(&(prop)->getter)                            \
-     || njs_is_function_or_undefined(&(prop)->setter))
-
-
-#define njs_is_generic_descriptor(prop)                                       \
-    (!njs_is_data_descriptor(prop) && !njs_is_accessor_descriptor(prop))
-
-
-#define njs_object_property_init(lhq, _key, hash)                             \
-    do {                                                                      \
-        (lhq)->proto = &njs_object_hash_proto;                                \
-        (lhq)->key_hash = hash;                                               \
-        (lhq)->key = njs_str_value(_key);                                     \
-    } while (0)
-
-
 typedef enum {
     NJS_OBJECT_PROP_DESCRIPTOR,
     NJS_OBJECT_PROP_GETTER,
@@ -65,9 +44,9 @@ njs_object_t *njs_object_value_copy(njs_
 njs_object_t *njs_object_value_alloc(njs_vm_t *vm, const njs_value_t *value,
     njs_uint_t type);
 njs_array_t *njs_object_enumerate(njs_vm_t *vm, const njs_object_t *object,
-    njs_object_enum_t kind, njs_bool_t all);
+    njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all);
 njs_array_t *njs_object_own_enumerate(njs_vm_t *vm, const njs_object_t *object,
-    njs_object_enum_t kind, njs_bool_t all);
+    njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all);
 njs_int_t njs_object_traverse(njs_vm_t *vm, njs_object_t *object, void *ctx,
     njs_object_traverse_cb_t cb);
 njs_int_t njs_object_hash_create(njs_vm_t *vm, njs_lvlhsh_t *hash,
@@ -101,7 +80,147 @@ njs_int_t njs_object_prop_descriptor(njs
     njs_value_t *value, njs_value_t *setval);
 const char *njs_prop_type_string(njs_object_prop_type_t type);
 
-extern const njs_object_type_init_t  njs_obj_type_init;
+
+njs_inline njs_bool_t
+njs_is_data_descriptor(njs_object_prop_t *prop)
+{
+    return prop->writable != NJS_ATTRIBUTE_UNSET || njs_is_valid(&prop->value);
+}
+
+
+njs_inline njs_bool_t
+njs_is_accessor_descriptor(njs_object_prop_t *prop)
+{
+    return njs_is_function_or_undefined(&prop->getter)
+           || njs_is_function_or_undefined(&prop->setter);
+}
+
+
+njs_inline njs_bool_t
+njs_is_generic_descriptor(njs_object_prop_t *prop)
+{
+    return !njs_is_data_descriptor(prop) && !njs_is_accessor_descriptor(prop);
+}
+
+
+njs_inline void
+njs_object_property_key_set(njs_lvlhsh_query_t *lhq, const njs_value_t *key,
+    uint32_t hash)
+{
+    if (njs_is_symbol(key)) {
+
+        lhq->key.length = 0;
+        lhq->key_hash = njs_symbol_key(key);
+
+    } else {
+
+        /* string. */
+
+        njs_string_get(key, &lhq->key);
+
+        if (hash == 0) {
+            lhq->key_hash = njs_djb_hash(lhq->key.start, lhq->key.length);
+
+        } else {
+            lhq->key_hash = hash;
+        }
+    }
+}
+
+
+njs_inline void
+njs_object_property_init(njs_lvlhsh_query_t *lhq, const njs_value_t *key,
+    uint32_t hash)
+{
+    lhq->proto = &njs_object_hash_proto;
+
+    njs_object_property_key_set(lhq, key, hash);
+}
+
+
+njs_inline njs_int_t
+njs_primitive_value_to_key(njs_vm_t *vm, njs_value_t *dst,
+    const njs_value_t *src)
+{
+    const njs_value_t  *value;
+
+    switch (src->type) {
+
+    case NJS_NULL:
+        value = &njs_string_null;
+        break;
+
+    case NJS_UNDEFINED:
+        value = &njs_string_undefined;
+        break;
+
+    case NJS_BOOLEAN:
+        value = njs_is_true(src) ? &njs_string_true : &njs_string_false;
+        break;
+
+    case NJS_NUMBER:
+        return njs_number_to_string(vm, dst, src);
+
+    case NJS_SYMBOL:
+    case NJS_STRING:
+        /* GC: njs_retain(src); */
+        value = src;
+        break;
+
+    default:
+        return NJS_ERROR;
+    }
+
+    *dst = *value;
+
+    return NJS_OK;
+}
+
+
+njs_inline njs_int_t
+njs_value_to_key(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value)
+{
+    njs_int_t    ret;
+    njs_value_t  primitive;
+
+    if (njs_slow_path(!njs_is_primitive(value))) {
+        if (njs_slow_path(value->type == NJS_OBJECT_SYMBOL)) {
+            /* should fail */
+            value = njs_object_value(value);
+
+        } else {
+            ret = njs_value_to_primitive(vm, &primitive, value, 1);
+            if (njs_slow_path(ret != NJS_OK)) {
+                return ret;
+            }
+
+            value = &primitive;
+        }
+    }
+
+    return njs_primitive_value_to_key(vm, dst, value);
+}
+
+
+njs_inline njs_int_t
+njs_key_string_get(njs_vm_t *vm, const njs_value_t *key, njs_str_t *str)
+{
+    njs_int_t    ret;
+    njs_value_t  dst;
+
+    if (njs_slow_path(njs_is_symbol(key))) {
+        ret = njs_symbol_to_string(vm, &dst, key);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return ret;
+        }
+
+        key = &dst;
+    }
+
+    njs_string_get(key, str);
+
+    return NJS_OK;
+}
 
 
 njs_inline njs_int_t
@@ -118,4 +237,7 @@ njs_object_length_set(njs_vm_t *vm, njs_


More information about the nginx-devel mailing list