From 2a9b6f1d97b79145c1c8052d2cffde272a57557c Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Tue, 8 Apr 2025 00:45:27 +0200 Subject: [PATCH] LibJS: Move computation out of the ECMAScriptFunctionObject constructor We were doing way too much computation every time an ESFO was instantiated. This was particularly sad, since the results of these computations were identical every time! This patch adds a new SharedFunctionInstanceData object that gets shared between all instances of an ESFO instantiated from some kind of AST FunctionNode. ~5% speedup on Speedometer 2.1 :^) --- Libraries/LibJS/AST.cpp | 31 +- Libraries/LibJS/AST.h | 25 +- Libraries/LibJS/Bytecode/Generator.cpp | 54 ++-- .../LibJS/Runtime/AbstractOperations.cpp | 4 +- .../Runtime/ECMAScriptFunctionObject.cpp | 230 +++++++++------ .../LibJS/Runtime/ECMAScriptFunctionObject.h | 279 ++++++++++-------- Libraries/LibJS/Runtime/FunctionObject.h | 2 +- 7 files changed, 367 insertions(+), 258 deletions(-) diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index 7664b483cdb..7e11a1d6bd6 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include namespace JS { @@ -363,7 +364,7 @@ ThrowCompletionOr ClassExpression::create_class_const TRY(class_constructor->internal_set_prototype_of(constructor_parent)); if (!m_super_class.is_null()) - class_constructor->set_constructor_kind(ECMAScriptFunctionObject::ConstructorKind::Derived); + class_constructor->set_constructor_kind(ConstructorKind::Derived); prototype->define_direct_property(vm.names.constructor, class_constructor, Attribute::Writable | Attribute::Configurable); @@ -825,6 +826,34 @@ void BindingPattern::dump(int indent) const } } +FunctionNode::FunctionNode(RefPtr name, ByteString source_text, NonnullRefPtr body, NonnullRefPtr parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, FunctionParsingInsights parsing_insights, bool is_arrow_function, Vector local_variables_names) + : m_name(move(name)) + , m_source_text(move(source_text)) + , m_body(move(body)) + , m_parameters(move(parameters)) + , m_function_length(function_length) + , m_kind(kind) + , m_is_strict_mode(is_strict_mode) + , m_is_arrow_function(is_arrow_function) + , m_parsing_insights(parsing_insights) + , m_local_variables_names(move(local_variables_names)) +{ + if (m_is_arrow_function) + VERIFY(!parsing_insights.might_need_arguments_object); +} + +FunctionNode::~FunctionNode() = default; + +void FunctionNode::set_shared_data(RefPtr shared_data) const +{ + m_shared_data = move(shared_data); +} + +RefPtr FunctionNode::shared_data() const +{ + return m_shared_data; +} + void FunctionNode::dump(int indent, ByteString const& class_name) const { print_indent(indent); diff --git a/Libraries/LibJS/AST.h b/Libraries/LibJS/AST.h index a2cbed7f582..b87fe85109f 100644 --- a/Libraries/LibJS/AST.h +++ b/Libraries/LibJS/AST.h @@ -41,6 +41,7 @@ class FunctionDeclaration; class Identifier; class MemberExpression; class VariableDeclaration; +class SharedFunctionInstanceData; template static inline NonnullRefPtr @@ -746,25 +747,13 @@ public: virtual bool has_name() const = 0; virtual Value instantiate_ordinary_function_expression(VM&, FlyString given_name) const = 0; - virtual ~FunctionNode() { } + RefPtr shared_data() const; + void set_shared_data(RefPtr) const; + + virtual ~FunctionNode(); protected: - FunctionNode(RefPtr name, ByteString source_text, NonnullRefPtr body, NonnullRefPtr parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, FunctionParsingInsights parsing_insights, bool is_arrow_function, Vector local_variables_names) - : m_name(move(name)) - , m_source_text(move(source_text)) - , m_body(move(body)) - , m_parameters(move(parameters)) - , m_function_length(function_length) - , m_kind(kind) - , m_is_strict_mode(is_strict_mode) - , m_is_arrow_function(is_arrow_function) - , m_parsing_insights(parsing_insights) - , m_local_variables_names(move(local_variables_names)) - { - if (m_is_arrow_function) - VERIFY(!parsing_insights.might_need_arguments_object); - } - + FunctionNode(RefPtr name, ByteString source_text, NonnullRefPtr body, NonnullRefPtr parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, FunctionParsingInsights parsing_insights, bool is_arrow_function, Vector local_variables_names); void dump(int indent, ByteString const& class_name) const; RefPtr m_name { nullptr }; @@ -780,6 +769,8 @@ private: FunctionParsingInsights m_parsing_insights; Vector m_local_variables_names; + + mutable RefPtr m_shared_data; }; class FunctionDeclaration final diff --git a/Libraries/LibJS/Bytecode/Generator.cpp b/Libraries/LibJS/Bytecode/Generator.cpp index 5b0be70dbbb..aac28aa6b7f 100644 --- a/Libraries/LibJS/Bytecode/Generator.cpp +++ b/Libraries/LibJS/Bytecode/Generator.cpp @@ -32,10 +32,10 @@ Generator::Generator(VM& vm, GC::Ptr function, M CodeGenerationErrorOr Generator::emit_function_declaration_instantiation(ECMAScriptFunctionObject const& function) { - if (function.m_has_parameter_expressions) { + if (function.shared_data().m_has_parameter_expressions) { bool has_non_local_parameters = false; - for (auto const& parameter_name : function.m_parameter_names) { - if (parameter_name.value == ECMAScriptFunctionObject::ParameterIsLocal::No) { + for (auto const& parameter_name : function.shared_data().m_parameter_names) { + if (parameter_name.value == SharedFunctionInstanceData::ParameterIsLocal::No) { has_non_local_parameters = true; break; } @@ -44,32 +44,32 @@ CodeGenerationErrorOr Generator::emit_function_declaration_instantiation(E emit(); } - for (auto const& parameter_name : function.m_parameter_names) { - if (parameter_name.value == ECMAScriptFunctionObject::ParameterIsLocal::No) { + for (auto const& parameter_name : function.shared_data().m_parameter_names) { + if (parameter_name.value == SharedFunctionInstanceData::ParameterIsLocal::No) { auto id = intern_identifier(parameter_name.key); emit(id, Op::EnvironmentMode::Lexical, false); - if (function.m_has_duplicates) { + if (function.shared_data().m_has_duplicates) { emit(id, add_constant(js_undefined())); } } } - if (function.m_arguments_object_needed) { + if (function.shared_data().m_arguments_object_needed) { Optional dst; - auto local_var_index = function.m_local_variables_names.find_first_index("arguments"_fly_string); + auto local_var_index = function.shared_data().m_local_variables_names.find_first_index("arguments"_fly_string); if (local_var_index.has_value()) dst = local(local_var_index.value()); - if (function.m_strict || !function.has_simple_parameter_list()) { - emit(dst, Op::CreateArguments::Kind::Unmapped, function.m_strict); + if (function.is_strict_mode() || !function.has_simple_parameter_list()) { + emit(dst, Op::CreateArguments::Kind::Unmapped, function.is_strict_mode()); } else { - emit(dst, Op::CreateArguments::Kind::Mapped, function.m_strict); + emit(dst, Op::CreateArguments::Kind::Mapped, function.is_strict_mode()); } } auto const& formal_parameters = function.formal_parameters(); - for (u32 param_index = 0; param_index < formal_parameters->size(); ++param_index) { - auto const& parameter = formal_parameters->parameters()[param_index]; + for (u32 param_index = 0; param_index < formal_parameters.size(); ++param_index) { + auto const& parameter = formal_parameters.parameters()[param_index]; if (parameter.is_rest) { auto argument_reg = allocate_register(); @@ -104,7 +104,7 @@ CodeGenerationErrorOr Generator::emit_function_declaration_instantiation(E auto id = intern_identifier((*identifier)->string()); auto argument_reg = allocate_register(); emit(argument_reg.operand(), param_index); - if (function.m_has_duplicates) { + if (function.shared_data().m_has_duplicates) { emit(id, argument_reg.operand()); } else { emit(id, argument_reg.operand()); @@ -113,18 +113,18 @@ CodeGenerationErrorOr Generator::emit_function_declaration_instantiation(E } else if (auto const* binding_pattern = parameter.binding.get_pointer>(); binding_pattern) { auto input_operand = allocate_register(); emit(input_operand.operand(), param_index); - auto init_mode = function.m_has_duplicates ? Op::BindingInitializationMode::Set : Bytecode::Op::BindingInitializationMode::Initialize; + auto init_mode = function.shared_data().m_has_duplicates ? Op::BindingInitializationMode::Set : Bytecode::Op::BindingInitializationMode::Initialize; TRY((*binding_pattern)->generate_bytecode(*this, init_mode, input_operand, false)); } } ScopeNode const* scope_body = nullptr; - if (is(*function.m_ecmascript_code)) - scope_body = static_cast(function.m_ecmascript_code.ptr()); + if (is(function.ecmascript_code())) + scope_body = &static_cast(function.ecmascript_code()); - if (!function.m_has_parameter_expressions) { + if (!function.shared_data().m_has_parameter_expressions) { if (scope_body) { - for (auto const& variable_to_initialize : function.m_var_names_to_initialize_binding) { + for (auto const& variable_to_initialize : function.shared_data().m_var_names_to_initialize_binding) { auto const& id = variable_to_initialize.identifier; if (id.is_local()) { emit(local(id.local_variable_index()), add_constant(js_undefined())); @@ -138,7 +138,7 @@ CodeGenerationErrorOr Generator::emit_function_declaration_instantiation(E } else { bool has_non_local_parameters = false; if (scope_body) { - for (auto const& variable_to_initialize : function.m_var_names_to_initialize_binding) { + for (auto const& variable_to_initialize : function.shared_data().m_var_names_to_initialize_binding) { auto const& id = variable_to_initialize.identifier; if (!id.is_local()) { has_non_local_parameters = true; @@ -148,10 +148,10 @@ CodeGenerationErrorOr Generator::emit_function_declaration_instantiation(E } if (has_non_local_parameters) - emit(function.m_var_environment_bindings_count); + emit(function.shared_data().m_var_environment_bindings_count); if (scope_body) { - for (auto const& variable_to_initialize : function.m_var_names_to_initialize_binding) { + for (auto const& variable_to_initialize : function.shared_data().m_var_names_to_initialize_binding) { auto const& id = variable_to_initialize.identifier; auto initial_value = allocate_register(); if (!variable_to_initialize.parameter_binding || variable_to_initialize.function_name) { @@ -175,18 +175,18 @@ CodeGenerationErrorOr Generator::emit_function_declaration_instantiation(E } } - if (!function.m_strict && scope_body) { - for (auto const& function_name : function.m_function_names_to_initialize_binding) { + if (!function.is_strict_mode() && scope_body) { + for (auto const& function_name : function.shared_data().m_function_names_to_initialize_binding) { auto intern_id = intern_identifier(function_name); emit(intern_id, Op::EnvironmentMode::Var, false); emit(intern_id, add_constant(js_undefined())); } } - if (!function.m_strict) { + if (!function.is_strict_mode()) { bool can_elide_lexical_environment = !scope_body || !scope_body->has_non_local_lexical_declarations(); if (!can_elide_lexical_environment) { - emit(function.m_lex_environment_bindings_count); + emit(function.shared_data().m_lex_environment_bindings_count); } } @@ -206,7 +206,7 @@ CodeGenerationErrorOr Generator::emit_function_declaration_instantiation(E })); } - for (auto const& declaration : function.m_functions_to_initialize) { + for (auto const& declaration : function.shared_data().m_functions_to_initialize) { auto const& identifier = *declaration.name_identifier(); if (identifier.is_local()) { auto local_index = identifier.local_variable_index(); diff --git a/Libraries/LibJS/Runtime/AbstractOperations.cpp b/Libraries/LibJS/Runtime/AbstractOperations.cpp index 5a2be62d536..5259d57e0c8 100644 --- a/Libraries/LibJS/Runtime/AbstractOperations.cpp +++ b/Libraries/LibJS/Runtime/AbstractOperations.cpp @@ -426,7 +426,7 @@ GC::Ref new_function_environment(ECMAScriptFunctionObject& env->set_function_object(function); // 3. If F.[[ThisMode]] is lexical, set env.[[ThisBindingStatus]] to lexical. - if (function.this_mode() == ECMAScriptFunctionObject::ThisMode::Lexical) + if (function.this_mode() == ThisMode::Lexical) env->set_this_binding_status(FunctionEnvironment::ThisBindingStatus::Lexical); // 4. Else, set env.[[ThisBindingStatus]] to uninitialized. else @@ -555,7 +555,7 @@ ThrowCompletionOr perform_eval(VM& vm, Value x, CallerMode strict_caller, in_method = this_function_environment_record.has_super_binding(); // iv. If F.[[ConstructorKind]] is derived, set inDerivedConstructor to true. - if (function.constructor_kind() == ECMAScriptFunctionObject::ConstructorKind::Derived) + if (function.constructor_kind() == ConstructorKind::Derived) in_derived_constructor = true; // v. Let classFieldInitializerName be F.[[ClassFieldInitializerName]]. diff --git a/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp b/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp index 96e4df787e5..d741a3c4578 100644 --- a/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp +++ b/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp @@ -1,7 +1,7 @@ /* * Copyright (c) 2020, Stephan Unverwerth * Copyright (c) 2020-2023, Linus Groh - * Copyright (c) 2023, Andreas Kling + * Copyright (c) 2023-2025, Andreas Kling * Copyright (c) 2023, Shannon Booth * * SPDX-License-Identifier: BSD-2-Clause @@ -33,7 +33,7 @@ namespace JS { GC_DEFINE_ALLOCATOR(ECMAScriptFunctionObject); -GC::Ref ECMAScriptFunctionObject::create(Realm& realm, FlyString name, ByteString source_text, Statement const& ecmascript_code, NonnullRefPtr parameters, i32 m_function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind kind, bool is_strict, FunctionParsingInsights parsing_insights, bool is_arrow_function, Variant class_field_initializer_name) +GC::Ref ECMAScriptFunctionObject::create(Realm& realm, FlyString name, ByteString source_text, Statement const& ecmascript_code, NonnullRefPtr parameters, i32 function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind kind, bool is_strict, FunctionParsingInsights parsing_insights, bool is_arrow_function, Variant class_field_initializer_name) { Object* prototype = nullptr; switch (kind) { @@ -50,12 +50,49 @@ GC::Ref ECMAScriptFunctionObject::create(Realm& realm, prototype = realm.intrinsics().async_generator_function_prototype(); break; } - return realm.create(move(name), move(source_text), ecmascript_code, move(parameters), m_function_length, move(local_variables_names), parent_environment, private_environment, *prototype, kind, is_strict, parsing_insights, is_arrow_function, move(class_field_initializer_name)); + + auto shared_data = adopt_ref(*new SharedFunctionInstanceData( + realm.vm(), + kind, + move(name), + function_length, + *parameters, + ecmascript_code, + source_text, + is_strict, + is_arrow_function, + parsing_insights, + move(local_variables_names))); + + shared_data->m_class_field_initializer_name = move(class_field_initializer_name); + + return realm.create( + move(shared_data), + parent_environment, + private_environment, + *prototype); } -GC::Ref ECMAScriptFunctionObject::create(Realm& realm, FlyString name, Object& prototype, ByteString source_text, Statement const& ecmascript_code, NonnullRefPtr parameters, i32 m_function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind kind, bool is_strict, FunctionParsingInsights parsing_insights, bool is_arrow_function, Variant class_field_initializer_name) +GC::Ref ECMAScriptFunctionObject::create(Realm& realm, FlyString name, Object& prototype, ByteString source_text, Statement const& ecmascript_code, NonnullRefPtr parameters, i32 function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind kind, bool is_strict, FunctionParsingInsights parsing_insights, bool is_arrow_function, Variant class_field_initializer_name) { - return realm.create(move(name), move(source_text), ecmascript_code, move(parameters), m_function_length, move(local_variables_names), parent_environment, private_environment, prototype, kind, is_strict, parsing_insights, is_arrow_function, move(class_field_initializer_name)); + auto shared_data = adopt_ref(*new SharedFunctionInstanceData( + realm.vm(), + kind, + move(name), + function_length, + *parameters, + ecmascript_code, + source_text, + is_strict, + is_arrow_function, + parsing_insights, + move(local_variables_names))); + shared_data->m_class_field_initializer_name = move(class_field_initializer_name); + return realm.create( + move(shared_data), + parent_environment, + private_environment, + prototype); } GC::Ref ECMAScriptFunctionObject::create_from_function_node( @@ -81,60 +118,62 @@ GC::Ref ECMAScriptFunctionObject::create_from_function break; } - return create( - *realm, - move(name), - *prototype, - function_node.source_text(), - *function_node.body_ptr(), - function_node.parameters(), - function_node.function_length(), - function_node.local_variables_names(), + RefPtr shared_data = function_node.shared_data(); + + if (!shared_data) { + shared_data = adopt_ref(*new SharedFunctionInstanceData(realm->vm(), + function_node.kind(), + move(name), + function_node.function_length(), + function_node.parameters(), + *function_node.body_ptr(), + function_node.source_text(), + function_node.is_strict_mode(), + function_node.is_arrow_function(), + function_node.parsing_insights(), + function_node.local_variables_names())); + function_node.set_shared_data(shared_data); + } + + return realm->create( + shared_data.release_nonnull(), parent_environment, private_environment, - function_node.kind(), - function_node.is_strict_mode(), - function_node.parsing_insights(), - function_node.is_arrow_function(), - Variant {}); + *prototype); } -ECMAScriptFunctionObject::ECMAScriptFunctionObject(FlyString name, ByteString source_text, Statement const& ecmascript_code, NonnullRefPtr formal_parameters, i32 function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, Object& prototype, FunctionKind kind, bool strict, FunctionParsingInsights parsing_insights, bool is_arrow_function, Variant class_field_initializer_name) - : FunctionObject(prototype) +SharedFunctionInstanceData::SharedFunctionInstanceData( + VM& vm, + FunctionKind kind, + FlyString name, + i32 function_length, + NonnullRefPtr formal_parameters, + NonnullRefPtr ecmascript_code, + ByteString source_text, + bool strict, + bool is_arrow_function, + FunctionParsingInsights const& parsing_insights, + Vector local_variables_names) + : m_formal_parameters(move(formal_parameters)) + , m_ecmascript_code(move(ecmascript_code)) , m_name(move(name)) - , m_function_length(function_length) - , m_local_variables_names(move(local_variables_names)) - , m_environment(parent_environment) - , m_private_environment(private_environment) - , m_formal_parameters(move(formal_parameters)) - , m_ecmascript_code(ecmascript_code) - , m_realm(&prototype.shape().realm()) , m_source_text(move(source_text)) - , m_class_field_initializer_name(move(class_field_initializer_name)) + , m_local_variables_names(move(local_variables_names)) + , m_function_length(function_length) + , m_kind(kind) , m_strict(strict) , m_might_need_arguments_object(parsing_insights.might_need_arguments_object) , m_contains_direct_call_to_eval(parsing_insights.contains_direct_call_to_eval) , m_is_arrow_function(is_arrow_function) - , m_kind(kind) + , m_uses_this(parsing_insights.uses_this) { - if (!m_is_arrow_function && m_kind == FunctionKind::Normal) - unsafe_set_shape(m_realm->intrinsics().normal_function_shape()); - - // NOTE: This logic is from OrdinaryFunctionCreate, https://tc39.es/ecma262/#sec-ordinaryfunctioncreate - - // 9. If thisMode is lexical-this, set F.[[ThisMode]] to lexical. if (m_is_arrow_function) m_this_mode = ThisMode::Lexical; - // 10. Else if Strict is true, set F.[[ThisMode]] to strict. else if (m_strict) m_this_mode = ThisMode::Strict; else - // 11. Else, set F.[[ThisMode]] to global. m_this_mode = ThisMode::Global; - // 15. Set F.[[ScriptOrModule]] to GetActiveScriptOrModule(). - m_script_or_module = vm().get_active_script_or_module(); - // 15.1.3 Static Semantics: IsSimpleParameterList, https://tc39.es/ecma262/#sec-static-semantics-issimpleparameterlist m_has_simple_parameter_list = all_of(m_formal_parameters->parameters(), [&](auto& parameter) { if (parameter.is_rest) @@ -157,7 +196,7 @@ ECMAScriptFunctionObject::ECMAScriptFunctionObject(FlyString name, ByteString so // 3. Let strict be func.[[Strict]]. // 4. Let formals be func.[[FormalParameters]]. - auto const& formals = m_formal_parameters; + auto const& formals = *m_formal_parameters; // 5. Let parameterNames be the BoundNames of formals. // 6. If parameterNames has any duplicate entries, let hasDuplicates be true. Otherwise, let hasDuplicates be false. @@ -165,7 +204,7 @@ ECMAScriptFunctionObject::ECMAScriptFunctionObject(FlyString name, ByteString so size_t parameters_in_environment = 0; // NOTE: This loop performs step 5, 6, and 8. - for (auto const& parameter : formals->parameters()) { + for (auto const& parameter : formals.parameters()) { if (parameter.default_value) m_has_parameter_expressions = true; @@ -194,13 +233,13 @@ ECMAScriptFunctionObject::ECMAScriptFunctionObject(FlyString name, ByteString so m_arguments_object_needed = m_might_need_arguments_object; // 16. If func.[[ThisMode]] is lexical, then - if (this_mode() == ThisMode::Lexical) { + if (m_this_mode == ThisMode::Lexical) { // a. NOTE: Arrow functions never have an arguments object. // b. Set argumentsObjectNeeded to false. m_arguments_object_needed = false; } // 17. Else if parameterNames contains "arguments", then - else if (m_parameter_names.contains(vm().names.arguments.as_string())) { + else if (m_parameter_names.contains(vm.names.arguments.as_string())) { // a. Set argumentsObjectNeeded to false. m_arguments_object_needed = false; } @@ -218,7 +257,7 @@ ECMAScriptFunctionObject::ECMAScriptFunctionObject(FlyString name, ByteString so m_functions_to_initialize.append(function); })); - auto const& arguments_name = vm().names.arguments.as_string(); + auto const& arguments_name = vm.names.arguments.as_string(); if (!m_has_parameter_expressions && function_names.contains(arguments_name)) m_arguments_object_needed = false; @@ -238,7 +277,7 @@ ECMAScriptFunctionObject::ECMAScriptFunctionObject(FlyString name, ByteString so size_t parameter_environment_bindings_count = 0; // 19. If strict is true or hasParameterExpressions is false, then - if (m_strict || !m_has_parameter_expressions) { + if (strict || !m_has_parameter_expressions) { // a. NOTE: Only a single Environment Record is needed for the parameters, since calls to eval in strict mode code cannot create new bindings which are visible outside of the eval. // b. Let env be the LexicalEnvironment of calleeContext // NOTE: Here we are only interested in the size of the environment. @@ -256,13 +295,13 @@ ECMAScriptFunctionObject::ECMAScriptFunctionObject(FlyString name, ByteString so HashMap parameter_bindings; - auto arguments_object_needs_binding = m_arguments_object_needed && !m_local_variables_names.contains_slow(vm().names.arguments.as_string()); + auto arguments_object_needs_binding = m_arguments_object_needed && !m_local_variables_names.contains_slow(vm.names.arguments.as_string()); // 22. If argumentsObjectNeeded is true, then if (m_arguments_object_needed) { // f. Let parameterBindings be the list-concatenation of parameterNames and « "arguments" ». parameter_bindings = m_parameter_names; - parameter_bindings.set(vm().names.arguments.as_string(), ParameterIsLocal::No); + parameter_bindings.set(vm.names.arguments.as_string(), ParameterIsLocal::No); if (arguments_object_needs_binding) (*environment_size)++; @@ -341,7 +380,7 @@ ECMAScriptFunctionObject::ECMAScriptFunctionObject(FlyString name, ByteString so if (parameter_bindings.contains(function_name)) return; - if (!instantiated_var_names.contains(function_name) && function_name != vm().names.arguments.as_string()) { + if (!instantiated_var_names.contains(function_name) && function_name != vm.names.arguments.as_string()) { m_function_names_to_initialize_binding.append(function_name); instantiated_var_names.set(function_name, ParameterIsLocal::No); (*var_environment_size)++; @@ -376,7 +415,24 @@ ECMAScriptFunctionObject::ECMAScriptFunctionObject(FlyString name, ByteString so } m_function_environment_needed = arguments_object_needs_binding || m_function_environment_bindings_count > 0 || m_var_environment_bindings_count > 0 || m_lex_environment_bindings_count > 0 || parsing_insights.uses_this_from_environment || m_contains_direct_call_to_eval; - m_uses_this = parsing_insights.uses_this; +} + +ECMAScriptFunctionObject::ECMAScriptFunctionObject( + NonnullRefPtr shared_data, + Environment* parent_environment, + PrivateEnvironment* private_environment, + Object& prototype) + : FunctionObject(prototype) + , m_shared_data(move(shared_data)) + , m_environment(parent_environment) + , m_private_environment(private_environment) + , m_realm(&prototype.shape().realm()) +{ + if (!is_arrow_function() && kind() == FunctionKind::Normal) + unsafe_set_shape(m_realm->intrinsics().normal_function_shape()); + + // 15. Set F.[[ScriptOrModule]] to GetActiveScriptOrModule(). + m_script_or_module = vm().get_active_script_or_module(); } void ECMAScriptFunctionObject::initialize(Realm& realm) @@ -389,22 +445,22 @@ void ECMAScriptFunctionObject::initialize(Realm& realm) // which must give the properties in chronological order which in this case is the order they // are defined in the spec. - m_name_string = PrimitiveString::create(vm, m_name); + m_name_string = PrimitiveString::create(vm, name()); - if (!m_is_arrow_function && m_kind == FunctionKind::Normal) { - put_direct(realm.intrinsics().normal_function_length_offset(), Value(m_function_length)); + if (!is_arrow_function() && kind() == FunctionKind::Normal) { + put_direct(realm.intrinsics().normal_function_length_offset(), Value(function_length())); put_direct(realm.intrinsics().normal_function_name_offset(), m_name_string); auto prototype = Object::create_with_premade_shape(realm.intrinsics().normal_function_prototype_shape()); prototype->put_direct(realm.intrinsics().normal_function_prototype_constructor_offset(), this); put_direct(realm.intrinsics().normal_function_prototype_offset(), prototype); } else { - MUST(define_property_or_throw(vm.names.length, { .value = Value(m_function_length), .writable = false, .enumerable = false, .configurable = true })); + MUST(define_property_or_throw(vm.names.length, { .value = Value(function_length()), .writable = false, .enumerable = false, .configurable = true })); MUST(define_property_or_throw(vm.names.name, { .value = m_name_string, .writable = false, .enumerable = false, .configurable = true })); - if (!m_is_arrow_function) { + if (!is_arrow_function()) { Object* prototype = nullptr; - switch (m_kind) { + switch (kind()) { case FunctionKind::Normal: VERIFY_NOT_REACHED(); break; @@ -420,7 +476,7 @@ void ECMAScriptFunctionObject::initialize(Realm& realm) } // 27.7.4 AsyncFunction Instances, https://tc39.es/ecma262/#sec-async-function-instances // AsyncFunction instances do not have a prototype property as they are not constructible. - if (m_kind != FunctionKind::Async) + if (kind() != FunctionKind::Async) define_direct_property(vm.names.prototype, prototype, Attribute::Writable); } } @@ -437,11 +493,11 @@ ThrowCompletionOr ECMAScriptFunctionObject::internal_call(Value this_argu auto callee_context = ExecutionContext::create(); // Non-standard - callee_context->arguments.ensure_capacity(max(arguments_list.size(), m_formal_parameters->size())); + callee_context->arguments.ensure_capacity(max(arguments_list.size(), formal_parameters().size())); callee_context->arguments.append(arguments_list.data(), arguments_list.size()); callee_context->passed_argument_count = arguments_list.size(); - if (arguments_list.size() < m_formal_parameters->size()) { - for (size_t i = arguments_list.size(); i < m_formal_parameters->size(); ++i) + if (arguments_list.size() < formal_parameters().size()) { + for (size_t i = arguments_list.size(); i < formal_parameters().size(); ++i) callee_context->arguments.append(js_undefined()); } @@ -453,10 +509,10 @@ ThrowCompletionOr ECMAScriptFunctionObject::internal_call(Value this_argu VERIFY(&vm.running_execution_context() == callee_context); // 4. If F.[[IsClassConstructor]] is true, then - if (m_is_class_constructor) { + if (is_class_constructor()) { // a. Let error be a newly created TypeError object. // b. NOTE: error is created in calleeContext with F's associated Realm Record. - auto throw_completion = vm.throw_completion(ErrorType::ClassConstructorWithoutNew, m_name); + auto throw_completion = vm.throw_completion(ErrorType::ClassConstructorWithoutNew, name()); // c. Remove calleeContext from the execution context stack and restore callerContext as the running execution context. vm.pop_execution_context(); @@ -466,7 +522,7 @@ ThrowCompletionOr ECMAScriptFunctionObject::internal_call(Value this_argu } // 5. Perform OrdinaryCallBindThis(F, calleeContext, thisArgument). - if (m_uses_this) + if (uses_this()) ordinary_call_bind_this(*callee_context, this_argument); // 6. Let result be Completion(OrdinaryCallEvaluateBody(F, argumentsList)). @@ -494,11 +550,11 @@ ThrowCompletionOr> ECMAScriptFunctionObject::internal_construct( auto callee_context = ExecutionContext::create(); // Non-standard - callee_context->arguments.ensure_capacity(max(arguments_list.size(), m_formal_parameters->size())); + callee_context->arguments.ensure_capacity(max(arguments_list.size(), formal_parameters().size())); callee_context->arguments.append(arguments_list.data(), arguments_list.size()); callee_context->passed_argument_count = arguments_list.size(); - if (arguments_list.size() < m_formal_parameters->size()) { - for (size_t i = arguments_list.size(); i < m_formal_parameters->size(); ++i) + if (arguments_list.size() < formal_parameters().size()) { + for (size_t i = arguments_list.size(); i < formal_parameters().size(); ++i) callee_context->arguments.append(js_undefined()); } @@ -506,7 +562,7 @@ ThrowCompletionOr> ECMAScriptFunctionObject::internal_construct( // NOTE: No-op, kept by the VM in its execution context stack. // 2. Let kind be F.[[ConstructorKind]]. - auto kind = m_constructor_kind; + auto kind = constructor_kind(); GC::Ptr this_argument; @@ -526,7 +582,7 @@ ThrowCompletionOr> ECMAScriptFunctionObject::internal_construct( // 6. If kind is base, then if (kind == ConstructorKind::Base) { // a. Perform OrdinaryCallBindThis(F, calleeContext, thisArgument). - if (m_uses_this) + if (uses_this()) ordinary_call_bind_this(*callee_context, this_argument); // b. Let initializeResult be Completion(InitializeInstanceElements(thisArgument, F)). @@ -631,7 +687,7 @@ ThrowCompletionOr ECMAScriptFunctionObject::prepare_for_ordinary_call(Exec auto& vm = this->vm(); // Non-standard - callee_context.is_strict_mode = m_strict; + callee_context.is_strict_mode = is_strict_mode(); // 1. Let callerContext be the running execution context. // 2. Let calleeContext be a new ECMAScript code execution context. @@ -661,10 +717,10 @@ ThrowCompletionOr ECMAScriptFunctionObject::prepare_for_ordinary_call(Exec // 6. Set the ScriptOrModule of calleeContext to F.[[ScriptOrModule]]. callee_context.script_or_module = m_script_or_module; - if (m_function_environment_needed) { + if (function_environment_needed()) { // 7. Let localEnv be NewFunctionEnvironment(F, newTarget). auto local_environment = new_function_environment(*this, new_target); - local_environment->ensure_capacity(m_function_environment_bindings_count); + local_environment->ensure_capacity(shared_data().m_function_environment_bindings_count); // 8. Set the LexicalEnvironment of calleeContext to localEnv. callee_context.lexical_environment = local_environment; @@ -695,10 +751,8 @@ void ECMAScriptFunctionObject::ordinary_call_bind_this(ExecutionContext& callee_ auto& vm = this->vm(); // 1. Let thisMode be F.[[ThisMode]]. - auto this_mode = m_this_mode; - // If thisMode is lexical, return unused. - if (this_mode == ThisMode::Lexical) + if (this_mode() == ThisMode::Lexical) return; // 3. Let calleeRealm be F.[[Realm]]. @@ -718,7 +772,7 @@ void ECMAScriptFunctionObject::ordinary_call_bind_this(ExecutionContext& callee_ Value this_value; // 5. If thisMode is strict, let thisValue be thisArgument. - if (this_mode == ThisMode::Strict) { + if (this_mode() == ThisMode::Strict) { this_value = this_argument; } // 6. Else, @@ -746,7 +800,7 @@ void ECMAScriptFunctionObject::ordinary_call_bind_this(ExecutionContext& callee_ // 8. Assert: The next step never returns an abrupt completion because localEnv.[[ThisBindingStatus]] is not initialized. // 9. Perform ! localEnv.BindThisValue(thisValue). callee_context.this_value = this_value; - if (m_function_environment_needed) + if (function_environment_needed()) MUST(as(*local_env).bind_this_value(vm, this_value)); // 10. Return unused. @@ -859,17 +913,17 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body() auto& realm = *vm.current_realm(); if (!m_bytecode_executable) { - if (!m_ecmascript_code->bytecode_executable()) { + if (!ecmascript_code().bytecode_executable()) { if (is_module_wrapper()) { - const_cast(*m_ecmascript_code).set_bytecode_executable(TRY(Bytecode::compile(vm, *m_ecmascript_code, m_kind, m_name))); + const_cast(ecmascript_code()).set_bytecode_executable(TRY(Bytecode::compile(vm, ecmascript_code(), kind(), name()))); } else { - const_cast(*m_ecmascript_code).set_bytecode_executable(TRY(Bytecode::compile(vm, *this))); + const_cast(ecmascript_code()).set_bytecode_executable(TRY(Bytecode::compile(vm, *this))); } } - m_bytecode_executable = m_ecmascript_code->bytecode_executable(); + m_bytecode_executable = ecmascript_code().bytecode_executable(); } - vm.running_execution_context().registers_and_constants_and_locals.resize_with_default_value(m_local_variables_names.size() + m_bytecode_executable->number_of_registers + m_bytecode_executable->constants.size(), js_special_empty_value()); + vm.running_execution_context().registers_and_constants_and_locals.resize_with_default_value(local_variables_names().size() + m_bytecode_executable->number_of_registers + m_bytecode_executable->constants.size(), js_special_empty_value()); auto result_and_frame = vm.bytecode_interpreter().run_executable(*m_bytecode_executable, {}); @@ -880,10 +934,10 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body() // NOTE: Running the bytecode should eventually return a completion. // Until it does, we assume "return" and include the undefined fallback from the call site. - if (m_kind == FunctionKind::Normal) + if (kind() == FunctionKind::Normal) return { Completion::Type::Return, result }; - if (m_kind == FunctionKind::AsyncGenerator) { + if (kind() == FunctionKind::AsyncGenerator) { auto async_generator_object = TRY(AsyncGenerator::create(realm, result, this, vm.running_execution_context().copy())); return { Completion::Type::Return, async_generator_object }; } @@ -892,18 +946,18 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body() // NOTE: Async functions are entirely transformed to generator functions, and wrapped in a custom driver that returns a promise // See AwaitExpression::generate_bytecode() for the transformation. - if (m_kind == FunctionKind::Async) + if (kind() == FunctionKind::Async) return { Completion::Type::Return, AsyncFunctionDriverWrapper::create(realm, generator_object) }; - VERIFY(m_kind == FunctionKind::Generator); + VERIFY(kind() == FunctionKind::Generator); return { Completion::Type::Return, generator_object }; } void ECMAScriptFunctionObject::set_name(FlyString const& name) { auto& vm = this->vm(); - m_name = name; - m_name_string = PrimitiveString::create(vm, m_name); + const_cast(shared_data()).m_name = name; + m_name_string = PrimitiveString::create(vm, name); MUST(define_property_or_throw(vm.names.name, { .value = m_name_string, .writable = false, .enumerable = false, .configurable = true })); } } diff --git a/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h b/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h index 949be80fabf..e9ba2439f92 100644 --- a/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h +++ b/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021, Andreas Kling + * Copyright (c) 2020-2025, Andreas Kling * Copyright (c) 2020-2022, Linus Groh * Copyright (c) 2023, Andreas Kling * @@ -22,138 +22,52 @@ void async_block_start(VM&, T const& async_body, PromiseCapability const&, Execu template void async_function_start(VM&, PromiseCapability const&, T const& async_function_body); -// 10.2 ECMAScript Function Objects, https://tc39.es/ecma262/#sec-ecmascript-function-objects -class ECMAScriptFunctionObject final : public FunctionObject { - JS_OBJECT(ECMAScriptFunctionObject, FunctionObject); - GC_DECLARE_ALLOCATOR(ECMAScriptFunctionObject); +enum class ThisMode : u8 { + Lexical, + Strict, + Global, +}; +enum class ConstructorKind : u8 { + Base, + Derived, +}; + +class SharedFunctionInstanceData : public RefCounted { public: - enum class ConstructorKind : u8 { - Base, - Derived, - }; - - enum class ThisMode : u8 { - Lexical, - Strict, - Global, - }; - - static GC::Ref create(Realm&, FlyString name, ByteString source_text, Statement const& ecmascript_code, NonnullRefPtr parameters, i32 m_function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind, bool is_strict, FunctionParsingInsights, bool is_arrow_function = false, Variant class_field_initializer_name = {}); - static GC::Ref create(Realm&, FlyString name, Object& prototype, ByteString source_text, Statement const& ecmascript_code, NonnullRefPtr parameters, i32 m_function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind, bool is_strict, FunctionParsingInsights, bool is_arrow_function = false, Variant class_field_initializer_name = {}); - - [[nodiscard]] static GC::Ref create_from_function_node( - FunctionNode const&, + SharedFunctionInstanceData( + VM& vm, + FunctionKind, FlyString name, - GC::Ref, - GC::Ptr parent_environment, - GC::Ptr); + i32 function_length, + NonnullRefPtr, + NonnullRefPtr ecmascript_code, + ByteString source_text, + bool strict, + bool is_arrow_function, + FunctionParsingInsights const&, + Vector local_variables_names); - virtual void initialize(Realm&) override; - virtual ~ECMAScriptFunctionObject() override = default; - - virtual ThrowCompletionOr internal_call(Value this_argument, ReadonlySpan arguments_list) override; - virtual ThrowCompletionOr> internal_construct(ReadonlySpan arguments_list, FunctionObject& new_target) override; - - void make_method(Object& home_object); - - [[nodiscard]] bool is_module_wrapper() const { return m_is_module_wrapper; } - void set_is_module_wrapper(bool b) { m_is_module_wrapper = b; } - - Statement const& ecmascript_code() const { return m_ecmascript_code; } - virtual NonnullRefPtr const& formal_parameters() const override { return m_formal_parameters; } - - virtual FlyString const& name() const override { return m_name; } - void set_name(FlyString const& name); - - void set_is_class_constructor() { m_is_class_constructor = true; } - - auto& bytecode_executable() const { return m_bytecode_executable; } - - Environment* environment() { return m_environment; } - virtual Realm* realm() const override { return m_realm; } - - ConstructorKind constructor_kind() const { return m_constructor_kind; } - void set_constructor_kind(ConstructorKind constructor_kind) { m_constructor_kind = constructor_kind; } - - ThisMode this_mode() const { return m_this_mode; } - - Object* home_object() const { return m_home_object; } - void set_home_object(Object* home_object) { m_home_object = home_object; } - - ByteString const& source_text() const { return m_source_text; } - void set_source_text(ByteString source_text) { m_source_text = move(source_text); } - - Vector const& fields() const { return m_fields; } - void add_field(ClassFieldDefinition field) { m_fields.append(move(field)); } - - Vector const& private_methods() const { return m_private_methods; } - void add_private_method(PrivateElement method) { m_private_methods.append(move(method)); } - - // This is for IsSimpleParameterList (static semantics) - bool has_simple_parameter_list() const { return m_has_simple_parameter_list; } - - // Equivalent to absence of [[Construct]] - virtual bool has_constructor() const override { return m_kind == FunctionKind::Normal && !m_is_arrow_function; } - - virtual Vector const& local_variables_names() const override { return m_local_variables_names; } - - FunctionKind kind() const { return m_kind; } - - // This is used by LibWeb to disassociate event handler attribute callback functions from the nearest script on the call stack. - // https://html.spec.whatwg.org/multipage/webappapis.html#getting-the-current-value-of-the-event-handler Step 3.11 - void set_script_or_module(ScriptOrModule script_or_module) { m_script_or_module = move(script_or_module); } - - Variant const& class_field_initializer_name() const { return m_class_field_initializer_name; } - - bool allocates_function_environment() const { return m_function_environment_needed; } - - friend class Bytecode::Generator; - -protected: - virtual bool is_strict_mode() const final { return m_strict; } - - virtual Completion ordinary_call_evaluate_body(); - -private: - ECMAScriptFunctionObject(FlyString name, ByteString source_text, Statement const& ecmascript_code, NonnullRefPtr, i32 m_function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, Object& prototype, FunctionKind, bool is_strict, FunctionParsingInsights, bool is_arrow_function, Variant class_field_initializer_name); - - virtual bool is_ecmascript_function_object() const override { return true; } - virtual void visit_edges(Visitor&) override; - - ThrowCompletionOr prepare_for_ordinary_call(ExecutionContext& callee_context, Object* new_target); - void ordinary_call_bind_this(ExecutionContext&, Value this_argument); + RefPtr m_formal_parameters; // [[FormalParameters]] + RefPtr m_ecmascript_code; // [[ECMAScriptCode]] FlyString m_name; - GC::Ptr m_name_string; + ByteString m_source_text; // [[SourceText]] - GC::Ptr m_bytecode_executable; - i32 m_function_length { 0 }; Vector m_local_variables_names; - // Internal Slots of ECMAScript Function Objects, https://tc39.es/ecma262/#table-internal-slots-of-ecmascript-function-objects - GC::Ptr m_environment; // [[Environment]] - GC::Ptr m_private_environment; // [[PrivateEnvironment]] - NonnullRefPtr m_formal_parameters; // [[FormalParameters]] - NonnullRefPtr m_ecmascript_code; // [[ECMAScriptCode]] - GC::Ptr m_realm; // [[Realm]] - ScriptOrModule m_script_or_module; // [[ScriptOrModule]] - GC::Ptr m_home_object; // [[HomeObject]] - ByteString m_source_text; // [[SourceText]] - Vector m_fields; // [[Fields]] - Vector m_private_methods; // [[PrivateMethods]] - Variant m_class_field_initializer_name; // [[ClassFieldInitializerName]] - ConstructorKind m_constructor_kind : 1 { ConstructorKind::Base }; // [[ConstructorKind]] - bool m_strict : 1 { false }; // [[Strict]] - bool m_is_class_constructor : 1 { false }; // [[IsClassConstructor]] - ThisMode m_this_mode : 2 { ThisMode::Global }; // [[ThisMode]] + i32 m_function_length { 0 }; - bool m_might_need_arguments_object : 1 { true }; - bool m_contains_direct_call_to_eval : 1 { true }; - bool m_is_arrow_function : 1 { false }; - bool m_has_simple_parameter_list : 1 { false }; + ThisMode m_this_mode : 2 { ThisMode::Global }; // [[ThisMode]] FunctionKind m_kind : 3 { FunctionKind::Normal }; + bool m_strict { false }; + bool m_might_need_arguments_object { true }; + bool m_contains_direct_call_to_eval { true }; + bool m_is_arrow_function { false }; + bool m_has_simple_parameter_list { false }; + bool m_is_module_wrapper { false }; + struct VariableNameToInitialize { Identifier const& identifier; bool parameter_binding { false }; @@ -169,7 +83,6 @@ private: HashMap m_parameter_names; Vector m_functions_to_initialize; bool m_arguments_object_needed { false }; - bool m_is_module_wrapper { false }; bool m_function_environment_needed { false }; bool m_uses_this { false }; Vector m_var_names_to_initialize_binding; @@ -178,6 +91,128 @@ private: size_t m_function_environment_bindings_count { 0 }; size_t m_var_environment_bindings_count { 0 }; size_t m_lex_environment_bindings_count { 0 }; + + Variant m_class_field_initializer_name; // [[ClassFieldInitializerName]] + ConstructorKind m_constructor_kind : 1 { ConstructorKind::Base }; // [[ConstructorKind]] + bool m_is_class_constructor : 1 { false }; // [[IsClassConstructor]] +}; + +// 10.2 ECMAScript Function Objects, https://tc39.es/ecma262/#sec-ecmascript-function-objects +class ECMAScriptFunctionObject final : public FunctionObject { + JS_OBJECT(ECMAScriptFunctionObject, FunctionObject); + GC_DECLARE_ALLOCATOR(ECMAScriptFunctionObject); + +public: + static GC::Ref create(Realm&, FlyString name, ByteString source_text, Statement const& ecmascript_code, NonnullRefPtr parameters, i32 function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind, bool is_strict, FunctionParsingInsights, bool is_arrow_function = false, Variant class_field_initializer_name = {}); + static GC::Ref create(Realm&, FlyString name, Object& prototype, ByteString source_text, Statement const& ecmascript_code, NonnullRefPtr parameters, i32 function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind, bool is_strict, FunctionParsingInsights, bool is_arrow_function = false, Variant class_field_initializer_name = {}); + + [[nodiscard]] static GC::Ref create_from_function_node( + FunctionNode const&, + FlyString name, + GC::Ref, + GC::Ptr parent_environment, + GC::Ptr); + + virtual void initialize(Realm&) override; + virtual ~ECMAScriptFunctionObject() override = default; + + virtual ThrowCompletionOr internal_call(Value this_argument, ReadonlySpan arguments_list) override; + virtual ThrowCompletionOr> internal_construct(ReadonlySpan arguments_list, FunctionObject& new_target) override; + + void make_method(Object& home_object); + + [[nodiscard]] bool is_module_wrapper() const { return shared_data().m_is_module_wrapper; } + void set_is_module_wrapper(bool b) { const_cast(shared_data()).m_is_module_wrapper = b; } + + Statement const& ecmascript_code() const { return *shared_data().m_ecmascript_code; } + [[nodiscard]] virtual FunctionParameters const& formal_parameters() const override { return *shared_data().m_formal_parameters; } + + virtual FlyString const& name() const override { return shared_data().m_name; } + void set_name(FlyString const& name); + + void set_is_class_constructor() { const_cast(shared_data()).m_is_class_constructor = true; } + + auto& bytecode_executable() const { return m_bytecode_executable; } + + Environment* environment() { return m_environment; } + virtual Realm* realm() const override { return m_realm; } + + [[nodiscard]] ConstructorKind constructor_kind() const { return shared_data().m_constructor_kind; } + void set_constructor_kind(ConstructorKind constructor_kind) { const_cast(shared_data()).m_constructor_kind = constructor_kind; } + + [[nodiscard]] ThisMode this_mode() const { return shared_data().m_this_mode; } + [[nodiscard]] bool is_arrow_function() const { return shared_data().m_is_arrow_function; } + [[nodiscard]] bool is_class_constructor() const { return shared_data().m_is_class_constructor; } + [[nodiscard]] bool uses_this() const { return shared_data().m_uses_this; } + [[nodiscard]] i32 function_length() const { return shared_data().m_function_length; } + + Object* home_object() const { return m_home_object; } + void set_home_object(Object* home_object) { m_home_object = home_object; } + + [[nodiscard]] ByteString const& source_text() const { return shared_data().m_source_text; } + void set_source_text(ByteString source_text) { const_cast(shared_data()).m_source_text = move(source_text); } + + Vector const& fields() const { return m_fields; } + void add_field(ClassFieldDefinition field) { m_fields.append(move(field)); } + + Vector const& private_methods() const { return m_private_methods; } + void add_private_method(PrivateElement method) { m_private_methods.append(move(method)); } + + // This is for IsSimpleParameterList (static semantics) + bool has_simple_parameter_list() const { return shared_data().m_has_simple_parameter_list; } + + // Equivalent to absence of [[Construct]] + virtual bool has_constructor() const override { return kind() == FunctionKind::Normal && !shared_data().m_is_arrow_function; } + + virtual Vector const& local_variables_names() const override { return shared_data().m_local_variables_names; } + + FunctionKind kind() const { return shared_data().m_kind; } + + // This is used by LibWeb to disassociate event handler attribute callback functions from the nearest script on the call stack. + // https://html.spec.whatwg.org/multipage/webappapis.html#getting-the-current-value-of-the-event-handler Step 3.11 + void set_script_or_module(ScriptOrModule script_or_module) { m_script_or_module = move(script_or_module); } + + Variant const& class_field_initializer_name() const { return shared_data().m_class_field_initializer_name; } + + bool allocates_function_environment() const { return shared_data().m_function_environment_needed; } + + friend class Bytecode::Generator; + +protected: + virtual bool is_strict_mode() const override { return shared_data().m_strict; } + + virtual Completion ordinary_call_evaluate_body(); + +private: + ECMAScriptFunctionObject( + NonnullRefPtr, + Environment* parent_environment, + PrivateEnvironment* private_environment, + Object& prototype); + + [[nodiscard]] bool function_environment_needed() const { return shared_data().m_function_environment_needed; } + SharedFunctionInstanceData const& shared_data() const { return m_shared_data; } + + virtual bool is_ecmascript_function_object() const override { return true; } + virtual void visit_edges(Visitor&) override; + + ThrowCompletionOr prepare_for_ordinary_call(ExecutionContext& callee_context, Object* new_target); + void ordinary_call_bind_this(ExecutionContext&, Value this_argument); + + NonnullRefPtr m_shared_data; + + GC::Ptr m_name_string; + + GC::Ptr m_bytecode_executable; + + // Internal Slots of ECMAScript Function Objects, https://tc39.es/ecma262/#table-internal-slots-of-ecmascript-function-objects + GC::Ptr m_environment; // [[Environment]] + GC::Ptr m_private_environment; // [[PrivateEnvironment]] + GC::Ptr m_realm; // [[Realm]] + ScriptOrModule m_script_or_module; // [[ScriptOrModule]] + GC::Ptr m_home_object; // [[HomeObject]] + Vector m_fields; // [[Fields]] + Vector m_private_methods; // [[PrivateMethods]] }; template<> diff --git a/Libraries/LibJS/Runtime/FunctionObject.h b/Libraries/LibJS/Runtime/FunctionObject.h index d875d9f6a4f..a482d85c7ac 100644 --- a/Libraries/LibJS/Runtime/FunctionObject.h +++ b/Libraries/LibJS/Runtime/FunctionObject.h @@ -40,7 +40,7 @@ public: virtual Vector const& local_variables_names() const { VERIFY_NOT_REACHED(); } - virtual NonnullRefPtr const& formal_parameters() const { VERIFY_NOT_REACHED(); } + virtual FunctionParameters const& formal_parameters() const { VERIFY_NOT_REACHED(); } protected: explicit FunctionObject(Realm&, Object* prototype, MayInterfereWithIndexedPropertyAccess = MayInterfereWithIndexedPropertyAccess::No);