[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