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);