]> git.kaiwu.me - njs.git/commitdiff
Added Object.getOwnPropertyNames().
authorArtem S. Povalyukhin <artem.povaluhin@gmail.com>
Tue, 26 Mar 2019 05:04:02 +0000 (08:04 +0300)
committerArtem S. Povalyukhin <artem.povaluhin@gmail.com>
Tue, 26 Mar 2019 05:04:02 +0000 (08:04 +0300)
This closes #4 issue on Github.

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

index 7c22b2446e7a2f30083f79234d09a3ac583d7dda..f62c3a92ec80aa7463782f7e13352098820208c9 100644 (file)
@@ -1099,7 +1099,7 @@ njs_json_push_parse_state(njs_vm_t *vm, njs_json_parse_t *parse,
         } else {
             state->type = NJS_JSON_OBJECT_START;
             state->prop_value = NULL;
-            state->keys = njs_object_enumerate(vm, value, NJS_ENUM_KEYS);
+            state->keys = njs_object_enumerate(vm, value, NJS_ENUM_KEYS, 0);
             if (state->keys == NULL) {
                 return NULL;
             }
@@ -1677,7 +1677,7 @@ njs_json_push_stringify_state(njs_vm_t *vm, njs_json_stringify_t *stringify,
                 state->keys = njs_extern_keys_array(vm, value->external.proto);
 
             } else {
-                state->keys = njs_object_enumerate(vm, value, NJS_ENUM_KEYS);
+                state->keys = njs_object_enumerate(vm, value, NJS_ENUM_KEYS, 0);
             }
 
             if (state->keys == NULL) {
index a627795605d2f27a12e7decedbaa8fba60715bad..b2f69fbbba4615d394f7f510c9ef06cf88c73609 100644 (file)
@@ -417,7 +417,7 @@ njs_object_property_query(njs_vm_t *vm, njs_property_query_t *pq,
     do {
         pq->prototype = proto;
 
-        /* length and other shared properties should be Own property */
+        /* TODO: length should be Own property */
 
         if (nxt_fast_path(!pq->own || proto == object)) {
             ret = nxt_lvlhsh_find(&proto->hash, &pq->lhq);
@@ -879,7 +879,7 @@ njs_object_keys(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
         return NXT_ERROR;
     }
 
-    keys = njs_object_enumerate(vm, value, NJS_ENUM_KEYS);
+    keys = njs_object_enumerate(vm, value, NJS_ENUM_KEYS, 0);
     if (keys == NULL) {
         return NXT_ERROR;
     }
@@ -908,7 +908,7 @@ njs_object_values(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
         return NXT_ERROR;
     }
 
-    array = njs_object_enumerate(vm, value, NJS_ENUM_VALUES);
+    array = njs_object_enumerate(vm, value, NJS_ENUM_VALUES, 0);
     if (array == NULL) {
         return NXT_ERROR;
     }
@@ -937,7 +937,7 @@ njs_object_entries(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
         return NXT_ERROR;
     }
 
-    array = njs_object_enumerate(vm, value, NJS_ENUM_BOTH);
+    array = njs_object_enumerate(vm, value, NJS_ENUM_BOTH, 0);
     if (array == NULL) {
         return NXT_ERROR;
     }
@@ -952,8 +952,9 @@ njs_object_entries(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
 
 njs_array_t *
 njs_object_enumerate(njs_vm_t *vm, const njs_value_t *value,
-    njs_object_enum_t kind)
+    njs_object_enum_t kind, nxt_bool_t all)
 {
+    nxt_bool_t         exotic_length;
     u_char             *dst;
     uint32_t           i, length, size, items_length, properties;
     njs_value_t        *string, *item;
@@ -964,6 +965,12 @@ njs_object_enumerate(njs_vm_t *vm, const njs_value_t *value,
     njs_string_prop_t  string_prop;
     nxt_lvlhsh_each_t  lhe;
 
+    static const njs_value_t  njs_string_length = njs_string("length");
+
+    /* TODO: "length" is in a shared_hash. */
+
+    exotic_length = 0;
+
     array = NULL;
     length = 0;
     items_length = 0;
@@ -979,6 +986,8 @@ njs_object_enumerate(njs_vm_t *vm, const njs_value_t *value,
             }
         }
 
+        exotic_length = all;
+
         break;
 
     case NJS_STRING:
@@ -992,8 +1001,15 @@ njs_object_enumerate(njs_vm_t *vm, const njs_value_t *value,
 
         length = njs_string_prop(&string_prop, string);
         items_length += length;
+        exotic_length = all;
+
         break;
 
+    case NJS_FUNCTION:
+        exotic_length = all && (value->data.u.function->native == 0);
+
+        /* Fall through. */
+
     default:
         break;
     }
@@ -1013,7 +1029,22 @@ njs_object_enumerate(njs_vm_t *vm, const njs_value_t *value,
                 break;
             }
 
-            if (prop->type != NJS_WHITEOUT && prop->enumerable) {
+            if (prop->type != NJS_WHITEOUT && (prop->enumerable || all)) {
+                properties++;
+            }
+        }
+
+        if (nxt_slow_path(all)) {
+            nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
+            hash = &value->data.u.object->shared_hash;
+
+            for ( ;; ) {
+                prop = nxt_lvlhsh_each(hash, &lhe);
+
+                if (prop == NULL) {
+                    break;
+                }
+
                 properties++;
             }
         }
@@ -1021,7 +1052,7 @@ njs_object_enumerate(njs_vm_t *vm, const njs_value_t *value,
         items_length += properties;
     }
 
-    items = njs_array_alloc(vm, items_length, NJS_ARRAY_SPARE);
+    items = njs_array_alloc(vm, items_length + exotic_length, NJS_ARRAY_SPARE);
     if (nxt_slow_path(items == NULL)) {
         return NULL;
     }
@@ -1179,12 +1210,17 @@ njs_object_enumerate(njs_vm_t *vm, const njs_value_t *value,
         }
     }
 
+    if (nxt_slow_path(exotic_length != 0)) {
+        *item++ = njs_string_length;
+    }
+
     if (nxt_fast_path(properties != 0)) {
         nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
 
         switch (kind) {
 
         case NJS_ENUM_KEYS:
+            hash = &value->data.u.object->hash;
             for ( ;; ) {
                 prop = nxt_lvlhsh_each(hash, &lhe);
 
@@ -1192,7 +1228,22 @@ njs_object_enumerate(njs_vm_t *vm, const njs_value_t *value,
                     break;
                 }
 
-                if (prop->type != NJS_WHITEOUT && prop->enumerable) {
+                if (prop->type != NJS_WHITEOUT && (prop->enumerable || all)) {
+                    njs_string_copy(item++, &prop->name);
+                }
+            }
+
+            if (nxt_slow_path(all)) {
+                nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
+                hash = &value->data.u.object->shared_hash;
+
+                for ( ;; ) {
+                    prop = nxt_lvlhsh_each(hash, &lhe);
+
+                    if (prop == NULL) {
+                        break;
+                    }
+
                     njs_string_copy(item++, &prop->name);
                 }
             }
@@ -1200,6 +1251,7 @@ njs_object_enumerate(njs_vm_t *vm, const njs_value_t *value,
             break;
 
         case NJS_ENUM_VALUES:
+            hash = &value->data.u.object->hash;
             for ( ;; ) {
                 prop = nxt_lvlhsh_each(hash, &lhe);
 
@@ -1720,6 +1772,35 @@ njs_object_get_own_property_descriptor(njs_vm_t *vm, njs_value_t *args,
 }
 
 
+static njs_ret_t
+njs_object_get_own_property_names(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    njs_array_t        *names;
+    const njs_value_t  *value;
+
+    value = njs_arg(args, nargs, 1);
+
+    if (njs_is_null_or_undefined(value)) {
+        njs_type_error(vm, "cannot convert %s argument to object",
+                       njs_type_string(value->type));
+
+        return NXT_ERROR;
+    }
+
+    names = njs_object_enumerate(vm, value, NJS_ENUM_KEYS, 1);
+    if (names == NULL) {
+        return NXT_ERROR;
+    }
+
+    vm->retval.data.u.array = names;
+    vm->retval.type = NJS_ARRAY;
+    vm->retval.data.truth = 1;
+
+    return NXT_OK;
+}
+
+
 static njs_ret_t
 njs_object_get_prototype_of(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
     njs_index_t unused)
@@ -2152,6 +2233,14 @@ static const njs_object_prop_t  njs_object_constructor_properties[] =
                                      NJS_STRING_ARG),
     },
 
+    /* Object.getOwnPropertyNames(). */
+    {
+        .type = NJS_METHOD,
+        .name = njs_long_string("getOwnPropertyNames"),
+        .value = njs_native_function(njs_object_get_own_property_names, 0,
+                                     NJS_SKIP_ARG, NJS_OBJECT_ARG),
+    },
+
     /* Object.getPrototypeOf(). */
     {
         .type = NJS_METHOD,
index 6c1c523956c92bcd1880cf28a4b10ae567de2302..c0ebb00b4142dcf4a07cd006f138cf29307c9f54 100644 (file)
@@ -18,7 +18,7 @@ typedef enum {
 
 
 typedef enum {
-    NJS_ENUM_KEYS = 0,
+    NJS_ENUM_KEYS,
     NJS_ENUM_VALUES,
     NJS_ENUM_BOTH,
 } njs_object_enum_t;
@@ -87,7 +87,7 @@ 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_object_enum_t kind);
+    njs_object_enum_t kind, nxt_bool_t all);
 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 c6e84c0d64403002c314c7248e2b79b32be0f877..870bd9c733b434a5eb6a34c801807ffce16d402e 100644 (file)
@@ -8673,6 +8673,41 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("var o = {}; o[void 0] = 'a'; Object.getOwnPropertyDescriptor(o, undefined).value"),
       nxt_string("a") },
 
+    { nxt_string("Object.getOwnPropertyNames()"),
+      nxt_string("TypeError: cannot convert undefined argument to object") },
+
+    { nxt_string("Array.isArray(Object.getOwnPropertyNames({}))"),
+      nxt_string("true") },
+
+    { nxt_string("Object.getOwnPropertyNames({a:1, b:1, c:1})"),
+      nxt_string("a,b,c") },
+
+    { nxt_string("Object.getOwnPropertyNames(Object.defineProperty({a:1}, 'b', {}))"),
+      nxt_string("a,b") },
+
+    { nxt_string("Object.getOwnPropertyNames(Object.defineProperty([], 'b', {}))"),
+      nxt_string("length,b") },
+
+    { nxt_string("Object.getOwnPropertyNames(Object.defineProperty(new String(), 'b', {}))"),
+      nxt_string("length,b") },
+
+    { nxt_string("Object.getOwnPropertyNames([1,2,3])"),
+      nxt_string("0,1,2,length") },
+
+    { nxt_string("Object.getOwnPropertyNames('abc')"),
+      nxt_string("0,1,2,length") },
+
+    { nxt_string("Object.getOwnPropertyNames(function() {})"),
+      nxt_string("length,prototype") },
+
+    { nxt_string("Object.getOwnPropertyNames(Array)"),
+      nxt_string("name,length,prototype,isArray,of") },
+
+#if 0
+    { nxt_string("Object.getOwnPropertyNames(Array.isArray)"),
+      nxt_string("length") },
+#endif
+
     { nxt_string("Object.defineProperty(Object.freeze({}), 'b', {})"),
       nxt_string("TypeError: object is not extensible") },