1 // Copyright 2021 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef INCLUDE_V8_MAYBE_H_
6 #define INCLUDE_V8_MAYBE_H_
7
8 #include <type_traits>
9 #include <utility>
10
11 #include "v8-internal.h" // NOLINT(build/include_directory)
12 #include "v8config.h" // NOLINT(build/include_directory)
13
14 namespace v8 {
15
16 namespace api_internal {
17 // Called when ToChecked is called on an empty Maybe.
18 V8_EXPORT void FromJustIsNothing();
19 } // namespace api_internal
20
21 /**
22 * A simple Maybe type, representing an object which may or may not have a
23 * value, see https://hackage.haskell.org/package/base/docs/Data-Maybe.html.
24 *
25 * If an API method returns a Maybe<>, the API method can potentially fail
26 * either because an exception is thrown, or because an exception is pending,
27 * e.g. because a previous API call threw an exception that hasn't been caught
28 * yet, or because a TerminateExecution exception was thrown. In that case, a
29 * "Nothing" value is returned.
30 */
31 template <class T>
32 class Maybe {
33 public:
IsNothing() const34 V8_INLINE bool IsNothing() const { return !has_value_; }
IsJust() const35 V8_INLINE bool IsJust() const { return has_value_; }
36
37 /**
38 * An alias for |FromJust|. Will crash if the Maybe<> is nothing.
39 */
ToChecked() const40 V8_INLINE T ToChecked() const { return FromJust(); }
41
42 /**
43 * Short-hand for ToChecked(), which doesn't return a value. To be used, where
44 * the actual value of the Maybe is not needed like Object::Set.
45 */
Check() const46 V8_INLINE void Check() const {
47 if (V8_UNLIKELY(!IsJust())) api_internal::FromJustIsNothing();
48 }
49
50 /**
51 * Converts this Maybe<> to a value of type T. If this Maybe<> is
52 * nothing (empty), |false| is returned and |out| is left untouched.
53 */
To(T* out) const54 V8_WARN_UNUSED_RESULT V8_INLINE bool To(T* out) const {
55 if (V8_LIKELY(IsJust())) *out = value_;
56 return IsJust();
57 }
58
59 /**
60 * Converts this Maybe<> to a value of type T. If this Maybe<> is
61 * nothing (empty), V8 will crash the process.
62 */
FromJust() const63 V8_INLINE T FromJust() const& {
64 if (V8_UNLIKELY(!IsJust())) api_internal::FromJustIsNothing();
65 return value_;
66 }
67
68 /**
69 * Converts this Maybe<> to a value of type T. If this Maybe<> is
70 * nothing (empty), V8 will crash the process.
71 */
FromJust()72 V8_INLINE T FromJust() && {
73 if (V8_UNLIKELY(!IsJust())) api_internal::FromJustIsNothing();
74 return std::move(value_);
75 }
76
77 /**
78 * Converts this Maybe<> to a value of type T, using a default value if this
79 * Maybe<> is nothing (empty).
80 */
FromMaybe(const T& default_value) const81 V8_INLINE T FromMaybe(const T& default_value) const {
82 return has_value_ ? value_ : default_value;
83 }
84
operator ==(const Maybe& other) const85 V8_INLINE bool operator==(const Maybe& other) const {
86 return (IsJust() == other.IsJust()) &&
87 (!IsJust() || FromJust() == other.FromJust());
88 }
89
operator !=(const Maybe& other) const90 V8_INLINE bool operator!=(const Maybe& other) const {
91 return !operator==(other);
92 }
93
94 private:
Maybe()95 Maybe() : has_value_(false) {}
Maybe(const T& t)96 explicit Maybe(const T& t) : has_value_(true), value_(t) {}
Maybe(T&& t)97 explicit Maybe(T&& t) : has_value_(true), value_(std::move(t)) {}
98
99 bool has_value_;
100 T value_;
101
102 template <class U>
103 friend Maybe<U> Nothing();
104 template <class U>
105 friend Maybe<U> Just(const U& u);
106 template <class U, std::enable_if_t<!std::is_lvalue_reference_v<U>>*>
107 friend Maybe<U> Just(U&& u);
108 };
109
110 template <class T>
Nothing()111 inline Maybe<T> Nothing() {
112 return Maybe<T>();
113 }
114
115 template <class T>
Just(const T& t)116 inline Maybe<T> Just(const T& t) {
117 return Maybe<T>(t);
118 }
119
120 // Don't use forwarding references here but instead use two overloads.
121 // Forwarding references only work when type deduction takes place, which is not
122 // the case for callsites such as Just<Type>(t).
123 template <class T, std::enable_if_t<!std::is_lvalue_reference_v<T>>* = nullptr>
Just(T&& t)124 inline Maybe<T> Just(T&& t) {
125 return Maybe<T>(std::move(t));
126 }
127
128 // A template specialization of Maybe<T> for the case of T = void.
129 template <>
130 class Maybe<void> {
131 public:
IsNothing() const132 V8_INLINE bool IsNothing() const { return !is_valid_; }
IsJust() const133 V8_INLINE bool IsJust() const { return is_valid_; }
134
operator ==(const Maybe& other) const135 V8_INLINE bool operator==(const Maybe& other) const {
136 return IsJust() == other.IsJust();
137 }
138
operator !=(const Maybe& other) const139 V8_INLINE bool operator!=(const Maybe& other) const {
140 return !operator==(other);
141 }
142
143 private:
144 struct JustTag {};
145
Maybe()146 Maybe() : is_valid_(false) {}
Maybe(JustTag)147 explicit Maybe(JustTag) : is_valid_(true) {}
148
149 bool is_valid_;
150
151 template <class U>
152 friend Maybe<U> Nothing();
153 friend Maybe<void> JustVoid();
154 };
155
JustVoid()156 inline Maybe<void> JustVoid() { return Maybe<void>(Maybe<void>::JustTag()); }
157
158 } // namespace v8
159
160 #endif // INCLUDE_V8_MAYBE_H_
161