123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- /*
- * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
- * Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #pragma once
- #include <AK/DeprecatedFlyString.h>
- #include <AK/Optional.h>
- #include <AK/Try.h>
- #include <AK/TypeCasts.h>
- #include <AK/Variant.h>
- #include <LibJS/Runtime/ErrorTypes.h>
- #include <LibJS/Runtime/Value.h>
- namespace JS {
- #define TRY_OR_THROW_OOM(vm, expression) \
- ({ \
- /* Ignore -Wshadow to allow nesting the macro. */ \
- AK_IGNORE_DIAGNOSTIC("-Wshadow", \
- auto&& _temporary_result = (expression)); \
- if (_temporary_result.is_error()) { \
- VERIFY(_temporary_result.error().code() == ENOMEM); \
- return (vm).throw_completion<JS::InternalError>((vm).error_message(::JS::VM::ErrorMessage::OutOfMemory)); \
- } \
- static_assert(!::AK::Detail::IsLvalueReference<decltype(_temporary_result.release_value())>, \
- "Do not return a reference from a fallible expression"); \
- _temporary_result.release_value(); \
- })
- #define MUST_OR_THROW_OOM(expression) \
- ({ \
- /* Ignore -Wshadow to allow nesting the macro. */ \
- AK_IGNORE_DIAGNOSTIC("-Wshadow", \
- auto&& _temporary_result = (expression)); \
- if (_temporary_result.is_error()) { \
- auto _completion = _temporary_result.release_error(); \
- \
- /* We can't explicitly check for OOM because InternalError does not store the ErrorType */ \
- VERIFY(_completion.value().has_value()); \
- VERIFY(_completion.value()->is_object()); \
- VERIFY(::AK::is<JS::InternalError>(_completion.value()->as_object())); \
- \
- return _completion; \
- } \
- static_assert(!::AK::Detail::IsLvalueReference<decltype(_temporary_result.release_value())>, \
- "Do not return a reference from a fallible expression"); \
- _temporary_result.release_value(); \
- })
- // 6.2.3 The Completion Record Specification Type, https://tc39.es/ecma262/#sec-completion-record-specification-type
- class [[nodiscard]] Completion {
- public:
- enum class Type {
- Empty,
- Normal,
- Break,
- Continue,
- Return,
- Throw,
- };
- ALWAYS_INLINE Completion(Type type, Optional<Value> value, Optional<DeprecatedFlyString> target)
- : m_type(type)
- , m_value(move(value))
- , m_target(move(target))
- {
- VERIFY(type != Type::Empty);
- if (m_value.has_value())
- VERIFY(!m_value->is_empty());
- }
- Completion(ThrowCompletionOr<Value> const&);
- // 5.2.3.1 Implicit Completion Values, https://tc39.es/ecma262/#sec-implicit-completion-values
- // Not `explicit` on purpose.
- ALWAYS_INLINE Completion(Value value)
- : Completion(Type::Normal, value, {})
- {
- }
- ALWAYS_INLINE Completion(Optional<Value> value)
- : Completion(Type::Normal, move(value), {})
- {
- }
- ALWAYS_INLINE Completion()
- : Completion(js_undefined())
- {
- }
- Completion(Completion const&) = default;
- Completion& operator=(Completion const&) = default;
- Completion(Completion&&) = default;
- Completion& operator=(Completion&&) = default;
- [[nodiscard]] Type type() const
- {
- VERIFY(m_type != Type::Empty);
- return m_type;
- }
- [[nodiscard]] Optional<Value>& value() { return m_value; }
- [[nodiscard]] Optional<Value> const& value() const { return m_value; }
- [[nodiscard]] Optional<DeprecatedFlyString>& target() { return m_target; }
- [[nodiscard]] Optional<DeprecatedFlyString> const& target() const { return m_target; }
- // "abrupt completion refers to any completion with a [[Type]] value other than normal"
- [[nodiscard]] bool is_abrupt() const { return m_type != Type::Normal; }
- // These are for compatibility with the TRY() macro in AK.
- [[nodiscard]] bool is_error() const { return m_type == Type::Throw; }
- [[nodiscard]] Optional<Value> release_value() { return move(m_value); }
- Completion release_error()
- {
- VERIFY(is_error());
- VERIFY(m_value.has_value());
- return { m_type, release_value(), move(m_target) };
- }
- // 6.2.3.4 UpdateEmpty ( completionRecord, value ), https://tc39.es/ecma262/#sec-updateempty
- Completion update_empty(Optional<Value> value) const
- {
- // 1. Assert: If completionRecord.[[Type]] is either return or throw, then completionRecord.[[Value]] is not empty.
- if (m_type == Type::Return || m_type == Type::Throw)
- VERIFY(m_value.has_value());
- // 2. If completionRecord.[[Value]] is not empty, return ? completionRecord.
- if (m_value.has_value())
- return *this;
- // 3. Return Completion Record { [[Type]]: completionRecord.[[Type]], [[Value]]: value, [[Target]]: completionRecord.[[Target]] }.
- return { m_type, move(value), m_target };
- }
- private:
- class EmptyTag {
- };
- friend AK::Optional<Completion>;
- Completion(EmptyTag)
- : m_type(Type::Empty)
- {
- }
- bool is_empty() const
- {
- return m_type == Type::Empty;
- }
- Type m_type { Type::Normal }; // [[Type]]
- Optional<Value> m_value; // [[Value]]
- Optional<DeprecatedFlyString> m_target; // [[Target]]
- };
- }
- namespace AK {
- template<>
- class Optional<JS::Completion> {
- template<typename U>
- friend class Optional;
- public:
- using ValueType = JS::Completion;
- Optional() = default;
- Optional(Optional<JS::Completion> const& other)
- {
- if (other.has_value())
- m_value = other.m_value;
- }
- Optional(Optional&& other)
- : m_value(move(other.m_value))
- {
- }
- template<typename U = JS::Completion>
- explicit(!IsConvertible<U&&, JS::Completion>) Optional(U&& value)
- requires(!IsSame<RemoveCVReference<U>, Optional<JS::Completion>> && IsConstructible<JS::Completion, U &&>)
- : m_value(forward<U>(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 { JS::Completion::EmptyTag {} };
- };
- }
- namespace JS {
- template<typename ValueType>
- requires(!IsLvalueReference<ValueType>)
- class [[nodiscard]] ThrowCompletionOr {
- public:
- ThrowCompletionOr()
- requires(IsSame<ValueType, Empty>)
- : m_value_or_throw_completion(Empty {})
- {
- }
- // Not `explicit` on purpose so that `return vm.throw_completion<Error>(...);` is possible.
- ThrowCompletionOr(Completion throw_completion)
- : m_value_or_throw_completion(move(throw_completion))
- {
- VERIFY(m_value_or_throw_completion.template get<Completion>().is_error());
- }
- // Not `explicit` on purpose so that `return value;` is possible.
- ThrowCompletionOr(ValueType value)
- : m_value_or_throw_completion(move(value))
- {
- if constexpr (IsSame<ValueType, Value>)
- VERIFY(!m_value_or_throw_completion.template get<ValueType>().is_empty());
- }
- ThrowCompletionOr(ThrowCompletionOr const&) = default;
- ThrowCompletionOr& operator=(ThrowCompletionOr const&) = default;
- ThrowCompletionOr(ThrowCompletionOr&&) = default;
- ThrowCompletionOr& operator=(ThrowCompletionOr&&) = default;
- ThrowCompletionOr(OptionalNone value)
- : m_value_or_throw_completion(ValueType { value })
- {
- }
- // Allows implicit construction of ThrowCompletionOr<T> from a type U if T(U) is a supported constructor.
- // Most commonly: Value from Object* or similar, so we can omit the curly braces from "return { TRY(...) };".
- // Disabled for POD types to avoid weird conversion shenanigans.
- template<typename WrappedValueType>
- ThrowCompletionOr(WrappedValueType&& value)
- requires(!IsPOD<ValueType>)
- : m_value_or_throw_completion(ValueType { value })
- {
- }
- [[nodiscard]] bool is_throw_completion() const { return m_value_or_throw_completion.template has<Completion>(); }
- Completion const& throw_completion() const { return m_value_or_throw_completion.template get<Completion>(); }
- [[nodiscard]] bool has_value() const
- requires(!IsSame<ValueType, Empty>)
- {
- return m_value_or_throw_completion.template has<ValueType>();
- }
- [[nodiscard]] ValueType const& value() const
- requires(!IsSame<ValueType, Empty>)
- {
- return m_value_or_throw_completion.template get<ValueType>();
- }
- // These are for compatibility with the TRY() macro in AK.
- [[nodiscard]] bool is_error() const { return m_value_or_throw_completion.template has<Completion>(); }
- [[nodiscard]] ValueType release_value() { return move(m_value_or_throw_completion.template get<ValueType>()); }
- Completion release_error() { return move(m_value_or_throw_completion.template get<Completion>()); }
- ValueType release_allocated_value_but_fixme_should_propagate_errors()
- {
- VERIFY(!is_error());
- return release_value();
- }
- private:
- Variant<ValueType, Completion> m_value_or_throw_completion;
- };
- template<>
- class [[nodiscard]] ThrowCompletionOr<void> : public ThrowCompletionOr<Empty> {
- public:
- using ThrowCompletionOr<Empty>::ThrowCompletionOr;
- };
- ThrowCompletionOr<Value> await(VM&, Value);
- // 6.2.4.1 NormalCompletion ( value ), https://tc39.es/ecma262/#sec-normalcompletion
- inline Completion normal_completion(Optional<Value> value)
- {
- // 1. Return Completion Record { [[Type]]: normal, [[Value]]: value, [[Target]]: empty }.
- return { Completion::Type::Normal, move(value), {} };
- }
- // 6.2.4.2 ThrowCompletion ( value ), https://tc39.es/ecma262/#sec-throwcompletion
- Completion throw_completion(Value);
- }
|