diff options
7 files changed, 105 insertions, 0 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h index e7f685ff7c..1001e535b2 100644 --- a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h +++ b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h @@ -161,10 +161,12 @@ namespace JS { P(isArray) \ P(isExtensible) \ P(isFinite) \ + P(isFrozen) \ P(isInteger) \ P(isNaN) \ P(isPrototypeOf) \ P(isSafeInteger) \ + P(isSealed) \ P(isView) \ P(join) \ P(keyFor) \ diff --git a/Userland/Libraries/LibJS/Runtime/Object.cpp b/Userland/Libraries/LibJS/Runtime/Object.cpp index 4bcf0bf33e..5c5d48a879 100644 --- a/Userland/Libraries/LibJS/Runtime/Object.cpp +++ b/Userland/Libraries/LibJS/Runtime/Object.cpp @@ -208,6 +208,32 @@ bool Object::set_integrity_level(IntegrityLevel level) return true; } +// 7.3.16 TestIntegrityLevel, https://tc39.es/ecma262/#sec-testintegritylevel +bool Object::test_integrity_level(IntegrityLevel level) +{ + auto& vm = this->vm(); + auto extensible = is_extensible(); + if (vm.exception()) + return false; + if (extensible) + return false; + auto keys = get_own_properties(PropertyKind::Key); + if (vm.exception()) + return false; + for (auto& key : keys) { + auto property_name = PropertyName::from_value(global_object(), key); + auto property_descriptor = get_own_property_descriptor(property_name); + VERIFY(property_descriptor.has_value()); + if (property_descriptor->attributes.is_configurable()) + return false; + if (level == IntegrityLevel::Frozen && property_descriptor->is_data_descriptor()) { + if (property_descriptor->attributes.is_writable()) + return false; + } + } + return true; +} + Value Object::get_own_property(const PropertyName& property_name, Value receiver) const { VERIFY(property_name.is_valid()); diff --git a/Userland/Libraries/LibJS/Runtime/Object.h b/Userland/Libraries/LibJS/Runtime/Object.h index 6cb4455cd4..bcfc660c8b 100644 --- a/Userland/Libraries/LibJS/Runtime/Object.h +++ b/Userland/Libraries/LibJS/Runtime/Object.h @@ -135,6 +135,7 @@ public: virtual bool prevent_extensions(); bool set_integrity_level(IntegrityLevel); + bool test_integrity_level(IntegrityLevel); virtual Value value_of() const { return Value(const_cast<Object*>(this)); } virtual Value ordinary_to_primitive(Value::PreferredType preferred_type) const; diff --git a/Userland/Libraries/LibJS/Runtime/ObjectConstructor.cpp b/Userland/Libraries/LibJS/Runtime/ObjectConstructor.cpp index 735a5ef4b2..bbbb3c4a33 100644 --- a/Userland/Libraries/LibJS/Runtime/ObjectConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/ObjectConstructor.cpp @@ -55,6 +55,8 @@ void ObjectConstructor::initialize(GlobalObject& global_object) define_native_function(vm.names.getPrototypeOf, get_prototype_of, 1, attr); define_native_function(vm.names.setPrototypeOf, set_prototype_of, 2, attr); define_native_function(vm.names.isExtensible, is_extensible, 1, attr); + define_native_function(vm.names.isFrozen, is_frozen, 1, attr); + define_native_function(vm.names.isSealed, is_sealed, 1, attr); define_native_function(vm.names.preventExtensions, prevent_extensions, 1, attr); define_native_function(vm.names.freeze, freeze, 1, attr); define_native_function(vm.names.seal, seal, 1, attr); @@ -144,6 +146,24 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::is_extensible) return Value(argument.as_object().is_extensible()); } +// 20.1.2.15 Object.isFrozen, https://tc39.es/ecma262/#sec-object.isfrozen +JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::is_frozen) +{ + auto argument = vm.argument(0); + if (!argument.is_object()) + return Value(true); + return Value(argument.as_object().test_integrity_level(Object::IntegrityLevel::Frozen)); +} + +// 20.1.2.16 Object.isSealed, https://tc39.es/ecma262/#sec-object.issealed +JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::is_sealed) +{ + auto argument = vm.argument(0); + if (!argument.is_object()) + return Value(true); + return Value(argument.as_object().test_integrity_level(Object::IntegrityLevel::Sealed)); +} + JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::prevent_extensions) { auto argument = vm.argument(0); diff --git a/Userland/Libraries/LibJS/Runtime/ObjectConstructor.h b/Userland/Libraries/LibJS/Runtime/ObjectConstructor.h index df7ff1f83c..364e72f6a9 100644 --- a/Userland/Libraries/LibJS/Runtime/ObjectConstructor.h +++ b/Userland/Libraries/LibJS/Runtime/ObjectConstructor.h @@ -52,6 +52,8 @@ private: JS_DECLARE_NATIVE_FUNCTION(get_prototype_of); JS_DECLARE_NATIVE_FUNCTION(set_prototype_of); JS_DECLARE_NATIVE_FUNCTION(is_extensible); + JS_DECLARE_NATIVE_FUNCTION(is_frozen); + JS_DECLARE_NATIVE_FUNCTION(is_sealed); JS_DECLARE_NATIVE_FUNCTION(prevent_extensions); JS_DECLARE_NATIVE_FUNCTION(seal); JS_DECLARE_NATIVE_FUNCTION(freeze); diff --git a/Userland/Libraries/LibJS/Tests/builtins/Object/Object.isFrozen.js b/Userland/Libraries/LibJS/Tests/builtins/Object/Object.isFrozen.js new file mode 100644 index 0000000000..e14aeba6e1 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Object/Object.isFrozen.js @@ -0,0 +1,27 @@ +test("length is 1", () => { + expect(Object.isFrozen).toHaveLength(1); +}); + +describe("normal behavior", () => { + test("returns true for non-object argument", () => { + expect(Object.isFrozen(42)).toBeTrue(); + expect(Object.isFrozen("foobar")).toBeTrue(); + }); + + test("returns false for regular object", () => { + const o = { foo: "bar" }; + expect(Object.isFrozen(o)).toBeFalse(); + }); + + test("returns true for frozen object", () => { + const o = { foo: "bar" }; + Object.freeze(o); + expect(Object.isFrozen(o)).toBeTrue(); + }); + + test("returns true for non-extensible empty object", () => { + const o = {}; + Object.preventExtensions(o); + expect(Object.isFrozen(o)).toBeTrue(); + }); +}); diff --git a/Userland/Libraries/LibJS/Tests/builtins/Object/Object.isSealed.js b/Userland/Libraries/LibJS/Tests/builtins/Object/Object.isSealed.js new file mode 100644 index 0000000000..e1d61825bc --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Object/Object.isSealed.js @@ -0,0 +1,27 @@ +test("length is 1", () => { + expect(Object.isSealed).toHaveLength(1); +}); + +describe("normal behavior", () => { + test("returns true for non-object argument", () => { + expect(Object.isSealed(42)).toBeTrue(); + expect(Object.isSealed("foobar")).toBeTrue(); + }); + + test("returns false for regular object", () => { + const o = { foo: "bar" }; + expect(Object.isSealed(o)).toBeFalse(); + }); + + test("returns true for sealed object", () => { + const o = { foo: "bar" }; + Object.seal(o); + expect(Object.isSealed(o)).toBeTrue(); + }); + + test("returns true for non-extensible empty object", () => { + const o = {}; + Object.preventExtensions(o); + expect(Object.isSealed(o)).toBeTrue(); + }); +}); |