Skip to content

Commit 44fa956

Browse files
committed
LibJS: Generate bytecode for the BlockDeclarationInstantiation AO
This necessitated adding some new instructions for creating mutable and immutable bindings.
1 parent 892c7d9 commit 44fa956

File tree

6 files changed

+131
-117
lines changed

6 files changed

+131
-117
lines changed

Libraries/LibJS/AST.cpp

Lines changed: 0 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1621,80 +1621,6 @@ bool ImportStatement::has_bound_name(Utf16FlyString const& name) const
16211621
});
16221622
}
16231623

1624-
// 14.2.3 BlockDeclarationInstantiation ( code, env ), https://tc39.es/ecma262/#sec-blockdeclarationinstantiation
1625-
void ScopeNode::block_declaration_instantiation(VM& vm, Environment* environment) const
1626-
{
1627-
// See also B.3.2.6 Changes to BlockDeclarationInstantiation, https://tc39.es/ecma262/#sec-web-compat-blockdeclarationinstantiation
1628-
auto& realm = *vm.current_realm();
1629-
1630-
VERIFY(environment);
1631-
1632-
// 1. Let declarations be the LexicallyScopedDeclarations of code.
1633-
1634-
// 2. Let privateEnv be the running execution context's PrivateEnvironment.
1635-
auto private_environment = vm.running_execution_context().private_environment;
1636-
1637-
// Note: All the calls here are ! and thus we do not need to TRY this callback.
1638-
// We use MUST to ensure it does not throw and to avoid discarding the returned ThrowCompletionOr<void>.
1639-
// 3. For each element d of declarations, do
1640-
MUST(for_each_lexically_scoped_declaration([&](Declaration const& declaration) {
1641-
auto is_constant_declaration = declaration.is_constant_declaration();
1642-
// NOTE: Due to the use of MUST with `create_immutable_binding` and `create_mutable_binding` below,
1643-
// an exception should not result from `for_each_bound_name`.
1644-
// a. For each element dn of the BoundNames of d, do
1645-
MUST(declaration.for_each_bound_identifier([&](Identifier const& identifier) {
1646-
if (identifier.is_local()) {
1647-
// NOTE: No need to create bindings for local variables as their values are not stored in an environment.
1648-
return;
1649-
}
1650-
1651-
auto const& name = identifier.string();
1652-
1653-
// i. If IsConstantDeclaration of d is true, then
1654-
if (is_constant_declaration) {
1655-
// 1. Perform ! env.CreateImmutableBinding(dn, true).
1656-
MUST(environment->create_immutable_binding(vm, name, true));
1657-
}
1658-
// ii. Else,
1659-
else {
1660-
// 1. Perform ! env.CreateMutableBinding(dn, false). NOTE: This step is replaced in section B.3.2.6.
1661-
if (!MUST(environment->has_binding(name)))
1662-
MUST(environment->create_mutable_binding(vm, name, false));
1663-
}
1664-
}));
1665-
1666-
// b. If d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration, then
1667-
if (is<FunctionDeclaration>(declaration)) {
1668-
// i. Let fn be the sole element of the BoundNames of d.
1669-
auto& function_declaration = static_cast<FunctionDeclaration const&>(declaration);
1670-
1671-
// ii. Let fo be InstantiateFunctionObject of d with arguments env and privateEnv.
1672-
auto function = ECMAScriptFunctionObject::create_from_function_node(
1673-
function_declaration,
1674-
function_declaration.name(),
1675-
realm,
1676-
environment,
1677-
private_environment);
1678-
1679-
// iii. Perform ! env.InitializeBinding(fn, fo). NOTE: This step is replaced in section B.3.2.6.
1680-
if (function_declaration.name_identifier()->is_local()) {
1681-
auto& running_execution_context = vm.running_execution_context();
1682-
auto number_of_registers = running_execution_context.executable->number_of_registers;
1683-
auto number_of_constants = running_execution_context.executable->constants.size();
1684-
auto local_index = function_declaration.name_identifier()->local_index();
1685-
if (local_index.is_variable()) {
1686-
running_execution_context.local(local_index.index + number_of_registers + number_of_constants) = function;
1687-
} else {
1688-
VERIFY_NOT_REACHED();
1689-
}
1690-
} else {
1691-
VERIFY(is<DeclarativeEnvironment>(*environment));
1692-
static_cast<DeclarativeEnvironment&>(*environment).initialize_or_set_mutable_binding({}, vm, function->name(), function);
1693-
}
1694-
}
1695-
}));
1696-
}
1697-
16981624
// 16.1.7 GlobalDeclarationInstantiation ( script, env ), https://tc39.es/ecma262/#sec-globaldeclarationinstantiation
16991625
ThrowCompletionOr<void> Program::global_declaration_instantiation(VM& vm, GlobalEnvironment& global_environment) const
17001626
{

Libraries/LibJS/AST.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,8 +334,6 @@ class JS_API ScopeNode : public Statement {
334334
ThrowCompletionOr<void> for_each_var_function_declaration_in_reverse_order(ThrowCompletionOrVoidCallback<FunctionDeclaration const&>&& callback) const;
335335
ThrowCompletionOr<void> for_each_var_scoped_variable_declaration(ThrowCompletionOrVoidCallback<VariableDeclaration const&>&& callback) const;
336336

337-
void block_declaration_instantiation(VM&, Environment*) const;
338-
339337
ThrowCompletionOr<void> for_each_function_hoistable_with_annexB_extension(ThrowCompletionOrVoidCallback<FunctionDeclaration&>&& callback) const;
340338

341339
auto const& local_variables_names() const { return m_local_variables_names; }

Libraries/LibJS/Bytecode/Generator.cpp

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021-2024, Andreas Kling <andreas@ladybird.org>
2+
* Copyright (c) 2021-2025, Andreas Kling <andreas@ladybird.org>
33
*
44
* SPDX-License-Identifier: BSD-2-Clause
55
*/
@@ -179,7 +179,7 @@ CodeGenerationErrorOr<void> Generator::emit_function_declaration_instantiation(E
179179
if (!function.is_strict_mode()) {
180180
bool can_elide_lexical_environment = !scope_body || !scope_body->has_non_local_lexical_declarations();
181181
if (!can_elide_lexical_environment) {
182-
emit<Op::CreateLexicalEnvironment>(function.shared_data().m_lex_environment_bindings_count);
182+
emit<Op::CreateLexicalEnvironment>(OptionalNone {}, function.shared_data().m_lex_environment_bindings_count);
183183
}
184184
}
185185

@@ -570,10 +570,58 @@ bool Generator::emit_block_declaration_instantiation(ScopeNode const& scope_node
570570
if (!needs_block_declaration_instantiation)
571571
return false;
572572

573-
// FIXME: Generate the actual bytecode for block declaration instantiation
574-
// and get rid of the BlockDeclarationInstantiation instruction.
573+
auto environment = allocate_register();
574+
emit<Bytecode::Op::CreateLexicalEnvironment>(environment);
575575
start_boundary(BlockBoundaryType::LeaveLexicalEnvironment);
576-
emit<Bytecode::Op::BlockDeclarationInstantiation>(scope_node);
576+
577+
MUST(scope_node.for_each_lexically_scoped_declaration([&](Declaration const& declaration) {
578+
auto is_constant_declaration = declaration.is_constant_declaration();
579+
// NOTE: Due to the use of MUST with `create_immutable_binding` and `create_mutable_binding` below,
580+
// an exception should not result from `for_each_bound_name`.
581+
// a. For each element dn of the BoundNames of d, do
582+
MUST(declaration.for_each_bound_identifier([&](Identifier const& identifier) {
583+
if (identifier.is_local()) {
584+
// NOTE: No need to create bindings for local variables as their values are not stored in an environment.
585+
return;
586+
}
587+
588+
auto const& name = identifier.string();
589+
590+
// i. If IsConstantDeclaration of d is true, then
591+
if (is_constant_declaration) {
592+
// 1. Perform ! env.CreateImmutableBinding(dn, true).
593+
emit<Bytecode::Op::CreateImmutableBinding>(environment, intern_identifier(name), true);
594+
}
595+
// ii. Else,
596+
else {
597+
// 1. Perform ! env.CreateMutableBinding(dn, false). NOTE: This step is replaced in section B.3.2.6.
598+
emit<Bytecode::Op::CreateMutableBinding>(environment, intern_identifier(name), false);
599+
}
600+
}));
601+
602+
// b. If d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration, then
603+
if (is<FunctionDeclaration>(declaration)) {
604+
// i. Let fn be the sole element of the BoundNames of d.
605+
auto& function_declaration = static_cast<FunctionDeclaration const&>(declaration);
606+
607+
// ii. Let fo be InstantiateFunctionObject of d with arguments env and privateEnv.
608+
auto fo = allocate_register();
609+
emit<Bytecode::Op::NewFunction>(fo, function_declaration, OptionalNone {});
610+
611+
// iii. Perform ! env.InitializeBinding(fn, fo). NOTE: This step is replaced in section B.3.2.6.
612+
if (function_declaration.name_identifier()->is_local()) {
613+
auto local_index = function_declaration.name_identifier()->local_index();
614+
if (local_index.is_variable()) {
615+
emit<Bytecode::Op::Mov>(local(local_index), fo);
616+
} else {
617+
VERIFY_NOT_REACHED();
618+
}
619+
} else {
620+
emit<Bytecode::Op::InitializeLexicalBinding>(intern_identifier(function_declaration.name()), fo);
621+
}
622+
}
623+
}));
624+
577625
return true;
578626
}
579627

Libraries/LibJS/Bytecode/Instruction.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
O(BitwiseNot) \
2525
O(BitwiseOr) \
2626
O(BitwiseXor) \
27-
O(BlockDeclarationInstantiation) \
2827
O(Call) \
2928
O(CallBuiltin) \
3029
O(CallConstruct) \
@@ -38,6 +37,8 @@
3837
O(CopyObjectExcludingProperties) \
3938
O(CreateArguments) \
4039
O(CreateLexicalEnvironment) \
40+
O(CreateImmutableBinding) \
41+
O(CreateMutableBinding) \
4142
O(CreatePrivateEnvironment) \
4243
O(CreateRestParams) \
4344
O(CreateVariable) \

Libraries/LibJS/Bytecode/Interpreter.cpp

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,6 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
558558
HANDLE_INSTRUCTION(BitwiseNot);
559559
HANDLE_INSTRUCTION(BitwiseOr);
560560
HANDLE_INSTRUCTION(BitwiseXor);
561-
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(BlockDeclarationInstantiation);
562561
HANDLE_INSTRUCTION(Call);
563562
HANDLE_INSTRUCTION(CallBuiltin);
564563
HANDLE_INSTRUCTION(CallConstruct);
@@ -569,6 +568,8 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
569568
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(Catch);
570569
HANDLE_INSTRUCTION(ConcatString);
571570
HANDLE_INSTRUCTION(CopyObjectExcludingProperties);
571+
HANDLE_INSTRUCTION(CreateImmutableBinding);
572+
HANDLE_INSTRUCTION(CreateMutableBinding);
572573
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(CreateLexicalEnvironment);
573574
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(CreateVariableEnvironment);
574575
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(CreatePrivateEnvironment);
@@ -2433,6 +2434,8 @@ void CreateLexicalEnvironment::execute_impl(Bytecode::Interpreter& interpreter)
24332434
};
24342435
auto& running_execution_context = interpreter.running_execution_context();
24352436
running_execution_context.saved_lexical_environments.append(make_and_swap_envs(running_execution_context.lexical_environment));
2437+
if (m_dst.has_value())
2438+
interpreter.set(*m_dst, running_execution_context.lexical_environment);
24362439
}
24372440

24382441
void CreatePrivateEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const
@@ -3433,16 +3436,6 @@ ThrowCompletionOr<void> TypeofBinding::execute_impl(Bytecode::Interpreter& inter
34333436
return {};
34343437
}
34353438

3436-
void BlockDeclarationInstantiation::execute_impl(Bytecode::Interpreter& interpreter) const
3437-
{
3438-
auto& vm = interpreter.vm();
3439-
auto old_environment = interpreter.running_execution_context().lexical_environment;
3440-
auto& running_execution_context = interpreter.running_execution_context();
3441-
running_execution_context.saved_lexical_environments.append(old_environment);
3442-
running_execution_context.lexical_environment = new_declarative_environment(*old_environment);
3443-
m_scope_node.block_declaration_instantiation(vm, running_execution_context.lexical_environment);
3444-
}
3445-
34463439
ByteString Mov::to_byte_string_impl(Bytecode::Executable const& executable) const
34473440
{
34483441
return ByteString::formatted("Mov {}, {}",
@@ -3565,8 +3558,10 @@ ByteString DeleteVariable::to_byte_string_impl(Bytecode::Executable const& execu
35653558
return ByteString::formatted("DeleteVariable {}", executable.identifier_table->get(m_identifier));
35663559
}
35673560

3568-
ByteString CreateLexicalEnvironment::to_byte_string_impl(Bytecode::Executable const&) const
3561+
ByteString CreateLexicalEnvironment::to_byte_string_impl(Bytecode::Executable const& executable) const
35693562
{
3563+
if (m_dst.has_value())
3564+
return ByteString::formatted("CreateLexicalEnvironment {}", format_operand("dst"sv, *m_dst, executable));
35703565
return "CreateLexicalEnvironment"sv;
35713566
}
35723567

@@ -4151,11 +4146,6 @@ ByteString TypeofBinding::to_byte_string_impl(Bytecode::Executable const& execut
41514146
executable.identifier_table->get(m_identifier));
41524147
}
41534148

4154-
ByteString BlockDeclarationInstantiation::to_byte_string_impl(Bytecode::Executable const&) const
4155-
{
4156-
return "BlockDeclarationInstantiation"sv;
4157-
}
4158-
41594149
ByteString ImportCall::to_byte_string_impl(Bytecode::Executable const& executable) const
41604150
{
41614151
return ByteString::formatted("ImportCall {}, {}, {}",
@@ -4234,4 +4224,32 @@ ByteString SetCompletionType::to_byte_string_impl(Bytecode::Executable const& ex
42344224
to_underlying(m_type));
42354225
}
42364226

4227+
ByteString CreateImmutableBinding::to_byte_string_impl(Executable const& executable) const
4228+
{
4229+
return ByteString::formatted("CreateImmutableBinding {} {} (strict: {})",
4230+
format_operand("environment"sv, m_environment, executable),
4231+
executable.get_identifier(m_identifier),
4232+
m_strict);
4233+
}
4234+
4235+
ThrowCompletionOr<void> CreateImmutableBinding::execute_impl(Bytecode::Interpreter& interpreter) const
4236+
{
4237+
auto& environment = as<Environment>(interpreter.get(m_environment).as_cell());
4238+
return environment.create_immutable_binding(interpreter.vm(), interpreter.get_identifier(m_identifier), m_strict);
4239+
}
4240+
4241+
ByteString CreateMutableBinding::to_byte_string_impl(Executable const& executable) const
4242+
{
4243+
return ByteString::formatted("CreateMutableBinding {} {} (can_be_deleted: {})",
4244+
format_operand("environment"sv, m_environment, executable),
4245+
executable.get_identifier(m_identifier),
4246+
m_can_be_deleted);
4247+
}
4248+
4249+
ThrowCompletionOr<void> CreateMutableBinding::execute_impl(Bytecode::Interpreter& interpreter) const
4250+
{
4251+
auto& environment = as<Environment>(interpreter.get(m_environment).as_cell());
4252+
return environment.create_mutable_binding(interpreter.vm(), interpreter.get_identifier(m_identifier), m_can_be_deleted);
4253+
}
4254+
42374255
}

Libraries/LibJS/Bytecode/Op.h

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -546,8 +546,9 @@ enum class BindingInitializationMode {
546546

547547
class CreateLexicalEnvironment final : public Instruction {
548548
public:
549-
explicit CreateLexicalEnvironment(u32 capacity = 0)
549+
explicit CreateLexicalEnvironment(Optional<Operand> dst = {}, u32 capacity = 0)
550550
: Instruction(Type::CreateLexicalEnvironment)
551+
, m_dst(dst)
551552
, m_capacity(capacity)
552553
{
553554
}
@@ -556,6 +557,7 @@ class CreateLexicalEnvironment final : public Instruction {
556557
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
557558

558559
private:
560+
Optional<Operand> m_dst;
559561
u32 m_capacity { 0 };
560562
};
561563

@@ -680,6 +682,44 @@ class CreateVariable final : public Instruction {
680682
bool m_is_strict { false };
681683
};
682684

685+
class CreateImmutableBinding final : public Instruction {
686+
public:
687+
CreateImmutableBinding(Operand environment, IdentifierTableIndex identifier, bool strict)
688+
: Instruction(Type::CreateImmutableBinding)
689+
, m_environment(environment)
690+
, m_identifier(identifier)
691+
, m_strict(strict)
692+
{
693+
}
694+
695+
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
696+
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
697+
698+
private:
699+
Operand m_environment;
700+
IdentifierTableIndex m_identifier;
701+
bool m_strict { false };
702+
};
703+
704+
class CreateMutableBinding final : public Instruction {
705+
public:
706+
CreateMutableBinding(Operand environment, IdentifierTableIndex identifier, bool can_be_deleted)
707+
: Instruction(Type::CreateMutableBinding)
708+
, m_environment(environment)
709+
, m_identifier(identifier)
710+
, m_can_be_deleted(can_be_deleted)
711+
{
712+
}
713+
714+
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
715+
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
716+
717+
private:
718+
Operand m_environment;
719+
IdentifierTableIndex m_identifier;
720+
bool m_can_be_deleted { false };
721+
};
722+
683723
class InitializeLexicalBinding final : public Instruction {
684724
public:
685725
explicit InitializeLexicalBinding(IdentifierTableIndex identifier, Operand src)
@@ -2248,23 +2288,6 @@ class NewFunction final : public Instruction {
22482288
Optional<Operand> m_home_object;
22492289
};
22502290

2251-
class BlockDeclarationInstantiation final : public Instruction {
2252-
public:
2253-
explicit BlockDeclarationInstantiation(ScopeNode const& scope_node)
2254-
: Instruction(Type::BlockDeclarationInstantiation)
2255-
, m_scope_node(scope_node)
2256-
{
2257-
}
2258-
2259-
void execute_impl(Bytecode::Interpreter&) const;
2260-
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
2261-
2262-
ScopeNode const& scope_node() const { return m_scope_node; }
2263-
2264-
private:
2265-
ScopeNode const& m_scope_node;
2266-
};
2267-
22682291
class Return final : public Instruction {
22692292
public:
22702293
constexpr static bool IsTerminator = true;

0 commit comments

Comments
 (0)