Skip to content

Commit

Permalink
Handling non-object values in Object.keys().
Browse files Browse the repository at this point in the history
This fixes #54 issue on Github.
  • Loading branch information
xeioex committed Nov 15, 2018
1 parent f10575f commit 27756d6
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 39 deletions.
115 changes: 80 additions & 35 deletions njs/njs_object.c
Original file line number Diff line number Diff line change
Expand Up @@ -865,7 +865,7 @@ njs_object_keys(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,

value = njs_arg(args, nargs, 1);

if (!njs_is_object(value)) {
if (njs_is_null_or_void(value)) {
njs_type_error(vm, "cannot convert %s argument to object",
njs_type_string(value->type));

Expand All @@ -874,7 +874,6 @@ njs_object_keys(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,

keys = njs_object_keys_array(vm, value);
if (keys == NULL) {
njs_memory_error(vm);
return NXT_ERROR;
}

Expand All @@ -887,43 +886,69 @@ njs_object_keys(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,


njs_array_t *
njs_object_keys_array(njs_vm_t *vm, const njs_value_t *object)
njs_object_keys_array(njs_vm_t *vm, const njs_value_t *value)
{
uint32_t i, n, keys_length, array_length;
njs_value_t *value;
uint32_t i, n, length, keys_length;
njs_value_t *string;
njs_array_t *keys, *array;
nxt_lvlhsh_t *hash;
njs_object_prop_t *prop;
njs_string_prop_t string_prop;
nxt_lvlhsh_each_t lhe;

array = NULL;
length = 0;
keys_length = 0;
array_length = 0;

if (njs_is_array(object)) {
array = object->data.u.array;
array_length = array->length;
switch (value->type) {
case NJS_ARRAY:
array = value->data.u.array;
length = array->length;

for (i = 0; i < array_length; i++) {
for (i = 0; i < length; i++) {
if (njs_is_valid(&array->start[i])) {
keys_length++;
}
}

break;

case NJS_STRING:
case NJS_OBJECT_STRING:
if (value->type == NJS_OBJECT_STRING) {
string = &value->data.u.object_value->value;

} else {
string = (njs_value_t *) value;
}

length = njs_string_prop(&string_prop, string);
keys_length += length;
break;

default:
break;
}

nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
if (nxt_fast_path(njs_is_object(value))) {
nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
hash = &value->data.u.object->hash;

hash = &object->data.u.object->hash;
} else {
hash = NULL;
}

for ( ;; ) {
prop = nxt_lvlhsh_each(hash, &lhe);
if (nxt_fast_path(hash != NULL)) {
for ( ;; ) {
prop = nxt_lvlhsh_each(hash, &lhe);

if (prop == NULL) {
break;
}
if (prop == NULL) {
break;
}

if (prop->type != NJS_WHITEOUT && prop->enumerable) {
keys_length++;
if (prop->type != NJS_WHITEOUT && prop->enumerable) {
keys_length++;
}
}
}

Expand All @@ -934,28 +959,48 @@ njs_object_keys_array(njs_vm_t *vm, const njs_value_t *object)

n = 0;

for (i = 0; i < array_length; i++) {
if (njs_is_valid(&array->start[i])) {
value = &keys->start[n++];
/*
* The maximum array index is 4294967294, so
* it can be stored as a short string inside value.
*/
njs_uint32_to_string(value, i);
switch (value->type) {
case NJS_ARRAY:
for (i = 0; i < length; i++) {
if (njs_is_valid(&array->start[i])) {
njs_uint32_to_string(&keys->start[n++], i);
}
}
}

nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
break;

for ( ;; ) {
prop = nxt_lvlhsh_each(hash, &lhe);
case NJS_STRING:
case NJS_OBJECT_STRING:
if (value->type == NJS_OBJECT_STRING) {
string = &value->data.u.object_value->value;

if (prop == NULL) {
break;
} else {
string = (njs_value_t *) value;
}

if (prop->type != NJS_WHITEOUT && prop->enumerable) {
njs_string_copy(&keys->start[n++], &prop->name);
for (i = 0; i < length; i++) {
njs_uint32_to_string(&keys->start[n++], i);
}

break;

default:
break;
}

if (nxt_fast_path(hash != NULL)) {
nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);

for ( ;; ) {
prop = nxt_lvlhsh_each(hash, &lhe);

if (prop == NULL) {
break;
}

if (prop->type != NJS_WHITEOUT && prop->enumerable) {
njs_string_copy(&keys->start[n++], &prop->name);
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion njs/njs_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ njs_object_t *njs_object_alloc(njs_vm_t *vm);
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_keys_array(njs_vm_t *vm, const njs_value_t *object);
njs_array_t *njs_object_keys_array(njs_vm_t *vm, const njs_value_t *value);
njs_ret_t njs_value_property(njs_vm_t *vm, njs_value_t *value,
const njs_value_t *property, njs_value_t *retval);
njs_object_prop_t *njs_object_property(njs_vm_t *vm, const njs_object_t *obj,
Expand Down
21 changes: 18 additions & 3 deletions njs/test/njs_unit_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -7236,11 +7236,26 @@ static njs_unit_test_t njs_test[] =
{ nxt_string("Object.keys()"),
nxt_string("TypeError: cannot convert undefined argument to object") },

{ nxt_string("Object.keys('a')"),
nxt_string("TypeError: cannot convert string argument to object") },
{ nxt_string("Object.keys('αβZ')"),
nxt_string("0,1,2") },

{ nxt_string("Object.keys(new String('αβZ'))"),
nxt_string("0,1,2") },

{ nxt_string("var s = new String('αβZ'); s.a = 1; Object.keys(s)"),
nxt_string("0,1,2,a") },

{ nxt_string("var r = new RegExp('αbc'); r.a = 1; Object.keys(r)"),
nxt_string("a") },

{ nxt_string("Object.keys(Object.create(new String('abc')))"),
nxt_string("") },

{ nxt_string("Object.keys(1)"),
nxt_string("TypeError: cannot convert number argument to object") },
nxt_string("") },

{ nxt_string("Object.keys(true)"),
nxt_string("") },

{ nxt_string("var o = {}; Object.defineProperty(o, 'a', {}); o.a"),
nxt_string("undefined") },
Expand Down

0 comments on commit 27756d6

Please sign in to comment.