From 6c504e2bff170cea266765946c6a24d4a5615b08 Mon Sep 17 00:00:00 2001 From: davidot Date: Mon, 28 Feb 2022 21:34:35 +0100 Subject: [PATCH] LibJS: Specialize Optional Since Completion has an enum for state we have plenty of space to add an extra type which indicates on empty completion. Since Optional is used in every ThrowCompletionOr<...> this saves quite some stack space as most function in LibJS return types like that. This saves 8 bytes for every Optional. --- Userland/Libraries/LibJS/Runtime/Completion.h | 137 +++++++++++++++++- 1 file changed, 136 insertions(+), 1 deletion(-) 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: