diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index 223a33a749e4d1a..9430bbc0bf72b86 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/Userland/Libraries/LibJS/AST.cpp @@ -106,7 +106,8 @@ Value FunctionExpression::instantiate_ordinary_function_expression(VM& vm, Depre auto private_environment = vm.running_execution_context().private_environment; - auto closure = ECMAScriptFunctionObject::create(realm, used_name, source_text(), body(), parameters(), function_length(), local_variables_names(), environment, private_environment, kind(), is_strict_mode(), uses_this(), might_need_arguments_object(), contains_direct_call_to_eval(), is_arrow_function()); + auto closure = ECMAScriptFunctionObject::create(realm, used_name, source_text(), body(), parameters(), function_length(), local_variables_names(), environment, private_environment, kind(), is_strict_mode(), + uses_this_from_environment(), might_need_arguments_object(), contains_direct_call_to_eval(), is_arrow_function()); // FIXME: 6. Perform SetFunctionName(closure, name). // FIXME: 7. Perform MakeConstructor(closure). @@ -151,7 +152,8 @@ ThrowCompletionOr ClassMethod::class_element_evaluatio { auto property_key_or_private_name = TRY(class_key_to_property_name(vm, *m_key, property_key)); - auto& method_function = *ECMAScriptFunctionObject::create(*vm.current_realm(), m_function->name(), m_function->source_text(), m_function->body(), m_function->parameters(), m_function->function_length(), m_function->local_variables_names(), vm.lexical_environment(), vm.running_execution_context().private_environment, m_function->kind(), m_function->is_strict_mode(), m_function->uses_this(), m_function->might_need_arguments_object(), m_function->contains_direct_call_to_eval(), m_function->is_arrow_function()); + auto& method_function = *ECMAScriptFunctionObject::create(*vm.current_realm(), m_function->name(), m_function->source_text(), m_function->body(), m_function->parameters(), m_function->function_length(), m_function->local_variables_names(), vm.lexical_environment(), vm.running_execution_context().private_environment, m_function->kind(), m_function->is_strict_mode(), + m_function->uses_this_from_environment(), m_function->might_need_arguments_object(), m_function->contains_direct_call_to_eval(), m_function->is_arrow_function()); auto method_value = Value(&method_function); method_function.make_method(target); @@ -238,7 +240,7 @@ ThrowCompletionOr ClassField::class_element_evaluation // FIXME: A potential optimization is not creating the functions here since these are never directly accessible. auto function_code = create_ast_node(m_initializer->source_range(), copy_initializer.release_nonnull(), name); - initializer = make_handle(*ECMAScriptFunctionObject::create(realm, "field", ByteString::empty(), *function_code, {}, 0, {}, vm.lexical_environment(), vm.running_execution_context().private_environment, FunctionKind::Normal, true, UsesThis::Yes, false, m_contains_direct_call_to_eval, false, property_key_or_private_name)); + initializer = make_handle(*ECMAScriptFunctionObject::create(realm, "field", ByteString::empty(), *function_code, {}, 0, {}, vm.lexical_environment(), vm.running_execution_context().private_environment, FunctionKind::Normal, true, UsesThisFromEnvironment::Yes, false, m_contains_direct_call_to_eval, false, property_key_or_private_name)); initializer->make_method(target); } @@ -282,7 +284,7 @@ ThrowCompletionOr StaticInitializer::class_element_eva // 4. Let formalParameters be an instance of the production FormalParameters : [empty] . // 5. Let bodyFunction be OrdinaryFunctionCreate(%Function.prototype%, sourceText, formalParameters, ClassStaticBlockBody, non-lexical-this, lex, privateEnv). // Note: The function bodyFunction is never directly accessible to ECMAScript code. - auto body_function = ECMAScriptFunctionObject::create(realm, ByteString::empty(), ByteString::empty(), *m_function_body, {}, 0, m_function_body->local_variables_names(), lexical_environment, private_environment, FunctionKind::Normal, true, UsesThis::Yes, false, m_contains_direct_call_to_eval, false); + auto body_function = ECMAScriptFunctionObject::create(realm, ByteString::empty(), ByteString::empty(), *m_function_body, {}, 0, m_function_body->local_variables_names(), lexical_environment, private_environment, FunctionKind::Normal, true, UsesThisFromEnvironment::Yes, false, m_contains_direct_call_to_eval, false); // 6. Perform MakeMethod(bodyFunction, homeObject). body_function->make_method(home_object); @@ -341,7 +343,7 @@ ThrowCompletionOr ClassExpression::create_class_const vm.running_execution_context().private_environment, constructor.kind(), constructor.is_strict_mode(), - UsesThis::Yes, + UsesThisFromEnvironment::Yes, constructor.might_need_arguments_object(), constructor.contains_direct_call_to_eval(), constructor.is_arrow_function()); @@ -1620,7 +1622,8 @@ void ScopeNode::block_declaration_instantiation(VM& vm, Environment* environment auto& function_declaration = static_cast(declaration); // ii. Let fo be InstantiateFunctionObject of d with arguments env and privateEnv. - auto function = ECMAScriptFunctionObject::create(realm, function_declaration.name(), function_declaration.source_text(), function_declaration.body(), function_declaration.parameters(), function_declaration.function_length(), function_declaration.local_variables_names(), environment, private_environment, function_declaration.kind(), function_declaration.is_strict_mode(), function_declaration.uses_this(), function_declaration.might_need_arguments_object(), function_declaration.contains_direct_call_to_eval()); + auto function = ECMAScriptFunctionObject::create(realm, function_declaration.name(), function_declaration.source_text(), function_declaration.body(), function_declaration.parameters(), function_declaration.function_length(), function_declaration.local_variables_names(), environment, private_environment, function_declaration.kind(), function_declaration.is_strict_mode(), + function_declaration.uses_this_from_environment(), function_declaration.might_need_arguments_object(), function_declaration.contains_direct_call_to_eval()); // iii. Perform ! env.InitializeBinding(fn, fo). NOTE: This step is replaced in section B.3.2.6. if (function_declaration.name_identifier()->is_local()) { @@ -1827,7 +1830,8 @@ ThrowCompletionOr Program::global_declaration_instantiation(VM& vm, Global for (auto& declaration : functions_to_initialize.in_reverse()) { // a. Let fn be the sole element of the BoundNames of f. // b. Let fo be InstantiateFunctionObject of f with arguments env and privateEnv. - auto function = ECMAScriptFunctionObject::create(realm, declaration.name(), declaration.source_text(), declaration.body(), declaration.parameters(), declaration.function_length(), declaration.local_variables_names(), &global_environment, private_environment, declaration.kind(), declaration.is_strict_mode(), declaration.uses_this(), declaration.might_need_arguments_object(), declaration.contains_direct_call_to_eval()); + auto function = ECMAScriptFunctionObject::create(realm, declaration.name(), declaration.source_text(), declaration.body(), declaration.parameters(), declaration.function_length(), declaration.local_variables_names(), &global_environment, private_environment, declaration.kind(), declaration.is_strict_mode(), + declaration.uses_this_from_environment(), declaration.might_need_arguments_object(), declaration.contains_direct_call_to_eval()); // c. Perform ? env.CreateGlobalFunctionBinding(fn, fo, false). TRY(global_environment.create_global_function_binding(declaration.name(), function, false)); diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index e91ec30aef6c27f..ff1786165658d3f 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -693,7 +693,7 @@ struct FunctionParameter { Handle bytecode_executable {}; }; -enum class UsesThis { +enum class UsesThisFromEnvironment { Yes, No }; @@ -712,7 +712,7 @@ class FunctionNode { bool contains_direct_call_to_eval() const { return m_contains_direct_call_to_eval; } bool is_arrow_function() const { return m_is_arrow_function; } FunctionKind kind() const { return m_kind; } - UsesThis uses_this() const { return m_uses_this; } + UsesThisFromEnvironment uses_this_from_environment() const { return m_uses_this_from_environment; } virtual bool has_name() const = 0; virtual Value instantiate_ordinary_function_expression(VM&, DeprecatedFlyString given_name) const = 0; @@ -720,7 +720,7 @@ class FunctionNode { virtual ~FunctionNode() {}; protected: - FunctionNode(RefPtr name, ByteString source_text, NonnullRefPtr body, Vector parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function, Vector local_variables_names, UsesThis uses_this) + FunctionNode(RefPtr name, ByteString source_text, NonnullRefPtr body, Vector parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function, Vector local_variables_names, UsesThisFromEnvironment uses_this_from_environment) : m_name(move(name)) , m_source_text(move(source_text)) , m_body(move(body)) @@ -731,7 +731,7 @@ class FunctionNode { , m_might_need_arguments_object(might_need_arguments_object) , m_contains_direct_call_to_eval(contains_direct_call_to_eval) , m_is_arrow_function(is_arrow_function) - , m_uses_this(uses_this) + , m_uses_this_from_environment(uses_this_from_environment) , m_local_variables_names(move(local_variables_names)) { if (m_is_arrow_function) @@ -752,7 +752,7 @@ class FunctionNode { bool m_might_need_arguments_object : 1 { false }; bool m_contains_direct_call_to_eval : 1 { false }; bool m_is_arrow_function : 1 { false }; - UsesThis m_uses_this : 1 { UsesThis::No }; + UsesThisFromEnvironment m_uses_this_from_environment : 1 { UsesThisFromEnvironment::No }; Vector m_local_variables_names; }; @@ -763,7 +763,7 @@ class FunctionDeclaration final public: static bool must_have_name() { return true; } - FunctionDeclaration(SourceRange source_range, RefPtr name, ByteString source_text, NonnullRefPtr body, Vector parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval, Vector local_variables_names, UsesThis uses_this) + FunctionDeclaration(SourceRange source_range, RefPtr name, ByteString source_text, NonnullRefPtr body, Vector parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval, Vector local_variables_names, UsesThisFromEnvironment uses_this) : Declaration(move(source_range)) , FunctionNode(move(name), move(source_text), move(body), move(parameters), function_length, kind, is_strict_mode, might_need_arguments_object, contains_direct_call_to_eval, false, move(local_variables_names), uses_this) { @@ -793,7 +793,7 @@ class FunctionExpression final public: static bool must_have_name() { return false; } - FunctionExpression(SourceRange source_range, RefPtr name, ByteString source_text, NonnullRefPtr body, Vector parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval, Vector local_variables_names, UsesThis uses_this = UsesThis::No, bool is_arrow_function = false) + FunctionExpression(SourceRange source_range, RefPtr name, ByteString source_text, NonnullRefPtr body, Vector parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval, Vector local_variables_names, UsesThisFromEnvironment uses_this = UsesThisFromEnvironment::No, bool is_arrow_function = false) : Expression(move(source_range)) , FunctionNode(move(name), move(source_text), move(body), move(parameters), function_length, kind, is_strict_mode, might_need_arguments_object, contains_direct_call_to_eval, is_arrow_function, move(local_variables_names), uses_this) { diff --git a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h index b4eb4d7dda6b82c..1f20d60f77fc405 100644 --- a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h +++ b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h @@ -417,7 +417,8 @@ inline Value new_function(VM& vm, FunctionNode const& function_node, Optional ResolveThisBinding::execute_impl(Bytecode::Interpreter& if (cached_this_value.is_empty()) { // OPTIMIZATION: Because the value of 'this' cannot be reassigned during a function execution, it's // resolved once and then saved for subsequent use. - auto& vm = interpreter.vm(); - cached_this_value = TRY(vm.resolve_this_binding()); + auto& running_execution_context = interpreter.running_execution_context(); + if (auto function = running_execution_context.function; function && is(*function) && !static_cast(*function).allocates_function_environment()) { + cached_this_value = running_execution_context.this_value; + } else { + auto& vm = interpreter.vm(); + cached_this_value = TRY(vm.resolve_this_binding()); + } } interpreter.set(dst(), cached_this_value); return {}; diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp index b78656b15c05e34..5ec1a7b51a678f5 100644 --- a/Userland/Libraries/LibJS/Parser.cpp +++ b/Userland/Libraries/LibJS/Parser.cpp @@ -433,16 +433,32 @@ class ScopePusher { } } - bool uses_this() + bool uses_this_from_environment() const { - return m_uses_this; + return m_uses_this_from_environment; } void set_uses_this() { - for (auto scope_ptr = this; scope_ptr; scope_ptr = scope_ptr->m_parent_scope) { + auto const* closest_function_scope = last_function_scope(); + if (!closest_function_scope || !closest_function_scope->m_is_arrow_function) + return; + + for (auto* scope_ptr = this; scope_ptr; scope_ptr = scope_ptr->m_parent_scope) { + if (scope_ptr->m_uses_this_from_environment) + break; + if (scope_ptr->m_type == ScopeType::Function) + scope_ptr->m_uses_this_from_environment = true; + } + } + + void set_uses_new_target() + { + for (auto* scope_ptr = this; scope_ptr; scope_ptr = scope_ptr->m_parent_scope) { + if (scope_ptr->m_uses_this_from_environment) + break; if (scope_ptr->m_type == ScopeType::Function) - scope_ptr->m_uses_this = true; + scope_ptr->m_uses_this_from_environment = true; } } @@ -491,7 +507,11 @@ class ScopePusher { bool m_contains_direct_call_to_eval { false }; bool m_contains_await_expression { false }; bool m_screwed_by_eval_in_scope_chain { false }; - bool m_uses_this { false }; + + // Function uses this binding from function environment if: + // 1. It's an arrow function or establish parent scope for an arrow function + // 2. Uses new.target + bool m_uses_this_from_environment { false }; bool m_is_arrow_function { false }; }; @@ -1071,7 +1091,7 @@ RefPtr Parser::try_parse_arrow_function_expression(boo if (m_state.strict_mode) const_cast(*return_block).set_strict_mode(); contains_direct_call_to_eval = m_state.current_scope_pusher->contains_direct_call_to_eval(); - uses_this = m_state.current_scope_pusher->uses_this(); + uses_this = m_state.current_scope_pusher->uses_this_from_environment(); return return_block; } // Invalid arrow function body @@ -1103,7 +1123,7 @@ RefPtr Parser::try_parse_arrow_function_expression(boo return create_ast_node( { m_source_code, rule_start.position(), position() }, nullptr, move(source_text), move(body), move(parameters), function_length, function_kind, body->in_strict_mode(), - /* might_need_arguments_object */ false, contains_direct_call_to_eval, move(local_variables_names), uses_this ? UsesThis::Yes : UsesThis::No, /* is_arrow_function */ true); + /* might_need_arguments_object */ false, contains_direct_call_to_eval, move(local_variables_names), uses_this ? UsesThisFromEnvironment::Yes : UsesThisFromEnvironment::No, /* is_arrow_function */ true); } RefPtr Parser::try_parse_labelled_statement(AllowLabelledFunction allow_function) @@ -1217,7 +1237,7 @@ RefPtr Parser::try_parse_new_target_expression() discard_saved_state(); if (m_state.current_scope_pusher) - m_state.current_scope_pusher->set_uses_this(); + m_state.current_scope_pusher->set_uses_new_target(); return create_ast_node({ m_source_code, rule_start.position(), position() }, MetaProperty::Type::NewTarget); } @@ -1677,7 +1697,7 @@ Parser::PrimaryExpressionParseResult Parser::parse_primary_expression() if (!m_state.allow_super_property_lookup) syntax_error("'super' keyword unexpected here"); if (m_state.current_scope_pusher) - m_state.current_scope_pusher->set_uses_this(); + m_state.current_scope_pusher->set_uses_new_target(); return { create_ast_node({ m_source_code, rule_start.position(), position() }) }; case TokenType::EscapedKeyword: if (match_invalid_escaped_keyword()) @@ -2822,7 +2842,7 @@ NonnullRefPtr Parser::parse_function_body(Vectortype() == ScopePusher::ScopeType::Function); contains_direct_call_to_eval = m_state.current_scope_pusher->contains_direct_call_to_eval(); - uses_this = m_state.current_scope_pusher->uses_this(); + uses_this = m_state.current_scope_pusher->uses_this_from_environment(); return function_body; } @@ -2949,7 +2969,7 @@ NonnullRefPtr Parser::parse_function_node(u16 parse_options, O function_kind, has_strict_directive, m_state.function_might_need_arguments_object, contains_direct_call_to_eval, move(local_variables_names), - uses_this ? UsesThis::Yes : UsesThis::No); + uses_this ? UsesThisFromEnvironment::Yes : UsesThisFromEnvironment::No); } Vector Parser::parse_formal_parameters(int& function_length, u16 parse_options) diff --git a/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp b/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp index 070258ffceeaf7e..1135393dc0484bc 100644 --- a/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp +++ b/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp @@ -972,7 +972,8 @@ ThrowCompletionOr eval_declaration_instantiation(VM& vm, Program const& pr for (auto& declaration : functions_to_initialize.in_reverse()) { // a. Let fn be the sole element of the BoundNames of f. // b. Let fo be InstantiateFunctionObject of f with arguments lexEnv and privateEnv. - auto function = ECMAScriptFunctionObject::create(realm, declaration.name(), declaration.source_text(), declaration.body(), declaration.parameters(), declaration.function_length(), declaration.local_variables_names(), lexical_environment, private_environment, declaration.kind(), declaration.is_strict_mode(), declaration.uses_this(), declaration.might_need_arguments_object()); + auto function = ECMAScriptFunctionObject::create(realm, declaration.name(), declaration.source_text(), declaration.body(), declaration.parameters(), declaration.function_length(), declaration.local_variables_names(), lexical_environment, private_environment, declaration.kind(), declaration.is_strict_mode(), + declaration.uses_this_from_environment(), declaration.might_need_arguments_object()); // c. If varEnv is a global Environment Record, then if (global_var_environment) { diff --git a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp index 5c5d56905e95c9c..ae7e5a90cafe1f1 100644 --- a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp @@ -33,7 +33,7 @@ namespace JS { JS_DEFINE_ALLOCATOR(ECMAScriptFunctionObject); -NonnullGCPtr ECMAScriptFunctionObject::create(Realm& realm, DeprecatedFlyString name, ByteString source_text, Statement const& ecmascript_code, Vector parameters, i32 m_function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind kind, bool is_strict, UsesThis uses_this, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function, Variant class_field_initializer_name) +NonnullGCPtr ECMAScriptFunctionObject::create(Realm& realm, DeprecatedFlyString name, ByteString source_text, Statement const& ecmascript_code, Vector parameters, i32 m_function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind kind, bool is_strict, UsesThisFromEnvironment uses_this, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function, Variant class_field_initializer_name) { Object* prototype = nullptr; switch (kind) { @@ -53,12 +53,12 @@ NonnullGCPtr ECMAScriptFunctionObject::create(Realm& r return realm.heap().allocate(realm, move(name), move(source_text), ecmascript_code, move(parameters), m_function_length, move(local_variables_names), parent_environment, private_environment, *prototype, kind, is_strict, uses_this, might_need_arguments_object, contains_direct_call_to_eval, is_arrow_function, move(class_field_initializer_name)); } -NonnullGCPtr ECMAScriptFunctionObject::create(Realm& realm, DeprecatedFlyString name, Object& prototype, ByteString source_text, Statement const& ecmascript_code, Vector parameters, i32 m_function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind kind, bool is_strict, UsesThis uses_this, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function, Variant class_field_initializer_name) +NonnullGCPtr ECMAScriptFunctionObject::create(Realm& realm, DeprecatedFlyString name, Object& prototype, ByteString source_text, Statement const& ecmascript_code, Vector parameters, i32 m_function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind kind, bool is_strict, UsesThisFromEnvironment uses_this, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function, Variant class_field_initializer_name) { return realm.heap().allocate(realm, move(name), move(source_text), ecmascript_code, move(parameters), m_function_length, move(local_variables_names), parent_environment, private_environment, prototype, kind, is_strict, uses_this, might_need_arguments_object, contains_direct_call_to_eval, is_arrow_function, move(class_field_initializer_name)); } -ECMAScriptFunctionObject::ECMAScriptFunctionObject(DeprecatedFlyString name, ByteString source_text, Statement const& ecmascript_code, Vector formal_parameters, i32 function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, Object& prototype, FunctionKind kind, bool strict, UsesThis uses_this, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function, Variant class_field_initializer_name) +ECMAScriptFunctionObject::ECMAScriptFunctionObject(DeprecatedFlyString name, ByteString source_text, Statement const& ecmascript_code, Vector formal_parameters, i32 function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, Object& prototype, FunctionKind kind, bool strict, UsesThisFromEnvironment uses_this, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function, Variant class_field_initializer_name) : FunctionObject(prototype) , m_name(move(name)) , m_function_length(function_length) @@ -331,7 +331,7 @@ ECMAScriptFunctionObject::ECMAScriptFunctionObject(DeprecatedFlyString name, Byt })); } - 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 || uses_this == UsesThis::Yes || m_contains_direct_call_to_eval; + 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 || uses_this == UsesThisFromEnvironment::Yes || m_contains_direct_call_to_eval; } void ECMAScriptFunctionObject::initialize(Realm& realm) @@ -414,8 +414,7 @@ ThrowCompletionOr ECMAScriptFunctionObject::internal_call(Value this_argu } // 5. Perform OrdinaryCallBindThis(F, calleeContext, thisArgument). - if (m_function_environment_needed) - ordinary_call_bind_this(*callee_context, this_argument); + ordinary_call_bind_this(*callee_context, this_argument); // 6. Let result be Completion(OrdinaryCallEvaluateBody(F, argumentsList)). auto result = ordinary_call_evaluate_body(); @@ -478,8 +477,7 @@ ThrowCompletionOr> ECMAScriptFunctionObject::internal_const // 6. If kind is base, then if (kind == ConstructorKind::Base) { // a. Perform OrdinaryCallBindThis(F, calleeContext, thisArgument). - if (m_function_environment_needed) - ordinary_call_bind_this(*callee_context, this_argument); + ordinary_call_bind_this(*callee_context, this_argument); // b. Let initializeResult be Completion(InitializeInstanceElements(thisArgument, F)). auto initialize_result = this_argument->initialize_instance_elements(*this); @@ -699,7 +697,9 @@ void ECMAScriptFunctionObject::ordinary_call_bind_this(ExecutionContext& callee_ // 7. Assert: localEnv is a function Environment Record. // 8. Assert: The next step never returns an abrupt completion because localEnv.[[ThisBindingStatus]] is not initialized. // 9. Perform ! localEnv.BindThisValue(thisValue). - MUST(verify_cast(*local_env).bind_this_value(vm, this_value)); + callee_context.this_value = this_value; + if (m_function_environment_needed) + MUST(verify_cast(*local_env).bind_this_value(vm, this_value)); // 10. Return unused. } diff --git a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h index 360e2f4deaa1f81..1fdce4b17bfa023 100644 --- a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h +++ b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h @@ -39,8 +39,8 @@ class ECMAScriptFunctionObject final : public FunctionObject { Global, }; - static NonnullGCPtr create(Realm&, DeprecatedFlyString name, ByteString source_text, Statement const& ecmascript_code, Vector parameters, i32 m_function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind, bool is_strict, UsesThis uses_this, bool might_need_arguments_object = true, bool contains_direct_call_to_eval = true, bool is_arrow_function = false, Variant class_field_initializer_name = {}); - static NonnullGCPtr create(Realm&, DeprecatedFlyString name, Object& prototype, ByteString source_text, Statement const& ecmascript_code, Vector parameters, i32 m_function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind, bool is_strict, UsesThis uses_this, bool might_need_arguments_object = true, bool contains_direct_call_to_eval = true, bool is_arrow_function = false, Variant class_field_initializer_name = {}); + static NonnullGCPtr create(Realm&, DeprecatedFlyString name, ByteString source_text, Statement const& ecmascript_code, Vector parameters, i32 m_function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind, bool is_strict, UsesThisFromEnvironment uses_this, bool might_need_arguments_object = true, bool contains_direct_call_to_eval = true, bool is_arrow_function = false, Variant class_field_initializer_name = {}); + static NonnullGCPtr create(Realm&, DeprecatedFlyString name, Object& prototype, ByteString source_text, Statement const& ecmascript_code, Vector parameters, i32 m_function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind, bool is_strict, UsesThisFromEnvironment uses_this, bool might_need_arguments_object = true, bool contains_direct_call_to_eval = true, bool is_arrow_function = false, Variant class_field_initializer_name = {}); virtual void initialize(Realm&) override; virtual ~ECMAScriptFunctionObject() override = default; @@ -99,6 +99,8 @@ class ECMAScriptFunctionObject final : public FunctionObject { 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: @@ -107,7 +109,7 @@ class ECMAScriptFunctionObject final : public FunctionObject { virtual Completion ordinary_call_evaluate_body(); private: - ECMAScriptFunctionObject(DeprecatedFlyString name, ByteString source_text, Statement const& ecmascript_code, Vector parameters, i32 m_function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, Object& prototype, FunctionKind, bool is_strict, UsesThis uses_this, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function, Variant class_field_initializer_name); + ECMAScriptFunctionObject(DeprecatedFlyString name, ByteString source_text, Statement const& ecmascript_code, Vector parameters, i32 m_function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, Object& prototype, FunctionKind, bool is_strict, UsesThisFromEnvironment uses_this, bool might_need_arguments_object, bool contains_direct_call_to_eval, 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; diff --git a/Userland/Libraries/LibJS/Runtime/FunctionConstructor.cpp b/Userland/Libraries/LibJS/Runtime/FunctionConstructor.cpp index bf53edc1a03d00f..dee3aff3f0faf8e 100644 --- a/Userland/Libraries/LibJS/Runtime/FunctionConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/FunctionConstructor.cpp @@ -219,7 +219,7 @@ ThrowCompletionOr FunctionConstructor::create_dynamic PrivateEnvironment* private_environment = nullptr; // 28. Let F be OrdinaryFunctionCreate(proto, sourceText, parameters, body, non-lexical-this, env, privateEnv). - auto function = ECMAScriptFunctionObject::create(realm, "anonymous", *prototype, move(source_text), expr->body(), expr->parameters(), expr->function_length(), expr->local_variables_names(), &environment, private_environment, expr->kind(), expr->is_strict_mode(), uses_this ? UsesThis::Yes : UsesThis::No, expr->might_need_arguments_object(), contains_direct_call_to_eval); + auto function = ECMAScriptFunctionObject::create(realm, "anonymous", *prototype, move(source_text), expr->body(), expr->parameters(), expr->function_length(), expr->local_variables_names(), &environment, private_environment, expr->kind(), expr->is_strict_mode(), uses_this ? UsesThisFromEnvironment::Yes : UsesThisFromEnvironment::No, expr->might_need_arguments_object(), contains_direct_call_to_eval); // FIXME: Remove the name argument from create() and do this instead. // 29. Perform SetFunctionName(F, "anonymous"). diff --git a/Userland/Libraries/LibJS/SourceTextModule.cpp b/Userland/Libraries/LibJS/SourceTextModule.cpp index 65da8ffd6ce9dbc..b917d613b0034fe 100644 --- a/Userland/Libraries/LibJS/SourceTextModule.cpp +++ b/Userland/Libraries/LibJS/SourceTextModule.cpp @@ -498,7 +498,8 @@ ThrowCompletionOr SourceTextModule::initialize_environment(VM& vm) DeprecatedFlyString function_name = function_declaration.name(); if (function_name == ExportStatement::local_name_for_default) function_name = "default"sv; - auto function = ECMAScriptFunctionObject::create(realm(), function_name, function_declaration.source_text(), function_declaration.body(), function_declaration.parameters(), function_declaration.function_length(), function_declaration.local_variables_names(), environment, private_environment, function_declaration.kind(), function_declaration.is_strict_mode(), function_declaration.uses_this(), function_declaration.might_need_arguments_object(), function_declaration.contains_direct_call_to_eval()); + auto function = ECMAScriptFunctionObject::create(realm(), function_name, function_declaration.source_text(), function_declaration.body(), function_declaration.parameters(), function_declaration.function_length(), function_declaration.local_variables_names(), environment, private_environment, function_declaration.kind(), function_declaration.is_strict_mode(), + function_declaration.uses_this_from_environment(), function_declaration.might_need_arguments_object(), function_declaration.contains_direct_call_to_eval()); // 2. Perform ! env.InitializeBinding(dn, fo, normal). MUST(environment->initialize_binding(vm, name, function, Environment::InitializeBindingHint::Normal)); @@ -759,7 +760,7 @@ ThrowCompletionOr SourceTextModule::execute_module(VM& vm, GCPtrm_ecmascript_code, - {}, 0, {}, environment(), nullptr, FunctionKind::Async, true, UsesThis::Yes, false, false); + {}, 0, {}, environment(), nullptr, FunctionKind::Async, true, UsesThisFromEnvironment::Yes, false, false); module_wrapper_function->set_is_module_wrapper(true); // AD-HOC: We push/pop the moduleContext around the call to ensure that the async execution context diff --git a/Userland/Libraries/LibWeb/DOM/EventTarget.cpp b/Userland/Libraries/LibWeb/DOM/EventTarget.cpp index cac43d38835c72b..7961a12c0d3325a 100644 --- a/Userland/Libraries/LibWeb/DOM/EventTarget.cpp +++ b/Userland/Libraries/LibWeb/DOM/EventTarget.cpp @@ -485,7 +485,8 @@ WebIDL::CallbackType* EventTarget::get_current_value_of_event_handler(FlyString // 6. Return scope. (NOTE: Not necessary) - auto function = JS::ECMAScriptFunctionObject::create(realm, name.to_deprecated_fly_string(), builder.to_byte_string(), program->body(), program->parameters(), program->function_length(), program->local_variables_names(), scope, nullptr, JS::FunctionKind::Normal, program->is_strict_mode(), program->uses_this(), program->might_need_arguments_object(), is_arrow_function); + auto function = JS::ECMAScriptFunctionObject::create(realm, name.to_deprecated_fly_string(), builder.to_byte_string(), program->body(), program->parameters(), program->function_length(), program->local_variables_names(), scope, nullptr, JS::FunctionKind::Normal, program->is_strict_mode(), + program->uses_this_from_environment(), program->might_need_arguments_object(), is_arrow_function); // 10. Remove settings object's realm execution context from the JavaScript execution context stack. VERIFY(vm.execution_context_stack().last() == &settings_object.realm_execution_context()); diff --git a/Userland/Libraries/LibWeb/WebDriver/ExecuteScript.cpp b/Userland/Libraries/LibWeb/WebDriver/ExecuteScript.cpp index a18b2f5ec611a0c..8b7f867656c8008 100644 --- a/Userland/Libraries/LibWeb/WebDriver/ExecuteScript.cpp +++ b/Userland/Libraries/LibWeb/WebDriver/ExecuteScript.cpp @@ -266,7 +266,8 @@ static JS::ThrowCompletionOr execute_a_function_body(Web::Page& page, // The result of parsing global scope above. // strict // The result of parsing strict above. - auto function = JS::ECMAScriptFunctionObject::create(realm, "", move(source_text), function_expression->body(), function_expression->parameters(), function_expression->function_length(), function_expression->local_variables_names(), &global_scope, nullptr, function_expression->kind(), function_expression->is_strict_mode(), function_expression->uses_this(), function_expression->might_need_arguments_object(), contains_direct_call_to_eval); + auto function = JS::ECMAScriptFunctionObject::create(realm, "", move(source_text), function_expression->body(), function_expression->parameters(), function_expression->function_length(), function_expression->local_variables_names(), &global_scope, nullptr, function_expression->kind(), function_expression->is_strict_mode(), + function_expression->uses_this_from_environment(), function_expression->might_need_arguments_object(), contains_direct_call_to_eval); // 9. Let completion be Function.[[Call]](window, parameters) with function as the this value. // NOTE: This is not entirely clear, but I don't think they mean actually passing `function` as