From 3ee092cd0cacb999469e50aa5ff220e397df2d79 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Tue, 18 May 2021 11:15:49 +0200 Subject: [PATCH] LibJS: Implement Object.hasOwn() :^) This is currently a TC39 Stage 2 proposal, but let's go for it! https://github.com/tc39/proposal-accessible-object-hasownproperty I wrote the C++, @linusg found bugs and wrote the test. --- .../LibJS/Runtime/CommonPropertyNames.h | 1 + .../LibJS/Runtime/ObjectConstructor.cpp | 12 +++++++ .../LibJS/Runtime/ObjectConstructor.h | 1 + .../Tests/builtins/Object/Object.hasOwn.js | 35 +++++++++++++++++++ 4 files changed, 49 insertions(+) create mode 100644 Userland/Libraries/LibJS/Tests/builtins/Object/Object.hasOwn.js diff --git a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h index fbccb97caca..cf740626c25 100644 --- a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h +++ b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h @@ -135,6 +135,7 @@ namespace JS { P(globalThis) \ P(groups) \ P(has) \ + P(hasOwn) \ P(hasOwnProperty) \ P(hypot) \ P(ignoreCase) \ diff --git a/Userland/Libraries/LibJS/Runtime/ObjectConstructor.cpp b/Userland/Libraries/LibJS/Runtime/ObjectConstructor.cpp index 5c1caabfa24..fddded47f13 100644 --- a/Userland/Libraries/LibJS/Runtime/ObjectConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/ObjectConstructor.cpp @@ -45,6 +45,7 @@ void ObjectConstructor::initialize(GlobalObject& global_object) define_native_function(vm.names.values, values, 1, attr); 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); } ObjectConstructor::~ObjectConstructor() @@ -310,4 +311,15 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::create) return object; } +JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::has_own) +{ + auto* object = vm.argument(0).to_object(global_object); + if (vm.exception()) + return {}; + auto string_or_symbol = StringOrSymbol::from_value(global_object, vm.argument(1)); + if (vm.exception()) + return {}; + return Value(object->has_own_property(string_or_symbol)); +} + } diff --git a/Userland/Libraries/LibJS/Runtime/ObjectConstructor.h b/Userland/Libraries/LibJS/Runtime/ObjectConstructor.h index 8a4e3361d60..b616a5ffb63 100644 --- a/Userland/Libraries/LibJS/Runtime/ObjectConstructor.h +++ b/Userland/Libraries/LibJS/Runtime/ObjectConstructor.h @@ -42,6 +42,7 @@ private: JS_DECLARE_NATIVE_FUNCTION(values); JS_DECLARE_NATIVE_FUNCTION(entries); JS_DECLARE_NATIVE_FUNCTION(create); + JS_DECLARE_NATIVE_FUNCTION(has_own); }; } diff --git a/Userland/Libraries/LibJS/Tests/builtins/Object/Object.hasOwn.js b/Userland/Libraries/LibJS/Tests/builtins/Object/Object.hasOwn.js new file mode 100644 index 00000000000..1ea82a309ae --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Object/Object.hasOwn.js @@ -0,0 +1,35 @@ +describe("basic functionality", () => { + test("length", () => { + expect(Object.hasOwn).toHaveLength(2); + }); + + test("returns true for existent own property", () => { + const o = { foo: "bar" }; + expect(Object.hasOwn(o, "foo")).toBeTrue(); + }); + + test("returns false for non-existent own property", () => { + const o = {}; + expect(Object.hasOwn(o, "foo")).toBeFalse(); + }); + + test("returns false for existent prototype chain property", () => { + const o = {}; + Object.prototype.foo = "bar"; + expect(Object.hasOwn(o, "foo")).toBeFalse(); + }); +}); + +describe("errors", () => { + test("null argument", () => { + expect(() => { + Object.hasOwn(null); + }).toThrowWithMessage(TypeError, "ToObject on null or undefined"); + }); + + test("undefined argument", () => { + expect(() => { + Object.hasOwn(undefined); + }).toThrowWithMessage(TypeError, "ToObject on null or undefined"); + }); +});