diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index 3a8ddd7dbb7..7b118a350ef 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/Userland/Libraries/LibJS/AST.cpp @@ -106,7 +106,7 @@ 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(), 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(), might_need_arguments_object(), contains_direct_call_to_eval(), is_arrow_function()); // FIXME: 6. Perform SetFunctionName(closure, name). // FIXME: 7. Perform MakeConstructor(closure). @@ -240,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, 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, UsesThis::Yes, false, m_contains_direct_call_to_eval, false, property_key_or_private_name)); initializer->make_method(target); } @@ -284,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, 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, UsesThis::Yes, false, m_contains_direct_call_to_eval, false); // 6. Perform MakeMethod(bodyFunction, homeObject). body_function->make_method(home_object); @@ -356,6 +356,7 @@ ThrowCompletionOr ClassExpression::create_class_const vm.running_execution_context().private_environment, constructor.kind(), constructor.is_strict_mode(), + UsesThis::Yes, constructor.might_need_arguments_object(), constructor.contains_direct_call_to_eval(), constructor.is_arrow_function()); @@ -1642,7 +1643,7 @@ 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.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(), 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()) { @@ -1846,7 +1847,7 @@ 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.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(), 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 2bfce04aa33..c846f332620 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -688,6 +688,11 @@ struct FunctionParameter { Handle bytecode_executable {}; }; +enum class UsesThis { + Yes, + No +}; + class FunctionNode { public: StringView name() const { return m_name ? m_name->string().view() : ""sv; } @@ -702,9 +707,10 @@ public: 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; } 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) + 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) : m_name(move(name)) , m_source_text(move(source_text)) , m_body(move(body)) @@ -715,6 +721,7 @@ protected: , 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_local_variables_names(move(local_variables_names)) { if (m_is_arrow_function) @@ -735,6 +742,7 @@ private: 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 }; Vector m_local_variables_names; }; @@ -745,9 +753,9 @@ 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) + 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) : 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)) + , 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) { } @@ -770,9 +778,9 @@ 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, 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, UsesThis uses_this = UsesThis::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)) + , 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 3aa5caaa565..5bec2c919ff 100644 --- a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h +++ b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h @@ -404,7 +404,7 @@ inline Value new_function(VM& vm, FunctionExpression const& function_node, Optio name = vm.bytecode_interpreter().current_executable().get_identifier(lhs_name.value()); value = function_node.instantiate_ordinary_function_expression(vm, name); } else { - value = ECMAScriptFunctionObject::create(*vm.current_realm(), function_node.name(), function_node.source_text(), function_node.body(), function_node.parameters(), function_node.function_length(), function_node.local_variables_names(), vm.lexical_environment(), vm.running_execution_context().private_environment, function_node.kind(), function_node.is_strict_mode(), function_node.might_need_arguments_object(), function_node.contains_direct_call_to_eval(), function_node.is_arrow_function()); + value = ECMAScriptFunctionObject::create(*vm.current_realm(), function_node.name(), function_node.source_text(), function_node.body(), function_node.parameters(), function_node.function_length(), function_node.local_variables_names(), vm.lexical_environment(), vm.running_execution_context().private_environment, function_node.kind(), function_node.is_strict_mode(), function_node.uses_this(), function_node.might_need_arguments_object(), function_node.contains_direct_call_to_eval(), function_node.is_arrow_function()); } if (home_object.has_value()) { diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp index debc70a72cb..75d581af5c3 100644 --- a/Userland/Libraries/LibJS/Parser.cpp +++ b/Userland/Libraries/LibJS/Parser.cpp @@ -435,6 +435,19 @@ public: } } + bool uses_this() + { + return m_uses_this; + } + + void set_uses_this() + { + for (auto scope_ptr = this; scope_ptr; scope_ptr = scope_ptr->m_parent_scope) { + if (scope_ptr->m_type == ScopeType::Function) + scope_ptr->m_uses_this = true; + } + } + private: void throw_identifier_declared(DeprecatedFlyString const& name, NonnullRefPtr const& declaration) { @@ -475,6 +488,7 @@ private: 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 }; }; class OperatorPrecedenceTable { @@ -978,6 +992,7 @@ RefPtr Parser::try_parse_arrow_function_expression(boo Vector parameters; i32 function_length = -1; bool contains_direct_call_to_eval = false; + bool uses_this = false; auto function_body_result = [&]() -> RefPtr { ScopePusher function_scope = ScopePusher::function_scope(*this); @@ -1031,7 +1046,7 @@ RefPtr Parser::try_parse_arrow_function_expression(boo if (match(TokenType::CurlyOpen)) { // Parse a function body with statements consume(TokenType::CurlyOpen); - auto body = parse_function_body(parameters, function_kind, contains_direct_call_to_eval); + auto body = parse_function_body(parameters, function_kind, contains_direct_call_to_eval, uses_this); consume(TokenType::CurlyClose); return body; } @@ -1051,6 +1066,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(); return return_block; } // Invalid arrow function body @@ -1082,7 +1098,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), /* is_arrow_function */ true); + /* might_need_arguments_object */ false, contains_direct_call_to_eval, move(local_variables_names), uses_this ? UsesThis::Yes : UsesThis::No, /* is_arrow_function */ true); } RefPtr Parser::try_parse_labelled_statement(AllowLabelledFunction allow_function) @@ -1194,6 +1210,10 @@ RefPtr Parser::try_parse_new_target_expression() state_rollback_guard.disarm(); discard_saved_state(); + + if (m_state.current_scope_pusher) + m_state.current_scope_pusher->set_uses_this(); + return create_ast_node({ m_source_code, rule_start.position(), position() }, MetaProperty::Type::NewTarget); } @@ -1639,15 +1659,20 @@ Parser::PrimaryExpressionParseResult Parser::parse_primary_expression() } return { move(expression) }; } - case TokenType::This: + case TokenType::This: { + if (m_state.current_scope_pusher) + m_state.current_scope_pusher->set_uses_this(); consume_and_allow_division(); return { create_ast_node({ m_source_code, rule_start.position(), position() }) }; + } case TokenType::Class: return { parse_class_expression(false) }; case TokenType::Super: consume(); 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(); return { create_ast_node({ m_source_code, rule_start.position(), position() }) }; case TokenType::EscapedKeyword: if (match_invalid_escaped_keyword()) @@ -2716,7 +2741,7 @@ void Parser::parse_statement_list(ScopeNode& output_node, AllowLabelledFunction } // FunctionBody, https://tc39.es/ecma262/#prod-FunctionBody -NonnullRefPtr Parser::parse_function_body(Vector const& parameters, FunctionKind function_kind, bool& contains_direct_call_to_eval) +NonnullRefPtr Parser::parse_function_body(Vector const& parameters, FunctionKind function_kind, bool& contains_direct_call_to_eval, bool& uses_this) { auto rule_start = push_start(); auto function_body = create_ast_node({ m_source_code, rule_start.position(), position() }); @@ -2792,6 +2817,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(); return function_body; } @@ -2877,6 +2903,7 @@ NonnullRefPtr Parser::parse_function_node(u16 parse_options, O i32 function_length = -1; Vector parameters; bool contains_direct_call_to_eval = false; + bool uses_this = false; auto body = [&] { ScopePusher function_scope = ScopePusher::function_scope(*this, name); @@ -2896,7 +2923,7 @@ NonnullRefPtr Parser::parse_function_node(u16 parse_options, O consume(TokenType::CurlyOpen); - auto body = parse_function_body(parameters, function_kind, contains_direct_call_to_eval); + auto body = parse_function_body(parameters, function_kind, contains_direct_call_to_eval, uses_this); return body; }(); @@ -2916,7 +2943,8 @@ NonnullRefPtr Parser::parse_function_node(u16 parse_options, O name, move(source_text), move(body), move(parameters), function_length, function_kind, has_strict_directive, m_state.function_might_need_arguments_object, contains_direct_call_to_eval, - move(local_variables_names)); + move(local_variables_names), + uses_this ? UsesThis::Yes : UsesThis::No); } Vector Parser::parse_formal_parameters(int& function_length, u16 parse_options) @@ -5100,7 +5128,7 @@ NonnullRefPtr Parser::create_identifier_and_register_in_curren return id; } -Parser Parser::parse_function_body_from_string(ByteString const& body_string, u16 parse_options, Vector const& parameters, FunctionKind kind, bool& contains_direct_call_to_eval) +Parser Parser::parse_function_body_from_string(ByteString const& body_string, u16 parse_options, Vector const& parameters, FunctionKind kind, bool& contains_direct_call_to_eval, bool& uses_this) { RefPtr function_body; @@ -5113,7 +5141,7 @@ Parser Parser::parse_function_body_from_string(ByteString const& body_string, u1 body_parser.m_state.await_expression_is_valid = true; if ((parse_options & FunctionNodeParseOptions::IsGeneratorFunction) != 0) body_parser.m_state.in_generator_function_context = true; - function_body = body_parser.parse_function_body(parameters, kind, contains_direct_call_to_eval); + function_body = body_parser.parse_function_body(parameters, kind, contains_direct_call_to_eval, uses_this); } return body_parser; diff --git a/Userland/Libraries/LibJS/Parser.h b/Userland/Libraries/LibJS/Parser.h index 6fe48767a51..dcc537250bb 100644 --- a/Userland/Libraries/LibJS/Parser.h +++ b/Userland/Libraries/LibJS/Parser.h @@ -87,7 +87,7 @@ public: NonnullRefPtr parse_statement(AllowLabelledFunction allow_labelled_function = AllowLabelledFunction::No); NonnullRefPtr parse_block_statement(); - NonnullRefPtr parse_function_body(Vector const& parameters, FunctionKind function_kind, bool& contains_direct_call_to_eval); + NonnullRefPtr parse_function_body(Vector const& parameters, FunctionKind function_kind, bool& contains_direct_call_to_eval, bool& uses_this); NonnullRefPtr parse_return_statement(); enum class IsForLoopVariableDeclaration { @@ -211,7 +211,7 @@ public: // Needs to mess with m_state, and we're not going to expose a non-const getter for that :^) friend ThrowCompletionOr FunctionConstructor::create_dynamic_function(VM&, FunctionObject&, FunctionObject*, FunctionKind, MarkedVector const&); - static Parser parse_function_body_from_string(ByteString const& body_string, u16 parse_options, Vector const& parameters, FunctionKind kind, bool& contains_direct_call_to_eval); + static Parser parse_function_body_from_string(ByteString const& body_string, u16 parse_options, Vector const& parameters, FunctionKind kind, bool& contains_direct_call_to_eval, bool& uses_this); private: friend class ScopePusher; diff --git a/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp b/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp index 42dab329f23..0ab3521a8b0 100644 --- a/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp +++ b/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp @@ -972,7 +972,7 @@ 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.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(), 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 8a369a672d2..a54097bba7c 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, 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, UsesThis 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) { @@ -50,15 +50,15 @@ NonnullGCPtr ECMAScriptFunctionObject::create(Realm& r prototype = realm.intrinsics().async_generator_function_prototype(); break; } - 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, might_need_arguments_object, contains_direct_call_to_eval, is_arrow_function, move(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)); } -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, 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, UsesThis 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, might_need_arguments_object, contains_direct_call_to_eval, is_arrow_function, move(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, 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, UsesThis 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) @@ -327,6 +327,8 @@ ECMAScriptFunctionObject::ECMAScriptFunctionObject(DeprecatedFlyString name, Byt (*lex_environment_size)++; })); } + + m_function_environment_needed = m_arguments_object_needed || 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; } void ECMAScriptFunctionObject::initialize(Realm& realm) @@ -405,7 +407,8 @@ ThrowCompletionOr ECMAScriptFunctionObject::internal_call(Value this_argu } // 5. Perform OrdinaryCallBindThis(F, calleeContext, thisArgument). - ordinary_call_bind_this(*callee_context, this_argument); + if (m_function_environment_needed) + ordinary_call_bind_this(*callee_context, this_argument); // 6. Let result be Completion(OrdinaryCallEvaluateBody(F, argumentsList)). auto result = ordinary_call_evaluate_body(); @@ -464,7 +467,8 @@ ThrowCompletionOr> ECMAScriptFunctionObject::internal_const // 6. If kind is base, then if (kind == ConstructorKind::Base) { // a. Perform OrdinaryCallBindThis(F, calleeContext, thisArgument). - ordinary_call_bind_this(*callee_context, this_argument); + if (m_function_environment_needed) + 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); @@ -929,7 +933,7 @@ ThrowCompletionOr ECMAScriptFunctionObject::function_declaration_instantia for (auto& declaration : m_functions_to_initialize) { // 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(), lex_environment, private_environment, declaration.kind(), declaration.is_strict_mode(), 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(), lex_environment, private_environment, declaration.kind(), declaration.is_strict_mode(), declaration.uses_this(), declaration.might_need_arguments_object(), declaration.contains_direct_call_to_eval()); // c. Perform ! varEnv.SetMutableBinding(fn, fo, false). if (declaration.name_identifier()->is_local()) { @@ -984,15 +988,20 @@ 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; - // 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); + if (m_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); - // 8. Set the LexicalEnvironment of calleeContext to localEnv. - callee_context.lexical_environment = local_environment; + // 8. Set the LexicalEnvironment of calleeContext to localEnv. + callee_context.lexical_environment = local_environment; - // 9. Set the VariableEnvironment of calleeContext to localEnv. - callee_context.variable_environment = local_environment; + // 9. Set the VariableEnvironment of calleeContext to localEnv. + callee_context.variable_environment = local_environment; + } else { + callee_context.lexical_environment = environment(); + callee_context.variable_environment = environment(); + } // 10. Set the PrivateEnvironment of calleeContext to F.[[PrivateEnvironment]]. callee_context.private_environment = m_private_environment; diff --git a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h index eec843fb6ac..425c126368f 100644 --- a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h +++ b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h @@ -38,8 +38,8 @@ public: 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, 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, 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, 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 = {}); virtual void initialize(Realm&) override; virtual ~ECMAScriptFunctionObject() override = default; @@ -104,7 +104,7 @@ protected: 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, 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, UsesThis 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; @@ -161,6 +161,7 @@ private: Vector m_functions_to_initialize; bool m_arguments_object_needed { false }; bool m_is_module_wrapper { false }; + bool m_function_environment_needed { false }; Vector m_var_names_to_initialize_binding; Vector m_function_names_to_initialize_binding; diff --git a/Userland/Libraries/LibJS/Runtime/FunctionConstructor.cpp b/Userland/Libraries/LibJS/Runtime/FunctionConstructor.cpp index f53d61c19a6..40cbad3bbf6 100644 --- a/Userland/Libraries/LibJS/Runtime/FunctionConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/FunctionConstructor.cpp @@ -183,7 +183,8 @@ ThrowCompletionOr FunctionConstructor::create_dynamic // 18. Let body be ParseText(StringToCodePoints(bodyString), bodySym). bool contains_direct_call_to_eval = false; - auto body_parser = Parser::parse_function_body_from_string(body_string, parse_options, parameters, kind, contains_direct_call_to_eval); + bool uses_this = false; + auto body_parser = Parser::parse_function_body_from_string(body_string, parse_options, parameters, kind, contains_direct_call_to_eval, uses_this); // 19. If body is a List of errors, throw a SyntaxError exception. if (body_parser.has_errors()) { @@ -218,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(), 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 ? UsesThis::Yes : UsesThis::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 4dd9d0affd3..ee87acbe254 100644 --- a/Userland/Libraries/LibJS/SourceTextModule.cpp +++ b/Userland/Libraries/LibJS/SourceTextModule.cpp @@ -498,7 +498,7 @@ 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.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(), 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 +759,7 @@ ThrowCompletionOr SourceTextModule::execute_module(VM& vm, GCPtrm_ecmascript_code, - {}, 0, {}, environment(), nullptr, FunctionKind::Async, true, false, false); + {}, 0, {}, environment(), nullptr, FunctionKind::Async, true, UsesThis::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 2d86e29899b..cac43d38835 100644 --- a/Userland/Libraries/LibWeb/DOM/EventTarget.cpp +++ b/Userland/Libraries/LibWeb/DOM/EventTarget.cpp @@ -485,7 +485,7 @@ 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->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(), 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 a3561a818b4..a18b2f5ec61 100644 --- a/Userland/Libraries/LibWeb/WebDriver/ExecuteScript.cpp +++ b/Userland/Libraries/LibWeb/WebDriver/ExecuteScript.cpp @@ -266,7 +266,7 @@ 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->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(), 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