From 8d0da61e403b6ccb3a0d94221dab877d7c348e16 Mon Sep 17 00:00:00 2001 From: Dmitry Volyntsev Date: Wed, 27 Nov 2019 15:48:32 +0300 Subject: [PATCH] Fixed Object.defineProperties() according to the specification. This closes #210 issue on Github. --- src/njs_object.c | 55 ++++++++++++++++++++++++---------------- src/njs_object_prop.c | 7 ++++- src/test/njs_unit_test.c | 37 +++++++++++++++++++++++++-- 3 files changed, 74 insertions(+), 25 deletions(-) diff --git a/src/njs_object.c b/src/njs_object.c index 7980ba8e..68a8404c 100644 --- a/src/njs_object.c +++ b/src/njs_object.c @@ -1262,44 +1262,55 @@ static njs_int_t njs_object_define_properties(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - njs_int_t ret; - njs_value_t *value, *desc; - njs_lvlhsh_t *hash; - njs_lvlhsh_each_t lhe; - njs_object_prop_t *prop; + uint32_t i, length; + njs_int_t ret; + njs_array_t *keys; + njs_value_t desc, *value, *descs; + njs_object_prop_t *prop; + njs_property_query_t pq; if (!njs_is_object(njs_arg(args, nargs, 1))) { njs_type_error(vm, "Object.defineProperties is called on non-object"); return NJS_ERROR; } - value = njs_argument(args, 1); - - desc = njs_arg(args, nargs, 2); + descs = njs_arg(args, nargs, 2); + ret = njs_value_to_object(vm, descs); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } - if (!njs_is_object(desc)) { - njs_type_error(vm, "descriptor is not an object"); + keys = njs_value_own_enumerate(vm, descs, NJS_ENUM_KEYS, + NJS_ENUM_STRING | NJS_ENUM_SYMBOL, 0); + if (njs_slow_path(keys == NULL)) { return NJS_ERROR; } - njs_lvlhsh_each_init(&lhe, &njs_object_hash_proto); + length = keys->length; + value = njs_argument(args, 1); + njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0); - hash = njs_object_hash(desc); + for (i = 0; i < length; i++) { + ret = njs_property_query(vm, &pq, descs, &keys->start[i]); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } - for ( ;; ) { - prop = njs_lvlhsh_each(hash, &lhe); + prop = pq.lhq.value; - if (prop == NULL) { - break; + if (ret == NJS_DECLINED || !prop->enumerable) { + continue; } - if (prop->enumerable && njs_is_object(&prop->value)) { - ret = njs_object_prop_define(vm, value, &prop->name, &prop->value, - NJS_OBJECT_PROP_DESCRIPTOR); + ret = njs_value_property(vm, descs, &keys->start[i], &desc); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } - if (njs_slow_path(ret != NJS_OK)) { - return NJS_ERROR; - } + ret = njs_object_prop_define(vm, value, &keys->start[i], &desc, + NJS_OBJECT_PROP_DESCRIPTOR); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; } } diff --git a/src/njs_object_prop.c b/src/njs_object_prop.c index 36379b0a..b93d671d 100644 --- a/src/njs_object_prop.c +++ b/src/njs_object_prop.c @@ -311,7 +311,7 @@ njs_object_prop_define(njs_vm_t *vm, njs_value_t *object, if (njs_is_valid(&prop->value) && prev->type != NJS_PROPERTY_HANDLER - && !njs_values_strict_equal(&prop->value, &prev->value)) + && !njs_values_same(&prop->value, &prev->value)) { goto exception; } @@ -464,6 +464,11 @@ njs_descriptor_prop(njs_vm_t *vm, njs_object_prop_t *prop, static const njs_value_t get_string = njs_string("get"); + if (!njs_is_object(desc)) { + njs_type_error(vm, "property descriptor must be an object"); + return NJS_ERROR; + } + data = 0; accessor = 0; diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index 0229870d..8466f9eb 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -11103,6 +11103,39 @@ static njs_unit_test_t njs_test[] = "Object.keys(o)"), njs_str("b") }, + { njs_str("var o = Object.defineProperties({}, { get x() { return { value: 1 }; } });" + "Object.getOwnPropertyDescriptor(o, 'x').value"), + njs_str("1") }, + + { njs_str("Object.defineProperties({}, { get x() { return 1; } })"), + njs_str("TypeError: property descriptor must be an object") }, + + { njs_str("var obj = {}; var desc = {value:NaN}; Object.defineProperty(obj, 'foo', desc); " + "Object.defineProperties(obj, { foo: desc } ).foo"), + njs_str("NaN") }, + + { njs_str("var obj = {}; var desc = {value:-0}; Object.defineProperty(obj, 'foo', desc); " + "Object.defineProperties(obj, { foo: desc } ).foo"), + njs_str("-0") }, + + { njs_str("var obj = {}; var desc = {value:-0}; Object.defineProperty(obj, 'foo', {value:0}); " + "Object.defineProperties(obj, { foo: desc } ).foo"), + njs_str("TypeError: Cannot redefine property: \"foo\"") }, + + { njs_str("var obj = {}; var desc = {value:0}; Object.defineProperty(obj, 'foo', {value:-0}); " + "Object.defineProperties(obj, { foo: desc } ).foo"), + njs_str("TypeError: Cannot redefine property: \"foo\"") }, + + { njs_str("var descs = {a:{value:1}}; Object.defineProperty(descs, 'b', {value:{value:2}});" + "var o = Object.defineProperties({}, descs);" + "njs.dump([o.a, o.b])"), + njs_str("[1,undefined]") }, + + { njs_str("var descs = {a:{value:1}}; Object.defineProperty(descs, 'b', {value:{value:2}, enumerable:true});" + "var o = Object.defineProperties({}, descs);" + "njs.dump([o.a, o.b])"), + njs_str("[1,2]") }, + { njs_str("var o = {a:1}; delete o.a;" "Object.defineProperty(o, 'a', { value: 1 }); o.a"), njs_str("1") }, @@ -11192,8 +11225,8 @@ static njs_unit_test_t njs_test[] = { njs_str("Object.defineProperties(1, {})"), njs_str("TypeError: Object.defineProperties is called on non-object") }, - { njs_str("Object.defineProperties({}, 1)"), - njs_str("TypeError: descriptor is not an object") }, + { njs_str("njs.dump(Object.defineProperties({}, 1))"), + njs_str("{}") }, { njs_str("Object.defineProperties(Object.freeze({b:1}), {b:{value:1}}).b"), njs_str("1") }, -- 2.47.3