Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/mozilla/CycleCollectedJSRuntime.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_CycleCollectedJSRuntime_h
8
#define mozilla_CycleCollectedJSRuntime_h
9
10
#include <queue>
11
12
#include "mozilla/CycleCollectedJSContext.h"
13
#include "mozilla/DeferredFinalize.h"
14
#include "mozilla/LinkedList.h"
15
#include "mozilla/mozalloc.h"
16
#include "mozilla/MemoryReporting.h"
17
#include "mozilla/SegmentedVector.h"
18
#include "jsapi.h"
19
#include "jsfriendapi.h"
20
21
#include "nsCycleCollectionParticipant.h"
22
#include "nsDataHashtable.h"
23
#include "nsHashKeys.h"
24
#include "nsTHashtable.h"
25
26
class nsCycleCollectionNoteRootCallback;
27
class nsIException;
28
class nsIRunnable;
29
class nsWrapperCache;
30
31
namespace js {
32
struct Class;
33
} // namespace js
34
35
namespace mozilla {
36
37
class JSGCThingParticipant: public nsCycleCollectionParticipant
38
{
39
public:
40
  constexpr JSGCThingParticipant()
41
0
    : nsCycleCollectionParticipant(false) {}
42
43
  NS_IMETHOD_(void) Root(void*) override
44
0
  {
45
0
    MOZ_ASSERT(false, "Don't call Root on GC things");
46
0
  }
47
48
  NS_IMETHOD_(void) Unlink(void*) override
49
0
  {
50
0
    MOZ_ASSERT(false, "Don't call Unlink on GC things, as they may be dead");
51
0
  }
52
53
  NS_IMETHOD_(void) Unroot(void*) override
54
0
  {
55
0
    MOZ_ASSERT(false, "Don't call Unroot on GC things, as they may be dead");
56
0
  }
57
58
  NS_IMETHOD_(void) DeleteCycleCollectable(void* aPtr) override
59
0
  {
60
0
    MOZ_ASSERT(false, "Can't directly delete a cycle collectable GC thing");
61
0
  }
62
63
  NS_IMETHOD TraverseNative(void* aPtr, nsCycleCollectionTraversalCallback& aCb) override;
64
65
  NS_DECL_CYCLE_COLLECTION_CLASS_NAME_METHOD(JSGCThingParticipant)
66
};
67
68
class JSZoneParticipant : public nsCycleCollectionParticipant
69
{
70
public:
71
  constexpr JSZoneParticipant(): nsCycleCollectionParticipant(false)
72
0
  {
73
0
  }
74
75
  NS_IMETHOD_(void) Root(void*) override
76
0
  {
77
0
    MOZ_ASSERT(false, "Don't call Root on GC things");
78
0
  }
79
80
  NS_IMETHOD_(void) Unlink(void*) override
81
0
  {
82
0
    MOZ_ASSERT(false, "Don't call Unlink on GC things, as they may be dead");
83
0
  }
84
85
  NS_IMETHOD_(void) Unroot(void*) override
86
0
  {
87
0
    MOZ_ASSERT(false, "Don't call Unroot on GC things, as they may be dead");
88
0
  }
89
90
  NS_IMETHOD_(void) DeleteCycleCollectable(void*) override
91
0
  {
92
0
    MOZ_ASSERT(false, "Can't directly delete a cycle collectable GC thing");
93
0
  }
94
95
  NS_IMETHOD TraverseNative(void* aPtr, nsCycleCollectionTraversalCallback& aCb) override;
96
97
  NS_DECL_CYCLE_COLLECTION_CLASS_NAME_METHOD(JSZoneParticipant)
98
};
99
100
class IncrementalFinalizeRunnable;
101
102
struct JSHolderInfo
103
{
104
  void* mHolder;
105
  nsScriptObjectTracer* mTracer;
106
};
107
108
class CycleCollectedJSRuntime
109
{
110
  friend class JSGCThingParticipant;
111
  friend class JSZoneParticipant;
112
  friend class IncrementalFinalizeRunnable;
113
  friend class CycleCollectedJSContext;
114
protected:
115
  CycleCollectedJSRuntime(JSContext* aMainContext);
116
  virtual ~CycleCollectedJSRuntime();
117
118
  virtual void Shutdown(JSContext* cx);
119
120
  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
121
  void UnmarkSkippableJSHolders();
122
123
  virtual void
124
0
  TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& aCb) {}
125
0
  virtual void TraceAdditionalNativeGrayRoots(JSTracer* aTracer) {}
126
127
0
  virtual void CustomGCCallback(JSGCStatus aStatus) {}
128
0
  virtual void CustomOutOfMemoryCallback() {}
129
130
  LinkedList<CycleCollectedJSContext>& Contexts()
131
36
  {
132
36
    return mContexts;
133
36
  }
134
135
private:
136
  void
137
  DescribeGCThing(bool aIsMarked, JS::GCCellPtr aThing,
138
                  nsCycleCollectionTraversalCallback& aCb) const;
139
140
  virtual bool
141
  DescribeCustomObjects(JSObject* aObject, const js::Class* aClasp,
142
                        char (&aName)[72]) const
143
0
  {
144
0
    return false; // We did nothing.
145
0
  }
146
147
  void
148
  NoteGCThingJSChildren(JS::GCCellPtr aThing,
149
                        nsCycleCollectionTraversalCallback& aCb) const;
150
151
  void
152
  NoteGCThingXPCOMChildren(const js::Class* aClasp, JSObject* aObj,
153
                           nsCycleCollectionTraversalCallback& aCb) const;
154
155
  virtual bool
156
  NoteCustomGCThingXPCOMChildren(const js::Class* aClasp, JSObject* aObj,
157
                                 nsCycleCollectionTraversalCallback& aCb) const
158
0
  {
159
0
    return false; // We did nothing.
160
0
  }
161
162
  enum TraverseSelect {
163
    TRAVERSE_CPP,
164
    TRAVERSE_FULL
165
  };
166
167
  void
168
  TraverseGCThing(TraverseSelect aTs, JS::GCCellPtr aThing,
169
                  nsCycleCollectionTraversalCallback& aCb);
170
171
  void
172
  TraverseZone(JS::Zone* aZone, nsCycleCollectionTraversalCallback& aCb);
173
174
  static void
175
  TraverseObjectShim(void* aData, JS::GCCellPtr aThing);
176
177
  void TraverseNativeRoots(nsCycleCollectionNoteRootCallback& aCb);
178
179
  static void TraceBlackJS(JSTracer* aTracer, void* aData);
180
  static void TraceGrayJS(JSTracer* aTracer, void* aData);
181
  static void GCCallback(JSContext* aContext, JSGCStatus aStatus, void* aData);
182
  static void GCSliceCallback(JSContext* aContext, JS::GCProgress aProgress,
183
                              const JS::GCDescription& aDesc);
184
  static void GCNurseryCollectionCallback(JSContext* aContext,
185
                                          JS::GCNurseryProgress aProgress,
186
                                          JS::gcreason::Reason aReason);
187
  static void OutOfMemoryCallback(JSContext* aContext, void* aData);
188
  /**
189
   * Callback for reporting external string memory.
190
   */
191
  static size_t SizeofExternalStringCallback(JSString* aStr,
192
                                             mozilla::MallocSizeOf aMallocSizeOf);
193
194
  static bool ContextCallback(JSContext* aCx, unsigned aOperation,
195
                              void* aData);
196
197
0
  virtual void TraceNativeBlackRoots(JSTracer* aTracer) { };
198
  void TraceNativeGrayRoots(JSTracer* aTracer);
199
200
public:
201
  void FinalizeDeferredThings(CycleCollectedJSContext::DeferredFinalizeType aType);
202
203
  virtual void PrepareForForgetSkippable() = 0;
204
  virtual void BeginCycleCollectionCallback() = 0;
205
  virtual void EndCycleCollectionCallback(CycleCollectorResults& aResults) = 0;
206
  virtual void DispatchDeferredDeletion(bool aContinuation, bool aPurge = false) = 0;
207
208
  // Two conditions, JSOutOfMemory and JSLargeAllocationFailure, are noted in
209
  // crash reports. Here are the values that can appear in the reports:
210
  enum class OOMState : uint32_t {
211
    // The condition has never happened. No entry appears in the crash report.
212
    OK,
213
214
    // We are currently reporting the given condition.
215
    //
216
    // Suppose a crash report contains "JSLargeAllocationFailure:
217
    // Reporting". This means we crashed while executing memory-pressure
218
    // observers, trying to shake loose some memory. The large allocation in
219
    // question did not return null: it is still on the stack. Had we not
220
    // crashed, it would have been retried.
221
    Reporting,
222
223
    // The condition has been reported since the last GC.
224
    //
225
    // If a crash report contains "JSOutOfMemory: Reported", that means a small
226
    // allocation failed, and then we crashed, probably due to buggy
227
    // error-handling code that ran after allocation returned null.
228
    //
229
    // This contrasts with "Reporting" which means that no error-handling code
230
    // had executed yet.
231
    Reported,
232
233
    // The condition has happened, but a GC cycle ended since then.
234
    //
235
    // GC is taken as a proxy for "we've been banging on the heap a good bit
236
    // now and haven't crashed; the OOM was probably handled correctly".
237
    Recovered
238
  };
239
240
  const char* OOMStateToString(const OOMState aOomState) const;
241
242
  void SetLargeAllocationFailure(OOMState aNewState);
243
244
  void AnnotateAndSetOutOfMemory(OOMState* aStatePtr, OOMState aNewState);
245
  void OnGC(JSContext* aContext, JSGCStatus aStatus);
246
  void OnOutOfMemory();
247
  void OnLargeAllocationFailure();
248
249
0
  JSRuntime* Runtime() { return mJSRuntime; }
250
0
  const JSRuntime* Runtime() const { return mJSRuntime; }
251
252
  bool HasPendingIdleGCTask() const
253
0
  {
254
0
    // Idle GC task associates with JSRuntime.
255
0
    MOZ_ASSERT_IF(mHasPendingIdleGCTask, Runtime());
256
0
    return mHasPendingIdleGCTask;
257
0
  }
258
  void SetPendingIdleGCTask()
259
0
  {
260
0
    // Idle GC task associates with JSRuntime.
261
0
    MOZ_ASSERT(Runtime());
262
0
    mHasPendingIdleGCTask = true;
263
0
  }
264
0
  void ClearPendingIdleGCTask() { mHasPendingIdleGCTask = false; }
265
266
  void RunIdleTimeGCTask()
267
0
  {
268
0
    if (HasPendingIdleGCTask()) {
269
0
      JS::RunIdleTimeGCTask(Runtime());
270
0
      ClearPendingIdleGCTask();
271
0
    }
272
0
  }
273
274
  bool IsIdleGCTaskNeeded()
275
0
  {
276
0
    return !HasPendingIdleGCTask() && Runtime() && JS::IsIdleGCTaskNeeded(Runtime());
277
0
  }
278
279
public:
280
  void AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer);
