Files
palemoon27/xpcom/base/CycleCollectedJSRuntime.cpp
T
roytam1 049b3235ac import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1207494 - Part 8: Remove use of expression closure from dom/identity/. r=ferjm (34fac889f8)
- minor crash thing (13fe2827c2)
- Bug 1243359 - initialize mScrollSeriesNumber in all ScrollWheelInput constructors. r=roc (94163d8eca)
- fix misspatch (b380a7bbe4)
- Bug 1207412 - Make SystemTimeConverter use TimeStamp::Now() when converting times; r=karlt (f408549605)
- Bug 1212102 - Drop unnecessary assertion comparing timestamps in SystemTimeConverter; r=karlt (1efaa04b3f)
- Bug 1241302 - Slightly modernize this code. r=jst (c1e97bc069)
- bug 1216916 clean up when InvokeDragSessionWithImage/Selection() fails r=roc (9cdf71b717)
- Bug 1231329 - run ConvertToUnscaledDevPixels if *aPresContext is valid. r=roc (d10f23633b)
- Bug 1245927: Innerize before creating DOM files from the file picker. r=baku (8754128362)
- Bug 1155214 - Mark nsIBidiKeyboard as builtinclass; r=mrbkap (2d00326d2b)
- reorder (c3c1ba92c9)
- Bug 1236566 - add override declarations for widget/windows/GfxInfo.h; r=roc (940b40ed91)
- Bug 1242659: Part 1. More information on the mismatches. r=jrmuizel (78331ddad2)
- Block some Radeon drivers that crash on D3D9. (bug 1213107, r=jrmuizel) (482b66d313)
- Bug 1203199 - Blacklist DXVA on some older intel drivers for causing crashes. r=jrmuizel (f1827d4498)
- Bug 1203199 - Fix driver range with blacklist to avoid blacklisting other OSes. (2c2d547296)
- Bug 1203199 - Bustage fix. CLOSED TREE (3233d407f3)
- Bug 1178385. Enable DXVA on the G45 r=ajones (3c59a58a15)
- Bug 1188442 IMMHandler::GetSelection() should use static Selection instance when IME doesn't have focus and return its or mSelection's reference r=emk (5cc4b30433)
- Bug 1238899 Create hidden native caret for ATOK at composing in windowless plugin r=m_kato (40ef5bb4aa)
- Bug 1257919 part 1. Kill off nsIXSLTException. r=khuey (11f37643f2)
- Bug 1242072 - Change implementation of BaseProxyHandler::get() to follow ES6 [[Get]] specification. r=jorendorff (094a67f284)
- Bug 489636 - Add a GC u-bench test for property tree splitting via deletion; r=sfink (9282fec25f)
- Bug 1257919 part 2. Make nsIException and nsIStackFrame builtinclass, so we can start using [implicit_jscontext] on them. r=khuey (0e65fb8554)
- Bug 1257919 part 3. Make the various toString methods on exceptions take an explicit JSContext. r=khuey (b342fb4930)
- Bug 1257919 part 4. Stop returning StackFrame instances from exceptions::CreateStack. C++ callers of GetCurrentJSStack or exceptions::CreateStack always check for null anyway, and none of them seem to want this non-JS thing. r=khuey (d34036ccf9)
- Bug 1257919 part 5. Get rid of the now-unused StackFrame class. r=khuey (aa13af0dfe)
- Bug 1141222 - Create ScriptError with stack from Cu.reportError. r=bholley (b83ad26aa0)
- Bug 1208641 - Extract stack from DOM/XPC exception. r=bholley (e382b5f48c)
- Bug 1254380 part 1. Find the relevant window when the compartment we've got is an addon sandbox compartment in AutoJSAPI exception/warning reporting code. r=bholley (4b1f6c64a9)
- Bug 1250630 - remove PBackgroundTest and ifdef ENABLE_TEST blocks. r=khuey (d7b36860a3)
- Bug 1250963 part 1. Change NotifyRunnable::Dispatch to not require a JSContext. r=khuey (48e03c2fc2)
- Bug 1250963 part 2. Remove the unused JSContext argument of WorkerPrivate::ModifyBusyCount. r=khuey (0f5ca42be1)
- Bug 1250291 part 1. Stop pretending to report exceptions in MainThreadStopSyncLoopRunnable::PostDispatch. r=khuey (6e1c81cc06)
- Bug 1250291 part 2. Stop pretending to report exceptions in MainThreadWorkerSyncRunnable::PostDispatch. r=khuey (f3d846ea31)
- Bug 1250291 part 3. Stop pretending to report exceptions in WorkerDebuggerRunnable::PostDispatch. r=khuey (10bc1710d1)
- Bug 1250975. Stop passing a JSContext argument to WorkerRunnable::PreDispatch and its overrides. r=khuey (71ab9046f0)
- Bug 1255298. Just pass through the JSContext when passing through the NotificationOptions in notification code. r=wchen (552ae8e33e)
- Bug 1250185 part 2. Remove some JSContext stuff that's no longer needed in Notification code. r=khuey (20b99ef28b)
- Bug 1251045 part 8. Remove the JSContext argument from some nsINotificationStorageCallback methods. r=khuey (170c4afabc)
- Bug 1227015 P7 Supress unused ErrorResult exception if the worker runnable failed to dispatch. r=catalinb (23a3cc6f45)
- Bug 1251045 part 3. Remove the JSContext argument of WorkerRunnable::PostDispatch and its overrides. r=khuey (6a666d1a4a)
- Bug 1251045 part 2. Remove the JSContext argument of ModifyBusyCountFromWorker. r=khuey (105bc4f59e)
- Bug 1252130. Remove unnecessary JSContext argument from NotificationWorkerRunnable::WorkerRunInternal. r=khuey (642727f6f6)
- Bug 1252127. Remove unnecessary JSContext argument from PromiseWorkerProxy::CleanUp. r=khuey (339e1157d1)
- Bug 1251045 part 1. Make it possible to dispatch a ModifyBusyCountRunnable without a JSContext. r=khuey (665c1f81b4)
- Bug 1251045 part 4. Remove the JSContext argument of WorkerRunnable::Dispatch. r=khuey (6c5cdab2b7)
- Bug 1250185 part 1. nsIStructuredCloneContainer.initFromBase64 doesn't need a JSContext argument. r=khuey (d63a219209)
- fix (773c0393aa)
- Bug 1251045 part 5. Remove the JSContext argument from WorkerPrivateParent::Freeze/Thaw. r=khuey (cd419bfd2c)
- Bug 1251045 part 6. Remove the JSContext argument from some worker debugger methods that no longer need it. r=khuey (abcc437cb9)
- Bug 1251045 part 7. Remove the JSContext argument from some worker methods that no longer need it. r=khuey (3ddcbca08c)
- Bug 1249652 part 1. Add a method on ErrorResult to steal an exception, if any, from a JSContext. r=khuey (9c07f5044b)
- Bug 1229071 - importScripts should throw the correct error result in case the loading of one of scripts fails, r=bz (cd37645d76)
- Bug 1229970 Abort script loading start requests if a load has been canceled. r=khuey (a44d9128ca)
- Bug 1233171 Cancel channel when ScriptLoader is aborted during service worker script load. r=khuey (3b6ceafec7)
- Bug 1249351 part 1. When doing importScripts of multiple scripts in a service worker, make sure to track the cache streams per-loadinfo, instead of trying to make them all wait on the same stream. r=bkelly (e1ccea9685)
- Bug 1157544 - Fix a typo in the test; r=baku (8c0bf8f504)
- Bug 1249351 part 2. Clean up test_importscript.html and add multiple-url importScript() case. r=bz (52b0a31032)
- Bug 1249652 part 2. ScriptExecutorRunnable::WorkerRun should immediately move JS exceptions to its ErrorResult instead of allowing them to linger on the JSContext. r=baku,khuey (f628d3c8ba)
- Bug 1249652 part 3. Simplify way we handle canceling when ScriptLoaderRunnable::RunInternal fails by canceling things with its actual failure code, so we don't have to guess which failed loads are actual failures and which are just canceled via this mechanism. r=baku,khuey (388543fbc3)
- Bug 1251369. Use an AutoJSAPI that reports its own exceptions around the main runloop in workers. r=khuey (28b33287cd)
- Bug 1251518. Fix js::ErrorReportToString to make a bit more sense, and change worker code to not use it anyway, so it matches the mainthread code. r=bholley,terrence (a48a40de90)
- Bug 1254380 part 2. Go ahead and log the stack from our exception in AutoJSAPI::ReportException even if we don't have a window. r=bholley (64532b6017)
- Bug 1212328 - Clean up some JSErrorReport-related code. r=Waldo,bz (d5aa611edc)
- Bug 1255192 part 1. Remove the JSContext argument of JS::ExceptionStackOrNull. r=bholley (39b631d5ad)
- bug 1252687 - make the ctor for CycleCollectorStats constexpr r=mccr8 (9e8168d8ac)
- Bug 1254380 part 3. Skip firing error events for mainthread out of memory exceptions via AutoJSAPI::ReportException. r=bholley (d5e4e7dc29)
- Bug 1254230 kinda-fix. Make sure to never send script errors with stacks attached to the console service if the associated windows have already had FreeInnerObjects called on them. r=bholley (8c379fe0e0)
- Bug 1255192 part 2. Clean up the JSContext usage around xpc::FindExceptionStackForConsoleReport now that it just needs a JSContext for rooting. r=bholley (cade862491)
- Bug 1255201. Improve the stack handling in nsXPCComponents_Utils::ReportError for the DOMException case to include the stack from the DOMException. r=bholley (fc6c065284)
- Bug 1257919 part 6. Make the filename getter on JSStackFrame take an explicit JSContext. r=khuey (cde115b789)
- Bug 1257919 part 7. Make the name getter on JSStackFrame take an explicit JSContext. r=khuey (d7466e9eab)
- Bug 1257919 part 8. Make the line/column number getters on JSStackFrame take an explicit JSContext. r=khuey (b8766b98b2)
- Bug 1257919 part 9. Make the asyncCause/asyncCaller getters on JSStackFrame take an explicit JSContext. r=khuey (b776ff9c6d)
- Bug 1257919 part 10. Make the caller and formattedStack getters on JSStackFrame take an explicit JSContext. r=khuey (dc4d3d9091)
- Bug 1252091. Add/RemoveFeature don't need a JSContext argument. r=khuey (5be6253a67)
- Bug 1252123. Remove some unnecessary JSContext arguments from worker ScriptLoader methods. r=khuey (1657a35268)
- Bug 1255181. Remove AutoJSAPI::InitWithLegacyErrorReporting. r=bholley (5ee7ac506e)
- Bug 1254847 part 1. Take ownership of error reporting on the AutoEntryScript in nsXPCWrappedJSClass::DelegatedQueryInterface. r=bholley (62d987b030)
- make style wrong again.... (fa4e6cfbc1)
- Bug 1254847 part 2. Change nsJSNPRuntime to always use AutoEntryScript and always take ownership of error reporting. r=bholley (d24cb80622)
- Bug 1251655 - Remove support for JavaScript-global-constructor-prototype-alias. r=bz. (2b354ea423)
- Bug 1251655 - Remove support for JavaScript-DOM-class and JavaScript-DOM-interface. r=bz. (dd6786ea57)
- Bug 1251275. Switch to using an AutoEntryScript in WorkerPrivate::RunExpiredTimeouts. r=khuey (f10d1b0dec)
- Bug 1072144 part 1. Just release-assert that Promise::Resolve does not fail in service worker code. It can only do that on OOM or overrecursion anyway, and overrecursion is not likely if we're coming right off a runnable. r=khuey (ba4c89a3bc)
- Bug 1072144 part 2. When UnregisterWorker tries to ScheduleWorker and that throws, just suppress the exception: there is no good place to report it anyway. r=khuey (ca563ec5cd)
- Bug 1072144 part 3. Hoist the exception reporting out of WorkerRunnable::PostRun into WorkerRunnable::Run and make it unconditional. r=khuey (9914acd4a6)
- Bug 1252221. When GetOrCreateGlobalScope fails while trying to run a ScriptExecutorRunnable::WorkerRun, just suppress the exception, because there is no way to report it without a compartment to work with. r=khuey (abcc9df148)
- Bug 1072144 part 4. Add a WorkerRunnable::PreRun so that we can move worker global creation to it and always have an AutoEntryScript by the time we're evaluating the main worker script. r=khuey (007b528868)
- Bug 1251276 part 1. Change WorkerPrivate::CancelAllTimeouts to no longer call RunExpiredTimeouts. r=khuey (6b937370a1)
- Bug 1251276 part 2. Remove the JSContext argument of WorkerPrivate::CancelAllTimeouts. r=khuey (336b788e03)
- Bug 1251380. Change things so that WorkerPrivate::NotifyInternal (hopefully) never throws. r=khuey (1d4863f83a)
- Bug 1254846. Add an AutoEntryScript constructor that takes a JSObject instead of an nsIGlobalObject, for convenience. r=bholley (693857f9b0)
- Bug 1255706 part 1. Remove JSContext argument from WorkerPrivate constructor. r=khuey (0486bdb01c)
- Bug 1255706 part 2. Remove JSContext argument from WorkerPrivate::Terminate. r=khuey (f0b62de092)
- Bug 1257568. Remove the JSContext argument of WorkerPrivate::Kill. r=khuey (ebe7d247b5)
- Bug 1252189. Remove the unnecessary JSContext argument from WorkerPrivate::FreezeInternal/ThawInternal. r=khuey (22b137b05e)
- Bug 1119490 - Expose the URL constructor to WorkerDebuggerGlobalScope;r=khuey (6f4fc13e1e)
- Bug 1241841 - Clear the worker's debugger event queue before destroying its context;r=khuey (a1e8dd4b2d)
- Bug 1249950 - Add Performance Markers for MessagePort - patch 1 - remove non-useful 'explicit', r=smaug (8a014b53e6)
- Bug 1249950 - Add Performance Markers for MessagePort - patch 2, r=smaug, r=vporof (560caf0ae1)
- bug 1250486 - make the ComponentsSH ctor constexpr r=bz (1c441dbb0f)
- Bug 1257032: Make files in dom/workers actually build without unification. r=baku (6ab4ae0b65)
- Bug 1241522 - handle OOM in nsExpatDriver::HandleCharacterData; r=hsivonen (b652220cdc)
- Bug 1219482: Replace PRLogModuleInfo with LazyLogModule in various files.r=benjamin (5fd5e8dbc8)
- Bug 1238545 - Remove nsISimpleUnicharStreamFactory; r=froydnj (f0018c5b16)
- Bug 1257335. Replace some AutoSafeJSContext uses with AutoJSAPI or AutoJSContext uses. r=bholley (7baf79deb7)
- Bug 1247635 - Unify PostMessageRunnable and DispatchEventRunnable in MessagePort.cpp, r=smaug (df2765c215)
- Bug 1250572 - Force a parent object in MessagePort/Channel and in StructuredCloneHolder, r=smaug (2a929d59d1)
- Bug 1255375 - MessagePort should not leak if DispatchMessage() fails, r=smaug (0a5cdebfb0)
- Bug 1251272. Remove the dead code in ReportErrorRunnable::ReportError that could fail, and make it infallible. r=khuey (89e80694d0)
- Bug 1072144 part 5. Stop fiddling with compartments on the JSContext before calling PostRun in WorkerRunnable::Run. Add some documentation explaining what's going on. r=khuey (91f4bb6a7c)
- Bug 1072144 part 6. Switch WorkerRunnable::Run to calling TakeOwnershipOfErrorReporting on its AutoJSAPI/AutoEntryScript and remove the remaining JS_ReportPendingException callers in worker code. r=khuey (405d9e0282)
- Bug 1179548 - Close the windows opened in private browsing worker tests; r=baku (acf3430b69)
- Bug 1179753 - Use pushPrefEnv more in worker tests; r=baku (4bfd949c72)
- Bug 1134224 - More test for test_bug1132395.html, r=ehsan (626eb100cd)
- Bug 1134224 - onerror for test_bug1132395.html, r=ehsan (147195f57e)
- Bug 1207635 - get rid of dom.workers.sharedWorkers.enabled pref, r=khuey (22fe2965c6)
- Bug 1252592. JS warnings should not trigger error events on shared workers. r=khuey (7f05c2c00d)
- Bug 1251308; r=luke (3cbec95738)
- Bug 1246838 - Handle const qualifiers and references better in Variant. r=waldo (30da6e3ebf)
- Bug 1254565 - Allow passing matchers as rvalues to Variant::match. r=froydnj (1dd799fb0a)
- Bug 1246841 - Allow construction of Variant values using type inference. r=waldo (6ba3e6704c)
- Bug 1250666 - Forward Variant's move constructor argument correctly to the underlying variant type. r=waldo (7353a53bf0)
- Bug 1252185. Remove the dead "target" variable from CloseEventRunnable::WorkerRun. r=khuey (be172da10b)
- Bug 1253059: Use fallible allocation in the worker error reporter. r=baku (963222bdd4)
- Bug 1253199 - MessagePort should handle the dispatching a message when the parent window is gone, r=smaug (d552e3cd9e)
- Bug 1252839 - Remove some if stmt after allocation with 'new' - patch 2, r=bz (3c7eb959bc)
- Bug 1252839 - Remove some if stmt after allocation with 'new' - patch 1, r=bz (85067dbfb6)
- Bug 1254855. Switch AutoEntryScripts in nsDirectoryViewer to take ownership of error reporting. r=bholley (8c10532323)
- Bug 1252565 part 1. Push the script environment preparer bits up from XPCJSRuntime to CycleCollectedJSRuntime, because we need them on workers to do ctypes on workers properly. r=bholley (aecf2595dd)
- Bug 1252565 part 2. Make dom::WarningOnlyErrorReporter handle workers. r=bholley (f415b8868b)
- Bug 1244222 - Check for function interfaces in DelegatedQueryInterface. r=bz (d213eec83c)
- Bug 1254393. Take ownership of error reporting on the AutoEntryScript in nsXPCWrappedJSClass::CallMethod. r=bholley (4765b501c0)
- Bug 1251769 - Remove remaining references to MOZILLA_XPCOMRT_API from mfbt. r=froydnj (236adc1d4c)
- Bug 1247835 (part 0) - Minor comment and style tweaks in BinarySearch.h. r=luke. (db04793721)
- Bug 1244074 - Part 1: Move SheetParsingMode to a separate file. r=dholbert (290adaadea)
- Bug 1244074 - Part 2: Add HandleRefPtr for refcounting StyleSheetHandles. r=dholbert r=waldo (8549b24392)
- Bug 1244068 - Part 1: Add enum to represent the style system backend type. r=dholbert (c2e53feaf2)
- Bug 1244068 - Part 2: Add skeleton ServoStyleSet and a StyleSetHandle smart pointer. r=dholbert (71a5c28629)
- Bug 1244068 - Part 3: Factor out nsStyleSet getting in RestyleManager/ElementRestyler. r=dholbert (9e9f4f2e80)
- Bug 1245406: In ReflowBlockFrame, use Maybe<> to destroy & reconstruct a stack-allocated object, instead of an explicit call to destructor & placement 'new'. r=dbaron (d36858b818)
- Bug 1244068 - Part 4: Use StyleSetHandle instead of concrete style set class in most places. r=dholbert (93f9bcf799)
- Bug 1244074 - Part 3: Add skeleton ServoStyleSheet and a StyleSheetHandle smart pointer. r=dholbert (5b4fbdce38)
- Bug 1195173 - Apply CSP to preloaded styles within layout/style/Loader.cpp (r=bz) (d1ba920fd3)
- Bug 1245681 - Fix addOverrideStyleSheet. r=bz (64099eac73)
- Bug 1244074 - Part 4: Use StyleSheetHandle instead of concrete style sheet class in most places. r=dholberti (867470ac83)
- Bug 1246013 (part 1) - Store a copy of the ETLDEntry directly in DomainEntry instead of a pointer to it. r=jduell. (587b3a9fb7)
- Bug 1246013 (part 2) - Change "explicit/xpcom/effective-TLD-service" path to the more sensible "explicit/network/effective-TLD-service". r=jduell. (2e0df6f28f)
- Bug 1247835 (part 1) - Use binary search instead of a hash table in nsEffectiveTLDService. r=jduell. (685dae01c0)
- Bug 1257121 part 1 - Use struct for passing some params of font metrics. r=jfkthame (1dfd7a7429)
- Bug 1257121 part 2 - Merge nsFontMetrics::Init to the constructor and remove unused failure handling code. r=jfkthame (0cdc441511)
- Bug 1257121 part 3 - Add GetInflatedFontMetricsForFrame function to simplify a common use pattern. r=jfkthame (d9630ebd1b)
- Bug 1257121 part 4 - Use return value rather than out param to retur font metrics. r=jfkthame (c9a5b1d1dd)
- Bug 1257121 part 5 - Make nsDeviceContext::mFontCache a RefPtr. r=jfkthame (9c53238a6a)
2024-01-29 15:26:23 +08:00

1651 lines
50 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// We're dividing JS objects into 3 categories:
//
// 1. "real" roots, held by the JS engine itself or rooted through the root
// and lock JS APIs. Roots from this category are considered black in the
// cycle collector, any cycle they participate in is uncollectable.
//
// 2. certain roots held by C++ objects that are guaranteed to be alive.
// Roots from this category are considered black in the cycle collector,
// and any cycle they participate in is uncollectable. These roots are
// traced from TraceNativeBlackRoots.
//
// 3. all other roots held by C++ objects that participate in cycle
// collection, held by us (see TraceNativeGrayRoots). Roots from this
// category are considered grey in the cycle collector; whether or not
// they are collected depends on the objects that hold them.
//
// Note that if a root is in multiple categories the fact that it is in
// category 1 or 2 that takes precedence, so it will be considered black.
//
// During garbage collection we switch to an additional mark color (gray)
// when tracing inside TraceNativeGrayRoots. This allows us to walk those
// roots later on and add all objects reachable only from them to the
// cycle collector.
//
// Phases:
//
// 1. marking of the roots in category 1 by having the JS GC do its marking
// 2. marking of the roots in category 2 by having the JS GC call us back
// (via JS_SetExtraGCRootsTracer) and running TraceNativeBlackRoots
// 3. marking of the roots in category 3 by TraceNativeGrayRoots using an
// additional color (gray).
// 4. end of GC, GC can sweep its heap
//
// At some later point, when the cycle collector runs:
//
// 5. walk gray objects and add them to the cycle collector, cycle collect
//
// JS objects that are part of cycles the cycle collector breaks will be
// collected by the next JS GC.
//
// If WantAllTraces() is false the cycle collector will not traverse roots
// from category 1 or any JS objects held by them. Any JS objects they hold
// will already be marked by the JS GC and will thus be colored black
// themselves. Any C++ objects they hold will have a missing (untraversed)
// edge from the JS object to the C++ object and so it will be marked black
// too. This decreases the number of objects that the cycle collector has to
// deal with.
// To improve debugging, if WantAllTraces() is true all JS objects are
// traversed.
#include "mozilla/CycleCollectedJSRuntime.h"
#include <algorithm>
#include "mozilla/ArrayUtils.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/Move.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Snprintf.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TimelineConsumers.h"
#include "mozilla/TimelineMarker.h"
#include "mozilla/DebuggerOnGCRunnable.h"
#include "mozilla/dom/DOMJSClass.h"
#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseBinding.h"
#include "mozilla/dom/ScriptSettings.h"
#include "jsprf.h"
#include "js/Debug.h"
#include "nsContentUtils.h"
#include "nsCycleCollectionNoteRootCallback.h"
#include "nsCycleCollectionParticipant.h"
#include "nsCycleCollector.h"
#include "nsDOMJSUtils.h"
#include "nsJSUtils.h"
#include "nsWrapperCache.h"
#ifdef MOZ_CRASHREPORTER
#include "nsExceptionHandler.h"
#endif
#include "nsIException.h"
#include "nsThread.h"
#include "nsThreadUtils.h"
#include "xpcpublic.h"
using namespace mozilla;
using namespace mozilla::dom;
namespace mozilla {
struct DeferredFinalizeFunctionHolder
{
DeferredFinalizeFunction run;
void* data;
};
class IncrementalFinalizeRunnable : public nsRunnable
{
typedef AutoTArray<DeferredFinalizeFunctionHolder, 16> DeferredFinalizeArray;
typedef CycleCollectedJSRuntime::DeferredFinalizerTable DeferredFinalizerTable;
CycleCollectedJSRuntime* mRuntime;
DeferredFinalizeArray mDeferredFinalizeFunctions;
uint32_t mFinalizeFunctionToRun;
bool mReleasing;
static const PRTime SliceMillis = 5; /* ms */
public:
IncrementalFinalizeRunnable(CycleCollectedJSRuntime* aRt,
DeferredFinalizerTable& aFinalizerTable);
virtual ~IncrementalFinalizeRunnable();
void ReleaseNow(bool aLimited);
NS_DECL_NSIRUNNABLE
};
} // namespace mozilla
struct NoteWeakMapChildrenTracer : public JS::CallbackTracer
{
NoteWeakMapChildrenTracer(JSRuntime* aRt,
nsCycleCollectionNoteRootCallback& aCb)
: JS::CallbackTracer(aRt), mCb(aCb), mTracedAny(false), mMap(nullptr),
mKey(nullptr), mKeyDelegate(nullptr)
{
}
void onChild(const JS::GCCellPtr& aThing) override;
nsCycleCollectionNoteRootCallback& mCb;
bool mTracedAny;
JSObject* mMap;
JS::GCCellPtr mKey;
JSObject* mKeyDelegate;
};
void
NoteWeakMapChildrenTracer::onChild(const JS::GCCellPtr& aThing)
{
if (aThing.is<JSString>()) {
return;
}
if (!JS::GCThingIsMarkedGray(aThing) && !mCb.WantAllTraces()) {
return;
}
if (AddToCCKind(aThing.kind())) {
mCb.NoteWeakMapping(mMap, mKey, mKeyDelegate, aThing);
mTracedAny = true;
} else {
JS::TraceChildren(this, aThing);
}
}
struct NoteWeakMapsTracer : public js::WeakMapTracer
{
NoteWeakMapsTracer(JSRuntime* aRt, nsCycleCollectionNoteRootCallback& aCccb)
: js::WeakMapTracer(aRt), mCb(aCccb), mChildTracer(aRt, aCccb)
{
}
void trace(JSObject* aMap, JS::GCCellPtr aKey, JS::GCCellPtr aValue) override;
nsCycleCollectionNoteRootCallback& mCb;
NoteWeakMapChildrenTracer mChildTracer;
};
void
NoteWeakMapsTracer::trace(JSObject* aMap, JS::GCCellPtr aKey,
JS::GCCellPtr aValue)
{
// If nothing that could be held alive by this entry is marked gray, return.
if ((!aKey || !JS::GCThingIsMarkedGray(aKey)) &&
MOZ_LIKELY(!mCb.WantAllTraces())) {
if (!aValue || !JS::GCThingIsMarkedGray(aValue) || aValue.is<JSString>()) {
return;
}
}
// The cycle collector can only properly reason about weak maps if it can
// reason about the liveness of their keys, which in turn requires that
// the key can be represented in the cycle collector graph. All existing
// uses of weak maps use either objects or scripts as keys, which are okay.
MOZ_ASSERT(AddToCCKind(aKey.kind()));
// As an emergency fallback for non-debug builds, if the key is not
// representable in the cycle collector graph, we treat it as marked. This
// can cause leaks, but is preferable to ignoring the binding, which could
// cause the cycle collector to free live objects.
if (!AddToCCKind(aKey.kind())) {
aKey = nullptr;
}
JSObject* kdelegate = nullptr;
if (aKey.is<JSObject>()) {
kdelegate = js::GetWeakmapKeyDelegate(&aKey.as<JSObject>());
}
if (AddToCCKind(aValue.kind())) {
mCb.NoteWeakMapping(aMap, aKey, kdelegate, aValue);
} else {
mChildTracer.mTracedAny = false;
mChildTracer.mMap = aMap;
mChildTracer.mKey = aKey;
mChildTracer.mKeyDelegate = kdelegate;
if (!aValue.is<JSString>()) {
JS::TraceChildren(&mChildTracer, aValue);
}
// The delegate could hold alive the key, so report something to the CC
// if we haven't already.
if (!mChildTracer.mTracedAny &&
aKey && JS::GCThingIsMarkedGray(aKey) && kdelegate) {
mCb.NoteWeakMapping(aMap, aKey, kdelegate, nullptr);
}
}
}
// This is based on the logic in FixWeakMappingGrayBitsTracer::trace.
struct FixWeakMappingGrayBitsTracer : public js::WeakMapTracer
{
explicit FixWeakMappingGrayBitsTracer(JSRuntime* aRt)
: js::WeakMapTracer(aRt)
{
}
void
FixAll()
{
do {
mAnyMarked = false;
js::TraceWeakMaps(this);
} while (mAnyMarked);
}
void trace(JSObject* aMap, JS::GCCellPtr aKey, JS::GCCellPtr aValue) override
{
// If nothing that could be held alive by this entry is marked gray, return.
bool delegateMightNeedMarking = aKey && JS::GCThingIsMarkedGray(aKey);
bool valueMightNeedMarking = aValue && JS::GCThingIsMarkedGray(aValue) &&
aValue.kind() != JS::TraceKind::String;
if (!delegateMightNeedMarking && !valueMightNeedMarking) {
return;
}
if (!AddToCCKind(aKey.kind())) {
aKey = nullptr;
}
if (delegateMightNeedMarking && aKey.is<JSObject>()) {
JSObject* kdelegate = js::GetWeakmapKeyDelegate(&aKey.as<JSObject>());
if (kdelegate && !JS::ObjectIsMarkedGray(kdelegate)) {
if (JS::UnmarkGrayGCThingRecursively(aKey)) {
mAnyMarked = true;
}
}
}
if (aValue && JS::GCThingIsMarkedGray(aValue) &&
(!aKey || !JS::GCThingIsMarkedGray(aKey)) &&
(!aMap || !JS::ObjectIsMarkedGray(aMap)) &&
aValue.kind() != JS::TraceKind::Shape) {
if (JS::UnmarkGrayGCThingRecursively(aValue)) {
mAnyMarked = true;
}
}
}
bool mAnyMarked;
};
static void
CheckParticipatesInCycleCollection(JS::GCCellPtr aThing, const char* aName,
void* aClosure)
{
bool* cycleCollectionEnabled = static_cast<bool*>(aClosure);
if (*cycleCollectionEnabled) {
return;
}
if (AddToCCKind(aThing.kind()) && JS::GCThingIsMarkedGray(aThing)) {
*cycleCollectionEnabled = true;
}
}
NS_IMETHODIMP
JSGCThingParticipant::Traverse(void* aPtr,
nsCycleCollectionTraversalCallback& aCb)
{
auto runtime = reinterpret_cast<CycleCollectedJSRuntime*>(
reinterpret_cast<char*>(this) - offsetof(CycleCollectedJSRuntime,
mGCThingCycleCollectorGlobal));
JS::GCCellPtr cellPtr(aPtr, js::GCThingTraceKind(aPtr));
runtime->TraverseGCThing(CycleCollectedJSRuntime::TRAVERSE_FULL, cellPtr, aCb);
return NS_OK;
}
// NB: This is only used to initialize the participant in
// CycleCollectedJSRuntime. It should never be used directly.
static JSGCThingParticipant sGCThingCycleCollectorGlobal;
NS_IMETHODIMP
JSZoneParticipant::Traverse(void* aPtr, nsCycleCollectionTraversalCallback& aCb)
{
auto runtime = reinterpret_cast<CycleCollectedJSRuntime*>(
reinterpret_cast<char*>(this) - offsetof(CycleCollectedJSRuntime,
mJSZoneCycleCollectorGlobal));
MOZ_ASSERT(!aCb.WantAllTraces());
JS::Zone* zone = static_cast<JS::Zone*>(aPtr);
runtime->TraverseZone(zone, aCb);
return NS_OK;
}
struct TraversalTracer : public JS::CallbackTracer
{
TraversalTracer(JSRuntime* aRt, nsCycleCollectionTraversalCallback& aCb)
: JS::CallbackTracer(aRt, DoNotTraceWeakMaps), mCb(aCb)
{
}
void onChild(const JS::GCCellPtr& aThing) override;
nsCycleCollectionTraversalCallback& mCb;
};
void
TraversalTracer::onChild(const JS::GCCellPtr& aThing)
{
// Don't traverse non-gray objects, unless we want all traces.
if (!JS::GCThingIsMarkedGray(aThing) && !mCb.WantAllTraces()) {
return;
}
/*
* This function needs to be careful to avoid stack overflow. Normally, when
* AddToCCKind is true, the recursion terminates immediately as we just add
* |thing| to the CC graph. So overflow is only possible when there are long
* or cyclic chains of non-AddToCCKind GC things. Places where this can occur
* use special APIs to handle such chains iteratively.
*/
if (AddToCCKind(aThing.kind())) {
if (MOZ_UNLIKELY(mCb.WantDebugInfo())) {
char buffer[200];
getTracingEdgeName(buffer, sizeof(buffer));
mCb.NoteNextEdgeName(buffer);
}
if (aThing.is<JSObject>()) {
mCb.NoteJSObject(&aThing.as<JSObject>());
} else {
mCb.NoteJSScript(&aThing.as<JSScript>());
}
} else if (aThing.is<js::Shape>()) {
// The maximum depth of traversal when tracing a Shape is unbounded, due to
// the parent pointers on the shape.
JS_TraceShapeCycleCollectorChildren(this, aThing);
} else if (aThing.is<js::ObjectGroup>()) {
// The maximum depth of traversal when tracing an ObjectGroup is unbounded,
// due to information attached to the groups which can lead other groups to
// be traced.
JS_TraceObjectGroupCycleCollectorChildren(this, aThing);
} else if (!aThing.is<JSString>()) {
JS::TraceChildren(this, aThing);
}
}
static void
NoteJSChildGrayWrapperShim(void* aData, JS::GCCellPtr aThing)
{
TraversalTracer* trc = static_cast<TraversalTracer*>(aData);
trc->onChild(aThing);
}
/*
* The cycle collection participant for a Zone is intended to produce the same
* results as if all of the gray GCthings in a zone were merged into a single node,
* except for self-edges. This avoids the overhead of representing all of the GCthings in
* the zone in the cycle collector graph, which should be much faster if many of
* the GCthings in the zone are gray.
*
* Zone merging should not always be used, because it is a conservative
* approximation of the true cycle collector graph that can incorrectly identify some
* garbage objects as being live. For instance, consider two cycles that pass through a
* zone, where one is garbage and the other is live. If we merge the entire
* zone, the cycle collector will think that both are alive.
*
* We don't have to worry about losing track of a garbage cycle, because any such garbage
* cycle incorrectly identified as live must contain at least one C++ to JS edge, and
* XPConnect will always add the C++ object to the CC graph. (This is in contrast to pure
* C++ garbage cycles, which must always be properly identified, because we clear the
* purple buffer during every CC, which may contain the last reference to a garbage
* cycle.)
*/
// NB: This is only used to initialize the participant in
// CycleCollectedJSRuntime. It should never be used directly.
static const JSZoneParticipant sJSZoneCycleCollectorGlobal;
static
void JSObjectsTenuredCb(JSRuntime* aRuntime, void* aData)
{
static_cast<CycleCollectedJSRuntime*>(aData)->JSObjectsTenured();
}
CycleCollectedJSRuntime::CycleCollectedJSRuntime()
: mGCThingCycleCollectorGlobal(sGCThingCycleCollectorGlobal)
, mJSZoneCycleCollectorGlobal(sJSZoneCycleCollectorGlobal)
, mJSRuntime(nullptr)
, mPrevGCSliceCallback(nullptr)
, mPrevGCNurseryCollectionCallback(nullptr)
, mJSHolders(256)
, mDoingStableStates(false)
, mOutOfMemoryState(OOMState::OK)
, mLargeAllocationFailureState(OOMState::OK)
{
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
mOwningThread = thread.forget().downcast<nsThread>().take();
MOZ_RELEASE_ASSERT(mOwningThread);
}
CycleCollectedJSRuntime::~CycleCollectedJSRuntime()
{
// If the allocation failed, here we are.
if (!mJSRuntime) {
return;
}
MOZ_ASSERT(!mDeferredFinalizerTable.Count());
// Last chance to process any events.
ProcessMetastableStateQueue(mBaseRecursionDepth);
MOZ_ASSERT(mMetastableStateEvents.IsEmpty());
ProcessStableStateQueue();
MOZ_ASSERT(mStableStateEvents.IsEmpty());
// Clear mPendingException first, since it might be cycle collected.
mPendingException = nullptr;
JS_DestroyRuntime(mJSRuntime);
mJSRuntime = nullptr;
nsCycleCollector_forgetJSRuntime();
mozilla::dom::DestroyScriptSettings();
mOwningThread->SetScriptObserver(nullptr);
NS_RELEASE(mOwningThread);
}
static void
MozCrashErrorReporter(JSContext*, const char*, JSErrorReport*)
{
MOZ_CRASH("Why is someone touching JSAPI without an AutoJSAPI?");
}
nsresult
CycleCollectedJSRuntime::Initialize(JSRuntime* aParentRuntime,
uint32_t aMaxBytes,
uint32_t aMaxNurseryBytes)
{
MOZ_ASSERT(!mJSRuntime);
mOwningThread->SetScriptObserver(this);
// The main thread has a base recursion depth of 0, workers of 1.
mBaseRecursionDepth = RecursionDepth();
mozilla::dom::InitScriptSettings();
mJSRuntime = JS_NewRuntime(aMaxBytes, aMaxNurseryBytes, aParentRuntime);
if (!mJSRuntime) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (!JS_AddExtraGCRootsTracer(mJSRuntime, TraceBlackJS, this)) {
MOZ_CRASH();
}
JS_SetGrayGCRootsTracer(mJSRuntime, TraceGrayJS, this);
JS_SetGCCallback(mJSRuntime, GCCallback, this);
mPrevGCSliceCallback = JS::SetGCSliceCallback(mJSRuntime, GCSliceCallback);
if (NS_IsMainThread()) {
// We would like to support all threads here, but the way timeline consumers
// are set up currently, you can either add a marker for one specific
// docshell, or for every consumer globally. We would like to add a marker
// for every consumer observing anything on this thread, but that is not
// currently possible. For now, add global markers only when we are on the
// main thread, since the UI for this tracing data only displays data
// relevant to the main-thread.
mPrevGCNurseryCollectionCallback = JS::SetGCNurseryCollectionCallback(
mJSRuntime, GCNurseryCollectionCallback);
}
JS_SetObjectsTenuredCallback(mJSRuntime, JSObjectsTenuredCb, this);
JS::SetOutOfMemoryCallback(mJSRuntime, OutOfMemoryCallback, this);
JS::SetLargeAllocationFailureCallback(mJSRuntime,
LargeAllocationFailureCallback, this);
JS_SetContextCallback(mJSRuntime, ContextCallback, this);
JS_SetDestroyZoneCallback(mJSRuntime, XPCStringConvert::FreeZoneCache);
JS_SetSweepZoneCallback(mJSRuntime, XPCStringConvert::ClearZoneCache);
// XPCJSRuntime currently overrides this because we don't
// TakeOwnershipOfErrorReporting everwhere on the main thread yet.
JS_SetErrorReporter(mJSRuntime, MozCrashErrorReporter);
static js::DOMCallbacks DOMcallbacks = {
InstanceClassHasProtoAtDepth
};
SetDOMCallbacks(mJSRuntime, &DOMcallbacks);
js::SetScriptEnvironmentPreparer(mJSRuntime, &mEnvironmentPreparer);
#ifdef SPIDERMONKEY_PROMISE
JS::SetEnqueuePromiseJobCallback(mJSRuntime, EnqueuePromiseJobCallback, this);
#endif // SPIDERMONKEY_PROMISE
JS::dbg::SetDebuggerMallocSizeOf(mJSRuntime, moz_malloc_size_of);
nsCycleCollector_registerJSRuntime(this);
return NS_OK;
}
size_t
CycleCollectedJSRuntime::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t n = 0;
// We're deliberately not measuring anything hanging off the entries in
// mJSHolders.
n += mJSHolders.ShallowSizeOfExcludingThis(aMallocSizeOf);
return n;
}
void
CycleCollectedJSRuntime::UnmarkSkippableJSHolders()
{
for (auto iter = mJSHolders.Iter(); !iter.Done(); iter.Next()) {
void* holder = iter.Key();
nsScriptObjectTracer*& tracer = iter.Data();
tracer->CanSkip(holder, true);
}
}
void
CycleCollectedJSRuntime::DescribeGCThing(bool aIsMarked, JS::GCCellPtr aThing,
nsCycleCollectionTraversalCallback& aCb) const
{
if (!aCb.WantDebugInfo()) {
aCb.DescribeGCedNode(aIsMarked, "JS Object");
return;
}
char name[72];
uint64_t compartmentAddress = 0;
if (aThing.is<JSObject>()) {
JSObject* obj = &aThing.as<JSObject>();
compartmentAddress = (uint64_t)js::GetObjectCompartment(obj);
const js::Class* clasp = js::GetObjectClass(obj);
// Give the subclass a chance to do something
if (DescribeCustomObjects(obj, clasp, name)) {
// Nothing else to do!
} else if (js::IsFunctionObject(obj)) {
JSFunction* fun = JS_GetObjectFunction(obj);
JSString* str = JS_GetFunctionDisplayId(fun);
if (str) {
JSFlatString* flat = JS_ASSERT_STRING_IS_FLAT(str);
nsAutoString chars;
AssignJSFlatString(chars, flat);
NS_ConvertUTF16toUTF8 fname(chars);
snprintf_literal(name, "JS Object (Function - %s)", fname.get());
} else {
snprintf_literal(name, "JS Object (Function)");
}
} else {
snprintf_literal(name, "JS Object (%s)", clasp->name);
}
} else {
snprintf_literal(name, "JS %s", JS::GCTraceKindToAscii(aThing.kind()));
}
// Disable printing global for objects while we figure out ObjShrink fallout.
aCb.DescribeGCedNode(aIsMarked, name, compartmentAddress);
}
void
CycleCollectedJSRuntime::NoteGCThingJSChildren(JS::GCCellPtr aThing,
nsCycleCollectionTraversalCallback& aCb) const
{
MOZ_ASSERT(mJSRuntime);
TraversalTracer trc(mJSRuntime, aCb);
JS::TraceChildren(&trc, aThing);
}
void
CycleCollectedJSRuntime::NoteGCThingXPCOMChildren(const js::Class* aClasp,
JSObject* aObj,
nsCycleCollectionTraversalCallback& aCb) const
{
MOZ_ASSERT(aClasp);
MOZ_ASSERT(aClasp == js::GetObjectClass(aObj));
if (NoteCustomGCThingXPCOMChildren(aClasp, aObj, aCb)) {
// Nothing else to do!
return;
}
// XXX This test does seem fragile, we should probably whitelist classes
// that do hold a strong reference, but that might not be possible.
else if (aClasp->flags & JSCLASS_HAS_PRIVATE &&
aClasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "js::GetObjectPrivate(obj)");
aCb.NoteXPCOMChild(static_cast<nsISupports*>(js::GetObjectPrivate(aObj)));
} else {
const DOMJSClass* domClass = GetDOMClass(aObj);
if (domClass) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "UnwrapDOMObject(obj)");
// It's possible that our object is an unforgeable holder object, in
// which case it doesn't actually have a C++ DOM object associated with
// it. Use UnwrapPossiblyNotInitializedDOMObject, which produces null in
// that case, since NoteXPCOMChild/NoteNativeChild are null-safe.
if (domClass->mDOMObjectIsISupports) {
aCb.NoteXPCOMChild(UnwrapPossiblyNotInitializedDOMObject<nsISupports>(aObj));
} else if (domClass->mParticipant) {
aCb.NoteNativeChild(UnwrapPossiblyNotInitializedDOMObject<void>(aObj),
domClass->mParticipant);
}
}
}
}
void
CycleCollectedJSRuntime::TraverseGCThing(TraverseSelect aTs, JS::GCCellPtr aThing,
nsCycleCollectionTraversalCallback& aCb)
{
bool isMarkedGray = JS::GCThingIsMarkedGray(aThing);
if (aTs == TRAVERSE_FULL) {
DescribeGCThing(!isMarkedGray, aThing, aCb);
}
// If this object is alive, then all of its children are alive. For JS objects,
// the black-gray invariant ensures the children are also marked black. For C++
// objects, the ref count from this object will keep them alive. Thus we don't
// need to trace our children, unless we are debugging using WantAllTraces.
if (!isMarkedGray && !aCb.WantAllTraces()) {
return;
}
if (aTs == TRAVERSE_FULL) {
NoteGCThingJSChildren(aThing, aCb);
}
if (aThing.is<JSObject>()) {
JSObject* obj = &aThing.as<JSObject>();
NoteGCThingXPCOMChildren(js::GetObjectClass(obj), obj, aCb);
}
}
struct TraverseObjectShimClosure
{
nsCycleCollectionTraversalCallback& cb;
CycleCollectedJSRuntime* self;
};
void
CycleCollectedJSRuntime::TraverseZone(JS::Zone* aZone,
nsCycleCollectionTraversalCallback& aCb)
{
MOZ_ASSERT(mJSRuntime);
/*
* We treat the zone as being gray. We handle non-gray GCthings in the
* zone by not reporting their children to the CC. The black-gray invariant
* ensures that any JS children will also be non-gray, and thus don't need to be
* added to the graph. For C++ children, not representing the edge from the
* non-gray JS GCthings to the C++ object will keep the child alive.
*
* We don't allow zone merging in a WantAllTraces CC, because then these
* assumptions don't hold.
*/
aCb.DescribeGCedNode(false, "JS Zone");
/*
* Every JS child of everything in the zone is either in the zone
* or is a cross-compartment wrapper. In the former case, we don't need to
* represent these edges in the CC graph because JS objects are not ref counted.
* In the latter case, the JS engine keeps a map of these wrappers, which we
* iterate over. Edges between compartments in the same zone will add
* unnecessary loop edges to the graph (bug 842137).
*/
TraversalTracer trc(mJSRuntime, aCb);
js::VisitGrayWrapperTargets(aZone, NoteJSChildGrayWrapperShim, &trc);
/*
* To find C++ children of things in the zone, we scan every JS Object in
* the zone. Only JS Objects can have C++ children.
*/
TraverseObjectShimClosure closure = { aCb, this };
js::IterateGrayObjects(aZone, TraverseObjectShim, &closure);
}
/* static */ void
CycleCollectedJSRuntime::TraverseObjectShim(void* aData, JS::GCCellPtr aThing)
{
TraverseObjectShimClosure* closure =
static_cast<TraverseObjectShimClosure*>(aData);
MOZ_ASSERT(aThing.is<JSObject>());
closure->self->TraverseGCThing(CycleCollectedJSRuntime::TRAVERSE_CPP,
aThing, closure->cb);
}
void
CycleCollectedJSRuntime::TraverseNativeRoots(nsCycleCollectionNoteRootCallback& aCb)
{
// NB: This is here just to preserve the existing XPConnect order. I doubt it
// would hurt to do this after the JS holders.
TraverseAdditionalNativeRoots(aCb);
for (auto iter = mJSHolders.Iter(); !iter.Done(); iter.Next()) {
void* holder = iter.Key();
nsScriptObjectTracer*& tracer = iter.Data();
bool noteRoot = false;
if (MOZ_UNLIKELY(aCb.WantAllTraces())) {
noteRoot = true;
} else {
tracer->Trace(holder,
TraceCallbackFunc(CheckParticipatesInCycleCollection),
&noteRoot);
}
if (noteRoot) {
aCb.NoteNativeRoot(holder, tracer);
}
}
}
/* static */ void
CycleCollectedJSRuntime::TraceBlackJS(JSTracer* aTracer, void* aData)
{
CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
self->TraceNativeBlackRoots(aTracer);
}
/* static */ void
CycleCollectedJSRuntime::TraceGrayJS(JSTracer* aTracer, void* aData)
{
CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
// Mark these roots as gray so the CC can walk them later.
self->TraceNativeGrayRoots(aTracer);
}
/* static */ void
CycleCollectedJSRuntime::GCCallback(JSRuntime* aRuntime,
JSGCStatus aStatus,
void* aData)
{
CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
MOZ_ASSERT(aRuntime == self->Runtime());
self->OnGC(aStatus);
}
/* static */ void
CycleCollectedJSRuntime::GCSliceCallback(JSRuntime* aRuntime,
JS::GCProgress aProgress,
const JS::GCDescription& aDesc)
{
CycleCollectedJSRuntime* self = CycleCollectedJSRuntime::Get();
MOZ_ASSERT(self->Runtime() == aRuntime);
if (aProgress == JS::GC_CYCLE_END) {
JS::gcreason::Reason reason = aDesc.reason_;
NS_WARN_IF(NS_FAILED(DebuggerOnGCRunnable::Enqueue(aRuntime, aDesc)) &&
reason != JS::gcreason::SHUTDOWN_CC &&
reason != JS::gcreason::DESTROY_RUNTIME &&
reason != JS::gcreason::XPCONNECT_SHUTDOWN);
}
if (self->mPrevGCSliceCallback) {
self->mPrevGCSliceCallback(aRuntime, aProgress, aDesc);
}
}
class MinorGCMarker : public TimelineMarker
{
private:
JS::gcreason::Reason mReason;
public:
MinorGCMarker(MarkerTracingType aTracingType,
JS::gcreason::Reason aReason)
: TimelineMarker("MinorGC",
aTracingType,
MarkerStackRequest::NO_STACK)
, mReason(aReason)
{
MOZ_ASSERT(aTracingType == MarkerTracingType::START ||
aTracingType == MarkerTracingType::END);
}
MinorGCMarker(JS::GCNurseryProgress aProgress,
JS::gcreason::Reason aReason)
: TimelineMarker("MinorGC",
aProgress == JS::GCNurseryProgress::GC_NURSERY_COLLECTION_START
? MarkerTracingType::START
: MarkerTracingType::END,
MarkerStackRequest::NO_STACK)
, mReason(aReason)
{ }
virtual void
AddDetails(JSContext* aCx,
dom::ProfileTimelineMarker& aMarker) override
{
TimelineMarker::AddDetails(aCx, aMarker);
if (GetTracingType() == MarkerTracingType::START) {
auto reason = JS::gcreason::ExplainReason(mReason);
aMarker.mCauseName.Construct(NS_ConvertUTF8toUTF16(reason));
}
}
virtual UniquePtr<AbstractTimelineMarker>
Clone() override
{
auto clone = MakeUnique<MinorGCMarker>(GetTracingType(), mReason);
clone->SetCustomTime(GetTime());
return UniquePtr<AbstractTimelineMarker>(Move(clone));
}
};
/* static */ void
CycleCollectedJSRuntime::GCNurseryCollectionCallback(JSRuntime* aRuntime,
JS::GCNurseryProgress aProgress,
JS::gcreason::Reason aReason)
{
CycleCollectedJSRuntime* self = CycleCollectedJSRuntime::Get();
MOZ_ASSERT(self->Runtime() == aRuntime);
MOZ_ASSERT(NS_IsMainThread());
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
if (timelines && !timelines->IsEmpty()) {
UniquePtr<AbstractTimelineMarker> abstractMarker(
MakeUnique<MinorGCMarker>(aProgress, aReason));
timelines->AddMarkerForAllObservedDocShells(abstractMarker);
}
if (self->mPrevGCNurseryCollectionCallback) {
self->mPrevGCNurseryCollectionCallback(aRuntime, aProgress, aReason);
}
}
/* static */ void
CycleCollectedJSRuntime::OutOfMemoryCallback(JSContext* aContext,
void* aData)
{
CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
MOZ_ASSERT(JS_GetRuntime(aContext) == self->Runtime());
self->OnOutOfMemory();
}
/* static */ void
CycleCollectedJSRuntime::LargeAllocationFailureCallback(void* aData)
{
CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
self->OnLargeAllocationFailure();
}
/* static */ bool
CycleCollectedJSRuntime::ContextCallback(JSContext* aContext,
unsigned aOperation,
void* aData)
{
CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
MOZ_ASSERT(JS_GetRuntime(aContext) == self->Runtime());
return self->CustomContextCallback(aContext, aOperation);
}
class PromiseJobRunnable final : public nsRunnable
{
public:
PromiseJobRunnable(JSContext* aCx, JS::HandleObject aCallback)
: mCallback(new PromiseJobCallback(aCx, aCallback, nullptr))
{
}
virtual ~PromiseJobRunnable()
{
}
protected:
NS_IMETHOD
Run() override
{
mCallback->Call();
return NS_OK;
}
private:
RefPtr<PromiseJobCallback> mCallback;
};
/* static */
bool
CycleCollectedJSRuntime::EnqueuePromiseJobCallback(JSContext* aCx,
JS::HandleObject aJob,
void* aData)
{
CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
MOZ_ASSERT(JS_GetRuntime(aCx) == self->Runtime());
MOZ_ASSERT(Get() == self);
nsCOMPtr<nsIRunnable> runnable = new PromiseJobRunnable(aCx, aJob);
self->GetPromiseMicroTaskQueue().push(runnable);
return true;
}
struct JsGcTracer : public TraceCallbacks
{
virtual void Trace(JS::Heap<JS::Value>* aPtr, const char* aName,
void* aClosure) const override
{
JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
}
virtual void Trace(JS::Heap<jsid>* aPtr, const char* aName,
void* aClosure) const override
{
JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
}
virtual void Trace(JS::Heap<JSObject*>* aPtr, const char* aName,
void* aClosure) const override
{
JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
}
virtual void Trace(JSObject** aPtr, const char* aName,
void* aClosure) const override
{
js::UnsafeTraceManuallyBarrieredEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
}
virtual void Trace(JS::TenuredHeap<JSObject*>* aPtr, const char* aName,
void* aClosure) const override
{
JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
}
virtual void Trace(JS::Heap<JSString*>* aPtr, const char* aName,
void* aClosure) const override
{
JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
}
virtual void Trace(JS::Heap<JSScript*>* aPtr, const char* aName,
void* aClosure) const override
{
JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
}
virtual void Trace(JS::Heap<JSFunction*>* aPtr, const char* aName,
void* aClosure) const override
{
JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
}
};
void
mozilla::TraceScriptHolder(nsISupports* aHolder, JSTracer* aTracer)
{
nsXPCOMCycleCollectionParticipant* participant = nullptr;
CallQueryInterface(aHolder, &participant);
participant->Trace(aHolder, JsGcTracer(), aTracer);
}
void
CycleCollectedJSRuntime::TraceNativeGrayRoots(JSTracer* aTracer)
{
MOZ_ASSERT(mJSRuntime);
// NB: This is here just to preserve the existing XPConnect order. I doubt it
// would hurt to do this after the JS holders.
TraceAdditionalNativeGrayRoots(aTracer);
for (auto iter = mJSHolders.Iter(); !iter.Done(); iter.Next()) {
void* holder = iter.Key();
nsScriptObjectTracer*& tracer = iter.Data();
tracer->Trace(holder, JsGcTracer(), aTracer);
}
}
void
CycleCollectedJSRuntime::AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer)
{
MOZ_ASSERT(mJSRuntime);
mJSHolders.Put(aHolder, aTracer);
}
struct ClearJSHolder : public TraceCallbacks
{
virtual void Trace(JS::Heap<JS::Value>* aPtr, const char*, void*) const override
{
aPtr->setUndefined();
}
virtual void Trace(JS::Heap<jsid>* aPtr, const char*, void*) const override
{
*aPtr = JSID_VOID;
}
virtual void Trace(JS::Heap<JSObject*>* aPtr, const char*, void*) const override
{
*aPtr = nullptr;
}
virtual void Trace(JSObject** aPtr, const char* aName,
void* aClosure) const override
{
*aPtr = nullptr;
}
virtual void Trace(JS::TenuredHeap<JSObject*>* aPtr, const char*, void*) const override
{
*aPtr = nullptr;
}
virtual void Trace(JS::Heap<JSString*>* aPtr, const char*, void*) const override
{
*aPtr = nullptr;
}
virtual void Trace(JS::Heap<JSScript*>* aPtr, const char*, void*) const override
{
*aPtr = nullptr;
}
virtual void Trace(JS::Heap<JSFunction*>* aPtr, const char*, void*) const override
{
*aPtr = nullptr;
}
};
void
CycleCollectedJSRuntime::RemoveJSHolder(void* aHolder)
{
MOZ_ASSERT(mJSRuntime);
nsScriptObjectTracer* tracer = mJSHolders.Get(aHolder);
if (!tracer) {
return;
}
tracer->Trace(aHolder, ClearJSHolder(), nullptr);
mJSHolders.Remove(aHolder);
}
#ifdef DEBUG
bool
CycleCollectedJSRuntime::IsJSHolder(void* aHolder)
{
MOZ_ASSERT(mJSRuntime);
return mJSHolders.Get(aHolder, nullptr);
}
static void
AssertNoGcThing(JS::GCCellPtr aGCThing, const char* aName, void* aClosure)
{
MOZ_ASSERT(!aGCThing);
}
void
CycleCollectedJSRuntime::AssertNoObjectsToTrace(void* aPossibleJSHolder)
{
MOZ_ASSERT(mJSRuntime);
nsScriptObjectTracer* tracer = mJSHolders.Get(aPossibleJSHolder);
if (tracer) {
tracer->Trace(aPossibleJSHolder, TraceCallbackFunc(AssertNoGcThing), nullptr);
}
}
#endif
already_AddRefed<nsIException>
CycleCollectedJSRuntime::GetPendingException() const
{
MOZ_ASSERT(mJSRuntime);
nsCOMPtr<nsIException> out = mPendingException;
return out.forget();
}
void
CycleCollectedJSRuntime::SetPendingException(nsIException* aException)
{
MOZ_ASSERT(mJSRuntime);
mPendingException = aException;
}
std::queue<nsCOMPtr<nsIRunnable>>&
CycleCollectedJSRuntime::GetPromiseMicroTaskQueue()
{
MOZ_ASSERT(mJSRuntime);
return mPromiseMicroTaskQueue;
}
nsCycleCollectionParticipant*
CycleCollectedJSRuntime::GCThingParticipant()
{
MOZ_ASSERT(mJSRuntime);
return &mGCThingCycleCollectorGlobal;
}
nsCycleCollectionParticipant*
CycleCollectedJSRuntime::ZoneParticipant()
{
MOZ_ASSERT(mJSRuntime);
return &mJSZoneCycleCollectorGlobal;
}
nsresult
CycleCollectedJSRuntime::TraverseRoots(nsCycleCollectionNoteRootCallback& aCb)
{
MOZ_ASSERT(mJSRuntime);
TraverseNativeRoots(aCb);
NoteWeakMapsTracer trc(mJSRuntime, aCb);
js::TraceWeakMaps(&trc);
return NS_OK;
}
/*
* Return true if there exists a JSContext with a default global whose current
* inner is gray. The intent is to look for JS Object windows. We don't merge
* system compartments, so we don't use them to trigger merging CCs.
*/
bool
CycleCollectedJSRuntime::UsefulToMergeZones() const
{
MOZ_ASSERT(mJSRuntime);
if (!NS_IsMainThread()) {
return false;
}
JSContext* iter = nullptr;
JSContext* cx;
JSAutoRequest ar(nsContentUtils::GetSafeJSContext());
while ((cx = JS_ContextIterator(mJSRuntime, &iter))) {
// Skip anything without an nsIScriptContext.
nsIScriptContext* scx = GetScriptContextFromJSContext(cx);
JS::RootedObject obj(cx, scx ? scx->GetWindowProxyPreserveColor() : nullptr);
if (!obj) {
continue;
}
MOZ_ASSERT(js::IsWindowProxy(obj));
// Grab the global from the WindowProxy.
obj = js::ToWindowIfWindowProxy(obj);
MOZ_ASSERT(JS_IsGlobalObject(obj));
if (JS::ObjectIsMarkedGray(obj) &&
!js::IsSystemCompartment(js::GetObjectCompartment(obj))) {
return true;
}
}
return false;
}
void
CycleCollectedJSRuntime::FixWeakMappingGrayBits() const
{
MOZ_ASSERT(mJSRuntime);
MOZ_ASSERT(!JS::IsIncrementalGCInProgress(mJSRuntime),
"Don't call FixWeakMappingGrayBits during a GC.");
FixWeakMappingGrayBitsTracer fixer(mJSRuntime);
fixer.FixAll();
}
bool
CycleCollectedJSRuntime::AreGCGrayBitsValid() const
{
MOZ_ASSERT(mJSRuntime);
return js::AreGCGrayBitsValid(mJSRuntime);
}
void
CycleCollectedJSRuntime::GarbageCollect(uint32_t aReason) const
{
MOZ_ASSERT(mJSRuntime);
MOZ_ASSERT(aReason < JS::gcreason::NUM_REASONS);
JS::gcreason::Reason gcreason = static_cast<JS::gcreason::Reason>(aReason);
JS::PrepareForFullGC(mJSRuntime);
JS::GCForReason(mJSRuntime, GC_NORMAL, gcreason);
}
void
CycleCollectedJSRuntime::JSObjectsTenured()
{
MOZ_ASSERT(mJSRuntime);
for (auto iter = mNurseryObjects.Iter(); !iter.Done(); iter.Next()) {
nsWrapperCache* cache = iter.Get();
JSObject* wrapper = cache->GetWrapperPreserveColor();
MOZ_ASSERT(wrapper);
if (!JS::ObjectIsTenured(wrapper)) {
MOZ_ASSERT(!cache->PreservingWrapper());
const JSClass* jsClass = js::GetObjectJSClass(wrapper);
jsClass->finalize(nullptr, wrapper);
}
}
#ifdef DEBUG
for (auto iter = mPreservedNurseryObjects.Iter(); !iter.Done(); iter.Next()) {
MOZ_ASSERT(JS::ObjectIsTenured(iter.Get().get()));
}
#endif
mNurseryObjects.Clear();
mPreservedNurseryObjects.Clear();
}
void
CycleCollectedJSRuntime::NurseryWrapperAdded(nsWrapperCache* aCache)
{
MOZ_ASSERT(mJSRuntime);
MOZ_ASSERT(aCache);
MOZ_ASSERT(aCache->GetWrapperPreserveColor());
MOZ_ASSERT(!JS::ObjectIsTenured(aCache->GetWrapperPreserveColor()));
mNurseryObjects.InfallibleAppend(aCache);
}
void
CycleCollectedJSRuntime::NurseryWrapperPreserved(JSObject* aWrapper)
{
MOZ_ASSERT(mJSRuntime);
mPreservedNurseryObjects.InfallibleAppend(
JS::PersistentRooted<JSObject*>(mJSRuntime, aWrapper));
}
void
CycleCollectedJSRuntime::DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
DeferredFinalizeFunction aFunc,
void* aThing)
{
MOZ_ASSERT(mJSRuntime);
void* thingArray = nullptr;
bool hadThingArray = mDeferredFinalizerTable.Get(aFunc, &thingArray);
thingArray = aAppendFunc(thingArray, aThing);
if (!hadThingArray) {
mDeferredFinalizerTable.Put(aFunc, thingArray);
}
}
void
CycleCollectedJSRuntime::DeferredFinalize(nsISupports* aSupports)
{
MOZ_ASSERT(mJSRuntime);
typedef DeferredFinalizerImpl<nsISupports> Impl;
DeferredFinalize(Impl::AppendDeferredFinalizePointer, Impl::DeferredFinalize,
aSupports);
}
void
CycleCollectedJSRuntime::DumpJSHeap(FILE* aFile)
{
MOZ_ASSERT(mJSRuntime);
js::DumpHeap(Runtime(), aFile, js::CollectNurseryBeforeDump);
}
void
CycleCollectedJSRuntime::ProcessStableStateQueue()
{
MOZ_ASSERT(mJSRuntime);
MOZ_RELEASE_ASSERT(!mDoingStableStates);
mDoingStableStates = true;
for (uint32_t i = 0; i < mStableStateEvents.Length(); ++i) {
nsCOMPtr<nsIRunnable> event = mStableStateEvents[i].forget();
event->Run();
}
mStableStateEvents.Clear();
mDoingStableStates = false;
}
void
CycleCollectedJSRuntime::ProcessMetastableStateQueue(uint32_t aRecursionDepth)
{
MOZ_ASSERT(mJSRuntime);
MOZ_RELEASE_ASSERT(!mDoingStableStates);
mDoingStableStates = true;
nsTArray<RunInMetastableStateData> localQueue = Move(mMetastableStateEvents);
for (uint32_t i = 0; i < localQueue.Length(); ++i)
{
RunInMetastableStateData& data = localQueue[i];
if (data.mRecursionDepth != aRecursionDepth) {
continue;
}
{
nsCOMPtr<nsIRunnable> runnable = data.mRunnable.forget();
runnable->Run();
}
localQueue.RemoveElementAt(i--);
}
// If the queue has events in it now, they were added from something we called,
// so they belong at the end of the queue.
localQueue.AppendElements(mMetastableStateEvents);
localQueue.SwapElements(mMetastableStateEvents);
mDoingStableStates = false;
}
void
CycleCollectedJSRuntime::AfterProcessTask(uint32_t aRecursionDepth)
{
MOZ_ASSERT(mJSRuntime);
// See HTML 6.1.4.2 Processing model
// Execute any events that were waiting for a microtask to complete.
// This is not (yet) in the spec.
ProcessMetastableStateQueue(aRecursionDepth);
// Step 4.1: Execute microtasks.
if (NS_IsMainThread()) {
nsContentUtils::PerformMainThreadMicroTaskCheckpoint();
}
Promise::PerformMicroTaskCheckpoint();
// Step 4.2 Execute any events that were waiting for a stable state.
ProcessStableStateQueue();
}
void
CycleCollectedJSRuntime::AfterProcessMicrotask()
{
MOZ_ASSERT(mJSRuntime);
AfterProcessMicrotask(RecursionDepth());
}
void
CycleCollectedJSRuntime::AfterProcessMicrotask(uint32_t aRecursionDepth)
{
MOZ_ASSERT(mJSRuntime);
// Between microtasks, execute any events that were waiting for a microtask
// to complete.
ProcessMetastableStateQueue(aRecursionDepth);
}
uint32_t
CycleCollectedJSRuntime::RecursionDepth()
{
return mOwningThread->RecursionDepth();
}
void
CycleCollectedJSRuntime::RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable)
{
MOZ_ASSERT(mJSRuntime);
mStableStateEvents.AppendElement(Move(aRunnable));
}
void
CycleCollectedJSRuntime::RunInMetastableState(already_AddRefed<nsIRunnable>&& aRunnable)
{
MOZ_ASSERT(mJSRuntime);
RunInMetastableStateData data;
data.mRunnable = aRunnable;
MOZ_ASSERT(mOwningThread);
data.mRecursionDepth = RecursionDepth();
// There must be an event running to get here.
#ifndef MOZ_WIDGET_COCOA
MOZ_ASSERT(data.mRecursionDepth > mBaseRecursionDepth);
#endif
mMetastableStateEvents.AppendElement(Move(data));
}
IncrementalFinalizeRunnable::IncrementalFinalizeRunnable(CycleCollectedJSRuntime* aRt,
DeferredFinalizerTable& aFinalizers)
: mRuntime(aRt)
, mFinalizeFunctionToRun(0)
, mReleasing(false)
{
for (auto iter = aFinalizers.Iter(); !iter.Done(); iter.Next()) {
DeferredFinalizeFunction& function = iter.Key();
void*& data = iter.Data();
DeferredFinalizeFunctionHolder* holder =
mDeferredFinalizeFunctions.AppendElement();
holder->run = function;
holder->data = data;
iter.Remove();
}
}
IncrementalFinalizeRunnable::~IncrementalFinalizeRunnable()
{
MOZ_ASSERT(this != mRuntime->mFinalizeRunnable);
}
void
IncrementalFinalizeRunnable::ReleaseNow(bool aLimited)
{
if (mReleasing) {
NS_WARNING("Re-entering ReleaseNow");
return;
}
{
mozilla::AutoRestore<bool> ar(mReleasing);
mReleasing = true;
MOZ_ASSERT(mDeferredFinalizeFunctions.Length() != 0,
"We should have at least ReleaseSliceNow to run");
MOZ_ASSERT(mFinalizeFunctionToRun < mDeferredFinalizeFunctions.Length(),
"No more finalizers to run?");
TimeDuration sliceTime = TimeDuration::FromMilliseconds(SliceMillis);
TimeStamp started = TimeStamp::Now();
bool timeout = false;
do {
const DeferredFinalizeFunctionHolder& function =
mDeferredFinalizeFunctions[mFinalizeFunctionToRun];
if (aLimited) {
bool done = false;
while (!timeout && !done) {
/*
* We don't want to read the clock too often, so we try to
* release slices of 100 items.
*/
done = function.run(100, function.data);
timeout = TimeStamp::Now() - started >= sliceTime;
}
if (done) {
++mFinalizeFunctionToRun;
}
if (timeout) {
break;
}
} else {
while (!function.run(UINT32_MAX, function.data));
++mFinalizeFunctionToRun;
}
} while (mFinalizeFunctionToRun < mDeferredFinalizeFunctions.Length());
}
if (mFinalizeFunctionToRun == mDeferredFinalizeFunctions.Length()) {
MOZ_ASSERT(mRuntime->mFinalizeRunnable == this);
mDeferredFinalizeFunctions.Clear();
// NB: This may delete this!
mRuntime->mFinalizeRunnable = nullptr;
}
}
NS_IMETHODIMP
IncrementalFinalizeRunnable::Run()
{
if (mRuntime->mFinalizeRunnable != this) {
/* These items were already processed synchronously in JSGC_END. */
MOZ_ASSERT(!mDeferredFinalizeFunctions.Length());
return NS_OK;
}
TimeStamp start = TimeStamp::Now();
ReleaseNow(true);
if (mDeferredFinalizeFunctions.Length()) {
nsresult rv = NS_DispatchToCurrentThread(this);
if (NS_FAILED(rv)) {
ReleaseNow(false);
}
}
uint32_t duration = (uint32_t)((TimeStamp::Now() - start).ToMilliseconds());
Telemetry::Accumulate(Telemetry::DEFERRED_FINALIZE_ASYNC, duration);
return NS_OK;
}
void
CycleCollectedJSRuntime::FinalizeDeferredThings(DeferredFinalizeType aType)
{
MOZ_ASSERT(mJSRuntime);
/*
* If the previous GC created a runnable to finalize objects
* incrementally, and if it hasn't finished yet, finish it now. We
* don't want these to build up. We also don't want to allow any
* existing incremental finalize runnables to run after a
* non-incremental GC, since they are often used to detect leaks.
*/
if (mFinalizeRunnable) {
mFinalizeRunnable->ReleaseNow(false);
if (mFinalizeRunnable) {
// If we re-entered ReleaseNow, we couldn't delete mFinalizeRunnable and
// we need to just continue processing it.
return;
}
}
if (mDeferredFinalizerTable.Count() == 0) {
return;
}
mFinalizeRunnable = new IncrementalFinalizeRunnable(this,
mDeferredFinalizerTable);
// Everything should be gone now.
MOZ_ASSERT(mDeferredFinalizerTable.Count() == 0);
if (aType == FinalizeIncrementally) {
NS_DispatchToCurrentThread(mFinalizeRunnable);
} else {
mFinalizeRunnable->ReleaseNow(false);
MOZ_ASSERT(!mFinalizeRunnable);
}
}
void
CycleCollectedJSRuntime::AnnotateAndSetOutOfMemory(OOMState* aStatePtr,
OOMState aNewState)
{
MOZ_ASSERT(mJSRuntime);
*aStatePtr = aNewState;
#ifdef MOZ_CRASHREPORTER
CrashReporter::AnnotateCrashReport(aStatePtr == &mOutOfMemoryState
? NS_LITERAL_CSTRING("JSOutOfMemory")
: NS_LITERAL_CSTRING("JSLargeAllocationFailure"),
aNewState == OOMState::Reporting
? NS_LITERAL_CSTRING("Reporting")
: aNewState == OOMState::Reported
? NS_LITERAL_CSTRING("Reported")
: NS_LITERAL_CSTRING("Recovered"));
#endif
}
void
CycleCollectedJSRuntime::OnGC(JSGCStatus aStatus)
{
MOZ_ASSERT(mJSRuntime);
switch (aStatus) {
case JSGC_BEGIN:
nsCycleCollector_prepareForGarbageCollection();
mZonesWaitingForGC.Clear();
break;
case JSGC_END: {
#ifdef MOZ_CRASHREPORTER
if (mOutOfMemoryState == OOMState::Reported) {
AnnotateAndSetOutOfMemory(&mOutOfMemoryState, OOMState::Recovered);
}
if (mLargeAllocationFailureState == OOMState::Reported) {
AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState, OOMState::Recovered);
}
#endif
// Do any deferred finalization of native objects.
FinalizeDeferredThings(JS::WasIncrementalGC(mJSRuntime) ? FinalizeIncrementally :
FinalizeNow);
break;
}
default:
MOZ_CRASH();
}
CustomGCCallback(aStatus);
}
void
CycleCollectedJSRuntime::OnOutOfMemory()
{
MOZ_ASSERT(mJSRuntime);
AnnotateAndSetOutOfMemory(&mOutOfMemoryState, OOMState::Reporting);
CustomOutOfMemoryCallback();
AnnotateAndSetOutOfMemory(&mOutOfMemoryState, OOMState::Reported);
}
void
CycleCollectedJSRuntime::OnLargeAllocationFailure()
{
MOZ_ASSERT(mJSRuntime);
AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState, OOMState::Reporting);
CustomLargeAllocationFailureCallback();
AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState, OOMState::Reported);
}
void
CycleCollectedJSRuntime::PrepareWaitingZonesForGC()
{
if (mZonesWaitingForGC.Count() == 0) {
JS::PrepareForFullGC(Runtime());
} else {
for (auto iter = mZonesWaitingForGC.Iter(); !iter.Done(); iter.Next()) {
JS::PrepareZoneForGC(iter.Get()->GetKey());
}
mZonesWaitingForGC.Clear();
}
}
void
CycleCollectedJSRuntime::EnvironmentPreparer::invoke(JS::HandleObject scope,
js::ScriptEnvironmentPreparer::Closure& closure)
{
nsIGlobalObject* global = xpc::NativeGlobal(scope);
// Not much we can do if we simply don't have a usable global here...
NS_ENSURE_TRUE_VOID(global && global->GetGlobalJSObject());
bool mainThread = NS_IsMainThread();
JSContext* cx =
mainThread ? nullptr : nsContentUtils::GetDefaultJSContextForThread();
AutoEntryScript aes(global, "JS-engine-initiated execution", mainThread, cx);
aes.TakeOwnershipOfErrorReporting();
MOZ_ASSERT(!JS_IsExceptionPending(aes.cx()));
DebugOnly<bool> ok = closure(aes.cx());
MOZ_ASSERT_IF(ok, !JS_IsExceptionPending(aes.cx()));
// The AutoEntryScript will check for JS_IsExceptionPending on the
// JSContext and report it as needed as it comes off the stack.
}