Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Object.getOwnPropertyDescriptor handles missing arguments incorrectly #5

Closed
drsm opened this issue Apr 19, 2018 · 6 comments
Closed
Assignees
Labels
Milestone

Comments

@drsm
Copy link
Contributor

drsm commented Apr 19, 2018

>> Object.getOwnPropertyDescriptor([])
TypeError: cannot convert array argument to object
    at Object.getOwnPropertyDescriptor (native)
    at main (native)

>> Object.getOwnPropertyDescriptor({})
TypeError: cannot convert object argument to object
    at Object.getOwnPropertyDescriptor (native)
    at main (native)
>> Object.getOwnPropertyDescriptor({}, undefined)
undefined
@xeioex
Copy link
Contributor

xeioex commented Apr 20, 2018

Please, try the patch below

# HG changeset patch
# User Dmitry Volyntsev <xeioex@nginx.com>
# Date 1524235519 -10800
#      Fri Apr 20 17:45:19 2018 +0300
# Node ID c482744a7bd96f26cb9ef03b43a19b3383ce1eaa
# Parent  7f75ccba396ee3a16b10dbce63579759907cbc1d
Fixed handling of missing arg of Object.getOwnPropertyDescriptor().

This fixes #5 issue on GitHub.

diff --git a/njs/njs_object.c b/njs/njs_object.c
--- a/njs/njs_object.c
+++ b/njs/njs_object.c
@@ -606,17 +606,20 @@ njs_object_get_own_property_descriptor(n
 {
     uint32_t            index;
     nxt_int_t           ret;
-    const char          *type;
     njs_array_t         *array;
     njs_object_t        *descriptor;
     njs_object_prop_t   *pr, *prop, array_prop;
     const njs_value_t   *value;
     nxt_lvlhsh_query_t  lhq;
 
+    if (nargs < 2 || njs_is_null_or_void(&args[1])) {
+        njs_type_error(vm, "cannot convert null argument to object", NULL);
+        return NXT_ERROR;
+    }
+
     if (nargs < 3 || !njs_is_object(&args[1])) {
-        type = (nargs > 1) ? njs_type_string(args[1].type) : "null";
-        njs_type_error(vm, "cannot convert %s argument to object", type);
-        return NXT_ERROR;
+        vm->retval = njs_value_void;
+        return NXT_OK;
     }
 
     prop = NULL;
diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c
+++ b/njs/test/njs_unit_test.c
@@ -6594,7 +6594,13 @@ static njs_unit_test_t  njs_test[] =
       nxt_string("undefined") },
 
     { nxt_string("Object.getOwnPropertyDescriptor(1, '0')"),
-      nxt_string("TypeError: cannot convert number argument to object") },
+      nxt_string("undefined") },
+
+    { nxt_string("Object.getOwnPropertyDescriptor()"),
+      nxt_string("TypeError: cannot convert null argument to object") },
+
+    { nxt_string("Object.getOwnPropertyDescriptor(undefined)"),
+      nxt_string("TypeError: cannot convert void to object") },
 
     { nxt_string("Object.defineProperty(Object.freeze({}), 'b', {})"),
       nxt_string("TypeError: object is not extensible") },

@xeioex xeioex added this to the 0.2.1 milestone Apr 20, 2018
@drsm
Copy link
Contributor Author

drsm commented Apr 20, 2018

the patch resolves original problem partially, but i'm sorry, i think it is wrong.

  1. this tests are the same actually
+    { nxt_string("Object.getOwnPropertyDescriptor()"),
+      nxt_string("TypeError: cannot convert null argument to object") },
+
+    { nxt_string("Object.getOwnPropertyDescriptor(undefined)"),
+      nxt_string("TypeError: cannot convert void to object") },

https://www.ecma-international.org/ecma-262/6.0/#sec-undefined-value

4.3.10 undefined value
primitive value used when a variable has not been assigned a value
4.3.12 null value
primitive value that represents the intentional absence of any object value

  1. the problem referenced in semantic equivalence of f() <=> f(undefined) and so on #7 arises here:
>> var x = {}
undefined
>> x[void 0] = 'test'
test
>> JSON.stringify(Object.getOwnPropertyDescriptor(x, void 0))
{"value":"test","configurable":"true","enumerable":"true","writable":"true"}
>> JSON.stringify(Object.getOwnPropertyDescriptor(x))
undefined

same code on nodejs:

> var x = {};
undefined
> x[void 0] = 'test'
'test'
> Object.getOwnPropertyDescriptor(x)
{ value: 'test',
  writable: true,
  enumerable: true,
  configurable: true }
> Object.getOwnPropertyDescriptor(x, void 0)
{ value: 'test',
  writable: true,
  enumerable: true,
  configurable: true }

@xeioex
Copy link
Contributor

xeioex commented Apr 23, 2018