281
  void RemoveJSHolder(void* aHolder);
282
#ifdef DEBUG
283
  bool IsJSHolder(void* aHolder);
284
  void AssertNoObjectsToTrace(void* aPossibleJSHolder);
285
#endif
286
287
  nsCycleCollectionParticipant* GCThingParticipant();
288
  nsCycleCollectionParticipant* ZoneParticipant();
289
290
  nsresult TraverseRoots(nsCycleCollectionNoteRootCallback& aCb);
291
  virtual bool UsefulToMergeZones() const;
292
  void FixWeakMappingGrayBits() const;
293
  void CheckGrayBits() const;
294
  bool AreGCGrayBitsValid() const;
295
  void GarbageCollect(uint32_t aReason) const;
296
297
  // This needs to be an nsWrapperCache, not a JSObject, because we need to know
298
  // when our object gets moved.  But we can't trace it (and hence update our
299
  // storage), because we do not want to keep it alive.  nsWrapperCache handles
300
  // this for us via its "object moved" handling.
301
  void NurseryWrapperAdded(nsWrapperCache* aCache);
302
  void NurseryWrapperPreserved(JSObject* aWrapper);
303
  void JSObjectsTenured();
304
305
  void DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
306
                        DeferredFinalizeFunction aFunc,
307
                        void* aThing);
