File: | var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/ProfilerLabels.h |
Warning: | line 259, column 9 Branch condition evaluates to a garbage value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */ | |||
2 | /* This Source Code Form is subject to the terms of the Mozilla Public | |||
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this | |||
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |||
5 | ||||
6 | #include "nsJSInspector.h" | |||
7 | #include "mozilla/HoldDropJSObjects.h" | |||
8 | #include "mozilla/SpinEventLoopUntil.h" | |||
9 | #include "mozilla/dom/ScriptSettings.h" | |||
10 | #include "nsTArray.h" | |||
11 | ||||
12 | #define JSINSPECTOR_CONTRACTID"@mozilla.org/jsinspector;1" "@mozilla.org/jsinspector;1" | |||
13 | ||||
14 | #define JSINSPECTOR_CID{ 0xec5aa99c, 0x7abb, 0x4142, { 0xac, 0x5f, 0xaa, 0xb2, 0x41, 0x9e, 0x38, 0xe2 } } \ | |||
15 | { \ | |||
16 | 0xec5aa99c, 0x7abb, 0x4142, { \ | |||
17 | 0xac, 0x5f, 0xaa, 0xb2, 0x41, 0x9e, 0x38, 0xe2 \ | |||
18 | } \ | |||
19 | } | |||
20 | ||||
21 | namespace mozilla { | |||
22 | namespace jsinspector { | |||
23 | ||||
24 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSInspector)nsresult nsJSInspector::QueryInterface(const nsIID& aIID, void** aInstancePtr) { do { if (!(aInstancePtr)) { NS_DebugBreak (NS_DEBUG_ASSERTION, "QueryInterface requires a non-NULL destination!" , "aInstancePtr", "/var/lib/jenkins/workspace/firefox-scan-build/devtools/platform/nsJSInspector.cpp" , 24); MOZ_PretendNoReturn(); } } while (0); nsISupports* foundInterface ; if (TopThreeWordsEquals( aIID, (nsXPCOMCycleCollectionParticipant ::COMTypeInfo<nsXPCOMCycleCollectionParticipant, void>:: kIID), (nsCycleCollectionISupports::COMTypeInfo<nsCycleCollectionISupports , void>::kIID)) && (LowWordEquals(aIID, (nsXPCOMCycleCollectionParticipant ::COMTypeInfo<nsXPCOMCycleCollectionParticipant, void>:: kIID)) || LowWordEquals(aIID, (nsCycleCollectionISupports::COMTypeInfo <nsCycleCollectionISupports, void>::kIID)))) { if (LowWordEquals (aIID, (nsXPCOMCycleCollectionParticipant::COMTypeInfo<nsXPCOMCycleCollectionParticipant , void>::kIID))) { *aInstancePtr = nsJSInspector::cycleCollection ::GetParticipant(); return NS_OK; } if (LowWordEquals(aIID, ( nsCycleCollectionISupports::COMTypeInfo<nsCycleCollectionISupports , void>::kIID))) { *aInstancePtr = nsJSInspector::cycleCollection ::Upcast(this); return NS_OK; } foundInterface = nullptr; } else | |||
25 | NS_INTERFACE_MAP_ENTRY(nsISupports)if (aIID.Equals(mozilla::detail::kImplementedIID<std::remove_reference_t <decltype(*this)>, nsISupports>)) foundInterface = static_cast <nsISupports*>(this); else | |||
26 | NS_INTERFACE_MAP_ENTRY(nsIJSInspector)if (aIID.Equals(mozilla::detail::kImplementedIID<std::remove_reference_t <decltype(*this)>, nsIJSInspector>)) foundInterface = static_cast<nsIJSInspector*>(this); else | |||
27 | NS_INTERFACE_MAP_ENDfoundInterface = 0; nsresult status; if (!foundInterface) { do { static_assert( mozilla::detail::AssertionConditionType< decltype(!aIID.Equals((nsISupports::COMTypeInfo<nsISupports , void>::kIID)))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!aIID.Equals((nsISupports::COMTypeInfo <nsISupports, void>::kIID))))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("!aIID.Equals((nsISupports::COMTypeInfo<nsISupports, void>::kIID))" , "/var/lib/jenkins/workspace/firefox-scan-build/devtools/platform/nsJSInspector.cpp" , 27); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aIID.Equals((nsISupports::COMTypeInfo<nsISupports, void>::kIID))" ")"); do { *((volatile int*)__null) = 27; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); status = NS_NOINTERFACE ; } else { (foundInterface)->AddRef(); status = NS_OK; } * aInstancePtr = foundInterface; return status; } | |||
28 | ||||
29 | NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSInspector)nsJSInspector::cycleCollection nsJSInspector::_cycleCollectorGlobal ; | |||
30 | ||||
31 | NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSInspector)MozExternalRefCountType nsJSInspector::AddRef(void) { static_assert (!std::is_destructible_v<nsJSInspector>, "Reference-counted class " "nsJSInspector" " should not have a public destructor. " "Make this class's destructor non-public" ); do { static_assert( mozilla::detail::AssertionConditionType <decltype(int32_t(mRefCnt) >= 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) >= 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) >= 0" " (" "illegal refcnt" ")", "/var/lib/jenkins/workspace/firefox-scan-build/devtools/platform/nsJSInspector.cpp" , 31); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) >= 0" ") (" "illegal refcnt" ")"); do { *((volatile int*)__null) = 31; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); _mOwningThread.AssertOwnership("nsJSInspector" " not thread-safe"); nsISupports* base = nsJSInspector::cycleCollection ::Upcast(this); nsrefcnt count = mRefCnt.incr(base); NS_LogAddRef ((this), (count), ("nsJSInspector"), (uint32_t)(sizeof(*this) )); return count; } | |||
32 | NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSInspector)MozExternalRefCountType nsJSInspector::Release(void) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(int32_t (mRefCnt) > 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) > 0))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) > 0" " (" "dup release" ")", "/var/lib/jenkins/workspace/firefox-scan-build/devtools/platform/nsJSInspector.cpp" , 32); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) > 0" ") (" "dup release" ")"); do { *((volatile int*)__null) = 32 ; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); _mOwningThread.AssertOwnership("nsJSInspector" " not thread-safe" ); nsISupports* base = nsJSInspector::cycleCollection::Upcast (this); nsrefcnt count = mRefCnt.decr(base); NS_LogRelease((this ), (count), ("nsJSInspector")); return count; } void nsJSInspector ::DeleteCycleCollectable(void) { delete (this); } | |||
33 | ||||
34 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSInspector)nsresult nsJSInspector::cycleCollection::TraverseNative( void * p, nsCycleCollectionTraversalCallback& cb) { nsJSInspector * tmp = DowncastCCParticipant<nsJSInspector>(p); cb.DescribeRefCountedNode (tmp->mRefCnt.get(), "nsJSInspector"); | |||
35 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END(void)tmp; return NS_OK; } | |||
36 | ||||
37 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSInspector)void nsJSInspector::cycleCollection::Unlink(void* p) { nsJSInspector * tmp = DowncastCCParticipant<nsJSInspector>(p); | |||
38 | tmp->mRequestors.Clear(); | |||
39 | tmp->mLastRequestor = JS::NullValue(); | |||
40 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END(void)tmp; } | |||
41 | ||||
42 | NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSInspector)void nsJSInspector::cycleCollection::Trace( void* p, const TraceCallbacks & aCallbacks, void* aClosure) { nsJSInspector* tmp = DowncastCCParticipant <nsJSInspector>(p); | |||
43 | for (uint32_t i = 0; i < tmp->mRequestors.Length(); ++i) { | |||
44 | NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRequestors[i])aCallbacks.Trace(&tmp->mRequestors[i], "mRequestors[i]" , aClosure); | |||
45 | } | |||
46 | NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mLastRequestor)aCallbacks.Trace(&tmp->mLastRequestor, "mLastRequestor" , aClosure); | |||
47 | NS_IMPL_CYCLE_COLLECTION_TRACE_END(void)tmp; } | |||
48 | ||||
49 | nsJSInspector::nsJSInspector() | |||
50 | : mNestedLoopLevel(0), mRequestors(1), mLastRequestor(JS::NullValue()) {} | |||
51 | ||||
52 | nsJSInspector::~nsJSInspector() { | |||
53 | MOZ_ASSERT(mRequestors.Length() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(mRequestors.Length() == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mRequestors.Length() == 0))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("mRequestors.Length() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/devtools/platform/nsJSInspector.cpp" , 53); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mRequestors.Length() == 0" ")"); do { *((volatile int*)__null) = 53; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | |||
54 | MOZ_ASSERT(mLastRequestor.isNull())do { static_assert( mozilla::detail::AssertionConditionType< decltype(mLastRequestor.isNull())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mLastRequestor.isNull()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("mLastRequestor.isNull()" , "/var/lib/jenkins/workspace/firefox-scan-build/devtools/platform/nsJSInspector.cpp" , 54); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mLastRequestor.isNull()" ")"); do { *((volatile int*)__null) = 54; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | |||
55 | mozilla::DropJSObjects(this); | |||
56 | } | |||
57 | ||||
58 | NS_IMETHODIMPnsresult | |||
59 | nsJSInspector::EnterNestedEventLoop(JS::Handle<JS::Value> requestor, | |||
60 | uint32_t* out) { | |||
61 | nsresult rv = NS_OK; | |||
62 | ||||
63 | mLastRequestor = requestor; | |||
64 | mRequestors.AppendElement(requestor); | |||
65 | mozilla::HoldJSObjects(this); | |||
66 | ||||
67 | mozilla::dom::AutoNoJSAPI nojsapi; | |||
68 | ||||
69 | uint32_t nestLevel = ++mNestedLoopLevel; | |||
70 | if (!SpinEventLoopUntil("nsJSInspector::EnterNestedEventLoop"_ns, | |||
| ||||
71 | [&]() { return mNestedLoopLevel < nestLevel; })) { | |||
72 | rv = NS_ERROR_UNEXPECTED; | |||
73 | } | |||
74 | ||||
75 | NS_ASSERTION(mNestedLoopLevel <= nestLevel,do { if (!(mNestedLoopLevel <= nestLevel)) { NS_DebugBreak (NS_DEBUG_ASSERTION, "nested event didn't unwind properly", "mNestedLoopLevel <= nestLevel" , "/var/lib/jenkins/workspace/firefox-scan-build/devtools/platform/nsJSInspector.cpp" , 76); MOZ_PretendNoReturn(); } } while (0) | |||
76 | "nested event didn't unwind properly")do { if (!(mNestedLoopLevel <= nestLevel)) { NS_DebugBreak (NS_DEBUG_ASSERTION, "nested event didn't unwind properly", "mNestedLoopLevel <= nestLevel" , "/var/lib/jenkins/workspace/firefox-scan-build/devtools/platform/nsJSInspector.cpp" , 76); MOZ_PretendNoReturn(); } } while (0); | |||
77 | ||||
78 | if (mNestedLoopLevel == nestLevel) { | |||
79 | mLastRequestor = mRequestors.ElementAt(--mNestedLoopLevel); | |||
80 | } | |||
81 | ||||
82 | *out = mNestedLoopLevel; | |||
83 | return rv; | |||
84 | } | |||
85 | ||||
86 | NS_IMETHODIMPnsresult | |||
87 | nsJSInspector::ExitNestedEventLoop(uint32_t* out) { | |||
88 | if (mNestedLoopLevel > 0) { | |||
89 | mRequestors.RemoveElementAt(--mNestedLoopLevel); | |||
90 | if (mNestedLoopLevel > 0) | |||
91 | mLastRequestor = mRequestors.ElementAt(mNestedLoopLevel - 1); | |||
92 | else | |||
93 | mLastRequestor = JS::NullValue(); | |||
94 | } else { | |||
95 | return NS_ERROR_FAILURE; | |||
96 | } | |||
97 | ||||
98 | *out = mNestedLoopLevel; | |||
99 | ||||
100 | return NS_OK; | |||
101 | } | |||
102 | ||||
103 | NS_IMETHODIMPnsresult | |||
104 | nsJSInspector::GetEventLoopNestLevel(uint32_t* out) { | |||
105 | *out = mNestedLoopLevel; | |||
106 | return NS_OK; | |||
107 | } | |||
108 | ||||
109 | NS_IMETHODIMPnsresult | |||
110 | nsJSInspector::GetLastNestRequestor(JS::MutableHandle<JS::Value> out) { | |||
111 | out.set(mLastRequestor); | |||
112 | return NS_OK; | |||
113 | } | |||
114 | ||||
115 | } // namespace jsinspector | |||
116 | } // namespace mozilla |
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 xpcom_threads_SpinEventLoopUntil_h__ | ||||||||||
8 | #define xpcom_threads_SpinEventLoopUntil_h__ | ||||||||||
9 | |||||||||||
10 | #include "MainThreadUtils.h" | ||||||||||
11 | #include "mozilla/Maybe.h" | ||||||||||
12 | #include "mozilla/ProfilerLabels.h" | ||||||||||
13 | #include "mozilla/ProfilerMarkers.h" | ||||||||||
14 | #include "mozilla/StaticMutex.h" | ||||||||||
15 | #include "nsString.h" | ||||||||||
16 | #include "nsThreadUtils.h" | ||||||||||
17 | #include "xpcpublic.h" | ||||||||||
18 | |||||||||||
19 | class nsIThread; | ||||||||||
20 | |||||||||||
21 | // A wrapper for nested event loops. | ||||||||||
22 | // | ||||||||||
23 | // This function is intended to make code more obvious (do you remember | ||||||||||
24 | // what NS_ProcessNextEvent(nullptr, true) means?) and slightly more | ||||||||||
25 | // efficient, as people often pass nullptr or NS_GetCurrentThread to | ||||||||||
26 | // NS_ProcessNextEvent, which results in needless querying of the current | ||||||||||
27 | // thread every time through the loop. | ||||||||||
28 | // | ||||||||||
29 | // You should use this function in preference to NS_ProcessNextEvent inside | ||||||||||
30 | // a loop unless one of the following is true: | ||||||||||
31 | // | ||||||||||
32 | // * You need to pass `false` to NS_ProcessNextEvent; or | ||||||||||
33 | // * You need to do unusual things around the call to NS_ProcessNextEvent, | ||||||||||
34 | // such as unlocking mutexes that you are holding. | ||||||||||
35 | // | ||||||||||
36 | // If you *do* need to call NS_ProcessNextEvent manually, please do call | ||||||||||
37 | // NS_GetCurrentThread() outside of your loop and pass the returned pointer | ||||||||||
38 | // into NS_ProcessNextEvent for a tiny efficiency win. | ||||||||||
39 | namespace mozilla { | ||||||||||
40 | |||||||||||
41 | // You should normally not need to deal with this template parameter. If | ||||||||||
42 | // you enjoy esoteric event loop details, read on. | ||||||||||
43 | // | ||||||||||
44 | // If you specify that NS_ProcessNextEvent wait for an event, it is possible | ||||||||||
45 | // for NS_ProcessNextEvent to return false, i.e. to indicate that an event | ||||||||||
46 | // was not processed. This can only happen when the thread has been shut | ||||||||||
47 | // down by another thread, but is still attempting to process events outside | ||||||||||
48 | // of a nested event loop. | ||||||||||
49 | // | ||||||||||
50 | // This behavior is admittedly strange. The scenario it deals with is the | ||||||||||
51 | // following: | ||||||||||
52 | // | ||||||||||
53 | // * The current thread has been shut down by some owner thread. | ||||||||||
54 | // * The current thread is spinning an event loop waiting for some condition | ||||||||||
55 | // to become true. | ||||||||||
56 | // * Said condition is actually being fulfilled by another thread, so there | ||||||||||
57 | // are timing issues in play. | ||||||||||
58 | // | ||||||||||
59 | // Thus, there is a small window where the current thread's event loop | ||||||||||
60 | // spinning can check the condition, find it false, and call | ||||||||||
61 | // NS_ProcessNextEvent to wait for another event. But we don't actually | ||||||||||
62 | // want it to wait indefinitely, because there might not be any other events | ||||||||||
63 | // in the event loop, and the current thread can't accept dispatched events | ||||||||||
64 | // because it's being shut down. Thus, actually blocking would hang the | ||||||||||
65 | // thread, which is bad. The solution, then, is to detect such a scenario | ||||||||||
66 | // and not actually block inside NS_ProcessNextEvent. | ||||||||||
67 | // | ||||||||||
68 | // But this is a problem, because we want to return the status of | ||||||||||
69 | // NS_ProcessNextEvent to the caller of SpinEventLoopUntil if possible. In | ||||||||||
70 | // the above scenario, however, we'd stop spinning prematurely and cause | ||||||||||
71 | // all sorts of havoc. We therefore have this template parameter to | ||||||||||
72 | // control whether errors are ignored or passed out to the caller of | ||||||||||
73 | // SpinEventLoopUntil. The latter is the default; if you find yourself | ||||||||||
74 | // wanting to use the former, you should think long and hard before doing | ||||||||||
75 | // so, and write a comment like this defending your choice. | ||||||||||
76 | |||||||||||
77 | enum class ProcessFailureBehavior { | ||||||||||
78 | IgnoreAndContinue, | ||||||||||
79 | ReportToCaller, | ||||||||||
80 | }; | ||||||||||
81 | |||||||||||
82 | // SpinEventLoopUntil is a dangerous operation that can result in hangs. | ||||||||||
83 | // In particular during shutdown we want to know if we are hanging | ||||||||||
84 | // inside a nested event loop on the main thread. | ||||||||||
85 | // This is a helper annotation class to keep track of this. | ||||||||||
86 | struct MOZ_STACK_CLASS AutoNestedEventLoopAnnotation { | ||||||||||
87 | explicit AutoNestedEventLoopAnnotation(const nsACString& aEntry) | ||||||||||
88 | : mPrev(nullptr) { | ||||||||||
89 | if (NS_IsMainThread()) { | ||||||||||
90 | StaticMutexAutoLock lock(sStackMutex); | ||||||||||
91 | mPrev = sCurrent; | ||||||||||
92 | sCurrent = this; | ||||||||||
93 | if (mPrev) { | ||||||||||
94 | mStack = mPrev->mStack + "|"_ns + aEntry; | ||||||||||
95 | } else { | ||||||||||
96 | mStack = aEntry; | ||||||||||
97 | } | ||||||||||
98 | AnnotateXPCOMSpinEventLoopStack(mStack); | ||||||||||
99 | } | ||||||||||
100 | } | ||||||||||
101 | |||||||||||
102 | ~AutoNestedEventLoopAnnotation() { | ||||||||||
103 | if (NS_IsMainThread()) { | ||||||||||
104 | StaticMutexAutoLock lock(sStackMutex); | ||||||||||
105 | MOZ_ASSERT(sCurrent == this)do { static_assert( mozilla::detail::AssertionConditionType< decltype(sCurrent == this)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(sCurrent == this))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("sCurrent == this" , "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/SpinEventLoopUntil.h" , 105); AnnotateMozCrashReason("MOZ_ASSERT" "(" "sCurrent == this" ")"); do { *((volatile int*)__null) = 105; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); | ||||||||||
106 | sCurrent = mPrev; | ||||||||||
107 | if (mPrev) { | ||||||||||
108 | AnnotateXPCOMSpinEventLoopStack(mPrev->mStack); | ||||||||||
109 | } else { | ||||||||||
110 | AnnotateXPCOMSpinEventLoopStack(""_ns); | ||||||||||
111 | } | ||||||||||
112 | } | ||||||||||
113 | } | ||||||||||
114 | |||||||||||
115 | static void CopyCurrentStack(nsCString& aNestedSpinStack) { | ||||||||||
116 | // We need to copy this behind a mutex as the | ||||||||||
117 | // memory for our instances is stack-bound and | ||||||||||
118 | // can go away at any time. | ||||||||||
119 | StaticMutexAutoLock lock(sStackMutex); | ||||||||||
120 | if (sCurrent) { | ||||||||||
121 | aNestedSpinStack = sCurrent->mStack; | ||||||||||
122 | } else { | ||||||||||
123 | aNestedSpinStack = "(no nested event loop active)"_ns; | ||||||||||
124 | } | ||||||||||
125 | } | ||||||||||
126 | |||||||||||
127 | private: | ||||||||||
128 | AutoNestedEventLoopAnnotation(const AutoNestedEventLoopAnnotation&) = delete; | ||||||||||
129 | AutoNestedEventLoopAnnotation& operator=( | ||||||||||
130 | const AutoNestedEventLoopAnnotation&) = delete; | ||||||||||
131 | |||||||||||
132 | // The declarations of these statics live in nsThreadManager.cpp. | ||||||||||
133 | static AutoNestedEventLoopAnnotation* sCurrent MOZ_GUARDED_BY(sStackMutex)__attribute__((guarded_by(sStackMutex))); | ||||||||||
134 | static StaticMutex sStackMutex; | ||||||||||
135 | |||||||||||
136 | // We need this to avoid the inclusion of nsExceptionHandler.h here | ||||||||||
137 | // which can include windows.h which disturbs some dom/media/gtest. | ||||||||||
138 | // The implementation lives in nsThreadManager.cpp. | ||||||||||
139 | static void AnnotateXPCOMSpinEventLoopStack(const nsACString& aStack); | ||||||||||
140 | |||||||||||
141 | AutoNestedEventLoopAnnotation* mPrev MOZ_GUARDED_BY(sStackMutex)__attribute__((guarded_by(sStackMutex))); | ||||||||||
142 | nsCString mStack MOZ_GUARDED_BY(sStackMutex)__attribute__((guarded_by(sStackMutex))); | ||||||||||
143 | }; | ||||||||||
144 | |||||||||||
145 | // Please see the above notes for the Behavior template parameter. | ||||||||||
146 | // | ||||||||||
147 | // aVeryGoodReasonToDoThis is usually a literal string unique to each | ||||||||||
148 | // caller that can be recognized in the XPCOMSpinEventLoopStack | ||||||||||
149 | // annotation. | ||||||||||
150 | // aPredicate is the condition we wait for. | ||||||||||
151 | // aThread can be used to specify a thread, see the above introduction. | ||||||||||
152 | // It defaults to the current thread. | ||||||||||
153 | template < | ||||||||||
154 | ProcessFailureBehavior Behavior = ProcessFailureBehavior::ReportToCaller, | ||||||||||
155 | typename Pred> | ||||||||||
156 | bool SpinEventLoopUntil(const nsACString& aVeryGoodReasonToDoThis, | ||||||||||
157 | Pred&& aPredicate, nsIThread* aThread = nullptr) { | ||||||||||
158 | // Prepare the annotations | ||||||||||
159 | AutoNestedEventLoopAnnotation annotation(aVeryGoodReasonToDoThis); | ||||||||||
160 | AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_NONSENSITIVE(mozilla::Maybe<nsAutoCString> autoCStr; mozilla::Maybe< mozilla::AutoProfilerLabel> raiiObjectNsCString; if (profiler_is_active ()) { autoCStr.emplace(aVeryGoodReasonToDoThis); raiiObjectNsCString .emplace( "SpinEventLoopUntil", autoCStr->get(), JS::ProfilingCategoryPair ::OTHER, uint32_t(js::ProfilingStackFrame::Flags::NONSENSITIVE )); } | ||||||||||
161 | "SpinEventLoopUntil", OTHER, aVeryGoodReasonToDoThis)mozilla::Maybe<nsAutoCString> autoCStr; mozilla::Maybe< mozilla::AutoProfilerLabel> raiiObjectNsCString; if (profiler_is_active ()) { autoCStr.emplace(aVeryGoodReasonToDoThis); raiiObjectNsCString .emplace( "SpinEventLoopUntil", autoCStr->get(), JS::ProfilingCategoryPair ::OTHER, uint32_t(js::ProfilingStackFrame::Flags::NONSENSITIVE )); }; | ||||||||||
162 | AUTO_PROFILER_MARKER_TEXT("SpinEventLoop", OTHER, MarkerStack::Capture(),AutoProfilerTextMarker raiiObject163( "SpinEventLoop", ::mozilla ::baseprofiler::category::OTHER, MarkerStack::Capture(), aVeryGoodReasonToDoThis ) | ||||||||||
163 | aVeryGoodReasonToDoThis)AutoProfilerTextMarker raiiObject163( "SpinEventLoop", ::mozilla ::baseprofiler::category::OTHER, MarkerStack::Capture(), aVeryGoodReasonToDoThis ); | ||||||||||
164 | |||||||||||
165 | nsIThread* thread = aThread
| ||||||||||
166 | |||||||||||
167 | // From a latency perspective, spinning the event loop is like leaving script | ||||||||||
168 | // and returning to the event loop. Tell the watchdog we stopped running | ||||||||||
169 | // script (until we return). | ||||||||||
170 | mozilla::Maybe<xpc::AutoScriptActivity> asa; | ||||||||||
171 | if (NS_IsMainThread()) { | ||||||||||
172 | asa.emplace(false); | ||||||||||
173 | } | ||||||||||
174 | |||||||||||
175 | while (!aPredicate()) { | ||||||||||
176 | bool didSomething = NS_ProcessNextEvent(thread, true); | ||||||||||
177 | |||||||||||
178 | if (Behavior
| ||||||||||
179 | // Don't care what happened, continue on. | ||||||||||
180 | continue; | ||||||||||
181 | } else if (!didSomething) { | ||||||||||
182 | return false; | ||||||||||
183 | } | ||||||||||
184 | } | ||||||||||
185 | |||||||||||
186 | return true; | ||||||||||
187 | } | ||||||||||
188 | |||||||||||
189 | } // namespace mozilla | ||||||||||
190 | |||||||||||
191 | #endif // xpcom_threads_SpinEventLoopUntil_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 | /* 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 | 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 | 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() : dummy() {} |
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 dummy; |
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: 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 | // This header contains all definitions related to profiler labels. | |||
8 | // It is safe to include unconditionally, and only defines empty macros if | |||
9 | // MOZ_GECKO_PROFILER is not set. | |||
10 | ||||
11 | #ifndef ProfilerLabels_h | |||
12 | #define ProfilerLabels_h | |||
13 | ||||
14 | #include "mozilla/ProfilerState.h" | |||
15 | #include "mozilla/ProfilerThreadState.h" | |||
16 | ||||
17 | #include "js/ProfilingCategory.h" | |||
18 | #include "js/ProfilingStack.h" | |||
19 | #include "js/RootingAPI.h" | |||
20 | #include "mozilla/Assertions.h" | |||
21 | #include "mozilla/Atomics.h" | |||
22 | #include "mozilla/Attributes.h" | |||
23 | #include "mozilla/BaseProfilerRAIIMacro.h" | |||
24 | #include "mozilla/Maybe.h" | |||
25 | #include "mozilla/ProfilerThreadRegistration.h" | |||
26 | #include "mozilla/ThreadLocal.h" | |||
27 | #include "nsString.h" | |||
28 | ||||
29 | #include <stdint.h> | |||
30 | ||||
31 | struct JSContext; | |||
32 | ||||
33 | // Insert an RAII object in this scope to enter a label stack frame. Any | |||
34 | // samples collected in this scope will contain this label in their stack. | |||
35 | // The label argument must be a static C string. It is usually of the | |||
36 | // form "ClassName::FunctionName". (Ideally we'd use the compiler to provide | |||
37 | // that for us, but __func__ gives us the function name without the class | |||
38 | // name.) If the label applies to only part of a function, you can qualify it | |||
39 | // like this: "ClassName::FunctionName:PartName". | |||
40 | // | |||
41 | // Use AUTO_PROFILER_LABEL_DYNAMIC_* if you want to add additional / dynamic | |||
42 | // information to the label stack frame, and AUTO_PROFILER_LABEL_HOT if you're | |||
43 | // instrumenting functions for which overhead on the order of nanoseconds is | |||
44 | // noticeable. | |||
45 | #define AUTO_PROFILER_LABEL(label, categoryPair)mozilla::AutoProfilerLabel raiiObject45( label, nullptr, JS:: ProfilingCategoryPair::categoryPair) \ | |||
46 | mozilla::AutoProfilerLabel PROFILER_RAIIraiiObject46( \ | |||
47 | label, nullptr, JS::ProfilingCategoryPair::categoryPair) | |||
48 | ||||
49 | // Like AUTO_PROFILER_LABEL, but for super-hot code where overhead must be | |||
50 | // kept to the absolute minimum. This variant doesn't push the label if the | |||
51 | // profiler isn't running. | |||
52 | // Don't use this for long-running functions: If the profiler is started in | |||
53 | // the middle of the function, this label won't be on the stack until the | |||
54 | // function is entered the next time. As a result, category information for | |||
55 | // samples at the start of the profile can be misleading. | |||
56 | // For short-running functions, that's often an acceptable trade-off. | |||
57 | #define AUTO_PROFILER_LABEL_HOT(label, categoryPair)mozilla::AutoProfilerLabelHot raiiObject57( label, nullptr, JS ::ProfilingCategoryPair::categoryPair) \ | |||
58 | mozilla::AutoProfilerLabelHot PROFILER_RAIIraiiObject58( \ | |||
59 | label, nullptr, JS::ProfilingCategoryPair::categoryPair) | |||
60 | ||||
61 | // Similar to AUTO_PROFILER_LABEL, but that adds the RELEVANT_FOR_JS flag. | |||
62 | #define AUTO_PROFILER_LABEL_RELEVANT_FOR_JS(label, categoryPair)mozilla::AutoProfilerLabel raiiObject62( label, nullptr, JS:: ProfilingCategoryPair::categoryPair, uint32_t(js::ProfilingStackFrame ::Flags::RELEVANT_FOR_JS)) \ | |||
63 | mozilla::AutoProfilerLabel PROFILER_RAIIraiiObject63( \ | |||
64 | label, nullptr, JS::ProfilingCategoryPair::categoryPair, \ | |||
65 | uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS)) | |||
66 | ||||
67 | // Similar to AUTO_PROFILER_LABEL, but with only one argument: the category | |||
68 | // pair. The label string is taken from the category pair. This is convenient | |||
69 | // for labels like AUTO_PROFILER_LABEL_CATEGORY_PAIR(GRAPHICS_LayerBuilding) | |||
70 | // which would otherwise just repeat the string. | |||
71 | #define AUTO_PROFILER_LABEL_CATEGORY_PAIR(categoryPair)mozilla::AutoProfilerLabel raiiObject71( "", nullptr, JS::ProfilingCategoryPair ::categoryPair, uint32_t( js::ProfilingStackFrame::Flags::LABEL_DETERMINED_BY_CATEGORY_PAIR )) \ | |||
72 | mozilla::AutoProfilerLabel PROFILER_RAIIraiiObject72( \ | |||
73 | "", nullptr, JS::ProfilingCategoryPair::categoryPair, \ | |||
74 | uint32_t( \ | |||
75 | js::ProfilingStackFrame::Flags::LABEL_DETERMINED_BY_CATEGORY_PAIR)) | |||
76 | ||||
77 | // Similar to AUTO_PROFILER_LABEL_CATEGORY_PAIR but adding the RELEVANT_FOR_JS | |||
78 | // flag. | |||
79 | #define AUTO_PROFILER_LABEL_CATEGORY_PAIR_RELEVANT_FOR_JS(categoryPair)mozilla::AutoProfilerLabel raiiObject79( "", nullptr, JS::ProfilingCategoryPair ::categoryPair, uint32_t( js::ProfilingStackFrame::Flags::LABEL_DETERMINED_BY_CATEGORY_PAIR ) | uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS) ) \ | |||
80 | mozilla::AutoProfilerLabel PROFILER_RAIIraiiObject80( \ | |||
81 | "", nullptr, JS::ProfilingCategoryPair::categoryPair, \ | |||
82 | uint32_t( \ | |||
83 | js::ProfilingStackFrame::Flags::LABEL_DETERMINED_BY_CATEGORY_PAIR) | \ | |||
84 | uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS)) | |||
85 | ||||
86 | // Similar to AUTO_PROFILER_LABEL, but with an additional string. The inserted | |||
87 | // RAII object stores the cStr pointer in a field; it does not copy the string. | |||
88 | // | |||
89 | // WARNING: This means that the string you pass to this macro needs to live at | |||
90 | // least until the end of the current scope. Be careful using this macro with | |||
91 | // ns[C]String; the other AUTO_PROFILER_LABEL_DYNAMIC_* macros below are | |||
92 | // preferred because they avoid this problem. | |||
93 | // | |||
94 | // If the profiler samples the current thread and walks the label stack while | |||
95 | // this RAII object is on the stack, it will copy the supplied string into the | |||
96 | // profile buffer. So there's one string copy operation, and it happens at | |||
97 | // sample time. | |||
98 | // | |||
99 | // Compare this to the plain AUTO_PROFILER_LABEL macro, which only accepts | |||
100 | // literal strings: When the label stack frames generated by | |||
101 | // AUTO_PROFILER_LABEL are sampled, no string copy needs to be made because the | |||
102 | // profile buffer can just store the raw pointers to the literal strings. | |||
103 | // Consequently, AUTO_PROFILER_LABEL frames take up considerably less space in | |||
104 | // the profile buffer than AUTO_PROFILER_LABEL_DYNAMIC_* frames. | |||
105 | #define AUTO_PROFILER_LABEL_DYNAMIC_CSTR(label, categoryPair, cStr)mozilla::AutoProfilerLabel raiiObject105( label, cStr, JS::ProfilingCategoryPair ::categoryPair) \ | |||
106 | mozilla::AutoProfilerLabel PROFILER_RAIIraiiObject106( \ | |||
107 | label, cStr, JS::ProfilingCategoryPair::categoryPair) | |||
108 | ||||
109 | // Like AUTO_PROFILER_LABEL_DYNAMIC_CSTR, but with the NONSENSITIVE flag to | |||
110 | // note that it does not contain sensitive information (so we can include it | |||
111 | // in, for example, the BackgroundHangMonitor) | |||
112 | #define AUTO_PROFILER_LABEL_DYNAMIC_CSTR_NONSENSITIVE(label, categoryPair, \mozilla::AutoProfilerLabel raiiObject113( label, cStr, JS::ProfilingCategoryPair ::categoryPair, uint32_t(js::ProfilingStackFrame::Flags::NONSENSITIVE )) | |||
113 | cStr)mozilla::AutoProfilerLabel raiiObject113( label, cStr, JS::ProfilingCategoryPair ::categoryPair, uint32_t(js::ProfilingStackFrame::Flags::NONSENSITIVE )) \ | |||
114 | mozilla::AutoProfilerLabel PROFILER_RAIIraiiObject114( \ | |||
115 | label, cStr, JS::ProfilingCategoryPair::categoryPair, \ | |||
116 | uint32_t(js::ProfilingStackFrame::Flags::NONSENSITIVE)) | |||
117 | ||||
118 | // Similar to AUTO_PROFILER_LABEL_DYNAMIC_CSTR, but takes an nsACString. | |||
119 | // | |||
120 | // Note: The use of the Maybe<>s ensures the scopes for the dynamic string and | |||
121 | // the AutoProfilerLabel are appropriate, while also not incurring the runtime | |||
122 | // cost of the string assignment unless the profiler is active. Therefore, | |||
123 | // unlike AUTO_PROFILER_LABEL and AUTO_PROFILER_LABEL_DYNAMIC_CSTR, this macro | |||
124 | // doesn't push/pop a label when the profiler is inactive. | |||
125 | #define AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(label, categoryPair, nsCStr)mozilla::Maybe<nsAutoCString> autoCStr; mozilla::Maybe< mozilla::AutoProfilerLabel> raiiObjectNsCString; if (profiler_is_active ()) { autoCStr.emplace(nsCStr); raiiObjectNsCString.emplace(label , autoCStr->get(), JS::ProfilingCategoryPair::categoryPair ); } \ | |||
126 | mozilla::Maybe<nsAutoCString> autoCStr; \ | |||
127 | mozilla::Maybe<mozilla::AutoProfilerLabel> raiiObjectNsCString; \ | |||
128 | if (profiler_is_active()) { \ | |||
129 | autoCStr.emplace(nsCStr); \ | |||
130 | raiiObjectNsCString.emplace(label, autoCStr->get(), \ | |||
131 | JS::ProfilingCategoryPair::categoryPair); \ | |||
132 | } | |||
133 | ||||
134 | #define AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_RELEVANT_FOR_JS( \mozilla::Maybe<nsAutoCString> autoCStr; mozilla::Maybe< mozilla::AutoProfilerLabel> raiiObjectNsCString; if (profiler_is_active ()) { autoCStr.emplace(nsCStr); raiiObjectNsCString.emplace( label , autoCStr->get(), JS::ProfilingCategoryPair::categoryPair , uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS)); } | |||
135 | label, categoryPair, nsCStr)mozilla::Maybe<nsAutoCString> autoCStr; mozilla::Maybe< mozilla::AutoProfilerLabel> raiiObjectNsCString; if (profiler_is_active ()) { autoCStr.emplace(nsCStr); raiiObjectNsCString.emplace( label , autoCStr->get(), JS::ProfilingCategoryPair::categoryPair , uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS)); } \ | |||
136 | mozilla::Maybe<nsAutoCString> autoCStr; \ | |||
137 | mozilla::Maybe<mozilla::AutoProfilerLabel> raiiObjectNsCString; \ | |||
138 | if (profiler_is_active()) { \ | |||
139 | autoCStr.emplace(nsCStr); \ | |||
140 | raiiObjectNsCString.emplace( \ | |||
141 | label, autoCStr->get(), JS::ProfilingCategoryPair::categoryPair, \ | |||
142 | uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS)); \ | |||
143 | } | |||
144 | ||||
145 | // Match the conditions for MOZ_ENABLE_BACKGROUND_HANG_MONITOR | |||
146 | #if defined(NIGHTLY_BUILD1) && !defined(MOZ_DEBUG1) && !defined(MOZ_TSAN) && \ | |||
147 | !defined(MOZ_ASAN) | |||
148 | # define SHOULD_CREATE_ALL_NONSENSITIVE_LABEL_FRAMESprofiler_is_active() true | |||
149 | #else | |||
150 | # define SHOULD_CREATE_ALL_NONSENSITIVE_LABEL_FRAMESprofiler_is_active() profiler_is_active() | |||
151 | #endif | |||
152 | ||||
153 | // See note above AUTO_PROFILER_LABEL_DYNAMIC_CSTR_NONSENSITIVE | |||
154 | #define AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_NONSENSITIVE( \mozilla::Maybe<nsAutoCString> autoCStr; mozilla::Maybe< mozilla::AutoProfilerLabel> raiiObjectNsCString; if (profiler_is_active ()) { autoCStr.emplace(nsCStr); raiiObjectNsCString.emplace( label , autoCStr->get(), JS::ProfilingCategoryPair::categoryPair , uint32_t(js::ProfilingStackFrame::Flags::NONSENSITIVE)); } | |||
155 | label, categoryPair, nsCStr)mozilla::Maybe<nsAutoCString> autoCStr; mozilla::Maybe< mozilla::AutoProfilerLabel> raiiObjectNsCString; if (profiler_is_active ()) { autoCStr.emplace(nsCStr); raiiObjectNsCString.emplace( label , autoCStr->get(), JS::ProfilingCategoryPair::categoryPair , uint32_t(js::ProfilingStackFrame::Flags::NONSENSITIVE)); } \ | |||
156 | mozilla::Maybe<nsAutoCString> autoCStr; \ | |||
157 | mozilla::Maybe<mozilla::AutoProfilerLabel> raiiObjectNsCString; \ | |||
158 | if (SHOULD_CREATE_ALL_NONSENSITIVE_LABEL_FRAMESprofiler_is_active()) { \ | |||
159 | autoCStr.emplace(nsCStr); \ | |||
160 | raiiObjectNsCString.emplace( \ | |||
161 | label, autoCStr->get(), JS::ProfilingCategoryPair::categoryPair, \ | |||
162 | uint32_t(js::ProfilingStackFrame::Flags::NONSENSITIVE)); \ | |||
163 | } | |||
164 | ||||
165 | // Similar to AUTO_PROFILER_LABEL_DYNAMIC_CSTR, but takes an nsString that is | |||
166 | // is lossily converted to an ASCII string. | |||
167 | // | |||
168 | // Note: The use of the Maybe<>s ensures the scopes for the converted dynamic | |||
169 | // string and the AutoProfilerLabel are appropriate, while also not incurring | |||
170 | // the runtime cost of the string conversion unless the profiler is active. | |||
171 | // Therefore, unlike AUTO_PROFILER_LABEL and AUTO_PROFILER_LABEL_DYNAMIC_CSTR, | |||
172 | // this macro doesn't push/pop a label when the profiler is inactive. | |||
173 | #define AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(label, categoryPair, nsStr)mozilla::Maybe<NS_LossyConvertUTF16toASCII> asciiStr; mozilla ::Maybe<mozilla::AutoProfilerLabel> raiiObjectLossyNsString ; if (profiler_is_active()) { asciiStr.emplace(nsStr); raiiObjectLossyNsString .emplace(label, asciiStr->get(), JS::ProfilingCategoryPair ::categoryPair); } \ | |||
174 | mozilla::Maybe<NS_LossyConvertUTF16toASCII> asciiStr; \ | |||
175 | mozilla::Maybe<mozilla::AutoProfilerLabel> raiiObjectLossyNsString; \ | |||
176 | if (profiler_is_active()) { \ | |||
177 | asciiStr.emplace(nsStr); \ | |||
178 | raiiObjectLossyNsString.emplace(label, asciiStr->get(), \ | |||
179 | JS::ProfilingCategoryPair::categoryPair); \ | |||
180 | } | |||
181 | ||||
182 | // Similar to AUTO_PROFILER_LABEL, but accepting a JSContext* parameter, and a | |||
183 | // no-op if the profiler is disabled. | |||
184 | // Used to annotate functions for which overhead in the range of nanoseconds is | |||
185 | // noticeable. It avoids overhead from the TLS lookup because it can get the | |||
186 | // ProfilingStack from the JS context, and avoids almost all overhead in the | |||
187 | // case where the profiler is disabled. | |||
188 | #define AUTO_PROFILER_LABEL_FAST(label, categoryPair, ctx)mozilla::AutoProfilerLabelHot raiiObject188( ctx, label, nullptr , JS::ProfilingCategoryPair::categoryPair) \ | |||
189 | mozilla::AutoProfilerLabelHot PROFILER_RAIIraiiObject189( \ | |||
190 | ctx, label, nullptr, JS::ProfilingCategoryPair::categoryPair) | |||
191 | ||||
192 | // Similar to AUTO_PROFILER_LABEL_FAST, but also takes an extra string and an | |||
193 | // additional set of flags. The flags parameter should carry values from the | |||
194 | // js::ProfilingStackFrame::Flags enum. | |||
195 | #define AUTO_PROFILER_LABEL_DYNAMIC_FAST(label, dynamicString, categoryPair, \mozilla::AutoProfilerLabelHot raiiObject196( ctx, label, dynamicString , JS::ProfilingCategoryPair::categoryPair, flags) | |||
196 | ctx, flags)mozilla::AutoProfilerLabelHot raiiObject196( ctx, label, dynamicString , JS::ProfilingCategoryPair::categoryPair, flags) \ | |||
197 | mozilla::AutoProfilerLabelHot PROFILER_RAIIraiiObject197( \ | |||
198 | ctx, label, dynamicString, JS::ProfilingCategoryPair::categoryPair, \ | |||
199 | flags) | |||
200 | ||||
201 | namespace mozilla { | |||
202 | ||||
203 | #ifndef MOZ_GECKO_PROFILER1 | |||
204 | ||||
205 | class MOZ_RAII AutoProfilerLabel { | |||
206 | public: | |||
207 | // This is the AUTO_PROFILER_LABEL and AUTO_PROFILER_LABEL_DYNAMIC variant. | |||
208 | AutoProfilerLabel(const char* aLabel, const char* aDynamicString, | |||
209 | JS::ProfilingCategoryPair aCategoryPair, | |||
210 | uint32_t aFlags = 0) {} | |||
211 | ||||
212 | ~AutoProfilerLabel() {} | |||
213 | }; | |||
214 | ||||
215 | class MOZ_RAII AutoProfilerLabelHot { | |||
216 | public: | |||
217 | // This is the AUTO_PROFILER_LABEL_HOT variant. | |||
218 | AutoProfilerLabelHot(const char* aLabel, const char* aDynamicString, | |||
219 | JS::ProfilingCategoryPair aCategoryPair, | |||
220 | uint32_t aFlags = 0) {} | |||
221 | ||||
222 | // This is the AUTO_PROFILER_LABEL_FAST variant. | |||
223 | AutoProfilerLabelHot(JSContext* aJSContext, const char* aLabel, | |||
224 | const char* aDynamicString, | |||
225 | JS::ProfilingCategoryPair aCategoryPair, | |||
226 | uint32_t aFlags) {} | |||
227 | ||||
228 | ~AutoProfilerLabelHot() {} | |||
229 | }; | |||
230 | ||||
231 | #else // !MOZ_GECKO_PROFILER | |||
232 | ||||
233 | // This class creates a non-owning ProfilingStack reference. Objects of this | |||
234 | // class are stack-allocated, and so exist within a thread, and are thus bounded | |||
235 | // by the lifetime of the thread, which ensures that the references held can't | |||
236 | // be used after the ProfilingStack is destroyed. | |||
237 | class MOZ_RAII AutoProfilerLabel { | |||
238 | public: | |||
239 | // This is the AUTO_PROFILER_LABEL and AUTO_PROFILER_LABEL_DYNAMIC variant. | |||
240 | AutoProfilerLabel(const char* aLabel, const char* aDynamicString, | |||
241 | JS::ProfilingCategoryPair aCategoryPair, | |||
242 | uint32_t aFlags = 0) { | |||
243 | // Get the ProfilingStack from TLS. | |||
244 | mProfilingStack = profiler::ThreadRegistration::WithOnThreadRefOr( | |||
245 | [](profiler::ThreadRegistration::OnThreadRef aThread) { | |||
246 | return &aThread.UnlockedConstReaderAndAtomicRWRef() | |||
247 | .ProfilingStackRef(); | |||
248 | }, | |||
249 | nullptr); | |||
250 | if (mProfilingStack) { | |||
251 | mProfilingStack->pushLabelFrame(aLabel, aDynamicString, this, | |||
252 | aCategoryPair, aFlags); | |||
253 | } | |||
254 | } | |||
255 | ||||
256 | ~AutoProfilerLabel() { | |||
257 | // This function runs both on and off the main thread. | |||
258 | ||||
259 | if (mProfilingStack) { | |||
| ||||
260 | mProfilingStack->pop(); | |||
261 | } | |||
262 | } | |||
263 | ||||
264 | private: | |||
265 | // We save a ProfilingStack pointer in the ctor so we don't have to redo the | |||
266 | // TLS lookup in the dtor. | |||
267 | ProfilingStack* mProfilingStack; | |||
268 | }; | |||
269 | ||||
270 | class MOZ_RAII AutoProfilerLabelHot { | |||
271 | public: | |||
272 | // This is the AUTO_PROFILER_LABEL_HOT variant. It does nothing if | |||
273 | // the profiler is inactive. | |||
274 | AutoProfilerLabelHot(const char* aLabel, const char* aDynamicString, | |||
275 | JS::ProfilingCategoryPair aCategoryPair, | |||
276 | uint32_t aFlags = 0) { | |||
277 | if (MOZ_LIKELY(!profiler_is_active())(__builtin_expect(!!(!profiler_is_active()), 1))) { | |||
278 | mProfilingStack = nullptr; | |||
279 | return; | |||
280 | } | |||
281 | ||||
282 | // Get the ProfilingStack from TLS. | |||
283 | mProfilingStack = profiler::ThreadRegistration::WithOnThreadRefOr( | |||
284 | [](profiler::ThreadRegistration::OnThreadRef aThread) { | |||
285 | return &aThread.UnlockedConstReaderAndAtomicRWRef() | |||
286 | .ProfilingStackRef(); | |||
287 | }, | |||
288 | nullptr); | |||
289 | if (mProfilingStack) { | |||
290 | mProfilingStack->pushLabelFrame(aLabel, aDynamicString, this, | |||
291 | aCategoryPair, aFlags); | |||
292 | } | |||
293 | } | |||
294 | ||||
295 | // This is the AUTO_PROFILER_LABEL_FAST variant. It retrieves the | |||
296 | // ProfilingStack from the JSContext and does nothing if the profiler is | |||
297 | // inactive. | |||
298 | AutoProfilerLabelHot(JSContext* aJSContext, const char* aLabel, | |||
299 | const char* aDynamicString, | |||
300 | JS::ProfilingCategoryPair aCategoryPair, | |||
301 | uint32_t aFlags) { | |||
302 | mProfilingStack = js::GetContextProfilingStackIfEnabled(aJSContext); | |||
303 | if (MOZ_UNLIKELY(mProfilingStack)(__builtin_expect(!!(mProfilingStack), 0))) { | |||
304 | mProfilingStack->pushLabelFrame(aLabel, aDynamicString, this, | |||
305 | aCategoryPair, aFlags); | |||
306 | } | |||
307 | } | |||
308 | ||||
309 | ~AutoProfilerLabelHot() { | |||
310 | // This function runs both on and off the main thread. | |||
311 | if (MOZ_UNLIKELY(mProfilingStack)(__builtin_expect(!!(mProfilingStack), 0))) { | |||
312 | mProfilingStack->pop(); | |||
313 | } | |||
314 | } | |||
315 | ||||
316 | private: | |||
317 | // We save a ProfilingStack pointer in the ctor so we don't have to redo the | |||
318 | // TLS lookup in the dtor. | |||
319 | ProfilingStack* mProfilingStack; | |||
320 | }; | |||
321 | ||||
322 | #endif // !MOZ_GECKO_PROFILER | |||
323 | ||||
324 | } // namespace mozilla | |||
325 | ||||
326 | #endif // ProfilerLabels_h |