소스 검색

LibJS: Implement Object.isFrozen() and Object.isSealed()

Linus Groh 4 년 전
부모
커밋
f3264b0dbd

+ 2 - 0
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)                                \

+ 26 - 0
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());

+ 1 - 0
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;

+ 20 - 0
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);

+ 2 - 0
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);

+ 27 - 0
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();
+    });
+});

+ 27 - 0
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();
+    });
+});