From: Dmitry Volyntsev Date: Tue, 14 Jan 2020 13:52:42 +0000 (+0300) Subject: Introduced "global" and "globalThis" aliases to the global object. X-Git-Tag: 0.3.8~10 X-Git-Url: http://www.kaiwu.me/postgresql/commit/?a=commitdiff_plain;h=8a3460bc13ed3dde94e17ac3417857adee491a20;p=njs.git Introduced "global" and "globalThis" aliases to the global object. --- diff --git a/src/njs_builtin.c b/src/njs_builtin.c index e544dc16..0f075019 100644 --- a/src/njs_builtin.c +++ b/src/njs_builtin.c @@ -836,6 +836,47 @@ njs_dump_value(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, } +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_global_this_object_properties[] = .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 --git a/src/njs_json.c b/src/njs_json.c index 595a2b90..42955b9a 100644 --- a/src/njs_json.c +++ b/src/njs_json.c @@ -1934,6 +1934,49 @@ njs_dump_is_recursive(const njs_value_t *value) } +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_t *retval, njs_value_t *value, 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_t *retval, njs_value_t *value, } 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_t *retval, njs_value_t *value, 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_t *retval, njs_value_t *value, } } + njs_arr_destroy(&visited); + done: ret = njs_chb_join(&chain, &str); diff --git a/src/njs_object.c b/src/njs_object.c index 65a57e8e..b2e941df 100644 --- a/src/njs_object.c +++ b/src/njs_object.c @@ -1215,18 +1215,61 @@ njs_object_own_enumerate_object(njs_vm_t *vm, const njs_object_t *object, } +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_object_t *object, void *ctx, 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_object_t *object, void *ctx, } if (depth == 0) { - return NJS_OK; + goto done; } depth--; @@ -1273,12 +1324,12 @@ njs_object_traverse(njs_vm_t *vm, njs_object_t *object, void *ctx, njs_string_get(&prop->name, &name); - /* - * "constructor" properties make loops in object hierarchies. - * Object.prototype.constructor -> Object. - */ + 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 (njs_is_object(&value) && !njs_strstr_eq(&name, &constructor_key)) { 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_object_t *object, void *ctx, } } + +done: + + njs_arr_destroy(&visited); + + return NJS_OK; } diff --git a/src/njs_object_hash.h b/src/njs_object_hash.h index 2392bed2..ea5136cd 100644 --- a/src/njs_object_hash.h +++ b/src/njs_object_hash.h @@ -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 --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index 14264d20..a86eabe0 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -10012,17 +10012,12 @@ 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("$r,$r2,$r3,global,njs,process") }, - { njs_str("var r = njs.dump(this); " - "['$r', 'global', njs.version].every(v=>r.includes(v))"), + { 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("var r = JSON.stringify(this); " - "['$r', 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") },