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