[njs] Refactored njs_object_traverse().

Dmitry Volyntsev xeioex at nginx.com
Thu Aug 19 16:18:54 UTC 2021


details:   https://hg.nginx.org/njs/rev/b0177571ce1d
branches:  
changeset: 1689:b0177571ce1d
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Thu Aug 19 16:17:19 2021 +0000
description:
Refactored njs_object_traverse().

The previous approach was inconsistent in treating shared properties
with other places (JSON.stringify()) where object is iterated.

diffstat:

 src/njs_builtin.c        |    1 +
 src/njs_object.c         |  109 +++++++++++++++++++++++++++-------------------
 src/njs_object.h         |    6 +-
 src/test/njs_unit_test.c |    3 +-
 test/njs_expect_test.exp |    6 +-
 5 files changed, 72 insertions(+), 53 deletions(-)

diffs (212 lines):

diff -r 0fb3ced41fdc -r b0177571ce1d src/njs_builtin.c
--- a/src/njs_builtin.c	Fri Aug 13 12:20:46 2021 +0000
+++ b/src/njs_builtin.c	Thu Aug 19 16:17:19 2021 +0000
@@ -470,6 +470,7 @@ njs_builtin_traverse(njs_vm_t *vm, njs_t
             }
         }
 
+        njs_assert(njs_is_string(&key));
         njs_string_get(&key, &name);
 
         if (njs_slow_path((p + name.length + 3) > end)) {
diff -r 0fb3ced41fdc -r b0177571ce1d src/njs_object.c
--- a/src/njs_object.c	Fri Aug 13 12:20:46 2021 +0000
+++ b/src/njs_object.c	Thu Aug 19 16:17:19 2021 +0000
@@ -1128,87 +1128,104 @@ 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          depth, ret;
-    njs_str_t          name;
-    njs_arr_t          visited;
-    njs_object_t       **start;
-    njs_value_t        value, obj;
-    njs_object_prop_t  *prop;
-    njs_traverse_t     state[NJS_TRAVERSE_MAX_DEPTH];
-
-    depth = 0;
-
-    state[depth].prop = NULL;
-    state[depth].parent = NULL;
-    state[depth].object = object;
-    state[depth].hash = &object->shared_hash;
-    njs_lvlhsh_each_init(&state[depth].lhe, &njs_object_hash_proto);
+    njs_int_t             ret;
+    njs_arr_t             visited;
+    njs_object_t          **start;
+    njs_value_t           value, *key;
+    njs_traverse_t        *s;
+    njs_object_prop_t     *prop;
+    njs_property_query_t  pq;
+    njs_traverse_t        state[NJS_TRAVERSE_MAX_DEPTH];
+
+    s = &state[0];
+    s->prop = NULL;
+    s->parent = NULL;
+    s->index = 0;
+    njs_set_object(&s->value, object);
+    s->keys = njs_value_own_enumerate(vm, &s->value, NJS_ENUM_KEYS,
+                                      NJS_ENUM_STRING | NJS_ENUM_SYMBOL, 1);
+    if (njs_slow_path(s->keys == NULL)) {
+        return NJS_ERROR;
+    }
 
     start = njs_arr_init(vm->mem_pool, &visited, NULL, 8, sizeof(void *));
     if (njs_slow_path(start == NULL)) {
         return NJS_ERROR;
     }
 
-    njs_set_object(&value, object);
-    (void) njs_traverse_visit(&visited, &value);
+    (void) njs_traverse_visit(&visited, &s->value);
 
     for ( ;; ) {
-        prop = njs_lvlhsh_each(state[depth].hash, &state[depth].lhe);
-
-        if (prop == NULL) {
-            if (state[depth].hash == &state[depth].object->shared_hash) {
-                state[depth].hash = &state[depth].object->hash;
-                njs_lvlhsh_each_init(&state[depth].lhe, &njs_object_hash_proto);
+
+        if (s->index >= s->keys->length) {
+            njs_array_destroy(vm, s->keys);
+            s->keys = NULL;
+
+            if (s == &state[0]) {
+                goto done;
+            }
+
+            s--;
+            continue;
+        }
+
+        njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0);
+        key = &s->keys->start[s->index++];
+
+        ret = njs_property_query(vm, &pq, &s->value, key);
+        if (njs_slow_path(ret != NJS_OK)) {
+            if (ret == NJS_DECLINED) {
                 continue;
             }
 
-            if (depth == 0) {
-                goto done;
-            }
-
-            depth--;
-            continue;
+            return NJS_ERROR;
         }
 
-        state[depth].prop = prop;
-
-        ret = cb(vm, &state[depth], ctx);
+        prop = pq.lhq.value;
+        s->prop = prop;
+
+        ret = cb(vm, s, ctx);
         if (njs_slow_path(ret != NJS_OK)) {
             return ret;
         }
 
-        value = prop->value;
+        njs_value_assign(&value, &prop->value);
 
         if (prop->type == NJS_PROPERTY_HANDLER) {
-            njs_set_object(&obj, state[depth].object);
-            ret = prop->value.data.u.prop_handler(vm, prop, &obj, NULL, &value);
+            ret = prop->value.data.u.prop_handler(vm, prop, &s->value, NULL,
+                                                  &value);
             if (njs_slow_path(ret == NJS_ERROR)) {
                 return ret;
 
             }
         }
 
-        njs_string_get(&prop->name, &name);
-
-        if (njs_is_object(&value) && !njs_traverse_visited(&visited, &value)) {
+        if (njs_is_object(&value)
+            && !njs_is_array(&value)
+            && !njs_traverse_visited(&visited, &value))
+        {
             ret = njs_traverse_visit(&visited, &value);
             if (njs_slow_path(ret != NJS_OK)) {
                 return NJS_ERROR;
             }
 
-            if (++depth > (NJS_TRAVERSE_MAX_DEPTH - 1)) {
+            if (s == &state[NJS_TRAVERSE_MAX_DEPTH - 1]) {
                 njs_type_error(vm, "njs_object_traverse() recursion limit:%d",
-                               depth);
+                               NJS_TRAVERSE_MAX_DEPTH);
                 return NJS_ERROR;
             }
 
-            state[depth].prop = NULL;
-            state[depth].parent = &state[depth - 1];
-            state[depth].object = njs_object(&value);
-            state[depth].hash = &njs_object(&value)->shared_hash;
-            njs_lvlhsh_each_init(&state[depth].lhe, &njs_object_hash_proto);
+            s++;
+            s->prop = NULL;
+            s->parent = &s[-1];
+            s->index = 0;
+            njs_value_assign(&s->value, &value);
+            s->keys = njs_value_own_enumerate(vm, &s->value, NJS_ENUM_KEYS,
+                                          NJS_ENUM_STRING | NJS_ENUM_SYMBOL, 1);
+            if (njs_slow_path(s->keys == NULL)) {
+                return NJS_ERROR;
+            }
         }
-
     }
 
 done:
diff -r 0fb3ced41fdc -r b0177571ce1d src/njs_object.h
--- a/src/njs_object.h	Fri Aug 13 12:20:46 2021 +0000
+++ b/src/njs_object.h	Thu Aug 19 16:17:19 2021 +0000
@@ -27,9 +27,9 @@ struct njs_traverse_s {
     struct njs_traverse_s      *parent;
     njs_object_prop_t          *prop;
 
-    njs_object_t               *object;
-    njs_lvlhsh_t               *hash;
-    njs_lvlhsh_each_t          lhe;
+    njs_value_t                value;
+    njs_array_t                *keys;
+    int64_t                    index;
 
 #define NJS_TRAVERSE_MAX_DEPTH 32
 };
diff -r 0fb3ced41fdc -r b0177571ce1d src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c	Fri Aug 13 12:20:46 2021 +0000
+++ b/src/test/njs_unit_test.c	Thu Aug 19 16:17:19 2021 +0000
@@ -21265,8 +21265,9 @@ static njs_unit_test_t  njs_shell_test[]
               "    at main (:1)\n") },
 
     { njs_str("$shared.method({}.a.a)" ENTER),
+    /* FIXME: at $shared.method (native) */
       njs_str("TypeError: cannot get property \"a\" of undefined\n"
-              "    at $shared.method (native)\n"
+              "    at $r.method (native)\n"
               "    at main (:1)\n") },
 
     { njs_str("new Function(\n\n@)" ENTER),
diff -r 0fb3ced41fdc -r b0177571ce1d test/njs_expect_test.exp
--- a/test/njs_expect_test.exp	Fri Aug 13 12:20:46 2021 +0000
+++ b/test/njs_expect_test.exp	Thu Aug 19 16:17:19 2021 +0000
@@ -121,10 +121,10 @@ njs_test {
 }
 
 njs_test {
-    {"Ma\t"
-     "Ma\a*th"}
+    {"JS\t"
+     "JS\a*ON"}
     {"\t\t"
-     "Math.abs*Math.atan2"}
+     "JSON.parse*JSON.stringify"}
 }
 
 # Global completions, no matches


More information about the nginx-devel mailing list