diff --git a/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 44dce2118de..0b410d75f53 100644 --- a/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -3183,11 +3183,23 @@ static Bytecode::CodeGenerationErrorOr> for_in_of_body_e generator.begin_continuable_scope(Bytecode::Label { loop_update }, label_set); // a. Let nextResult be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]]). - auto next_result = generator.allocate_register(); - generator.emit(next_result, *head_result.iterator); + auto next_value = generator.allocate_register(); + auto done = generator.allocate_register(); - // b. If iteratorKind is async, set nextResult to ? Await(nextResult). - if (iterator_kind == IteratorHint::Async) { + if (iterator_kind == IteratorHint::Sync) { + generator.emit(next_value, done, *head_result.iterator); + + auto& loop_continue = generator.make_block(); + generator.emit_jump_if( + done, + Bytecode::Label { loop_end }, + Bytecode::Label { loop_continue }); + generator.switch_to_basic_block(loop_continue); + } else { + auto next_result = generator.allocate_register(); + generator.emit(next_result, *head_result.iterator); + + // b. If iteratorKind is async, set nextResult to ? Await(nextResult). auto received_completion = generator.allocate_register(); auto received_completion_type = generator.allocate_register(); auto received_completion_value = generator.allocate_register(); @@ -3195,27 +3207,25 @@ static Bytecode::CodeGenerationErrorOr> for_in_of_body_e generator.emit_mov(received_completion, generator.accumulator()); auto new_result = generate_await(generator, next_result, received_completion, received_completion_type, received_completion_value); generator.emit_mov(next_result, new_result); + + // c. If Type(nextResult) is not Object, throw a TypeError exception. + generator.emit(next_result); + + // d. Let done be ? IteratorComplete(nextResult). + generator.emit_iterator_complete(done, next_result); + + // e. If done is true, return V. + auto& loop_continue = generator.make_block(); + generator.emit_jump_if( + done, + Bytecode::Label { loop_end }, + Bytecode::Label { loop_continue }); + generator.switch_to_basic_block(loop_continue); + + // f. Let nextValue be ? IteratorValue(nextResult). + generator.emit_iterator_value(next_value, next_result); } - // c. If Type(nextResult) is not Object, throw a TypeError exception. - generator.emit(next_result); - - // d. Let done be ? IteratorComplete(nextResult). - auto done = generator.allocate_register(); - generator.emit_iterator_complete(done, next_result); - - // e. If done is true, return V. - auto& loop_continue = generator.make_block(); - generator.emit_jump_if( - done, - Bytecode::Label { loop_end }, - Bytecode::Label { loop_continue }); - generator.switch_to_basic_block(loop_continue); - - // f. Let nextValue be ? IteratorValue(nextResult). - auto next_value = generator.allocate_register(); - generator.emit_iterator_value(next_value, next_result); - // g. If lhsKind is either assignment or varBinding, then if (head_result.lhs_kind != LHSKind::LexicalBinding) { // i. If destructuring is false, then diff --git a/Libraries/LibJS/Bytecode/Instruction.h b/Libraries/LibJS/Bytecode/Instruction.h index 0b81a8fd93d..41234ef18a3 100644 --- a/Libraries/LibJS/Bytecode/Instruction.h +++ b/Libraries/LibJS/Bytecode/Instruction.h @@ -51,6 +51,7 @@ O(EnterObjectEnvironment) \ O(EnterUnwindContext) \ O(Exp) \ + O(ForOfNext) \ O(GetById) \ O(GetByIdWithThis) \ O(GetByValue) \ diff --git a/Libraries/LibJS/Bytecode/Interpreter.cpp b/Libraries/LibJS/Bytecode/Interpreter.cpp index 960c85fcdb1..3b3cdb24ccf 100644 --- a/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2021-2025, Andreas Kling + * Copyright (c) 2025, Aliaksandr Kalenik * * SPDX-License-Identifier: BSD-2-Clause */ @@ -611,6 +612,7 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point) HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(Dump); HANDLE_INSTRUCTION(EnterObjectEnvironment); HANDLE_INSTRUCTION(Exp); + HANDLE_INSTRUCTION(ForOfNext); HANDLE_INSTRUCTION(GetById); HANDLE_INSTRUCTION(GetByIdWithThis); HANDLE_INSTRUCTION(GetByValue); @@ -2980,6 +2982,27 @@ ThrowCompletionOr IteratorNext::execute_impl(Bytecode::Interpreter& interp return {}; } +ThrowCompletionOr ForOfNext::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& vm = interpreter.vm(); + auto& iterator_record = static_cast(interpreter.get(m_iterator_record).as_cell()); + + Value value; + bool done = false; + if (auto* builtin_iterator = iterator_record.iterator->as_builtin_iterator()) { + TRY(builtin_iterator->next(vm, done, value)); + } else { + auto result = TRY(iterator_next(vm, iterator_record)); + value = TRY(result->internal_get(vm.names.value, {})); + done = TRY(result->internal_get(vm.names.done, {})).to_boolean(); + } + + interpreter.set(dst_done(), Value(done)); + interpreter.set(dst_value(), value); + + return {}; +} + ThrowCompletionOr NewClass::execute_impl(Bytecode::Interpreter& interpreter) const { Value super_class; @@ -3754,6 +3777,14 @@ ByteString IteratorNext::to_byte_string_impl(Executable const& executable) const format_operand("iterator_record"sv, m_iterator_record, executable)); } +ByteString ForOfNext::to_byte_string_impl(Executable const& executable) const +{ + return ByteString::formatted("ForOfNext {}, {}, {}", + format_operand("dst_value"sv, m_dst_value, executable), + format_operand("dst_done"sv, m_dst_done, executable), + format_operand("iterator_record"sv, m_iterator_record, executable)); +} + ByteString ResolveThisBinding::to_byte_string_impl(Bytecode::Executable const&) const { return "ResolveThisBinding"sv; diff --git a/Libraries/LibJS/Bytecode/Op.h b/Libraries/LibJS/Bytecode/Op.h index 4cf41763a49..908f3a98448 100644 --- a/Libraries/LibJS/Bytecode/Op.h +++ b/Libraries/LibJS/Bytecode/Op.h @@ -2721,6 +2721,35 @@ private: Operand m_iterator_record; }; +class ForOfNext final : public Instruction { +public: + ForOfNext(Operand dst_value, Operand dst_done, Operand iterator_record) + : Instruction(Type::ForOfNext) + , m_dst_value(dst_value) + , m_dst_done(dst_done) + , m_iterator_record(iterator_record) + { + } + + ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; + ByteString to_byte_string_impl(Bytecode::Executable const&) const; + void visit_operands_impl(Function visitor) + { + visitor(m_dst_value); + visitor(m_dst_done); + visitor(m_iterator_record); + } + + Operand dst_value() const { return m_dst_value; } + Operand dst_done() const { return m_dst_done; } + Operand iterator_record() const { return m_iterator_record; } + +private: + Operand m_dst_value; + Operand m_dst_done; + Operand m_iterator_record; +}; + class ResolveThisBinding final : public Instruction { public: ResolveThisBinding()