the second try (the patch also fixes #6 issue and partially #7 issue):

# HG changeset patch
# User Dmitry Volyntsev <xeioex@nginx.com>
# Date 1524491304 -10800
#      Mon Apr 23 16:48:24 2018 +0300
# Node ID a65dbae9b0216c5b88895f429853d46451600c81
# Parent  7f75ccba396ee3a16b10dbce63579759907cbc1d
Fixed handling of undefined arguments of functions.

diff --git a/njs/njs_array.c b/njs/njs_array.c
--- a/njs/njs_array.c
+++ b/njs/njs_array.c
@@ -1628,7 +1628,7 @@ njs_array_prototype_find_apply(njs_vm_t 
 
     /* GC: array elt, array */
 
-    value = (nargs > 2) ? &args[2] : &njs_value_void;
+    value = njs_arg(args, nargs, 2);
     arguments[0] = *value;
 
     n = iter->index;
@@ -1847,7 +1847,7 @@ njs_array_iterator_apply(njs_vm_t *vm, n
 
     /* GC: array elt, array */
 
-    value = (nargs > 2) ? &args[2] : &njs_value_void;
+    value = njs_arg(args, nargs, 2);
     arguments[0] = *value;
 
     n = iter->index;
diff --git a/njs/njs_function.h b/njs/njs_function.h
--- a/njs/njs_function.h
+++ b/njs/njs_function.h
@@ -40,6 +40,9 @@ struct njs_function_lambda_s {
 };
 
 
+#define njs_arg(args, nargs, n)                                               \
+    ((n < nargs) ? &(args)[n] : &njs_value_void)
+
 /* The frame size must be aligned to njs_value_t. */
 #define NJS_NATIVE_FRAME_SIZE                                                 \
     nxt_align_size(sizeof(njs_native_frame_t), sizeof(njs_value_t))
diff --git a/njs/njs_object.c b/njs/njs_object.c
--- a/njs/njs_object.c
+++ b/njs/njs_object.c
@@ -232,13 +232,14 @@ njs_ret_t
 njs_object_constructor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
     njs_index_t unused)
 {
-    nxt_uint_t    type;
-    njs_value_t   *value;
-    njs_object_t  *object;
+    nxt_uint_t         type;
+    njs_object_t       *object;
+    const njs_value_t  *value;
 
     type = NJS_OBJECT;
+    value = njs_arg(args, nargs, 1);
 
-    if (nargs == 1 || njs_is_null_or_void(&args[1])) {
+    if (njs_is_null_or_void(value)) {
 
         object = njs_object_alloc(vm);
         if (nxt_slow_path(object == NULL)) {
@@ -246,7 +247,6 @@ njs_object_constructor(njs_vm_t *vm, njs
         }
 
     } else {
-        value = &args[1];
 
         if (njs_is_object(value)) {
             object = value->data.u.object;
@@ -283,20 +283,23 @@ static njs_ret_t
 njs_object_create(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
     njs_index_t unused)
 {
-    njs_object_t  *object;
+    njs_object_t       *object;
+    const njs_value_t  *obj;
+
+    obj = njs_arg(args, nargs, 1);
 
     if (nargs > 1) {
 
-        if (njs_is_object(&args[1]) || njs_is_null(&args[1])) {
+        if (njs_is_object(obj) || njs_is_null(obj)) {
 
             object = njs_object_alloc(vm);
             if (nxt_slow_path(object == NULL)) {
                 return NXT_ERROR;
             }
 
-            if (!njs_is_null(&args[1])) {
+            if (!njs_is_null(obj)) {
                 /* GC */
-                object->__proto__ = args[1].data.u.object;
+                object->__proto__ = obj->data.u.object;
 
             } else {
                 object->__proto__ = NULL;
@@ -320,11 +323,14 @@ static njs_ret_t
 njs_object_keys(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
     njs_index_t unused)
 {
-    njs_array_t  *keys;
+    njs_array_t        *keys;
+    const njs_value_t  *obj;
 
-    if (nargs < 2 || !njs_is_object(&args[1])) {
+    obj = njs_arg(args, nargs, 1);
+
+    if (!njs_is_object(obj)) {
         njs_type_error(vm, "cannot convert %s argument to object",
-                       (nargs >= 2) ? njs_type_string(args[1].type) : "null");
+                       njs_type_string(obj->type));
 
         return NXT_ERROR;
     }
@@ -427,12 +433,15 @@ njs_object_define_property(njs_vm_t *vm,
     njs_index_t unused)
 {
     nxt_int_t   ret;
-    const char  *type;
+    const njs_value_t  *obj, *name, *descriptor;
 
-    if (nargs < 4 || !njs_is_object(&args[1]) || !njs_is_object(&args[3])) {
-        if (nargs < 2 || !njs_is_object(&args[1])) {
-            type = (nargs > 1) ? njs_type_string(args[1].type) : "null";
-            njs_type_error(vm, "cannot convert %s argument to object", type);
+    obj = njs_arg(args, nargs, 1);
+    descriptor = njs_arg(args, nargs, 3);
+
+    if (!njs_is_object(obj) || !njs_is_object(descriptor)) {
+        if (!njs_is_object(obj)) {
+            njs_type_error(vm, "cannot convert %s argument to object",
+                           njs_type_string(obj->type));
 
         } else {
             njs_type_error(vm, "descriptor is not an object", NULL);
@@ -441,19 +450,21 @@ njs_object_define_property(njs_vm_t *vm,
         return NXT_ERROR;
     }
 
-    if (!args[1].data.u.object->extensible) {
+    if (!obj->data.u.object->extensible) {
         njs_type_error(vm, "object is not extensible", NULL);
         return NXT_ERROR;
     }
 
-    ret = njs_define_property(vm, args[1].data.u.object, &args[2],
-                              args[3].data.u.object);
+    name = njs_arg(args, nargs, 2);
+
+    ret = njs_define_property(vm, obj->data.u.object, (njs_value_t *) name,
+                              descriptor->data.u.object);
 
     if (nxt_slow_path(ret != NXT_OK)) {
         return NXT_ERROR;
     }
 
-    vm->retval = args[1];
+    vm->retval = *obj;
 
     return NXT_OK;
 }
@@ -464,16 +475,19 @@ njs_object_define_properties(njs_vm_t *v
     njs_index_t unused)
 {
     nxt_int_t          ret;
-    const char         *type;
     nxt_lvlhsh_t       *hash;
     njs_object_t       *object;
     nxt_lvlhsh_each_t  lhe;
     njs_object_prop_t  *prop;
+    const njs_value_t  *obj, *descriptor;
 
-    if (nargs < 3 || !njs_is_object(&args[1]) || !njs_is_object(&args[2])) {
-        if (nargs < 2 || !njs_is_object(&args[1])) {
-            type = (nargs > 1) ? njs_type_string(args[1].type) : "null";
-            njs_type_error(vm, "cannot convert %s argument to object", type);
+    obj = njs_arg(args, nargs, 1);
+    descriptor = njs_arg(args, nargs, 2);
+
+    if (!njs_is_object(obj) || !njs_is_object(descriptor)) {
+        if (!njs_is_object(obj)) {
+            njs_type_error(vm, "cannot convert %s argument to object",
+                           njs_type_string(obj->type));
 
         } else {
             njs_type_error(vm, "descriptor is not an object", NULL);
@@ -482,15 +496,15 @@ njs_object_define_properties(njs_vm_t *v
         return NXT_ERROR;
     }
 
-    if (!args[1].data.u.object->extensible) {
+    if (!obj->data.u.object->extensible) {
         njs_type_error(vm, "object is not extensible", NULL);
         return NXT_ERROR;
     }
 
     nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
 
-    object = args[1].data.u.object;
-    hash = &args[2].data.u.object->hash;
+    object = obj->data.u.object;
+    hash = &descriptor->data.u.object->hash;
 
     for ( ;; ) {
         prop = nxt_lvlhsh_each(hash, &lhe);
@@ -509,7 +523,7 @@ njs_object_define_properties(njs_vm_t *v
         }
     }
 
-    vm->retval = args[1];
+    vm->retval = *obj;
 
     return NXT_OK;
 }
@@ -606,29 +620,31 @@ njs_object_get_own_property_descriptor(n
 {
     uint32_t            index;
     nxt_int_t           ret;
-    const char          *type;
     njs_array_t         *array;
     njs_object_t        *descriptor;
     njs_object_prop_t   *pr, *prop, array_prop;
-    const njs_value_t   *value;
+    const njs_value_t   *value, *obj, *property;
     nxt_lvlhsh_query_t  lhq;
 
-    if (nargs < 3 || !njs_is_object(&args[1])) {
-        type = (nargs > 1) ? njs_type_string(args[1].type) : "null";
-        njs_type_error(vm, "cannot convert %s argument to object", type);
+    obj = njs_arg(args, nargs, 1);
+
+    if (!njs_is_object(obj)) {
+        njs_type_error(vm, "cannot convert %s argument to object",
+                       njs_type_string(obj->type));
         return NXT_ERROR;
     }
 
     prop = NULL;
+    property = njs_arg(args, nargs, 2);
 
-    if (njs_is_array(&args[1])) {
-        array = args[1].data.u.array;
-        index = njs_string_to_index(&args[2]);
+    if (njs_is_array(obj)) {
+        array = obj->data.u.array;
+        index = njs_string_to_index((njs_value_t *) property);
 
         if (index < array->length && njs_is_valid(&array->start[index])) {
             prop = &array_prop;
 
-            array_prop.name = args[2];
+            array_prop.name = *property;
             array_prop.value = array->start[index];
 
             array_prop.configurable = 1;
@@ -640,10 +656,10 @@ njs_object_get_own_property_descriptor(n
     lhq.proto = &njs_object_hash_proto;
 
     if (prop == NULL) {
-        njs_string_get(&args[2], &lhq.key);
+        njs_string_get(property, &lhq.key);
         lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
 
-        ret = nxt_lvlhsh_find(&args[1].data.u.object->hash, &lhq);
+        ret = nxt_lvlhsh_find(&obj->data.u.object->hash, &lhq);
 
         if (ret != NXT_OK) {
             vm->retval = njs_string_void;
@@ -739,13 +755,19 @@ 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)
 {
-    if (nargs > 1 && njs_is_object(&args[1])) {
-        njs_object_prototype_get_proto(vm, &args[1], NULL, &vm->retval);
+    const njs_value_t  *value;
+
+    value = njs_arg(args, nargs, 1);
+
+    if (njs_is_object(value)) {
+        njs_object_prototype_get_proto(vm, (njs_value_t *) value, NULL,
+                                       &vm->retval);
         return NXT_OK;
     }
 
     njs_type_error(vm, "cannot convert %s argument to object",
-                   (nargs > 1) ? njs_type_string(args[1].type) : "null");
+                   njs_type_string(value->type));
+
     return NXT_ERROR;
 }
 
@@ -758,13 +780,16 @@ njs_object_freeze(njs_vm_t *vm, njs_valu
     njs_object_t       *object;
     njs_object_prop_t  *prop;
     nxt_lvlhsh_each_t  lhe;
+    const njs_value_t  *value;
 
-    if (nargs < 2 || !njs_is_object(&args[1])) {
+    value = njs_arg(args, nargs, 1);
+
+    if (!njs_is_object(value)) {
         vm->retval = njs_value_void;
         return NXT_OK;
     }
 
-    object = args[1].data.u.object;
+    object = value->data.u.object;
     object->extensible = 0;
 
     nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
@@ -782,7 +807,7 @@ njs_object_freeze(njs_vm_t *vm, njs_valu
         prop->configurable = 0;
     }
 
-    vm->retval = args[1];
+    vm->retval = *value;
 
     return NXT_OK;
 }
@@ -796,16 +821,18 @@ njs_object_is_frozen(njs_vm_t *vm, njs_v
     njs_object_t       *object;
     njs_object_prop_t  *prop;
     nxt_lvlhsh_each_t  lhe;
-    const njs_value_t  *retval;
+    const njs_value_t  *value, *retval;
 
-    if (nargs < 2 || !njs_is_object(&args[1])) {
+    value = njs_arg(args, nargs, 1);
+
+    if (!njs_is_object(value)) {
         vm->retval = njs_string_true;
         return NXT_OK;
     }
 
     retval = &njs_string_false;
 
-    object = args[1].data.u.object;
+    object = value->data.u.object;
     nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
 
     hash = &object->hash;
@@ -842,17 +869,18 @@ njs_object_seal(njs_vm_t *vm, njs_value_
 {
     nxt_lvlhsh_t       *hash;
     njs_object_t       *object;
-    const njs_value_t  *retval;
+    const njs_value_t  *value;
     njs_object_prop_t  *prop;
     nxt_lvlhsh_each_t  lhe;
 
-    if (nargs < 2 || !njs_is_object(&args[1])) {
-        retval = (nargs < 2) ? &njs_value_void : &args[1];
-        vm->retval = *retval;
+    value = njs_arg(args, nargs, 1);
+
+    if (!njs_is_object(value)) {
+        vm->retval = *value;
         return NXT_OK;
     }
 
-    object = args[1].data.u.object;
+    object = value->data.u.object;
     object->extensible = 0;
 
     nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
@@ -869,7 +897,7 @@ njs_object_seal(njs_vm_t *vm, njs_value_
         prop->configurable = 0;
     }
 
-    vm->retval = args[1];
+    vm->retval = *value;
 
     return NXT_OK;
 }
@@ -883,16 +911,18 @@ njs_object_is_sealed(njs_vm_t *vm, njs_v
     njs_object_t       *object;
     njs_object_prop_t  *prop;
     nxt_lvlhsh_each_t  lhe;
-    const njs_value_t  *retval;
+    const njs_value_t  *value, *retval;
 
-    if (nargs < 2 || !njs_is_object(&args[1])) {
+    value = njs_arg(args, nargs, 1);
+
+    if (!njs_is_object(value)) {
         vm->retval = njs_string_true;
         return NXT_OK;
     }
 
     retval = &njs_string_false;
 
-    object = args[1].data.u.object;
+    object = value->data.u.object;
     nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
 
     hash = &object->hash;
@@ -927,17 +957,18 @@ static njs_ret_t
 njs_object_prevent_extensions(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
     njs_index_t unused)
 {
-    const njs_value_t  *retval;
+    const njs_value_t  *value;
 
-    if (nargs < 2 || !njs_is_object(&args[1])) {
-        retval = (nargs < 2) ? &njs_value_void : &args[1];
-        vm->retval = *retval;
+    value = njs_arg(args, nargs, 1);
+
+    if (!njs_is_object(value)) {
+        vm->retval = *value;
         return NXT_OK;
     }
 
     args[1].data.u.object->extensible = 0;
 
-    vm->retval = args[1];
+    vm->retval = *value;
 
     return NXT_OK;
 }
@@ -947,15 +978,17 @@ static njs_ret_t
 njs_object_is_extensible(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
     njs_index_t unused)
 {
-    const njs_value_t  *retval;
+    const njs_value_t  *value, *retval;
 
-    if (nargs < 2 || !njs_is_object(&args[1])) {
+    value = njs_arg(args, nargs, 1);
+
+    if (!njs_is_object(value)) {
         vm->retval = njs_string_false;
         return NXT_OK;
     }
 
-    retval = args[1].data.u.object->extensible ? &njs_string_true
-                                               : &njs_string_false;
+    retval = value->data.u.object->extensible ? &njs_string_true
+                                              : &njs_string_false;
 
     vm->retval = *retval;
 
diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c
+++ b/njs/test/njs_unit_test.c
@@ -6382,7 +6382,7 @@ static njs_unit_test_t  njs_test[] =
       nxt_string("1,3,one") },
 
     { nxt_string("Object.keys()"),
-      nxt_string("TypeError: cannot convert null argument to object") },
+      nxt_string("TypeError: cannot convert void argument to object") },
 
     { nxt_string("Object.keys('a')"),
       nxt_string("TypeError: cannot convert string argument to object") },
@@ -6443,7 +6443,7 @@ static njs_unit_test_t  njs_test[] =
       nxt_string("2") },
 
     { nxt_string("var o = {}; Object.defineProperty()"),
-      nxt_string("TypeError: cannot convert null argument to object") },
+      nxt_string("TypeError: cannot convert void argument to object") },
 
     { nxt_string("var o = {}; Object.defineProperty(o)"),
       nxt_string("TypeError: descriptor is not an object") },
# HG changeset patch
# User Dmitry Volyntsev <xeioex@nginx.com>
# Date 1524491380 -10800
#      Mon Apr 23 16:49:40 2018 +0300
# Node ID d0e4045b57dd6edf8e42a387419af8c45267d66d
# Parent  a65dbae9b0216c5b88895f429853d46451600c81
Fixed handling of missing arg of Object.getOwnPropertyDescriptor().

This fixes #5 issue on GitHub.

diff --git a/njs/njs_object.c b/njs/njs_object.c
--- a/njs/njs_object.c
+++ b/njs/njs_object.c
@@ -629,9 +629,14 @@ njs_object_get_own_property_descriptor(n
     obj = njs_arg(args, nargs, 1);
 
     if (!njs_is_object(obj)) {
-        njs_type_error(vm, "cannot convert %s argument to object",
-                       njs_type_string(obj->type));
-        return NXT_ERROR;
+        if (njs_is_null_or_void(obj)) {
+            njs_type_error(vm, "cannot convert %s argument to object",
+                           njs_type_string(obj->type));
+            return NXT_ERROR;
+        }
+
+        vm->retval = njs_value_void;
+        return NXT_OK;
     }
 
     prop = NULL;
@@ -662,7 +667,7 @@ njs_object_get_own_property_descriptor(n
         ret = nxt_lvlhsh_find(&obj->data.u.object->hash, &lhq);
 
         if (ret != NXT_OK) {
-            vm->retval = njs_string_void;
+            vm->retval = njs_value_void;
             return NXT_OK;
         }
 
@@ -1164,7 +1169,7 @@ static const njs_object_prop_t  njs_obje
         .type = NJS_METHOD,
         .name = njs_long_string("getOwnPropertyDescriptor"),
         .value = njs_native_function(njs_object_get_own_property_descriptor, 0,
-                                     NJS_SKIP_ARG, NJS_OBJECT_ARG,
+                                     NJS_SKIP_ARG, NJS_SKIP_ARG,
                                      NJS_STRING_ARG),
     },
 
diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c
+++ b/njs/test/njs_unit_test.c
@@ -6594,7 +6594,13 @@ static njs_unit_test_t  njs_test[] =
       nxt_string("undefined") },
 
     { nxt_string("Object.getOwnPropertyDescriptor(1, '0')"),
-      nxt_string("TypeError: cannot convert number argument to object") },
+      nxt_string("undefined") },
+
+    { nxt_string("Object.getOwnPropertyDescriptor()"),
+      nxt_string("TypeError: cannot convert void argument to object") },
+
+    { nxt_string("Object.getOwnPropertyDescriptor(undefined)"),
+      nxt_string("TypeError: cannot convert void argument to object") },
 
     { nxt_string("Object.defineProperty(Object.freeze({}), 'b', {})"),
       nxt_string("TypeError: object is not extensible") },
# HG changeset patch
# User Dmitry Volyntsev <xeioex@nginx.com>
# Date 1524491807 -10800
#      Mon Apr 23 16:56:47 2018 +0300
# Node ID d563435d47cf2a8c7bcf71c0b5ec426c3ca22025
# Parent  d0e4045b57dd6edf8e42a387419af8c45267d66d
Fixed handling of props in Object.getOwnPropertyDescriptor().

diff --git a/njs/njs_object.c b/njs/njs_object.c
--- a/njs/njs_object.c
+++ b/njs/njs_object.c
@@ -19,7 +19,9 @@
 #include <njs_string.h>
 #include <njs_object.h>
 #include <njs_object_hash.h>
+#include <njs_number.h>
 #include <njs_array.h>
+#include <njs_extern.h>
 #include <njs_function.h>
 #include <njs_error.h>
 #include <stdio.h>
@@ -27,6 +29,12 @@
 
 
 static nxt_int_t njs_object_hash_test(nxt_lvlhsh_query_t *lhq, void *data);
+static njs_ret_t njs_object_property_query(njs_vm_t *vm,
+    njs_property_query_t *pq, njs_value_t *value, njs_object_t *object);
+static njs_ret_t njs_array_property_query(njs_vm_t *vm,
+    njs_property_query_t *pq, njs_value_t *object, uint32_t index);
+static njs_ret_t njs_object_query_prop_handler(njs_property_query_t *pq,
+    njs_object_t *object);
 static njs_ret_t njs_define_property(njs_vm_t *vm, njs_object_t *object,
     njs_value_t *name, njs_object_t *descriptor);
 
@@ -228,6 +236,303 @@ njs_object_property(njs_vm_t *vm, njs_ob
 }
 
 
+/*
+ * The njs_property_query() returns values
+ *   NXT_OK               property has been found in object,
+ *   NXT_DECLINED         property was not found in object,
+ *   NJS_PRIMITIVE_VALUE  property operation was applied to a numeric
+ *                        or boolean value,
+ *   NJS_STRING_VALUE     property operation was applied to a string,
+ *   NJS_ARRAY_VALUE      object is array,
+ *   NJS_EXTERNAL_VALUE   object is external entity,
+ *   NJS_TRAP_PROPERTY    the property trap must be called,
+ *   NXT_ERROR            exception has been thrown.
+ */
+
+njs_ret_t
+njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *object,
+    njs_value_t *property)
+{
+    uint32_t            index;
+    uint32_t            (*hash)(const void *, size_t);
+    njs_ret_t           ret;
+    njs_object_t        *obj;
+    njs_function_t      *function;
+    const njs_extern_t  *ext_proto;
+
+    hash = nxt_djb_hash;
+
+    switch (object->type) {
+
+    case NJS_BOOLEAN:
+    case NJS_NUMBER:
+        if (pq->query != NJS_PROPERTY_QUERY_GET) {
+            return NJS_PRIMITIVE_VALUE;
+        }
+
+        index = njs_primitive_prototype_index(object->type);
+        obj = &vm->prototypes[index].object;
+        break;
+
+    case NJS_STRING:
+        if (pq->query == NJS_PROPERTY_QUERY_DELETE) {
+            return NXT_DECLINED;
+        }
+
+        obj = &vm->prototypes[NJS_PROTOTYPE_STRING].object;
+        break;
+
+    case NJS_ARRAY:
+        if (nxt_fast_path(!njs_is_null_or_void_or_boolean(property))) {
+
+            if (nxt_fast_path(njs_is_primitive(property))) {
+                index = njs_value_to_index(property);
+
+                if (nxt_fast_path(index < NJS_ARRAY_MAX_LENGTH)) {
+                    return njs_array_property_query(vm, pq, object, index);
+                }
+
+            } else {
+                return NJS_TRAP_PROPERTY;
+            }
+        }
+
+        /* Fall through. */
+
+    case NJS_OBJECT:
+    case NJS_OBJECT_BOOLEAN:
+    case NJS_OBJECT_NUMBER:
+    case NJS_OBJECT_STRING:
+    case NJS_REGEXP:
+    case NJS_DATE:
+    case NJS_OBJECT_ERROR:
+    case NJS_OBJECT_EVAL_ERROR:
+    case NJS_OBJECT_INTERNAL_ERROR:
+    case NJS_OBJECT_RANGE_ERROR:
+    case NJS_OBJECT_REF_ERROR:
+    case NJS_OBJECT_SYNTAX_ERROR:
+    case NJS_OBJECT_TYPE_ERROR:
+    case NJS_OBJECT_URI_ERROR:
+    case NJS_OBJECT_VALUE:
+        obj = object->data.u.object;
+        break;
+
+    case NJS_FUNCTION:
+        function = njs_function_value_copy(vm, object);
+        if (nxt_slow_path(function == NULL)) {
+            return NXT_ERROR;
+        }
+
+        obj = &function->object;
+        break;
+
+    case NJS_EXTERNAL:
+        ext_proto = object->external.proto;
+
+        if (ext_proto->type == NJS_EXTERN_CASELESS_OBJECT) {
+            hash = nxt_djb_hash_lowcase;
+        }
+
+        obj = NULL;
+        break;
+
+    case NJS_VOID:
+    case NJS_NULL:
+    default:
+        if (nxt_fast_path(njs_is_primitive(property))) {
+
+            ret = njs_primitive_value_to_string(vm, &pq->value, property);
+
+            if (nxt_fast_path(ret == NXT_OK)) {
+                njs_string_get(&pq->value, &pq->lhq.key);
+                njs_type_error(vm, "cannot get property '%.*s' of undefined",
+                               (int) pq->lhq.key.length, pq->lhq.key.start);
+                return NXT_ERROR;
+            }
+        }
+
+        njs_type_error(vm, "cannot get property 'unknown' of undefined", NULL);
+
+        return NXT_ERROR;
+    }
+
+    if (nxt_fast_path(njs_is_primitive(property))) {
+
+        ret = njs_primitive_value_to_string(vm, &pq->value, property);
+
+        if (nxt_fast_path(ret == NXT_OK)) {
+
+            njs_string_get(&pq->value, &pq->lhq.key);
+            pq->lhq.key_hash = hash(pq->lhq.key.start, pq->lhq.key.length);
+
+            if (obj == NULL) {
+                pq->lhq.proto = &njs_extern_hash_proto;
+
+                return NJS_EXTERNAL_VALUE;
+            }
+
+            return njs_object_property_query(vm, pq, object, obj);
+        }
+
+        return ret;
+    }
+
+    return NJS_TRAP_PROPERTY;
+}
+
+
+njs_ret_t
+njs_object_property_query(njs_vm_t *vm, njs_property_query_t *pq,
+    njs_value_t *value, njs_object_t *object)
+{
+    njs_ret_t          ret;
+    njs_object_prop_t  *prop;
+
+    pq->lhq.proto = &njs_object_hash_proto;
+
+    if (pq->query == NJS_PROPERTY_QUERY_SET) {
+        ret = njs_object_query_prop_handler(pq, object);
+        if (ret == NXT_OK) {
+            return ret;
+        }
+    }
+
+    do {
+        pq->prototype = object;
+
+        ret = nxt_lvlhsh_find(&object->hash, &pq->lhq);
+
+        if (ret == NXT_OK) {
+            prop = pq->lhq.value;
+
+            if (prop->type != NJS_WHITEOUT) {
+                pq->shared = 0;
+
+                return ret;
+            }
+
+            goto next;
+        }
+
+        if (pq->query > NJS_PROPERTY_QUERY_IN) {
+            /* NXT_DECLINED */
+            return ret;
+        }
+
+        ret = nxt_lvlhsh_find(&object->shared_hash, &pq->lhq);
+
+        if (ret == NXT_OK) {
+            pq->shared = 1;
+
+            if (pq->query == NJS_PROPERTY_QUERY_GET) {
+                prop = pq->lhq.value;
+
+                if (prop->type == NJS_PROPERTY_HANDLER) {
+                    pq->scratch = *prop;
+                    prop = &pq->scratch;
+                    ret = prop->value.data.u.prop_handler(vm, value, NULL,
+                                                          &prop->value);
+
+                    if (nxt_fast_path(ret == NXT_OK)) {
+                        prop->type = NJS_PROPERTY;
+                        pq->lhq.value = prop;
+                    }
+                }
+            }
+
+            return ret;
+        }
+
+        if (pq->query > NJS_PROPERTY_QUERY_IN) {
+            /* NXT_DECLINED */
+            return ret;
+        }
+
+    next:
+
+        object = object->__proto__;
+
+    } while (object != NULL);
+
+    if (njs_is_string(value)) {
+        return NJS_STRING_VALUE;
+    }
+
+    /* NXT_DECLINED */
+
+    return ret;
+}
+
+
+static njs_ret_t
+njs_array_property_query(njs_vm_t *vm, njs_property_query_t *pq,
+    njs_value_t *object, uint32_t index)
+{
+    uint32_t     size;
+    njs_ret_t    ret;
+    njs_value_t  *value;
+    njs_array_t  *array;
+
+    array = object->data.u.array;
+
+    if (index >= array->length) {
+        if (pq->query != NJS_PROPERTY_QUERY_SET) {
+            return NXT_DECLINED;
+        }
+
+        size = index - array->length;
+
+        ret = njs_array_expand(vm, array, 0, size + 1);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return ret;
+        }
+
+        value = &array->start[array->length];
+
+        while (size != 0) {
+            njs_set_invalid(value);
+            value++;
+            size--;
+        }
+
+        array->length = index + 1;
+    }
+
+    pq->lhq.value = &array->start[index];
+
+    return NJS_ARRAY_VALUE;
+}
+
+
+static njs_ret_t
+njs_object_query_prop_handler(njs_property_query_t *pq, njs_object_t *object)
+{
+    njs_ret_t          ret;
+    njs_object_prop_t  *prop;
+
+    do {
+        pq->prototype = object;
+
+        ret = nxt_lvlhsh_find(&object->shared_hash, &pq->lhq);
+
+        if (ret == NXT_OK) {
+            prop = pq->lhq.value;
+
+            if (prop->type == NJS_PROPERTY_HANDLER) {
+                return NXT_OK;
+            }
+        }
+
+        object = object->__proto__;
+
+    } while (object != NULL);
+
+    return NXT_DECLINED;
+}
+
+
+
+
 njs_ret_t
 njs_object_constructor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
     njs_index_t unused)
@@ -618,13 +923,15 @@ static njs_ret_t
 njs_object_get_own_property_descriptor(njs_vm_t *vm, njs_value_t *args,
     nxt_uint_t nargs, njs_index_t unused)
 {
-    uint32_t            index;
-    nxt_int_t           ret;
-    njs_array_t         *array;
-    njs_object_t        *descriptor;
-    njs_object_prop_t   *pr, *prop, array_prop;
-    const njs_value_t   *value, *obj, *property;
-    nxt_lvlhsh_query_t  lhq;
+    double                num;
+    uint32_t              index;
+    nxt_int_t             ret;
+    njs_array_t           *array;
+    njs_object_t          *descriptor;
+    njs_object_prop_t     *pr, *prop, array_prop;
+    const njs_value_t     *value, *obj, *property;
+    nxt_lvlhsh_query_t    lhq;
+    njs_property_query_t  pq;
 
     obj = njs_arg(args, nargs, 1);
 
@@ -644,9 +951,13 @@ njs_object_get_own_property_descriptor(n
 
     if (njs_is_array(obj)) {
         array = obj->data.u.array;
-        index = njs_string_to_index((njs_value_t *) property);
+        num = njs_string_to_index((njs_value_t *) property);
+        index = num;
 
-        if (index < array->length && njs_is_valid(&array->start[index])) {
+        if ((double) index == num
+            && index < array->length
+            && njs_is_valid(&array->start[index]))
+        {
             prop = &array_prop;
 
             array_prop.name = *property;
@@ -661,17 +972,19 @@ njs_object_get_own_property_descriptor(n
     lhq.proto = &njs_object_hash_proto;
 
     if (prop == NULL) {
-        njs_string_get(property, &lhq.key);
-        lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
+        pq.query = NJS_PROPERTY_QUERY_GET;
+        pq.lhq.key.length = 0;
+        pq.lhq.key.start = NULL;
 
-        ret = nxt_lvlhsh_find(&obj->data.u.object->hash, &lhq);
+        ret = njs_property_query(vm, &pq,  (njs_value_t *) obj,
+                                 (njs_value_t *) property);
 
         if (ret != NXT_OK) {
             vm->retval = njs_value_void;
             return NXT_OK;
         }
 
-        prop = lhq.value;
+        prop = pq.lhq.value;
     }
 
     descriptor = njs_object_alloc(vm);
@@ -681,6 +994,7 @@ njs_object_get_own_property_descriptor(n
 
     lhq.replace = 0;
     lhq.pool = vm->mem_cache_pool;
+    lhq.proto = &njs_object_hash_proto;
 
     lhq.key = nxt_string_value("value");
     lhq.key_hash = NJS_VALUE_HASH;
diff --git a/njs/njs_object.h b/njs/njs_object.h
--- a/njs/njs_object.h
+++ b/njs/njs_object.h
@@ -30,6 +30,20 @@ typedef struct {
 } njs_object_prop_t;
 
 
+typedef struct {
+    nxt_lvlhsh_query_t          lhq;
+
+    /* scratch is used to get the value of an NJS_PROPERTY_HANDLER property. */
+    njs_object_prop_t           scratch;
+
+    njs_value_t                 value;
+    njs_object_t                *prototype;
+    uint8_t                     query;
+    uint8_t                     shared;
+} njs_property_query_t;
+
+
+
 struct njs_object_init_s {
     nxt_str_t                   name;
     const njs_object_prop_t     *properties;
@@ -44,6 +58,8 @@ njs_object_t *njs_object_value_alloc(njs
 njs_array_t *njs_object_keys_array(njs_vm_t *vm, njs_value_t *object);
 njs_object_prop_t *njs_object_property(njs_vm_t *vm, njs_object_t *obj,
     nxt_lvlhsh_query_t *lhq);
+njs_ret_t njs_property_query(njs_vm_t *vm, njs_property_query_t *pq,
+    njs_value_t *object, njs_value_t *property);
 nxt_int_t njs_object_hash_create(njs_vm_t *vm, nxt_lvlhsh_t *hash,
     const njs_object_prop_t *prop, nxt_uint_t n);
 njs_ret_t njs_object_constructor(njs_vm_t *vm, njs_value_t *args,
diff --git a/njs/njs_vm.c b/njs/njs_vm.c
--- a/njs/njs_vm.c
+++ b/njs/njs_vm.c
@@ -33,36 +33,6 @@
 #include <stdio.h>
 
 
-/* The values must be greater than NXT_OK. */
-#define NJS_PRIMITIVE_VALUE        1
-#define NJS_STRING_VALUE           2
-#define NJS_ARRAY_VALUE            3
-#define NJS_EXTERNAL_VALUE         4
-
-
-/*
- * NJS_PROPERTY_QUERY_GET must be less or equal to NJS_PROPERTY_QUERY_IN,
- * NJS_PROPERTY_QUERY_SET and NJS_PROPERTY_QUERY_DELETE must be greater
- * than NJS_PROPERTY_QUERY_IN.
- */
-#define NJS_PROPERTY_QUERY_GET     0
-#define NJS_PROPERTY_QUERY_IN      1
-#define NJS_PROPERTY_QUERY_SET     2
-#define NJS_PROPERTY_QUERY_DELETE  3
-
-
-typedef struct {
-    nxt_lvlhsh_query_t             lhq;
-
-    /* scratch is used to get the value of an NJS_PROPERTY_HANDLER property. */
-    njs_object_prop_t              scratch;
-
-    njs_value_t                    value;
-    njs_object_t                   *prototype;
-    uint8_t                        query;
-    uint8_t                        shared;
-} njs_property_query_t;
-
 
 struct njs_property_next_s {
     int32_t                        index;
@@ -76,14 +46,6 @@ struct njs_property_next_s {
  * and should fit in CPU L1 instruction cache.
  */
 
-static nxt_noinline njs_ret_t njs_property_query(njs_vm_t *vm,
-    njs_property_query_t *pq, njs_value_t *object, njs_value_t *property);
-static njs_ret_t njs_array_property_query(njs_vm_t *vm,
-    njs_property_query_t *pq, njs_value_t *object, uint32_t index);
-static njs_ret_t njs_object_property_query(njs_vm_t *vm,
-    njs_property_query_t *pq, njs_value_t *value, njs_object_t *object);
-static njs_ret_t njs_object_query_prop_handler(njs_property_query_t *pq,
-    njs_object_t *object);
 static njs_ret_t njs_method_private_copy(njs_vm_t *vm,
     njs_property_query_t *pq);
 static nxt_noinline njs_ret_t njs_values_equal(const njs_value_t *val1,
@@ -972,301 +934,6 @@ njs_vmcode_property_delete(njs_vm_t *vm,
 }
 
 
-/*
- * The njs_property_query() returns values
- *   NXT_OK               property has been found in object,
- *   NXT_DECLINED         property was not found in object,
- *   NJS_PRIMITIVE_VALUE  property operation was applied to a numeric
- *                        or boolean value,
- *   NJS_STRING_VALUE     property operation was applied to a string,
- *   NJS_ARRAY_VALUE      object is array,
- *   NJS_EXTERNAL_VALUE   object is external entity,
- *   NJS_TRAP_PROPERTY    the property trap must be called,
- *   NXT_ERROR            exception has been thrown.
- */
-
-static nxt_noinline njs_ret_t
-njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *object,
-    njs_value_t *property)
-{
-    uint32_t            index;
-    uint32_t            (*hash)(const void *, size_t);
-    njs_ret_t           ret;
-    njs_object_t        *obj;
-    njs_function_t      *function;
-    const njs_extern_t  *ext_proto;
-
-    hash = nxt_djb_hash;
-
-    switch (object->type) {
-
-    case NJS_BOOLEAN:
-    case NJS_NUMBER:
-        if (pq->query != NJS_PROPERTY_QUERY_GET) {
-            return NJS_PRIMITIVE_VALUE;
-        }
-
-        index = njs_primitive_prototype_index(object->type);
-        obj = &vm->prototypes[index].object;
-        break;
-
-    case NJS_STRING:
-        if (pq->query == NJS_PROPERTY_QUERY_DELETE) {
-            return NXT_DECLINED;
-        }
-
-        obj = &vm->prototypes[NJS_PROTOTYPE_STRING].object;
-        break;
-
-    case NJS_ARRAY:
-        if (nxt_fast_path(!njs_is_null_or_void_or_boolean(property))) {
-
-            if (nxt_fast_path(njs_is_primitive(property))) {
-                index = njs_value_to_index(property);
-
-                if (nxt_fast_path(index < NJS_ARRAY_MAX_LENGTH)) {
-                    return njs_array_property_query(vm, pq, object, index);
-                }
-
-            } else {
-                return NJS_TRAP_PROPERTY;
-            }
-        }
-
-        /* Fall through. */
-
-    case NJS_OBJECT:
-    case NJS_OBJECT_BOOLEAN:
-    case NJS_OBJECT_NUMBER:
-    case NJS_OBJECT_STRING:
-    case NJS_REGEXP:
-    case NJS_DATE:
-    case NJS_OBJECT_ERROR:
-    case NJS_OBJECT_EVAL_ERROR:
-    case NJS_OBJECT_INTERNAL_ERROR:
-    case NJS_OBJECT_RANGE_ERROR:
-    case NJS_OBJECT_REF_ERROR:
-    case NJS_OBJECT_SYNTAX_ERROR:
-    case NJS_OBJECT_TYPE_ERROR:
-    case NJS_OBJECT_URI_ERROR:
-    case NJS_OBJECT_VALUE:
-        obj = object->data.u.object;
-        break;
-
-    case NJS_FUNCTION:
-        function = njs_function_value_copy(vm, object);
-        if (nxt_slow_path(function == NULL)) {
-            return NXT_ERROR;
-        }
-
-        obj = &function->object;
-        break;
-
-    case NJS_EXTERNAL:
-        ext_proto = object->external.proto;
-
-        if (ext_proto->type == NJS_EXTERN_CASELESS_OBJECT) {
-            hash = nxt_djb_hash_lowcase;
-        }
-
-        obj = NULL;
-        break;
-
-    case NJS_VOID:
-    case NJS_NULL:
-    default:
-        if (nxt_fast_path(njs_is_primitive(property))) {
-
-            ret = njs_primitive_value_to_string(vm, &pq->value, property);
-
-            if (nxt_fast_path(ret == NXT_OK)) {
-                njs_string_get(&pq->value, &pq->lhq.key);
-                njs_type_error(vm, "cannot get property '%.*s' of undefined",
-                               (int) pq->lhq.key.length, pq->lhq.key.start);
-                return NXT_ERROR;
-            }
-        }
-
-        njs_type_error(vm, "cannot get property 'unknown' of undefined", NULL);
-
-        return NXT_ERROR;
-    }
-
-    if (nxt_fast_path(njs_is_primitive(property))) {
-
-        ret = njs_primitive_value_to_string(vm, &pq->value, property);
-
-        if (nxt_fast_path(ret == NXT_OK)) {
-
-            njs_string_get(&pq->value, &pq->lhq.key);
-            pq->lhq.key_hash = hash(pq->lhq.key.start, pq->lhq.key.length);
-
-            if (obj == NULL) {
-                pq->lhq.proto = &njs_extern_hash_proto;
-
-                return NJS_EXTERNAL_VALUE;
-            }
-
-            return njs_object_property_query(vm, pq, object, obj);
-        }
-
-        return ret;
-    }
-
-    return NJS_TRAP_PROPERTY;
-}
-
-
-static njs_ret_t
-njs_array_property_query(njs_vm_t *vm, njs_property_query_t *pq,
-    njs_value_t *object, uint32_t index)
-{
-    uint32_t     size;
-    njs_ret_t    ret;
-    njs_value_t  *value;
-    njs_array_t  *array;
-
-    array = object->data.u.array;
-
-    if (index >= array->length) {
-        if (pq->query != NJS_PROPERTY_QUERY_SET) {
-            return NXT_DECLINED;
-        }
-
-        size = index - array->length;
-
-        ret = njs_array_expand(vm, array, 0, size + 1);
-        if (nxt_slow_path(ret != NXT_OK)) {
-            return ret;
-        }
-
-        value = &array->start[array->length];
-
-        while (size != 0) {
-            njs_set_invalid(value);
-            value++;
-            size--;
-        }
-
-        array->length = index + 1;
-    }
-
-    pq->lhq.value = &array->start[index];
-
-    return NJS_ARRAY_VALUE;
-}
-
-
-static njs_ret_t
-njs_object_property_query(njs_vm_t *vm, njs_property_query_t *pq,
-    njs_value_t *value, njs_object_t *object)
-{
-    njs_ret_t          ret;
-    njs_object_prop_t  *prop;
-
-    pq->lhq.proto = &njs_object_hash_proto;
-
-    if (pq->query == NJS_PROPERTY_QUERY_SET) {
-        ret = njs_object_query_prop_handler(pq, object);
-        if (ret == NXT_OK) {
-            return ret;
-        }
-    }
-
-    do {
-        pq->prototype = object;
-
-        ret = nxt_lvlhsh_find(&object->hash, &pq->lhq);
-
-        if (ret == NXT_OK) {
-            prop = pq->lhq.value;
-
-            if (prop->type != NJS_WHITEOUT) {
-                pq->shared = 0;
-
-                return ret;
-            }
-
-            goto next;
-        }
-
-        if (pq->query > NJS_PROPERTY_QUERY_IN) {
-            /* NXT_DECLINED */
-            return ret;
-        }
-
-        ret = nxt_lvlhsh_find(&object->shared_hash, &pq->lhq);
-
-        if (ret == NXT_OK) {
-            pq->shared = 1;
-
-            if (pq->query == NJS_PROPERTY_QUERY_GET) {
-                prop = pq->lhq.value;
-
-                if (prop->type == NJS_PROPERTY_HANDLER) {
-                    pq->scratch = *prop;
-                    prop = &pq->scratch;
-                    ret = prop->value.data.u.prop_handler(vm, value, NULL,
-                                                          &prop->value);
-
-                    if (nxt_fast_path(ret == NXT_OK)) {
-                        prop->type = NJS_PROPERTY;
-                        pq->lhq.value = prop;
-                    }
-                }
-            }
-
-            return ret;
-        }
-
-        if (pq->query > NJS_PROPERTY_QUERY_IN) {
-            /* NXT_DECLINED */
-            return ret;
-        }
-
-    next:
-
-        object = object->__proto__;
-
-    } while (object != NULL);
-
-    if (njs_is_string(value)) {
-        return NJS_STRING_VALUE;
-    }
-
-    /* NXT_DECLINED */
-
-    return ret;
-}
-
-
-static njs_ret_t
-njs_object_query_prop_handler(njs_property_query_t *pq, njs_object_t *object)
-{
-    njs_ret_t          ret;
-    njs_object_prop_t  *prop;
-
-    do {
-        pq->prototype = object;
-
-        ret = nxt_lvlhsh_find(&object->shared_hash, &pq->lhq);
-
-        if (ret == NXT_OK) {
-            prop = pq->lhq.value;
-
-            if (prop->type == NJS_PROPERTY_HANDLER) {
-                return NXT_OK;
-            }
-        }
-
-        object = object->__proto__;
-
-    } while (object != NULL);
-
-    return NXT_DECLINED;
-}
-
-
 static njs_ret_t
 njs_method_private_copy(njs_vm_t *vm, njs_property_query_t *pq)
 {
diff --git a/njs/njs_vm.h b/njs/njs_vm.h
--- a/njs/njs_vm.h
+++ b/njs/njs_vm.h
@@ -49,6 +49,24 @@
 #define NJS_APPLIED              NXT_DONE
 
 
+/* The values must be greater than NXT_OK. */
+#define NJS_PRIMITIVE_VALUE        1
+#define NJS_STRING_VALUE           2
+#define NJS_ARRAY_VALUE            3
+#define NJS_EXTERNAL_VALUE         4
+
+
+/*
+ * NJS_PROPERTY_QUERY_GET must be less or equal to NJS_PROPERTY_QUERY_IN,
+ * NJS_PROPERTY_QUERY_SET and NJS_PROPERTY_QUERY_DELETE must be greater
+ * than NJS_PROPERTY_QUERY_IN.
+ */
+#define NJS_PROPERTY_QUERY_GET     0
+#define NJS_PROPERTY_QUERY_IN      1
+#define NJS_PROPERTY_QUERY_SET     2
+#define NJS_PROPERTY_QUERY_DELETE  3
+
+
 /*
  * The order of the enum is used in njs_vmcode_typeof()
  * and njs_object_prototype_to_string().
diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c
+++ b/njs/test/njs_unit_test.c
@@ -6587,6 +6587,9 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("Object.getOwnPropertyDescriptor([3,4], 1).value"),
       nxt_string("4") },
 
+    { nxt_string("Object.getOwnPropertyDescriptor([], 'length').value"),
+      nxt_string("0") },
+
     { nxt_string("Object.getOwnPropertyDescriptor([3,4], '3')"),
       nxt_string("undefined") },
 
@@ -6602,6 +6605,12 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("Object.getOwnPropertyDescriptor(undefined)"),
       nxt_string("TypeError: cannot convert void argument to object") },
 
+    { nxt_string("var o = {}; o[void 0] = 'a'; Object.getOwnPropertyDescriptor(o).value"),
+      nxt_string("a") },
+
+    { nxt_string("var o = {}; o[void 0] = 'a'; Object.getOwnPropertyDescriptor(o, undefined).value"),
+      nxt_string("a") },
+
     { nxt_string("Object.defineProperty(Object.freeze({}), 'b', {})"),
       nxt_string("TypeError: object is not extensible") },
 
# HG changeset patch
# User Dmitry Volyntsev <xeioex@nginx.com>
# Date 1524491810 -10800
#      Mon Apr 23 16:56:50 2018 +0300
# Node ID 52fbe21ea2d022d36b223ec49b3648a9da43e4b8
# Parent  d563435d47cf2a8c7bcf71c0b5ec426c3ca22025
Fixed writeable flag of Array.length property.

This fixes #6 issue on GitHub.

diff --git a/njs/njs_array.c b/njs/njs_array.c
--- a/njs/njs_array.c
+++ b/njs/njs_array.c
@@ -2097,6 +2097,7 @@ static const njs_object_prop_t  njs_arra
         .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("length"),
         .value = njs_prop_handler(njs_array_prototype_length),
+        .writable = 1
     },
 
     {
diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c
+++ b/njs/test/njs_unit_test.c
@@ -6590,6 +6590,9 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("Object.getOwnPropertyDescriptor([], 'length').value"),
       nxt_string("0") },
 
+    { nxt_string("JSON.stringify(Object.getOwnPropertyDescriptor([3,4], 'length'))"),
+      nxt_string("{\"value\":2,\"configurable\":\"false\",\"enumerable\":\"false\",\"writable\":\"true\"}") },
+
     { nxt_string("Object.getOwnPropertyDescriptor([3,4], '3')"),
       nxt_string("undefined") },
 

@drsm
Copy link
Contributor Author

drsm commented Apr 23, 2018

Thank you for the patch! It works for me (#6 is fixed too).
But here is some nitpicking:

>> Object.create()
TypeError: too few arguments
    at Object.create (native)
    at main (native)

>> Object.create(undefined)
TypeError: too few arguments
    at Object.create (native)
    at main (native)

>> Object.create(1.11)
TypeError: too few arguments
    at Object.create (native)
    at main (native)

>> Object.create('')
TypeError: too few arguments
    at Object.create (native)
    at main (native)

nodejs:

> Object.create()
TypeError: Object prototype may only be an Object or null: undefined
    at Function.create (<anonymous>)
> Object.create(undefined)
TypeError: Object prototype may only be an Object or null: undefined
    at Function.create (<anonymous>)
> Object.create('')
TypeError: Object prototype may only be an Object or null: 
    at Function.create (<anonymous>)
> Object.create(1.11)
TypeError: Object prototype may only be an Object or null: 1.11
    at Function.create (<anonymous>)

@xeioex
Copy link
Contributor

xeioex commented Apr 23, 2018

@drsm thank you for the feedback.

But here is some nitpicking:

I am going to cover it in the scope of #7.

@drsm
Copy link
Contributor Author

drsm commented Apr 23, 2018

@xeioex PS: I think it is better not to dump an invalid value in such cases :).

> Object.create(Array(100).fill('a').join())
TypeError: Object prototype may only be an Object or null: a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a
    at Function.create (<anonymous>)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants