1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 17:57:35 +00:00

LibJS: More properly implement scoping rules in bytecode codegen

Now we emit CreateVariable and SetVariable with the appropriate
initialization/environment modes, much closer to the spec.
This makes a whole lot of things like let/const variables, function
and variable hoisting and some other things work :^)
This commit is contained in:
Ali Mohammad Pur 2022-02-12 19:48:45 +03:30 committed by Linus Groh
parent c7e6b65fd2
commit 1bbfaf8627
12 changed files with 503 additions and 38 deletions

View file

@ -260,12 +260,63 @@ ThrowCompletionOr<void> GetVariable::execute_impl(Bytecode::Interpreter& interpr
return {};
}
ThrowCompletionOr<void> CreateEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const
{
auto make_and_swap_envs = [&](auto*& old_environment) {
Environment* environment = new_declarative_environment(*old_environment);
swap(old_environment, environment);
return environment;
};
if (m_mode == EnvironmentMode::Lexical)
interpreter.saved_lexical_environment_stack().append(make_and_swap_envs(interpreter.vm().running_execution_context().lexical_environment));
else if (m_mode == EnvironmentMode::Var)
interpreter.saved_variable_environment_stack().append(make_and_swap_envs(interpreter.vm().running_execution_context().variable_environment));
return {};
}
ThrowCompletionOr<void> CreateVariable::execute_impl(Bytecode::Interpreter& interpreter) const
{
auto& vm = interpreter.vm();
auto const& name = interpreter.current_executable().get_identifier(m_identifier);
if (m_mode == EnvironmentMode::Lexical) {
// Note: This is papering over an issue where "FunctionDeclarationInstantiation" creates these bindings for us.
// Instead of crashing in there, we'll just raise an exception here.
if (TRY(vm.lexical_environment()->has_binding(name)))
return vm.throw_completion<InternalError>(interpreter.global_object(), String::formatted("Lexical environment already has binding '{}'", name));
if (m_is_immutable)
vm.lexical_environment()->create_immutable_binding(interpreter.global_object(), name, vm.in_strict_mode());
else
vm.lexical_environment()->create_mutable_binding(interpreter.global_object(), name, vm.in_strict_mode());
} else {
if (m_is_immutable)
vm.variable_environment()->create_immutable_binding(interpreter.global_object(), name, vm.in_strict_mode());
else
vm.variable_environment()->create_mutable_binding(interpreter.global_object(), name, vm.in_strict_mode());
}
return {};
}
ThrowCompletionOr<void> SetVariable::execute_impl(Bytecode::Interpreter& interpreter) const
{
auto& vm = interpreter.vm();
auto reference = TRY(vm.resolve_binding(interpreter.current_executable().get_identifier(m_identifier)));
TRY(reference.put_value(interpreter.global_object(), interpreter.accumulator()));
auto const& name = interpreter.current_executable().get_identifier(m_identifier);
auto environment = m_mode == EnvironmentMode::Lexical ? vm.running_execution_context().lexical_environment : vm.running_execution_context().variable_environment;
auto reference = TRY(vm.resolve_binding(name, environment));
switch (m_initialization_mode) {
case InitializationMode::Initialize:
TRY(reference.initialize_referenced_binding(interpreter.global_object(), interpreter.accumulator()));
break;
case InitializationMode::Set:
TRY(reference.put_value(interpreter.global_object(), interpreter.accumulator()));
break;
case InitializationMode::InitializeOrSet:
VERIFY(reference.is_environment_reference());
VERIFY(reference.base_environment().is_declarative_environment());
TRY(static_cast<DeclarativeEnvironment&>(reference.base_environment()).initialize_or_set_mutable_binding(interpreter.global_object(), name, interpreter.accumulator()));
break;
}
return {};
}
@ -440,6 +491,15 @@ void FinishUnwind::replace_references_impl(BasicBlock const& from, BasicBlock co
m_next_target = Label { to };
}
ThrowCompletionOr<void> LeaveEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const
{
if (m_mode == EnvironmentMode::Lexical)
interpreter.vm().running_execution_context().lexical_environment = interpreter.saved_lexical_environment_stack().take_last();
if (m_mode == EnvironmentMode::Var)
interpreter.vm().running_execution_context().variable_environment = interpreter.saved_variable_environment_stack().take_last();
return {};
}
ThrowCompletionOr<void> LeaveUnwindContext::execute_impl(Bytecode::Interpreter& interpreter) const
{
interpreter.leave_unwind_context();
@ -628,9 +688,27 @@ String GetVariable::to_string_impl(Bytecode::Executable const& executable) const
return String::formatted("GetVariable {} ({})", m_identifier, executable.identifier_table->get(m_identifier));
}
String CreateEnvironment::to_string_impl(Bytecode::Executable const&) const
{
auto mode_string = m_mode == EnvironmentMode::Lexical
? "Lexical"
: "Variable";
return String::formatted("CreateEnvironment mode:{}", mode_string);
}
String CreateVariable::to_string_impl(Bytecode::Executable const& executable) const
{
auto mode_string = m_mode == EnvironmentMode::Lexical ? "Lexical" : "Variable";
return String::formatted("CreateVariable env:{} immutable:{} {} ({})", mode_string, m_is_immutable, m_identifier, executable.identifier_table->get(m_identifier));
}
String SetVariable::to_string_impl(Bytecode::Executable const& executable) const
{
return String::formatted("SetVariable {} ({})", m_identifier, executable.identifier_table->get(m_identifier));
auto initialization_mode_name = m_initialization_mode == InitializationMode ::Initialize ? "Initialize"
: m_initialization_mode == InitializationMode::Set ? "Set"
: "InitializeOrSet";
auto mode_string = m_mode == EnvironmentMode::Lexical ? "Lexical" : "Variable";
return String::formatted("SetVariable env:{} init:{} {} ({})", mode_string, initialization_mode_name, m_identifier, executable.identifier_table->get(m_identifier));
}
String PutById::to_string_impl(Bytecode::Executable const& executable) const
@ -729,6 +807,14 @@ String FinishUnwind::to_string_impl(const Bytecode::Executable&) const
return String::formatted("FinishUnwind next:{}", m_next_target);
}
String LeaveEnvironment::to_string_impl(Bytecode::Executable const&) const
{
auto mode_string = m_mode == EnvironmentMode::Lexical
? "Lexical"
: "Variable";
return String::formatted("LeaveEnvironment env:{}", mode_string);
}
String LeaveUnwindContext::to_string_impl(Bytecode::Executable const&) const
{
return "LeaveUnwindContext";