Created
April 18, 2019 09:16
-
-
Save lexborisov/c2be8396bc662b8029f423b97ee43096 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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