diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index 0d1752b493d..87c763a49cf 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -1158,6 +1158,8 @@ void VariableDeclaration::dump(int indent) const { char const* declaration_kind_string = nullptr; switch (m_declaration_kind) { + case DeclarationKind::None: + VERIFY_NOT_REACHED(); case DeclarationKind::Let: declaration_kind_string = "Let"; break; diff --git a/Libraries/LibJS/AST.h b/Libraries/LibJS/AST.h index cf64ebc26d8..c58527efec8 100644 --- a/Libraries/LibJS/AST.h +++ b/Libraries/LibJS/AST.h @@ -159,6 +159,13 @@ private: u32 m_tail_size { 0 }; }; +enum class DeclarationKind { + None, + Var, + Let, + Const, +}; + class Statement : public ASTNode { public: explicit Statement(SourceRange source_range) @@ -702,6 +709,9 @@ public: bool is_global() const { return m_is_global; } void set_is_global() { m_is_global = true; } + [[nodiscard]] DeclarationKind declaration_kind() const { return m_declaration_kind; } + void set_declaration_kind(DeclarationKind kind) { m_declaration_kind = kind; } + virtual void dump(int indent) const override; virtual Bytecode::CodeGenerationErrorOr> generate_bytecode(Bytecode::Generator&, Optional preferred_dst = {}) const override; @@ -712,6 +722,7 @@ private: Optional m_local_index; bool m_is_global { false }; + DeclarationKind m_declaration_kind { DeclarationKind::None }; }; struct FunctionParameter { @@ -1764,12 +1775,6 @@ private: bool m_prefixed; }; -enum class DeclarationKind { - Var, - Let, - Const, -}; - class VariableDeclarator final : public ASTNode { public: VariableDeclarator(SourceRange source_range, NonnullRefPtr id) diff --git a/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 196c7b2d826..13732eef188 100644 --- a/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -482,7 +482,11 @@ Bytecode::CodeGenerationErrorOr> Identifier::generate_by if (is_global()) { generator.emit(dst, generator.intern_identifier(m_string), generator.next_global_variable_cache()); } else { - generator.emit(dst, generator.intern_identifier(m_string)); + if (declaration_kind() == DeclarationKind::Var) { + generator.emit(dst, generator.intern_identifier(m_string)); + } else { + generator.emit(dst, generator.intern_identifier(m_string)); + } } return dst; } diff --git a/Libraries/LibJS/Bytecode/Instruction.h b/Libraries/LibJS/Bytecode/Instruction.h index 8043c5b3bdc..045b79d57f6 100644 --- a/Libraries/LibJS/Bytecode/Instruction.h +++ b/Libraries/LibJS/Bytecode/Instruction.h @@ -69,6 +69,7 @@ O(GetObjectPropertyIterator) \ O(GetPrivateById) \ O(GetBinding) \ + O(GetInitializedBinding) \ O(GreaterThan) \ O(GreaterThanEquals) \ O(HasPrivateId) \ diff --git a/Libraries/LibJS/Bytecode/Interpreter.cpp b/Libraries/LibJS/Bytecode/Interpreter.cpp index 8ad691e8d34..17a13736e6c 100644 --- a/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -631,6 +631,7 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point) HANDLE_INSTRUCTION(GetObjectPropertyIterator); HANDLE_INSTRUCTION(GetPrivateById); HANDLE_INSTRUCTION(GetBinding); + HANDLE_INSTRUCTION(GetInitializedBinding); HANDLE_INSTRUCTION(GreaterThan); HANDLE_INSTRUCTION(GreaterThanEquals); HANDLE_INSTRUCTION(HasPrivateId); @@ -2239,29 +2240,51 @@ ThrowCompletionOr ConcatString::execute_impl(Bytecode::Interpreter& interp return {}; } -ThrowCompletionOr GetBinding::execute_impl(Bytecode::Interpreter& interpreter) const +enum class BindingIsKnownToBeInitialized { + No, + Yes, +}; + +template +static ThrowCompletionOr get_binding(Interpreter& interpreter, Operand dst, IdentifierTableIndex identifier, EnvironmentCoordinate& cache) { auto& vm = interpreter.vm(); auto& executable = interpreter.current_executable(); - if (m_cache.is_valid()) { + if (cache.is_valid()) { auto const* environment = interpreter.running_execution_context().lexical_environment.ptr(); - for (size_t i = 0; i < m_cache.hops; ++i) + for (size_t i = 0; i < cache.hops; ++i) environment = environment->outer_environment(); if (!environment->is_permanently_screwed_by_eval()) { - interpreter.set(dst(), TRY(static_cast(*environment).get_binding_value_direct(vm, m_cache.index))); + Value value; + if constexpr (binding_is_known_to_be_initialized == BindingIsKnownToBeInitialized::No) { + value = TRY(static_cast(*environment).get_binding_value_direct(vm, cache.index)); + } else { + value = static_cast(*environment).get_initialized_binding_value_direct(cache.index); + } + interpreter.set(dst, value); return {}; } - m_cache = {}; + cache = {}; } - auto reference = TRY(vm.resolve_binding(executable.get_identifier(m_identifier))); + auto reference = TRY(vm.resolve_binding(executable.get_identifier(identifier))); if (reference.environment_coordinate().has_value()) - m_cache = reference.environment_coordinate().value(); - interpreter.set(dst(), TRY(reference.get_value(vm))); + cache = reference.environment_coordinate().value(); + interpreter.set(dst, TRY(reference.get_value(vm))); return {}; } +ThrowCompletionOr GetBinding::execute_impl(Bytecode::Interpreter& interpreter) const +{ + return get_binding(interpreter, m_dst, m_identifier, m_cache); +} + +ThrowCompletionOr GetInitializedBinding::execute_impl(Bytecode::Interpreter& interpreter) const +{ + return get_binding(interpreter, m_dst, m_identifier, m_cache); +} + ThrowCompletionOr GetCalleeAndThisFromEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const { auto callee_and_this = TRY(get_callee_and_this_from_environment( @@ -3285,6 +3308,13 @@ ByteString GetBinding::to_byte_string_impl(Bytecode::Executable const& executabl executable.identifier_table->get(m_identifier)); } +ByteString GetInitializedBinding::to_byte_string_impl(Bytecode::Executable const& executable) const +{ + return ByteString::formatted("GetInitializedBinding {}, {}", + format_operand("dst"sv, dst(), executable), + executable.identifier_table->get(m_identifier)); +} + ByteString GetGlobal::to_byte_string_impl(Bytecode::Executable const& executable) const { return ByteString::formatted("GetGlobal {}, {}", format_operand("dst"sv, dst(), executable), diff --git a/Libraries/LibJS/Bytecode/Op.h b/Libraries/LibJS/Bytecode/Op.h index 2c1ceccb73b..a1f2ea47cb4 100644 --- a/Libraries/LibJS/Bytecode/Op.h +++ b/Libraries/LibJS/Bytecode/Op.h @@ -833,6 +833,32 @@ private: mutable EnvironmentCoordinate m_cache; }; +class GetInitializedBinding final : public Instruction { +public: + explicit GetInitializedBinding(Operand dst, IdentifierTableIndex identifier) + : Instruction(Type::GetInitializedBinding) + , m_dst(dst) + , m_identifier(identifier) + { + } + + ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; + ByteString to_byte_string_impl(Bytecode::Executable const&) const; + + Operand dst() const { return m_dst; } + IdentifierTableIndex identifier() const { return m_identifier; } + + void visit_operands_impl(Function visitor) + { + visitor(m_dst); + } + +private: + Operand m_dst; + IdentifierTableIndex m_identifier; + mutable EnvironmentCoordinate m_cache; +}; + class GetGlobal final : public Instruction { public: GetGlobal(Operand dst, IdentifierTableIndex identifier, u32 cache_index) diff --git a/Libraries/LibJS/Parser.cpp b/Libraries/LibJS/Parser.cpp index b6393bb6313..7019a0d3577 100644 --- a/Libraries/LibJS/Parser.cpp +++ b/Libraries/LibJS/Parser.cpp @@ -292,6 +292,12 @@ public: auto const& identifier_group_name = it.key; auto& identifier_group = it.value; + if (identifier_group.declaration_kind.has_value()) { + for (auto& identifier : identifier_group.identifiers) { + identifier->set_declaration_kind(identifier_group.declaration_kind.value()); + } + } + bool scope_has_declaration = false; if (is_top_level() && m_var_names.contains(identifier_group_name)) scope_has_declaration = true; diff --git a/Libraries/LibJS/Runtime/DeclarativeEnvironment.h b/Libraries/LibJS/Runtime/DeclarativeEnvironment.h index 722811b14db..639b78fb5e0 100644 --- a/Libraries/LibJS/Runtime/DeclarativeEnvironment.h +++ b/Libraries/LibJS/Runtime/DeclarativeEnvironment.h @@ -59,6 +59,7 @@ public: ThrowCompletionOr initialize_binding_direct(VM&, size_t index, Value, InitializeBindingHint); ThrowCompletionOr set_mutable_binding_direct(VM&, size_t index, Value, bool strict); ThrowCompletionOr get_binding_value_direct(VM&, size_t index) const; + Value get_initialized_binding_value_direct(size_t index) const { return m_bindings[index].value; } void shrink_to_fit();