Coverage Report

Created: 2018-09-25 14:53

/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