LibWeb+LibJS: Skip function environment allocation if possible

If a function has the following properties:
- uses only local variables and registers
- does not use `this`
- does not use `new.target`
- does not use `super`
- does not use direct eval() calls

then it is possible to entirely skip function environment allocation
because it will never be used

This change adds gathering of information whether a function needs to
access `this` from environment and updates `prepare_for_ordinary_call()`
to skip allocation when possible.

For now, this optimisation is too aggressively blocked; e.g. if `this`
is used in a function scope, then all functions in outer scopes have to
allocate an environment. It could be improved in the future, although
this implementation already allows skipping >80% of environment
allocations on Discord, GitHub and Twitter.
This commit is contained in:
Aliaksandr Kalenik 2024-05-03 22:54:12 +02:00 committed by Andreas Kling
commit 4d5823a5bc
Notes: sideshowbarker 2024-07-17 06:20:50 +09:00
12 changed files with 94 additions and 46 deletions

View file

@ -498,7 +498,7 @@ ThrowCompletionOr<void> 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<void> SourceTextModule::execute_module(VM& vm, GCPtr<PromiseCa
auto module_wrapper_function = ECMAScriptFunctionObject::create(
realm(), "module code with top-level await", StringView {}, this->m_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