[njs] Introduced "global" and "globalThis" aliases to the global object.

Dmitry Volyntsev xeioex at nginx.com
Tue Jan 14 13:53:55 UTC 2020


details:   https://hg.nginx.org/njs/rev/b840b7af946e
branches:  
changeset: 1299:b840b7af946e
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Tue Jan 14 16:52:42 2020 +0300
description:
Introduced "global" and "globalThis" aliases to the global object.

diffstat:

 src/njs_builtin.c        |  62 ++++++++++++++++++++++++++++++++++++++
 src/njs_json.c           |  77 ++++++++++++++++++++++++++++++++++++++++++++++-
 src/njs_object.c         |  75 +++++++++++++++++++++++++++++++++++++++++-----
 src/njs_object_hash.h    |  24 ++++++++++++++
 src/test/njs_unit_test.c |  21 ++++++------
 5 files changed, 238 insertions(+), 21 deletions(-)

diffs (399 lines):

diff -r 6ff44c2312ac -r b840b7af946e src/njs_builtin.c
--- a/src/njs_builtin.c	Mon Jan 13 21:35:15 2020 +0300
+++ b/src/njs_builtin.c	Tue Jan 14 16:52:42 2020 +0300
@@ -837,6 +837,47 @@ njs_dump_value(njs_vm_t *vm, njs_value_t
 
 
 static njs_int_t
+njs_global_this_object(njs_vm_t *vm, njs_object_prop_t *self,
+    njs_value_t *global, njs_value_t *setval, njs_value_t *retval)
+{
+    njs_int_t           ret;
+    njs_object_prop_t   *prop;
+    njs_lvlhsh_query_t  lhq;
+
+    *retval = *global;
+
+    if (njs_slow_path(setval != NULL)) {
+        *retval = *setval;
+    }
+
+    prop = njs_object_prop_alloc(vm, &self->name, retval, 1);
+    if (njs_slow_path(prop == NULL)) {
+        return NJS_ERROR;
+    }
+
+    /* GC */
+
+    prop->value = *retval;
+    prop->enumerable = self->enumerable;
+
+    lhq.value = prop;
+    njs_string_get(&self->name, &lhq.key);
+    lhq.key_hash = self->value.data.magic32;
+    lhq.replace = 1;
+    lhq.pool = vm->mem_pool;
+    lhq.proto = &njs_object_hash_proto;
+
+    ret = njs_lvlhsh_insert(njs_object_hash(global), &lhq);
+    if (njs_slow_path(ret != NJS_OK)) {
+        njs_internal_error(vm, "lvlhsh insert/replace failed");
+        return NJS_ERROR;
+    }
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
 njs_top_level_object(njs_vm_t *vm, njs_object_prop_t *self,
     njs_value_t *global, njs_value_t *setval, njs_value_t *retval)
 {
@@ -938,6 +979,27 @@ static const njs_object_prop_t  njs_glob
         .configurable = 1,
     },
 
+    /* Global aliases. */
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("global"),
+        .value = njs_prop_handler2(njs_global_this_object, 0, NJS_GLOBAL_HASH),
+        .writable = 1,
+        .enumerable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("globalThis"),
+        .value = njs_prop_handler2(njs_global_this_object, 0,
+                                   NJS_GLOBAL_THIS_HASH),
+        .writable = 1,
+        .enumerable = 0,
+        .configurable = 1,
+    },
+
     /* Global constants. */
 
     {
diff -r 6ff44c2312ac -r b840b7af946e src/njs_json.c
--- a/src/njs_json.c	Mon Jan 13 21:35:15 2020 +0300
+++ b/src/njs_json.c	Tue Jan 14 16:52:42 2020 +0300
@@ -1934,6 +1934,49 @@ njs_dump_is_recursive(const njs_value_t 
 }
 
 
+njs_inline njs_int_t
+njs_dump_visit(njs_arr_t *list, const njs_value_t *value)
+{
+    njs_object_t  **p;
+
+    if (njs_is_object(value)) {
+        p = njs_arr_add(list);
+        if (njs_slow_path(p == NULL)) {
+            return NJS_ERROR;
+        }
+
+        *p = njs_object(value);
+    }
+
+    return NJS_OK;
+}
+
+
+njs_inline njs_int_t
+njs_dump_visited(njs_arr_t *list, const njs_value_t *value)
+{
+    njs_uint_t    items, n;
+    njs_object_t  **start, *obj;
+
+    if (!njs_is_object(value)) {
+        /* External. */
+        return 0;
+    }
+
+    start = list->start;
+    items = list->items;
+    obj = njs_object(value);
+
+    for (n = 0; n < items; n++) {
+        if (start[n] == obj) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+
 static const njs_value_t  string_get = njs_string("[Getter]");
 static const njs_value_t  string_set = njs_string("[Setter]");
 static const njs_value_t  string_get_set = njs_long_string("[Getter/Setter]");
@@ -1943,11 +1986,12 @@ njs_int_t
 njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, njs_value_t *value,
     njs_uint_t console, njs_uint_t indent)
 {
-    njs_int_t             i;
-    njs_int_t             ret;
+    njs_int_t             i, ret;
     njs_chb_t             chain;
     njs_str_t             str;
+    njs_arr_t             visited;
     njs_value_t           *key, *val, tag;
+    njs_object_t          **start;
     njs_json_state_t      *state;
     njs_string_prop_t     string;
     njs_object_prop_t     *prop;
@@ -1983,6 +2027,13 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_
         goto memory_error;
     }
 
+    start = njs_arr_init(vm->mem_pool, &visited, NULL, 8, sizeof(void *));
+    if (njs_slow_path(start == NULL)) {
+        goto memory_error;
+    }
+
+    (void) njs_dump_visit(&visited, value);
+
     for ( ;; ) {
         switch (state->type) {
         case NJS_JSON_OBJECT:
@@ -2076,6 +2127,16 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_
             }
 
             if (njs_dump_is_recursive(val)) {
+                if (njs_slow_path(njs_dump_visited(&visited, val))) {
+                    njs_chb_append_literal(&chain, "[Circular]");
+                    break;
+                }
+
+                ret = njs_dump_visit(&visited, val);
+                if (njs_slow_path(ret != NJS_OK)) {
+                    goto memory_error;
+                }
+
                 state = njs_json_push_stringify_state(vm, stringify, val);
                 if (njs_slow_path(state == NULL)) {
                     goto exception;
@@ -2132,6 +2193,16 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_
             val = &njs_array_start(&state->value)[state->index++];
 
             if (njs_dump_is_recursive(val)) {
+                if (njs_slow_path(njs_dump_visited(&visited, val))) {
+                    njs_chb_append_literal(&chain, "[Circular]");
+                    break;
+                }
+
+                ret = njs_dump_visit(&visited, val);
+                if (njs_slow_path(ret != NJS_OK)) {
+                    goto memory_error;
+                }
+
                 state = njs_json_push_stringify_state(vm, stringify, val);
                 if (njs_slow_path(state == NULL)) {
                     goto exception;
@@ -2155,6 +2226,8 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_
         }
     }
 
+    njs_arr_destroy(&visited);
+
 done:
 
     ret = njs_chb_join(&chain, &str);
diff -r 6ff44c2312ac -r b840b7af946e src/njs_object.c
--- a/src/njs_object.c	Mon Jan 13 21:35:15 2020 +0300
+++ b/src/njs_object.c	Tue Jan 14 16:52:42 2020 +0300
@@ -1215,18 +1215,61 @@ njs_object_own_enumerate_object(njs_vm_t
 }
 
 
+njs_inline njs_int_t
+njs_traverse_visit(njs_arr_t *list, const njs_value_t *value)
+{
+    njs_object_t  **p;
+
+    if (njs_is_object(value)) {
+        p = njs_arr_add(list);
+        if (njs_slow_path(p == NULL)) {
+            return NJS_ERROR;
+        }
+
+        *p = njs_object(value);
+    }
+
+    return NJS_OK;
+}
+
+
+njs_inline njs_int_t
+njs_traverse_visited(njs_arr_t *list, const njs_value_t *value)
+{
+    njs_uint_t    items, n;
+    njs_object_t  **start, *obj;
+
+    if (!njs_is_object(value)) {
+        /* External. */
+        return 0;
+    }
+
+    start = list->start;
+    items = list->items;
+    obj = njs_object(value);
+
+    for (n = 0; n < items; n++) {
+        if (start[n] == obj) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+
 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];
 
-    static const njs_str_t  constructor_key = njs_str("constructor");
-
     depth = 0;
 
     state[depth].prop = NULL;
@@ -1235,6 +1278,14 @@ njs_object_traverse(njs_vm_t *vm, njs_ob
     state[depth].hash = &object->shared_hash;
     njs_lvlhsh_each_init(&state[depth].lhe, &njs_object_hash_proto);
 
+    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);
+
     for ( ;; ) {
         prop = njs_lvlhsh_each(state[depth].hash, &state[depth].lhe);
 
@@ -1246,7 +1297,7 @@ njs_object_traverse(njs_vm_t *vm, njs_ob
             }
 
             if (depth == 0) {
-                return NJS_OK;
+                goto done;
             }
 
             depth--;
@@ -1273,12 +1324,12 @@ njs_object_traverse(njs_vm_t *vm, njs_ob
 
         njs_string_get(&prop->name, &name);
 
-        /*
-         * "constructor" properties make loops in object hierarchies.
-         * Object.prototype.constructor -> Object.
-         */
-
-        if (njs_is_object(&value) && !njs_strstr_eq(&name, &constructor_key)) {
+        if (njs_is_object(&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)) {
                 njs_type_error(vm, "njs_object_traverse() recursion limit:%d",
                                depth);
@@ -1293,6 +1344,12 @@ njs_object_traverse(njs_vm_t *vm, njs_ob
         }
 
     }
+
+done:
+
+    njs_arr_destroy(&visited);
+
+    return NJS_OK;
 }
 
 
diff -r 6ff44c2312ac -r b840b7af946e src/njs_object_hash.h
--- a/src/njs_object_hash.h	Mon Jan 13 21:35:15 2020 +0300
+++ b/src/njs_object_hash.h	Tue Jan 14 16:52:42 2020 +0300
@@ -178,6 +178,30 @@
         'g'), 'e'), 't')
 
 
+#define NJS_GLOBAL_HASH                                                       \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(NJS_DJB_HASH_INIT,                                       \
+        'g'), 'l'), 'o'), 'b'), 'a'), 'l')
+
+
+#define NJS_GLOBAL_THIS_HASH                                                  \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(NJS_DJB_HASH_INIT,                                       \
+        'g'), 'l'), 'o'), 'b'), 'a'), 'l'), 'T'), 'h'), 'i'), 's')
+
+
 #define NJS_FUNCTION_HASH                                                     \
     njs_djb_hash_add(                                                         \
     njs_djb_hash_add(                                                         \
diff -r 6ff44c2312ac -r b840b7af946e src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c	Mon Jan 13 21:35:15 2020 +0300
+++ b/src/test/njs_unit_test.c	Tue Jan 14 16:52:42 2020 +0300
@@ -10012,16 +10012,11 @@ static njs_unit_test_t  njs_test[] =
       njs_str("true") },
 
     { njs_str("Object.keys(this).sort()"),
-      njs_str("$r,$r2,$r3,njs,process") },
-
-    { njs_str("var r = njs.dump(this); "
-              "['$r', 'global', njs.version].every(v=>r.includes(v))"),
-      njs_str("true") },
-
-    { njs_str("var r = JSON.stringify(this); "
-              "['$r', njs.version].every(v=>r.includes(v))"),
-      njs_str("true") },
-
+      njs_str("$r,$r2,$r3,global,njs,process") },
+
+    { njs_str("[this, global, globalThis]"
+              ".every(v=> { var r = njs.dump(v); return ['$r', 'global', njs.version].every(v=>r.includes(v))})"),
+      njs_str("true") },
 
     { njs_str("this.a = 1; this.a"),
       njs_str("1") },
@@ -15414,6 +15409,12 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("njs.dump(njs) == `njs {version:'${njs.version}'}`"),
       njs_str("true") },
 
+    { njs_str("var a = []; a[0] = a; njs.dump(a)"),
+      njs_str("[[Circular]]") },
+
+    { njs_str("var a = [], b = [a];  a[0] = b; njs.dump(a)"),
+      njs_str("[[[Circular]]]") },
+
     { njs_str("njs.dump(-0)"),
       njs_str("-0") },
 


More information about the nginx-devel mailing list