/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 |