Skip to content

Instantly share code, notes, and snippets.

@lexborisov
Created April 18, 2019 09:16
Show Gist options
  • Save lexborisov/c2be8396bc662b8029f423b97ee43096 to your computer and use it in GitHub Desktop.
Save lexborisov/c2be8396bc662b8029f423b97ee43096 to your computer and use it in GitHub Desktop.
# HG changeset patch
# User Alexander Borisov <alexander.borisov@nginx.com>
# Date 1554739316 -10800
# Mon Apr 08 19:01:56 2019 +0300
# Node ID 58845872f31f680583ec9d4ccace6ec0a9a6b5ee
# Parent a88bf03264b43e939888db5eb5c75d903f3a9f65
Saving hash key to result for each() function in level hash.
diff -r a88bf03264b4 -r 58845872f31f nxt/nxt_lvlhsh.c
--- a/nxt/nxt_lvlhsh.c Tue Apr 16 18:34:57 2019 +0300
+++ b/nxt/nxt_lvlhsh.c Mon Apr 08 19:01:56 2019 +0300
@@ -842,6 +842,7 @@ nxt_lvlhsh_bucket_each(nxt_lvlhsh_each_t
} while (nxt_lvlhsh_free_entry(bucket));
value = nxt_lvlhsh_entry_value(bucket);
+ lhe->key_hash = nxt_lvlhsh_entry_key(bucket);
lhe->entries--;
diff -r a88bf03264b4 -r 58845872f31f nxt/nxt_lvlhsh.h
--- a/nxt/nxt_lvlhsh.h Tue Apr 16 18:34:57 2019 +0300
+++ b/nxt/nxt_lvlhsh.h Mon Apr 08 19:01:56 2019 +0300
@@ -171,6 +171,7 @@ typedef struct {
uint32_t current;
uint32_t entry;
uint32_t entries;
+ uint32_t key_hash;
} nxt_lvlhsh_each_t;
# HG changeset patch
# User Alexander Borisov <alexander.borisov@nginx.com>
# Date 1555510419 -10800
# Wed Apr 17 17:13:39 2019 +0300
# Node ID d13ce57bf9cd5cfbb817b5db36b1a72bec6c5c47
# Parent 58845872f31f680583ec9d4ccace6ec0a9a6b5ee
Added function to get length of the string.
Renamed function njs_string_length() to njs_string_eval_length().
diff -r 58845872f31f -r d13ce57bf9cd njs/njs_regexp.c
--- a/njs/njs_regexp.c Mon Apr 08 19:01:56 2019 +0300
+++ b/njs/njs_regexp.c Wed Apr 17 17:13:39 2019 +0300
@@ -722,7 +722,7 @@ njs_regexp_exec_result(njs_vm_t *vm, njs
start = &string[captures[n]];
size = captures[n + 1] - captures[n];
- length = njs_string_length(utf8, start, size);
+ length = njs_string_eval_length(utf8, start, size);
ret = njs_regexp_string_create(vm, &array->start[i], start, size,
length);
diff -r 58845872f31f -r d13ce57bf9cd njs/njs_string.c
--- a/njs/njs_string.c Mon Apr 08 19:01:56 2019 +0300
+++ b/njs/njs_string.c Wed Apr 17 17:13:39 2019 +0300
@@ -2691,7 +2691,7 @@ njs_string_match_multiple(njs_vm_t *vm,
size = captures[1] - captures[0];
- length = njs_string_length(utf8, start, size);
+ length = njs_string_eval_length(utf8, start, size);
ret = njs_string_new(vm, &array->start[array->length],
start, size, length);
@@ -2890,7 +2890,7 @@ njs_string_split_part_add(njs_vm_t *vm,
{
ssize_t length;
- length = njs_string_length(utf8, start, size);
+ length = njs_string_eval_length(utf8, start, size);
return njs_array_string_add(vm, array, start, size, length);
}
@@ -3133,7 +3133,7 @@ njs_string_replace_regexp_function(njs_v
size = captures[k + 1] - captures[k];
k += 2;
- length = njs_string_length(r->utf8, start, size);
+ length = njs_string_eval_length(r->utf8, start, size);
ret = njs_string_new(vm, &arguments[i], start, size, length);
if (nxt_slow_path(ret != NXT_OK)) {
@@ -3145,7 +3145,7 @@ njs_string_replace_regexp_function(njs_v
njs_value_number_set(&arguments[n + 1], captures[0]);
/* The whole string being examined. */
- length = njs_string_length(r->utf8, r->part[0].start, r->part[0].size);
+ length = njs_string_eval_length(r->utf8, r->part[0].start, r->part[0].size);
ret = njs_string_new(vm, &arguments[n + 2], r->part[0].start,
r->part[0].size, length);
diff -r 58845872f31f -r d13ce57bf9cd njs/njs_string.h
--- a/njs/njs_string.h Mon Apr 08 19:01:56 2019 +0300
+++ b/njs/njs_string.h Wed Apr 17 17:13:39 2019 +0300
@@ -100,7 +100,7 @@ typedef enum {
nxt_inline uint32_t
-njs_string_length(njs_utf8_t utf8, const u_char *start, size_t size)
+njs_string_eval_length(njs_utf8_t utf8, const u_char *start, size_t size)
{
ssize_t length;
@@ -121,6 +121,24 @@ njs_string_length(njs_utf8_t utf8, const
}
+nxt_inline uint32_t
+njs_string_length(njs_value_t *string)
+{
+ uint32_t length, size;
+
+ if (string->short_string.size != NJS_STRING_LONG) {
+ size = string->short_string.size;
+ length = string->short_string.length;
+
+ } else {
+ size = string->long_string.size;
+ length = string->long_string.data->length;
+ }
+
+ return (length == 0) ? size : length;
+}
+
+
njs_ret_t njs_string_set(njs_vm_t *vm, njs_value_t *value, const u_char *start,
uint32_t size);
u_char *njs_string_alloc(njs_vm_t *vm, njs_value_t *value, uint32_t size,
# HG changeset patch
# User Alexander Borisov <alexander.borisov@nginx.com>
# Date 1555511234 -10800
# Wed Apr 17 17:27:14 2019 +0300
# Node ID 49ec16c591ba014663aef740c5c93e44f47e74d9
# Parent d13ce57bf9cd5cfbb817b5db36b1a72bec6c5c47
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.
diff -r d13ce57bf9cd -r 49ec16c591ba 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 d13ce57bf9cd -r 49ec16c591ba 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;
+
for ( ;; ) {
prop = nxt_lvlhsh_each(hash, &lhe);
@@ -1254,46 +1676,42 @@ njs_object_enumerate(njs_vm_t *vm, const
break;
}
- if (prop->type != NJS_WHITEOUT && prop->enumerable) {
- /* GC: retain. */
- *item++ = prop->value;
+ 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) {
+ 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_BOTH:
- for ( ;; ) {
- prop = nxt_lvlhsh_each(hash, &lhe);
-
- if (prop == NULL) {
- break;
- }
-
- if (prop->type != NJS_WHITEOUT && prop->enumerable) {
- entry = njs_array_alloc(vm, 2, 0);
- if (nxt_slow_path(entry == NULL)) {
- return NULL;
- }
-
- 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;
}
+
+ break;
}
- return items;
+ items->start = item;
+
+ return NJS_OK;
}
@@ -1799,7 +2217,7 @@ njs_object_get_own_property_descriptors(
return NXT_ERROR;
}
- names = njs_object_enumerate(vm, value, NJS_ENUM_KEYS, 1);
+ names = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, 1);
if (nxt_slow_path(names == NULL)) {
return NXT_ERROR;
}
@@ -1862,7 +2280,7 @@ njs_object_get_own_property_names(njs_vm
return NXT_ERROR;
}
- names = njs_object_enumerate(vm, value, NJS_ENUM_KEYS, 1);
+ names = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, 1);
if (names == NULL) {
return NXT_ERROR;
}
diff -r d13ce57bf9cd -r 49ec16c591ba njs/njs_object.h
--- a/njs/njs_object.h Wed Apr 17 17:13:39 2019 +0300
+++ b/njs/njs_object.h Wed Apr 17 17:27:14 2019 +0300
@@ -17,13 +17,6 @@ typedef enum {
} njs_object_property_type_t;
-typedef enum {
- NJS_ENUM_KEYS,
- NJS_ENUM_VALUES,
- NJS_ENUM_BOTH,
-} njs_object_enum_t;
-
-
/*
* Attributes are generally used as Boolean values.
* The UNSET value is used internally only by njs_define_property().
@@ -86,7 +79,9 @@ njs_object_t *njs_object_alloc(njs_vm_t
njs_object_t *njs_object_value_copy(njs_vm_t *vm, njs_value_t *value);
njs_object_t *njs_object_value_alloc(njs_vm_t *vm, const njs_value_t *value,
nxt_uint_t type);
-njs_array_t *njs_object_enumerate(njs_vm_t *vm, const njs_value_t *value,
+njs_array_t *njs_object_enumerate(njs_vm_t *vm, const njs_object_t *object,
+ njs_object_enum_t kind, nxt_bool_t all);
+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);
njs_ret_t njs_value_property(njs_vm_t *vm, const njs_value_t *value,
const njs_value_t *property, njs_value_t *retval);
diff -r d13ce57bf9cd -r 49ec16c591ba njs/njs_vm.c
--- a/njs/njs_vm.c Wed Apr 17 17:13:39 2019 +0300
+++ b/njs/njs_vm.c Wed Apr 17 17:27:14 2019 +0300
@@ -3636,3 +3636,45 @@ njs_lvlhsh_free(void *data, void *p, siz
{
nxt_mp_free(data, p);
}
+
+
+njs_array_t *
+njs_value_enumerate(njs_vm_t *vm, const njs_value_t *value,
+ njs_object_enum_t kind, nxt_bool_t all)
+{
+ njs_object_value_t obj_val;
+
+ if (njs_is_object(value)) {
+ return njs_object_enumerate(vm, value->data.u.object, kind, all);
+ }
+
+ if (value->type != NJS_STRING) {
+ return njs_array_alloc(vm, 0, NJS_ARRAY_SPARE);
+ }
+
+ obj_val.object = vm->string_object;
+ obj_val.value = *value;
+
+ return njs_object_enumerate(vm, (njs_object_t *) &obj_val, kind, all);
+}
+
+
+njs_array_t *
+njs_value_own_enumerate(njs_vm_t *vm, const njs_value_t *value,
+ njs_object_enum_t kind, nxt_bool_t all)
+{
+ njs_object_value_t obj_val;
+
+ if (njs_is_object(value)) {
+ return njs_object_own_enumerate(vm, value->data.u.object, kind, all);
+ }
+
+ if (value->type != NJS_STRING) {
+ return njs_array_alloc(vm, 0, NJS_ARRAY_SPARE);
+ }
+
+ obj_val.object = vm->string_object;
+ obj_val.value = *value;
+
+ return njs_object_own_enumerate(vm, (njs_object_t *) &obj_val, kind, all);
+}
diff -r d13ce57bf9cd -r 49ec16c591ba njs/njs_vm.h
--- a/njs/njs_vm.h Wed Apr 17 17:13:39 2019 +0300
+++ b/njs/njs_vm.h Wed Apr 17 17:27:14 2019 +0300
@@ -1137,6 +1137,13 @@ struct njs_vm_shared_s {
};
+typedef enum {
+ NJS_ENUM_KEYS,
+ NJS_ENUM_VALUES,
+ NJS_ENUM_BOTH,
+} njs_object_enum_t;
+
+
nxt_int_t njs_vmcode_interpreter(njs_vm_t *vm);
void njs_value_retain(njs_value_t *value);
@@ -1295,6 +1302,10 @@ nxt_array_t *njs_vm_backtrace(njs_vm_t *
void *njs_lvlhsh_alloc(void *data, size_t size, nxt_uint_t nalloc);
void njs_lvlhsh_free(void *data, void *p, size_t size);
+njs_array_t * njs_value_enumerate(njs_vm_t *vm, const njs_value_t *value,
+ njs_object_enum_t kind, nxt_bool_t all);
+njs_array_t * njs_value_own_enumerate(njs_vm_t *vm, const njs_value_t *value,
+ njs_object_enum_t kind, nxt_bool_t all);
extern const njs_value_t njs_value_undefined;
extern const njs_value_t njs_value_null;
# HG changeset patch
# User Alexander Borisov <alexander.borisov@nginx.com>
# Date 1555513256 -10800
# Wed Apr 17 18:00:56 2019 +0300
# Node ID 7fe423a948530b6481470fc1457dd671f0e6ebd9
# Parent 49ec16c591ba014663aef740c5c93e44f47e74d9
Walking over prototypes chain during iteration over an object.
This closes #33 issue on Github.
diff -r 49ec16c591ba -r 7fe423a94853 njs/njs_vm.c
--- a/njs/njs_vm.c Wed Apr 17 17:27:14 2019 +0300
+++ b/njs/njs_vm.c Wed Apr 17 18:00:56 2019 +0300
@@ -10,8 +10,8 @@
struct njs_property_next_s {
- int32_t index;
- nxt_lvlhsh_each_t lhe;
+ uint32_t index;
+ njs_array_t *array;
};
@@ -764,23 +764,7 @@ njs_vmcode_property_foreach(njs_vm_t *vm
const njs_extern_t *ext_proto;
njs_vmcode_prop_foreach_t *code;
- if (njs_is_object(object)) {
- next = nxt_mp_alloc(vm->mem_pool, sizeof(njs_property_next_t));
- if (nxt_slow_path(next == NULL)) {
- njs_memory_error(vm);
- return NXT_ERROR;
- }
-
- vm->retval.data.u.next = next;
-
- nxt_lvlhsh_each_init(&next->lhe, &njs_object_hash_proto);
- next->index = -1;
-
- if (njs_is_array(object) && object->data.u.array->length != 0) {
- next->index = 0;
- }
-
- } else if (njs_is_external(object)) {
+ if (njs_is_external(object)) {
ext_proto = object->external.proto;
if (ext_proto->foreach != NULL) {
@@ -791,8 +775,27 @@ njs_vmcode_property_foreach(njs_vm_t *vm
return ret;
}
}
+
+ goto done;
}
+ next = nxt_mp_alloc(vm->mem_pool, sizeof(njs_property_next_t));
+ if (nxt_slow_path(next == NULL)) {
+ njs_memory_error(vm);
+ return NXT_ERROR;
+ }
+
+ next->index = 0;
+ next->array = njs_value_enumerate(vm, object, NJS_ENUM_KEYS, 0);
+ if (nxt_slow_path(next->array == NULL)) {
+ njs_memory_error(vm);
+ return NXT_ERROR;
+ }
+
+ vm->retval.data.u.next = next;
+
+done:
+
code = (njs_vmcode_prop_foreach_t *) vm->current;
return code->offset;
@@ -804,10 +807,7 @@ njs_vmcode_property_next(njs_vm_t *vm, n
{
void *obj;
njs_ret_t ret;
- nxt_uint_t n;
njs_value_t *retval;
- njs_array_t *array;
- njs_object_prop_t *prop;
njs_property_next_t *next;
const njs_extern_t *ext_proto;
njs_vmcode_prop_next_t *code;
@@ -815,42 +815,7 @@ njs_vmcode_property_next(njs_vm_t *vm, n
code = (njs_vmcode_prop_next_t *) vm->current;
retval = njs_vmcode_operand(vm, code->retval);
- if (njs_is_object(object)) {
- next = value->data.u.next;
-
- if (next->index >= 0) {
- array = object->data.u.array;
-
- while ((uint32_t) next->index < array->length) {
- n = next->index++;
-
- if (njs_is_valid(&array->start[n])) {
- njs_uint32_to_string(retval, n);
-
- return code->offset;
- }
- }
-
- next->index = -1;
- }
-
- for ( ;; ) {
- prop = nxt_lvlhsh_each(&object->data.u.object->hash, &next->lhe);
-
- if (prop == NULL) {
- break;
- }
-
- if (prop->type != NJS_WHITEOUT && prop->enumerable) {
- *retval = prop->name;
-
- return code->offset;
- }
- }
-
- nxt_mp_free(vm->mem_pool, next);
-
- } else if (njs_is_external(object)) {
+ if (njs_is_external(object)) {
ext_proto = object->external.proto;
if (ext_proto->next != NULL) {
@@ -868,8 +833,20 @@ njs_vmcode_property_next(njs_vm_t *vm, n
/* ret == NJS_DONE. */
}
+
+ return sizeof(njs_vmcode_prop_next_t);
}
+ next = value->data.u.next;
+
+ if (next->index < next->array->length) {
+ *retval = next->array->data[ next->index++ ];
+
+ return code->offset;
+ }
+
+ nxt_mp_free(vm->mem_pool, next);
+
return sizeof(njs_vmcode_prop_next_t);
}
diff -r 49ec16c591ba -r 7fe423a94853 njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c Wed Apr 17 17:27:14 2019 +0300
+++ b/njs/test/njs_unit_test.c Wed Apr 17 18:00:56 2019 +0300
@@ -7779,6 +7779,62 @@ static njs_unit_test_t njs_test[] =
{ nxt_string("Object.prototype.__proto__.f()"),
nxt_string("TypeError: cannot get property \"f\" of undefined") },
+ { nxt_string("var obj = Object.create(null); obj.one = 1;"
+ "var res = [];"
+ "for (var val in obj) res.push(val); res"),
+ nxt_string("one") },
+
+ { nxt_string("var o1 = Object.create(null); o1.one = 1;"
+ "var o2 = Object.create(o1); o2.two = 2;"
+ "var o3 = Object.create(o2); o3.three = 3;"
+ "var res = [];"
+ "for (var val in o3) res.push(val); res"),
+ nxt_string("three,two,one") },
+
+ { nxt_string("var o1 = Object.create(null); o1.one = 1;"
+ "var o2 = Object.create(o1);"
+ "var o3 = Object.create(o2); o3.three = 3;"
+ "var res = [];"
+ "for (var val in o3) res.push(val); res"),
+ nxt_string("three,one") },
+
+ { nxt_string("var o1 = Object.create(null); o1.one = 1;"
+ "var o2 = Object.create(o1);"
+ "var o3 = Object.create(o2);"
+ "var res = [];"
+ "for (var val in o3) res.push(val); res"),
+ nxt_string("one") },
+
+ { nxt_string("var o1 = Object.create(null); o1.one = 1;"
+ "var o2 = Object.create(o1); o2.two = 2;"
+ "var o3 = Object.create(o2); o3.three = 3;"
+ "o3.two = -2; o3.one = -1;"
+ "var res = [];"
+ "for (var val in o3) res.push(val); res"),
+ nxt_string("three,two,one") },
+
+ { nxt_string("var a = []; for(var p in 'abc') a.push(p); a"),
+ nxt_string("0,1,2") },
+
+ { nxt_string("var a = []; for(var p in Object('abc')) a.push(p); a"),
+ nxt_string("0,1,2") },
+
+ { nxt_string("var o = Object('abc'); var x = Object.create(o);"
+ "x.a = 1; x.b = 2;"
+ "var a = []; for(var p in x) a.push(p); a"),
+ nxt_string("a,b,0,1,2") },
+
+#if 0
+ /* TODO: No properties implementation for array type
+ * (enumerable, writable, configurable).
+ */
+
+ { nxt_string("var o = Object("abc"); var x = Object.create(o);"
+ "x['sd'] = 44; x[1] = 8; x[55] = 8;"
+ "Object.keys(x)"),
+ nxt_string("55,sd") },
+#endif
+
{ nxt_string("Object.prototype.toString.call(Object.prototype)"),
nxt_string("[object Object]") },
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment