1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-03 04:52:13 +00:00

LibJS: Hoist variable declarations to the nearest relevant scope

"var" declarations are hoisted to the nearest function scope, while
"let" and "const" are hoisted to the nearest block scope.

This is done by the parser, which keeps two scope stacks, one stack
for the current var scope and one for the current let/const scope.

When the interpreter enters a scope, we walk all of the declarations
and insert them into the variable environment.

We don't support the temporal dead zone for let/const yet.
This commit is contained in:
Andreas Kling 2020-04-13 16:42:54 +02:00
parent b9415dc0e9
commit ac7459cb40
7 changed files with 158 additions and 21 deletions

View file

@ -92,9 +92,20 @@ Value Interpreter::run(const Statement& statement, ArgumentVector arguments, Sco
void Interpreter::enter_scope(const ScopeNode& scope_node, ArgumentVector arguments, ScopeType scope_type)
{
HashMap<FlyString, Variable> scope_variables_with_declaration_kind;
for (auto& declaration : scope_node.variables()) {
for (auto& declarator : declaration.declarations()) {
if (scope_node.is_program())
global_object().put(declarator.id().string(), js_undefined());
else
scope_variables_with_declaration_kind.set(declarator.id().string(), { js_undefined(), declaration.declaration_kind() });
}
}
for (auto& argument : arguments) {
scope_variables_with_declaration_kind.set(argument.name, { argument.value, DeclarationKind::Var });
}
m_scope_stack.append({ scope_type, scope_node, move(scope_variables_with_declaration_kind) });
}
@ -118,9 +129,6 @@ void Interpreter::declare_variable(const FlyString& name, DeclarationKind declar
for (ssize_t i = m_scope_stack.size() - 1; i >= 0; --i) {
auto& scope = m_scope_stack.at(i);
if (scope.type == ScopeType::Function) {
if (scope.variables.get(name).has_value() && scope.variables.get(name).value().declaration_kind != DeclarationKind::Var)
ASSERT_NOT_REACHED();
scope.variables.set(move(name), { js_undefined(), declaration_kind });
return;
}
@ -130,9 +138,6 @@ void Interpreter::declare_variable(const FlyString& name, DeclarationKind declar
break;
case DeclarationKind::Let:
case DeclarationKind::Const:
if (m_scope_stack.last().variables.get(name).has_value())
ASSERT_NOT_REACHED();
m_scope_stack.last().variables.set(move(name), { js_undefined(), declaration_kind });
break;
}