mirror of
https://github.com/zebrajr/ladybird.git
synced 2025-12-06 00:19:53 +01:00
LibJS: Generate bytecode for the BlockDeclarationInstantiation AO
This necessitated adding some new instructions for creating mutable and immutable bindings.
This commit is contained in:
parent
892c7d980e
commit
44fa9566a8
|
|
@ -1621,80 +1621,6 @@ bool ImportStatement::has_bound_name(Utf16FlyString const& name) const
|
|||
});
|
||||
}
|
||||
|
||||
// 14.2.3 BlockDeclarationInstantiation ( code, env ), https://tc39.es/ecma262/#sec-blockdeclarationinstantiation
|
||||
void ScopeNode::block_declaration_instantiation(VM& vm, Environment* environment) const
|
||||
{
|
||||
// See also B.3.2.6 Changes to BlockDeclarationInstantiation, https://tc39.es/ecma262/#sec-web-compat-blockdeclarationinstantiation
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
VERIFY(environment);
|
||||
|
||||
// 1. Let declarations be the LexicallyScopedDeclarations of code.
|
||||
|
||||
// 2. Let privateEnv be the running execution context's PrivateEnvironment.
|
||||
auto private_environment = vm.running_execution_context().private_environment;
|
||||
|
||||
// Note: All the calls here are ! and thus we do not need to TRY this callback.
|
||||
// We use MUST to ensure it does not throw and to avoid discarding the returned ThrowCompletionOr<void>.
|
||||
// 3. For each element d of declarations, do
|
||||
MUST(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).
|
||||
MUST(environment->create_immutable_binding(vm, name, true));
|
||||
}
|
||||
// ii. Else,
|
||||
else {
|
||||
// 1. Perform ! env.CreateMutableBinding(dn, false). NOTE: This step is replaced in section B.3.2.6.
|
||||
if (!MUST(environment->has_binding(name)))
|
||||
MUST(environment->create_mutable_binding(vm, 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 function = ECMAScriptFunctionObject::create_from_function_node(
|
||||
function_declaration,
|
||||
function_declaration.name(),
|
||||
realm,
|
||||
environment,
|
||||
private_environment);
|
||||
|
||||
// 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& running_execution_context = vm.running_execution_context();
|
||||
auto number_of_registers = running_execution_context.executable->number_of_registers;
|
||||
auto number_of_constants = running_execution_context.executable->constants.size();
|
||||
auto local_index = function_declaration.name_identifier()->local_index();
|
||||
if (local_index.is_variable()) {
|
||||
running_execution_context.local(local_index.index + number_of_registers + number_of_constants) = function;
|
||||
} else {
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
} else {
|
||||
VERIFY(is<DeclarativeEnvironment>(*environment));
|
||||
static_cast<DeclarativeEnvironment&>(*environment).initialize_or_set_mutable_binding({}, vm, function->name(), function);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// 16.1.7 GlobalDeclarationInstantiation ( script, env ), https://tc39.es/ecma262/#sec-globaldeclarationinstantiation
|
||||
ThrowCompletionOr<void> Program::global_declaration_instantiation(VM& vm, GlobalEnvironment& global_environment) const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -334,8 +334,6 @@ public:
|
|||
ThrowCompletionOr<void> for_each_var_function_declaration_in_reverse_order(ThrowCompletionOrVoidCallback<FunctionDeclaration const&>&& callback) const;
|
||||
ThrowCompletionOr<void> for_each_var_scoped_variable_declaration(ThrowCompletionOrVoidCallback<VariableDeclaration const&>&& callback) const;
|
||||
|
||||
void block_declaration_instantiation(VM&, Environment*) const;
|
||||
|
||||
ThrowCompletionOr<void> for_each_function_hoistable_with_annexB_extension(ThrowCompletionOrVoidCallback<FunctionDeclaration&>&& callback) const;
|
||||
|
||||
auto const& local_variables_names() const { return m_local_variables_names; }
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@
|
|||
O(BitwiseNot) \
|
||||
O(BitwiseOr) \
|
||||
O(BitwiseXor) \
|
||||
O(BlockDeclarationInstantiation) \
|
||||
O(Call) \
|
||||
O(CallBuiltin) \
|
||||
O(CallConstruct) \
|
||||
|
|
@ -38,6 +37,8 @@
|
|||
O(CopyObjectExcludingProperties) \
|
||||
O(CreateArguments) \
|
||||
O(CreateLexicalEnvironment) \
|
||||
O(CreateImmutableBinding) \
|
||||
O(CreateMutableBinding) \
|
||||
O(CreatePrivateEnvironment) \
|
||||
O(CreateRestParams) \
|
||||
O(CreateVariable) \
|
||||
|
|
|
|||
|
|
@ -558,7 +558,6 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
|
|||
HANDLE_INSTRUCTION(BitwiseNot);
|
||||
HANDLE_INSTRUCTION(BitwiseOr);
|
||||
HANDLE_INSTRUCTION(BitwiseXor);
|
||||
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(BlockDeclarationInstantiation);
|
||||
HANDLE_INSTRUCTION(Call);
|
||||
HANDLE_INSTRUCTION(CallBuiltin);
|
||||
HANDLE_INSTRUCTION(CallConstruct);
|
||||
|
|
@ -569,6 +568,8 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
|
|||
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(Catch);
|
||||
HANDLE_INSTRUCTION(ConcatString);
|
||||
HANDLE_INSTRUCTION(CopyObjectExcludingProperties);
|
||||
HANDLE_INSTRUCTION(CreateImmutableBinding);
|
||||
HANDLE_INSTRUCTION(CreateMutableBinding);
|
||||
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(CreateLexicalEnvironment);
|
||||
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(CreateVariableEnvironment);
|
||||
HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(CreatePrivateEnvironment);
|
||||
|
|
@ -2433,6 +2434,8 @@ void CreateLexicalEnvironment::execute_impl(Bytecode::Interpreter& interpreter)
|
|||
};
|
||||
auto& running_execution_context = interpreter.running_execution_context();
|
||||
running_execution_context.saved_lexical_environments.append(make_and_swap_envs(running_execution_context.lexical_environment));
|
||||
if (m_dst.has_value())
|
||||
interpreter.set(*m_dst, running_execution_context.lexical_environment);
|
||||
}
|
||||
|
||||
void CreatePrivateEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
|
|
@ -3433,16 +3436,6 @@ ThrowCompletionOr<void> TypeofBinding::execute_impl(Bytecode::Interpreter& inter
|
|||
return {};
|
||||
}
|
||||
|
||||
void BlockDeclarationInstantiation::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
auto& vm = interpreter.vm();
|
||||
auto old_environment = interpreter.running_execution_context().lexical_environment;
|
||||
auto& running_execution_context = interpreter.running_execution_context();
|
||||
running_execution_context.saved_lexical_environments.append(old_environment);
|
||||
running_execution_context.lexical_environment = new_declarative_environment(*old_environment);
|
||||
m_scope_node.block_declaration_instantiation(vm, running_execution_context.lexical_environment);
|
||||
}
|
||||
|
||||
ByteString Mov::to_byte_string_impl(Bytecode::Executable const& executable) const
|
||||
{
|
||||
return ByteString::formatted("Mov {}, {}",
|
||||
|
|
@ -3565,8 +3558,10 @@ ByteString DeleteVariable::to_byte_string_impl(Bytecode::Executable const& execu
|
|||
return ByteString::formatted("DeleteVariable {}", executable.identifier_table->get(m_identifier));
|
||||
}
|
||||
|
||||
ByteString CreateLexicalEnvironment::to_byte_string_impl(Bytecode::Executable const&) const
|
||||
ByteString CreateLexicalEnvironment::to_byte_string_impl(Bytecode::Executable const& executable) const
|
||||
{
|
||||
if (m_dst.has_value())
|
||||
return ByteString::formatted("CreateLexicalEnvironment {}", format_operand("dst"sv, *m_dst, executable));
|
||||
return "CreateLexicalEnvironment"sv;
|
||||
}
|
||||
|
||||
|
|
@ -4151,11 +4146,6 @@ ByteString TypeofBinding::to_byte_string_impl(Bytecode::Executable const& execut
|
|||
executable.identifier_table->get(m_identifier));
|
||||
}
|
||||
|
||||
ByteString BlockDeclarationInstantiation::to_byte_string_impl(Bytecode::Executable const&) const
|
||||
{
|
||||
return "BlockDeclarationInstantiation"sv;
|
||||
}
|
||||
|
||||
ByteString ImportCall::to_byte_string_impl(Bytecode::Executable const& executable) const
|
||||
{
|
||||
return ByteString::formatted("ImportCall {}, {}, {}",
|
||||
|
|
@ -4234,4 +4224,32 @@ ByteString SetCompletionType::to_byte_string_impl(Bytecode::Executable const& ex
|
|||
to_underlying(m_type));
|
||||
}
|
||||
|
||||
ByteString CreateImmutableBinding::to_byte_string_impl(Executable const& executable) const
|
||||
{
|
||||
return ByteString::formatted("CreateImmutableBinding {} {} (strict: {})",
|
||||
format_operand("environment"sv, m_environment, executable),
|
||||
executable.get_identifier(m_identifier),
|
||||
m_strict);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> CreateImmutableBinding::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
auto& environment = as<Environment>(interpreter.get(m_environment).as_cell());
|
||||
return environment.create_immutable_binding(interpreter.vm(), interpreter.get_identifier(m_identifier), m_strict);
|
||||
}
|
||||
|
||||
ByteString CreateMutableBinding::to_byte_string_impl(Executable const& executable) const
|
||||
{
|
||||
return ByteString::formatted("CreateMutableBinding {} {} (can_be_deleted: {})",
|
||||
format_operand("environment"sv, m_environment, executable),
|
||||
executable.get_identifier(m_identifier),
|
||||
m_can_be_deleted);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> CreateMutableBinding::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||
{
|
||||
auto& environment = as<Environment>(interpreter.get(m_environment).as_cell());
|
||||
return environment.create_mutable_binding(interpreter.vm(), interpreter.get_identifier(m_identifier), m_can_be_deleted);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -546,8 +546,9 @@ enum class BindingInitializationMode {
|
|||
|
||||
class CreateLexicalEnvironment final : public Instruction {
|
||||
public:
|
||||
explicit CreateLexicalEnvironment(u32 capacity = 0)
|
||||
explicit CreateLexicalEnvironment(Optional<Operand> dst = {}, u32 capacity = 0)
|
||||
: Instruction(Type::CreateLexicalEnvironment)
|
||||
, m_dst(dst)
|
||||
, m_capacity(capacity)
|
||||
{
|
||||
}
|
||||
|
|
@ -556,6 +557,7 @@ public:
|
|||
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
|
||||
|
||||
private:
|
||||
Optional<Operand> m_dst;
|
||||
u32 m_capacity { 0 };
|
||||
};
|
||||
|
||||
|
|
@ -680,6 +682,44 @@ private:
|
|||
bool m_is_strict { false };
|
||||
};
|
||||
|
||||
class CreateImmutableBinding final : public Instruction {
|
||||
public:
|
||||
CreateImmutableBinding(Operand environment, IdentifierTableIndex identifier, bool strict)
|
||||
: Instruction(Type::CreateImmutableBinding)
|
||||
, m_environment(environment)
|
||||
, m_identifier(identifier)
|
||||
, m_strict(strict)
|
||||
{
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
|
||||
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
|
||||
|
||||
private:
|
||||
Operand m_environment;
|
||||
IdentifierTableIndex m_identifier;
|
||||
bool m_strict { false };
|
||||
};
|
||||
|
||||
class CreateMutableBinding final : public Instruction {
|
||||
public:
|
||||
CreateMutableBinding(Operand environment, IdentifierTableIndex identifier, bool can_be_deleted)
|
||||
: Instruction(Type::CreateMutableBinding)
|
||||
, m_environment(environment)
|
||||
, m_identifier(identifier)
|
||||
, m_can_be_deleted(can_be_deleted)
|
||||
{
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
|
||||
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
|
||||
|
||||
private:
|
||||
Operand m_environment;
|
||||
IdentifierTableIndex m_identifier;
|
||||
bool m_can_be_deleted { false };
|
||||
};
|
||||
|
||||
class InitializeLexicalBinding final : public Instruction {
|
||||
public:
|
||||
explicit InitializeLexicalBinding(IdentifierTableIndex identifier, Operand src)
|
||||
|
|
@ -2248,23 +2288,6 @@ private:
|
|||
Optional<Operand> m_home_object;
|
||||
};
|
||||
|
||||
class BlockDeclarationInstantiation final : public Instruction {
|
||||
public:
|
||||
explicit BlockDeclarationInstantiation(ScopeNode const& scope_node)
|
||||
: Instruction(Type::BlockDeclarationInstantiation)
|
||||
, m_scope_node(scope_node)
|
||||
{
|
||||
}
|
||||
|
||||
void execute_impl(Bytecode::Interpreter&) const;
|
||||
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
|
||||
|
||||
ScopeNode const& scope_node() const { return m_scope_node; }
|
||||
|
||||
private:
|
||||
ScopeNode const& m_scope_node;
|
||||
};
|
||||
|
||||
class Return final : public Instruction {
|
||||
public:
|
||||
constexpr static bool IsTerminator = true;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user