From 726ae23b1c420934df2d4446bda7dd268f92eeec Mon Sep 17 00:00:00 2001 From: Alexander Mazyrin Date: Sat, 12 Oct 2019 16:23:25 +0300 Subject: [PATCH] Added Object.assign(). --- src/njs_object.c | 70 ++++++++++++++++++++++++++++++++++++++++ src/njs_value.c | 3 +- src/test/njs_unit_test.c | 65 +++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 2 deletions(-) diff --git a/src/njs_object.c b/src/njs_object.c index 26c051de..76d8698e 100644 --- a/src/njs_object.c +++ b/src/njs_object.c @@ -1637,6 +1637,67 @@ njs_object_is_extensible(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, } +static njs_int_t +njs_object_assign(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + uint32_t i, j, length; + njs_int_t ret; + njs_array_t *names; + njs_value_t *key, *source, *value, setval; + njs_object_prop_t *prop; + njs_property_query_t pq; + + value = njs_arg(args, nargs, 1); + + ret = njs_value_to_object(vm, value); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + for (i = 2; i < nargs; i++) { + source = &args[i]; + + names = njs_value_own_enumerate(vm, source, NJS_ENUM_KEYS, 1); + if (njs_slow_path(names == NULL)) { + return NJS_ERROR; + } + + length = names->length; + + for (j = 0; j < length; j++) { + key = &names->start[j]; + + njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 1); + + ret = njs_property_query(vm, &pq, source, key); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + prop = pq.lhq.value; + if (!prop->enumerable) { + continue; + } + + ret = njs_value_property(vm, source, key, &setval); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + ret = njs_value_property_set(vm, value, key, &setval); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + } + } + + vm->retval = *value; + + return NJS_OK; +} + + /* * The __proto__ property of booleans, numbers and strings primitives, * of objects created by Boolean(), Number(), and String() constructors, @@ -1908,6 +1969,15 @@ static const njs_object_prop_t njs_object_constructor_properties[] = .writable = 1, .configurable = 1, }, + + /* Object.assign(). */ + { + .type = NJS_PROPERTY, + .name = njs_string("assign"), + .value = njs_native_function(njs_object_assign, 2), + .writable = 1, + .configurable = 1, + }, }; diff --git a/src/njs_value.c b/src/njs_value.c index a716f2db..8d4756cb 100644 --- a/src/njs_value.c +++ b/src/njs_value.c @@ -1186,9 +1186,8 @@ njs_value_to_object(njs_vm_t *vm, njs_value_t *value) return NJS_ERROR; } - if (njs_is_object(value)) { + if (njs_fast_path(njs_is_object(value))) { return NJS_OK; - } if (njs_is_primitive(value)) { diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index 0872dcc6..8780e343 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -14531,6 +14531,71 @@ static njs_unit_test_t njs_test[] = { njs_str("export"), njs_str("SyntaxError: Illegal export statement in 1") }, + { njs_str("Object.assign(undefined)"), + njs_str("TypeError: cannot convert null or undefined to object") }, + + { njs_str("Object.assign(null)"), + njs_str("TypeError: cannot convert null or undefined to object") }, + + { njs_str("Object.assign({x:123}).toString()"), + njs_str("[object Object]") }, + + { njs_str("Object.assign({x:123}).x"), + njs_str("123") }, + + { njs_str("Object.assign(true)"), + njs_str("true") }, + + { njs_str("Object.assign(123)"), + njs_str("123") }, + + { njs_str("var o1 = {a:1, b:1, c:1}; var o2 = {b:2, c:2}; " + "var o3 = {c:3}; var obj = Object.assign({}, o1, o2, o3); " + "Object.values(obj);"), + njs_str("1,2,3") }, + + { njs_str("var v1 = 'abc'; var v2 = true; var v3 = 10; " + "var obj = Object.assign({}, v1, null, v2, undefined, v3); " + "Object.values(obj);"), + njs_str("a,b,c") }, + + { njs_str("Object.assign(true, {a:123})"), + njs_str("true") }, + + { njs_str("Object.assign(true, {a:123}).a"), + njs_str("123") }, + + { njs_str("var y = Object.create({s:123}); y.z = 456;" + "Object.assign({}, y).s;"), + njs_str("undefined") }, + + { njs_str("var obj = {s:123}; Object.defineProperty(obj," + "'p1', {value:12, enumerable:false});" + "Object.assign({}, obj).p1"), + njs_str("undefined") }, + + { njs_str("var obj = {s:123}; Object.defineProperty(obj," + "'x', {value:12, writable:false});" + "Object.assign(obj, {x:4})"), + njs_str("TypeError: Cannot assign to read-only property \"x\" of object") }, + + { njs_str("var obj = {foo:1, get bar() {return 2;}};" + "var copy = Object.assign({}, obj);" + "Object.getOwnPropertyDescriptor(copy, 'bar').get"), + njs_str("undefined") }, + + { njs_str("try{var x = Object.defineProperty({}, 'foo'," + "{value:1, writable:false});" + "Object.assign(x, {bar:2}, {foo:2});}catch(error){};" + "x.bar"), + njs_str("2") }, + + { njs_str("var a = Object.defineProperty({}, 'a'," + "{get(){Object.defineProperty(this, 'b'," + "{value:2,enumerable:false});" + "return 1}, enumerable:1}); a.b =1;" + "var x = Object.assign({}, a);x.b;"), + njs_str("undefined") }, }; -- 2.47.3