]> git.kaiwu.me - njs.git/commitdiff
Handling non-object values in Object.keys().
authorDmitry Volyntsev <xeioex@nginx.com>
Thu, 15 Nov 2018 17:31:35 +0000 (20:31 +0300)
committerDmitry Volyntsev <xeioex@nginx.com>
Thu, 15 Nov 2018 17:31:35 +0000 (20:31 +0300)
This fixes #54 issue on Github.

njs/njs_object.c
njs/njs_object.h
njs/test/njs_unit_test.c

index 383ca5b3fc37bd6a8c1480b0ce404a7070e9befd..89d29996687298a43f4637a07251cc8f91d14514 100644 (file)
@@ -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));
 
@@ -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;
     }
 
@@ -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++;
+            }
         }
     }
 
@@ -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);
+            }
         }
     }
 
index 87d53a47ab78d9c7d7148045da5507a4d0f024c3..a7fbde0b84a3eaa1cdb36e88afe0af2c7b3d2b09 100644 (file)
@@ -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,
index 957ab7552e862755df808698ef1b33817ab8ba7a..ccd793be46a5651f50858eb918a1577c13dcd4fd 100644 (file)
@@ -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") },