1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 03:17:34 +00:00

LibJS: De-duplicate ClassFieldDefinition Records

This was defined twice, despite being the very same thing:
- ClassElement::ClassFieldDefinition
- ECMAScriptFunctionObject::InstanceField

Move the former to a new header and use it everywhere. Also update the
define_field() AO to take a single field instead of separate name and
initializer arguments.
This commit is contained in:
Linus Groh 2022-04-20 00:06:45 +02:00
parent 7a02d33cd5
commit e815d3f9ce
9 changed files with 66 additions and 41 deletions

View file

@ -1487,13 +1487,13 @@ Completion ClassElement::execute(Interpreter&, GlobalObject&) const
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
static ThrowCompletionOr<ClassElement::ClassElementName> class_key_to_property_name(Interpreter& interpreter, GlobalObject& global_object, Expression const& key) static ThrowCompletionOr<ClassElementName> class_key_to_property_name(Interpreter& interpreter, GlobalObject& global_object, Expression const& key)
{ {
if (is<PrivateIdentifier>(key)) { if (is<PrivateIdentifier>(key)) {
auto& private_identifier = static_cast<PrivateIdentifier const&>(key); auto& private_identifier = static_cast<PrivateIdentifier const&>(key);
auto* private_environment = interpreter.vm().running_execution_context().private_environment; auto* private_environment = interpreter.vm().running_execution_context().private_environment;
VERIFY(private_environment); VERIFY(private_environment);
return ClassElement::ClassElementName { private_environment->resolve_private_identifier(private_identifier.string()) }; return ClassElementName { private_environment->resolve_private_identifier(private_identifier.string()) };
} }
auto prop_key = TRY(key.execute(interpreter, global_object)).release_value(); auto prop_key = TRY(key.execute(interpreter, global_object)).release_value();
@ -1502,7 +1502,7 @@ static ThrowCompletionOr<ClassElement::ClassElementName> class_key_to_property_n
prop_key = TRY(prop_key.to_primitive(global_object, Value::PreferredType::String)); prop_key = TRY(prop_key.to_primitive(global_object, Value::PreferredType::String));
auto property_key = TRY(PropertyKey::from_value(global_object, prop_key)); auto property_key = TRY(PropertyKey::from_value(global_object, prop_key));
return ClassElement::ClassElementName { property_key }; return ClassElementName { property_key };
} }
// 15.4.5 Runtime Semantics: MethodDefinitionEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-methoddefinitionevaluation // 15.4.5 Runtime Semantics: MethodDefinitionEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-methoddefinitionevaluation
@ -1838,11 +1838,11 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::class_definition_e
prototype->define_direct_property(vm.names.constructor, class_constructor, Attribute::Writable | Attribute::Configurable); prototype->define_direct_property(vm.names.constructor, class_constructor, Attribute::Writable | Attribute::Configurable);
using StaticElement = Variant<ClassElement::ClassFieldDefinition, Handle<ECMAScriptFunctionObject>>; using StaticElement = Variant<ClassFieldDefinition, Handle<ECMAScriptFunctionObject>>;
Vector<PrivateElement> static_private_methods; Vector<PrivateElement> static_private_methods;
Vector<PrivateElement> instance_private_methods; Vector<PrivateElement> instance_private_methods;
Vector<ClassElement::ClassFieldDefinition> instance_fields; Vector<ClassFieldDefinition> instance_fields;
Vector<StaticElement> static_elements; Vector<StaticElement> static_elements;
for (auto const& element : m_elements) { for (auto const& element : m_elements) {
@ -1871,7 +1871,7 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::class_definition_e
if (!added_to_existing) if (!added_to_existing)
container.append(move(element_value.get<PrivateElement>())); container.append(move(element_value.get<PrivateElement>()));
} else if (auto* class_field_definition_ptr = element_value.get_pointer<ClassElement::ClassFieldDefinition>()) { } else if (auto* class_field_definition_ptr = element_value.get_pointer<ClassFieldDefinition>()) {
if (element.is_static()) if (element.is_static())
static_elements.append(move(*class_field_definition_ptr)); static_elements.append(move(*class_field_definition_ptr));
else else
@ -1892,7 +1892,7 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::class_definition_e
MUST(class_scope->initialize_binding(global_object, binding_name, class_constructor)); MUST(class_scope->initialize_binding(global_object, binding_name, class_constructor));
for (auto& field : instance_fields) for (auto& field : instance_fields)
class_constructor->add_field(field.name, field.initializer.is_null() ? nullptr : field.initializer.cell()); class_constructor->add_field(field);
for (auto& private_method : instance_private_methods) for (auto& private_method : instance_private_methods)
class_constructor->add_private_method(private_method); class_constructor->add_private_method(private_method);
@ -1902,8 +1902,8 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::class_definition_e
for (auto& element : static_elements) { for (auto& element : static_elements) {
TRY(element.visit( TRY(element.visit(
[&](ClassElement::ClassFieldDefinition& field) -> ThrowCompletionOr<void> { [&](ClassFieldDefinition& field) -> ThrowCompletionOr<void> {
return TRY(class_constructor->define_field(field.name, field.initializer.is_null() ? nullptr : field.initializer.cell())); return TRY(class_constructor->define_field(field));
}, },
[&](Handle<ECMAScriptFunctionObject> static_block_function) -> ThrowCompletionOr<void> { [&](Handle<ECMAScriptFunctionObject> static_block_function) -> ThrowCompletionOr<void> {
VERIFY(!static_block_function.is_null()); VERIFY(!static_block_function.is_null());

View file

@ -19,6 +19,7 @@
#include <LibJS/Bytecode/CodeGenerationError.h> #include <LibJS/Bytecode/CodeGenerationError.h>
#include <LibJS/Forward.h> #include <LibJS/Forward.h>
#include <LibJS/Heap/Handle.h> #include <LibJS/Heap/Handle.h>
#include <LibJS/Runtime/ClassFieldDefinition.h>
#include <LibJS/Runtime/Completion.h> #include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/EnvironmentCoordinate.h> #include <LibJS/Runtime/EnvironmentCoordinate.h>
#include <LibJS/Runtime/FunctionKind.h> #include <LibJS/Runtime/FunctionKind.h>
@ -1248,13 +1249,6 @@ public:
virtual ElementKind class_element_kind() const = 0; virtual ElementKind class_element_kind() const = 0;
bool is_static() const { return m_is_static; } bool is_static() const { return m_is_static; }
using ClassElementName = Variant<PropertyKey, PrivateName>;
struct ClassFieldDefinition {
ClassElementName name;
Handle<ECMAScriptFunctionObject> initializer;
};
// We use the Completion also as a ClassStaticBlockDefinition Record. // We use the Completion also as a ClassStaticBlockDefinition Record.
using ClassValue = Variant<ClassFieldDefinition, Completion, PrivateElement>; using ClassValue = Variant<ClassFieldDefinition, Completion, PrivateElement>;
virtual ThrowCompletionOr<ClassValue> class_element_evaluation(Interpreter&, GlobalObject&, Object& home_object) const = 0; virtual ThrowCompletionOr<ClassValue> class_element_evaluation(Interpreter&, GlobalObject&, Object& home_object) const = 0;

View file

@ -141,6 +141,7 @@ class BoundFunction;
class Cell; class Cell;
class CellAllocator; class CellAllocator;
class ClassExpression; class ClassExpression;
struct ClassFieldDefinition;
class Completion; class Completion;
class Console; class Console;
class DeclarativeEnvironment; class DeclarativeEnvironment;

View file

@ -0,0 +1,22 @@
/*
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Forward.h>
#include <LibJS/Forward.h>
namespace JS {
using ClassElementName = Variant<PropertyKey, PrivateName>;
// 6.2.10 The ClassFieldDefinition Record Specification Type, https://tc39.es/ecma262/#sec-classfielddefinition-record-specification-type
struct ClassFieldDefinition {
ClassElementName name; // [[Name]]
Handle<ECMAScriptFunctionObject> initializer; // [[Initializer]]
};
}

View file

@ -299,8 +299,6 @@ void ECMAScriptFunctionObject::visit_edges(Visitor& visitor)
for (auto& field : m_fields) { for (auto& field : m_fields) {
if (auto* property_key_ptr = field.name.get_pointer<PropertyKey>(); property_key_ptr && property_key_ptr->is_symbol()) if (auto* property_key_ptr = field.name.get_pointer<PropertyKey>(); property_key_ptr && property_key_ptr->is_symbol())
visitor.visit(property_key_ptr->as_symbol()); visitor.visit(property_key_ptr->as_symbol());
visitor.visit(field.initializer);
} }
} }
@ -886,9 +884,4 @@ void ECMAScriptFunctionObject::set_name(FlyString const& name)
VERIFY(success); VERIFY(success);
} }
void ECMAScriptFunctionObject::add_field(ClassElement::ClassElementName property_key, ECMAScriptFunctionObject* initializer)
{
m_fields.empend(property_key, initializer);
}
} }

View file

@ -68,13 +68,8 @@ public:
String const& source_text() const { return m_source_text; } String const& source_text() const { return m_source_text; }
void set_source_text(String source_text) { m_source_text = move(source_text); } void set_source_text(String source_text) { m_source_text = move(source_text); }
struct InstanceField { Vector<ClassFieldDefinition> const& fields() const { return m_fields; }
Variant<PropertyKey, PrivateName> name; void add_field(ClassFieldDefinition field) { m_fields.append(move(field)); }
ECMAScriptFunctionObject* initializer { nullptr };
};
Vector<InstanceField> const& fields() const { return m_fields; }
void add_field(Variant<PropertyKey, PrivateName> property_key, ECMAScriptFunctionObject* initializer);
Vector<PrivateElement> const& private_methods() const { return m_private_methods; } Vector<PrivateElement> const& private_methods() const { return m_private_methods; }
void add_private_method(PrivateElement method) { m_private_methods.append(move(method)); }; void add_private_method(PrivateElement method) { m_private_methods.append(move(method)); };
@ -123,7 +118,7 @@ private:
ScriptOrModule m_script_or_module; // [[ScriptOrModule]] ScriptOrModule m_script_or_module; // [[ScriptOrModule]]
Object* m_home_object { nullptr }; // [[HomeObject]] Object* m_home_object { nullptr }; // [[HomeObject]]
String m_source_text; // [[SourceText]] String m_source_text; // [[SourceText]]
Vector<InstanceField> m_fields; // [[Fields]] Vector<ClassFieldDefinition> m_fields; // [[Fields]]
Vector<PrivateElement> m_private_methods; // [[PrivateMethods]] Vector<PrivateElement> m_private_methods; // [[PrivateMethods]]
Variant<PropertyKey, PrivateName, Empty> m_class_field_initializer_name; // [[ClassFieldInitializerName]] Variant<PropertyKey, PrivateName, Empty> m_class_field_initializer_name; // [[ClassFieldInitializerName]]
ConstructorKind m_constructor_kind : 1 { ConstructorKind::Base }; // [[ConstructorKind]] ConstructorKind m_constructor_kind : 1 { ConstructorKind::Base }; // [[ConstructorKind]]

View file

@ -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/ClassFieldDefinition.h>
#include <LibJS/Runtime/ECMAScriptFunctionObject.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>
@ -556,17 +557,36 @@ ThrowCompletionOr<void> Object::private_set(PrivateName const& name, Value value
} }
// 7.3.32 DefineField ( receiver, fieldRecord ), https://tc39.es/ecma262/#sec-definefield // 7.3.32 DefineField ( receiver, fieldRecord ), https://tc39.es/ecma262/#sec-definefield
ThrowCompletionOr<void> Object::define_field(Variant<PropertyKey, PrivateName> name, ECMAScriptFunctionObject* initializer) ThrowCompletionOr<void> Object::define_field(ClassFieldDefinition const& field)
{ {
Value init_value = js_undefined(); // 1. Let fieldName be fieldRecord.[[Name]].
if (initializer) auto const& field_name = field.name;
init_value = TRY(call(global_object(), *initializer, this));
if (auto* property_key_ptr = name.get_pointer<PropertyKey>()) // 2. Let initializer be fieldRecord.[[Initializer]].
TRY(create_data_property_or_throw(*property_key_ptr, init_value)); auto const& initializer = field.initializer;
else
TRY(private_field_add(name.get<PrivateName>(), init_value));
auto init_value = js_undefined();
// 3. If initializer is not empty, then
if (!initializer.is_null()) {
// a. Let initValue be ? Call(initializer, receiver).
init_value = TRY(call(global_object(), initializer.cell(), this));
}
// 4. Else, let initValue be undefined.
// 5. If fieldName is a Private Name, then
if (field_name.has<PrivateName>()) {
// a. Perform ? PrivateFieldAdd(receiver, fieldName, initValue).
TRY(private_field_add(field_name.get<PrivateName>(), init_value));
}
// 6. Else,
else {
// a. Assert: IsPropertyKey(fieldName) is true.
// b. Perform ? CreateDataPropertyOrThrow(receiver, fieldName, initValue).
TRY(create_data_property_or_throw(field_name.get<PropertyKey>(), init_value));
}
// 7. Return unused.
return {}; return {};
} }

View file

@ -110,7 +110,7 @@ public:
ThrowCompletionOr<void> private_method_or_accessor_add(PrivateElement element); ThrowCompletionOr<void> private_method_or_accessor_add(PrivateElement element);
ThrowCompletionOr<Value> private_get(PrivateName const& name); ThrowCompletionOr<Value> private_get(PrivateName const& name);
ThrowCompletionOr<void> private_set(PrivateName const& name, Value value); ThrowCompletionOr<void> private_set(PrivateName const& name, Value value);
ThrowCompletionOr<void> define_field(Variant<PropertyKey, PrivateName> name, ECMAScriptFunctionObject* initializer); ThrowCompletionOr<void> define_field(ClassFieldDefinition const&);
// 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

View file

@ -600,7 +600,7 @@ ThrowCompletionOr<void> VM::initialize_instance_elements(Object& object, ECMAScr
TRY(object.private_method_or_accessor_add(method)); TRY(object.private_method_or_accessor_add(method));
for (auto& field : constructor.fields()) for (auto& field : constructor.fields())
TRY(object.define_field(field.name, field.initializer)); TRY(object.define_field(field));
return {}; return {};
} }