/work/obj-fuzz/dist/include/mozilla/CycleCollectedJSContext.h
Line | Count | Source (jump to first uncovered line) |
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 mozilla_CycleCollectedJSContext_h |
8 | | #define mozilla_CycleCollectedJSContext_h |
9 | | |
10 | | #include <queue> |
11 | | |
12 | | #include "mozilla/DeferredFinalize.h" |
13 | | #include "mozilla/LinkedList.h" |
14 | | #include "mozilla/mozalloc.h" |
15 | | #include "mozilla/MemoryReporting.h" |
16 | | #include "mozilla/dom/AtomList.h" |
17 | | #include "jsapi.h" |
18 | | #include "jsfriendapi.h" |
19 | | |
20 | | #include "nsCOMPtr.h" |
21 | | #include "nsCycleCollectionParticipant.h" |
22 | | #include "nsTArray.h" |
23 | | |
24 | | class nsCycleCollectionNoteRootCallback; |
25 | | class nsIRunnable; |
26 | | class nsThread; |
27 | | class nsWrapperCache; |
28 | | |
29 | | namespace mozilla { |
30 | | class AutoSlowOperation; |
31 | | |
32 | | class CycleCollectedJSRuntime; |
33 | | |
34 | | namespace dom { |
35 | | class Exception; |
36 | | class WorkerJSContext; |
37 | | class WorkletJSContext; |
38 | | } // namespace dom |
39 | | |
40 | | // Contains various stats about the cycle collection. |
41 | | struct CycleCollectorResults |
42 | | { |
43 | | CycleCollectorResults() |
44 | 3 | { |
45 | 3 | // Initialize here so when we increment mNumSlices the first time we're |
46 | 3 | // not using uninitialized memory. |
47 | 3 | Init(); |
48 | 3 | } |
49 | | |
50 | | void Init() |
51 | 3 | { |
52 | 3 | mForcedGC = false; |
53 | 3 | mMergedZones = false; |
54 | 3 | mAnyManual = false; |
55 | 3 | mVisitedRefCounted = 0; |
56 | 3 | mVisitedGCed = 0; |
57 | 3 | mFreedRefCounted = 0; |
58 | 3 | mFreedGCed = 0; |
59 | 3 | mFreedJSZones = 0; |
60 | 3 | mNumSlices = 1; |
61 | 3 | // mNumSlices is initialized to one, because we call Init() after the |
62 | 3 | // per-slice increment of mNumSlices has already occurred. |
63 | 3 | } |
64 | | |
65 | | bool mForcedGC; |
66 | | bool mMergedZones; |
67 | | bool mAnyManual; // true if any slice of the CC was manually triggered, or at shutdown. |
68 | | uint32_t mVisitedRefCounted; |
69 | | uint32_t mVisitedGCed; |
70 | | uint32_t mFreedRefCounted; |
71 | | uint32_t mFreedGCed; |
72 | | uint32_t mFreedJSZones; |
73 | | uint32_t mNumSlices; |
74 | | }; |
75 | | |
76 | | class MicroTaskRunnable |
77 | | { |
78 | | public: |
79 | 0 | MicroTaskRunnable() {} |
80 | | NS_INLINE_DECL_REFCOUNTING(MicroTaskRunnable) |
81 | | virtual void Run(AutoSlowOperation& aAso) = 0; |
82 | 0 | virtual bool Suppressed() { return false; } |
83 | | protected: |
84 | 0 | virtual ~MicroTaskRunnable() {} |
85 | | }; |
86 | | |
87 | | class CycleCollectedJSContext |
88 | | : dom::PerThreadAtomCache |
89 | | , public LinkedListElement<CycleCollectedJSContext> |
90 | | { |
91 | | friend class CycleCollectedJSRuntime; |
92 | | |
93 | | protected: |
94 | | CycleCollectedJSContext(); |
95 | | virtual ~CycleCollectedJSContext(); |
96 | | |
97 | | MOZ_IS_CLASS_INIT |
98 | | nsresult Initialize(JSRuntime* aParentRuntime, |
99 | | uint32_t aMaxBytes, |
100 | | uint32_t aMaxNurseryBytes); |
101 | | |
102 | | // See explanation in mIsPrimaryContext. |
103 | | MOZ_IS_CLASS_INIT |
104 | | nsresult InitializeNonPrimary(CycleCollectedJSContext* aPrimaryContext); |
105 | | |
106 | | virtual CycleCollectedJSRuntime* CreateRuntime(JSContext* aCx) = 0; |
107 | | |
108 | | size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; |
109 | | |
110 | | private: |
111 | | MOZ_IS_CLASS_INIT |
112 | | void InitializeCommon(); |
113 | | |
114 | | static JSObject* GetIncumbentGlobalCallback(JSContext* aCx); |
115 | | static bool EnqueuePromiseJobCallback(JSContext* aCx, |
116 | | JS::HandleObject aJob, |
117 | | JS::HandleObject aAllocationSite, |
118 | | JS::HandleObject aIncumbentGlobal, |
119 | | void* aData); |
120 | | static void PromiseRejectionTrackerCallback(JSContext* aCx, |
121 | | JS::HandleObject aPromise, |
122 | | JS::PromiseRejectionHandlingState state, |
123 | | void* aData); |
124 | | |
125 | | void AfterProcessMicrotasks(); |
126 | | public: |
127 | | void ProcessStableStateQueue(); |
128 | | private: |
129 | | void CleanupIDBTransactions(uint32_t aRecursionDepth); |
130 | | |
131 | | public: |
132 | | enum DeferredFinalizeType { |
133 | | FinalizeIncrementally, |
134 | | FinalizeNow, |
135 | | }; |
136 | | |
137 | 0 | virtual dom::WorkerJSContext* GetAsWorkerJSContext() { return nullptr; } |
138 | 0 | virtual dom::WorkletJSContext* GetAsWorkletJSContext() { return nullptr; } |
139 | | |
140 | | CycleCollectedJSRuntime* Runtime() const |
141 | 20.4M | { |
142 | 20.4M | MOZ_ASSERT(mRuntime); |
143 | 20.4M | return mRuntime; |
144 | 20.4M | } |
145 | | |
146 | | already_AddRefed<dom::Exception> GetPendingException() const; |
147 | | void SetPendingException(dom::Exception* aException); |
148 | | |
149 | | std::queue<RefPtr<MicroTaskRunnable>>& GetMicroTaskQueue(); |
150 | | std::queue<RefPtr<MicroTaskRunnable>>& GetDebuggerMicroTaskQueue(); |
151 | | |
152 | | JSContext* Context() const |
153 | 61.7M | { |
154 | 61.7M | MOZ_ASSERT(mJSContext); |
155 | 61.7M | return mJSContext; |
156 | 61.7M | } |
157 | | |
158 | | JS::RootingContext* RootingCx() const |
159 | 3.24M | { |
160 | 3.24M | MOZ_ASSERT(mJSContext); |
161 | 3.24M | return JS::RootingContext::get(mJSContext); |
162 | 3.24M | } |
163 | | |
164 | | void SetTargetedMicroTaskRecursionDepth(uint32_t aDepth) |
165 | 0 | { |
166 | 0 | mTargetedMicroTaskRecursionDepth = aDepth; |
167 | 0 | } |
168 | | |
169 | | protected: |
170 | 0 | JSContext* MaybeContext() const { return mJSContext; } |
171 | | |
172 | | public: |
173 | | // nsThread entrypoints |
174 | | virtual void BeforeProcessTask(bool aMightBlock); |
175 | | virtual void AfterProcessTask(uint32_t aRecursionDepth); |
176 | | |
177 | | // Check whether we need an idle GC task. |
178 | | void IsIdleGCTaskNeeded(); |
179 | | |
180 | | uint32_t RecursionDepth(); |
181 | | |
182 | | // Run in stable state (call through nsContentUtils) |
183 | | void RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable); |
184 | | |
185 | | void AddPendingIDBTransaction(already_AddRefed<nsIRunnable>&& aTransaction); |
186 | | |
187 | | // Get the CycleCollectedJSContext for a JSContext. |
188 | | // Returns null only if Initialize() has not completed on or during |
189 | | // destruction of the CycleCollectedJSContext. |
190 | | static CycleCollectedJSContext* GetFor(JSContext* aCx); |
191 | | |
192 | | // Get the current thread's CycleCollectedJSContext. Returns null if there |
193 | | // isn't one. |
194 | | static CycleCollectedJSContext* Get(); |
195 | | |
196 | | // Queue an async microtask to the current main or worker thread. |
197 | | virtual void DispatchToMicroTask(already_AddRefed<MicroTaskRunnable> aRunnable); |
198 | | |
199 | | // Call EnterMicroTask when you're entering JS execution. |
200 | | // Usually the best way to do this is to use nsAutoMicroTask. |
201 | | void EnterMicroTask() |
202 | 0 | { |
203 | 0 | ++mMicroTaskLevel; |
204 | 0 | } |
205 | | |
206 | | void LeaveMicroTask() |
207 | 0 | { |
208 | 0 | if (--mMicroTaskLevel == 0) { |
209 | 0 | PerformMicroTaskCheckPoint(); |
210 | 0 | } |
211 | 0 | } |
212 | | |
213 | | bool IsInMicroTask() |
214 | 0 | { |
215 | 0 | return mMicroTaskLevel != 0; |
216 | 0 | } |
217 | | |
218 | | uint32_t MicroTaskLevel() |
219 | 0 | { |
220 | 0 | return mMicroTaskLevel; |
221 | 0 | } |
222 | | |
223 | | void SetMicroTaskLevel(uint32_t aLevel) |
224 | 0 | { |
225 | 0 | mMicroTaskLevel = aLevel; |
226 | 0 | } |
227 | | |
228 | | bool PerformMicroTaskCheckPoint(bool aForce = false); |
229 | | |
230 | | void PerformDebuggerMicroTaskCheckpoint(); |
231 | | |
232 | | bool IsInStableOrMetaStableState() |
233 | 0 | { |
234 | 0 | return mDoingStableStates; |
235 | 0 | } |
236 | | |
237 | | // Storage for watching rejected promises waiting for some client to |
238 | | // consume their rejection. |
239 | | // Promises in this list have been rejected in the last turn of the |
240 | | // event loop without the rejection being handled. |
241 | | // Note that this can contain nullptrs in place of promises removed because |
242 | | // they're consumed before it'd be reported. |
243 | | JS::PersistentRooted<JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>> mUncaughtRejections; |
244 | | |
245 | | // Promises in this list have previously been reported as rejected |
246 | | // (because they were in the above list), but the rejection was handled |
247 | | // in the last turn of the event loop. |
248 | | JS::PersistentRooted<JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>> mConsumedRejections; |
249 | | nsTArray<nsCOMPtr<nsISupports /* UncaughtRejectionObserver */ >> mUncaughtRejectionObservers; |
250 | | |
251 | | virtual bool IsSystemCaller() const = 0; |
252 | | |
253 | | private: |
254 | | // A primary context owns the mRuntime. Non-main-thread contexts should always |
255 | | // be primary. On the main thread, the primary context should be the first one |
256 | | // created and the last one destroyed. Non-primary contexts are used for |
257 | | // cooperatively scheduled threads. |
258 | | bool mIsPrimaryContext; |
259 | | |
260 | | CycleCollectedJSRuntime* mRuntime; |
261 | | |
262 | | JSContext* mJSContext; |
263 | | |
264 | | nsCOMPtr<dom::Exception> mPendingException; |
265 | | nsThread* mOwningThread; // Manual refcounting to avoid include hell. |
266 | | |
267 | | struct PendingIDBTransactionData |
268 | | { |
269 | | nsCOMPtr<nsIRunnable> mTransaction; |
270 | | uint32_t mRecursionDepth; |
271 | | }; |
272 | | |
273 | | nsTArray<nsCOMPtr<nsIRunnable>> mStableStateEvents; |
274 | | nsTArray<PendingIDBTransactionData> mPendingIDBTransactions; |
275 | | uint32_t mBaseRecursionDepth; |
276 | | bool mDoingStableStates; |
277 | | |
278 | | // If set to none 0, microtasks will be processed only when recursion depth |
279 | | // is the set value. |
280 | | uint32_t mTargetedMicroTaskRecursionDepth; |
281 | | |
282 | | uint32_t mMicroTaskLevel; |
283 | | |
284 | | std::queue<RefPtr<MicroTaskRunnable>> mPendingMicroTaskRunnables; |
285 | | std::queue<RefPtr<MicroTaskRunnable>> mDebuggerMicroTaskQueue; |
286 | | |
287 | | uint32_t mMicroTaskRecursionDepth; |
288 | | }; |
289 | | |
290 | | class MOZ_STACK_CLASS nsAutoMicroTask |
291 | | { |
292 | | public: |
293 | | nsAutoMicroTask() |
294 | 0 | { |
295 | 0 | CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get(); |
296 | 0 | if (ccjs) { |
297 | 0 | ccjs->EnterMicroTask(); |
298 | 0 | } |
299 | 0 | } |
300 | | ~nsAutoMicroTask() |
301 | 0 | { |
302 | 0 | CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get(); |
303 | 0 | if (ccjs) { |
304 | 0 | ccjs->LeaveMicroTask(); |
305 | 0 | } |
306 | 0 | } |
307 | | }; |
308 | | |
309 | | } // namespace mozilla |
310 | | |
311 | | #endif // mozilla_CycleCollectedJSContext_h |