[njs] Added two new function for working with enumerate value.

Alexander Borisov alexander.borisov at nginx.com
Thu Apr 18 16:10:17 UTC 2019


details:   https://hg.nginx.org/njs/rev/519785f57b81
branches:  
changeset: 906:519785f57b81
user:      Alexander Borisov <alexander.borisov at nginx.com>
date:      Wed Apr 17 17:27:14 2019 +0300
description:
Added two new function for working with enumerate value.

New functions njs_value_enumerate() and njs_value_own_enumerate().
Changed function njs_object_enumerate() for working only with object.

diffstat:

 njs/njs_json.c   |    5 +-
 njs/njs_object.c |  966 +++++++++++++++++++++++++++++++++++++++---------------
 njs/njs_object.h |   11 +-
 njs/njs_vm.c     |   42 ++
 njs/njs_vm.h     |   11 +
 5 files changed, 751 insertions(+), 284 deletions(-)

diffs (truncated from 1205 to 1000 lines):

diff -r e4f3b3b97990 -r 519785f57b81 njs/njs_json.c
--- a/njs/njs_json.c	Wed Apr 17 17:13:39 2019 +0300
+++ b/njs/njs_json.c	Wed Apr 17 17:27:14 2019 +0300
@@ -1127,7 +1127,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_enumerate(vm, value, NJS_ENUM_KEYS, 0);
+            state->keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, 0);
             if (state->keys == NULL) {
                 return NULL;
             }
@@ -1705,7 +1705,8 @@ njs_json_push_stringify_state(njs_vm_t *
                 state->keys = njs_extern_keys_array(vm, value->external.proto);
 
             } else {
-                state->keys = njs_object_enumerate(vm, value, NJS_ENUM_KEYS, 0);
+                state->keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS,
+                                                      0);
             }
 
             if (state->keys == NULL) {
diff -r e4f3b3b97990 -r 519785f57b81 njs/njs_object.c
--- a/njs/njs_object.c	Wed Apr 17 17:13:39 2019 +0300
+++ b/njs/njs_object.c	Wed Apr 17 17:27:14 2019 +0300
@@ -27,6 +27,25 @@ static njs_ret_t njs_object_query_prop_h
 static njs_ret_t njs_define_property(njs_vm_t *vm, njs_value_t *object,
     const njs_value_t *name, const njs_object_t *descriptor);
 
+static njs_object_prop_t * njs_object_exist_in_proto(const njs_object_t *begin,
+    const njs_object_t *end, nxt_lvlhsh_query_t *lhq);
+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,
+    nxt_bool_t all);
+static uint32_t njs_object_own_enumerate_object_length(
+    const njs_object_t *object, const njs_object_t *parent, nxt_bool_t all);
+static njs_ret_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_ret_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_ret_t njs_object_enumerate_object(njs_vm_t *vm,
+    const njs_object_t *object, njs_array_t *items, njs_object_enum_t kind,
+    nxt_bool_t all);
+static njs_ret_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, nxt_bool_t all);
+
 
 nxt_noinline njs_object_t *
 njs_object_alloc(njs_vm_t *vm)
@@ -886,7 +905,7 @@ njs_object_keys(njs_vm_t *vm, njs_value_
         return NXT_ERROR;
     }
 
-    keys = njs_object_enumerate(vm, value, NJS_ENUM_KEYS, 0);
+    keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, 0);
     if (keys == NULL) {
         return NXT_ERROR;
     }
@@ -915,7 +934,7 @@ njs_object_values(njs_vm_t *vm, njs_valu
         return NXT_ERROR;
     }
 
-    array = njs_object_enumerate(vm, value, NJS_ENUM_VALUES, 0);
+    array = njs_value_own_enumerate(vm, value, NJS_ENUM_VALUES, 0);
     if (array == NULL) {
         return NXT_ERROR;
     }
@@ -944,7 +963,7 @@ njs_object_entries(njs_vm_t *vm, njs_val
         return NXT_ERROR;
     }
 
-    array = njs_object_enumerate(vm, value, NJS_ENUM_BOTH, 0);
+    array = njs_value_own_enumerate(vm, value, NJS_ENUM_BOTH, 0);
     if (array == NULL) {
         return NXT_ERROR;
     }
@@ -957,69 +976,306 @@ njs_object_entries(njs_vm_t *vm, njs_val
 }
 
 
-njs_array_t *
-njs_object_enumerate(njs_vm_t *vm, const njs_value_t *value,
-    njs_object_enum_t kind, nxt_bool_t all)
+static njs_object_prop_t *
+njs_object_exist_in_proto(const njs_object_t *begin, const njs_object_t *end,
+    nxt_lvlhsh_query_t *lhq)
 {
-    u_char             *dst;
-    uint32_t           i, length, size, items_length, properties;
-    njs_value_t        *string, *item;
-    njs_array_t        *items, *array, *entry;
-    nxt_lvlhsh_t       *hash;
-    const u_char       *src, *end;
-    njs_object_t       *object;
+    nxt_int_t          ret;
     njs_object_prop_t  *prop;
-    njs_string_prop_t  string_prop;
-    nxt_lvlhsh_each_t  lhe;
-
-    array = NULL;
-    length = 0;
-    object = NULL;
-    items_length = 0;
-
-    switch (value->type) {
+
+    lhq->proto = &njs_object_hash_proto;
+
+    while (begin != end) {
+        ret = nxt_lvlhsh_find(&begin->hash, lhq);
+
+        if (nxt_fast_path(ret == NXT_OK)) {
+            prop = lhq->value;
+
+            if (prop->type == NJS_WHITEOUT) {
+                goto next;
+            }
+
+            return lhq->value;
+        }
+
+        ret = nxt_lvlhsh_find(&begin->shared_hash, lhq);
+
+        if (nxt_fast_path(ret == NXT_OK)) {
+            return lhq->value;
+        }
+
+next:
+
+        begin = begin->__proto__;
+    }
+
+    return NULL;
+}
+
+
+nxt_inline uint32_t
+njs_object_enumerate_length(const njs_object_t *object, nxt_bool_t all)
+{
+    uint32_t  length;
+
+    length = njs_object_enumerate_object_length(object, all);
+
+    switch (object->type) {
     case NJS_ARRAY:
-        array = value->data.u.array;
-        length = array->length;
-
-        for (i = 0; i < length; i++) {
-            if (njs_is_valid(&array->start[i])) {
-                items_length++;
-            }
-        }
-
+        length += njs_object_enumerate_array_length(object);
+        break;
+
+    case NJS_OBJECT_STRING:
+        length += njs_object_enumerate_string_length(object);
+        break;
+
+    default:
         break;
-
-    case NJS_STRING:
+    }
+
+    return length;
+}
+
+
+nxt_inline uint32_t
+njs_object_own_enumerate_length(const njs_object_t *object,
+    const njs_object_t *parent, nxt_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:
-        if (value->type == NJS_OBJECT_STRING) {
-            string = &value->data.u.object_value->value;
-
-        } else {
-            string = (njs_value_t *) value;
-            object = &vm->string_object;
-        }
-
-        length = njs_string_prop(&string_prop, string);
-        items_length += length;
-
+        length += njs_object_enumerate_string_length(object);
         break;
 
     default:
         break;
     }
 
-    /* GCC 4 and Clang 3 complain about uninitialized hash. */
-    hash = NULL;
-    properties = 0;
-
-    if (nxt_fast_path(njs_is_object(value))) {
-        object = value->data.u.object;
+    return length;
+}
+
+
+nxt_inline njs_ret_t
+njs_object_enumerate_value(njs_vm_t *vm, const njs_object_t *object,
+    njs_array_t *items, njs_object_enum_t kind, nxt_bool_t all)
+{
+    njs_ret_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 (nxt_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
+
+object:
+
+    ret = njs_object_enumerate_object(vm, object, items, kind, all);
+    if (nxt_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
+
+    return NJS_OK;
+}
+
+
+nxt_inline njs_ret_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,
+    nxt_bool_t all)
+{
+    njs_ret_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 (nxt_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
+
+object:
+
+    ret = njs_object_own_enumerate_object(vm, object, parent, items, kind, all);
+    if (nxt_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
+
+    return NJS_OK;
+}
+
+
+njs_array_t *
+njs_object_enumerate(njs_vm_t *vm, const njs_object_t *object,
+    njs_object_enum_t kind, nxt_bool_t all)
+{
+    uint32_t     length;
+    njs_ret_t    ret;
+    njs_array_t  *items;
+
+    length = njs_object_enumerate_length(object, all);
+
+    items = njs_array_alloc(vm, length, NJS_ARRAY_SPARE);
+    if (nxt_slow_path(items == NULL)) {
+        return NULL;
+    }
+
+    ret = njs_object_enumerate_value(vm, object, items, kind, all);
+    if (nxt_slow_path(ret != NJS_OK)) {
+        return NULL;
     }
 
-    if (object != NULL) {
+    items->start -= items->length;
+
+    return items;
+}
+
+
+njs_array_t *
+njs_object_own_enumerate(njs_vm_t *vm, const njs_object_t *object,
+    njs_object_enum_t kind, nxt_bool_t all)
+{
+    uint32_t     length;
+    njs_ret_t    ret;
+    njs_array_t  *items;
+
+    length = njs_object_own_enumerate_length(object, object, all);
+
+    items = njs_array_alloc(vm, length, NJS_ARRAY_SPARE);
+    if (nxt_slow_path(items == NULL)) {
+        return NULL;
+    }
+
+    ret = njs_object_own_enumerate_value(vm, object, object, items, kind, all);
+    if (nxt_slow_path(ret != NJS_OK)) {
+        return NULL;
+    }
+
+    items->start -= items->length;
+
+    return items;
+}
+
+
+static uint32_t
+njs_object_enumerate_array_length(const njs_object_t *object)
+{
+    uint32_t     i, length;
+    njs_array_t  *array;
+
+    length = 0;
+    array = (njs_array_t *) object;
+
+    for (i = 0; i < array->length; i++) {
+        if (njs_is_valid(&array->start[i])) {
+            length++;
+        }
+    }
+
+    return length;
+}
+
+
+static uint32_t
+njs_object_enumerate_string_length(const njs_object_t *object)
+{
+    njs_object_value_t  *obj_val;
+
+    obj_val = (njs_object_value_t *) object;
+
+    return njs_string_length(&obj_val->value);
+}
+
+
+static uint32_t
+njs_object_enumerate_object_length(const njs_object_t *object, nxt_bool_t all)
+{
+    uint32_t            length;
+    const njs_object_t  *ptr;
+
+    length = njs_object_own_enumerate_object_length(object, object, all);
+
+    for (ptr = object->__proto__; ptr != NULL; ptr = ptr->__proto__) {
+
+        length += njs_object_own_enumerate_length(ptr, object, all);
+    }
+
+    return length;
+}
+
+
+static uint32_t
+njs_object_own_enumerate_object_length(const njs_object_t *object,
+    const njs_object_t *parent, nxt_bool_t all)
+{
+    uint32_t            length;
+    nxt_int_t           ret;
+    nxt_lvlhsh_each_t   lhe;
+    njs_object_prop_t   *prop, *ext_prop;
+    nxt_lvlhsh_query_t  lhq;
+    const nxt_lvlhsh_t  *hash;
+
+    nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
+    hash = &object->hash;
+
+    length = 0;
+
+    for ( ;; ) {
+        prop = nxt_lvlhsh_each(hash, &lhe);
+
+        if (prop == NULL) {
+            break;
+        }
+
+        lhq.key_hash = lhe.key_hash;
+        njs_string_get(&prop->name, &lhq.key);
+
+        ext_prop = njs_object_exist_in_proto(parent, object, &lhq);
+
+        if (ext_prop == NULL && prop->type != NJS_WHITEOUT
+            && (prop->enumerable || all))
+        {
+            length++;
+        }
+    }
+
+    if (nxt_slow_path(all)) {
         nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
-        hash = &object->hash;
+        hash = &object->shared_hash;
 
         for ( ;; ) {
             prop = nxt_lvlhsh_each(hash, &lhe);
@@ -1028,8 +1284,271 @@ njs_object_enumerate(njs_vm_t *vm, const
                 break;
             }
 
-            if (prop->type != NJS_WHITEOUT && (prop->enumerable || all)) {
-                properties++;
+            lhq.key_hash = lhe.key_hash;
+            njs_string_get(&prop->name, &lhq.key);
+
+            lhq.proto = &njs_object_hash_proto;
+            ret = nxt_lvlhsh_find(&object->hash, &lhq);
+
+            if (ret != NXT_OK) {
+                ext_prop = njs_object_exist_in_proto(parent, object, &lhq);
+
+                if (ext_prop == NULL) {
+                    length++;
+                }
+            }
+        }
+    }
+
+    return length;
+}
+
+
+static njs_ret_t
+njs_object_enumerate_array(njs_vm_t *vm, const njs_array_t *array,
+    njs_array_t *items, njs_object_enum_t kind)
+{
+    uint32_t     i;
+    njs_value_t  *item;
+    njs_array_t  *entry;
+
+    item = items->start;
+
+    switch (kind) {
+    case NJS_ENUM_KEYS:
+        for (i = 0; i < array->length; i++) {
+            if (njs_is_valid(&array->start[i])) {
+                njs_uint32_to_string(item++, i);
+            }
+        }
+
+        break;
+
+    case NJS_ENUM_VALUES:
+        for (i = 0; i < array->length; i++) {
+            if (njs_is_valid(&array->start[i])) {
+                /* GC: retain. */
+                *item++ = array->start[i];
+            }
+        }
+
+        break;
+
+    case NJS_ENUM_BOTH:
+        for (i = 0; i < array->length; i++) {
+            if (njs_is_valid(&array->start[i])) {
+
+                entry = njs_array_alloc(vm, 2, 0);
+                if (nxt_slow_path(entry == NULL)) {
+                    return NJS_ERROR;
+                }
+
+                njs_uint32_to_string(&entry->start[0], i);
+
+                /* GC: retain. */
+                entry->start[1] = array->start[i];
+
+                item->data.u.array = entry;
+                item->type = NJS_ARRAY;
+                item->data.truth = 1;
+
+                item++;
+            }
+        }
+
+        break;
+    }
+
+    items->start = item;
+
+    return NJS_OK;
+}
+
+
+static njs_ret_t
+njs_object_enumerate_string(njs_vm_t *vm, const njs_value_t *value,
+    njs_array_t *items, njs_object_enum_t kind)
+{
+    u_char             *begin;
+    uint32_t           i, len, size;
+    njs_value_t        *item, *string;
+    njs_array_t        *entry;
+    const u_char       *src, *end;
+    njs_string_prop_t  str_prop;
+
+    item = items->start;
+    len = (uint32_t) njs_string_prop(&str_prop, value);
+
+    switch (kind) {
+    case NJS_ENUM_KEYS:
+        for (i = 0; i < len; i++) {
+            njs_uint32_to_string(item++, i);
+        }
+
+        break;
+
+    case NJS_ENUM_VALUES:
+        if (str_prop.size == (size_t) len) {
+            /* Byte or ASCII string. */
+
+            for (i = 0; i < len; i++) {
+                begin = njs_string_short_start(item);
+                *begin = str_prop.start[i];
+
+                njs_string_short_set(item, 1, 1);
+
+                item++;
+            }
+
+        } else {
+            /* UTF-8 string. */
+
+            src = str_prop.start;
+            end = src + str_prop.size;
+
+            do {
+                begin = (u_char *) src;
+                nxt_utf8_copy(njs_string_short_start(item), &src, end);
+                size = (uint32_t) (src - begin);
+
+                njs_string_short_set(item, size, 1);
+
+                item++;
+
+            } while (src != end);
+        }
+
+        break;
+
+    case NJS_ENUM_BOTH:
+        if (str_prop.size == (size_t) len) {
+            /* Byte or ASCII string. */
+
+            for (i = 0; i < len; i++) {
+
+                entry = njs_array_alloc(vm, 2, 0);
+                if (nxt_slow_path(entry == NULL)) {
+                    return NJS_ERROR;
+                }
+
+                njs_uint32_to_string(&entry->start[0], i);
+
+                string = &entry->start[1];
+
+                begin = njs_string_short_start(string);
+                *begin = str_prop.start[i];
+
+                njs_string_short_set(string, 1, 1);
+
+                item->data.u.array = entry;
+                item->type = NJS_ARRAY;
+                item->data.truth = 1;
+
+                item++;
+            }
+
+        } else {
+            /* UTF-8 string. */
+
+            src = str_prop.start;
+            end = src + str_prop.size;
+            i = 0;
+
+            do {
+                entry = njs_array_alloc(vm, 2, 0);
+                if (nxt_slow_path(entry == NULL)) {
+                    return NJS_ERROR;
+                }
+
+                njs_uint32_to_string(&entry->start[0], i++);
+
+                string = &entry->start[1];
+
+                begin = (u_char *) src;
+                nxt_utf8_copy(njs_string_short_start(string), &src, end);
+                size = (uint32_t) (src - begin);
+
+                njs_string_short_set(string, size, 1);
+
+                item->data.u.array = entry;
+                item->type = NJS_ARRAY;
+                item->data.truth = 1;
+
+                item++;
+
+            } while (src != end);
+        }
+
+        break;
+    }
+
+    items->start = item;
+
+    return NJS_OK;
+}
+
+
+static njs_ret_t
+njs_object_enumerate_object(njs_vm_t *vm, const njs_object_t *object,
+    njs_array_t *items, njs_object_enum_t kind, nxt_bool_t all)
+{
+    njs_ret_t           ret;
+    const njs_object_t  *ptr;
+
+    ret = njs_object_own_enumerate_object(vm, object, object, items, kind, all);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return NXT_ERROR;
+    }
+
+    for (ptr = object->__proto__; ptr != NULL; ptr = ptr->__proto__) {
+
+        ret = njs_object_own_enumerate_value(vm, ptr, object, items, kind,
+                                             all);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return NXT_ERROR;
+        }
+    }
+
+    return NJS_OK;
+}
+
+
+static njs_ret_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,
+    nxt_bool_t all)
+{
+    nxt_int_t           ret;
+    njs_value_t         *item;
+    njs_array_t         *entry;
+    nxt_lvlhsh_each_t   lhe;
+    njs_object_prop_t   *prop, *ext_prop;
+    nxt_lvlhsh_query_t  lhq;
+    const nxt_lvlhsh_t  *hash;
+
+    nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
+
+    item = items->start;
+    hash = &object->hash;
+
+    switch (kind) {
+    case NJS_ENUM_KEYS:
+        for ( ;; ) {
+            prop = nxt_lvlhsh_each(hash, &lhe);
+
+            if (prop == NULL) {
+                break;
+            }
+
+            lhq.key_hash = lhe.key_hash;
+            njs_string_get(&prop->name, &lhq.key);
+
+            ext_prop = njs_object_exist_in_proto(parent, object, &lhq);
+
+            if (ext_prop == NULL && prop->type != NJS_WHITEOUT
+                && (prop->enumerable || all))
+            {
+                njs_string_copy(item++, &prop->name);
             }
         }
 
@@ -1044,179 +1563,49 @@ njs_object_enumerate(njs_vm_t *vm, const
                     break;
                 }
 
-                properties++;
-            }
-        }
-
-        items_length += properties;
-    }
-
-    items = njs_array_alloc(vm, items_length, NJS_ARRAY_SPARE);
-    if (nxt_slow_path(items == NULL)) {
-        return NULL;
-    }
-
-    item = items->start;
-
-    if (array != NULL) {
-
-        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;
-
-        case NJS_ENUM_BOTH:
-            for (i = 0; i < length; i++) {
-                if (njs_is_valid(&array->start[i])) {
-                    entry = njs_array_alloc(vm, 2, 0);
-                    if (nxt_slow_path(entry == NULL)) {
-                        return NULL;
+                lhq.key_hash = lhe.key_hash;
+                njs_string_get(&prop->name, &lhq.key);
+
+                lhq.proto = &njs_object_hash_proto;
+                ret = nxt_lvlhsh_find(&object->hash, &lhq);
+
+                if (ret != NXT_OK) {
+                    ext_prop = njs_object_exist_in_proto(parent, object, &lhq);
+
+                    if (ext_prop == NULL) {
+                        njs_string_copy(item++, &prop->name);
                     }
-
-                    njs_uint32_to_string(&entry->start[0], i);
-
-                    /* GC: retain. */
-                    entry->start[1] = array->start[i];
-
-                    item->data.u.array = entry;
-                    item->type = NJS_ARRAY;
-                    item->data.truth = 1;
-
-                    item++;
                 }
             }
-
-            break;
         }
 
-    } 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;
+
+    case NJS_ENUM_VALUES:
+        for ( ;; ) {
+            prop = nxt_lvlhsh_each(hash, &lhe);
+
+            if (prop == NULL) {
+                break;
             }
 
-            break;
-
-        case NJS_ENUM_BOTH:
-            if (string_prop.size == (size_t) length) {
-                /* Byte or ASCII string. */
-
-                for (i = 0; i < length; i++) {
-                    entry = njs_array_alloc(vm, 2, 0);
-                    if (nxt_slow_path(entry == NULL)) {
-                        return NULL;
-                    }
-
-                    njs_uint32_to_string(&entry->start[0], i);
-
-                    string = &entry->start[1];
-
-                    dst = njs_string_short_start(string);
-                    dst[0] = string_prop.start[i];
-
-                    njs_string_short_set(string, 1, 1);
-
-                    item->data.u.array = entry;
-                    item->type = NJS_ARRAY;
-                    item->data.truth = 1;
-
-                    item++;
-                }
-
-            } else {
-                /* UTF-8 string. */
-
-                src = string_prop.start;
-                end = src + string_prop.size;
-                i = 0;
-
-                do {
-                    entry = njs_array_alloc(vm, 2, 0);
-                    if (nxt_slow_path(entry == NULL)) {
-                        return NULL;
-                    }
-
-                    njs_uint32_to_string(&entry->start[0], i++);
-
-                    string = &entry->start[1];
-
-                    dst = njs_string_short_start(string);
-                    dst = nxt_utf8_copy(dst, &src, end);
-                    size = dst - njs_string_short_start(value);
-
-                    njs_string_short_set(string, size, 1);
-
-                    item->data.u.array = entry;
-                    item->type = NJS_ARRAY;
-                    item->data.truth = 1;
-
-                    item++;
-
-                } while (src != end);
+            lhq.key_hash = lhe.key_hash;
+            njs_string_get(&prop->name, &lhq.key);
+
+            ext_prop = njs_object_exist_in_proto(parent, object, &lhq);
+
+            if (ext_prop == NULL && prop->type != NJS_WHITEOUT
+                && (prop->enumerable || all))
+            {
+                /* GC: retain. */
+                *item++ = prop->value;
             }
-
-            break;
         }
-    }
-
-    if (nxt_fast_path(properties != 0)) {
-        nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
-
-        hash = &object->hash;
-
-        switch (kind) {
-
-        case NJS_ENUM_KEYS:
+
+        if (nxt_slow_path(all)) {
+            nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
+            hash = &object->shared_hash;
+
             for ( ;; ) {
                 prop = nxt_lvlhsh_each(hash, &lhe);
 
@@ -1224,29 +1613,62 @@ njs_object_enumerate(njs_vm_t *vm, const
                     break;
                 }
 
-                if (prop->type != NJS_WHITEOUT && (prop->enumerable || all)) {
-                    njs_string_copy(item++, &prop->name);
+                lhq.key_hash = lhe.key_hash;
+                njs_string_get(&prop->name, &lhq.key);
+
+                lhq.proto = &njs_object_hash_proto;
+                ret = nxt_lvlhsh_find(&object->hash, &lhq);
+
+                if (ret != NXT_OK) {
+                    ext_prop = njs_object_exist_in_proto(parent, object, &lhq);
+
+                    if (ext_prop == NULL) {
+                        *item++ = prop->value;
+                    }
                 }
             }
-
-            if (nxt_slow_path(all)) {
-                nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
-                hash = &object->shared_hash;
-
-                for ( ;; ) {
-                    prop = nxt_lvlhsh_each(hash, &lhe);
-
-                    if (prop == NULL) {
-                        break;
-                    }
-
-                    njs_string_copy(item++, &prop->name);
+        }
+
+        break;
+
+    case NJS_ENUM_BOTH:
+        for ( ;; ) {
+            prop = nxt_lvlhsh_each(hash, &lhe);
+
+            if (prop == NULL) {
+                break;
+            }
+
+            lhq.key_hash = lhe.key_hash;
+            njs_string_get(&prop->name, &lhq.key);
+
+            ext_prop = njs_object_exist_in_proto(parent, object, &lhq);
+
+            if (ext_prop == NULL && prop->type != NJS_WHITEOUT
+                && (prop->enumerable || all))
+            {
+                entry = njs_array_alloc(vm, 2, 0);
+                if (nxt_slow_path(entry == NULL)) {
+                    return NJS_ERROR;
                 }
+
+                njs_string_copy(&entry->start[0], &prop->name);
+
+                /* GC: retain. */
+                entry->start[1] = prop->value;
+
+                item->data.u.array = entry;
+                item->type = NJS_ARRAY;
+                item->data.truth = 1;
+
+                item++;
             }
-
-            break;
-
-        case NJS_ENUM_VALUES:
+        }
+
+        if (nxt_slow_path(all)) {
+            nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
+            hash = &object->shared_hash;


More information about the nginx-devel mailing list