308
  void DeferredFinalize(nsISupports* aSupports);
309
310
  void DumpJSHeap(FILE* aFile);
311
312
  // Add aZone to the set of zones waiting for a GC.
313
  void AddZoneWaitingForGC(JS::Zone* aZone)
314
0
  {
315
0
    mZonesWaitingForGC.PutEntry(aZone);
316
0
  }
317
318
  // Prepare any zones for GC that have been passed to AddZoneWaitingForGC()
319
  // since the last GC or since the last call to PrepareWaitingZonesForGC(),
320
  // whichever was most recent. If there were no such zones, prepare for a
321
  // full GC.
322
  void PrepareWaitingZonesForGC();
323
324
  // Get the current thread's CycleCollectedJSRuntime.  Returns null if there
325
  // isn't one.
326
  static CycleCollectedJSRuntime* Get();
327
328
  void AddContext(CycleCollectedJSContext* aContext);
329
  void RemoveContext(CycleCollectedJSContext* aContext);
330
331
#ifdef NIGHTLY_BUILD
332
  bool GetRecentDevError(JSContext* aContext, JS::MutableHandle<JS::Value> aError);
333
  void ClearRecentDevError();
334
#endif // defined(NIGHTLY_BUILD)
335
336
private:
337
  LinkedList<CycleCollectedJSContext> mContexts;
