diff options
Diffstat (limited to 'Userland')
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/ErrorTypes.h | 5 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/Object.cpp | 98 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/Object.h | 21 |
3 files changed, 124 insertions, 0 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h index 2f6f8467cb..4bb5ff35bd 100644 --- a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h +++ b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h @@ -86,6 +86,11 @@ "Object prototype must not be {} on a super property access") \ M(ObjectPrototypeWrongType, "Prototype must be an object or null") \ M(OptionIsNotValidValue, "{} is not a valid value for option {}") \ + M(PrivateFieldAlreadyDeclared, "Private field '{}' has already been declared") \ + M(PrivateFieldDoesNotExistOnObject, "Private field '{}' does not exist on object") \ + M(PrivateFieldGetAccessorWithoutGetter, "Cannot get private field '{}' as accessor without getter") \ + M(PrivateFieldSetAccessorWithoutSetter, "Cannot set private field '{}' as accessor without setter") \ + M(PrivateFieldSetMethod, "Cannot set private method '{}'") \ M(PromiseExecutorNotAFunction, "Promise executor must be a function") \ M(ProxyConstructBadReturnType, "Proxy handler's construct trap violates invariant: must return " \ "an object") \ diff --git a/Userland/Libraries/LibJS/Runtime/Object.cpp b/Userland/Libraries/LibJS/Runtime/Object.cpp index ff64550682..8b11d69455 100644 --- a/Userland/Libraries/LibJS/Runtime/Object.cpp +++ b/Userland/Libraries/LibJS/Runtime/Object.cpp @@ -10,6 +10,7 @@ #include <LibJS/Runtime/AbstractOperations.h> #include <LibJS/Runtime/Accessor.h> #include <LibJS/Runtime/Array.h> +#include <LibJS/Runtime/ECMAScriptFunctionObject.h> #include <LibJS/Runtime/Error.h> #include <LibJS/Runtime/GlobalObject.h> #include <LibJS/Runtime/NativeFunction.h> @@ -466,6 +467,100 @@ ThrowCompletionOr<Object*> Object::copy_data_properties(Value source, HashTable< return this; } +// 7.3.26 PrivateElementFind ( O, P ), https://tc39.es/ecma262/#sec-privateelementfind +PrivateElement* Object::private_element_find(PrivateName const& name) +{ + auto element = m_private_elements.find_if([&](auto const& element) { + return element.key == name; + }); + + if (element.is_end()) + return nullptr; + + return &(*element); +} + +// 7.3.27 PrivateFieldAdd ( O, P, value ), https://tc39.es/ecma262/#sec-privatefieldadd +ThrowCompletionOr<void> Object::private_field_add(PrivateName const& name, Value value) +{ + if (auto* entry = private_element_find(name); entry) + return vm().throw_completion<TypeError>(global_object(), ErrorType::PrivateFieldAlreadyDeclared, name.description); + m_private_elements.empend(name, PrivateElement::Kind::Field, value); + return {}; +} + +// 7.3.28 PrivateMethodOrAccessorAdd ( O, method ), https://tc39.es/ecma262/#sec-privatemethodoraccessoradd +ThrowCompletionOr<void> Object::private_method_or_accessor_add(PrivateElement element) +{ + VERIFY(element.kind == PrivateElement::Kind::Method || element.kind == PrivateElement::Kind::Accessor); + if (auto* entry = private_element_find(element.key); entry) + return vm().throw_completion<TypeError>(global_object(), ErrorType::PrivateFieldAlreadyDeclared, element.key.description); + m_private_elements.append(move(element)); + return {}; +} + +// 7.3.29 PrivateGet ( O, P ), https://tc39.es/ecma262/#sec-privateget +ThrowCompletionOr<Value> Object::private_get(PrivateName const& name) +{ + auto* entry = private_element_find(name); + if (!entry) + return vm().throw_completion<TypeError>(global_object(), ErrorType::PrivateFieldDoesNotExistOnObject, name.description); + + auto& value = entry->value; + + if (entry->kind != PrivateElement::Kind::Accessor) + return value; + + VERIFY(value.is_accessor()); + auto* getter = value.as_accessor().getter(); + if (!getter) + return vm().throw_completion<TypeError>(global_object(), ErrorType::PrivateFieldGetAccessorWithoutGetter, name.description); + + // 8. Return ? Call(getter, Receiver). + return TRY(vm().call(*getter, this)); +} + +// 7.3.30 PrivateSet ( O, P, value ), https://tc39.es/ecma262/#sec-privateset +ThrowCompletionOr<void> Object::private_set(PrivateName const& name, Value value) +{ + auto* entry = private_element_find(name); + if (!entry) + return vm().throw_completion<TypeError>(global_object(), ErrorType::PrivateFieldDoesNotExistOnObject, name.description); + + if (entry->kind == PrivateElement::Kind::Field) { + entry->value = value; + return {}; + } else if (entry->kind == PrivateElement::Kind::Method) { + return vm().throw_completion<TypeError>(global_object(), ErrorType::PrivateFieldSetMethod, name.description); + } + + VERIFY(entry->kind == PrivateElement::Kind::Accessor); + + auto& accessor = entry->value; + VERIFY(accessor.is_accessor()); + auto* setter = accessor.as_accessor().setter(); + if (!setter) + return vm().throw_completion<TypeError>(global_object(), ErrorType::PrivateFieldSetAccessorWithoutSetter, name.description); + + TRY(vm().call(*setter, this, value)); + return {}; +} + +// 7.3.31 DefineField ( receiver, fieldRecord ), https://tc39.es/ecma262/#sec-definefield +ThrowCompletionOr<void> Object::define_field(Variant<PropertyName, PrivateName> name, ECMAScriptFunctionObject* initializer) +{ + Value init_value = js_undefined(); + if (initializer) + init_value = TRY(vm().call(*initializer, this)); + + if (auto* property_name_ptr = name.get_pointer<PropertyName>()) + TRY(create_data_property_or_throw(*property_name_ptr, init_value)); + else + TRY(private_field_add(name.get<PrivateName>(), init_value)); + + return {}; +} + // 10.1 Ordinary Object Internal Methods and Internal Slots, https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots // 10.1.1 [[GetPrototypeOf]] ( ), https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getprototypeof @@ -1130,6 +1225,9 @@ void Object::visit_edges(Cell::Visitor& visitor) m_indexed_properties.for_each_value([&visitor](auto& value) { visitor.visit(value); }); + + for (auto& private_element : m_private_elements) + visitor.visit(private_element.value); } // 7.1.1.1 OrdinaryToPrimitive ( O, hint ), https://tc39.es/ecma262/#sec-ordinarytoprimitive diff --git a/Userland/Libraries/LibJS/Runtime/Object.h b/Userland/Libraries/LibJS/Runtime/Object.h index 06fa5746a8..3ca024cf38 100644 --- a/Userland/Libraries/LibJS/Runtime/Object.h +++ b/Userland/Libraries/LibJS/Runtime/Object.h @@ -16,6 +16,7 @@ #include <LibJS/Runtime/IndexedProperties.h> #include <LibJS/Runtime/MarkedValueList.h> #include <LibJS/Runtime/PrimitiveString.h> +#include <LibJS/Runtime/PrivateEnvironment.h> #include <LibJS/Runtime/PropertyDescriptor.h> #include <LibJS/Runtime/PropertyName.h> #include <LibJS/Runtime/Shape.h> @@ -28,6 +29,18 @@ public: \ using Base = base_class; \ virtual const char* class_name() const override { return #class_; } +struct PrivateElement { + enum class Kind { + Field, + Method, + Accessor + }; + + PrivateName key; + Kind kind { Kind::Field }; + Value value; +}; + class Object : public Cell { public: static Object* create(GlobalObject&, Object* prototype); @@ -90,6 +103,13 @@ public: ThrowCompletionOr<MarkedValueList> enumerable_own_property_names(PropertyKind kind) const; ThrowCompletionOr<Object*> copy_data_properties(Value source, HashTable<PropertyName, PropertyNameTraits> const& seen_names, GlobalObject& global_object); + PrivateElement* private_element_find(PrivateName const& name); + ThrowCompletionOr<void> private_field_add(PrivateName const& name, Value value); + ThrowCompletionOr<void> private_method_or_accessor_add(PrivateElement element); + ThrowCompletionOr<Value> private_get(PrivateName const& name); + ThrowCompletionOr<void> private_set(PrivateName const& name, Value value); + ThrowCompletionOr<void> define_field(Variant<PropertyName, PrivateName> name, ECMAScriptFunctionObject* initializer); + // 10.1 Ordinary Object Internal Methods and Internal Slots, https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots virtual ThrowCompletionOr<Object*> internal_get_prototype_of() const; @@ -192,6 +212,7 @@ private: Shape* m_shape { nullptr }; Vector<Value> m_storage; IndexedProperties m_indexed_properties; + Vector<PrivateElement> m_private_elements; // [[PrivateElements]] }; } |