mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 07:17:35 +00:00
LibJS: Add slot [[PrivateElements]] and related methods to Object
This commit is contained in:
parent
13ead80ee6
commit
d94b5e511f
3 changed files with 124 additions and 0 deletions
|
@ -86,6 +86,11 @@
|
||||||
"Object prototype must not be {} on a super property access") \
|
"Object prototype must not be {} on a super property access") \
|
||||||
M(ObjectPrototypeWrongType, "Prototype must be an object or null") \
|
M(ObjectPrototypeWrongType, "Prototype must be an object or null") \
|
||||||
M(OptionIsNotValidValue, "{} is not a valid value for option {}") \
|
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(PromiseExecutorNotAFunction, "Promise executor must be a function") \
|
||||||
M(ProxyConstructBadReturnType, "Proxy handler's construct trap violates invariant: must return " \
|
M(ProxyConstructBadReturnType, "Proxy handler's construct trap violates invariant: must return " \
|
||||||
"an object") \
|
"an object") \
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <LibJS/Runtime/AbstractOperations.h>
|
#include <LibJS/Runtime/AbstractOperations.h>
|
||||||
#include <LibJS/Runtime/Accessor.h>
|
#include <LibJS/Runtime/Accessor.h>
|
||||||
#include <LibJS/Runtime/Array.h>
|
#include <LibJS/Runtime/Array.h>
|
||||||
|
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
|
||||||
#include <LibJS/Runtime/Error.h>
|
#include <LibJS/Runtime/Error.h>
|
||||||
#include <LibJS/Runtime/GlobalObject.h>
|
#include <LibJS/Runtime/GlobalObject.h>
|
||||||
#include <LibJS/Runtime/NativeFunction.h>
|
#include <LibJS/Runtime/NativeFunction.h>
|
||||||
|
@ -466,6 +467,100 @@ ThrowCompletionOr<Object*> Object::copy_data_properties(Value source, HashTable<
|
||||||
return this;
|
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 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
|
// 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) {
|
m_indexed_properties.for_each_value([&visitor](auto& value) {
|
||||||
visitor.visit(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
|
// 7.1.1.1 OrdinaryToPrimitive ( O, hint ), https://tc39.es/ecma262/#sec-ordinarytoprimitive
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <LibJS/Runtime/IndexedProperties.h>
|
#include <LibJS/Runtime/IndexedProperties.h>
|
||||||
#include <LibJS/Runtime/MarkedValueList.h>
|
#include <LibJS/Runtime/MarkedValueList.h>
|
||||||
#include <LibJS/Runtime/PrimitiveString.h>
|
#include <LibJS/Runtime/PrimitiveString.h>
|
||||||
|
#include <LibJS/Runtime/PrivateEnvironment.h>
|
||||||
#include <LibJS/Runtime/PropertyDescriptor.h>
|
#include <LibJS/Runtime/PropertyDescriptor.h>
|
||||||
#include <LibJS/Runtime/PropertyName.h>
|
#include <LibJS/Runtime/PropertyName.h>
|
||||||
#include <LibJS/Runtime/Shape.h>
|
#include <LibJS/Runtime/Shape.h>
|
||||||
|
@ -28,6 +29,18 @@ public: \
|
||||||
using Base = base_class; \
|
using Base = base_class; \
|
||||||
virtual const char* class_name() const override { return #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 {
|
class Object : public Cell {
|
||||||
public:
|
public:
|
||||||
static Object* create(GlobalObject&, Object* prototype);
|
static Object* create(GlobalObject&, Object* prototype);
|
||||||
|
@ -90,6 +103,13 @@ public:
|
||||||
ThrowCompletionOr<MarkedValueList> enumerable_own_property_names(PropertyKind kind) const;
|
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);
|
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
|
// 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;
|
virtual ThrowCompletionOr<Object*> internal_get_prototype_of() const;
|
||||||
|
@ -192,6 +212,7 @@ private:
|
||||||
Shape* m_shape { nullptr };
|
Shape* m_shape { nullptr };
|
||||||
Vector<Value> m_storage;
|
Vector<Value> m_storage;
|
||||||
IndexedProperties m_indexed_properties;
|
IndexedProperties m_indexed_properties;
|
||||||
|
Vector<PrivateElement> m_private_elements; // [[PrivateElements]]
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue