| File: | var/lib/jenkins/workspace/firefox-scan-build/js/src/threading/LockGuard.h | 
| Warning: | line 31, column 19 Called C++ object pointer is uninitialized | 
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- | |||
| 2 | * vim: set ts=8 sts=2 et sw=2 tw=80: | |||
| 3 | * This Source Code Form is subject to the terms of the Mozilla Public | |||
| 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this | |||
| 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |||
| 6 | ||||
| 7 | /* Various JS utility functions. */ | |||
| 8 | ||||
| 9 | #include "js/Utility.h" | |||
| 10 | ||||
| 11 | #include "mozilla/Assertions.h" | |||
| 12 | #include "mozilla/Atomics.h" | |||
| 13 | #include "mozilla/Maybe.h" | |||
| 14 | #include "mozilla/ThreadLocal.h" | |||
| 15 | ||||
| 16 | #include <stdio.h> | |||
| 17 | ||||
| 18 | #include "jstypes.h" | |||
| 19 | ||||
| 20 | #include "util/Poison.h" | |||
| 21 | #include "vm/HelperThreads.h" | |||
| 22 | #include "vm/JSContext.h" | |||
| 23 | ||||
| 24 | using namespace js; | |||
| 25 | ||||
| 26 | using mozilla::Maybe; | |||
| 27 | ||||
| 28 | #if defined(DEBUG1) || defined(JS_OOM_BREAKPOINT) | |||
| 29 | /* For OOM testing functionality in Utility.h. */ | |||
| 30 | namespace js { | |||
| 31 | ||||
| 32 | mozilla::Atomic<AutoEnterOOMUnsafeRegion*> AutoEnterOOMUnsafeRegion::owner_; | |||
| 33 | ||||
| 34 | namespace oom { | |||
| 35 | ||||
| 36 | JS_PUBLIC_DATA FailureSimulator simulator; | |||
| 37 | static MOZ_THREAD_LOCAL(uint32_t)__thread ::mozilla::detail::ThreadLocal< uint32_t, ::mozilla ::detail::ThreadLocalNativeStorage> threadType; | |||
| 38 | ||||
| 39 | bool InitThreadType() { return threadType.init(); } | |||
| 40 | ||||
| 41 | void SetThreadType(ThreadType type) { threadType.set(type); } | |||
| 42 | ||||
| 43 | uint32_t GetThreadType(void) { return threadType.get(); } | |||
| 44 | ||||
| 45 | static inline bool IsHelperThreadType(uint32_t thread) { | |||
| 46 | return thread != THREAD_TYPE_NONE && thread != THREAD_TYPE_MAIN; | |||
| 47 | } | |||
| 48 | ||||
| 49 | void FailureSimulator::simulateFailureAfter(Kind kind, uint64_t checks, | |||
| 50 | uint32_t thread, bool always) { | |||
| 51 | Maybe<AutoLockHelperThreadState> lock; | |||
| 52 | if (IsHelperThreadType(targetThread_) || IsHelperThreadType(thread)) { | |||
| 53 | lock.emplace(); | |||
| 54 | WaitForAllHelperThreads(lock.ref()); | |||
| 55 | } | |||
| 56 | ||||
| 57 | MOZ_ASSERT(counter_ + checks > counter_)do { static_assert( mozilla::detail::AssertionConditionType< decltype(counter_ + checks > counter_)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(counter_ + checks > counter_ ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "counter_ + checks > counter_", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/util/Utility.cpp" , 57); AnnotateMozCrashReason("MOZ_ASSERT" "(" "counter_ + checks > counter_" ")"); do { *((volatile int*)__null) = 57; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | |||
| 58 | MOZ_ASSERT(thread > js::THREAD_TYPE_NONE && thread < js::THREAD_TYPE_MAX)do { static_assert( mozilla::detail::AssertionConditionType< decltype(thread > js::THREAD_TYPE_NONE && thread < js::THREAD_TYPE_MAX)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(thread > js::THREAD_TYPE_NONE && thread < js::THREAD_TYPE_MAX))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("thread > js::THREAD_TYPE_NONE && thread < js::THREAD_TYPE_MAX" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/util/Utility.cpp" , 58); AnnotateMozCrashReason("MOZ_ASSERT" "(" "thread > js::THREAD_TYPE_NONE && thread < js::THREAD_TYPE_MAX" ")"); do { *((volatile int*)__null) = 58; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | |||
| 59 | targetThread_ = thread; | |||
| 60 | maxChecks_ = counter_ + checks; | |||
| 61 | failAlways_ = always; | |||
| 62 | kind_ = kind; | |||
| 63 | } | |||
| 64 | ||||
| 65 | void FailureSimulator::reset() { | |||
| 66 | Maybe<AutoLockHelperThreadState> lock; | |||
| 67 | if (IsHelperThreadType(targetThread_)) { | |||
| 
 | ||||
| 68 | lock.emplace(); | |||
| 69 | WaitForAllHelperThreads(lock.ref()); | |||
| 70 | } | |||
| 71 | ||||
| 72 | targetThread_ = THREAD_TYPE_NONE; | |||
| 73 | maxChecks_ = UINT64_MAX(18446744073709551615UL); | |||
| 74 | failAlways_ = false; | |||
| 75 | kind_ = Kind::Nothing; | |||
| 76 | } | |||
| 77 | ||||
| 78 | } // namespace oom | |||
| 79 | } // namespace js | |||
| 80 | #endif // defined(DEBUG) || defined(JS_OOM_BREAKPOINT) | |||
| 81 | ||||
| 82 | #if defined(FUZZING) | |||
| 83 | namespace js { | |||
| 84 | namespace oom { | |||
| 85 | JS_PUBLIC_DATA size_t largeAllocLimit = 0; | |||
| 86 | void InitLargeAllocLimit() { | |||
| 87 | char* limitStr = getenv("MOZ_FUZZ_LARGE_ALLOC_LIMIT"); | |||
| 88 | if (limitStr) { | |||
| 89 | largeAllocLimit = atoll(limitStr); | |||
| 90 | } | |||
| 91 | } | |||
| 92 | } // namespace oom | |||
| 93 | } // namespace js | |||
| 94 | #endif | |||
| 95 | ||||
| 96 | JS_PUBLIC_DATA arena_id_t js::MallocArena; | |||
| 97 | JS_PUBLIC_DATA arena_id_t js::BackgroundMallocArena; | |||
| 98 | JS_PUBLIC_DATA arena_id_t js::ArrayBufferContentsArena; | |||
| 99 | JS_PUBLIC_DATA arena_id_t js::StringBufferArena; | |||
| 100 | ||||
| 101 | void js::InitMallocAllocator() { | |||
| 102 | arena_params_t mallocArenaParams; | |||
| 103 | mallocArenaParams.mMaxDirtyIncreaseOverride = 5; | |||
| 104 | MallocArena = moz_create_arena_with_params(&mallocArenaParams); | |||
| 105 | BackgroundMallocArena = moz_create_arena_with_params(&mallocArenaParams); | |||
| 106 | ||||
| 107 | arena_params_t params; | |||
| 108 | params.mMaxDirtyIncreaseOverride = 5; | |||
| 109 | params.mFlags |= ARENA_FLAG_RANDOMIZE_SMALL_ENABLED1; | |||
| 110 | ArrayBufferContentsArena = moz_create_arena_with_params(¶ms); | |||
| 111 | StringBufferArena = moz_create_arena_with_params(¶ms); | |||
| 112 | } | |||
| 113 | ||||
| 114 | void js::ShutDownMallocAllocator() { | |||
| 115 | // Until Bug 1364359 is fixed it is unsafe to call moz_dispose_arena. | |||
| 116 | // moz_dispose_arena(MallocArena); | |||
| 117 | // moz_dispose_arena(ArrayBufferContentsArena); | |||
| 118 | } | |||
| 119 | ||||
| 120 | extern void js::AssertJSStringBufferInCorrectArena(const void* ptr) { | |||
| 121 | // `jemalloc_ptr_info()` only exists if MOZ_MEMORY is defined, and it only | |||
| 122 | // returns an arenaId if MOZ_DEBUG is defined. Otherwise, this function is | |||
| 123 | // a no-op. | |||
| 124 | #if defined(MOZ_MEMORY1) && defined(MOZ_DEBUG1) | |||
| 125 | if (ptr && !TlsContext.get()->nursery().isInside(ptr)) { | |||
| 126 | jemalloc_ptr_info_t ptrInfo{}; | |||
| 127 | jemalloc_ptr_info(ptr, &ptrInfo); | |||
| 128 | MOZ_ASSERT(ptrInfo.tag != TagUnknown)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ptrInfo.tag != TagUnknown)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ptrInfo.tag != TagUnknown))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("ptrInfo.tag != TagUnknown" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/util/Utility.cpp" , 128); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ptrInfo.tag != TagUnknown" ")"); do { *((volatile int*)__null) = 128; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | |||
| 129 | MOZ_ASSERT(ptrInfo.arenaId == js::StringBufferArena)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ptrInfo.arenaId == js::StringBufferArena)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ptrInfo.arenaId == js::StringBufferArena))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ptrInfo.arenaId == js::StringBufferArena" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/util/Utility.cpp" , 129); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ptrInfo.arenaId == js::StringBufferArena" ")"); do { *((volatile int*)__null) = 129; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | |||
| 130 | } | |||
| 131 | #endif | |||
| 132 | } | |||
| 133 | ||||
| 134 | JS_PUBLIC_API void JS_Assert(const char* s, const char* file, int ln) { | |||
| 135 | MOZ_ReportAssertionFailure(s, file, ln); | |||
| 136 | MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/util/Utility.cpp" , 136); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile int*)__null) = 136; __attribute__((nomerge)) ::abort(); } while (false); } while (false); | |||
| 137 | } | |||
| 138 | ||||
| 139 | #ifdef __linux__1 | |||
| 140 | ||||
| 141 | # include <malloc.h> | |||
| 142 | # include <stdlib.h> | |||
| 143 | ||||
| 144 | namespace js { | |||
| 145 | ||||
| 146 | // This function calls all the vanilla heap allocation functions. It is never | |||
| 147 | // called, and exists purely to help config/check_vanilla_allocations.py. See | |||
| 148 | // that script for more details. | |||
| 149 | extern MOZ_COLD__attribute__((cold)) void AllTheNonBasicVanillaNewAllocations() { | |||
| 150 | // posix_memalign and aligned_alloc aren't available on all Linux | |||
| 151 | // configurations. | |||
| 152 | // valloc was deprecated in Android 5.0 | |||
| 153 | // char* q; | |||
| 154 | // posix_memalign((void**)&q, 16, 16); | |||
| 155 | ||||
| 156 | intptr_t p = intptr_t(malloc(16)) + intptr_t(calloc(1, 16)) + | |||
| 157 | intptr_t(realloc(nullptr, 16)) + intptr_t(new char) + | |||
| 158 | intptr_t(new char) + intptr_t(new char) + | |||
| 159 | intptr_t(new char[16]) + intptr_t(memalign(16, 16)) + | |||
| 160 | // intptr_t(q) + | |||
| 161 | // intptr_t(aligned_alloc(16, 16)) + | |||
| 162 | // intptr_t(valloc(4096)) + | |||
| 163 | intptr_t(strdup("dummy")); | |||
| 164 | ||||
| 165 | printf("%u\n", uint32_t(p)); // make sure |p| is not optimized away | |||
| 166 | ||||
| 167 | free((int*)p); // this would crash if ever actually called | |||
| 168 | ||||
| 169 | MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/util/Utility.cpp" , 169); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile int*)__null) = 169; __attribute__((nomerge)) ::abort(); } while (false); } while (false); | |||
| 170 | } | |||
| 171 | ||||
| 172 | } // namespace js | |||
| 173 | ||||
| 174 | #endif // __linux__ | 
| 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | ||||||||||
| 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ | ||||||||||
| 3 | /* This Source Code Form is subject to the terms of the Mozilla Public | ||||||||||
| 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||||||||
| 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||||||||||
| 6 | |||||||||||
| 7 | /* A class for optional values and in-place lazy construction. */ | ||||||||||
| 8 | |||||||||||
| 9 | #ifndef mozilla_Maybe_h | ||||||||||
| 10 | #define mozilla_Maybe_h | ||||||||||
| 11 | |||||||||||
| 12 | #include <functional> | ||||||||||
| 13 | #include <new> // for placement new | ||||||||||
| 14 | #include <ostream> | ||||||||||
| 15 | #include <type_traits> | ||||||||||
| 16 | #include <utility> | ||||||||||
| 17 | |||||||||||
| 18 | #include "mozilla/Alignment.h" | ||||||||||
| 19 | #include "mozilla/Assertions.h" | ||||||||||
| 20 | #include "mozilla/Attributes.h" | ||||||||||
| 21 | #include "mozilla/MaybeStorageBase.h" | ||||||||||
| 22 | #include "mozilla/MemoryChecking.h" | ||||||||||
| 23 | #include "mozilla/OperatorNewExtensions.h" | ||||||||||
| 24 | #include "mozilla/Poison.h" | ||||||||||
| 25 | #include "mozilla/ThreadSafety.h" | ||||||||||
| 26 | |||||||||||
| 27 | class nsCycleCollectionTraversalCallback; | ||||||||||
| 28 | |||||||||||
| 29 | template <typename T> | ||||||||||
| 30 | inline void CycleCollectionNoteChild( | ||||||||||
| 31 | nsCycleCollectionTraversalCallback& aCallback, T* aChild, const char* aName, | ||||||||||
| 32 | uint32_t aFlags); | ||||||||||
| 33 | |||||||||||
| 34 | namespace mozilla { | ||||||||||
| 35 | |||||||||||
| 36 | struct Nothing {}; | ||||||||||
| 37 | |||||||||||
| 38 | inline constexpr bool operator==(const Nothing&, const Nothing&) { | ||||||||||
| 39 | return true; | ||||||||||
| 40 | } | ||||||||||
| 41 | |||||||||||
| 42 | template <class T> | ||||||||||
| 43 | class Maybe; | ||||||||||
| 44 | |||||||||||
| 45 | namespace detail { | ||||||||||
| 46 | |||||||||||
| 47 | // You would think that poisoning Maybe instances could just be a call | ||||||||||
| 48 | // to mozWritePoison. Unfortunately, using a simple call to | ||||||||||
| 49 | // mozWritePoison generates poor code on MSVC for small structures. The | ||||||||||
| 50 | // generated code contains (always not-taken) branches and does a bunch | ||||||||||
| 51 | // of setup for `rep stos{l,q}`, even though we know at compile time | ||||||||||
| 52 | // exactly how many words we're poisoning. Instead, we're going to | ||||||||||
| 53 | // force MSVC to generate the code we want via recursive templates. | ||||||||||
| 54 | |||||||||||
| 55 | // Write the given poisonValue into p at offset*sizeof(uintptr_t). | ||||||||||
| 56 | template <size_t offset> | ||||||||||
| 57 | inline void WritePoisonAtOffset(void* p, const uintptr_t poisonValue) { | ||||||||||
| 58 | memcpy(static_cast<char*>(p) + offset * sizeof(poisonValue), &poisonValue, | ||||||||||
| 59 | sizeof(poisonValue)); | ||||||||||
| 60 | } | ||||||||||
| 61 | |||||||||||
| 62 | template <size_t Offset, size_t NOffsets> | ||||||||||
| 63 | struct InlinePoisoner { | ||||||||||
| 64 | static void poison(void* p, const uintptr_t poisonValue) { | ||||||||||
| 65 | WritePoisonAtOffset<Offset>(p, poisonValue); | ||||||||||
| 66 | InlinePoisoner<Offset + 1, NOffsets>::poison(p, poisonValue); | ||||||||||
| 67 | } | ||||||||||
| 68 | }; | ||||||||||
| 69 | |||||||||||
| 70 | template <size_t N> | ||||||||||
| 71 | struct InlinePoisoner<N, N> { | ||||||||||
| 72 | static void poison(void*, const uintptr_t) { | ||||||||||
| 73 | // All done! | ||||||||||
| 74 | } | ||||||||||
| 75 | }; | ||||||||||
| 76 | |||||||||||
| 77 | // We can't generate inline code for large structures, though, because we'll | ||||||||||
| 78 | // blow out recursive template instantiation limits, and the code would be | ||||||||||
| 79 | // bloated to boot. So provide a fallback to the out-of-line poisoner. | ||||||||||
| 80 | template <size_t ObjectSize> | ||||||||||
| 81 | struct OutOfLinePoisoner { | ||||||||||
| 82 | static MOZ_NEVER_INLINE__attribute__((noinline)) void poison(void* p, const uintptr_t) { | ||||||||||
| 83 | mozWritePoison(p, ObjectSize); | ||||||||||
| 84 | } | ||||||||||
| 85 | }; | ||||||||||
| 86 | |||||||||||
| 87 | template <typename T> | ||||||||||
| 88 | inline void PoisonObject(T* p) { | ||||||||||
| 89 | const uintptr_t POISON = mozPoisonValue(); | ||||||||||
| 90 | std::conditional_t<(sizeof(T) <= 8 * sizeof(POISON)), | ||||||||||
| 91 | InlinePoisoner<0, sizeof(T) / sizeof(POISON)>, | ||||||||||
| 92 | OutOfLinePoisoner<sizeof(T)>>::poison(p, POISON); | ||||||||||
| 93 | } | ||||||||||
| 94 | |||||||||||
| 95 | template <typename T> | ||||||||||
| 96 | struct MaybePoisoner { | ||||||||||
| 97 | static const size_t N = sizeof(T); | ||||||||||
| 98 | |||||||||||
| 99 | static void poison(void* aPtr) { | ||||||||||
| 100 | #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED1 | ||||||||||
| 101 | if (N >= sizeof(uintptr_t)) { | ||||||||||
| 102 | PoisonObject(static_cast<std::remove_cv_t<T>*>(aPtr)); | ||||||||||
| 103 | } | ||||||||||
| 104 | #endif | ||||||||||
| 105 | MOZ_MAKE_MEM_UNDEFINED(aPtr, N)do { } while (0); | ||||||||||
| 106 | } | ||||||||||
| 107 | }; | ||||||||||
| 108 | |||||||||||
| 109 | template <typename T, | ||||||||||
| 110 | bool TriviallyDestructibleAndCopyable = | ||||||||||
| 111 | IsTriviallyDestructibleAndCopyable<T>, | ||||||||||
| 112 | bool Copyable = std::is_copy_constructible_v<T>, | ||||||||||
| 113 | bool Movable = std::is_move_constructible_v<T>> | ||||||||||
| 114 | class Maybe_CopyMove_Enabler; | ||||||||||
| 115 | |||||||||||
| 116 | #define MOZ_MAYBE_COPY_OPS() \ | ||||||||||
| 117 | Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler& aOther) { \ | ||||||||||
| 118 | if (downcast(aOther).isSome()) { \ | ||||||||||
| 119 | downcast(*this).emplace(*downcast(aOther)); \ | ||||||||||
| 120 | } \ | ||||||||||
| 121 | } \ | ||||||||||
| 122 | \ | ||||||||||
| 123 | Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler& aOther) { \ | ||||||||||
| 124 | return downcast(*this).template operator= <T>(downcast(aOther)); \ | ||||||||||
| 125 | } | ||||||||||
| 126 | |||||||||||
| 127 | #define MOZ_MAYBE_MOVE_OPS() \ | ||||||||||
| 128 | constexpr Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&& aOther) { \ | ||||||||||
| 129 | if (downcast(aOther).isSome()) { \ | ||||||||||
| 130 | downcast(*this).emplace(std::move(*downcast(aOther))); \ | ||||||||||
| 131 | downcast(aOther).reset(); \ | ||||||||||
| 132 | } \ | ||||||||||
| 133 | } \ | ||||||||||
| 134 | \ | ||||||||||
| 135 | constexpr Maybe_CopyMove_Enabler& operator=( \ | ||||||||||
| 136 | Maybe_CopyMove_Enabler&& aOther) { \ | ||||||||||
| 137 | downcast(*this).template operator= <T>(std::move(downcast(aOther))); \ | ||||||||||
| 138 | \ | ||||||||||
| 139 | return *this; \ | ||||||||||
| 140 | } | ||||||||||
| 141 | |||||||||||
| 142 | #define MOZ_MAYBE_DOWNCAST() \ | ||||||||||
| 143 | static constexpr Maybe<T>& downcast(Maybe_CopyMove_Enabler& aObj) { \ | ||||||||||
| 144 | return static_cast<Maybe<T>&>(aObj); \ | ||||||||||
| 145 | } \ | ||||||||||
| 146 | static constexpr const Maybe<T>& downcast( \ | ||||||||||
| 147 | const Maybe_CopyMove_Enabler& aObj) { \ | ||||||||||
| 148 | return static_cast<const Maybe<T>&>(aObj); \ | ||||||||||
| 149 | } | ||||||||||
| 150 | |||||||||||
| 151 | template <typename T> | ||||||||||
| 152 | class Maybe_CopyMove_Enabler<T, true, true, true> { | ||||||||||
| 153 | public: | ||||||||||
| 154 | Maybe_CopyMove_Enabler() = default; | ||||||||||
| 155 | |||||||||||
| 156 | Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler&) = default; | ||||||||||
| 157 | Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler&) = default; | ||||||||||
| 158 | constexpr Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&& aOther) { | ||||||||||
| 159 | downcast(aOther).reset(); | ||||||||||
| 160 | } | ||||||||||
| 161 | constexpr Maybe_CopyMove_Enabler& operator=(Maybe_CopyMove_Enabler&& aOther) { | ||||||||||
| 162 | downcast(aOther).reset(); | ||||||||||
| 163 | return *this; | ||||||||||
| 164 | } | ||||||||||
| 165 | |||||||||||
| 166 | private: | ||||||||||
| 167 | MOZ_MAYBE_DOWNCAST() | ||||||||||
| 168 | }; | ||||||||||
| 169 | |||||||||||
| 170 | template <typename T> | ||||||||||
| 171 | class Maybe_CopyMove_Enabler<T, true, false, true> { | ||||||||||
| 172 | public: | ||||||||||
| 173 | Maybe_CopyMove_Enabler() = default; | ||||||||||
| 174 | |||||||||||
| 175 | Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler&) = delete; | ||||||||||
| 176 | Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler&) = delete; | ||||||||||
| 177 | constexpr Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&& aOther) { | ||||||||||
| 178 | downcast(aOther).reset(); | ||||||||||
| 179 | } | ||||||||||
| 180 | constexpr Maybe_CopyMove_Enabler& operator=(Maybe_CopyMove_Enabler&& aOther) { | ||||||||||
| 181 | downcast(aOther).reset(); | ||||||||||
| 182 | return *this; | ||||||||||
| 183 | } | ||||||||||
| 184 | |||||||||||
| 185 | private: | ||||||||||
| 186 | MOZ_MAYBE_DOWNCAST() | ||||||||||
| 187 | }; | ||||||||||
| 188 | |||||||||||
| 189 | template <typename T> | ||||||||||
| 190 | class Maybe_CopyMove_Enabler<T, false, true, true> { | ||||||||||
| 191 | public: | ||||||||||
| 192 | Maybe_CopyMove_Enabler() = default; | ||||||||||
| 193 | |||||||||||
| 194 | MOZ_MAYBE_COPY_OPS() | ||||||||||
| 195 | MOZ_MAYBE_MOVE_OPS() | ||||||||||
| 196 | |||||||||||
| 197 | private: | ||||||||||
| 198 | MOZ_MAYBE_DOWNCAST() | ||||||||||
| 199 | }; | ||||||||||
| 200 | |||||||||||
| 201 | template <typename T> | ||||||||||
| 202 | class Maybe_CopyMove_Enabler<T, false, false, true> { | ||||||||||
| 203 | public: | ||||||||||
| 204 | Maybe_CopyMove_Enabler() = default; | ||||||||||
| 205 | |||||||||||
| 206 | Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler&) = delete; | ||||||||||
| 207 | Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler&) = delete; | ||||||||||
| 208 | MOZ_MAYBE_MOVE_OPS() | ||||||||||
| 209 | |||||||||||
| 210 | private: | ||||||||||
| 211 | MOZ_MAYBE_DOWNCAST() | ||||||||||
| 212 | }; | ||||||||||
| 213 | |||||||||||
| 214 | template <typename T> | ||||||||||
| 215 | class Maybe_CopyMove_Enabler<T, false, true, false> { | ||||||||||
| 216 | public: | ||||||||||
| 217 | Maybe_CopyMove_Enabler() = default; | ||||||||||
| 218 | |||||||||||
| 219 | MOZ_MAYBE_COPY_OPS() | ||||||||||
| 220 | Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&&) = delete; | ||||||||||
| 221 | Maybe_CopyMove_Enabler& operator=(Maybe_CopyMove_Enabler&&) = delete; | ||||||||||
| 222 | |||||||||||
| 223 | private: | ||||||||||
| 224 | MOZ_MAYBE_DOWNCAST() | ||||||||||
| 225 | }; | ||||||||||
| 226 | |||||||||||
| 227 | template <typename T, bool TriviallyDestructibleAndCopyable> | ||||||||||
| 228 | class Maybe_CopyMove_Enabler<T, TriviallyDestructibleAndCopyable, false, | ||||||||||
| 229 | false> { | ||||||||||
| 230 | public: | ||||||||||
| 231 | Maybe_CopyMove_Enabler() = default; | ||||||||||
| 232 | |||||||||||
| 233 | Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler&) = delete; | ||||||||||
| 234 | Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler&) = delete; | ||||||||||
| 235 | Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&&) = delete; | ||||||||||
| 236 | Maybe_CopyMove_Enabler& operator=(Maybe_CopyMove_Enabler&&) = delete; | ||||||||||
| 237 | }; | ||||||||||
| 238 | |||||||||||
| 239 | #undef MOZ_MAYBE_COPY_OPS | ||||||||||
| 240 | #undef MOZ_MAYBE_MOVE_OPS | ||||||||||
| 241 | #undef MOZ_MAYBE_DOWNCAST | ||||||||||
| 242 | |||||||||||
| 243 | template <typename T, bool TriviallyDestructibleAndCopyable = | ||||||||||
| 244 | IsTriviallyDestructibleAndCopyable<T>> | ||||||||||
| 245 | struct MaybeStorage; | ||||||||||
| 246 | |||||||||||
| 247 | template <typename T> | ||||||||||
| 248 | struct MaybeStorage<T, false> : MaybeStorageBase<T> { | ||||||||||
| 249 | protected: | ||||||||||
| 250 | char mIsSome = false; // not bool -- guarantees minimal space consumption | ||||||||||
| 251 | |||||||||||
| 252 | constexpr MaybeStorage() = default; | ||||||||||
| 253 | explicit MaybeStorage(const T& aVal) | ||||||||||
| 254 | : MaybeStorageBase<T>{aVal}, mIsSome{true} {} | ||||||||||
| 255 | explicit MaybeStorage(T&& aVal) | ||||||||||
| 256 | : MaybeStorageBase<T>{std::move(aVal)}, mIsSome{true} {} | ||||||||||
| 257 | |||||||||||
| 258 | template <typename... Args> | ||||||||||
| 259 | explicit MaybeStorage(std::in_place_t, Args&&... aArgs) | ||||||||||
| 260 | : MaybeStorageBase<T>{std::in_place, std::forward<Args>(aArgs)...}, | ||||||||||
| 261 | mIsSome{true} {} | ||||||||||
| 262 | |||||||||||
| 263 | public: | ||||||||||
| 264 | // Copy and move operations are no-ops, since copying is moving is implemented | ||||||||||
| 265 | // by Maybe_CopyMove_Enabler. | ||||||||||
| 266 | |||||||||||
| 267 | MaybeStorage(const MaybeStorage&) : MaybeStorageBase<T>{} {} | ||||||||||
| 268 | MaybeStorage& operator=(const MaybeStorage&) { return *this; } | ||||||||||
| 269 | MaybeStorage(MaybeStorage&&) : MaybeStorageBase<T>{} {} | ||||||||||
| 270 | MaybeStorage& operator=(MaybeStorage&&) { return *this; } | ||||||||||
| 271 | |||||||||||
| 272 | ~MaybeStorage() { | ||||||||||
| 273 | if (mIsSome 
 
 
 
 
 | ||||||||||
| 274 | this->addr()->T::~T(); | ||||||||||
| 275 | } | ||||||||||
| 276 | } | ||||||||||
| 277 | }; | ||||||||||
| 278 | |||||||||||
| 279 | template <typename T> | ||||||||||
| 280 | struct MaybeStorage<T, true> : MaybeStorageBase<T> { | ||||||||||
| 281 | protected: | ||||||||||
| 282 | char mIsSome = false; // not bool -- guarantees minimal space consumption | ||||||||||
| 283 | |||||||||||
| 284 | constexpr MaybeStorage() = default; | ||||||||||
| 285 | constexpr explicit MaybeStorage(const T& aVal) | ||||||||||
| 286 | : MaybeStorageBase<T>{aVal}, mIsSome{true} {} | ||||||||||
| 287 | constexpr explicit MaybeStorage(T&& aVal) | ||||||||||
| 288 | : MaybeStorageBase<T>{std::move(aVal)}, mIsSome{true} {} | ||||||||||
| 289 | |||||||||||
| 290 | template <typename... Args> | ||||||||||
| 291 | constexpr explicit MaybeStorage(std::in_place_t, Args&&... aArgs) | ||||||||||
| 292 | : MaybeStorageBase<T>{std::in_place, std::forward<Args>(aArgs)...}, | ||||||||||
| 293 | mIsSome{true} {} | ||||||||||
| 294 | }; | ||||||||||
| 295 | |||||||||||
| 296 | template <typename T> | ||||||||||
| 297 | struct IsMaybeImpl : std::false_type {}; | ||||||||||
| 298 | |||||||||||
| 299 | template <typename T> | ||||||||||
| 300 | struct IsMaybeImpl<Maybe<T>> : std::true_type {}; | ||||||||||
| 301 | |||||||||||
| 302 | template <typename T> | ||||||||||
| 303 | using IsMaybe = IsMaybeImpl<std::decay_t<T>>; | ||||||||||
| 304 | |||||||||||
| 305 | } // namespace detail | ||||||||||
| 306 | |||||||||||
| 307 | template <typename T, typename U = typename std::remove_cv< | ||||||||||
| 308 | typename std::remove_reference<T>::type>::type> | ||||||||||
| 309 | constexpr Maybe<U> Some(T&& aValue); | ||||||||||
| 310 | |||||||||||
| 311 | /* | ||||||||||
| 312 | * Maybe is a container class which contains either zero or one elements. It | ||||||||||
| 313 | * serves two roles. It can represent values which are *semantically* optional, | ||||||||||
| 314 | * augmenting a type with an explicit 'Nothing' value. In this role, it provides | ||||||||||
| 315 | * methods that make it easy to work with values that may be missing, along with | ||||||||||
| 316 | * equality and comparison operators so that Maybe values can be stored in | ||||||||||
| 317 | * containers. Maybe values can be constructed conveniently in expressions using | ||||||||||
| 318 | * type inference, as follows: | ||||||||||
| 319 | * | ||||||||||
| 320 | * void doSomething(Maybe<Foo> aFoo) { | ||||||||||
| 321 | * if (aFoo) // Make sure that aFoo contains a value... | ||||||||||
| 322 | * aFoo->takeAction(); // and then use |aFoo->| to access it. | ||||||||||
| 323 | * } // |*aFoo| also works! | ||||||||||
| 324 | * | ||||||||||
| 325 | * doSomething(Nothing()); // Passes a Maybe<Foo> containing no value. | ||||||||||
| 326 | * doSomething(Some(Foo(100))); // Passes a Maybe<Foo> containing |Foo(100)|. | ||||||||||
| 327 | * | ||||||||||
| 328 | * You'll note that it's important to check whether a Maybe contains a value | ||||||||||
| 329 | * before using it, using conversion to bool, |isSome()|, or |isNothing()|. You | ||||||||||
| 330 | * can avoid these checks, and sometimes write more readable code, using | ||||||||||
| 331 | * |valueOr()|, |ptrOr()|, and |refOr()|, which allow you to retrieve the value | ||||||||||
| 332 | * in the Maybe and provide a default for the 'Nothing' case. You can also use | ||||||||||
| 333 | * |apply()| to call a function only if the Maybe holds a value, and |map()| to | ||||||||||
| 334 | * transform the value in the Maybe, returning another Maybe with a possibly | ||||||||||
| 335 | * different type. | ||||||||||
| 336 | * | ||||||||||
| 337 | * Maybe's other role is to support lazily constructing objects without using | ||||||||||
| 338 | * dynamic storage. A Maybe directly contains storage for a value, but it's | ||||||||||
| 339 | * empty by default. |emplace()|, as mentioned above, can be used to construct a | ||||||||||
| 340 | * value in Maybe's storage. The value a Maybe contains can be destroyed by | ||||||||||
| 341 | * calling |reset()|; this will happen automatically if a Maybe is destroyed | ||||||||||
| 342 | * while holding a value. | ||||||||||
| 343 | * | ||||||||||
| 344 | * It's a common idiom in C++ to use a pointer as a 'Maybe' type, with a null | ||||||||||
| 345 | * value meaning 'Nothing' and any other value meaning 'Some'. You can convert | ||||||||||
| 346 | * from such a pointer to a Maybe value using 'ToMaybe()'. | ||||||||||
| 347 | * | ||||||||||
| 348 | * Maybe is inspired by similar types in the standard library of many other | ||||||||||
| 349 | * languages (e.g. Haskell's Maybe and Rust's Option). In the C++ world it's | ||||||||||
| 350 | * very similar to std::optional, which was proposed for C++14 and originated in | ||||||||||
| 351 | * Boost. The most important differences between Maybe and std::optional are: | ||||||||||
| 352 | * | ||||||||||
| 353 | * - std::optional<T> may be compared with T. We deliberately forbid that. | ||||||||||
| 354 | * - std::optional has |valueOr()|, equivalent to Maybe's |valueOr()|, but | ||||||||||
| 355 | * lacks corresponding methods for |refOr()| and |ptrOr()|. | ||||||||||
| 356 | * - std::optional lacks |map()| and |apply()|, making it less suitable for | ||||||||||
| 357 | * functional-style code. | ||||||||||
| 358 | * - std::optional lacks many convenience functions that Maybe has. Most | ||||||||||
| 359 | * unfortunately, it lacks equivalents of the type-inferred constructor | ||||||||||
| 360 | * functions |Some()| and |Nothing()|. | ||||||||||
| 361 | */ | ||||||||||
| 362 | template <class T> | ||||||||||
| 363 | class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe | ||||||||||
| 364 | : private detail::MaybeStorage<T>, | ||||||||||
| 365 | public detail::Maybe_CopyMove_Enabler<T> { | ||||||||||
| 366 | template <typename, bool, bool, bool> | ||||||||||
| 367 | friend class detail::Maybe_CopyMove_Enabler; | ||||||||||
| 368 | |||||||||||
| 369 | template <typename U, typename V> | ||||||||||
| 370 | friend constexpr Maybe<V> Some(U&& aValue); | ||||||||||
| 371 | |||||||||||
| 372 | struct SomeGuard {}; | ||||||||||
| 373 | |||||||||||
| 374 | template <typename U> | ||||||||||
| 375 | constexpr Maybe(U&& aValue, SomeGuard) | ||||||||||
| 376 | : detail::MaybeStorage<T>{std::forward<U>(aValue)} {} | ||||||||||
| 377 | |||||||||||
| 378 | using detail::MaybeStorage<T>::mIsSome; | ||||||||||
| 379 | using detail::MaybeStorage<T>::mStorage; | ||||||||||
| 380 | |||||||||||
| 381 | void poisonData() { detail::MaybePoisoner<T>::poison(&mStorage.val); } | ||||||||||
| 382 | |||||||||||
| 383 | public: | ||||||||||
| 384 | using ValueType = T; | ||||||||||
| 385 | |||||||||||
| 386 | MOZ_ALLOW_TEMPORARY constexpr Maybe() = default; | ||||||||||
| 387 | |||||||||||
| 388 | MOZ_ALLOW_TEMPORARY MOZ_IMPLICIT constexpr Maybe(Nothing) : Maybe{} {} | ||||||||||
| 389 | |||||||||||
| 390 | template <typename... Args> | ||||||||||
| 391 | constexpr explicit Maybe(std::in_place_t, Args&&... aArgs) | ||||||||||
| 392 | : detail::MaybeStorage<T>{std::in_place, std::forward<Args>(aArgs)...} {} | ||||||||||
| 393 | |||||||||||
| 394 | /** | ||||||||||
| 395 | * Maybe<T> can be copy-constructed from a Maybe<U> if T is constructible from | ||||||||||
| 396 | * a const U&. | ||||||||||
| 397 | */ | ||||||||||
| 398 | template <typename U, | ||||||||||
| 399 | std::enable_if_t<std::is_constructible_v<T, const U&>, bool> = true> | ||||||||||
| 400 | MOZ_IMPLICIT Maybe(const Maybe<U>& aOther) { | ||||||||||
| 401 | if (aOther.isSome()) { | ||||||||||
| 402 | emplace(*aOther); | ||||||||||
| 403 | } | ||||||||||
| 404 | } | ||||||||||
| 405 | |||||||||||
| 406 | template <typename U, std::enable_if_t<!std::is_constructible_v<T, const U&>, | ||||||||||
| 407 | bool> = true> | ||||||||||
| 408 | explicit Maybe(const Maybe<U>& aOther) = delete; | ||||||||||
| 409 | |||||||||||
| 410 | /** | ||||||||||
| 411 | * Maybe<T> can be move-constructed from a Maybe<U> if T is constructible from | ||||||||||
| 412 | * a U&&. | ||||||||||
| 413 | */ | ||||||||||
| 414 | template <typename U, | ||||||||||
| 415 | std::enable_if_t<std::is_constructible_v<T, U&&>, bool> = true> | ||||||||||
| 416 | MOZ_IMPLICIT Maybe(Maybe<U>&& aOther) { | ||||||||||
| 417 | if (aOther.isSome()) { | ||||||||||
| 418 | emplace(std::move(*aOther)); | ||||||||||
| 419 | aOther.reset(); | ||||||||||
| 420 | } | ||||||||||
| 421 | } | ||||||||||
| 422 | template <typename U, | ||||||||||
| 423 | std::enable_if_t<!std::is_constructible_v<T, U&&>, bool> = true> | ||||||||||
| 424 | explicit Maybe(Maybe<U>&& aOther) = delete; | ||||||||||
| 425 | |||||||||||
| 426 | template <typename U, | ||||||||||
| 427 | std::enable_if_t<std::is_constructible_v<T, const U&>, bool> = true> | ||||||||||
| 428 | Maybe& operator=(const Maybe<U>& aOther) { | ||||||||||
| 429 | if (aOther.isSome()) { | ||||||||||
| 430 | if (mIsSome) { | ||||||||||
| 431 | ref() = aOther.ref(); | ||||||||||
| 432 | } else { | ||||||||||
| 433 | emplace(*aOther); | ||||||||||
| 434 | } | ||||||||||
| 435 | } else { | ||||||||||
| 436 | reset(); | ||||||||||
| 437 | } | ||||||||||
| 438 | return *this; | ||||||||||
| 439 | } | ||||||||||
| 440 | |||||||||||
| 441 | template <typename U, std::enable_if_t<!std::is_constructible_v<T, const U&>, | ||||||||||
| 442 | bool> = true> | ||||||||||
| 443 | Maybe& operator=(const Maybe<U>& aOther) = delete; | ||||||||||
| 444 | |||||||||||
| 445 | template <typename U, | ||||||||||
| 446 | std::enable_if_t<std::is_constructible_v<T, U&&>, bool> = true> | ||||||||||
| 447 | Maybe& operator=(Maybe<U>&& aOther) { | ||||||||||
| 448 | if (aOther.isSome()) { | ||||||||||
| 449 | if (mIsSome) { | ||||||||||
| 450 | ref() = std::move(aOther.ref()); | ||||||||||
| 451 | } else { | ||||||||||
| 452 | emplace(std::move(*aOther)); | ||||||||||
| 453 | } | ||||||||||
| 454 | aOther.reset(); | ||||||||||
| 455 | } else { | ||||||||||
| 456 | reset(); | ||||||||||
| 457 | } | ||||||||||
| 458 | |||||||||||
| 459 | return *this; | ||||||||||
| 460 | } | ||||||||||
| 461 | |||||||||||
| 462 | template <typename U, | ||||||||||
| 463 | std::enable_if_t<!std::is_constructible_v<T, U&&>, bool> = true> | ||||||||||
| 464 | Maybe& operator=(Maybe<U>&& aOther) = delete; | ||||||||||
| 465 | |||||||||||
| 466 | constexpr Maybe& operator=(Nothing) { | ||||||||||
| 467 | reset(); | ||||||||||
| 468 | return *this; | ||||||||||
| 469 | } | ||||||||||
| 470 | |||||||||||
| 471 | /* Methods that check whether this Maybe contains a value */ | ||||||||||
| 472 | constexpr explicit operator bool() const { return isSome(); } | ||||||||||
| 473 | constexpr bool isSome() const { return mIsSome; } | ||||||||||
| 474 | constexpr bool isNothing() const { return !mIsSome; } | ||||||||||
| 475 | |||||||||||
| 476 | /* Returns the contents of this Maybe<T> by value. Unsafe unless |isSome()|. | ||||||||||
| 477 | */ | ||||||||||
| 478 | constexpr T value() const&; | ||||||||||
| 479 | constexpr T value() &&; | ||||||||||
| 480 | constexpr T value() const&&; | ||||||||||
| 481 | |||||||||||
| 482 | /** | ||||||||||
| 483 | * Move the contents of this Maybe<T> out of internal storage and return it | ||||||||||
| 484 | * without calling the destructor. The internal storage is also reset to | ||||||||||
| 485 | * avoid multiple calls. Unsafe unless |isSome()|. | ||||||||||
| 486 | */ | ||||||||||
| 487 | constexpr T extract() { | ||||||||||
| 488 | MOZ_RELEASE_ASSERT(isSome())do { static_assert( mozilla::detail::AssertionConditionType< decltype(isSome())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(isSome()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("isSome()", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/Maybe.h" , 488); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "isSome()" ")"); do { *((volatile int*)__null) = 488; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | ||||||||||
| 489 | T v = std::move(mStorage.val); | ||||||||||
| 490 | reset(); | ||||||||||
| 491 | return v; | ||||||||||
| 492 | } | ||||||||||
| 493 | |||||||||||
| 494 | /** | ||||||||||
| 495 | * Returns the value (possibly |Nothing()|) by moving it out of this Maybe<T> | ||||||||||
| 496 | * and leaving |Nothing()| in its place. | ||||||||||
| 497 | */ | ||||||||||
| 498 | Maybe<T> take() { return std::exchange(*this, Nothing()); } | ||||||||||
| 499 | |||||||||||
| 500 | /* | ||||||||||
| 501 | * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns | ||||||||||
| 502 | * the default value provided. | ||||||||||
| 503 | * | ||||||||||
| 504 | * Note: If the value passed to aDefault is not the result of a trivial | ||||||||||
| 505 | * expression, but expensive to evaluate, e.g. |valueOr(ExpensiveFunction())|, | ||||||||||
| 506 | * use |valueOrFrom| instead, e.g. | ||||||||||
| 507 | * |valueOrFrom([arg] { return ExpensiveFunction(arg); })|. This ensures | ||||||||||
| 508 | * that the expensive expression is only evaluated when its result will | ||||||||||
| 509 | * actually be used. | ||||||||||
| 510 | */ | ||||||||||
| 511 | template <typename V> | ||||||||||
| 512 | constexpr T valueOr(V&& aDefault) const { | ||||||||||
| 513 | if (isSome()) { | ||||||||||
| 514 | return ref(); | ||||||||||
| 515 | } | ||||||||||
| 516 | return std::forward<V>(aDefault); | ||||||||||
| 517 | } | ||||||||||
| 518 | |||||||||||
| 519 | /* | ||||||||||
| 520 | * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns | ||||||||||
| 521 | * the value returned from the function or functor provided. | ||||||||||
| 522 | */ | ||||||||||
| 523 | template <typename F> | ||||||||||
| 524 | constexpr T valueOrFrom(F&& aFunc) const { | ||||||||||
| 525 | if (isSome()) { | ||||||||||
| 526 | return ref(); | ||||||||||
| 527 | } | ||||||||||
| 528 | return aFunc(); | ||||||||||
| 529 | } | ||||||||||
| 530 | |||||||||||
| 531 | /* Returns the contents of this Maybe<T> by pointer. Unsafe unless |isSome()|. | ||||||||||
| 532 | */ | ||||||||||
| 533 | T* ptr(); | ||||||||||
| 534 | constexpr const T* ptr() const; | ||||||||||
| 535 | |||||||||||
| 536 | /* | ||||||||||
| 537 | * Returns the contents of this Maybe<T> by pointer. If |isNothing()|, | ||||||||||
| 538 | * returns the default value provided. | ||||||||||
| 539 | */ | ||||||||||
| 540 | T* ptrOr(T* aDefault) { | ||||||||||
| 541 | if (isSome()) { | ||||||||||
| 542 | return ptr(); | ||||||||||
| 543 | } | ||||||||||
| 544 | return aDefault; | ||||||||||
| 545 | } | ||||||||||
| 546 | |||||||||||
| 547 | constexpr const T* ptrOr(const T* aDefault) const { | ||||||||||
| 548 | if (isSome()) { | ||||||||||
| 549 | return ptr(); | ||||||||||
| 550 | } | ||||||||||
| 551 | return aDefault; | ||||||||||
| 552 | } | ||||||||||
| 553 | |||||||||||
| 554 | /* | ||||||||||
| 555 | * Returns the contents of this Maybe<T> by pointer. If |isNothing()|, | ||||||||||
| 556 | * returns the value returned from the function or functor provided. | ||||||||||
| 557 | */ | ||||||||||
| 558 | template <typename F> | ||||||||||
| 559 | T* ptrOrFrom(F&& aFunc) { | ||||||||||
| 560 | if (isSome()) { | ||||||||||
| 561 | return ptr(); | ||||||||||
| 562 | } | ||||||||||
| 563 | return aFunc(); | ||||||||||
| 564 | } | ||||||||||
| 565 | |||||||||||
| 566 | template <typename F> | ||||||||||
| 567 | const T* ptrOrFrom(F&& aFunc) const { | ||||||||||
| 568 | if (isSome()) { | ||||||||||
| 569 | return ptr(); | ||||||||||
| 570 | } | ||||||||||
| 571 | return aFunc(); | ||||||||||
| 572 | } | ||||||||||
| 573 | |||||||||||
| 574 | constexpr T* operator->(); | ||||||||||
| 575 | constexpr const T* operator->() const; | ||||||||||
| 576 | |||||||||||
| 577 | /* Returns the contents of this Maybe<T> by ref. Unsafe unless |isSome()|. */ | ||||||||||
| 578 | constexpr T& ref() &; | ||||||||||
| 579 | constexpr const T& ref() const&; | ||||||||||
| 580 | constexpr T&& ref() &&; | ||||||||||
| 581 | constexpr const T&& ref() const&&; | ||||||||||
| 582 | |||||||||||
| 583 | /* | ||||||||||
| 584 | * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns | ||||||||||
| 585 | * the default value provided. | ||||||||||
| 586 | */ | ||||||||||
| 587 | constexpr T& refOr(T& aDefault) { | ||||||||||
| 588 | if (isSome()) { | ||||||||||
| 589 | return ref(); | ||||||||||
| 590 | } | ||||||||||
| 591 | return aDefault; | ||||||||||
| 592 | } | ||||||||||
| 593 | |||||||||||
| 594 | constexpr const T& refOr(const T& aDefault) const { | ||||||||||
| 595 | if (isSome()) { | ||||||||||
| 596 | return ref(); | ||||||||||
| 597 | } | ||||||||||
| 598 | return aDefault; | ||||||||||
| 599 | } | ||||||||||
| 600 | |||||||||||
| 601 | /* | ||||||||||
| 602 | * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns the | ||||||||||
| 603 | * value returned from the function or functor provided. | ||||||||||
| 604 | */ | ||||||||||
| 605 | template <typename F> | ||||||||||
| 606 | constexpr T& refOrFrom(F&& aFunc) { | ||||||||||
| 607 | if (isSome()) { | ||||||||||
| 608 | return ref(); | ||||||||||
| 609 | } | ||||||||||
| 610 | return aFunc(); | ||||||||||
| 611 | } | ||||||||||
| 612 | |||||||||||
| 613 | template <typename F> | ||||||||||
| 614 | constexpr const T& refOrFrom(F&& aFunc) const { | ||||||||||
| 615 | if (isSome()) { | ||||||||||
| 616 | return ref(); | ||||||||||
| 617 | } | ||||||||||
| 618 | return aFunc(); | ||||||||||
| 619 | } | ||||||||||
| 620 | |||||||||||
| 621 | constexpr T& operator*() &; | ||||||||||
| 622 | constexpr const T& operator*() const&; | ||||||||||
| 623 | constexpr T&& operator*() &&; | ||||||||||
| 624 | constexpr const T&& operator*() const&&; | ||||||||||
| 625 | |||||||||||
| 626 | /* If |isSome()|, runs the provided function or functor on the contents of | ||||||||||
| 627 | * this Maybe. */ | ||||||||||
| 628 | template <typename Func> | ||||||||||
| 629 | constexpr Maybe& apply(Func&& aFunc) & { | ||||||||||
| 630 | if (isSome()) { | ||||||||||
| 631 | std::forward<Func>(aFunc)(ref()); | ||||||||||
| 632 | } | ||||||||||
| 633 | return *this; | ||||||||||
| 634 | } | ||||||||||
| 635 | |||||||||||
| 636 | template <typename Func> | ||||||||||
| 637 | constexpr const Maybe& apply(Func&& aFunc) const& { | ||||||||||
| 638 | if (isSome()) { | ||||||||||
| 639 | std::forward<Func>(aFunc)(ref()); | ||||||||||
| 640 | } | ||||||||||
| 641 | return *this; | ||||||||||
| 642 | } | ||||||||||
| 643 | |||||||||||
| 644 | template <typename Func> | ||||||||||
| 645 | constexpr Maybe& apply(Func&& aFunc) && { | ||||||||||
| 646 | if (isSome()) { | ||||||||||
| 647 | std::forward<Func>(aFunc)(extract()); | ||||||||||
| 648 | } | ||||||||||
| 649 | return *this; | ||||||||||
| 650 | } | ||||||||||
| 651 | |||||||||||
| 652 | template <typename Func> | ||||||||||
| 653 | constexpr Maybe& apply(Func&& aFunc) const&& { | ||||||||||
| 654 | if (isSome()) { | ||||||||||
| 655 | std::forward<Func>(aFunc)(extract()); | ||||||||||
| 656 | } | ||||||||||
| 657 | return *this; | ||||||||||
| 658 | } | ||||||||||
| 659 | |||||||||||
| 660 | /* | ||||||||||
| 661 | * If |isSome()|, runs the provided function and returns the result wrapped | ||||||||||
| 662 | * in a Maybe. If |isNothing()|, returns an empty Maybe value with the same | ||||||||||
| 663 | * value type as what the provided function would have returned. | ||||||||||
| 664 | */ | ||||||||||
| 665 | template <typename Func> | ||||||||||
| 666 | constexpr auto map(Func&& aFunc) & { | ||||||||||
| 667 | if (isSome()) { | ||||||||||
| 668 | return Some(std::forward<Func>(aFunc)(ref())); | ||||||||||
| 669 | } | ||||||||||
| 670 | return Maybe<decltype(std::forward<Func>(aFunc)(ref()))>{}; | ||||||||||
| 671 | } | ||||||||||
| 672 | |||||||||||
| 673 | template <typename Func> | ||||||||||
| 674 | constexpr auto map(Func&& aFunc) const& { | ||||||||||
| 675 | if (isSome()) { | ||||||||||
| 676 | return Some(std::forward<Func>(aFunc)(ref())); | ||||||||||
| 677 | } | ||||||||||
| 678 | return Maybe<decltype(std::forward<Func>(aFunc)(ref()))>{}; | ||||||||||
| 679 | } | ||||||||||
| 680 | |||||||||||
| 681 | template <typename Func> | ||||||||||
| 682 | constexpr auto map(Func&& aFunc) && { | ||||||||||
| 683 | if (isSome()) { | ||||||||||
| 684 | return Some(std::forward<Func>(aFunc)(extract())); | ||||||||||
| 685 | } | ||||||||||
| 686 | return Maybe<decltype(std::forward<Func>(aFunc)(extract()))>{}; | ||||||||||
| 687 | } | ||||||||||
| 688 | |||||||||||
| 689 | template <typename Func> | ||||||||||
| 690 | constexpr auto map(Func&& aFunc) const&& { | ||||||||||
| 691 | if (isSome()) { | ||||||||||
| 692 | return Some(std::forward<Func>(aFunc)(extract())); | ||||||||||
| 693 | } | ||||||||||
| 694 | return Maybe<decltype(std::forward<Func>(aFunc)(extract()))>{}; | ||||||||||
| 695 | } | ||||||||||
| 696 | |||||||||||
| 697 | /* | ||||||||||
| 698 | * If |isSome()|, runs the provided function or functor on the contents of | ||||||||||
| 699 | * this Maybe and returns the result. Note that the provided function or | ||||||||||
| 700 | * functor must return a Maybe<U> of any type U. | ||||||||||
| 701 | * If |isNothing()|, returns an empty Maybe value with the same type as what | ||||||||||
| 702 | * the provided function would have returned. | ||||||||||
| 703 | */ | ||||||||||
| 704 | template <typename Func> | ||||||||||
| 705 | constexpr auto andThen(Func&& aFunc) & { | ||||||||||
| 706 | static_assert(std::is_invocable_v<Func, T&>); | ||||||||||
| 707 | using U = std::invoke_result_t<Func, T&>; | ||||||||||
| 708 | static_assert(detail::IsMaybe<U>::value); | ||||||||||
| 709 | if (isSome()) { | ||||||||||
| 710 | return std::invoke(std::forward<Func>(aFunc), ref()); | ||||||||||
| 711 | } | ||||||||||
| 712 | return std::remove_cv_t<std::remove_reference_t<U>>{}; | ||||||||||
| 713 | } | ||||||||||
| 714 | |||||||||||
| 715 | template <typename Func> | ||||||||||
| 716 | constexpr auto andThen(Func&& aFunc) const& { | ||||||||||
| 717 | static_assert(std::is_invocable_v<Func, const T&>); | ||||||||||
| 718 | using U = std::invoke_result_t<Func, const T&>; | ||||||||||
| 719 | static_assert(detail::IsMaybe<U>::value); | ||||||||||
| 720 | if (isSome()) { | ||||||||||
| 721 | return std::invoke(std::forward<Func>(aFunc), ref()); | ||||||||||
| 722 | } | ||||||||||
| 723 | return std::remove_cv_t<std::remove_reference_t<U>>{}; | ||||||||||
| 724 | } | ||||||||||
| 725 | |||||||||||
| 726 | template <typename Func> | ||||||||||
| 727 | constexpr auto andThen(Func&& aFunc) && { | ||||||||||
| 728 | static_assert(std::is_invocable_v<Func, T&&>); | ||||||||||
| 729 | using U = std::invoke_result_t<Func, T&&>; | ||||||||||
| 730 | static_assert(detail::IsMaybe<U>::value); | ||||||||||
| 731 | if (isSome()) { | ||||||||||
| 732 | return std::invoke(std::forward<Func>(aFunc), extract()); | ||||||||||
| 733 | } | ||||||||||
| 734 | return std::remove_cv_t<std::remove_reference_t<U>>{}; | ||||||||||
| 735 | } | ||||||||||
| 736 | |||||||||||
| 737 | template <typename Func> | ||||||||||
| 738 | constexpr auto andThen(Func&& aFunc) const&& { | ||||||||||
| 739 | static_assert(std::is_invocable_v<Func, const T&&>); | ||||||||||
| 740 | using U = std::invoke_result_t<Func, const T&&>; | ||||||||||
| 741 | static_assert(detail::IsMaybe<U>::value); | ||||||||||
| 742 | if (isSome()) { | ||||||||||
| 743 | return std::invoke(std::forward<Func>(aFunc), extract()); | ||||||||||
| 744 | } | ||||||||||
| 745 | return std::remove_cv_t<std::remove_reference_t<U>>{}; | ||||||||||
| 746 | } | ||||||||||
| 747 | |||||||||||
| 748 | /* | ||||||||||
| 749 | * If |isNothing()|, runs the provided function or functor and returns its | ||||||||||
| 750 | * result. If |isSome()|, returns the contained value wrapped in a Maybe. | ||||||||||
| 751 | */ | ||||||||||
| 752 | template <typename Func> | ||||||||||
| 753 | constexpr Maybe orElse(Func&& aFunc) & { | ||||||||||
| 754 | static_assert(std::is_invocable_v<Func>); | ||||||||||
| 755 | using U = std::invoke_result_t<Func>; | ||||||||||
| 756 | static_assert( | ||||||||||
| 757 | std::is_same_v<Maybe, std::remove_cv_t<std::remove_reference_t<U>>>); | ||||||||||
| 758 | if (isSome()) { | ||||||||||
| 759 | return *this; | ||||||||||
| 760 | } | ||||||||||
| 761 | return std::invoke(std::forward<Func>(aFunc)); | ||||||||||
| 762 | } | ||||||||||
| 763 | |||||||||||
| 764 | template <typename Func> | ||||||||||
| 765 | constexpr Maybe orElse(Func&& aFunc) const& { | ||||||||||
| 766 | static_assert(std::is_invocable_v<Func>); | ||||||||||
| 767 | using U = std::invoke_result_t<Func>; | ||||||||||
| 768 | static_assert( | ||||||||||
| 769 | std::is_same_v<Maybe, std::remove_cv_t<std::remove_reference_t<U>>>); | ||||||||||
| 770 | if (isSome()) { | ||||||||||
| 771 | return *this; | ||||||||||
| 772 | } | ||||||||||
| 773 | return std::invoke(std::forward<Func>(aFunc)); | ||||||||||
| 774 | } | ||||||||||
| 775 | |||||||||||
| 776 | template <typename Func> | ||||||||||
| 777 | constexpr Maybe orElse(Func&& aFunc) && { | ||||||||||
| 778 | static_assert(std::is_invocable_v<Func>); | ||||||||||
| 779 | using U = std::invoke_result_t<Func>; | ||||||||||
| 780 | static_assert( | ||||||||||
| 781 | std::is_same_v<Maybe, std::remove_cv_t<std::remove_reference_t<U>>>); | ||||||||||
| 782 | if (isSome()) { | ||||||||||
| 783 | return std::move(*this); | ||||||||||
| 784 | } | ||||||||||
| 785 | return std::invoke(std::forward<Func>(aFunc)); | ||||||||||
| 786 | } | ||||||||||
| 787 | |||||||||||
| 788 | template <typename Func> | ||||||||||
| 789 | constexpr Maybe orElse(Func&& aFunc) const&& { | ||||||||||
| 790 | static_assert(std::is_invocable_v<Func>); | ||||||||||
| 791 | using U = std::invoke_result_t<Func>; | ||||||||||
| 792 | static_assert( | ||||||||||
| 793 | std::is_same_v<Maybe, std::remove_cv_t<std::remove_reference_t<U>>>); | ||||||||||
| 794 | if (isSome()) { | ||||||||||
| 795 | return std::move(*this); | ||||||||||
| 796 | } | ||||||||||
| 797 | return std::invoke(std::forward<Func>(aFunc)); | ||||||||||
| 798 | } | ||||||||||
| 799 | |||||||||||
| 800 | /* If |isSome()|, empties this Maybe and destroys its contents. */ | ||||||||||
| 801 | constexpr void reset() { | ||||||||||
| 802 | if (isSome()) { | ||||||||||
| 803 | if constexpr (!std::is_trivially_destructible_v<T>) { | ||||||||||
| 804 | /* | ||||||||||
| 805 | * Static analyzer gets confused if we have Maybe<MutexAutoLock>, | ||||||||||
| 806 | * so we suppress thread-safety warnings here | ||||||||||
| 807 | */ | ||||||||||
| 808 | MOZ_PUSH_IGNORE_THREAD_SAFETYGCC diagnostic push
 GCC diagnostic ignored "-Wthread-safety" | ||||||||||
| 809 | ref().T::~T(); | ||||||||||
| 810 | MOZ_POP_THREAD_SAFETYGCC diagnostic pop | ||||||||||
| 811 | poisonData(); | ||||||||||
| 812 | } | ||||||||||
| 813 | mIsSome = false; | ||||||||||
| 814 | } | ||||||||||
| 815 | } | ||||||||||
| 816 | |||||||||||
| 817 | /* | ||||||||||
| 818 | * Constructs a T value in-place in this empty Maybe<T>'s storage. The | ||||||||||
| 819 | * arguments to |emplace()| are the parameters to T's constructor. | ||||||||||
| 820 | */ | ||||||||||
| 821 | template <typename... Args> | ||||||||||
| 822 | constexpr void emplace(Args&&... aArgs); | ||||||||||
| 823 | |||||||||||
| 824 | template <typename U> | ||||||||||
| 825 | constexpr std::enable_if_t<std::is_same_v<T, U> && | ||||||||||
| 826 | std::is_copy_constructible_v<U> && | ||||||||||
| 827 | !std::is_move_constructible_v<U>> | ||||||||||
| 828 | emplace(U&& aArgs) { | ||||||||||
| 829 | emplace(aArgs); | ||||||||||
| 830 | } | ||||||||||
| 831 | |||||||||||
| 832 | friend std::ostream& operator<<(std::ostream& aStream, | ||||||||||
| 833 | const Maybe<T>& aMaybe) { | ||||||||||
| 834 | if (aMaybe) { | ||||||||||
| 835 | aStream << aMaybe.ref(); | ||||||||||
| 836 | } else { | ||||||||||
| 837 | aStream << "<Nothing>"; | ||||||||||
| 838 | } | ||||||||||
| 839 | return aStream; | ||||||||||
| 840 | } | ||||||||||
| 841 | }; | ||||||||||
| 842 | |||||||||||
| 843 | template <typename T> | ||||||||||
| 844 | class Maybe<T&> { | ||||||||||
| 845 | public: | ||||||||||
| 846 | constexpr Maybe() = default; | ||||||||||
| 847 | constexpr MOZ_IMPLICIT Maybe(Nothing) {} | ||||||||||
| 848 | |||||||||||
| 849 | void emplace(T& aRef) { mValue = &aRef; } | ||||||||||
| 850 | |||||||||||
| 851 | /* Methods that check whether this Maybe contains a value */ | ||||||||||
| 852 | constexpr explicit operator bool() const { return isSome(); } | ||||||||||
| 853 | constexpr bool isSome() const { return mValue; } | ||||||||||
| 854 | constexpr bool isNothing() const { return !mValue; } | ||||||||||
| 855 | |||||||||||
| 856 | T& ref() const { | ||||||||||
| 857 | MOZ_RELEASE_ASSERT(isSome())do { static_assert( mozilla::detail::AssertionConditionType< decltype(isSome())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(isSome()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("isSome()", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/Maybe.h" , 857); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "isSome()" ")"); do { *((volatile int*)__null) = 857; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | ||||||||||
| 858 | return *mValue; | ||||||||||
| 859 | } | ||||||||||
| 860 | |||||||||||
| 861 | T* operator->() const { return &ref(); } | ||||||||||
| 862 | T& operator*() const { return ref(); } | ||||||||||
| 863 | |||||||||||
| 864 | // Deliberately not defining value and ptr accessors, as these may be | ||||||||||
| 865 | // confusing on a reference-typed Maybe. | ||||||||||
| 866 | |||||||||||
| 867 | // XXX Should we define refOr? | ||||||||||
| 868 | |||||||||||
| 869 | void reset() { mValue = nullptr; } | ||||||||||
| 870 | |||||||||||
| 871 | template <typename Func> | ||||||||||
| 872 | const Maybe& apply(Func&& aFunc) const { | ||||||||||
| 873 | if (isSome()) { | ||||||||||
| 874 | std::forward<Func>(aFunc)(ref()); | ||||||||||
| 875 | } | ||||||||||
| 876 | return *this; | ||||||||||
| 877 | } | ||||||||||
| 878 | |||||||||||
| 879 | template <typename Func> | ||||||||||
| 880 | auto map(Func&& aFunc) const { | ||||||||||
| 881 | Maybe<decltype(std::forward<Func>(aFunc)(ref()))> val; | ||||||||||
| 882 | if (isSome()) { | ||||||||||
| 883 | val.emplace(std::forward<Func>(aFunc)(ref())); | ||||||||||
| 884 | } | ||||||||||
| 885 | return val; | ||||||||||
| 886 | } | ||||||||||
| 887 | |||||||||||
| 888 | template <typename Func> | ||||||||||
| 889 | constexpr auto andThen(Func&& aFunc) const { | ||||||||||
| 890 | static_assert(std::is_invocable_v<Func, T&>); | ||||||||||
| 891 | using U = std::invoke_result_t<Func, T&>; | ||||||||||
| 892 | static_assert(detail::IsMaybe<U>::value); | ||||||||||
| 893 | if (isSome()) { | ||||||||||
| 894 | return std::invoke(std::forward<Func>(aFunc), ref()); | ||||||||||
| 895 | } | ||||||||||
| 896 | return std::remove_cv_t<std::remove_reference_t<U>>{}; | ||||||||||
| 897 | } | ||||||||||
| 898 | |||||||||||
| 899 | template <typename Func> | ||||||||||
| 900 | constexpr Maybe orElse(Func&& aFunc) const { | ||||||||||
| 901 | static_assert(std::is_invocable_v<Func>); | ||||||||||
| 902 | using U = std::invoke_result_t<Func>; | ||||||||||
| 903 | static_assert( | ||||||||||
| 904 | std::is_same_v<Maybe, std::remove_cv_t<std::remove_reference_t<U>>>); | ||||||||||
| 905 | if (isSome()) { | ||||||||||
| 906 | return *this; | ||||||||||
| 907 | } | ||||||||||
| 908 | return std::invoke(std::forward<Func>(aFunc)); | ||||||||||
| 909 | } | ||||||||||
| 910 | |||||||||||
| 911 | bool refEquals(const Maybe<T&>& aOther) const { | ||||||||||
| 912 | return mValue == aOther.mValue; | ||||||||||
| 913 | } | ||||||||||
| 914 | |||||||||||
| 915 | bool refEquals(const T& aOther) const { return mValue == &aOther; } | ||||||||||
| 916 | |||||||||||
| 917 | private: | ||||||||||
| 918 | T* mValue = nullptr; | ||||||||||
| 919 | }; | ||||||||||
| 920 | |||||||||||
| 921 | template <typename T> | ||||||||||
| 922 | constexpr T Maybe<T>::value() const& { | ||||||||||
| 923 | MOZ_RELEASE_ASSERT(isSome())do { static_assert( mozilla::detail::AssertionConditionType< decltype(isSome())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(isSome()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("isSome()", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/Maybe.h" , 923); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "isSome()" ")"); do { *((volatile int*)__null) = 923; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | ||||||||||
| 924 | return ref(); | ||||||||||
| 925 | } | ||||||||||
| 926 | |||||||||||
| 927 | template <typename T> | ||||||||||
| 928 | constexpr T Maybe<T>::value() && { | ||||||||||
| 929 | MOZ_RELEASE_ASSERT(isSome())do { static_assert( mozilla::detail::AssertionConditionType< decltype(isSome())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(isSome()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("isSome()", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/Maybe.h" , 929); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "isSome()" ")"); do { *((volatile int*)__null) = 929; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | ||||||||||
| 930 | return std::move(ref()); | ||||||||||
| 931 | } | ||||||||||
| 932 | |||||||||||
| 933 | template <typename T> | ||||||||||
| 934 | constexpr T Maybe<T>::value() const&& { | ||||||||||
| 935 | MOZ_RELEASE_ASSERT(isSome())do { static_assert( mozilla::detail::AssertionConditionType< decltype(isSome())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(isSome()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("isSome()", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/Maybe.h" , 935); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "isSome()" ")"); do { *((volatile int*)__null) = 935; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | ||||||||||
| 936 | return std::move(ref()); | ||||||||||
| 937 | } | ||||||||||
| 938 | |||||||||||
| 939 | template <typename T> | ||||||||||
| 940 | T* Maybe<T>::ptr() { | ||||||||||
| 941 | MOZ_RELEASE_ASSERT(isSome())do { static_assert( mozilla::detail::AssertionConditionType< decltype(isSome())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(isSome()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("isSome()", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/Maybe.h" , 941); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "isSome()" ")"); do { *((volatile int*)__null) = 941; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | ||||||||||
| 942 | return &ref(); | ||||||||||
| 943 | } | ||||||||||
| 944 | |||||||||||
| 945 | template <typename T> | ||||||||||
| 946 | constexpr const T* Maybe<T>::ptr() const { | ||||||||||
| 947 | MOZ_RELEASE_ASSERT(isSome())do { static_assert( mozilla::detail::AssertionConditionType< decltype(isSome())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(isSome()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("isSome()", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/Maybe.h" , 947); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "isSome()" ")"); do { *((volatile int*)__null) = 947; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | ||||||||||
| 948 | return &ref(); | ||||||||||
| 949 | } | ||||||||||
| 950 | |||||||||||
| 951 | template <typename T> | ||||||||||
| 952 | constexpr T* Maybe<T>::operator->() { | ||||||||||
| 953 | MOZ_RELEASE_ASSERT(isSome())do { static_assert( mozilla::detail::AssertionConditionType< decltype(isSome())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(isSome()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("isSome()", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/Maybe.h" , 953); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "isSome()" ")"); do { *((volatile int*)__null) = 953; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | ||||||||||
| 954 | return ptr(); | ||||||||||
| 955 | } | ||||||||||
| 956 | |||||||||||
| 957 | template <typename T> | ||||||||||
| 958 | constexpr const T* Maybe<T>::operator->() const { | ||||||||||
| 959 | MOZ_RELEASE_ASSERT(isSome())do { static_assert( mozilla::detail::AssertionConditionType< decltype(isSome())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(isSome()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("isSome()", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/Maybe.h" , 959); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "isSome()" ")"); do { *((volatile int*)__null) = 959; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | ||||||||||
| 960 | return ptr(); | ||||||||||
| 961 | } | ||||||||||
| 962 | |||||||||||
| 963 | template <typename T> | ||||||||||
| 964 | constexpr T& Maybe<T>::ref() & { | ||||||||||
| 965 | MOZ_RELEASE_ASSERT(isSome())do { static_assert( mozilla::detail::AssertionConditionType< decltype(isSome())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(isSome()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("isSome()", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/Maybe.h" , 965); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "isSome()" ")"); do { *((volatile int*)__null) = 965; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | ||||||||||
| 966 | return mStorage.val; | ||||||||||
| 967 | } | ||||||||||
| 968 | |||||||||||
| 969 | template <typename T> | ||||||||||
| 970 | constexpr const T& Maybe<T>::ref() const& { | ||||||||||
| 971 | MOZ_RELEASE_ASSERT(isSome())do { static_assert( mozilla::detail::AssertionConditionType< decltype(isSome())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(isSome()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("isSome()", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/Maybe.h" , 971); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "isSome()" ")"); do { *((volatile int*)__null) = 971; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | ||||||||||
| 972 | return mStorage.val; | ||||||||||
| 973 | } | ||||||||||
| 974 | |||||||||||
| 975 | template <typename T> | ||||||||||
| 976 | constexpr T&& Maybe<T>::ref() && { | ||||||||||
| 977 | MOZ_RELEASE_ASSERT(isSome())do { static_assert( mozilla::detail::AssertionConditionType< decltype(isSome())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(isSome()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("isSome()", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/Maybe.h" , 977); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "isSome()" ")"); do { *((volatile int*)__null) = 977; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | ||||||||||
| 978 | return std::move(mStorage.val); | ||||||||||
| 979 | } | ||||||||||
| 980 | |||||||||||
| 981 | template <typename T> | ||||||||||
| 982 | constexpr const T&& Maybe<T>::ref() const&& { | ||||||||||
| 983 | MOZ_RELEASE_ASSERT(isSome())do { static_assert( mozilla::detail::AssertionConditionType< decltype(isSome())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(isSome()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("isSome()", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/Maybe.h" , 983); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "isSome()" ")"); do { *((volatile int*)__null) = 983; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | ||||||||||
| 984 | return std::move(mStorage.val); | ||||||||||
| 985 | } | ||||||||||
| 986 | |||||||||||
| 987 | template <typename T> | ||||||||||
| 988 | constexpr T& Maybe<T>::operator*() & { | ||||||||||
| 989 | MOZ_RELEASE_ASSERT(isSome())do { static_assert( mozilla::detail::AssertionConditionType< decltype(isSome())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(isSome()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("isSome()", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/Maybe.h" , 989); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "isSome()" ")"); do { *((volatile int*)__null) = 989; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | ||||||||||
| 990 | return ref(); | ||||||||||
| 991 | } | ||||||||||
| 992 | |||||||||||
| 993 | template <typename T> | ||||||||||
| 994 | constexpr const T& Maybe<T>::operator*() const& { | ||||||||||
| 995 | MOZ_RELEASE_ASSERT(isSome())do { static_assert( mozilla::detail::AssertionConditionType< decltype(isSome())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(isSome()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("isSome()", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/Maybe.h" , 995); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "isSome()" ")"); do { *((volatile int*)__null) = 995; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | ||||||||||
| 996 | return ref(); | ||||||||||
| 997 | } | ||||||||||
| 998 | |||||||||||
| 999 | template <typename T> | ||||||||||
| 1000 | constexpr T&& Maybe<T>::operator*() && { | ||||||||||
| 1001 | MOZ_RELEASE_ASSERT(isSome())do { static_assert( mozilla::detail::AssertionConditionType< decltype(isSome())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(isSome()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("isSome()", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/Maybe.h" , 1001); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "isSome()" ")"); do { *((volatile int*)__null) = 1001; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | ||||||||||
| 1002 | return std::move(ref()); | ||||||||||
| 1003 | } | ||||||||||
| 1004 | |||||||||||
| 1005 | template <typename T> | ||||||||||
| 1006 | constexpr const T&& Maybe<T>::operator*() const&& { | ||||||||||
| 1007 | MOZ_RELEASE_ASSERT(isSome())do { static_assert( mozilla::detail::AssertionConditionType< decltype(isSome())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(isSome()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("isSome()", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/Maybe.h" , 1007); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "isSome()" ")"); do { *((volatile int*)__null) = 1007; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | ||||||||||
| 1008 | return std::move(ref()); | ||||||||||
| 1009 | } | ||||||||||
| 1010 | |||||||||||
| 1011 | template <typename T> | ||||||||||
| 1012 | template <typename... Args> | ||||||||||
| 1013 | constexpr void Maybe<T>::emplace(Args&&... aArgs) { | ||||||||||
| 1014 | MOZ_RELEASE_ASSERT(!isSome())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!isSome())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!isSome()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!isSome()", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/Maybe.h" , 1014); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "!isSome()" ")"); do { *((volatile int*)__null) = 1014; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | ||||||||||
| 1015 | ::new (KnownNotNull, &mStorage.val) T(std::forward<Args>(aArgs)...); | ||||||||||
| 1016 | mIsSome = true; | ||||||||||
| 1017 | } | ||||||||||
| 1018 | |||||||||||
| 1019 | /* | ||||||||||
| 1020 | * Some() creates a Maybe<T> value containing the provided T value. If T has a | ||||||||||
| 1021 | * move constructor, it's used to make this as efficient as possible. | ||||||||||
| 1022 | * | ||||||||||
| 1023 | * Some() selects the type of Maybe it returns by removing any const, volatile, | ||||||||||
| 1024 | * or reference qualifiers from the type of the value you pass to it. This gives | ||||||||||
| 1025 | * it more intuitive behavior when used in expressions, but it also means that | ||||||||||
| 1026 | * if you need to construct a Maybe value that holds a const, volatile, or | ||||||||||
| 1027 | * reference value, you need to use emplace() instead. | ||||||||||
| 1028 | */ | ||||||||||
| 1029 | template <typename T, typename U> | ||||||||||
| 1030 | constexpr Maybe<U> Some(T&& aValue) { | ||||||||||
| 1031 | return {std::forward<T>(aValue), typename Maybe<U>::SomeGuard{}}; | ||||||||||
| 1032 | } | ||||||||||
| 1033 | |||||||||||
| 1034 | template <typename T> | ||||||||||
| 1035 | constexpr Maybe<T&> SomeRef(T& aValue) { | ||||||||||
| 1036 | Maybe<T&> value; | ||||||||||
| 1037 | value.emplace(aValue); | ||||||||||
| 1038 | return value; | ||||||||||
| 1039 | } | ||||||||||
| 1040 | |||||||||||
| 1041 | template <typename T> | ||||||||||
| 1042 | constexpr Maybe<T&> ToMaybeRef(T* const aPtr) { | ||||||||||
| 1043 | return aPtr ? SomeRef(*aPtr) : Nothing{}; | ||||||||||
| 1044 | } | ||||||||||
| 1045 | |||||||||||
| 1046 | template <typename T> | ||||||||||
| 1047 | Maybe<std::remove_cv_t<std::remove_reference_t<T>>> ToMaybe(T* aPtr) { | ||||||||||
| 1048 | if (aPtr) { | ||||||||||
| 1049 | return Some(*aPtr); | ||||||||||
| 1050 | } | ||||||||||
| 1051 | return Nothing(); | ||||||||||
| 1052 | } | ||||||||||
| 1053 | |||||||||||
| 1054 | /* | ||||||||||
| 1055 | * Two Maybe<T> values are equal if | ||||||||||
| 1056 | * - both are Nothing, or | ||||||||||
| 1057 | * - both are Some, and the values they contain are equal. | ||||||||||
| 1058 | */ | ||||||||||
| 1059 | template <typename T> | ||||||||||
| 1060 | constexpr bool operator==(const Maybe<T>& aLHS, const Maybe<T>& aRHS) { | ||||||||||
| 1061 | static_assert(!std::is_reference_v<T>, | ||||||||||
| 1062 | "operator== is not defined for Maybe<T&>, compare values or " | ||||||||||
| 1063 | "addresses explicitly instead"); | ||||||||||
| 1064 | if (aLHS.isNothing() != aRHS.isNothing()) { | ||||||||||
| 1065 | return false; | ||||||||||
| 1066 | } | ||||||||||
| 1067 | return aLHS.isNothing() || *aLHS == *aRHS; | ||||||||||
| 1068 | } | ||||||||||
| 1069 | |||||||||||
| 1070 | template <typename T> | ||||||||||
| 1071 | constexpr bool operator!=(const Maybe<T>& aLHS, const Maybe<T>& aRHS) { | ||||||||||
| 1072 | return !(aLHS == aRHS); | ||||||||||
| 1073 | } | ||||||||||
| 1074 | |||||||||||
| 1075 | /* | ||||||||||
| 1076 | * We support comparison to Nothing to allow reasonable expressions like: | ||||||||||
| 1077 | * if (maybeValue == Nothing()) { ... } | ||||||||||
| 1078 | */ | ||||||||||
| 1079 | template <typename T> | ||||||||||
| 1080 | constexpr bool operator==(const Maybe<T>& aLHS, const Nothing& aRHS) { | ||||||||||
| 1081 | return aLHS.isNothing(); | ||||||||||
| 1082 | } | ||||||||||
| 1083 | |||||||||||
| 1084 | template <typename T> | ||||||||||
| 1085 | constexpr bool operator!=(const Maybe<T>& aLHS, const Nothing& aRHS) { | ||||||||||
| 1086 | return !(aLHS == aRHS); | ||||||||||
| 1087 | } | ||||||||||
| 1088 | |||||||||||
| 1089 | template <typename T> | ||||||||||
| 1090 | constexpr bool operator==(const Nothing& aLHS, const Maybe<T>& aRHS) { | ||||||||||
| 1091 | return aRHS.isNothing(); | ||||||||||
| 1092 | } | ||||||||||
| 1093 | |||||||||||
| 1094 | template <typename T> | ||||||||||
| 1095 | constexpr bool operator!=(const Nothing& aLHS, const Maybe<T>& aRHS) { | ||||||||||
| 1096 | return !(aLHS == aRHS); | ||||||||||
| 1097 | } | ||||||||||
| 1098 | |||||||||||
| 1099 | /* | ||||||||||
| 1100 | * Maybe<T> values are ordered in the same way T values are ordered, except that | ||||||||||
| 1101 | * Nothing comes before anything else. | ||||||||||
| 1102 | */ | ||||||||||
| 1103 | template <typename T> | ||||||||||
| 1104 | constexpr bool operator<(const Maybe<T>& aLHS, const Maybe<T>& aRHS) { | ||||||||||
| 1105 | if (aLHS.isNothing()) { | ||||||||||
| 1106 | return aRHS.isSome(); | ||||||||||
| 1107 | } | ||||||||||
| 1108 | if (aRHS.isNothing()) { | ||||||||||
| 1109 | return false; | ||||||||||
| 1110 | } | ||||||||||
| 1111 | return *aLHS < *aRHS; | ||||||||||
| 1112 | } | ||||||||||
| 1113 | |||||||||||
| 1114 | template <typename T> | ||||||||||
| 1115 | constexpr bool operator>(const Maybe<T>& aLHS, const Maybe<T>& aRHS) { | ||||||||||
| 1116 | return !(aLHS < aRHS || aLHS == aRHS); | ||||||||||
| 1117 | } | ||||||||||
| 1118 | |||||||||||
| 1119 | template <typename T> | ||||||||||
| 1120 | constexpr bool operator<=(const Maybe<T>& aLHS, const Maybe<T>& aRHS) { | ||||||||||
| 1121 | return aLHS < aRHS || aLHS == aRHS; | ||||||||||
| 1122 | } | ||||||||||
| 1123 | |||||||||||
| 1124 | template <typename T> | ||||||||||
| 1125 | constexpr bool operator>=(const Maybe<T>& aLHS, const Maybe<T>& aRHS) { | ||||||||||
| 1126 | return !(aLHS < aRHS); | ||||||||||
| 1127 | } | ||||||||||
| 1128 | |||||||||||
| 1129 | template <typename T> | ||||||||||
| 1130 | inline void ImplCycleCollectionTraverse( | ||||||||||
| 1131 | nsCycleCollectionTraversalCallback& aCallback, mozilla::Maybe<T>& aField, | ||||||||||
| 1132 | const char* aName, uint32_t aFlags = 0) { | ||||||||||
| 1133 | if (aField) { | ||||||||||
| 1134 | ImplCycleCollectionTraverse(aCallback, aField.ref(), aName, aFlags); | ||||||||||
| 1135 | } | ||||||||||
| 1136 | } | ||||||||||
| 1137 | |||||||||||
| 1138 | template <typename T> | ||||||||||
| 1139 | inline void ImplCycleCollectionUnlink(mozilla::Maybe<T>& aField) { | ||||||||||
| 1140 | if (aField) { | ||||||||||
| 1141 | ImplCycleCollectionUnlink(aField.ref()); | ||||||||||
| 1142 | } | ||||||||||
| 1143 | } | ||||||||||
| 1144 | |||||||||||
| 1145 | } // namespace mozilla | ||||||||||
| 1146 | |||||||||||
| 1147 | #endif /* mozilla_Maybe_h */ | 
| 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | 
| 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ | 
| 3 | /* This Source Code Form is subject to the terms of the Mozilla Public | 
| 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this | 
| 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | 
| 6 | |
| 7 | /* Internal storage class used e.g. by Maybe and Result. This file doesn't | 
| 8 | * contain any public declarations. */ | 
| 9 | |
| 10 | #ifndef mfbt_MaybeStorageBase_h | 
| 11 | #define mfbt_MaybeStorageBase_h | 
| 12 | |
| 13 | #include <type_traits> | 
| 14 | #include <utility> | 
| 15 | |
| 16 | namespace mozilla::detail { | 
| 17 | |
| 18 | template <typename T> | 
| 19 | constexpr bool IsTriviallyDestructibleAndCopyable = | 
| 20 | std::is_trivially_destructible_v<T> && | 
| 21 | (std::is_trivially_copy_constructible_v<T> || | 
| 22 | !std::is_copy_constructible_v<T>); | 
| 23 | |
| 24 | template <typename T, bool TriviallyDestructibleAndCopyable = | 
| 25 | IsTriviallyDestructibleAndCopyable<T>> | 
| 26 | struct MaybeStorageBase; | 
| 27 | |
| 28 | template <typename T> | 
| 29 | struct MaybeStorageBase<T, false> { | 
| 30 | protected: | 
| 31 | using NonConstT = std::remove_const_t<T>; | 
| 32 | |
| 33 | union Union { | 
| 34 | Union() {} | 
| 35 | explicit Union(const T& aVal) : val{aVal} {} | 
| 36 | template <typename U, | 
| 37 | typename = std::enable_if_t<std::is_move_constructible_v<U>>> | 
| 38 | explicit Union(U&& aVal) : val{std::forward<U>(aVal)} {} | 
| 39 | template <typename... Args> | 
| 40 | explicit Union(std::in_place_t, Args&&... aArgs) | 
| 41 | : val{std::forward<Args>(aArgs)...} {} | 
| 42 | |
| 43 | ~Union() {} | 
| 44 | |
| 45 | NonConstT val; | 
| 46 | } mStorage; | 
| 47 | |
| 48 | public: | 
| 49 | constexpr MaybeStorageBase() = default; | 
| 50 | explicit MaybeStorageBase(const T& aVal) : mStorage{aVal} {} | 
| 51 | explicit MaybeStorageBase(T&& aVal) : mStorage{std::move(aVal)} {} | 
| 52 | template <typename... Args> | 
| 53 | explicit MaybeStorageBase(std::in_place_t, Args&&... aArgs) | 
| 54 | : mStorage{std::in_place, std::forward<Args>(aArgs)...} {} | 
| 55 | |
| 56 | const T* addr() const { return &mStorage.val; } | 
| 57 | T* addr() { return &mStorage.val; } | 
| 58 | }; | 
| 59 | |
| 60 | template <typename T> | 
| 61 | struct MaybeStorageBase<T, true> { | 
| 62 | protected: | 
| 63 | using NonConstT = std::remove_const_t<T>; | 
| 64 | |
| 65 | union Union { | 
| 66 | constexpr Union() : empty() {} | 
| 67 | constexpr explicit Union(const T& aVal) : val{aVal} {} | 
| 68 | constexpr explicit Union(T&& aVal) : val{std::move(aVal)} {} | 
| 69 | template <typename... Args> | 
| 70 | constexpr explicit Union(std::in_place_t, Args&&... aArgs) | 
| 71 | : val{std::forward<Args>(aArgs)...} {} | 
| 72 | |
| 73 | NonConstT val; | 
| 74 | char empty; | 
| 75 | } mStorage; | 
| 76 | |
| 77 | public: | 
| 78 | constexpr MaybeStorageBase() = default; | 
| 79 | constexpr explicit MaybeStorageBase(const T& aVal) : mStorage{aVal} {} | 
| 80 | constexpr explicit MaybeStorageBase(T&& aVal) : mStorage{std::move(aVal)} {} | 
| 81 | |
| 82 | template <typename... Args> | 
| 83 | constexpr explicit MaybeStorageBase(std::in_place_t, Args&&... aArgs) | 
| 84 | : mStorage{std::in_place, std::forward<Args>(aArgs)...} {} | 
| 85 | |
| 86 | constexpr const T* addr() const { return &mStorage.val; } | 
| 87 | constexpr T* addr() { return &mStorage.val; } | 
| 88 | }; | 
| 89 | |
| 90 | } // namespace mozilla::detail | 
| 91 | |
| 92 | #endif | 
| 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- | 
| 2 | * vim: set ts=8 sts=2 et sw=2 tw=80: | 
| 3 | * This Source Code Form is subject to the terms of the Mozilla Public | 
| 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this | 
| 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | 
| 6 | |
| 7 | /* | 
| 8 | * API for managing off-thread work. | 
| 9 | */ | 
| 10 | |
| 11 | #ifndef vm_HelperThreads_h | 
| 12 | #define vm_HelperThreads_h | 
| 13 | |
| 14 | #include "mozilla/Variant.h" | 
| 15 | |
| 16 | #include "js/AllocPolicy.h" | 
| 17 | #include "js/HelperThreadAPI.h" | 
| 18 | #include "js/shadow/Zone.h" | 
| 19 | #include "js/UniquePtr.h" | 
| 20 | #include "js/Vector.h" | 
| 21 | #include "threading/LockGuard.h" | 
| 22 | #include "threading/Mutex.h" | 
| 23 | #include "wasm/WasmConstants.h" | 
| 24 | |
| 25 | namespace mozilla { | 
| 26 | union Utf8Unit; | 
| 27 | } | 
| 28 | |
| 29 | namespace JS { | 
| 30 | class JS_PUBLIC_API ReadOnlyCompileOptions; | 
| 31 | class JS_PUBLIC_API ReadOnlyDecodeOptions; | 
| 32 | class Zone; | 
| 33 | |
| 34 | template <typename UnitT> | 
| 35 | class SourceText; | 
| 36 | } // namespace JS | 
| 37 | |
| 38 | namespace js { | 
| 39 | |
| 40 | class AutoLockHelperThreadState; | 
| 41 | struct PromiseHelperTask; | 
| 42 | class SourceCompressionTask; | 
| 43 | |
| 44 | namespace frontend { | 
| 45 | struct InitialStencilAndDelazifications; | 
| 46 | } | 
| 47 | |
| 48 | namespace gc { | 
| 49 | class GCRuntime; | 
| 50 | } | 
| 51 | |
| 52 | namespace jit { | 
| 53 | class BaselineCompileTask; | 
| 54 | class IonCompileTask; | 
| 55 | class IonFreeTask; | 
| 56 | class JitRuntime; | 
| 57 | using IonFreeCompileTasks = Vector<IonCompileTask*, 8, SystemAllocPolicy>; | 
| 58 | } // namespace jit | 
| 59 | |
| 60 | namespace wasm { | 
| 61 | struct CompileTask; | 
| 62 | struct CompileTaskState; | 
| 63 | struct CompleteTier2GeneratorTask; | 
| 64 | using UniqueCompleteTier2GeneratorTask = UniquePtr<CompleteTier2GeneratorTask>; | 
| 65 | struct PartialTier2CompileTask; | 
| 66 | using UniquePartialTier2CompileTask = UniquePtr<PartialTier2CompileTask>; | 
| 67 | } // namespace wasm | 
| 68 | |
| 69 | /* | 
| 70 | * Lock protecting all mutable shared state accessed by helper threads, and used | 
| 71 | * by all condition variables. | 
| 72 | */ | 
| 73 | extern Mutex gHelperThreadLock MOZ_UNANNOTATED; | 
| 74 | |
| 75 | // Set of tasks to dispatch when the helper thread state lock is released. | 
| 76 | class AutoHelperTaskQueue { | 
| 77 | public: | 
| 78 | ~AutoHelperTaskQueue() { dispatchQueuedTasks(); } | 
| 79 | bool hasQueuedTasks() const { return !tasksToDispatch.empty(); } | 
| 80 | void queueTaskToDispatch(JS::HelperThreadTask* task) const; | 
| 81 | void dispatchQueuedTasks(); | 
| 82 | |
| 83 | private: | 
| 84 | // TODO: Convert this to use a linked list. | 
| 85 | mutable Vector<JS::HelperThreadTask*, 1, SystemAllocPolicy> tasksToDispatch; | 
| 86 | }; | 
| 87 | |
| 88 | // A lock guard for data protected by the helper thread lock. | 
| 89 | // | 
| 90 | // This can also queue helper thread tasks to be triggered when the lock is | 
| 91 | // released. | 
| 92 | class MOZ_RAII AutoLockHelperThreadState | 
| 93 | : public AutoHelperTaskQueue, // Must come before LockGuard. | 
| 94 | public LockGuard<Mutex> { | 
| 95 | public: | 
| 96 | AutoLockHelperThreadState() : LockGuard<Mutex>(gHelperThreadLock) {} | 
| 97 | AutoLockHelperThreadState(const AutoLockHelperThreadState&) = delete; | 
| 98 | |
| 99 | private: | 
| 100 | friend class UnlockGuard<AutoLockHelperThreadState>; | 
| 101 | void unlock() { | 
| 102 | LockGuard<Mutex>::unlock(); | 
| 103 | dispatchQueuedTasks(); | 
| 104 | } | 
| 105 | |
| 106 | friend class GlobalHelperThreadState; | 
| 107 | }; | 
| 108 | |
| 109 | using AutoUnlockHelperThreadState = UnlockGuard<AutoLockHelperThreadState>; | 
| 110 | |
| 111 | // Create data structures used by helper threads. | 
| 112 | bool CreateHelperThreadsState(); | 
| 113 | |
| 114 | // Destroy data structures used by helper threads. | 
| 115 | void DestroyHelperThreadsState(); | 
| 116 | |
| 117 | // Initialize helper threads unless already initialized. | 
| 118 | bool EnsureHelperThreadsInitialized(); | 
| 119 | |
| 120 | size_t GetHelperThreadCount(); | 
| 121 | size_t GetHelperThreadCPUCount(); | 
| 122 | size_t GetMaxWasmCompilationThreads(); | 
| 123 | |
| 124 | // This allows the JS shell to override GetCPUCount() when passed the | 
| 125 | // --thread-count=N option. | 
| 126 | bool SetFakeCPUCount(size_t count); | 
| 127 | |
| 128 | // Enqueues a wasm compilation task. | 
| 129 | bool StartOffThreadWasmCompile(wasm::CompileTask* task, | 
| 130 | wasm::CompileState state); | 
| 131 | |
| 132 | // Remove any pending wasm compilation tasks queued with | 
| 133 | // StartOffThreadWasmCompile that match the arguments. Return the number | 
| 134 | // removed. | 
| 135 | size_t RemovePendingWasmCompileTasks(const wasm::CompileTaskState& taskState, | 
| 136 | wasm::CompileState state, | 
| 137 | const AutoLockHelperThreadState& lock); | 
| 138 | |
| 139 | // Enqueues a wasm Complete Tier-2 compilation task. This (logically, at | 
| 140 | // least) manages a set of sub-tasks that perform compilation of groups of | 
| 141 | // functions. | 
| 142 | void StartOffThreadWasmCompleteTier2Generator( | 
| 143 | wasm::UniqueCompleteTier2GeneratorTask task); | 
| 144 | |
| 145 | // Enqueues a wasm Partial Tier-2 compilation task. This compiles one | 
| 146 | // function, doing so itself, without any sub-tasks. | 
| 147 | void StartOffThreadWasmPartialTier2Compile( | 
| 148 | wasm::UniquePartialTier2CompileTask task); | 
| 149 | |
| 150 | // Cancel all background Wasm Complete Tier-2 compilations, both the generator | 
| 151 | // task and the individual compilation tasks. | 
| 152 | void CancelOffThreadWasmCompleteTier2Generator(); | 
| 153 | |
| 154 | // Cancel a single background Wasm Partial Tier-2 compilation. | 
| 155 | void CancelOffThreadWasmPartialTier2Compile(); | 
| 156 | |
| 157 | /* | 
| 158 | * If helper threads are available, call execute() then dispatchResolve() on the | 
| 159 | * given task in a helper thread. If no helper threads are available, the given | 
| 160 | * task is executed and resolved synchronously. | 
| 161 | * | 
| 162 | * This function takes ownership of task unconditionally; if it fails, task is | 
| 163 | * deleted. | 
| 164 | */ | 
| 165 | bool StartOffThreadPromiseHelperTask(JSContext* cx, | 
| 166 | UniquePtr<PromiseHelperTask> task); | 
| 167 | |
| 168 | /* | 
| 169 | * Like the JSContext-accepting version, but only safe to use when helper | 
| 170 | * threads are available, so we can be sure we'll never need to fall back on | 
| 171 | * synchronous execution. | 
| 172 | * | 
| 173 | * This function can be called from any thread, but takes ownership of the task | 
| 174 | * only on success. On OOM, it is the caller's responsibility to arrange for the | 
| 175 | * task to be cleaned up properly. | 
| 176 | */ | 
| 177 | bool StartOffThreadPromiseHelperTask(PromiseHelperTask* task); | 
| 178 | |
| 179 | /* | 
| 180 | * Schedule an off-thread Baseline compilation for a script, given a task. | 
| 181 | */ | 
| 182 | bool StartOffThreadBaselineCompile(jit::BaselineCompileTask* task, | 
| 183 | const AutoLockHelperThreadState& lock); | 
| 184 | |
| 185 | void FinishOffThreadBaselineCompile(jit::BaselineCompileTask* task, | 
| 186 | const AutoLockHelperThreadState& lock); | 
| 187 | |
| 188 | /* | 
| 189 | * Schedule an off-thread Ion compilation for a script, given a task. | 
| 190 | */ | 
| 191 | bool StartOffThreadIonCompile(jit::IonCompileTask* task, | 
| 192 | const AutoLockHelperThreadState& lock); | 
| 193 | |
| 194 | void FinishOffThreadIonCompile(jit::IonCompileTask* task, | 
| 195 | const AutoLockHelperThreadState& lock); | 
| 196 | |
| 197 | // RAII class to handle batching compile tasks and starting an IonFreeTask. | 
| 198 | class MOZ_RAII AutoStartIonFreeTask { | 
| 199 | jit::JitRuntime* jitRuntime_; | 
| 200 | |
| 201 | // If true, start an IonFreeTask even if the batch is small. | 
| 202 | bool force_; | 
| 203 | |
| 204 | public: | 
| 205 | explicit AutoStartIonFreeTask(jit::JitRuntime* jitRuntime, bool force = false) | 
| 206 | : jitRuntime_(jitRuntime), force_(force) {} | 
| 207 | ~AutoStartIonFreeTask(); | 
| 208 | |
| 209 | [[nodiscard]] bool addIonCompileToFreeTaskBatch(jit::IonCompileTask* task); | 
| 210 | }; | 
| 211 | |
| 212 | struct ZonesInState { | 
| 213 | JSRuntime* runtime; | 
| 214 | JS::shadow::Zone::GCState state; | 
| 215 | }; | 
| 216 | |
| 217 | using CompilationSelector = | 
| 218 | mozilla::Variant<JSScript*, JS::Zone*, ZonesInState, JSRuntime*>; | 
| 219 | |
| 220 | /* | 
| 221 | * Cancel scheduled or in progress Ion compilations. | 
| 222 | */ | 
| 223 | void CancelOffThreadIonCompile(const CompilationSelector& selector); | 
| 224 | |
| 225 | inline void CancelOffThreadIonCompile(JSScript* script) { | 
| 226 | CancelOffThreadIonCompile(CompilationSelector(script)); | 
| 227 | } | 
| 228 | |
| 229 | inline void CancelOffThreadIonCompile(JS::Zone* zone) { | 
| 230 | CancelOffThreadIonCompile(CompilationSelector(zone)); | 
| 231 | } | 
| 232 | |
| 233 | inline void CancelOffThreadIonCompile(JSRuntime* runtime, | 
| 234 | JS::shadow::Zone::GCState state) { | 
| 235 | CancelOffThreadIonCompile(CompilationSelector(ZonesInState{runtime, state})); | 
| 236 | } | 
| 237 | |
| 238 | inline void CancelOffThreadIonCompile(JSRuntime* runtime) { | 
| 239 | CancelOffThreadIonCompile(CompilationSelector(runtime)); | 
| 240 | } | 
| 241 | |
| 242 | #ifdef DEBUG1 | 
| 243 | bool HasOffThreadIonCompile(JS::Zone* zone); | 
| 244 | #endif | 
| 245 | |
| 246 | /* | 
| 247 | * Cancel scheduled or in progress Baseline compilations. | 
| 248 | */ | 
| 249 | void CancelOffThreadBaselineCompile(const CompilationSelector& selector); | 
| 250 | |
| 251 | inline void CancelOffThreadBaselineCompile(JSScript* script) { | 
| 252 | CancelOffThreadBaselineCompile(CompilationSelector(script)); | 
| 253 | } | 
| 254 | |
| 255 | inline void CancelOffThreadBaselineCompile(JS::Zone* zone) { | 
| 256 | CancelOffThreadBaselineCompile(CompilationSelector(zone)); | 
| 257 | } | 
| 258 | |
| 259 | inline void CancelOffThreadBaselineCompile(JSRuntime* runtime, | 
| 260 | JS::shadow::Zone::GCState state) { | 
| 261 | CancelOffThreadBaselineCompile( | 
| 262 | CompilationSelector(ZonesInState{runtime, state})); | 
| 263 | } | 
| 264 | |
| 265 | inline void CancelOffThreadBaselineCompile(JSRuntime* runtime) { | 
| 266 | CancelOffThreadBaselineCompile(CompilationSelector(runtime)); | 
| 267 | } | 
| 268 | |
| 269 | /* | 
| 270 | * Cancel baseline and Ion compilations. | 
| 271 | */ | 
| 272 | inline void CancelOffThreadCompile(JSRuntime* runtime, | 
| 273 | JS::shadow::Zone::GCState state) { | 
| 274 | CancelOffThreadBaselineCompile( | 
| 275 | CompilationSelector(ZonesInState{runtime, state})); | 
| 276 | CancelOffThreadIonCompile(CompilationSelector(ZonesInState{runtime, state})); | 
| 277 | } | 
| 278 | |
| 279 | inline void CancelOffThreadCompile(JSRuntime* runtime) { | 
| 280 | CancelOffThreadBaselineCompile(runtime); | 
| 281 | CancelOffThreadIonCompile(runtime); | 
| 282 | } | 
| 283 | |
| 284 | /* | 
| 285 | * Cancel all scheduled or in progress eager delazification phases for a | 
| 286 | * runtime. | 
| 287 | */ | 
| 288 | void CancelOffThreadDelazify(JSRuntime* runtime); | 
| 289 | |
| 290 | /* | 
| 291 | * Wait for all delazification to complete. | 
| 292 | */ | 
| 293 | void WaitForAllDelazifyTasks(JSRuntime* rt); | 
| 294 | |
| 295 | // Start off-thread delazification task, to race the delazification of inner | 
| 296 | // functions. | 
| 297 | void StartOffThreadDelazification( | 
| 298 | JSContext* maybeCx, const JS::ReadOnlyCompileOptions& options, | 
| 299 | frontend::InitialStencilAndDelazifications* stencils); | 
| 300 | |
| 301 | // Drain the task queues and wait for all helper threads to finish running. | 
| 302 | // | 
| 303 | // Note that helper threads are shared between runtimes and it's possible that | 
| 304 | // another runtime could saturate the helper thread system and cause this to | 
| 305 | // never return. | 
| 306 | void WaitForAllHelperThreads(); | 
| 307 | void WaitForAllHelperThreads(AutoLockHelperThreadState& lock); | 
| 308 | |
| 309 | // Enqueue a compression job to be processed later. These are started at the | 
| 310 | // start of the major GC after the next one. | 
| 311 | bool EnqueueOffThreadCompression(JSContext* cx, | 
| 312 | UniquePtr<SourceCompressionTask> task); | 
| 313 | |
| 314 | // Start handling any compression tasks for this runtime. Called at the start of | 
| 315 | // major GC. | 
| 316 | void StartHandlingCompressionsOnGC(JSRuntime* rt); | 
| 317 | |
| 318 | // Cancel all scheduled, in progress, or finished compression tasks for | 
| 319 | // runtime. | 
| 320 | void CancelOffThreadCompressions(JSRuntime* runtime); | 
| 321 | |
| 322 | void AttachFinishedCompressions(JSRuntime* runtime, | 
| 323 | AutoLockHelperThreadState& lock); | 
| 324 | |
| 325 | // Sweep pending tasks that are holding onto should-be-dead ScriptSources. | 
| 326 | void SweepPendingCompressions(AutoLockHelperThreadState& lock); | 
| 327 | |
| 328 | // Run all pending source compression tasks synchronously, for testing purposes | 
| 329 | void RunPendingSourceCompressions(JSRuntime* runtime); | 
| 330 | |
| 331 | // False if the off-thread source compression mechanism isn't being used. This | 
| 332 | // happens on low core count machines where we are concerned about blocking | 
| 333 | // main-thread execution. | 
| 334 | bool IsOffThreadSourceCompressionEnabled(); | 
| 335 | |
| 336 | void AttachFinishedBaselineCompilations(JSContext* cx, | 
| 337 | AutoLockHelperThreadState& lock); | 
| 338 | |
| 339 | } // namespace js | 
| 340 | |
| 341 | #endif /* vm_HelperThreads_h */ | 
| 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | |||
| 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ | |||
| 3 | /* This Source Code Form is subject to the terms of the Mozilla Public | |||
| 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this | |||
| 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |||
| 6 | ||||
| 7 | #ifndef threading_LockGuard_h | |||
| 8 | #define threading_LockGuard_h | |||
| 9 | ||||
| 10 | #include "mozilla/Attributes.h" | |||
| 11 | ||||
| 12 | namespace js { | |||
| 13 | ||||
| 14 | template <typename GuardT> | |||
| 15 | class MOZ_RAII UnlockGuard; | |||
| 16 | ||||
| 17 | template <typename Mutex> | |||
| 18 | class MOZ_RAII LockGuard { | |||
| 19 | friend class UnlockGuard<LockGuard<Mutex>>; | |||
| 20 | friend class ConditionVariable; | |||
| 21 | Mutex& mutex; | |||
| 22 | ||||
| 23 | public: | |||
| 24 | explicit LockGuard(Mutex& mutex) : mutex(mutex) { lock(); } | |||
| 25 | ~LockGuard() { unlock(); } | |||
| 26 | ||||
| 27 | LockGuard(const LockGuard& other) = delete; | |||
| 28 | ||||
| 29 | protected: | |||
| 30 | void lock() { mutex.lock(); } | |||
| 31 | void unlock() { mutex.unlock(); } | |||
| 
 | ||||
| 32 | }; | |||
| 33 | ||||
| 34 | // RAII class to temporarily unlock a LockGuard. | |||
| 35 | template <typename GuardT> | |||
| 36 | class MOZ_RAII UnlockGuard { | |||
| 37 | GuardT& guard; | |||
| 38 | ||||
| 39 | public: | |||
| 40 | explicit UnlockGuard(GuardT& guard) : guard(guard) { guard.unlock(); } | |||
| 41 | ~UnlockGuard() { guard.lock(); } | |||
| 42 | ||||
| 43 | UnlockGuard(const UnlockGuard& other) = delete; | |||
| 44 | }; | |||
| 45 | ||||
| 46 | } // namespace js | |||
| 47 | ||||
| 48 | #endif // threading_LockGuard_h |