diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index 90ca7d5c652..b69a2863703 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/Userland/Libraries/LibJS/AST.cpp @@ -395,9 +395,9 @@ Value WhileStatement::execute(Interpreter& interpreter, GlobalObject& global_obj if (interpreter.exception()) return {}; if (interpreter.vm().should_unwind()) { - if (interpreter.vm().should_unwind_until(ScopeType::Continuable, m_label)) { + if (interpreter.vm().should_unwind_until(ScopeType::Continuable, m_labels)) { interpreter.vm().stop_unwind(); - } else if (interpreter.vm().should_unwind_until(ScopeType::Breakable, m_label)) { + } else if (interpreter.vm().should_unwind_until(ScopeType::Breakable, m_labels)) { interpreter.vm().stop_unwind(); break; } else { @@ -421,9 +421,9 @@ Value DoWhileStatement::execute(Interpreter& interpreter, GlobalObject& global_o if (interpreter.exception()) return {}; if (interpreter.vm().should_unwind()) { - if (interpreter.vm().should_unwind_until(ScopeType::Continuable, m_label)) { + if (interpreter.vm().should_unwind_until(ScopeType::Continuable, m_labels)) { interpreter.vm().stop_unwind(); - } else if (interpreter.vm().should_unwind_until(ScopeType::Breakable, m_label)) { + } else if (interpreter.vm().should_unwind_until(ScopeType::Breakable, m_labels)) { interpreter.vm().stop_unwind(); break; } else { @@ -477,9 +477,9 @@ Value ForStatement::execute(Interpreter& interpreter, GlobalObject& global_objec if (interpreter.exception()) return {}; if (interpreter.vm().should_unwind()) { - if (interpreter.vm().should_unwind_until(ScopeType::Continuable, m_label)) { + if (interpreter.vm().should_unwind_until(ScopeType::Continuable, m_labels)) { interpreter.vm().stop_unwind(); - } else if (interpreter.vm().should_unwind_until(ScopeType::Breakable, m_label)) { + } else if (interpreter.vm().should_unwind_until(ScopeType::Breakable, m_labels)) { interpreter.vm().stop_unwind(); break; } else { @@ -498,9 +498,9 @@ Value ForStatement::execute(Interpreter& interpreter, GlobalObject& global_objec if (interpreter.exception()) return {}; if (interpreter.vm().should_unwind()) { - if (interpreter.vm().should_unwind_until(ScopeType::Continuable, m_label)) { + if (interpreter.vm().should_unwind_until(ScopeType::Continuable, m_labels)) { interpreter.vm().stop_unwind(); - } else if (interpreter.vm().should_unwind_until(ScopeType::Breakable, m_label)) { + } else if (interpreter.vm().should_unwind_until(ScopeType::Breakable, m_labels)) { interpreter.vm().stop_unwind(); break; } else { @@ -570,9 +570,9 @@ Value ForInStatement::execute(Interpreter& interpreter, GlobalObject& global_obj if (interpreter.exception()) return {}; if (interpreter.vm().should_unwind()) { - if (interpreter.vm().should_unwind_until(ScopeType::Continuable, m_label)) { + if (interpreter.vm().should_unwind_until(ScopeType::Continuable, m_labels)) { interpreter.vm().stop_unwind(); - } else if (interpreter.vm().should_unwind_until(ScopeType::Breakable, m_label)) { + } else if (interpreter.vm().should_unwind_until(ScopeType::Breakable, m_labels)) { interpreter.vm().stop_unwind(); break; } else { @@ -613,9 +613,9 @@ Value ForOfStatement::execute(Interpreter& interpreter, GlobalObject& global_obj if (interpreter.exception()) return IterationDecision::Break; if (interpreter.vm().should_unwind()) { - if (interpreter.vm().should_unwind_until(ScopeType::Continuable, m_label)) { + if (interpreter.vm().should_unwind_until(ScopeType::Continuable, m_labels)) { interpreter.vm().stop_unwind(); - } else if (interpreter.vm().should_unwind_until(ScopeType::Breakable, m_label)) { + } else if (interpreter.vm().should_unwind_until(ScopeType::Breakable, m_labels)) { interpreter.vm().stop_unwind(); return IterationDecision::Break; } else { @@ -2429,10 +2429,10 @@ Value SwitchStatement::execute(Interpreter& interpreter, GlobalObject& global_ob if (interpreter.exception()) return Value {}; if (interpreter.vm().should_unwind()) { - if (interpreter.vm().should_unwind_until(ScopeType::Continuable, m_label)) { + if (interpreter.vm().should_unwind_until(ScopeType::Continuable, m_labels)) { // No stop_unwind(), the outer loop will handle that - we just need to break out of the switch/case. return last_value; - } else if (interpreter.vm().should_unwind_until(ScopeType::Breakable, m_label)) { + } else if (interpreter.vm().should_unwind_until(ScopeType::Breakable, m_labels)) { interpreter.vm().stop_unwind(); return last_value; } else { diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index 37f21efb628..c9aeb8bfafb 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -80,11 +80,12 @@ public: { } - FlyString const& label() const { return m_label; } - void set_label(FlyString string) { m_label = move(string); } + HashTable const& labels() const { return m_labels; } + void add_label(FlyString label) { m_labels.set(move(label)); } + bool has_label(FlyString const& label) const { return m_labels.contains(label); } protected: - FlyString m_label; + HashTable m_labels; }; class EmptyStatement final : public Statement { diff --git a/Userland/Libraries/LibJS/Interpreter.cpp b/Userland/Libraries/LibJS/Interpreter.cpp index 0d7ff1a3460..04d5d64f011 100644 --- a/Userland/Libraries/LibJS/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Interpreter.cpp @@ -193,7 +193,7 @@ Value Interpreter::execute_statement(GlobalObject& global_object, const Statemen if (!value.is_empty()) last_value = value; if (vm().should_unwind()) { - if (!block.label().is_null() && vm().should_unwind_until(ScopeType::Breakable, block.label())) + if (!block.labels().is_empty() && vm().should_unwind_until(ScopeType::Breakable, block.labels())) vm().stop_unwind(); break; } diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp index 6ef37cf761e..cb31c63a9b8 100644 --- a/Userland/Libraries/LibJS/Parser.cpp +++ b/Userland/Libraries/LibJS/Parser.cpp @@ -621,7 +621,7 @@ RefPtr Parser::try_parse_labelled_statement(AllowLabelledFunction all m_state.labels_in_scope.remove(identifier); - labelled_statement->set_label(identifier); + labelled_statement->add_label(identifier); state_rollback_guard.disarm(); discard_saved_state(); return labelled_statement.release_nonnull(); diff --git a/Userland/Libraries/LibJS/Runtime/VM.h b/Userland/Libraries/LibJS/Runtime/VM.h index 6f1f3ba2f1c..0bd7d9a714d 100644 --- a/Userland/Libraries/LibJS/Runtime/VM.h +++ b/Userland/Libraries/LibJS/Runtime/VM.h @@ -193,11 +193,11 @@ public: m_unwind_until = ScopeType::None; m_unwind_until_label = {}; } - bool should_unwind_until(ScopeType type, FlyString const& label) const + bool should_unwind_until(ScopeType type, HashTable const& labels) const { if (m_unwind_until_label.is_null()) return m_unwind_until == type; - return m_unwind_until == type && m_unwind_until_label == label; + return m_unwind_until == type && labels.contains(m_unwind_until_label); } bool should_unwind() const { return m_unwind_until != ScopeType::None; } diff --git a/Userland/Libraries/LibJS/Tests/statement-with-many-labels.js b/Userland/Libraries/LibJS/Tests/statement-with-many-labels.js new file mode 100644 index 00000000000..534811252e7 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/statement-with-many-labels.js @@ -0,0 +1,9 @@ +test("basic support for statement with many labels", () => { + function foo() { + a: b: c: for (;;) { + break b; + } + return 1; + } + expect(foo()).toBe(1); +});