diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index 8191c09ec63..501c1b28d14 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -225,24 +225,35 @@ ThrowCompletionOr ClassField::class_element_evaluation auto& realm = *vm.current_realm(); auto property_key_or_private_name = TRY(class_key_to_property_name(vm, *m_key, property_key)); - GC::Ptr initializer; + Variant, Value, Empty> initializer; if (m_initializer) { - auto copy_initializer = m_initializer; - auto name = property_key_or_private_name.visit( - [&](PropertyKey const& property_key) -> String { - return property_key.is_number() ? property_key.to_string() : property_key.to_string_or_symbol().to_display_string(); - }, - [&](PrivateName const& private_name) -> String { - return private_name.description.to_string(); - }); + if (auto const* literal = as_if(*m_initializer)) { + initializer = literal->value(); + } else if (auto const* literal = as_if(*m_initializer)) { + initializer = literal->value(); + } else if (auto const* literal = as_if(*m_initializer)) { + initializer = literal->value(); + } else if (auto const* literal = as_if(*m_initializer)) { + initializer = Value(PrimitiveString::create(vm, literal->value())); + } else { + auto copy_initializer = m_initializer; + auto name = property_key_or_private_name.visit( + [&](PropertyKey const& property_key) -> String { + return property_key.is_number() ? property_key.to_string() : property_key.to_string_or_symbol().to_display_string(); + }, + [&](PrivateName const& private_name) -> String { + return private_name.description.to_string(); + }); - // FIXME: A potential optimization is not creating the functions here since these are never directly accessible. - auto function_code = create_ast_node(m_initializer->source_range(), copy_initializer.release_nonnull(), name); - FunctionParsingInsights parsing_insights; - parsing_insights.uses_this_from_environment = true; - parsing_insights.uses_this = true; - initializer = ECMAScriptFunctionObject::create(realm, "field"_string, ByteString::empty(), *function_code, FunctionParameters::empty(), 0, {}, vm.lexical_environment(), vm.running_execution_context().private_environment, FunctionKind::Normal, true, parsing_insights, false, property_key_or_private_name); - initializer->make_method(target); + // FIXME: A potential optimization is not creating the functions here since these are never directly accessible. + auto function_code = create_ast_node(m_initializer->source_range(), copy_initializer.release_nonnull(), name); + FunctionParsingInsights parsing_insights; + parsing_insights.uses_this_from_environment = true; + parsing_insights.uses_this = true; + auto function = ECMAScriptFunctionObject::create(realm, "field"_string, ByteString::empty(), *function_code, FunctionParameters::empty(), 0, {}, vm.lexical_environment(), vm.running_execution_context().private_environment, FunctionKind::Normal, true, parsing_insights, false, property_key_or_private_name); + function->make_method(target); + initializer = function; + } } return ClassValue { diff --git a/Libraries/LibJS/AST.h b/Libraries/LibJS/AST.h index 0519696d9d5..8dd04fa60aa 100644 --- a/Libraries/LibJS/AST.h +++ b/Libraries/LibJS/AST.h @@ -94,6 +94,8 @@ public: virtual bool is_object_expression() const { return false; } virtual bool is_numeric_literal() const { return false; } virtual bool is_string_literal() const { return false; } + virtual bool is_boolean_literal() const { return false; } + virtual bool is_null_literal() const { return false; } virtual bool is_update_expression() const { return false; } virtual bool is_call_expression() const { return false; } virtual bool is_labelled_statement() const { return false; } @@ -1214,6 +1216,8 @@ public: virtual Value value() const override { return Value(m_value); } private: + virtual bool is_boolean_literal() const override { return true; } + bool m_value { false }; }; @@ -1281,6 +1285,9 @@ public: virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; virtual Value value() const override { return js_null(); } + +private: + virtual bool is_null_literal() const override { return true; } }; class RegExpLiteral final : public Expression { @@ -2267,6 +2274,15 @@ inline bool ASTNode::fast_is() const { return is_object_expres template<> inline bool ASTNode::fast_is() const { return is_import_call(); } +template<> +inline bool ASTNode::fast_is() const { return is_numeric_literal(); } + +template<> +inline bool ASTNode::fast_is() const { return is_boolean_literal(); } + +template<> +inline bool ASTNode::fast_is() const { return is_null_literal(); } + template<> inline bool ASTNode::fast_is() const { return is_string_literal(); } diff --git a/Libraries/LibJS/Runtime/ClassFieldDefinition.h b/Libraries/LibJS/Runtime/ClassFieldDefinition.h index 72aada6e339..3cc55bfe788 100644 --- a/Libraries/LibJS/Runtime/ClassFieldDefinition.h +++ b/Libraries/LibJS/Runtime/ClassFieldDefinition.h @@ -17,8 +17,8 @@ using ClassElementName = Variant; // 6.2.10 The ClassFieldDefinition Record Specification Type, https://tc39.es/ecma262/#sec-classfielddefinition-record-specification-type struct ClassFieldDefinition { - ClassElementName name; // [[Name]] - GC::Ptr initializer; // [[Initializer]] + ClassElementName name; // [[Name]] + Variant, Value, Empty> initializer; // [[Initializer]] }; } diff --git a/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp b/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp index b9cb9adf381..3cdb3c247a8 100644 --- a/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp +++ b/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp @@ -553,7 +553,14 @@ void ECMAScriptFunctionObject::visit_edges(Visitor& visitor) visitor.visit(m_bytecode_executable); for (auto& field : m_fields) { - visitor.visit(field.initializer); + field.initializer.visit( + [&visitor](GC::Ref& initializer) { + visitor.visit(initializer); + }, + [&visitor](Value initializer) { + visitor.visit(initializer); + }, + [](Empty) {}); if (auto* property_key_ptr = field.name.get_pointer(); property_key_ptr && property_key_ptr->is_symbol()) visitor.visit(property_key_ptr->as_symbol()); } diff --git a/Libraries/LibJS/Runtime/Object.cpp b/Libraries/LibJS/Runtime/Object.cpp index 69a9172d7f7..0a2d5d9270a 100644 --- a/Libraries/LibJS/Runtime/Object.cpp +++ b/Libraries/LibJS/Runtime/Object.cpp @@ -669,9 +669,14 @@ ThrowCompletionOr Object::define_field(ClassFieldDefinition const& field) auto init_value = js_undefined(); // 3. If initializer is not empty, then - if (initializer) { - // a. Let initValue be ? Call(initializer, receiver). - init_value = TRY(call(vm, initializer, this)); + if (!initializer.has()) { + // OPTIMIZATION: If the initializer is a value (from a literal), we can skip the call. + if (auto const* initializer_value = initializer.get_pointer()) { + init_value = *initializer_value; + } else { + // a. Let initValue be ? Call(initializer, receiver). + init_value = TRY(call(vm, *initializer.get>(), this)); + } } // 4. Else, let initValue be undefined.