LibJS: Skip ordinary_call_bind_this() when possible

If during parsing it was found that function won't use `this` then
there is no need to initialise `this_value` during call.
This commit is contained in:
Aliaksandr Kalenik 2024-05-22 18:50:45 +01:00 committed by Andreas Kling
commit f29ac8517e
Notes: sideshowbarker 2024-07-16 19:17:47 +09:00
6 changed files with 25 additions and 16 deletions

View file

@ -242,6 +242,7 @@ ThrowCompletionOr<ClassElement::ClassValue> ClassField::class_element_evaluation
auto function_code = create_ast_node<ClassFieldInitializerStatement>(m_initializer->source_range(), copy_initializer.release_nonnull(), name); auto function_code = create_ast_node<ClassFieldInitializerStatement>(m_initializer->source_range(), copy_initializer.release_nonnull(), name);
FunctionParsingInsights parsing_insights; FunctionParsingInsights parsing_insights;
parsing_insights.uses_this_from_environment = true; parsing_insights.uses_this_from_environment = true;
parsing_insights.uses_this = true;
initializer = make_handle(*ECMAScriptFunctionObject::create(realm, "field", ByteString::empty(), *function_code, {}, 0, {}, vm.lexical_environment(), vm.running_execution_context().private_environment, FunctionKind::Normal, true, parsing_insights, 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, parsing_insights, false, property_key_or_private_name));
initializer->make_method(target); initializer->make_method(target);
} }
@ -288,6 +289,7 @@ ThrowCompletionOr<ClassElement::ClassValue> StaticInitializer::class_element_eva
// Note: The function bodyFunction is never directly accessible to ECMAScript code. // Note: The function bodyFunction is never directly accessible to ECMAScript code.
FunctionParsingInsights parsing_insights; FunctionParsingInsights parsing_insights;
parsing_insights.uses_this_from_environment = true; parsing_insights.uses_this_from_environment = true;
parsing_insights.uses_this = true;
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, parsing_insights, 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, parsing_insights, false);
// 6. Perform MakeMethod(bodyFunction, homeObject). // 6. Perform MakeMethod(bodyFunction, homeObject).
@ -337,6 +339,7 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::create_class_const
auto const& constructor = *m_constructor; auto const& constructor = *m_constructor;
auto parsing_insights = constructor.parsing_insights(); auto parsing_insights = constructor.parsing_insights();
parsing_insights.uses_this_from_environment = true; parsing_insights.uses_this_from_environment = true;
parsing_insights.uses_this = true;
auto class_constructor = ECMAScriptFunctionObject::create( auto class_constructor = ECMAScriptFunctionObject::create(
realm, realm,
constructor.name(), constructor.name(),

View file

@ -694,6 +694,7 @@ struct FunctionParameter {
}; };
struct FunctionParsingInsights { struct FunctionParsingInsights {
bool uses_this { false };
bool uses_this_from_environment { false }; bool uses_this_from_environment { false };
bool contains_direct_call_to_eval { false }; bool contains_direct_call_to_eval { false };
bool might_need_arguments_object { false }; bool might_need_arguments_object { false };

View file

@ -433,34 +433,31 @@ public:
} }
} }
bool uses_this_from_environment() const bool uses_this() const { return m_uses_this; }
{ bool uses_this_from_environment() const { return m_uses_this_from_environment; }
return m_uses_this_from_environment;
}
void set_uses_this() void set_uses_this()
{ {
auto const* closest_function_scope = last_function_scope(); auto const* closest_function_scope = last_function_scope();
if (!closest_function_scope || !closest_function_scope->m_is_arrow_function) auto uses_this_from_environment = 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) { for (auto* scope_ptr = this; scope_ptr; scope_ptr = scope_ptr->m_parent_scope) {
if (scope_ptr->m_uses_this_from_environment) if (scope_ptr->m_type == ScopeType::Function) {
break; scope_ptr->m_uses_this = true;
if (scope_ptr->m_type == ScopeType::Function) if (uses_this_from_environment)
scope_ptr->m_uses_this_from_environment = true; scope_ptr->m_uses_this_from_environment = true;
} }
} }
}
void set_uses_new_target() void set_uses_new_target()
{ {
for (auto* scope_ptr = this; scope_ptr; scope_ptr = scope_ptr->m_parent_scope) { for (auto* scope_ptr = this; scope_ptr; scope_ptr = scope_ptr->m_parent_scope) {
if (scope_ptr->m_uses_this_from_environment) if (scope_ptr->m_type == ScopeType::Function) {
break; scope_ptr->m_uses_this = true;
if (scope_ptr->m_type == ScopeType::Function)
scope_ptr->m_uses_this_from_environment = true; scope_ptr->m_uses_this_from_environment = true;
} }
} }
}
void set_is_arrow_function() void set_is_arrow_function()
{ {
@ -512,6 +509,7 @@ private:
// 1. It's an arrow function or establish parent scope for an arrow function // 1. It's an arrow function or establish parent scope for an arrow function
// 2. Uses new.target // 2. Uses new.target
bool m_uses_this_from_environment { false }; bool m_uses_this_from_environment { false };
bool m_uses_this { false };
bool m_is_arrow_function { false }; bool m_is_arrow_function { false };
}; };
@ -2307,6 +2305,7 @@ NonnullRefPtr<Expression const> Parser::parse_expression(int min_precedence, Ass
auto& callee = static_ptr_cast<CallExpression const>(expression)->callee(); auto& callee = static_ptr_cast<CallExpression const>(expression)->callee();
if (is<Identifier>(callee) && static_cast<Identifier const&>(callee).string() == "eval"sv) { if (is<Identifier>(callee) && static_cast<Identifier const&>(callee).string() == "eval"sv) {
m_state.current_scope_pusher->set_contains_direct_call_to_eval(); m_state.current_scope_pusher->set_contains_direct_call_to_eval();
m_state.current_scope_pusher->set_uses_this();
} }
} }
@ -2845,6 +2844,7 @@ NonnullRefPtr<FunctionBody const> Parser::parse_function_body(Vector<FunctionPar
VERIFY(m_state.current_scope_pusher->type() == ScopePusher::ScopeType::Function); VERIFY(m_state.current_scope_pusher->type() == ScopePusher::ScopeType::Function);
parsing_insights.contains_direct_call_to_eval = m_state.current_scope_pusher->contains_direct_call_to_eval(); parsing_insights.contains_direct_call_to_eval = m_state.current_scope_pusher->contains_direct_call_to_eval();
parsing_insights.uses_this_from_environment = m_state.current_scope_pusher->uses_this_from_environment(); parsing_insights.uses_this_from_environment = m_state.current_scope_pusher->uses_this_from_environment();
parsing_insights.uses_this = m_state.current_scope_pusher->uses_this();
return function_body; return function_body;
} }

