diff --git a/Userland/Libraries/LibJS/Runtime/Completion.h b/Userland/Libraries/LibJS/Runtime/Completion.h index dc7b607ef5..fb3b377b5b 100644 --- a/Userland/Libraries/LibJS/Runtime/Completion.h +++ b/Userland/Libraries/LibJS/Runtime/Completion.h @@ -19,6 +19,7 @@ namespace JS { class [[nodiscard]] Completion { public: enum class Type { + Empty, Normal, Break, Continue, @@ -31,6 +32,7 @@ public: , m_value(move(value)) , m_target(move(target)) { + VERIFY(type != Type::Empty); if (m_value.has_value()) VERIFY(!m_value->is_empty()); } @@ -59,7 +61,11 @@ public: Completion(Completion&&) = default; Completion& operator=(Completion&&) = default; - [[nodiscard]] Type type() const { return m_type; } + [[nodiscard]] Type type() const + { + VERIFY(m_type != Type::Empty); + return m_type; + } [[nodiscard]] Optional& value() { return m_value; } [[nodiscard]] Optional const& value() const { return m_value; } [[nodiscard]] Optional& target() { return m_target; } @@ -94,11 +100,140 @@ public: } private: + class EmptyTag { + }; + friend AK::Optional; + + Completion(EmptyTag) + : m_type(Type::Empty) + { + } + + bool is_empty() const + { + return m_type == Type::Empty; + } + Type m_type { Type::Normal }; // [[Type]] Optional m_value; // [[Value]] Optional m_target; // [[Target]] }; +} + +namespace AK { + +template<> +class Optional { + template + friend class Optional; + +public: + using ValueType = JS::Completion; + + Optional() + : m_value(JS::Completion(JS::Completion::EmptyTag {})) + { + } + + Optional(Optional const& other) + { + if (other.has_value()) + m_value = other.m_value; + } + + Optional(Optional&& other) + : m_value(other.m_value) + { + } + + template + explicit(!IsConvertible) Optional(U&& value) requires(!IsSame, Optional> && IsConstructible) + : m_value(forward(value)) + { + } + + Optional& operator=(Optional const& other) + { + if (this != &other) { + clear(); + m_value = other.m_value; + } + return *this; + } + + Optional& operator=(Optional&& other) + { + if (this != &other) { + clear(); + m_value = other.m_value; + } + return *this; + } + + void clear() + { + m_value = JS::Completion(JS::Completion::EmptyTag {}); + } + + [[nodiscard]] bool has_value() const + { + return !m_value.is_empty(); + } + + [[nodiscard]] JS::Completion& value() & + { + VERIFY(has_value()); + return m_value; + } + + [[nodiscard]] JS::Completion const& value() const& + { + VERIFY(has_value()); + return m_value; + } + + [[nodiscard]] JS::Completion value() && + { + return release_value(); + } + + [[nodiscard]] JS::Completion release_value() + { + VERIFY(has_value()); + JS::Completion released_value = m_value; + clear(); + return released_value; + } + + JS::Completion value_or(JS::Completion const& fallback) const& + { + if (has_value()) + return value(); + return fallback; + } + + [[nodiscard]] JS::Completion value_or(JS::Completion&& fallback) && + { + if (has_value()) + return value(); + return fallback; + } + + JS::Completion const& operator*() const { return value(); } + JS::Completion& operator*() { return value(); } + + JS::Completion const* operator->() const { return &value(); } + JS::Completion* operator->() { return &value(); } + +private: + JS::Completion m_value; +}; + +} + +namespace JS { + template class [[nodiscard]] ThrowCompletionOr { public: