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
14namespace v8 {
15
16namespace api_internal {
17// Called when ToChecked is called on an empty Maybe.
18V8_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 */
31template <class T>
32class Maybe {
33 public:
34  V8_INLINE bool IsNothing() const { return !has_value_; }
35  V8_INLINE bool IsJust() const { return has_value_; }
36
37  /**
38   * An alias for |FromJust|. Will crash if the Maybe<> is nothing.
39   */
40  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   */
46  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   */
54  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   */
63  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   */
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   */
81  V8_INLINE T FromMaybe(const T& default_value) const {
82    return has_value_ ? value_ : default_value;
83  }
84
85  V8_INLINE bool operator==(const Maybe& other) const {
86    return (IsJust() == other.IsJust()) &&
87           (!IsJust() || FromJust() == other.FromJust());
88  }
89
90  V8_INLINE bool operator!=(const Maybe& other) const {
91    return !operator==(other);
92  }
93
94 private:
95  Maybe() : has_value_(false) {}
96  explicit Maybe(const T& t) : has_value_(true), value_(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
110template <class T>
111inline Maybe<T> Nothing() {
112  return Maybe<T>();
113}
114
115template <class T>
116inline 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).
123template <class T, std::enable_if_t<!std::is_lvalue_reference_v<T>>* = nullptr>
124inline 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.
129template <>
130class Maybe<void> {
131 public:
132  V8_INLINE bool IsNothing() const { return !is_valid_; }
133  V8_INLINE bool IsJust() const { return is_valid_; }
134
135  V8_INLINE bool operator==(const Maybe& other) const {
136    return IsJust() == other.IsJust();
137  }
138
139  V8_INLINE bool operator!=(const Maybe& other) const {
140    return !operator==(other);
141  }
142
143 private:
144  struct JustTag {};
145
146  Maybe() : is_valid_(false) {}
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
156inline Maybe<void> JustVoid() { return Maybe<void>(Maybe<void>::JustTag()); }
157
158}  // namespace v8
159
160#endif  // INCLUDE_V8_MAYBE_H_
161