View file

@ -332,6 +332,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 || parsing_insights.uses_this_from_environment || 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 || parsing_insights.uses_this_from_environment || m_contains_direct_call_to_eval;
m_uses_this = parsing_insights.uses_this;
} }
void ECMAScriptFunctionObject::initialize(Realm& realm) void ECMAScriptFunctionObject::initialize(Realm& realm)
@ -414,6 +415,7 @@ ThrowCompletionOr<Value> ECMAScriptFunctionObject::internal_call(Value this_argu
} }
// 5. Perform OrdinaryCallBindThis(F, calleeContext, thisArgument). // 5. Perform OrdinaryCallBindThis(F, calleeContext, thisArgument).
if (m_uses_this)
ordinary_call_bind_this(*callee_context, this_argument); ordinary_call_bind_this(*callee_context, this_argument);
// 6. Let result be Completion(OrdinaryCallEvaluateBody(F, argumentsList)). // 6. Let result be Completion(OrdinaryCallEvaluateBody(F, argumentsList)).
@ -477,6 +479,7 @@ ThrowCompletionOr<NonnullGCPtr<Object>> ECMAScriptFunctionObject::internal_const
// 6. If kind is base, then // 6. If kind is base, then
if (kind == ConstructorKind::Base) { if (kind == ConstructorKind::Base) {
// a. Perform OrdinaryCallBindThis(F, calleeContext, thisArgument). // a. Perform OrdinaryCallBindThis(F, calleeContext, thisArgument).
if (m_uses_this)
ordinary_call_bind_this(*callee_context, this_argument); ordinary_call_bind_this(*callee_context, this_argument);
// b. Let initializeResult be Completion(InitializeInstanceElements(thisArgument, F)). // b. Let initializeResult be Completion(InitializeInstanceElements(thisArgument, F)).

View file

@ -164,6 +164,7 @@ private:
bool m_arguments_object_needed { false }; bool m_arguments_object_needed { false };
bool m_is_module_wrapper { false }; bool m_is_module_wrapper { false };
bool m_function_environment_needed { false }; bool m_function_environment_needed { false };
bool m_uses_this { false };
Vector<VariableNameToInitialize> m_var_names_to_initialize_binding; Vector<VariableNameToInitialize> m_var_names_to_initialize_binding;
Vector<DeprecatedFlyString> m_function_names_to_initialize_binding; Vector<DeprecatedFlyString> m_function_names_to_initialize_binding;

View file

@ -760,6 +760,7 @@ ThrowCompletionOr<void> SourceTextModule::execute_module(VM& vm, GCPtr<PromiseCa
FunctionParsingInsights parsing_insights; FunctionParsingInsights parsing_insights;
parsing_insights.uses_this_from_environment = true; parsing_insights.uses_this_from_environment = true;
parsing_insights.uses_this = true;
auto module_wrapper_function = ECMAScriptFunctionObject::create( auto module_wrapper_function = ECMAScriptFunctionObject::create(
realm(), "module code with top-level await", StringView {}, this->m_ecmascript_code, realm(), "module code with top-level await", StringView {}, this->m_ecmascript_code,
{}, 0, {}, environment(), nullptr, FunctionKind::Async, true, parsing_insights); {}, 0, {}, environment(), nullptr, FunctionKind::Async, true, parsing_insights);