LibJS: Generate bytecode for the BlockDeclarationInstantiation AO

This necessitated adding some new instructions for creating mutable and
immutable bindings.
This commit is contained in:
Andreas Kling 2025-10-25 14:06:48 +02:00 committed by Andreas Kling
parent 892c7d980e
commit 44fa9566a8
Notes: github-actions[bot] 2025-10-27 20:15:37 +00:00
6 changed files with 131 additions and 117 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2024, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2021-2025, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -179,7 +179,7 @@ CodeGenerationErrorOr<void> Generator::emit_function_declaration_instantiation(E
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<Op::CreateLexicalEnvironment>(function.shared_data().m_lex_environment_bindings_count);
emit<Op::CreateLexicalEnvironment>(OptionalNone {}, function.shared_data().m_lex_environment_bindings_count);
}
}
@ -570,10 +570,58 @@ bool Generator::emit_block_declaration_instantiation(ScopeNode const& scope_node
if (!needs_block_declaration_instantiation)
return false;
// FIXME: Generate the actual bytecode for block declaration instantiation
// and get rid of the BlockDeclarationInstantiation instruction.
auto environment = allocate_register();
emit<Bytecode::Op::CreateLexicalEnvironment>(environment);
start_boundary(BlockBoundaryType::LeaveLexicalEnvironment);
emit<Bytecode::Op::BlockDeclarationInstantiation>(scope_node);
MUST(scope_node.for_each_lexically_scoped_declaration([&](Declaration const& declaration) {
auto is_constant_declaration = declaration.is_constant_declaration();
// NOTE: Due to the use of MUST with `create_immutable_binding` and `create_mutable_binding` below,
// an exception should not result from `for_each_bound_name`.
// a. For each element dn of the BoundNames of d, do
MUST(declaration.for_each_bound_identifier([&](Identifier const& identifier) {
if (identifier.is_local()) {
// NOTE: No need to create bindings for local variables as their values are not stored in an environment.
return;
}
auto const& name = identifier.string();
// i. If IsConstantDeclaration of d is true, then
if (is_constant_declaration) {
// 1. Perform ! env.CreateImmutableBinding(dn, true).
emit<Bytecode::Op::CreateImmutableBinding>(environment, intern_identifier(name), true);
}
// ii. Else,
else {
// 1. Perform ! env.CreateMutableBinding(dn, false). NOTE: This step is replaced in section B.3.2.6.
emit<Bytecode::Op::CreateMutableBinding>(environment, intern_identifier(name), false);
}
}));
// b. If d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration, then
if (is<FunctionDeclaration>(declaration)) {
// i. Let fn be the sole element of the BoundNames of d.
auto& function_declaration = static_cast<FunctionDeclaration const&>(declaration);
// ii. Let fo be InstantiateFunctionObject of d with arguments env and privateEnv.
auto fo = allocate_register();
emit<Bytecode::Op::NewFunction>(fo, function_declaration, OptionalNone {});
// iii. Perform ! env.InitializeBinding(fn, fo). NOTE: This step is replaced in section B.3.2.6.
if (function_declaration.name_identifier()->is_local()) {
auto local_index = function_declaration.name_identifier()->local_index();
if (local_index.is_variable()) {
emit<Bytecode::Op::Mov>(local(local_index), fo);
} else {
VERIFY_NOT_REACHED();
}
} else {
emit<Bytecode::Op::InitializeLexicalBinding>(intern_identifier(function_declaration.name()), fo);
}
}
}));
return true;
}