1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-10 04:17:34 +00:00

LibJS: Update bytecode generator to use local variables

- Update ECMAScriptFunctionObject::function_declaration_instantiation
  to initialize local variables
- Introduce GetLocal, SetLocal, TypeofLocal that will be used to
  operate on local variables.
- Update bytecode generator to emit instructions for local variables
This commit is contained in:
Aliaksandr Kalenik 2023-07-05 02:17:10 +02:00 committed by Andreas Kling
parent 0daff637e2
commit ae3a7fd4b8
8 changed files with 170 additions and 37 deletions

View file

@ -221,7 +221,11 @@ Bytecode::CodeGenerationErrorOr<void> UnaryExpression::generate_bytecode(Bytecod
case UnaryOp::Typeof: case UnaryOp::Typeof:
if (is<Identifier>(*m_lhs)) { if (is<Identifier>(*m_lhs)) {
auto& identifier = static_cast<Identifier const&>(*m_lhs); auto& identifier = static_cast<Identifier const&>(*m_lhs);
generator.emit<Bytecode::Op::TypeofVariable>(generator.intern_identifier(identifier.string())); if (identifier.is_local()) {
generator.emit<Bytecode::Op::TypeofLocal>(identifier.local_variable_index());
} else {
generator.emit<Bytecode::Op::TypeofVariable>(generator.intern_identifier(identifier.string()));
}
break; break;
} }
@ -291,7 +295,11 @@ Bytecode::CodeGenerationErrorOr<void> RegExpLiteral::generate_bytecode(Bytecode:
Bytecode::CodeGenerationErrorOr<void> Identifier::generate_bytecode(Bytecode::Generator& generator) const Bytecode::CodeGenerationErrorOr<void> Identifier::generate_bytecode(Bytecode::Generator& generator) const
{ {
generator.emit<Bytecode::Op::GetVariable>(generator.intern_identifier(m_string)); if (is_local()) {
generator.emit<Bytecode::Op::GetLocal>(local_variable_index());
} else {
generator.emit<Bytecode::Op::GetVariable>(generator.intern_identifier(m_string));
}
return {}; return {};
} }
@ -415,7 +423,7 @@ Bytecode::CodeGenerationErrorOr<void> AssignmentExpression::generate_bytecode(By
// e. Perform ? PutValue(lref, rval). // e. Perform ? PutValue(lref, rval).
if (is<Identifier>(*lhs)) { if (is<Identifier>(*lhs)) {
auto& identifier = static_cast<Identifier const&>(*lhs); auto& identifier = static_cast<Identifier const&>(*lhs);
generator.emit<Bytecode::Op::SetVariable>(generator.intern_identifier(identifier.string())); generator.emit_set_variable(identifier);
} else if (is<MemberExpression>(*lhs)) { } else if (is<MemberExpression>(*lhs)) {
auto& expression = static_cast<MemberExpression const&>(*lhs); auto& expression = static_cast<MemberExpression const&>(*lhs);
@ -1043,13 +1051,15 @@ static Bytecode::CodeGenerationErrorOr<void> generate_object_binding_pattern_byt
if (is_rest) { if (is_rest) {
VERIFY(!initializer); VERIFY(!initializer);
if (name.has<NonnullRefPtr<Identifier const>>()) { if (name.has<NonnullRefPtr<Identifier const>>()) {
auto identifier = name.get<NonnullRefPtr<Identifier const>>()->string(); auto identifier = name.get<NonnullRefPtr<Identifier const>>();
auto interned_identifier = generator.intern_identifier(identifier); auto interned_identifier = generator.intern_identifier(identifier->string());
generator.emit_with_extra_register_slots<Bytecode::Op::CopyObjectExcludingProperties>(excluded_property_names.size(), value_reg, excluded_property_names); generator.emit_with_extra_register_slots<Bytecode::Op::CopyObjectExcludingProperties>(excluded_property_names.size(), value_reg, excluded_property_names);
if (create_variables) if (create_variables) {
VERIFY(!identifier->is_local());
generator.emit<Bytecode::Op::CreateVariable>(interned_identifier, Bytecode::Op::EnvironmentMode::Lexical, false); generator.emit<Bytecode::Op::CreateVariable>(interned_identifier, Bytecode::Op::EnvironmentMode::Lexical, false);
generator.emit<Bytecode::Op::SetVariable>(interned_identifier, initialization_mode); }
generator.emit_set_variable(*identifier, initialization_mode);
return {}; return {};
} }
@ -1126,19 +1136,19 @@ static Bytecode::CodeGenerationErrorOr<void> generate_object_binding_pattern_byt
}; };
} }
auto& identifier = name.get<NonnullRefPtr<Identifier const>>()->string(); auto const& identifier = *name.get<NonnullRefPtr<Identifier const>>();
auto identifier_ref = generator.intern_identifier(identifier); auto identifier_ref = generator.intern_identifier(identifier.string());
if (create_variables) if (create_variables)
generator.emit<Bytecode::Op::CreateVariable>(identifier_ref, Bytecode::Op::EnvironmentMode::Lexical, false); generator.emit<Bytecode::Op::CreateVariable>(identifier_ref, Bytecode::Op::EnvironmentMode::Lexical, false);
generator.emit<Bytecode::Op::SetVariable>(identifier_ref, initialization_mode); generator.emit_set_variable(identifier, initialization_mode);
} else if (alias.has<NonnullRefPtr<MemberExpression const>>()) { } else if (alias.has<NonnullRefPtr<MemberExpression const>>()) {
TRY(generator.emit_store_to_reference(alias.get<NonnullRefPtr<MemberExpression const>>())); TRY(generator.emit_store_to_reference(alias.get<NonnullRefPtr<MemberExpression const>>()));
} else { } else {
auto& identifier = alias.get<NonnullRefPtr<Identifier const>>()->string(); auto const& identifier = *alias.get<NonnullRefPtr<Identifier const>>();
auto identifier_ref = generator.intern_identifier(identifier); auto identifier_ref = generator.intern_identifier(identifier.string());
if (create_variables) if (create_variables)
generator.emit<Bytecode::Op::CreateVariable>(identifier_ref, Bytecode::Op::EnvironmentMode::Lexical, false); generator.emit<Bytecode::Op::CreateVariable>(identifier_ref, Bytecode::Op::EnvironmentMode::Lexical, false);
generator.emit<Bytecode::Op::SetVariable>(identifier_ref, initialization_mode); generator.emit_set_variable(identifier, initialization_mode);
} }
} }
return {}; return {};
@ -1189,7 +1199,7 @@ static Bytecode::CodeGenerationErrorOr<void> generate_array_binding_pattern_byte
auto interned_index = generator.intern_identifier(identifier->string()); auto interned_index = generator.intern_identifier(identifier->string());
if (create_variables) if (create_variables)
generator.emit<Bytecode::Op::CreateVariable>(interned_index, Bytecode::Op::EnvironmentMode::Lexical, false); generator.emit<Bytecode::Op::CreateVariable>(interned_index, Bytecode::Op::EnvironmentMode::Lexical, false);
generator.emit<Bytecode::Op::SetVariable>(interned_index, initialization_mode); generator.emit_set_variable(*identifier, initialization_mode);
return {}; return {};
}, },
[&](NonnullRefPtr<BindingPattern const> const& pattern) -> Bytecode::CodeGenerationErrorOr<void> { [&](NonnullRefPtr<BindingPattern const> const& pattern) -> Bytecode::CodeGenerationErrorOr<void> {
@ -1355,7 +1365,7 @@ static Bytecode::CodeGenerationErrorOr<void> assign_accumulator_to_variable_decl
return declarator.target().visit( return declarator.target().visit(
[&](NonnullRefPtr<Identifier const> const& id) -> Bytecode::CodeGenerationErrorOr<void> { [&](NonnullRefPtr<Identifier const> const& id) -> Bytecode::CodeGenerationErrorOr<void> {
generator.emit<Bytecode::Op::SetVariable>(generator.intern_identifier(id->string()), initialization_mode); generator.emit_set_variable(*id, initialization_mode);
return {}; return {};
}, },
[&](NonnullRefPtr<BindingPattern const> const& pattern) -> Bytecode::CodeGenerationErrorOr<void> { [&](NonnullRefPtr<BindingPattern const> const& pattern) -> Bytecode::CodeGenerationErrorOr<void> {
@ -2309,7 +2319,7 @@ Bytecode::CodeGenerationErrorOr<void> ClassDeclaration::generate_bytecode(Byteco
generator.emit<Bytecode::Op::Store>(accumulator_backup_reg); generator.emit<Bytecode::Op::Store>(accumulator_backup_reg);
TRY(m_class_expression->generate_bytecode(generator)); TRY(m_class_expression->generate_bytecode(generator));
generator.emit<Bytecode::Op::SetVariable>(generator.intern_identifier(m_class_expression.ptr()->name()), Bytecode::Op::SetVariable::InitializationMode::Initialize); generator.emit_set_variable(*m_class_expression.ptr()->m_name, Bytecode::Op::SetVariable::InitializationMode::Initialize);
generator.emit<Bytecode::Op::Load>(accumulator_backup_reg); generator.emit<Bytecode::Op::Load>(accumulator_backup_reg);
return {}; return {};
@ -2464,9 +2474,10 @@ static Bytecode::CodeGenerationErrorOr<ForInOfHeadEvaluationResult> for_in_of_he
auto& variable = variable_declaration.declarations().first(); auto& variable = variable_declaration.declarations().first();
if (variable->init()) { if (variable->init()) {
VERIFY(variable->target().has<NonnullRefPtr<Identifier const>>()); VERIFY(variable->target().has<NonnullRefPtr<Identifier const>>());
auto binding_id = generator.intern_identifier(variable->target().get<NonnullRefPtr<Identifier const>>()->string()); auto identifier = variable->target().get<NonnullRefPtr<Identifier const>>();
TRY(generator.emit_named_evaluation_if_anonymous_function(*variable->init(), binding_id)); auto identifier_table_ref = generator.intern_identifier(identifier->string());
generator.emit<Bytecode::Op::SetVariable>(binding_id); TRY(generator.emit_named_evaluation_if_anonymous_function(*variable->init(), identifier_table_ref));
generator.emit_set_variable(*identifier);
} }
} else { } else {
// 1. Let oldEnv be the running execution context's LexicalEnvironment. // 1. Let oldEnv be the running execution context's LexicalEnvironment.
@ -2652,11 +2663,10 @@ static Bytecode::CodeGenerationErrorOr<void> for_in_of_body_evaluation(Bytecode:
if (!destructuring) { if (!destructuring) {
// 1. Assert: lhs binds a single name. // 1. Assert: lhs binds a single name.
// 2. Let lhsName be the sole element of BoundNames of lhs. // 2. Let lhsName be the sole element of BoundNames of lhs.
auto lhs_name = variable_declaration.declarations().first()->target().get<NonnullRefPtr<Identifier const>>()->string(); auto lhs_name = variable_declaration.declarations().first()->target().get<NonnullRefPtr<Identifier const>>();
// 3. Let lhsRef be ! ResolveBinding(lhsName). // 3. Let lhsRef be ! ResolveBinding(lhsName).
// NOTE: We're skipping all the completion stuff that the spec does, as the unwinding mechanism will take case of doing that. // NOTE: We're skipping all the completion stuff that the spec does, as the unwinding mechanism will take case of doing that.
auto identifier = generator.intern_identifier(lhs_name); generator.emit_set_variable(*lhs_name, Bytecode::Op::SetVariable::InitializationMode::Initialize, Bytecode::Op::EnvironmentMode::Lexical);
generator.emit<Bytecode::Op::SetVariable>(identifier, Bytecode::Op::SetVariable::InitializationMode::Initialize, Bytecode::Op::EnvironmentMode::Lexical);
} }
} }
// i. If destructuring is false, then // i. If destructuring is false, then

View file

@ -141,7 +141,7 @@ CodeGenerationErrorOr<void> Generator::emit_load_from_reference(JS::ASTNode cons
{ {
if (is<Identifier>(node)) { if (is<Identifier>(node)) {
auto& identifier = static_cast<Identifier const&>(node); auto& identifier = static_cast<Identifier const&>(node);
emit<Bytecode::Op::GetVariable>(intern_identifier(identifier.string())); TRY(identifier.generate_bytecode(*this));
return {}; return {};
} }
if (is<MemberExpression>(node)) { if (is<MemberExpression>(node)) {
@ -217,7 +217,7 @@ CodeGenerationErrorOr<void> Generator::emit_store_to_reference(JS::ASTNode const
{ {
if (is<Identifier>(node)) { if (is<Identifier>(node)) {
auto& identifier = static_cast<Identifier const&>(node); auto& identifier = static_cast<Identifier const&>(node);
emit<Bytecode::Op::SetVariable>(intern_identifier(identifier.string())); emit_set_variable(identifier);
return {}; return {};
} }
if (is<MemberExpression>(node)) { if (is<MemberExpression>(node)) {
@ -306,6 +306,15 @@ CodeGenerationErrorOr<void> Generator::emit_delete_reference(JS::ASTNode const&
return {}; return {};
} }
void Generator::emit_set_variable(JS::Identifier const& identifier, Bytecode::Op::SetVariable::InitializationMode initialization_mode, Bytecode::Op::EnvironmentMode mode)
{
if (identifier.is_local()) {
emit<Bytecode::Op::SetLocal>(identifier.local_variable_index());
} else {
emit<Bytecode::Op::SetVariable>(intern_identifier(identifier.string()), initialization_mode, mode);
}
}
void Generator::generate_break() void Generator::generate_break()
{ {
bool last_was_finally = false; bool last_was_finally = false;

View file

@ -83,6 +83,8 @@ public:
CodeGenerationErrorOr<void> emit_store_to_reference(JS::ASTNode const&); CodeGenerationErrorOr<void> emit_store_to_reference(JS::ASTNode const&);
CodeGenerationErrorOr<void> emit_delete_reference(JS::ASTNode const&); CodeGenerationErrorOr<void> emit_delete_reference(JS::ASTNode const&);
void emit_set_variable(JS::Identifier const& identifier, Bytecode::Op::SetVariable::InitializationMode initialization_mode = Bytecode::Op::SetVariable::InitializationMode::Set, Bytecode::Op::EnvironmentMode mode = Bytecode::Op::EnvironmentMode::Lexical);
void push_home_object(Register); void push_home_object(Register);
void pop_home_object(); void pop_home_object();
void emit_new_function(JS::FunctionExpression const&, Optional<IdentifierTableIndex> lhs_name); void emit_new_function(JS::FunctionExpression const&, Optional<IdentifierTableIndex> lhs_name);

View file

@ -41,6 +41,7 @@
O(GetObjectPropertyIterator) \ O(GetObjectPropertyIterator) \
O(GetPrivateById) \ O(GetPrivateById) \
O(GetVariable) \ O(GetVariable) \
O(GetLocal) \
O(GreaterThan) \ O(GreaterThan) \
O(GreaterThanEquals) \ O(GreaterThanEquals) \
O(HasPrivateId) \ O(HasPrivateId) \
@ -87,6 +88,7 @@
O(RightShift) \ O(RightShift) \
O(ScheduleJump) \ O(ScheduleJump) \
O(SetVariable) \ O(SetVariable) \
O(SetLocal) \
O(Store) \ O(Store) \
O(StrictlyEquals) \ O(StrictlyEquals) \
O(StrictlyInequals) \ O(StrictlyInequals) \
@ -98,6 +100,7 @@
O(ToNumeric) \ O(ToNumeric) \
O(Typeof) \ O(Typeof) \
O(TypeofVariable) \ O(TypeofVariable) \
O(TypeofLocal) \
O(UnaryMinus) \ O(UnaryMinus) \
O(UnaryPlus) \ O(UnaryPlus) \
O(UnsignedRightShift) \ O(UnsignedRightShift) \

View file

@ -422,6 +422,17 @@ ThrowCompletionOr<void> GetVariable::execute_impl(Bytecode::Interpreter& interpr
return {}; return {};
} }
ThrowCompletionOr<void> GetLocal::execute_impl(Bytecode::Interpreter& interpreter) const
{
auto& vm = interpreter.vm();
if (vm.running_execution_context().local_variables[m_index].is_empty()) {
auto const& variable_name = vm.running_execution_context().function->local_variables_names()[m_index];
return interpreter.vm().throw_completion<ReferenceError>(ErrorType::BindingNotInitialized, variable_name);
}
interpreter.accumulator() = vm.running_execution_context().local_variables[m_index];
return {};
}
ThrowCompletionOr<void> DeleteVariable::execute_impl(Bytecode::Interpreter& interpreter) const ThrowCompletionOr<void> DeleteVariable::execute_impl(Bytecode::Interpreter& interpreter) const
{ {
auto& vm = interpreter.vm(); auto& vm = interpreter.vm();
@ -506,6 +517,12 @@ ThrowCompletionOr<void> SetVariable::execute_impl(Bytecode::Interpreter& interpr
return {}; return {};
} }
ThrowCompletionOr<void> SetLocal::execute_impl(Bytecode::Interpreter& interpreter) const
{
interpreter.vm().running_execution_context().local_variables[m_index] = interpreter.accumulator();
return {};
}
ThrowCompletionOr<void> GetById::execute_impl(Bytecode::Interpreter& interpreter) const ThrowCompletionOr<void> GetById::execute_impl(Bytecode::Interpreter& interpreter) const
{ {
auto& vm = interpreter.vm(); auto& vm = interpreter.vm();
@ -1240,6 +1257,14 @@ ThrowCompletionOr<void> TypeofVariable::execute_impl(Bytecode::Interpreter& inte
return {}; return {};
} }
ThrowCompletionOr<void> TypeofLocal::execute_impl(Bytecode::Interpreter& interpreter) const
{
auto& vm = interpreter.vm();
auto const& value = vm.running_execution_context().local_variables[m_index];
interpreter.accumulator() = MUST_OR_THROW_OOM(PrimitiveString::create(vm, value.typeof()));
return {};
}
ThrowCompletionOr<void> ToNumeric::execute_impl(Bytecode::Interpreter& interpreter) const ThrowCompletionOr<void> ToNumeric::execute_impl(Bytecode::Interpreter& interpreter) const
{ {
interpreter.accumulator() = TRY(interpreter.accumulator().to_numeric(interpreter.vm())); interpreter.accumulator() = TRY(interpreter.accumulator().to_numeric(interpreter.vm()));
@ -1335,6 +1360,11 @@ DeprecatedString GetVariable::to_deprecated_string_impl(Bytecode::Executable con
return DeprecatedString::formatted("GetVariable {} ({})", m_identifier, executable.identifier_table->get(m_identifier)); return DeprecatedString::formatted("GetVariable {} ({})", m_identifier, executable.identifier_table->get(m_identifier));
} }
DeprecatedString GetLocal::to_deprecated_string_impl(Bytecode::Executable const&) const
{
return DeprecatedString::formatted("GetLocal {}", m_index);
}
DeprecatedString DeleteVariable::to_deprecated_string_impl(Bytecode::Executable const& executable) const DeprecatedString DeleteVariable::to_deprecated_string_impl(Bytecode::Executable const& executable) const
{ {
return DeprecatedString::formatted("DeleteVariable {} ({})", m_identifier, executable.identifier_table->get(m_identifier)); return DeprecatedString::formatted("DeleteVariable {} ({})", m_identifier, executable.identifier_table->get(m_identifier));
@ -1365,6 +1395,11 @@ DeprecatedString SetVariable::to_deprecated_string_impl(Bytecode::Executable con
return DeprecatedString::formatted("SetVariable env:{} init:{} {} ({})", mode_string, initialization_mode_name, m_identifier, executable.identifier_table->get(m_identifier)); return DeprecatedString::formatted("SetVariable env:{} init:{} {} ({})", mode_string, initialization_mode_name, m_identifier, executable.identifier_table->get(m_identifier));
} }
DeprecatedString SetLocal::to_deprecated_string_impl(Bytecode::Executable const&) const
{
return DeprecatedString::formatted("SetLocal {}", m_index);
}
DeprecatedString PutById::to_deprecated_string_impl(Bytecode::Executable const& executable) const DeprecatedString PutById::to_deprecated_string_impl(Bytecode::Executable const& executable) const
{ {
auto kind = m_kind == PropertyKind::Getter auto kind = m_kind == PropertyKind::Getter
@ -1652,6 +1687,11 @@ DeprecatedString TypeofVariable::to_deprecated_string_impl(Bytecode::Executable
return DeprecatedString::formatted("TypeofVariable {} ({})", m_identifier, executable.identifier_table->get(m_identifier)); return DeprecatedString::formatted("TypeofVariable {} ({})", m_identifier, executable.identifier_table->get(m_identifier));
} }
DeprecatedString TypeofLocal::to_deprecated_string_impl(Bytecode::Executable const&) const
{
return DeprecatedString::formatted("TypeofLocal {}", m_index);
}
DeprecatedString ToNumeric::to_deprecated_string_impl(Bytecode::Executable const&) const DeprecatedString ToNumeric::to_deprecated_string_impl(Bytecode::Executable const&) const
{ {
return "ToNumeric"sv; return "ToNumeric"sv;

View file

@ -484,6 +484,25 @@ private:
InitializationMode m_initialization_mode { InitializationMode::Set }; InitializationMode m_initialization_mode { InitializationMode::Set };
}; };
class SetLocal final : public Instruction {
public:
explicit SetLocal(size_t index)
: Instruction(Type::SetLocal)
, m_index(index)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const;
void replace_references_impl(BasicBlock const&, BasicBlock const&) { }
void replace_references_impl(Register, Register) { }
size_t index() const { return m_index; }
private:
size_t m_index;
};
class GetVariable final : public Instruction { class GetVariable final : public Instruction {
public: public:
explicit GetVariable(IdentifierTableIndex identifier) explicit GetVariable(IdentifierTableIndex identifier)
@ -505,6 +524,25 @@ private:
Optional<EnvironmentCoordinate> mutable m_cached_environment_coordinate; Optional<EnvironmentCoordinate> mutable m_cached_environment_coordinate;
}; };
class GetLocal final : public Instruction {
public:
explicit GetLocal(size_t index)
: Instruction(Type::GetLocal)
, m_index(index)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const;
void replace_references_impl(BasicBlock const&, BasicBlock const&) { }
void replace_references_impl(Register, Register) { }
size_t index() const { return m_index; }
private:
size_t m_index;
};
class DeleteVariable final : public Instruction { class DeleteVariable final : public Instruction {
public: public:
explicit DeleteVariable(IdentifierTableIndex identifier) explicit DeleteVariable(IdentifierTableIndex identifier)
@ -1338,6 +1376,23 @@ private:
IdentifierTableIndex m_identifier; IdentifierTableIndex m_identifier;
}; };
class TypeofLocal final : public Instruction {
public:
explicit TypeofLocal(size_t index)
: Instruction(Type::TypeofLocal)
, m_index(index)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const;
void replace_references_impl(BasicBlock const&, BasicBlock const&) { }
void replace_references_impl(Register, Register) { }
private:
size_t m_index;
};
} }
namespace JS::Bytecode { namespace JS::Bytecode {

View file

@ -184,6 +184,7 @@ class HeapBlock;
struct ImportEntry; struct ImportEntry;
class ImportStatement; class ImportStatement;
class Interpreter; class Interpreter;
class Identifier;
class Intrinsics; class Intrinsics;
struct IteratorRecord; struct IteratorRecord;
class MetaProperty; class MetaProperty;

View file

@ -503,10 +503,14 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
if (scope_body) { if (scope_body) {
// NOTE: Due to the use of MUST with `create_mutable_binding` and `initialize_binding` below, // NOTE: Due to the use of MUST with `create_mutable_binding` and `initialize_binding` below,
// an exception should not result from `for_each_var_declared_name`. // an exception should not result from `for_each_var_declared_name`.
MUST(scope_body->for_each_var_declared_name([&](auto const& name) { MUST(scope_body->for_each_var_declared_identifier([&](auto const& id) {
if (!parameter_names.contains(name) && instantiated_var_names.set(name) == AK::HashSetResult::InsertedNewEntry) { if (!parameter_names.contains(id.string()) && instantiated_var_names.set(id.string()) == AK::HashSetResult::InsertedNewEntry) {
MUST(environment->create_mutable_binding(vm, name, false)); if (vm.bytecode_interpreter_if_exists() && id.is_local()) {
MUST(environment->initialize_binding(vm, name, js_undefined(), Environment::InitializeBindingHint::Normal)); callee_context.local_variables[id.local_variable_index()] = js_undefined();
} else {
MUST(environment->create_mutable_binding(vm, id.string(), false));
MUST(environment->initialize_binding(vm, id.string(), js_undefined(), Environment::InitializeBindingHint::Normal));
}
} }
})); }));
} }
@ -518,18 +522,23 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
if (scope_body) { if (scope_body) {
// NOTE: Due to the use of MUST with `create_mutable_binding`, `get_binding_value` and `initialize_binding` below, // NOTE: Due to the use of MUST with `create_mutable_binding`, `get_binding_value` and `initialize_binding` below,
// an exception should not result from `for_each_var_declared_name`. // an exception should not result from `for_each_var_declared_name`.
MUST(scope_body->for_each_var_declared_name([&](auto const& name) { MUST(scope_body->for_each_var_declared_identifier([&](auto const& id) {
if (instantiated_var_names.set(name) != AK::HashSetResult::InsertedNewEntry) if (instantiated_var_names.set(id.string()) != AK::HashSetResult::InsertedNewEntry)
return; return;
MUST(var_environment->create_mutable_binding(vm, name, false)); MUST(var_environment->create_mutable_binding(vm, id.string(), false));
Value initial_value; Value initial_value;
if (!parameter_names.contains(name) || function_names.contains(name)) if (!parameter_names.contains(id.string()) || function_names.contains(id.string()))
initial_value = js_undefined(); initial_value = js_undefined();
else else
initial_value = MUST(environment->get_binding_value(vm, name, false)); initial_value = MUST(environment->get_binding_value(vm, id.string(), false));
MUST(var_environment->initialize_binding(vm, name, initial_value, Environment::InitializeBindingHint::Normal)); if (vm.bytecode_interpreter_if_exists() && id.is_local()) {
// NOTE: Local variables are supported only in bytecode interpreter
callee_context.local_variables[id.local_variable_index()] = initial_value;
} else {
MUST(var_environment->initialize_binding(vm, id.string(), initial_value, Environment::InitializeBindingHint::Normal));
}
})); }));
} }
} }
@ -586,11 +595,15 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
MUST(scope_body->for_each_lexically_scoped_declaration([&](Declaration const& declaration) { MUST(scope_body->for_each_lexically_scoped_declaration([&](Declaration const& declaration) {
// NOTE: Due to the use of MUST with `create_immutable_binding` and `create_mutable_binding` below, // 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`. // an exception should not result from `for_each_bound_name`.
MUST(declaration.for_each_bound_name([&](auto const& name) { MUST(declaration.for_each_bound_identifier([&](auto const& id) {
if (vm.bytecode_interpreter_if_exists() && id.is_local()) {
// NOTE: Local variables are supported only in bytecode interpreter
return;
}
if (declaration.is_constant_declaration()) if (declaration.is_constant_declaration())
MUST(lex_environment->create_immutable_binding(vm, name, true)); MUST(lex_environment->create_immutable_binding(vm, id.string(), true));
else else
MUST(lex_environment->create_mutable_binding(vm, name, false)); MUST(lex_environment->create_mutable_binding(vm, id.string(), false));
})); }));
})); }));