Bläddra i källkod

LibJS: Add the [[Unimplemented]] attribute

Properties marked with the [[Unimplemented]] attribute behave as normal
but invoke the `VM::on_unimplemented_property_access callback` when
they are accessed.
Tim Ledbetter 1 år sedan
förälder
incheckning
88d425f32b

+ 7 - 0
Userland/Libraries/LibJS/Runtime/Object.cpp

@@ -808,6 +808,13 @@ ThrowCompletionOr<Optional<PropertyDescriptor>> Object::internal_get_own_propert
     // 3. Let X be O's own property whose key is P.
     // 3. Let X be O's own property whose key is P.
     auto [value, attributes, property_offset] = *maybe_storage_entry;
     auto [value, attributes, property_offset] = *maybe_storage_entry;
 
 
+    // AD-HOC: Properties with the [[Unimplemented]] attribute are used for reporting unimplemented IDL interfaces.
+    if (attributes.is_unimplemented()) {
+        if (vm().on_unimplemented_property_access)
+            vm().on_unimplemented_property_access(*this, property_key);
+        descriptor.unimplemented = true;
+    }
+
     // 4. If X is a data property, then
     // 4. If X is a data property, then
     if (!value.is_accessor()) {
     if (!value.is_accessor()) {
         // a. Set D.[[Value]] to the value of X's [[Value]] attribute.
         // a. Set D.[[Value]] to the value of X's [[Value]] attribute.

+ 3 - 0
Userland/Libraries/LibJS/Runtime/PropertyAttributes.h

@@ -19,6 +19,8 @@ struct Attribute {
         Writable = 1 << 0,
         Writable = 1 << 0,
         Enumerable = 1 << 1,
         Enumerable = 1 << 1,
         Configurable = 1 << 2,
         Configurable = 1 << 2,
+        // AD-HOC: This is used for reporting unimplemented IDL interfaces.
+        Unimplemented = 1 << 3,
     };
     };
 };
 };
 
 
@@ -33,6 +35,7 @@ public:
     [[nodiscard]] bool is_writable() const { return m_bits & Attribute::Writable; }
     [[nodiscard]] bool is_writable() const { return m_bits & Attribute::Writable; }
     [[nodiscard]] bool is_enumerable() const { return m_bits & Attribute::Enumerable; }
     [[nodiscard]] bool is_enumerable() const { return m_bits & Attribute::Enumerable; }
     [[nodiscard]] bool is_configurable() const { return m_bits & Attribute::Configurable; }
     [[nodiscard]] bool is_configurable() const { return m_bits & Attribute::Configurable; }
+    [[nodiscard]] bool is_unimplemented() const { return m_bits & Attribute::Unimplemented; }
 
 
     void set_writable(bool writable = true)
     void set_writable(bool writable = true)
     {
     {

+ 4 - 1
Userland/Libraries/LibJS/Runtime/PropertyDescriptor.h

@@ -31,7 +31,7 @@ public:
     // Not a standard abstract operation, but "If every field in Desc is absent".
     // Not a standard abstract operation, but "If every field in Desc is absent".
     [[nodiscard]] bool is_empty() const
     [[nodiscard]] bool is_empty() const
     {
     {
-        return !value.has_value() && !get.has_value() && !set.has_value() && !writable.has_value() && !enumerable.has_value() && !configurable.has_value();
+        return !value.has_value() && !get.has_value() && !set.has_value() && !writable.has_value() && !enumerable.has_value() && !configurable.has_value() && !unimplemented.has_value();
     }
     }
 
 
     Optional<Value> value {};
     Optional<Value> value {};
@@ -40,6 +40,7 @@ public:
     Optional<bool> writable {};
     Optional<bool> writable {};
     Optional<bool> enumerable {};
     Optional<bool> enumerable {};
     Optional<bool> configurable {};
     Optional<bool> configurable {};
+    Optional<bool> unimplemented {};
 
 
     Optional<u32> property_offset {};
     Optional<u32> property_offset {};
 };
 };
@@ -65,6 +66,8 @@ struct Formatter<JS::PropertyDescriptor> : Formatter<StringView> {
             TRY(parts.try_append(TRY(String::formatted("[[Enumerable]]: {}", *property_descriptor.enumerable))));
             TRY(parts.try_append(TRY(String::formatted("[[Enumerable]]: {}", *property_descriptor.enumerable))));
         if (property_descriptor.configurable.has_value())
         if (property_descriptor.configurable.has_value())
             TRY(parts.try_append(TRY(String::formatted("[[Configurable]]: {}", *property_descriptor.configurable))));
             TRY(parts.try_append(TRY(String::formatted("[[Configurable]]: {}", *property_descriptor.configurable))));
+        if (property_descriptor.unimplemented.has_value())
+            TRY(parts.try_append(TRY(String::formatted("[[Unimplemented]]: {}", *property_descriptor.unimplemented))));
         return Formatter<StringView>::format(builder, TRY(String::formatted("PropertyDescriptor {{ {} }}", TRY(String::join(", "sv, parts)))));
         return Formatter<StringView>::format(builder, TRY(String::formatted("PropertyDescriptor {{ {} }}", TRY(String::join(", "sv, parts)))));
     }
     }
 };
 };

+ 1 - 0
Userland/Libraries/LibJS/Runtime/VM.h

@@ -223,6 +223,7 @@ public:
     Function<void()> on_call_stack_emptied;
     Function<void()> on_call_stack_emptied;
     Function<void(Promise&)> on_promise_unhandled_rejection;
     Function<void(Promise&)> on_promise_unhandled_rejection;
     Function<void(Promise&)> on_promise_rejection_handled;
     Function<void(Promise&)> on_promise_rejection_handled;
+    Function<void(Object const&, PropertyKey const&)> on_unimplemented_property_access;
 
 
     CustomData* custom_data() { return m_custom_data; }
     CustomData* custom_data() { return m_custom_data; }