338
339
  JSGCThingParticipant mGCThingCycleCollectorGlobal;
340
341
  JSZoneParticipant mJSZoneCycleCollectorGlobal;
342
343
  JSRuntime* mJSRuntime;
344
  bool mHasPendingIdleGCTask;
345
346
  JS::GCSliceCallback mPrevGCSliceCallback;
347
  JS::GCNurseryCollectionCallback mPrevGCNurseryCollectionCallback;
348
349
  mozilla::TimeStamp mLatestNurseryCollectionStart;
350
351
  SegmentedVector<JSHolderInfo, 1024, InfallibleAllocPolicy> mJSHolders;
352
  nsDataHashtable<nsPtrHashKey<void>, JSHolderInfo*> mJSHolderMap;
353
354
  typedef nsDataHashtable<nsFuncPtrHashKey<DeferredFinalizeFunction>, void*>
355
    DeferredFinalizerTable;
356
  DeferredFinalizerTable mDeferredFinalizerTable;
357
358
  RefPtr<IncrementalFinalizeRunnable> mFinalizeRunnable;
359
360
  OOMState mOutOfMemoryState;
361
  OOMState mLargeAllocationFailureState;
362
363
  static const size_t kSegmentSize = 512;
364
  SegmentedVector<nsWrapperCache*, kSegmentSize, InfallibleAllocPolicy>
365
    mNurseryObjects;
366
  SegmentedVector<JS::PersistentRooted<JSObject*>, kSegmentSize,
367
                  InfallibleAllocPolicy>
368
    mPreservedNurseryObjects;
369
370
  nsTHashtable<nsPtrHashKey<JS::Zone>> mZonesWaitingForGC;
371
372
  struct EnvironmentPreparer : public js::ScriptEnvironmentPreparer {
373
    void invoke(JS::HandleObject global, Closure& closure) override;
374
  };
375
  EnvironmentPreparer mEnvironmentPreparer;
376
377
#ifdef DEBUG
378
  bool mShutdownCalled;
379
#endif
380
381
#ifdef NIGHTLY_BUILD
382
  // Implementation of the error interceptor.
383
  // Built on nightly only to avoid any possible performance impact on release
384
385
  struct ErrorInterceptor final : public JSErrorInterceptor {
386
    virtual void interceptError(JSContext* cx, const JS::Value& val) override;
387
    void Shutdown(JSRuntime* rt);
388
389
    // Copy of the details of the exception.
390
    // We store this rather than the exception itself to avoid dealing with complicated
391
    // garbage-collection scenarios, e.g. a JSContext being killed while we still hold
392
    // onto an exception thrown from it.
393
    struct ErrorDetails {
394
      nsString mFilename;
395
      nsString mMessage;
396
      nsString mStack;
397
      JSExnType mType;
398
      uint32_t mLine;
399
      uint32_t mColumn;
400
    };
401
402
    // If we have encountered at least one developer error,
403
    // the first error we have encountered. Otherwise, or
404
    // if we have reset since the latest error, `None`.
405
    Maybe<ErrorDetails> mThrownError;
406
  };
407
  ErrorInterceptor mErrorInterceptor;
408
409
#endif // defined(NIGHTLY_BUILD)
410
411
};
412
413
void TraceScriptHolder(nsISupports* aHolder, JSTracer* aTracer);
414
415
// Returns true if the JS::TraceKind is one the cycle collector cares about.
416
// Everything used as WeakMap key should be listed here, to represent the key
417
// in cycle collector's graph, otherwise the key is considered to be pointed
418
// from somewhere unknown, and results in leaking the subgraph which contains
419
// the key.
420
// See the comments in NoteWeakMapsTracer::trace for more details.
421
inline bool AddToCCKind(JS::TraceKind aKind)
422
0
{
423
0
  return aKind == JS::TraceKind::Object ||
424
0
         aKind == JS::TraceKind::Script ||
425
0
         aKind == JS::TraceKind::LazyScript ||
426
0
         aKind == JS::TraceKind::Scope ||
427
0
         aKind == JS::TraceKind::RegExpShared;
428
0
}
429
430
} // namespace mozilla
431
432
#endif // mozilla_CycleCollectedJSRuntime_h