diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index 309decb3d9..d1cf0710bb 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -456,6 +456,7 @@ Value UpdateExpression::execute(Interpreter& interpreter) const break; case UpdateOp::Decrement: interpreter.set_variable(name, Value(previous_value.as_double() - 1)); + break; } return previous_value; @@ -520,19 +521,22 @@ Value VariableDeclaration::execute(Interpreter& interpreter) const void VariableDeclaration::dump(int indent) const { - const char* op_string = nullptr; + const char* declaration_type_string = nullptr; switch (m_declaration_type) { case DeclarationType::Let: - op_string = "Let"; + declaration_type_string = "Let"; break; case DeclarationType::Var: - op_string = "Var"; + declaration_type_string = "Var"; + break; + case DeclarationType::Const: + declaration_type_string = "Const"; break; } ASTNode::dump(indent); print_indent(indent + 1); - printf("%s\n", op_string); + printf("%s\n", declaration_type_string); m_name->dump(indent + 1); if (m_initializer) m_initializer->dump(indent + 1); diff --git a/Libraries/LibJS/AST.h b/Libraries/LibJS/AST.h index 5c28f44db3..9fd8d7baa6 100644 --- a/Libraries/LibJS/AST.h +++ b/Libraries/LibJS/AST.h @@ -470,6 +470,7 @@ private: enum class DeclarationType { Var, Let, + Const, }; class VariableDeclaration : public Statement { diff --git a/Libraries/LibJS/Interpreter.cpp b/Libraries/LibJS/Interpreter.cpp index 2f0b20d87b..8a569e0fe3 100644 --- a/Libraries/LibJS/Interpreter.cpp +++ b/Libraries/LibJS/Interpreter.cpp @@ -57,7 +57,12 @@ Value Interpreter::run(const ScopeNode& scope_node, HashMap scope void Interpreter::enter_scope(const ScopeNode& scope_node, HashMap scope_variables, ScopeType scope_type) { - m_scope_stack.append({ scope_type, scope_node, move(scope_variables) }); + HashMap scope_variables_with_declaration_type; + for (String name : scope_variables.keys()) { + scope_variables_with_declaration_type.set(name, { scope_variables.get(name).value(), DeclarationType::Var }); + } + + m_scope_stack.append({ scope_type, scope_node, move(scope_variables_with_declaration_type) }); } void Interpreter::exit_scope(const ScopeNode& scope_node) @@ -78,7 +83,10 @@ void Interpreter::declare_variable(String name, DeclarationType declaration_type for (ssize_t i = m_scope_stack.size() - 1; i >= 0; --i) { auto& scope = m_scope_stack.at(i); if (scope.type == ScopeType::Function) { - scope.variables.set(move(name), js_undefined()); + if (scope.variables.get(name).has_value() && scope.variables.get(name).value().declaration_type != DeclarationType::Var) + ASSERT_NOT_REACHED(); + + scope.variables.set(move(name), { js_undefined(), declaration_type }); return; } } @@ -86,7 +94,11 @@ void Interpreter::declare_variable(String name, DeclarationType declaration_type global_object().put(move(name), js_undefined()); break; case DeclarationType::Let: - m_scope_stack.last().variables.set(move(name), js_undefined()); + case DeclarationType::Const: + if (m_scope_stack.last().variables.get(name).has_value() && m_scope_stack.last().variables.get(name).value().declaration_type != DeclarationType::Var) + ASSERT_NOT_REACHED(); + + m_scope_stack.last().variables.set(move(name), { js_undefined(), declaration_type }); break; } } @@ -95,8 +107,13 @@ void Interpreter::set_variable(String name, Value value) { for (ssize_t i = m_scope_stack.size() - 1; i >= 0; --i) { auto& scope = m_scope_stack.at(i); - if (scope.variables.contains(name)) { - scope.variables.set(move(name), move(value)); + + auto possible_match = scope.variables.get(name); + if (possible_match.has_value()) { + if (possible_match.value().declaration_type == DeclarationType::Const) + ASSERT_NOT_REACHED(); + + scope.variables.set(move(name), { move(value), possible_match.value().declaration_type }); return; } } @@ -110,7 +127,7 @@ Value Interpreter::get_variable(const String& name) auto& scope = m_scope_stack.at(i); auto value = scope.variables.get(name); if (value.has_value()) - return value.value(); + return value.value().value; } return global_object().get(name); @@ -122,8 +139,8 @@ void Interpreter::collect_roots(Badge, HashTable& roots) for (auto& scope : m_scope_stack) { for (auto& it : scope.variables) { - if (it.value.is_cell()) - roots.set(it.value.as_cell()); + if (it.value.value.is_cell()) + roots.set(it.value.value.as_cell()); } } } diff --git a/Libraries/LibJS/Interpreter.h b/Libraries/LibJS/Interpreter.h index 6e590f3a3d..39fd4166af 100644 --- a/Libraries/LibJS/Interpreter.h +++ b/Libraries/LibJS/Interpreter.h @@ -30,6 +30,7 @@ #include #include #include +#include namespace JS { @@ -38,10 +39,15 @@ enum class ScopeType { Block, }; +struct Variable { + Value value; + DeclarationType declaration_type; +}; + struct ScopeFrame { ScopeType type; const ScopeNode& scope_node; - HashMap variables; + HashMap variables; }; class Interpreter { diff --git a/Libraries/LibJS/Parser.cpp b/Libraries/LibJS/Parser.cpp index 3128e44c8d..fc8fb39d74 100644 --- a/Libraries/LibJS/Parser.cpp +++ b/Libraries/LibJS/Parser.cpp @@ -66,6 +66,7 @@ NonnullOwnPtr Parser::parse_statement() return parse_return_statement(); case TokenType::Var: case TokenType::Let: + case TokenType::Const: return parse_variable_declaration(); case TokenType::For: return parse_for_statement(); @@ -261,6 +262,10 @@ NonnullOwnPtr Parser::parse_variable_declaration() declaration_type = DeclarationType::Let; consume(TokenType::Let); break; + case TokenType::Const: + declaration_type = DeclarationType::Const; + consume(TokenType::Const); + break; default: ASSERT_NOT_REACHED(); }