diff options
author | Linus Groh <mail@linusgroh.de> | 2021-06-12 11:35:53 +0100 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2021-06-12 11:36:17 +0100 |
commit | 7e1bffdeb85c832c7cea1a71e2c9e9ebe7ff96ae (patch) | |
tree | 041445f418bfe9b9b41f991e71e86efe6a5e1691 /Userland/Libraries | |
parent | de77946a8c6bbe78b829be165736587fb823b690 (diff) | |
download | serenity-7e1bffdeb85c832c7cea1a71e2c9e9ebe7ff96ae.zip |
LibJS: Implement Object.assign()
Diffstat (limited to 'Userland/Libraries')
4 files changed, 78 insertions, 0 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h index cac94e060c..baea3fbf69 100644 --- a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h +++ b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h @@ -56,6 +56,7 @@ namespace JS { P(asin) \ P(asinh) \ P(assert) \ + P(assign) \ P(at) \ P(atan) \ P(atan2) \ diff --git a/Userland/Libraries/LibJS/Runtime/ObjectConstructor.cpp b/Userland/Libraries/LibJS/Runtime/ObjectConstructor.cpp index f6315ffb3b..72a4e19836 100644 --- a/Userland/Libraries/LibJS/Runtime/ObjectConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/ObjectConstructor.cpp @@ -46,6 +46,7 @@ void ObjectConstructor::initialize(GlobalObject& global_object) define_native_function(vm.names.entries, entries, 1, attr); define_native_function(vm.names.create, create, 2, attr); define_native_function(vm.names.hasOwn, has_own, 2, attr); + define_native_function(vm.names.assign, assign, 2, attr); } ObjectConstructor::~ObjectConstructor() @@ -322,4 +323,37 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::has_own) return Value(object->has_own_property(property_key)); } +// 20.1.2.1 Object.assign, https://tc39.es/ecma262/#sec-object.assign +JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::assign) +{ + auto* to = vm.argument(0).to_object(global_object); + if (vm.exception()) + return {}; + if (vm.argument_count() == 1) + return to; + for (size_t i = 1; i < vm.argument_count(); ++i) { + auto next_source = vm.argument(i); + if (next_source.is_nullish()) + continue; + auto from = next_source.to_object(global_object); + VERIFY(!vm.exception()); + auto keys = from->get_own_properties(PropertyKind::Key); + if (vm.exception()) + return {}; + for (auto& key : keys) { + auto property_name = PropertyName::from_value(global_object, key); + auto property_descriptor = from->get_own_property_descriptor(property_name); + if (!property_descriptor.has_value() || !property_descriptor->attributes.is_enumerable()) + continue; + auto value = from->get(property_name); + if (vm.exception()) + return {}; + to->put(property_name, value); + if (vm.exception()) + return {}; + } + } + return to; +} + } diff --git a/Userland/Libraries/LibJS/Runtime/ObjectConstructor.h b/Userland/Libraries/LibJS/Runtime/ObjectConstructor.h index b616a5ffb6..192f8a0267 100644 --- a/Userland/Libraries/LibJS/Runtime/ObjectConstructor.h +++ b/Userland/Libraries/LibJS/Runtime/ObjectConstructor.h @@ -43,6 +43,7 @@ private: JS_DECLARE_NATIVE_FUNCTION(entries); JS_DECLARE_NATIVE_FUNCTION(create); JS_DECLARE_NATIVE_FUNCTION(has_own); + JS_DECLARE_NATIVE_FUNCTION(assign); }; } diff --git a/Userland/Libraries/LibJS/Tests/builtins/Object/Object.assign.js b/Userland/Libraries/LibJS/Tests/builtins/Object/Object.assign.js new file mode 100644 index 0000000000..61b00fe870 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Object/Object.assign.js @@ -0,0 +1,42 @@ +test("length is 2", () => { + expect(Object.assign).toHaveLength(2); +}); + +describe("errors", () => { + test("first argument must coercible to object", () => { + expect(() => { + Object.assign(null); + }).toThrowWithMessage(TypeError, "ToObject on null or undefined"); + expect(() => { + Object.assign(undefined); + }).toThrowWithMessage(TypeError, "ToObject on null or undefined"); + }); +}); + +describe("normal behavior", () => { + test("returns first argument coerced to object", () => { + const o = {}; + expect(Object.assign(o)).toBe(o); + expect(Object.assign(o, {})).toBe(o); + expect(Object.assign(42)).toEqual(new Number(42)); + }); + + test("alters first argument object if sources are given", () => { + const o = { foo: 0 }; + expect(Object.assign(o, { foo: 1 })).toBe(o); + expect(o).toEqual({ foo: 1 }); + }); + + test("merges objects", () => { + const s = Symbol(); + expect( + Object.assign( + {}, + { foo: 0, bar: "baz" }, + { [s]: [1, 2, 3] }, + { foo: 1 }, + { [42]: "test" } + ) + ).toEqual({ foo: 1, bar: "baz", [s]: [1, 2, 3], 42: "test" }); + }); +}); |