js/xpconnect/src/XPCJSRuntime.cpp

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* vim: set ts=8 sts=4 et sw=4 tw=99: */
     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/. */
     7 /* Per JSRuntime object */
     9 #include "mozilla/MemoryReporting.h"
    11 #include "xpcprivate.h"
    12 #include "xpcpublic.h"
    13 #include "XPCWrapper.h"
    14 #include "XPCJSMemoryReporter.h"
    15 #include "WrapperFactory.h"
    16 #include "dom_quickstubs.h"
    17 #include "mozJSComponentLoader.h"
    19 #include "nsIMemoryInfoDumper.h"
    20 #include "nsIMemoryReporter.h"
    21 #include "nsIObserverService.h"
    22 #include "nsIDebug2.h"
    23 #include "amIAddonManager.h"
    24 #include "nsPIDOMWindow.h"
    25 #include "nsPrintfCString.h"
    26 #include "mozilla/Preferences.h"
    27 #include "mozilla/Telemetry.h"
    28 #include "mozilla/Services.h"
    30 #include "nsContentUtils.h"
    31 #include "nsCxPusher.h"
    32 #include "nsCCUncollectableMarker.h"
    33 #include "nsCycleCollectionNoteRootCallback.h"
    34 #include "nsScriptLoader.h"
    35 #include "jsfriendapi.h"
    36 #include "jsprf.h"
    37 #include "js/MemoryMetrics.h"
    38 #include "js/OldDebugAPI.h"
    39 #include "mozilla/dom/GeneratedAtomList.h"
    40 #include "mozilla/dom/BindingUtils.h"
    41 #include "mozilla/dom/Element.h"
    42 #include "mozilla/dom/WindowBinding.h"
    43 #include "mozilla/Atomics.h"
    44 #include "mozilla/Attributes.h"
    45 #include "AccessCheck.h"
    46 #include "nsGlobalWindow.h"
    47 #include "nsAboutProtocolUtils.h"
    49 #include "GeckoProfiler.h"
    50 #include "nsIXULRuntime.h"
    51 #include "nsJSPrincipals.h"
    53 #ifdef MOZ_CRASHREPORTER
    54 #include "nsExceptionHandler.h"
    55 #endif
    57 using namespace mozilla;
    58 using namespace xpc;
    59 using namespace JS;
    60 using mozilla::dom::PerThreadAtomCache;
    62 /***************************************************************************/
    64 const char* const XPCJSRuntime::mStrings[] = {
    65     "constructor",          // IDX_CONSTRUCTOR
    66     "toString",             // IDX_TO_STRING
    67     "toSource",             // IDX_TO_SOURCE
    68     "lastResult",           // IDX_LAST_RESULT
    69     "returnCode",           // IDX_RETURN_CODE
    70     "value",                // IDX_VALUE
    71     "QueryInterface",       // IDX_QUERY_INTERFACE
    72     "Components",           // IDX_COMPONENTS
    73     "wrappedJSObject",      // IDX_WRAPPED_JSOBJECT
    74     "Object",               // IDX_OBJECT
    75     "Function",             // IDX_FUNCTION
    76     "prototype",            // IDX_PROTOTYPE
    77     "createInstance",       // IDX_CREATE_INSTANCE
    78     "item",                 // IDX_ITEM
    79     "__proto__",            // IDX_PROTO
    80     "__iterator__",         // IDX_ITERATOR
    81     "__exposedProps__",     // IDX_EXPOSEDPROPS
    82     "eval",                 // IDX_EVAL
    83     "controllers",           // IDX_CONTROLLERS
    84 };
    86 /***************************************************************************/
    88 static mozilla::Atomic<bool> sDiscardSystemSource(false);
    90 bool
    91 xpc::ShouldDiscardSystemSource() { return sDiscardSystemSource; }
    93 static void * const UNMARK_ONLY = nullptr;
    94 static void * const UNMARK_AND_SWEEP = (void *)1;
    96 static PLDHashOperator
    97 NativeInterfaceSweeper(PLDHashTable *table, PLDHashEntryHdr *hdr,
    98                        uint32_t number, void *arg)
    99 {
   100     XPCNativeInterface* iface = ((IID2NativeInterfaceMap::Entry*)hdr)->value;
   101     if (iface->IsMarked()) {
   102         iface->Unmark();
   103         return PL_DHASH_NEXT;
   104     }
   106     if (arg == UNMARK_ONLY)
   107         return PL_DHASH_NEXT;
   109     XPCNativeInterface::DestroyInstance(iface);
   110     return PL_DHASH_REMOVE;
   111 }
   113 // *Some* NativeSets are referenced from mClassInfo2NativeSetMap.
   114 // *All* NativeSets are referenced from mNativeSetMap.
   115 // So, in mClassInfo2NativeSetMap we just clear references to the unmarked.
   116 // In mNativeSetMap we clear the references to the unmarked *and* delete them.
   118 static PLDHashOperator
   119 NativeUnMarkedSetRemover(PLDHashTable *table, PLDHashEntryHdr *hdr,
   120                          uint32_t number, void *arg)
   121 {
   122     XPCNativeSet* set = ((ClassInfo2NativeSetMap::Entry*)hdr)->value;
   123     if (set->IsMarked())
   124         return PL_DHASH_NEXT;
   125     return PL_DHASH_REMOVE;
   126 }
   128 static PLDHashOperator
   129 NativeSetSweeper(PLDHashTable *table, PLDHashEntryHdr *hdr,
   130                  uint32_t number, void *arg)
   131 {
   132     XPCNativeSet* set = ((NativeSetMap::Entry*)hdr)->key_value;
   133     if (set->IsMarked()) {
   134         set->Unmark();
   135         return PL_DHASH_NEXT;
   136     }
   138     if (arg == UNMARK_ONLY)
   139         return PL_DHASH_NEXT;
   141     XPCNativeSet::DestroyInstance(set);
   142     return PL_DHASH_REMOVE;
   143 }
   145 static PLDHashOperator
   146 JSClassSweeper(PLDHashTable *table, PLDHashEntryHdr *hdr,
   147                uint32_t number, void *arg)
   148 {
   149     XPCNativeScriptableShared* shared =
   150         ((XPCNativeScriptableSharedMap::Entry*) hdr)->key;
   151     if (shared->IsMarked()) {
   152         shared->Unmark();
   153         return PL_DHASH_NEXT;
   154     }
   156     if (arg == UNMARK_ONLY)
   157         return PL_DHASH_NEXT;
   159     delete shared;
   160     return PL_DHASH_REMOVE;
   161 }
   163 static PLDHashOperator
   164 DyingProtoKiller(PLDHashTable *table, PLDHashEntryHdr *hdr,
   165                  uint32_t number, void *arg)
   166 {
   167     XPCWrappedNativeProto* proto =
   168         (XPCWrappedNativeProto*)((PLDHashEntryStub*)hdr)->key;
   169     delete proto;
   170     return PL_DHASH_REMOVE;
   171 }
   173 static PLDHashOperator
   174 DetachedWrappedNativeProtoMarker(PLDHashTable *table, PLDHashEntryHdr *hdr,
   175                                  uint32_t number, void *arg)
   176 {
   177     XPCWrappedNativeProto* proto =
   178         (XPCWrappedNativeProto*)((PLDHashEntryStub*)hdr)->key;
   180     proto->Mark();
   181     return PL_DHASH_NEXT;
   182 }
   184 bool
   185 XPCJSRuntime::CustomContextCallback(JSContext *cx, unsigned operation)
   186 {
   187     if (operation == JSCONTEXT_NEW) {
   188         if (!OnJSContextNew(cx)) {
   189             return false;
   190         }
   191     } else if (operation == JSCONTEXT_DESTROY) {
   192         delete XPCContext::GetXPCContext(cx);
   193     }
   195     nsTArray<xpcContextCallback> callbacks(extraContextCallbacks);
   196     for (uint32_t i = 0; i < callbacks.Length(); ++i) {
   197         if (!callbacks[i](cx, operation)) {
   198             return false;
   199         }
   200     }
   202     return true;
   203 }
   205 class AsyncFreeSnowWhite : public nsRunnable
   206 {
   207 public:
   208   NS_IMETHOD Run()
   209   {
   210       TimeStamp start = TimeStamp::Now();
   211       bool hadSnowWhiteObjects = nsCycleCollector_doDeferredDeletion();
   212       Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_ASYNC_SNOW_WHITE_FREEING,
   213                             uint32_t((TimeStamp::Now() - start).ToMilliseconds()));
   214       if (hadSnowWhiteObjects && !mContinuation) {
   215           mContinuation = true;
   216           if (NS_FAILED(NS_DispatchToCurrentThread(this))) {
   217               mActive = false;
   218           }
   219       } else {
   220           mActive = false;
   221       }
   222       return NS_OK;
   223   }
   225   void Dispatch(bool aContinuation = false)
   226   {
   227       if (mContinuation) {
   228           mContinuation = aContinuation;
   229       }
   230       if (!mActive && NS_SUCCEEDED(NS_DispatchToCurrentThread(this))) {
   231           mActive = true;
   232       }
   233   }
   235   AsyncFreeSnowWhite() : mContinuation(false), mActive(false) {}
   237 public:
   238   bool mContinuation;
   239   bool mActive;
   240 };
   242 namespace xpc {
   244 static uint32_t kLivingAdopters = 0;
   246 void
   247 RecordAdoptedNode(JSCompartment *c)
   248 {
   249     CompartmentPrivate *priv = EnsureCompartmentPrivate(c);
   250     if (!priv->adoptedNode) {
   251         priv->adoptedNode = true;
   252         ++kLivingAdopters;
   253     }
   254 }
   256 void
   257 RecordDonatedNode(JSCompartment *c)
   258 {
   259     EnsureCompartmentPrivate(c)->donatedNode = true;
   260 }
   262 CompartmentPrivate::~CompartmentPrivate()
   263 {
   264     MOZ_COUNT_DTOR(xpc::CompartmentPrivate);
   266     Telemetry::Accumulate(Telemetry::COMPARTMENT_ADOPTED_NODE, adoptedNode);
   267     Telemetry::Accumulate(Telemetry::COMPARTMENT_DONATED_NODE, donatedNode);
   268     if (adoptedNode)
   269         --kLivingAdopters;
   270 }
   272 static bool
   273 TryParseLocationURICandidate(const nsACString& uristr,
   274                              CompartmentPrivate::LocationHint aLocationHint,
   275                              nsIURI** aURI)
   276 {
   277     static NS_NAMED_LITERAL_CSTRING(kGRE, "resource://gre/");
   278     static NS_NAMED_LITERAL_CSTRING(kToolkit, "chrome://global/");
   279     static NS_NAMED_LITERAL_CSTRING(kBrowser, "chrome://browser/");
   281     if (aLocationHint == CompartmentPrivate::LocationHintAddon) {
   282         // Blacklist some known locations which are clearly not add-on related.
   283         if (StringBeginsWith(uristr, kGRE) ||
   284             StringBeginsWith(uristr, kToolkit) ||
   285             StringBeginsWith(uristr, kBrowser))
   286             return false;
   287     }
   289     nsCOMPtr<nsIURI> uri;
   290     if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), uristr)))
   291         return false;
   293     nsAutoCString scheme;
   294     if (NS_FAILED(uri->GetScheme(scheme)))
   295         return false;
   297     // Cannot really map data: and blob:.
   298     // Also, data: URIs are pretty memory hungry, which is kinda bad
   299     // for memory reporter use.
   300     if (scheme.EqualsLiteral("data") || scheme.EqualsLiteral("blob"))
   301         return false;
   303     uri.forget(aURI);
   304     return true;
   305 }
   307 bool CompartmentPrivate::TryParseLocationURI(CompartmentPrivate::LocationHint aLocationHint,
   308                                              nsIURI **aURI)
   309 {
   310     if (!aURI)
   311         return false;
   313     // Need to parse the URI.
   314     if (location.IsEmpty())
   315         return false;
   317     // Handle Sandbox location strings.
   318     // A sandbox string looks like this:
   319     // <sandboxName> (from: <js-stack-frame-filename>:<lineno>)
   320     // where <sandboxName> is user-provided via Cu.Sandbox()
   321     // and <js-stack-frame-filename> and <lineno> is the stack frame location
   322     // from where Cu.Sandbox was called.
   323     // <js-stack-frame-filename> furthermore is "free form", often using a
   324     // "uri -> uri -> ..." chain. The following code will and must handle this
   325     // common case.
   326     // It should be noted that other parts of the code may already rely on the
   327     // "format" of these strings, such as the add-on SDK.
   329     static const nsDependentCString from("(from: ");
   330     static const nsDependentCString arrow(" -> ");
   331     static const size_t fromLength = from.Length();
   332     static const size_t arrowLength = arrow.Length();
   334     // See: XPCComponents.cpp#AssembleSandboxMemoryReporterName
   335     int32_t idx = location.Find(from);
   336     if (idx < 0)
   337         return TryParseLocationURICandidate(location, aLocationHint, aURI);
   340     // When parsing we're looking for the right-most URI. This URI may be in
   341     // <sandboxName>, so we try this first.
   342     if (TryParseLocationURICandidate(Substring(location, 0, idx), aLocationHint,
   343                                      aURI))
   344         return true;
   346     // Not in <sandboxName> so we need to inspect <js-stack-frame-filename> and
   347     // the chain that is potentially contained within and grab the rightmost
   348     // item that is actually a URI.
   350     // First, hack off the :<lineno>) part as well
   351     int32_t ridx = location.RFind(NS_LITERAL_CSTRING(":"));
   352     nsAutoCString chain(Substring(location, idx + fromLength,
   353                                   ridx - idx - fromLength));
   355     // Loop over the "->" chain. This loop also works for non-chains, or more
   356     // correctly chains with only one item.
   357     for (;;) {
   358         idx = chain.RFind(arrow);
   359         if (idx < 0) {
   360             // This is the last chain item. Try to parse what is left.
   361             return TryParseLocationURICandidate(chain, aLocationHint, aURI);
   362         }
   364         // Try to parse current chain item
   365         if (TryParseLocationURICandidate(Substring(chain, idx + arrowLength),
   366                                          aLocationHint, aURI))
   367             return true;
   369         // Current chain item couldn't be parsed.
   370         // Strip current item and continue.
   371         chain = Substring(chain, 0, idx);
   372     }
   373     MOZ_ASSUME_UNREACHABLE("Chain parser loop does not terminate");
   374 }
   376 CompartmentPrivate*
   377 EnsureCompartmentPrivate(JSObject *obj)
   378 {
   379     return EnsureCompartmentPrivate(js::GetObjectCompartment(obj));
   380 }
   382 CompartmentPrivate*
   383 EnsureCompartmentPrivate(JSCompartment *c)
   384 {
   385     CompartmentPrivate *priv = GetCompartmentPrivate(c);
   386     if (priv)
   387         return priv;
   388     priv = new CompartmentPrivate(c);
   389     JS_SetCompartmentPrivate(c, priv);
   390     return priv;
   391 }
   393 XPCWrappedNativeScope*
   394 MaybeGetObjectScope(JSObject *obj)
   395 {
   396     MOZ_ASSERT(obj);
   397     JSCompartment *compartment = js::GetObjectCompartment(obj);
   399     MOZ_ASSERT(compartment);
   400     CompartmentPrivate *priv = GetCompartmentPrivate(compartment);
   401     if (!priv)
   402         return nullptr;
   404     return priv->scope;
   405 }
   407 static bool
   408 PrincipalImmuneToScriptPolicy(nsIPrincipal* aPrincipal)
   409 {
   410     // System principal gets a free pass.
   411     if (XPCWrapper::GetSecurityManager()->IsSystemPrincipal(aPrincipal))
   412         return true;
   414     // nsExpandedPrincipal gets a free pass.
   415     nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
   416     if (ep)
   417         return true;
   419     // Check whether our URI is an "about:" URI that allows scripts.  If it is,
   420     // we need to allow JS to run.
   421     nsCOMPtr<nsIURI> principalURI;
   422     aPrincipal->GetURI(getter_AddRefs(principalURI));
   423     MOZ_ASSERT(principalURI);
   424     bool isAbout;
   425     nsresult rv = principalURI->SchemeIs("about", &isAbout);
   426     if (NS_SUCCEEDED(rv) && isAbout) {
   427         nsCOMPtr<nsIAboutModule> module;
   428         rv = NS_GetAboutModule(principalURI, getter_AddRefs(module));
   429         if (NS_SUCCEEDED(rv)) {
   430             uint32_t flags;
   431             rv = module->GetURIFlags(principalURI, &flags);
   432             if (NS_SUCCEEDED(rv) &&
   433                 (flags & nsIAboutModule::ALLOW_SCRIPT)) {
   434                 return true;
   435             }
   436         }
   437     }
   439     return false;
   440 }
   442 Scriptability::Scriptability(JSCompartment *c) : mScriptBlocks(0)
   443                                                , mDocShellAllowsScript(true)
   444                                                , mScriptBlockedByPolicy(false)
   445 {
   446     nsIPrincipal *prin = nsJSPrincipals::get(JS_GetCompartmentPrincipals(c));
   447     mImmuneToScriptPolicy = PrincipalImmuneToScriptPolicy(prin);
   449     // If we're not immune, we should have a real principal with a codebase URI.
   450     // Check the URI against the new-style domain policy.
   451     if (!mImmuneToScriptPolicy) {
   452         nsIScriptSecurityManager* ssm = XPCWrapper::GetSecurityManager();
   453         nsCOMPtr<nsIURI> codebase;
   454         nsresult rv = prin->GetURI(getter_AddRefs(codebase));
   455         bool policyAllows;
   456         if (NS_SUCCEEDED(rv) && codebase &&
   457             NS_SUCCEEDED(ssm->PolicyAllowsScript(codebase, &policyAllows)))
   458         {
   459             mScriptBlockedByPolicy = !policyAllows;
   460         } else {
   461             // Something went wrong - be safe and block script.
   462             mScriptBlockedByPolicy = true;
   463         }
   464     }
   465 }
   467 bool
   468 Scriptability::Allowed()
   469 {
   470     return mDocShellAllowsScript && !mScriptBlockedByPolicy &&
   471            mScriptBlocks == 0;
   472 }
   474 bool
   475 Scriptability::IsImmuneToScriptPolicy()
   476 {
   477     return mImmuneToScriptPolicy;
   478 }
   480 void
   481 Scriptability::Block()
   482 {
   483     ++mScriptBlocks;
   484 }
   486 void
   487 Scriptability::Unblock()
   488 {
   489     MOZ_ASSERT(mScriptBlocks > 0);
   490     --mScriptBlocks;
   491 }
   493 void
   494 Scriptability::SetDocShellAllowsScript(bool aAllowed)
   495 {
   496     mDocShellAllowsScript = aAllowed || mImmuneToScriptPolicy;
   497 }
   499 /* static */
   500 Scriptability&
   501 Scriptability::Get(JSObject *aScope)
   502 {
   503     return EnsureCompartmentPrivate(aScope)->scriptability;
   504 }
   506 bool
   507 IsXBLScope(JSCompartment *compartment)
   508 {
   509     // We always eagerly create compartment privates for XBL scopes.
   510     CompartmentPrivate *priv = GetCompartmentPrivate(compartment);
   511     if (!priv || !priv->scope)
   512         return false;
   513     return priv->scope->IsXBLScope();
   514 }
   516 bool
   517 IsInXBLScope(JSObject *obj)
   518 {
   519     return IsXBLScope(js::GetObjectCompartment(obj));
   520 }
   522 bool
   523 IsUniversalXPConnectEnabled(JSCompartment *compartment)
   524 {
   525     CompartmentPrivate *priv = GetCompartmentPrivate(compartment);
   526     if (!priv)
   527         return false;
   528     return priv->universalXPConnectEnabled;
   529 }
   531 bool
   532 IsUniversalXPConnectEnabled(JSContext *cx)
   533 {
   534     JSCompartment *compartment = js::GetContextCompartment(cx);
   535     if (!compartment)
   536         return false;
   537     return IsUniversalXPConnectEnabled(compartment);
   538 }
   540 bool
   541 EnableUniversalXPConnect(JSContext *cx)
   542 {
   543     JSCompartment *compartment = js::GetContextCompartment(cx);
   544     if (!compartment)
   545         return true;
   546     // Never set universalXPConnectEnabled on a chrome compartment - it confuses
   547     // the security wrapping code.
   548     if (AccessCheck::isChrome(compartment))
   549         return true;
   550     CompartmentPrivate *priv = GetCompartmentPrivate(compartment);
   551     if (!priv)
   552         return true;
   553     priv->universalXPConnectEnabled = true;
   555     // Recompute all the cross-compartment wrappers leaving the newly-privileged
   556     // compartment.
   557     bool ok = js::RecomputeWrappers(cx, js::SingleCompartment(compartment),
   558                                     js::AllCompartments());
   559     NS_ENSURE_TRUE(ok, false);
   561     // The Components object normally isn't defined for unprivileged web content,
   562     // but we define it when UniversalXPConnect is enabled to support legacy
   563     // tests.
   564     XPCWrappedNativeScope *scope = priv->scope;
   565     if (!scope)
   566         return true;
   567     scope->ForcePrivilegedComponents();
   568     return scope->AttachComponentsObject(cx);
   569 }
   571 JSObject *
   572 GetJunkScope()
   573 {
   574     XPCJSRuntime *self = nsXPConnect::GetRuntimeInstance();
   575     NS_ENSURE_TRUE(self, nullptr);
   576     return self->GetJunkScope();
   577 }
   579 nsIGlobalObject *
   580 GetJunkScopeGlobal()
   581 {
   582     JSObject *junkScope = GetJunkScope();
   583     // GetJunkScope would ideally never fail, currently it is not yet the case
   584     // unfortunately...(see Bug 874158)
   585     if (!junkScope)
   586         return nullptr;
   587     return GetNativeForGlobal(junkScope);
   588 }
   590 JSObject *
   591 GetCompilationScope()
   592 {
   593     XPCJSRuntime *self = nsXPConnect::GetRuntimeInstance();
   594     NS_ENSURE_TRUE(self, nullptr);
   595     return self->GetCompilationScope();
   596 }
   598 JSObject *
   599 GetSafeJSContextGlobal()
   600 {
   601     return XPCJSRuntime::Get()->GetJSContextStack()->GetSafeJSContextGlobal();
   602 }
   604 nsGlobalWindow*
   605 WindowOrNull(JSObject *aObj)
   606 {
   607     MOZ_ASSERT(aObj);
   608     MOZ_ASSERT(!js::IsWrapper(aObj));
   610     // This will always return null until we have Window on WebIDL bindings,
   611     // at which point it will do the right thing.
   612     if (!IS_WN_CLASS(js::GetObjectClass(aObj))) {
   613         nsGlobalWindow* win = nullptr;
   614         UNWRAP_OBJECT(Window, aObj, win);
   615         return win;
   616     }
   618     nsISupports* supports = XPCWrappedNative::Get(aObj)->GetIdentityObject();
   619     nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(supports);
   620     if (!piWin)
   621         return nullptr;
   622     return static_cast<nsGlobalWindow*>(piWin.get());
   623 }
   625 nsGlobalWindow*
   626 WindowGlobalOrNull(JSObject *aObj)
   627 {
   628     MOZ_ASSERT(aObj);
   629     JSObject *glob = js::GetGlobalForObjectCrossCompartment(aObj);
   631     return WindowOrNull(glob);
   632 }
   634 }
   636 static void
   637 CompartmentDestroyedCallback(JSFreeOp *fop, JSCompartment *compartment)
   638 {
   639     // NB - This callback may be called in JS_DestroyRuntime, which happens
   640     // after the XPCJSRuntime has been torn down.
   642     // Get the current compartment private into an AutoPtr (which will do the
   643     // cleanup for us), and null out the private (which may already be null).
   644     nsAutoPtr<CompartmentPrivate> priv(GetCompartmentPrivate(compartment));
   645     JS_SetCompartmentPrivate(compartment, nullptr);
   646 }
   648 void XPCJSRuntime::TraceNativeBlackRoots(JSTracer* trc)
   649 {
   650     // Skip this part if XPConnect is shutting down. We get into
   651     // bad locking problems with the thread iteration otherwise.
   652     if (!nsXPConnect::XPConnect()->IsShuttingDown()) {
   653         // Trace those AutoMarkingPtr lists!
   654         if (AutoMarkingPtr *roots = Get()->mAutoRoots)
   655             roots->TraceJSAll(trc);
   656     }
   658     // XPCJSObjectHolders don't participate in cycle collection, so always
   659     // trace them here.
   660     XPCRootSetElem *e;
   661     for (e = mObjectHolderRoots; e; e = e->GetNextRoot())
   662         static_cast<XPCJSObjectHolder*>(e)->TraceJS(trc);
   664     dom::TraceBlackJS(trc, JS_GetGCParameter(Runtime(), JSGC_NUMBER),
   665                       nsXPConnect::XPConnect()->IsShuttingDown());
   666 }
   668 void XPCJSRuntime::TraceAdditionalNativeGrayRoots(JSTracer *trc)
   669 {
   670     XPCWrappedNativeScope::TraceWrappedNativesInAllScopes(trc, this);
   672     for (XPCRootSetElem *e = mVariantRoots; e ; e = e->GetNextRoot())
   673         static_cast<XPCTraceableVariant*>(e)->TraceJS(trc);
   675     for (XPCRootSetElem *e = mWrappedJSRoots; e ; e = e->GetNextRoot())
   676         static_cast<nsXPCWrappedJS*>(e)->TraceJS(trc);
   677 }
   679 // static
   680 void
   681 XPCJSRuntime::SuspectWrappedNative(XPCWrappedNative *wrapper,
   682                                    nsCycleCollectionNoteRootCallback &cb)
   683 {
   684     if (!wrapper->IsValid() || wrapper->IsWrapperExpired())
   685         return;
   687     MOZ_ASSERT(NS_IsMainThread(),
   688                "Suspecting wrapped natives from non-main thread");
   690     // Only record objects that might be part of a cycle as roots, unless
   691     // the callback wants all traces (a debug feature).
   692     JSObject* obj = wrapper->GetFlatJSObjectPreserveColor();
   693     if (xpc_IsGrayGCThing(obj) || cb.WantAllTraces())
   694         cb.NoteJSRoot(obj);
   695 }
   697 void
   698 XPCJSRuntime::TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback &cb)
   699 {
   700     XPCWrappedNativeScope::SuspectAllWrappers(this, cb);
   702     for (XPCRootSetElem *e = mVariantRoots; e ; e = e->GetNextRoot()) {
   703         XPCTraceableVariant* v = static_cast<XPCTraceableVariant*>(e);
   704         if (nsCCUncollectableMarker::InGeneration(cb,
   705                                                   v->CCGeneration())) {
   706            jsval val = v->GetJSValPreserveColor();
   707            if (val.isObject() && !xpc_IsGrayGCThing(&val.toObject()))
   708                continue;
   709         }
   710         cb.NoteXPCOMRoot(v);
   711     }
   713     for (XPCRootSetElem *e = mWrappedJSRoots; e ; e = e->GetNextRoot()) {
   714         cb.NoteXPCOMRoot(ToSupports(static_cast<nsXPCWrappedJS*>(e)));
   715     }
   716 }
   718 void
   719 XPCJSRuntime::UnmarkSkippableJSHolders()
   720 {
   721     CycleCollectedJSRuntime::UnmarkSkippableJSHolders();
   722 }
   724 void
   725 XPCJSRuntime::PrepareForForgetSkippable()
   726 {
   727     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   728     if (obs) {
   729         obs->NotifyObservers(nullptr, "cycle-collector-forget-skippable", nullptr);
   730     }
   731 }
   733 void
   734 XPCJSRuntime::BeginCycleCollectionCallback()
   735 {
   736     nsJSContext::BeginCycleCollectionCallback();
   738     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   739     if (obs) {
   740         obs->NotifyObservers(nullptr, "cycle-collector-begin", nullptr);
   741     }
   742 }
   744 void
   745 XPCJSRuntime::EndCycleCollectionCallback(CycleCollectorResults &aResults)
   746 {
   747     nsJSContext::EndCycleCollectionCallback(aResults);
   749     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   750     if (obs) {
   751         obs->NotifyObservers(nullptr, "cycle-collector-end", nullptr);
   752     }
   753 }
   755 void
   756 XPCJSRuntime::DispatchDeferredDeletion(bool aContinuation)
   757 {
   758     mAsyncSnowWhiteFreer->Dispatch(aContinuation);
   759 }
   761 void
   762 xpc_UnmarkSkippableJSHolders()
   763 {
   764     if (nsXPConnect::XPConnect()->GetRuntime()) {
   765         nsXPConnect::XPConnect()->GetRuntime()->UnmarkSkippableJSHolders();
   766     }
   767 }
   769 template<class T> static void
   770 DoDeferredRelease(nsTArray<T> &array)
   771 {
   772     while (1) {
   773         uint32_t count = array.Length();
   774         if (!count) {
   775             array.Compact();
   776             break;
   777         }
   778         T wrapper = array[count-1];
   779         array.RemoveElementAt(count-1);
   780         NS_RELEASE(wrapper);
   781     }
   782 }
   784 /* static */ void
   785 XPCJSRuntime::GCSliceCallback(JSRuntime *rt,
   786                               JS::GCProgress progress,
   787                               const JS::GCDescription &desc)
   788 {
   789     XPCJSRuntime *self = nsXPConnect::GetRuntimeInstance();
   790     if (!self)
   791         return;
   793 #ifdef MOZ_CRASHREPORTER
   794     CrashReporter::SetGarbageCollecting(progress == JS::GC_CYCLE_BEGIN ||
   795                                         progress == JS::GC_SLICE_BEGIN);
   796 #endif
   798     if (self->mPrevGCSliceCallback)
   799         (*self->mPrevGCSliceCallback)(rt, progress, desc);
   800 }
   802 void
   803 XPCJSRuntime::CustomGCCallback(JSGCStatus status)
   804 {
   805     // Record kLivingAdopters once per GC to approximate time sampling during
   806     // periods of activity.
   807     if (status == JSGC_BEGIN) {
   808         Telemetry::Accumulate(Telemetry::COMPARTMENT_LIVING_ADOPTERS,
   809                               kLivingAdopters);
   810     }
   812     nsTArray<xpcGCCallback> callbacks(extraGCCallbacks);
   813     for (uint32_t i = 0; i < callbacks.Length(); ++i)
   814         callbacks[i](status);
   815 }
   817 /* static */ void
   818 XPCJSRuntime::FinalizeCallback(JSFreeOp *fop, JSFinalizeStatus status, bool isCompartmentGC)
   819 {
   820     XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
   821     if (!self)
   822         return;
   824     switch (status) {
   825         case JSFINALIZE_GROUP_START:
   826         {
   827             MOZ_ASSERT(!self->mDoingFinalization, "bad state");
   829             MOZ_ASSERT(!self->mGCIsRunning, "bad state");
   830             self->mGCIsRunning = true;
   832             nsTArray<nsXPCWrappedJS*>* dyingWrappedJSArray =
   833                 &self->mWrappedJSToReleaseArray;
   835             // Add any wrappers whose JSObjects are to be finalized to
   836             // this array. Note that we do not want to be changing the
   837             // refcount of these wrappers.
   838             // We add them to the array now and Release the array members
   839             // later to avoid the posibility of doing any JS GCThing
   840             // allocations during the gc cycle.
   841             self->mWrappedJSMap->FindDyingJSObjects(dyingWrappedJSArray);
   843             // Find dying scopes.
   844             XPCWrappedNativeScope::StartFinalizationPhaseOfGC(fop, self);
   846             self->mDoingFinalization = true;
   847             break;
   848         }
   849         case JSFINALIZE_GROUP_END:
   850         {
   851             MOZ_ASSERT(self->mDoingFinalization, "bad state");
   852             self->mDoingFinalization = false;
   854             // Release all the members whose JSObjects are now known
   855             // to be dead.
   856             DoDeferredRelease(self->mWrappedJSToReleaseArray);
   858             // Sweep scopes needing cleanup
   859             XPCWrappedNativeScope::FinishedFinalizationPhaseOfGC();
   861             MOZ_ASSERT(self->mGCIsRunning, "bad state");
   862             self->mGCIsRunning = false;
   864             break;
   865         }
   866         case JSFINALIZE_COLLECTION_END:
   867         {
   868             MOZ_ASSERT(!self->mGCIsRunning, "bad state");
   869             self->mGCIsRunning = true;
   871             // We use this occasion to mark and sweep NativeInterfaces,
   872             // NativeSets, and the WrappedNativeJSClasses...
   874             // Do the marking...
   875             XPCWrappedNativeScope::MarkAllWrappedNativesAndProtos();
   877             self->mDetachedWrappedNativeProtoMap->
   878                 Enumerate(DetachedWrappedNativeProtoMarker, nullptr);
   880             DOM_MarkInterfaces();
   882             // Mark the sets used in the call contexts. There is a small
   883             // chance that a wrapper's set will change *while* a call is
   884             // happening which uses that wrapper's old interfface set. So,
   885             // we need to do this marking to avoid collecting those sets
   886             // that might no longer be otherwise reachable from the wrappers
   887             // or the wrapperprotos.
   889             // Skip this part if XPConnect is shutting down. We get into
   890             // bad locking problems with the thread iteration otherwise.
   891             if (!nsXPConnect::XPConnect()->IsShuttingDown()) {
   893                 // Mark those AutoMarkingPtr lists!
   894                 if (AutoMarkingPtr *roots = Get()->mAutoRoots)
   895                     roots->MarkAfterJSFinalizeAll();
   897                 XPCCallContext* ccxp = XPCJSRuntime::Get()->GetCallContext();
   898                 while (ccxp) {
   899                     // Deal with the strictness of callcontext that
   900                     // complains if you ask for a set when
   901                     // it is in a state where the set could not
   902                     // possibly be valid.
   903                     if (ccxp->CanGetSet()) {
   904                         XPCNativeSet* set = ccxp->GetSet();
   905                         if (set)
   906                             set->Mark();
   907                     }
   908                     if (ccxp->CanGetInterface()) {
   909                         XPCNativeInterface* iface = ccxp->GetInterface();
   910                         if (iface)
   911                             iface->Mark();
   912                     }
   913                     ccxp = ccxp->GetPrevCallContext();
   914                 }
   915             }
   917             // Do the sweeping. During a compartment GC, only
   918             // WrappedNativeProtos in collected compartments will be
   919             // marked. Therefore, some reachable NativeInterfaces will not be
   920             // marked, so it is not safe to sweep them. We still need to unmark
   921             // them, since the ones pointed to by WrappedNativeProtos in a
   922             // compartment being collected will be marked.
   923             //
   924             // Ideally, if NativeInterfaces from different compartments were
   925             // kept separate, we could sweep only the ones belonging to
   926             // compartments being collected. Currently, though, NativeInterfaces
   927             // are shared between compartments. This ought to be fixed.
   928             void *sweepArg = isCompartmentGC ? UNMARK_ONLY : UNMARK_AND_SWEEP;
   930             // We don't want to sweep the JSClasses at shutdown time.
   931             // At this point there may be JSObjects using them that have
   932             // been removed from the other maps.
   933             if (!nsXPConnect::XPConnect()->IsShuttingDown()) {
   934                 self->mNativeScriptableSharedMap->
   935                     Enumerate(JSClassSweeper, sweepArg);
   936             }
   938             if (!isCompartmentGC) {
   939                 self->mClassInfo2NativeSetMap->
   940                     Enumerate(NativeUnMarkedSetRemover, nullptr);
   941             }
   943             self->mNativeSetMap->
   944                 Enumerate(NativeSetSweeper, sweepArg);
   946             self->mIID2NativeInterfaceMap->
   947                 Enumerate(NativeInterfaceSweeper, sweepArg);
   949 #ifdef DEBUG
   950             XPCWrappedNativeScope::ASSERT_NoInterfaceSetsAreMarked();
   951 #endif
   953             // Now we are going to recycle any unused WrappedNativeTearoffs.
   954             // We do this by iterating all the live callcontexts
   955             // and marking the tearoffs in use. And then we
   956             // iterate over all the WrappedNative wrappers and sweep their
   957             // tearoffs.
   958             //
   959             // This allows us to perhaps minimize the growth of the
   960             // tearoffs. And also makes us not hold references to interfaces
   961             // on our wrapped natives that we are not actually using.
   962             //
   963             // XXX We may decide to not do this on *every* gc cycle.
   965             // Skip this part if XPConnect is shutting down. We get into
   966             // bad locking problems with the thread iteration otherwise.
   967             if (!nsXPConnect::XPConnect()->IsShuttingDown()) {
   968                 // Do the marking...
   970                 XPCCallContext* ccxp = XPCJSRuntime::Get()->GetCallContext();
   971                 while (ccxp) {
   972                     // Deal with the strictness of callcontext that
   973                     // complains if you ask for a tearoff when
   974                     // it is in a state where the tearoff could not
   975                     // possibly be valid.
   976                     if (ccxp->CanGetTearOff()) {
   977                         XPCWrappedNativeTearOff* to =
   978                             ccxp->GetTearOff();
   979                         if (to)
   980                             to->Mark();
   981                     }
   982                     ccxp = ccxp->GetPrevCallContext();
   983                 }
   985                 // Do the sweeping...
   986                 XPCWrappedNativeScope::SweepAllWrappedNativeTearOffs();
   987             }
   989             // Now we need to kill the 'Dying' XPCWrappedNativeProtos.
   990             // We transfered these native objects to this table when their
   991             // JSObject's were finalized. We did not destroy them immediately
   992             // at that point because the ordering of JS finalization is not
   993             // deterministic and we did not yet know if any wrappers that
   994             // might still be referencing the protos where still yet to be
   995             // finalized and destroyed. We *do* know that the protos'
   996             // JSObjects would not have been finalized if there were any
   997             // wrappers that referenced the proto but where not themselves
   998             // slated for finalization in this gc cycle. So... at this point
   999             // we know that any and all wrappers that might have been
  1000             // referencing the protos in the dying list are themselves dead.
  1001             // So, we can safely delete all the protos in the list.
  1003             self->mDyingWrappedNativeProtoMap->
  1004                 Enumerate(DyingProtoKiller, nullptr);
  1006             MOZ_ASSERT(self->mGCIsRunning, "bad state");
  1007             self->mGCIsRunning = false;
  1009             break;
  1014 static void WatchdogMain(void *arg);
  1015 class Watchdog;
  1016 class WatchdogManager;
  1017 class AutoLockWatchdog {
  1018     Watchdog* const mWatchdog;
  1019   public:
  1020     AutoLockWatchdog(Watchdog* aWatchdog);
  1021     ~AutoLockWatchdog();
  1022 };
  1024 class Watchdog
  1026   public:
  1027     Watchdog(WatchdogManager *aManager)
  1028       : mManager(aManager)
  1029       , mLock(nullptr)
  1030       , mWakeup(nullptr)
  1031       , mThread(nullptr)
  1032       , mHibernating(false)
  1033       , mInitialized(false)
  1034       , mShuttingDown(false)
  1035     {}
  1036     ~Watchdog() { MOZ_ASSERT(!Initialized()); }
  1038     WatchdogManager* Manager() { return mManager; }
  1039     bool Initialized() { return mInitialized; }
  1040     bool ShuttingDown() { return mShuttingDown; }
  1041     PRLock *GetLock() { return mLock; }
  1042     bool Hibernating() { return mHibernating; }
  1043     void WakeUp()
  1045         MOZ_ASSERT(Initialized());
  1046         MOZ_ASSERT(Hibernating());
  1047         mHibernating = false;
  1048         PR_NotifyCondVar(mWakeup);
  1051     //
  1052     // Invoked by the main thread only.
  1053     //
  1055     void Init()
  1057         MOZ_ASSERT(NS_IsMainThread());
  1058         mLock = PR_NewLock();
  1059         if (!mLock)
  1060             NS_RUNTIMEABORT("PR_NewLock failed.");
  1061         mWakeup = PR_NewCondVar(mLock);
  1062         if (!mWakeup)
  1063             NS_RUNTIMEABORT("PR_NewCondVar failed.");
  1066             AutoLockWatchdog lock(this);
  1068             mThread = PR_CreateThread(PR_USER_THREAD, WatchdogMain, this,
  1069                                       PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
  1070                                       PR_UNJOINABLE_THREAD, 0);
  1071             if (!mThread)
  1072                 NS_RUNTIMEABORT("PR_CreateThread failed!");
  1074             // WatchdogMain acquires the lock and then asserts mInitialized. So
  1075             // make sure to set mInitialized before releasing the lock here so
  1076             // that it's atomic with the creation of the thread.
  1077             mInitialized = true;
  1081     void Shutdown()
  1083         MOZ_ASSERT(NS_IsMainThread());
  1084         MOZ_ASSERT(Initialized());
  1085         {   // Scoped lock.
  1086             AutoLockWatchdog lock(this);
  1088             // Signal to the watchdog thread that it's time to shut down.
  1089             mShuttingDown = true;
  1091             // Wake up the watchdog, and wait for it to call us back.
  1092             PR_NotifyCondVar(mWakeup);
  1093             PR_WaitCondVar(mWakeup, PR_INTERVAL_NO_TIMEOUT);
  1094             MOZ_ASSERT(!mShuttingDown);
  1097         // Destroy state.
  1098         mThread = nullptr;
  1099         PR_DestroyCondVar(mWakeup);
  1100         mWakeup = nullptr;
  1101         PR_DestroyLock(mLock);
  1102         mLock = nullptr;
  1104         // All done.
  1105         mInitialized = false;
  1108     //
  1109     // Invoked by the watchdog thread only.
  1110     //
  1112     void Hibernate()
  1114         MOZ_ASSERT(!NS_IsMainThread());
  1115         mHibernating = true;
  1116         Sleep(PR_INTERVAL_NO_TIMEOUT);
  1118     void Sleep(PRIntervalTime timeout)
  1120         MOZ_ASSERT(!NS_IsMainThread());
  1121         MOZ_ALWAYS_TRUE(PR_WaitCondVar(mWakeup, timeout) == PR_SUCCESS);
  1123     void Finished()
  1125         MOZ_ASSERT(!NS_IsMainThread());
  1126         mShuttingDown = false;
  1127         PR_NotifyCondVar(mWakeup);
  1130   private:
  1131     WatchdogManager *mManager;
  1133     PRLock *mLock;
  1134     PRCondVar *mWakeup;
  1135     PRThread *mThread;
  1136     bool mHibernating;
  1137     bool mInitialized;
  1138     bool mShuttingDown;
  1139 };
  1141 #ifdef MOZ_NUWA_PROCESS
  1142 #include "ipc/Nuwa.h"
  1143 #endif
  1145 class WatchdogManager : public nsIObserver
  1147   public:
  1149     NS_DECL_ISUPPORTS
  1150     WatchdogManager(XPCJSRuntime *aRuntime) : mRuntime(aRuntime)
  1151                                             , mRuntimeState(RUNTIME_INACTIVE)
  1153         // All the timestamps start at zero except for runtime state change.
  1154         PodArrayZero(mTimestamps);
  1155         mTimestamps[TimestampRuntimeStateChange] = PR_Now();
  1157         // Enable the watchdog, if appropriate.
  1158         RefreshWatchdog();
  1160         // Register ourselves as an observer to get updates on the pref.
  1161         mozilla::Preferences::AddStrongObserver(this, "dom.use_watchdog");
  1163     virtual ~WatchdogManager()
  1165         // Shutting down the watchdog requires context-switching to the watchdog
  1166         // thread, which isn't great to do in a destructor. So we require
  1167         // consumers to shut it down manually before releasing it.
  1168         MOZ_ASSERT(!mWatchdog);
  1169         mozilla::Preferences::RemoveObserver(this, "dom.use_watchdog");
  1172     NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
  1173                        const char16_t* aData)
  1175         RefreshWatchdog();
  1176         return NS_OK;
  1179     // Runtime statistics. These live on the watchdog manager, are written
  1180     // from the main thread, and are read from the watchdog thread (holding
  1181     // the lock in each case).
  1182     void
  1183     RecordRuntimeActivity(bool active)
  1185         // The watchdog reads this state, so acquire the lock before writing it.
  1186         MOZ_ASSERT(NS_IsMainThread());
  1187         Maybe<AutoLockWatchdog> lock;
  1188         if (mWatchdog)
  1189             lock.construct(mWatchdog);
  1191         // Write state.
  1192         mTimestamps[TimestampRuntimeStateChange] = PR_Now();
  1193         mRuntimeState = active ? RUNTIME_ACTIVE : RUNTIME_INACTIVE;
  1195         // The watchdog may be hibernating, waiting for the runtime to go
  1196         // active. Wake it up if necessary.
  1197         if (active && mWatchdog && mWatchdog->Hibernating())
  1198             mWatchdog->WakeUp();
  1200     bool IsRuntimeActive() { return mRuntimeState == RUNTIME_ACTIVE; }
  1201     PRTime TimeSinceLastRuntimeStateChange()
  1203         return PR_Now() - GetTimestamp(TimestampRuntimeStateChange);
  1206     // Note - Because of the runtime activity timestamp, these are read and
  1207     // written from both threads.
  1208     void RecordTimestamp(WatchdogTimestampCategory aCategory)
  1210         // The watchdog thread always holds the lock when it runs.
  1211         Maybe<AutoLockWatchdog> maybeLock;
  1212         if (NS_IsMainThread() && mWatchdog)
  1213             maybeLock.construct(mWatchdog);
  1214         mTimestamps[aCategory] = PR_Now();
  1216     PRTime GetTimestamp(WatchdogTimestampCategory aCategory)
  1218         // The watchdog thread always holds the lock when it runs.
  1219         Maybe<AutoLockWatchdog> maybeLock;
  1220         if (NS_IsMainThread() && mWatchdog)
  1221             maybeLock.construct(mWatchdog);
  1222         return mTimestamps[aCategory];
  1225     XPCJSRuntime* Runtime() { return mRuntime; }
  1226     Watchdog* GetWatchdog() { return mWatchdog; }
  1228     void RefreshWatchdog()
  1230         bool wantWatchdog = Preferences::GetBool("dom.use_watchdog", true);
  1231         if (wantWatchdog == !!mWatchdog)
  1232             return;
  1233         if (wantWatchdog)
  1234             StartWatchdog();
  1235         else
  1236             StopWatchdog();
  1239     void StartWatchdog()
  1241         MOZ_ASSERT(!mWatchdog);
  1242         mWatchdog = new Watchdog(this);
  1243         mWatchdog->Init();
  1246     void StopWatchdog()
  1248         MOZ_ASSERT(mWatchdog);
  1249         mWatchdog->Shutdown();
  1250         mWatchdog = nullptr;
  1253   private:
  1254     XPCJSRuntime *mRuntime;
  1255     nsAutoPtr<Watchdog> mWatchdog;
  1257     enum { RUNTIME_ACTIVE, RUNTIME_INACTIVE } mRuntimeState;
  1258     PRTime mTimestamps[TimestampCount];
  1259 };
  1261 NS_IMPL_ISUPPORTS(WatchdogManager, nsIObserver)
  1263 AutoLockWatchdog::AutoLockWatchdog(Watchdog *aWatchdog) : mWatchdog(aWatchdog)
  1265     PR_Lock(mWatchdog->GetLock());
  1268 AutoLockWatchdog::~AutoLockWatchdog()
  1270     PR_Unlock(mWatchdog->GetLock());
  1273 static void
  1274 WatchdogMain(void *arg)
  1276     PR_SetCurrentThreadName("JS Watchdog");
  1278 #ifdef MOZ_NUWA_PROCESS
  1279     if (IsNuwaProcess()) {
  1280         NS_ASSERTION(NuwaMarkCurrentThread != nullptr,
  1281                      "NuwaMarkCurrentThread is undefined!");
  1282         NuwaMarkCurrentThread(nullptr, nullptr);
  1283         NuwaFreezeCurrentThread();
  1285 #endif
  1287     Watchdog* self = static_cast<Watchdog*>(arg);
  1288     WatchdogManager* manager = self->Manager();
  1290     // Lock lasts until we return
  1291     AutoLockWatchdog lock(self);
  1293     MOZ_ASSERT(self->Initialized());
  1294     MOZ_ASSERT(!self->ShuttingDown());
  1295     while (!self->ShuttingDown()) {
  1296         // Sleep only 1 second if recently (or currently) active; otherwise, hibernate
  1297         if (manager->IsRuntimeActive() ||
  1298             manager->TimeSinceLastRuntimeStateChange() <= PRTime(2*PR_USEC_PER_SEC))
  1300             self->Sleep(PR_TicksPerSecond());
  1301         } else {
  1302             manager->RecordTimestamp(TimestampWatchdogHibernateStart);
  1303             self->Hibernate();
  1304             manager->RecordTimestamp(TimestampWatchdogHibernateStop);
  1307         // Rise and shine.
  1308         manager->RecordTimestamp(TimestampWatchdogWakeup);
  1310         // Don't request an interrupt callback if activity started less than one second ago.
  1311         // The callback is only used for detecting long running scripts, and triggering the
  1312         // callback from off the main thread can be expensive.
  1313         if (manager->IsRuntimeActive() &&
  1314             manager->TimeSinceLastRuntimeStateChange() >= PRTime(PR_USEC_PER_SEC))
  1316             bool debuggerAttached = false;
  1317             nsCOMPtr<nsIDebug2> dbg = do_GetService("@mozilla.org/xpcom/debug;1");
  1318             if (dbg)
  1319                 dbg->GetIsDebuggerAttached(&debuggerAttached);
  1320             if (!debuggerAttached)
  1321                 JS_RequestInterruptCallback(manager->Runtime()->Runtime());
  1325     // Tell the manager that we've shut down.
  1326     self->Finished();
  1329 PRTime
  1330 XPCJSRuntime::GetWatchdogTimestamp(WatchdogTimestampCategory aCategory)
  1332     return mWatchdogManager->GetTimestamp(aCategory);
  1335 void
  1336 xpc::SimulateActivityCallback(bool aActive)
  1338     XPCJSRuntime::ActivityCallback(XPCJSRuntime::Get(), aActive);
  1341 // static
  1342 JSContext*
  1343 XPCJSRuntime::DefaultJSContextCallback(JSRuntime *rt)
  1345     MOZ_ASSERT(rt == Get()->Runtime());
  1346     return Get()->GetJSContextStack()->GetSafeJSContext();
  1349 // static
  1350 void
  1351 XPCJSRuntime::ActivityCallback(void *arg, bool active)
  1353     XPCJSRuntime* self = static_cast<XPCJSRuntime*>(arg);
  1354     self->mWatchdogManager->RecordRuntimeActivity(active);
  1357 // static
  1358 //
  1359 // JS-CTypes creates and caches a JSContext that it uses when executing JS
  1360 // callbacks. When we're notified that ctypes is about to call into some JS,
  1361 // push the cx to maintain the integrity of the context stack.
  1362 void
  1363 XPCJSRuntime::CTypesActivityCallback(JSContext *cx, js::CTypesActivityType type)
  1365   if (type == js::CTYPES_CALLBACK_BEGIN) {
  1366     if (!xpc::PushJSContextNoScriptContext(cx))
  1367       MOZ_CRASH();
  1368   } else if (type == js::CTYPES_CALLBACK_END) {
  1369     xpc::PopJSContextNoScriptContext();
  1373 // static
  1374 bool
  1375 XPCJSRuntime::InterruptCallback(JSContext *cx)
  1377     XPCJSRuntime *self = XPCJSRuntime::Get();
  1379     // If this is the first time the interrupt callback has fired since we last
  1380     // returned to the event loop, mark the checkpoint.
  1381     if (self->mSlowScriptCheckpoint.IsNull()) {
  1382         self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
  1383         return true;
  1386     // This is at least the second interrupt callback we've received since
  1387     // returning to the event loop. See how long it's been, and what the limit
  1388     // is.
  1389     TimeDuration duration = TimeStamp::NowLoRes() - self->mSlowScriptCheckpoint;
  1390     bool chrome =
  1391       nsContentUtils::IsSystemPrincipal(nsContentUtils::GetSubjectPrincipal());
  1392     const char *prefName = chrome ? "dom.max_chrome_script_run_time"
  1393                                   : "dom.max_script_run_time";
  1394     int32_t limit = Preferences::GetInt(prefName, chrome ? 20 : 10);
  1396     // If there's no limit, or we're within the limit, let it go.
  1397     if (limit == 0 || duration.ToSeconds() < limit)
  1398         return true;
  1400     //
  1401     // This has gone on long enough! Time to take action. ;-)
  1402     //
  1404     // Get the DOM window associated with the running script. If the script is
  1405     // running in a non-DOM scope, we have to just let it keep running.
  1406     RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
  1407     nsRefPtr<nsGlobalWindow> win = WindowOrNull(global);
  1408     if (!win && IsSandbox(global)) {
  1409         // If this is a sandbox associated with a DOMWindow via a
  1410         // sandboxPrototype, use that DOMWindow. This supports GreaseMonkey
  1411         // and JetPack content scripts.
  1412         JS::Rooted<JSObject*> proto(cx);
  1413         if (!JS_GetPrototype(cx, global, &proto))
  1414             return false;
  1415         if (proto && IsSandboxPrototypeProxy(proto) &&
  1416             (proto = js::CheckedUnwrap(proto, /* stopAtOuter = */ false)))
  1418             win = WindowGlobalOrNull(proto);
  1421     if (!win)
  1422         return true;
  1424     // Show the prompt to the user, and kill if requested.
  1425     nsGlobalWindow::SlowScriptResponse response = win->ShowSlowScriptDialog();
  1426     if (response == nsGlobalWindow::KillSlowScript)
  1427         return false;
  1429     // The user chose to continue the script. Reset the timer, and disable this
  1430     // machinery with a pref of the user opted out of future slow-script dialogs.
  1431     self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
  1432     if (response == nsGlobalWindow::AlwaysContinueSlowScript)
  1433         Preferences::SetInt(prefName, 0);
  1435     return true;
  1438 /* static */ void
  1439 XPCJSRuntime::OutOfMemoryCallback(JSContext *cx)
  1441     if (!Preferences::GetBool("memory.dump_reports_on_oom")) {
  1442         return;
  1445     nsCOMPtr<nsIMemoryInfoDumper> dumper =
  1446         do_GetService("@mozilla.org/memory-info-dumper;1");
  1447     if (!dumper) {
  1448         return;
  1451     // If this fails, it fails silently.
  1452     dumper->DumpMemoryInfoToTempDir(NS_LITERAL_STRING("due-to-JS-OOM"),
  1453                                     /* minimizeMemoryUsage = */ false);
  1456 size_t
  1457 XPCJSRuntime::SizeOfIncludingThis(MallocSizeOf mallocSizeOf)
  1459     size_t n = 0;
  1460     n += mallocSizeOf(this);
  1461     n += mWrappedJSMap->SizeOfIncludingThis(mallocSizeOf);
  1462     n += mIID2NativeInterfaceMap->SizeOfIncludingThis(mallocSizeOf);
  1463     n += mClassInfo2NativeSetMap->ShallowSizeOfIncludingThis(mallocSizeOf);
  1464     n += mNativeSetMap->SizeOfIncludingThis(mallocSizeOf);
  1466     n += CycleCollectedJSRuntime::SizeOfExcludingThis(mallocSizeOf);
  1468     // There are other XPCJSRuntime members that could be measured; the above
  1469     // ones have been seen by DMD to be worth measuring.  More stuff may be
  1470     // added later.
  1472     return n;
  1475 nsString*
  1476 XPCJSRuntime::NewShortLivedString()
  1478     for (uint32_t i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) {
  1479         if (mScratchStrings[i].empty()) {
  1480             mScratchStrings[i].construct();
  1481             return mScratchStrings[i].addr();
  1485     // All our internal string wrappers are used, allocate a new string.
  1486     return new nsString();
  1489 void
  1490 XPCJSRuntime::DeleteShortLivedString(nsString *string)
  1492     if (string == &EmptyString() || string == &NullString())
  1493         return;
  1495     for (uint32_t i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) {
  1496         if (!mScratchStrings[i].empty() &&
  1497             mScratchStrings[i].addr() == string) {
  1498             // One of our internal strings is no longer in use, mark
  1499             // it as such and free its data.
  1500             mScratchStrings[i].destroy();
  1501             return;
  1505     // We're done with a string that's not one of our internal
  1506     // strings, delete it.
  1507     delete string;
  1510 /***************************************************************************/
  1512 static PLDHashOperator
  1513 DetachedWrappedNativeProtoShutdownMarker(PLDHashTable *table, PLDHashEntryHdr *hdr,
  1514                                          uint32_t number, void *arg)
  1516     XPCWrappedNativeProto* proto =
  1517         (XPCWrappedNativeProto*)((PLDHashEntryStub*)hdr)->key;
  1519     proto->SystemIsBeingShutDown();
  1520     return PL_DHASH_NEXT;
  1523 void XPCJSRuntime::DestroyJSContextStack()
  1525     delete mJSContextStack;
  1526     mJSContextStack = nullptr;
  1529 void XPCJSRuntime::SystemIsBeingShutDown()
  1531     DOM_ClearInterfaces();
  1533     if (mDetachedWrappedNativeProtoMap)
  1534         mDetachedWrappedNativeProtoMap->
  1535             Enumerate(DetachedWrappedNativeProtoShutdownMarker, nullptr);
  1538 #define JS_OPTIONS_DOT_STR "javascript.options."
  1540 static void
  1541 ReloadPrefsCallback(const char *pref, void *data)
  1543     XPCJSRuntime *runtime = reinterpret_cast<XPCJSRuntime *>(data);
  1544     JSRuntime *rt = runtime->Runtime();
  1546     bool safeMode = false;
  1547     nsCOMPtr<nsIXULRuntime> xr = do_GetService("@mozilla.org/xre/runtime;1");
  1548     if (xr) {
  1549         xr->GetInSafeMode(&safeMode);
  1552     bool useBaseline = Preferences::GetBool(JS_OPTIONS_DOT_STR "baselinejit") && !safeMode;
  1553     bool useIon = Preferences::GetBool(JS_OPTIONS_DOT_STR "ion") && !safeMode;
  1554     bool useAsmJS = Preferences::GetBool(JS_OPTIONS_DOT_STR "asmjs") && !safeMode;
  1556     bool parallelParsing = Preferences::GetBool(JS_OPTIONS_DOT_STR "parallel_parsing");
  1557     bool parallelIonCompilation = Preferences::GetBool(JS_OPTIONS_DOT_STR
  1558                                                        "ion.parallel_compilation");
  1559     bool useBaselineEager = Preferences::GetBool(JS_OPTIONS_DOT_STR
  1560                                                  "baselinejit.unsafe_eager_compilation");
  1561     bool useIonEager = Preferences::GetBool(JS_OPTIONS_DOT_STR "ion.unsafe_eager_compilation");
  1563     sDiscardSystemSource = Preferences::GetBool(JS_OPTIONS_DOT_STR "discardSystemSource");
  1565     JS::RuntimeOptionsRef(rt).setBaseline(useBaseline)
  1566                              .setIon(useIon)
  1567                            .  setAsmJS(useAsmJS);
  1569     JS_SetParallelParsingEnabled(rt, parallelParsing);
  1570     JS_SetParallelIonCompilationEnabled(rt, parallelIonCompilation);
  1571     JS_SetGlobalJitCompilerOption(rt, JSJITCOMPILER_BASELINE_USECOUNT_TRIGGER,
  1572                                   useBaselineEager ? 0 : -1);
  1573     JS_SetGlobalJitCompilerOption(rt, JSJITCOMPILER_ION_USECOUNT_TRIGGER,
  1574                                   useIonEager ? 0 : -1);
  1577 XPCJSRuntime::~XPCJSRuntime()
  1579     // This destructor runs before ~CycleCollectedJSRuntime, which does the
  1580     // actual JS_DestroyRuntime() call. But destroying the runtime triggers
  1581     // one final GC, which can call back into the runtime with various
  1582     // callback if we aren't careful. Null out the relevant callbacks.
  1583     js::SetActivityCallback(Runtime(), nullptr, nullptr);
  1584     JS_SetFinalizeCallback(Runtime(), nullptr);
  1586     // Clear any pending exception.  It might be an XPCWrappedJS, and if we try
  1587     // to destroy it later we will crash.
  1588     SetPendingException(nullptr);
  1590     JS::SetGCSliceCallback(Runtime(), mPrevGCSliceCallback);
  1592     xpc_DelocalizeRuntime(Runtime());
  1594     if (mWatchdogManager->GetWatchdog())
  1595         mWatchdogManager->StopWatchdog();
  1597     if (mCallContext)
  1598         mCallContext->SystemIsBeingShutDown();
  1600     auto rtPrivate = static_cast<PerThreadAtomCache*>(JS_GetRuntimePrivate(Runtime()));
  1601     delete rtPrivate;
  1602     JS_SetRuntimePrivate(Runtime(), nullptr);
  1604     // clean up and destroy maps...
  1605     if (mWrappedJSMap) {
  1606         mWrappedJSMap->ShutdownMarker();
  1607         delete mWrappedJSMap;
  1608         mWrappedJSMap = nullptr;
  1611     if (mWrappedJSClassMap) {
  1612         delete mWrappedJSClassMap;
  1613         mWrappedJSClassMap = nullptr;
  1616     if (mIID2NativeInterfaceMap) {
  1617         delete mIID2NativeInterfaceMap;
  1618         mIID2NativeInterfaceMap = nullptr;
  1621     if (mClassInfo2NativeSetMap) {
  1622         delete mClassInfo2NativeSetMap;
  1623         mClassInfo2NativeSetMap = nullptr;
  1626     if (mNativeSetMap) {
  1627         delete mNativeSetMap;
  1628         mNativeSetMap = nullptr;
  1631     if (mThisTranslatorMap) {
  1632         delete mThisTranslatorMap;
  1633         mThisTranslatorMap = nullptr;
  1636     if (mNativeScriptableSharedMap) {
  1637         delete mNativeScriptableSharedMap;
  1638         mNativeScriptableSharedMap = nullptr;
  1641     if (mDyingWrappedNativeProtoMap) {
  1642         delete mDyingWrappedNativeProtoMap;
  1643         mDyingWrappedNativeProtoMap = nullptr;
  1646     if (mDetachedWrappedNativeProtoMap) {
  1647         delete mDetachedWrappedNativeProtoMap;
  1648         mDetachedWrappedNativeProtoMap = nullptr;
  1651 #ifdef MOZ_ENABLE_PROFILER_SPS
  1652     // Tell the profiler that the runtime is gone
  1653     if (PseudoStack *stack = mozilla_get_pseudo_stack())
  1654         stack->sampleRuntime(nullptr);
  1655 #endif
  1657 #ifdef DEBUG
  1658     for (uint32_t i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) {
  1659         MOZ_ASSERT(mScratchStrings[i].empty(), "Short lived string still in use");
  1661 #endif
  1663     Preferences::UnregisterCallback(ReloadPrefsCallback, JS_OPTIONS_DOT_STR, this);
  1666 static void
  1667 GetCompartmentName(JSCompartment *c, nsCString &name, bool replaceSlashes)
  1669     if (js::IsAtomsCompartment(c)) {
  1670         name.AssignLiteral("atoms");
  1671     } else if (JSPrincipals *principals = JS_GetCompartmentPrincipals(c)) {
  1672         nsJSPrincipals::get(principals)->GetScriptLocation(name);
  1674         // If the compartment's location (name) differs from the principal's
  1675         // script location, append the compartment's location to allow
  1676         // differentiation of multiple compartments owned by the same principal
  1677         // (e.g. components owned by the system or null principal).
  1678         CompartmentPrivate *compartmentPrivate = GetCompartmentPrivate(c);
  1679         if (compartmentPrivate) {
  1680             const nsACString& location = compartmentPrivate->GetLocation();
  1681             if (!location.IsEmpty() && !location.Equals(name)) {
  1682                 name.AppendLiteral(", ");
  1683                 name.Append(location);
  1687         // A hack: replace forward slashes with '\\' so they aren't
  1688         // treated as path separators.  Users of the reporters
  1689         // (such as about:memory) have to undo this change.
  1690         if (replaceSlashes)
  1691             name.ReplaceChar('/', '\\');
  1692     } else {
  1693         name.AssignLiteral("null-principal");
  1697 static int64_t
  1698 JSMainRuntimeGCHeapDistinguishedAmount()
  1700     JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
  1701     return int64_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) *
  1702            js::gc::ChunkSize;
  1705 static int64_t
  1706 JSMainRuntimeTemporaryPeakDistinguishedAmount()
  1708     JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
  1709     return JS::PeakSizeOfTemporary(rt);
  1712 static int64_t
  1713 JSMainRuntimeCompartmentsSystemDistinguishedAmount()
  1715     JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
  1716     return JS::SystemCompartmentCount(rt);
  1719 static int64_t
  1720 JSMainRuntimeCompartmentsUserDistinguishedAmount()
  1722     JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
  1723     return JS::UserCompartmentCount(rt);
  1726 class JSMainRuntimeTemporaryPeakReporter MOZ_FINAL : public nsIMemoryReporter
  1728   public:
  1729     NS_DECL_ISUPPORTS
  1731     NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
  1732                               nsISupports* aData)
  1734         return MOZ_COLLECT_REPORT("js-main-runtime-temporary-peak",
  1735             KIND_OTHER, UNITS_BYTES,
  1736             JSMainRuntimeTemporaryPeakDistinguishedAmount(),
  1737             "Peak transient data size in the main JSRuntime (the current size "
  1738             "of which is reported as "
  1739             "'explicit/js-non-window/runtime/temporary').");
  1741 };
  1743 NS_IMPL_ISUPPORTS(JSMainRuntimeTemporaryPeakReporter, nsIMemoryReporter)
  1745 // The REPORT* macros do an unconditional report.  The ZCREPORT* macros are for
  1746 // compartments and zones; they aggregate any entries smaller than
  1747 // SUNDRIES_THRESHOLD into the "sundries/gc-heap" and "sundries/malloc-heap"
  1748 // entries for the compartment.
  1750 #define SUNDRIES_THRESHOLD js::MemoryReportingSundriesThreshold()
  1752 #define REPORT(_path, _kind, _units, _amount, _desc)                          \
  1753     do {                                                                      \
  1754         nsresult rv;                                                          \
  1755         rv = cb->Callback(EmptyCString(), _path,                              \
  1756                           nsIMemoryReporter::_kind,                           \
  1757                           nsIMemoryReporter::_units,                          \
  1758                           _amount,                                            \
  1759                           NS_LITERAL_CSTRING(_desc),                          \
  1760                           closure);                                           \
  1761         NS_ENSURE_SUCCESS(rv, rv);                                            \
  1762     } while (0)
  1764 #define REPORT_BYTES(_path, _kind, _amount, _desc)                            \
  1765     REPORT(_path, _kind, UNITS_BYTES, _amount, _desc);
  1767 #define REPORT_GC_BYTES(_path, _amount, _desc)                                \
  1768     do {                                                                      \
  1769         size_t amount = _amount;  /* evaluate _amount only once */            \
  1770         nsresult rv;                                                          \
  1771         rv = cb->Callback(EmptyCString(), _path,                              \
  1772                           nsIMemoryReporter::KIND_NONHEAP,                    \
  1773                           nsIMemoryReporter::UNITS_BYTES, amount,             \
  1774                           NS_LITERAL_CSTRING(_desc), closure);                \
  1775         NS_ENSURE_SUCCESS(rv, rv);                                            \
  1776         gcTotal += amount;                                                    \
  1777     } while (0)
  1779 // Report compartment/zone non-GC (KIND_HEAP) bytes.
  1780 #define ZCREPORT_BYTES(_path, _amount, _desc)                                 \
  1781     do {                                                                      \
  1782         /* Assign _descLiteral plus "" into a char* to prove that it's */     \
  1783         /* actually a literal. */                                             \
  1784         size_t amount = _amount;  /* evaluate _amount only once */            \
  1785         if (amount >= SUNDRIES_THRESHOLD) {                                   \
  1786             nsresult rv;                                                      \
  1787             rv = cb->Callback(EmptyCString(), _path,                          \
  1788                               nsIMemoryReporter::KIND_HEAP,                   \
  1789                               nsIMemoryReporter::UNITS_BYTES, amount,         \
  1790                               NS_LITERAL_CSTRING(_desc), closure);            \
  1791             NS_ENSURE_SUCCESS(rv, rv);                                        \
  1792         } else {                                                              \
  1793             sundriesMallocHeap += amount;                                     \
  1794         }                                                                     \
  1795     } while (0)
  1797 // Report compartment/zone GC bytes.
  1798 #define ZCREPORT_GC_BYTES(_path, _amount, _desc)                              \
  1799     do {                                                                      \
  1800         size_t amount = _amount;  /* evaluate _amount only once */            \
  1801         if (amount >= SUNDRIES_THRESHOLD) {                                   \
  1802             nsresult rv;                                                      \
  1803             rv = cb->Callback(EmptyCString(), _path,                          \
  1804                               nsIMemoryReporter::KIND_NONHEAP,                \
  1805                               nsIMemoryReporter::UNITS_BYTES, amount,         \
  1806                               NS_LITERAL_CSTRING(_desc), closure);            \
  1807             NS_ENSURE_SUCCESS(rv, rv);                                        \
  1808             gcTotal += amount;                                                \
  1809         } else {                                                              \
  1810             sundriesGCHeap += amount;                                         \
  1811         }                                                                     \
  1812     } while (0)
  1814 // Report runtime bytes.
  1815 #define RREPORT_BYTES(_path, _kind, _amount, _desc)                           \
  1816     do {                                                                      \
  1817         size_t amount = _amount;  /* evaluate _amount only once */            \
  1818         nsresult rv;                                                          \
  1819         rv = cb->Callback(EmptyCString(), _path,                              \
  1820                           nsIMemoryReporter::_kind,                           \
  1821                           nsIMemoryReporter::UNITS_BYTES, amount,             \
  1822                           NS_LITERAL_CSTRING(_desc), closure);                \
  1823         NS_ENSURE_SUCCESS(rv, rv);                                            \
  1824         rtTotal += amount;                                                    \
  1825     } while (0)
  1827 MOZ_DEFINE_MALLOC_SIZE_OF(JSMallocSizeOf)
  1829 namespace xpc {
  1831 static nsresult
  1832 ReportZoneStats(const JS::ZoneStats &zStats,
  1833                 const xpc::ZoneStatsExtras &extras,
  1834                 nsIMemoryReporterCallback *cb,
  1835                 nsISupports *closure, size_t *gcTotalOut = nullptr)
  1837     const nsAutoCString& pathPrefix = extras.pathPrefix;
  1838     size_t gcTotal = 0, sundriesGCHeap = 0, sundriesMallocHeap = 0;
  1840     MOZ_ASSERT(!gcTotalOut == zStats.isTotals);
  1842     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap-arena-admin"),
  1843         zStats.gcHeapArenaAdmin,
  1844         "Bookkeeping information and alignment padding within GC arenas.");
  1846     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("unused-gc-things"),
  1847         zStats.unusedGCThings,
  1848         "Empty GC thing cells within non-empty arenas.");
  1850     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts/gc-heap"),
  1851         zStats.lazyScriptsGCHeap,
  1852         "Scripts that haven't executed yet.");
  1854     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts/malloc-heap"),
  1855         zStats.lazyScriptsMallocHeap,
  1856         "Lazy script tables containing free variables or inner functions.");
  1858     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("jit-codes-gc-heap"),
  1859         zStats.jitCodesGCHeap,
  1860         "References to executable code pools used by the JITs.");
  1862     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-objects/gc-heap"),
  1863         zStats.typeObjectsGCHeap,
  1864         "Type inference information about objects.");
  1866     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-objects/malloc-heap"),
  1867         zStats.typeObjectsMallocHeap,
  1868         "Type object addenda.");
  1870     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-pool"),
  1871         zStats.typePool,
  1872         "Type sets and related data.");
  1874     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("baseline/optimized-stubs"),
  1875         zStats.baselineStubsOptimized,
  1876         "The Baseline JIT's optimized IC stubs (excluding code).");
  1878     size_t stringsNotableAboutMemoryGCHeap = 0;
  1879     size_t stringsNotableAboutMemoryMallocHeap = 0;
  1881     #define MAYBE_INLINE \
  1882         "The characters may be inline or on the malloc heap."
  1883     #define MAYBE_OVERALLOCATED \
  1884         "Sometimes over-allocated to simplify string concatenation."
  1886     for (size_t i = 0; i < zStats.notableStrings.length(); i++) {
  1887         const JS::NotableStringInfo& info = zStats.notableStrings[i];
  1889         MOZ_ASSERT(!zStats.isTotals);
  1891         nsDependentCString notableString(info.buffer);
  1893         // Viewing about:memory generates many notable strings which contain
  1894         // "string(length=".  If we report these as notable, then we'll create
  1895         // even more notable strings the next time we open about:memory (unless
  1896         // there's a GC in the meantime), and so on ad infinitum.
  1897         //
  1898         // To avoid cluttering up about:memory like this, we stick notable
  1899         // strings which contain "string(length=" into their own bucket.
  1900 #       define STRING_LENGTH "string(length="
  1901         if (FindInReadable(NS_LITERAL_CSTRING(STRING_LENGTH), notableString)) {
  1902             stringsNotableAboutMemoryGCHeap += info.gcHeap;
  1903             stringsNotableAboutMemoryMallocHeap += info.mallocHeap;
  1904             continue;
  1907         // Escape / to \ before we put notableString into the memory reporter
  1908         // path, because we don't want any forward slashes in the string to
  1909         // count as path separators.
  1910         nsCString escapedString(notableString);
  1911         escapedString.ReplaceSubstring("/", "\\");
  1913         bool truncated = notableString.Length() < info.length;
  1915         nsCString path = pathPrefix +
  1916             nsPrintfCString("strings/" STRING_LENGTH "%d, copies=%d, \"%s\"%s)/",
  1917                             info.length, info.numCopies, escapedString.get(),
  1918                             truncated ? " (truncated)" : "");
  1920         if (info.gcHeap > 0) {
  1921             REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("gc-heap"),
  1922                 info.gcHeap,
  1923                 "Strings. " MAYBE_INLINE);
  1926         if (info.mallocHeap > 0) {
  1927             REPORT_BYTES(path + NS_LITERAL_CSTRING("malloc-heap"),
  1928                 KIND_HEAP, info.mallocHeap,
  1929                 "Non-inline string characters. " MAYBE_OVERALLOCATED);
  1933     nsCString nonNotablePath = pathPrefix;
  1934     nonNotablePath += zStats.isTotals
  1935                     ? NS_LITERAL_CSTRING("strings/")
  1936                     : NS_LITERAL_CSTRING("strings/string(<non-notable strings>)/");
  1938     if (zStats.stringInfo.gcHeap > 0) {
  1939         REPORT_GC_BYTES(nonNotablePath + NS_LITERAL_CSTRING("gc-heap"),
  1940             zStats.stringInfo.gcHeap,
  1941             "Strings. " MAYBE_INLINE);
  1944     if (zStats.stringInfo.mallocHeap > 0) {
  1945         REPORT_BYTES(nonNotablePath + NS_LITERAL_CSTRING("malloc-heap"),
  1946             KIND_HEAP, zStats.stringInfo.mallocHeap,
  1947             "Non-inline string characters. " MAYBE_OVERALLOCATED);
  1950     if (stringsNotableAboutMemoryGCHeap > 0) {
  1951         MOZ_ASSERT(!zStats.isTotals);
  1952         REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/string(<about-memory>)/gc-heap"),
  1953             stringsNotableAboutMemoryGCHeap,
  1954             "Strings that contain the characters '" STRING_LENGTH "', which "
  1955             "are probably from about:memory itself." MAYBE_INLINE
  1956             " We filter them out rather than display them, because displaying "
  1957             "them would create even more such strings every time about:memory "
  1958             "is refreshed.");
  1961     if (stringsNotableAboutMemoryMallocHeap > 0) {
  1962         MOZ_ASSERT(!zStats.isTotals);
  1963         REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/string(<about-memory>)/malloc-heap"),
  1964             KIND_HEAP, stringsNotableAboutMemoryMallocHeap,
  1965             "Non-inline string characters of strings that contain the "
  1966             "characters '" STRING_LENGTH "', which are probably from "
  1967             "about:memory itself. " MAYBE_OVERALLOCATED
  1968             " We filter them out rather than display them, because displaying "
  1969             "them would create even more such strings every time about:memory "
  1970             "is refreshed.");
  1973     if (sundriesGCHeap > 0) {
  1974         // We deliberately don't use ZCREPORT_GC_BYTES here.
  1975         REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("sundries/gc-heap"),
  1976             sundriesGCHeap,
  1977             "The sum of all 'gc-heap' measurements that are too small to be "
  1978             "worth showing individually.");
  1981     if (sundriesMallocHeap > 0) {
  1982         // We deliberately don't use ZCREPORT_BYTES here.
  1983         REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("sundries/malloc-heap"),
  1984             KIND_HEAP, sundriesMallocHeap,
  1985             "The sum of all 'malloc-heap' measurements that are too small to "
  1986             "be worth showing individually.");
  1989     if (gcTotalOut)
  1990         *gcTotalOut += gcTotal;
  1992     return NS_OK;
  1994 #   undef STRING_LENGTH
  1997 static nsresult
  1998 ReportCompartmentStats(const JS::CompartmentStats &cStats,
  1999                        const xpc::CompartmentStatsExtras &extras,
  2000                        amIAddonManager *addonManager,
  2001                        nsIMemoryReporterCallback *cb,
  2002                        nsISupports *closure, size_t *gcTotalOut = nullptr)
  2004     static const nsDependentCString addonPrefix("explicit/add-ons/");
  2006     size_t gcTotal = 0, sundriesGCHeap = 0, sundriesMallocHeap = 0;
  2007     nsAutoCString cJSPathPrefix = extras.jsPathPrefix;
  2008     nsAutoCString cDOMPathPrefix = extras.domPathPrefix;
  2010     // Only attempt to prefix if we got a location and the path wasn't already
  2011     // prefixed.
  2012     if (extras.location && addonManager &&
  2013         cJSPathPrefix.Find(addonPrefix, false, 0, 0) != 0) {
  2014         nsAutoCString addonId;
  2015         bool ok;
  2016         if (NS_SUCCEEDED(addonManager->MapURIToAddonID(extras.location,
  2017                                                         addonId, &ok))
  2018             && ok) {
  2019             // Insert the add-on id as "add-ons/@id@/" after "explicit/" to
  2020             // aggregate add-on compartments.
  2021             static const size_t explicitLength = strlen("explicit/");
  2022             addonId.Insert(NS_LITERAL_CSTRING("add-ons/"), 0);
  2023             addonId += "/";
  2024             cJSPathPrefix.Insert(addonId, explicitLength);
  2025             cDOMPathPrefix.Insert(addonId, explicitLength);
  2029     ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/ordinary"),
  2030         cStats.objectsGCHeapOrdinary,
  2031         "Ordinary objects, i.e. not otherwise distinguished by memory "
  2032         "reporters.");
  2034     ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/function"),
  2035         cStats.objectsGCHeapFunction,
  2036         "Function objects.");
  2038     ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/dense-array"),
  2039         cStats.objectsGCHeapDenseArray,
  2040         "Dense array objects.");
  2042     ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/slow-array"),
  2043         cStats.objectsGCHeapSlowArray,
  2044         "Slow array objects.");
  2046     ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/cross-compartment-wrapper"),
  2047         cStats.objectsGCHeapCrossCompartmentWrapper,
  2048         "Cross-compartment wrapper objects.");
  2050     // Note that we use cDOMPathPrefix here.  This is because we measure orphan
  2051     // DOM nodes in the JS reporter, but we want to report them in a "dom"
  2052     // sub-tree rather than a "js" sub-tree.
  2053     ZCREPORT_BYTES(cDOMPathPrefix + NS_LITERAL_CSTRING("orphan-nodes"),
  2054         cStats.objectsPrivate,
  2055         "Orphan DOM nodes, i.e. those that are only reachable from JavaScript "
  2056         "objects.");
  2058     ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/tree/global-parented"),
  2059         cStats.shapesGCHeapTreeGlobalParented,
  2060         "Shapes that (a) are in a property tree, and (b) represent an object "
  2061         "whose parent is the global object.");
  2063     ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/tree/non-global-parented"),
  2064         cStats.shapesGCHeapTreeNonGlobalParented,
  2065         "Shapes that (a) are in a property tree, and (b) represent an object "
  2066         "whose parent is not the global object.");
  2068     ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/dict"),
  2069         cStats.shapesGCHeapDict,
  2070         "Shapes that are in dictionary mode.");
  2072     ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/base"),
  2073         cStats.shapesGCHeapBase,
  2074         "Base shapes, which collate data common to many shapes.");
  2076     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/tree-tables"),
  2077         cStats.shapesMallocHeapTreeTables,
  2078         "Property tables belonging to shapes that are in a property tree.");
  2080     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/dict-tables"),
  2081         cStats.shapesMallocHeapDictTables,
  2082         "Property tables that belong to shapes that are in dictionary mode.");
  2084     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/tree-shape-kids"),
  2085         cStats.shapesMallocHeapTreeShapeKids,
  2086         "Kid hashes that belong to shapes that are in a property tree.");
  2088     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/compartment-tables"),
  2089         cStats.shapesMallocHeapCompartmentTables,
  2090         "Compartment-wide tables storing shape information used during object "
  2091         "construction.");
  2093     ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("scripts/gc-heap"),
  2094         cStats.scriptsGCHeap,
  2095         "JSScript instances. There is one per user-defined function in a "
  2096         "script, and one for the top-level code in a script.");
  2098     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("scripts/malloc-heap/data"),
  2099         cStats.scriptsMallocHeapData,
  2100         "Various variable-length tables in JSScripts.");
  2102     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("baseline/data"),
  2103         cStats.baselineData,
  2104         "The Baseline JIT's compilation data (BaselineScripts).");
  2106     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("baseline/fallback-stubs"),
  2107         cStats.baselineStubsFallback,
  2108         "The Baseline JIT's fallback IC stubs (excluding code).");
  2110     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("ion-data"),
  2111         cStats.ionData,
  2112         "The IonMonkey JIT's compilation data (IonScripts).");
  2114     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/type-scripts"),
  2115         cStats.typeInferenceTypeScripts,
  2116         "Type sets associated with scripts.");
  2118     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/allocation-site-tables"),
  2119         cStats.typeInferenceAllocationSiteTables,
  2120         "Tables of type objects associated with allocation sites.");
  2122     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/array-type-tables"),
  2123         cStats.typeInferenceArrayTypeTables,
  2124         "Tables of type objects associated with array literals.");
  2126     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/object-type-tables"),
  2127         cStats.typeInferenceObjectTypeTables,
  2128         "Tables of type objects associated with object literals.");
  2130     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("compartment-object"),
  2131         cStats.compartmentObject,
  2132         "The JSCompartment object itself.");
  2134     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("cross-compartment-wrapper-table"),
  2135         cStats.crossCompartmentWrappersTable,
  2136         "The cross-compartment wrapper table.");
  2138     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("regexp-compartment"),
  2139         cStats.regexpCompartment,
  2140         "The regexp compartment.");
  2142     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("debuggees-set"),
  2143         cStats.debuggeesSet,
  2144         "The debuggees set.");
  2146     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/slots"),
  2147         cStats.objectsExtra.mallocHeapSlots,
  2148         "Non-fixed object slot arrays, which represent object properties.");
  2150     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/elements/non-asm.js"),
  2151         cStats.objectsExtra.mallocHeapElementsNonAsmJS,
  2152         "Non-asm.js indexed elements.");
  2154     // asm.js arrays are heap-allocated on some platforms and
  2155     // non-heap-allocated on others.  We never put them under sundries,
  2156     // because (a) in practice they're almost always larger than the sundries
  2157     // threshold, and (b) we'd need a third category of sundries ("non-heap"),
  2158     // which would be a pain.
  2159     size_t mallocHeapElementsAsmJS = cStats.objectsExtra.mallocHeapElementsAsmJS;
  2160     size_t nonHeapElementsAsmJS    = cStats.objectsExtra.nonHeapElementsAsmJS;
  2161     MOZ_ASSERT(mallocHeapElementsAsmJS == 0 || nonHeapElementsAsmJS == 0);
  2162     if (mallocHeapElementsAsmJS > 0) {
  2163         REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/elements/asm.js"),
  2164             KIND_HEAP, mallocHeapElementsAsmJS,
  2165             "asm.js array buffer elements on the malloc heap.");
  2167     if (nonHeapElementsAsmJS > 0) {
  2168         REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/non-heap/elements/asm.js"),
  2169             KIND_NONHEAP, nonHeapElementsAsmJS,
  2170             "asm.js array buffer elements outside both the malloc heap and "
  2171             "the GC heap.");
  2174     if (cStats.objectsExtra.nonHeapElementsMapped > 0) {
  2175         REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/non-heap/elements/mapped"),
  2176             KIND_NONHEAP, cStats.objectsExtra.nonHeapElementsMapped,
  2177             "Memory-mapped array buffer elements.");
  2180     REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/non-heap/code/asm.js"),
  2181         KIND_NONHEAP, cStats.objectsExtra.nonHeapCodeAsmJS,
  2182         "AOT-compiled asm.js code.");
  2184     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/asm.js-module-data"),
  2185         cStats.objectsExtra.mallocHeapAsmJSModuleData,
  2186         "asm.js module data.");
  2188     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/arguments-data"),
  2189         cStats.objectsExtra.mallocHeapArgumentsData,
  2190         "Data belonging to Arguments objects.");
  2192     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/regexp-statics"),
  2193         cStats.objectsExtra.mallocHeapRegExpStatics,
  2194         "Data belonging to the RegExpStatics object.");
  2196     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/property-iterator-data"),
  2197         cStats.objectsExtra.mallocHeapPropertyIteratorData,
  2198         "Data belonging to property iterator objects.");
  2200     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/ctypes-data"),
  2201         cStats.objectsExtra.mallocHeapCtypesData,
  2202         "Data belonging to ctypes objects.");
  2204     if (sundriesGCHeap > 0) {
  2205         // We deliberately don't use ZCREPORT_GC_BYTES here.
  2206         REPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("sundries/gc-heap"),
  2207             sundriesGCHeap,
  2208             "The sum of all 'gc-heap' measurements that are too small to be "
  2209             "worth showing individually.");
  2212     if (sundriesMallocHeap > 0) {
  2213         // We deliberately don't use ZCREPORT_BYTES here.
  2214         REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("sundries/malloc-heap"),
  2215             KIND_HEAP, sundriesMallocHeap,
  2216             "The sum of all 'malloc-heap' measurements that are too small to "
  2217             "be worth showing individually.");
  2220     if (gcTotalOut)
  2221         *gcTotalOut += gcTotal;
  2223     return NS_OK;
  2226 static nsresult
  2227 ReportScriptSourceStats(const ScriptSourceInfo &scriptSourceInfo,
  2228                         const nsACString &path,
  2229                         nsIHandleReportCallback *cb, nsISupports *closure,
  2230                         size_t &rtTotal)
  2232     if (scriptSourceInfo.compressed > 0) {
  2233         RREPORT_BYTES(path + NS_LITERAL_CSTRING("compressed"),
  2234             KIND_HEAP, scriptSourceInfo.compressed,
  2235             "Compressed JavaScript source code.");
  2238     if (scriptSourceInfo.uncompressed > 0) {
  2239         RREPORT_BYTES(path + NS_LITERAL_CSTRING("uncompressed"),
  2240             KIND_HEAP, scriptSourceInfo.uncompressed,
  2241             "Uncompressed JavaScript source code.");
  2244     if (scriptSourceInfo.misc > 0) {
  2245         RREPORT_BYTES(path + NS_LITERAL_CSTRING("misc"),
  2246             KIND_HEAP, scriptSourceInfo.misc,
  2247             "Miscellaneous data relating to JavaScript source code.");
  2250     return NS_OK;
  2253 static nsresult
  2254 ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats,
  2255                                  const nsACString &rtPath,
  2256                                  amIAddonManager* addonManager,
  2257                                  nsIMemoryReporterCallback *cb,
  2258                                  nsISupports *closure, size_t *rtTotalOut)
  2260     nsresult rv;
  2262     size_t gcTotal = 0;
  2264     for (size_t i = 0; i < rtStats.zoneStatsVector.length(); i++) {
  2265         const JS::ZoneStats &zStats = rtStats.zoneStatsVector[i];
  2266         const xpc::ZoneStatsExtras *extras =
  2267           static_cast<const xpc::ZoneStatsExtras*>(zStats.extra);
  2268         rv = ReportZoneStats(zStats, *extras, cb, closure, &gcTotal);
  2269         NS_ENSURE_SUCCESS(rv, rv);
  2272     for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++) {
  2273         JS::CompartmentStats cStats = rtStats.compartmentStatsVector[i];
  2274         const xpc::CompartmentStatsExtras *extras =
  2275             static_cast<const xpc::CompartmentStatsExtras*>(cStats.extra);
  2276         rv = ReportCompartmentStats(cStats, *extras, addonManager, cb, closure,
  2277                                     &gcTotal);
  2278         NS_ENSURE_SUCCESS(rv, rv);
  2281     // Report the rtStats.runtime numbers under "runtime/", and compute their
  2282     // total for later.
  2284     size_t rtTotal = 0;
  2286     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/runtime-object"),
  2287         KIND_HEAP, rtStats.runtime.object,
  2288         "The JSRuntime object.");
  2290     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/atoms-table"),
  2291         KIND_HEAP, rtStats.runtime.atomsTable,
  2292         "The atoms table.");
  2294     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/contexts"),
  2295         KIND_HEAP, rtStats.runtime.contexts,
  2296         "JSContext objects and structures that belong to them.");
  2298     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/dtoa"),
  2299         KIND_HEAP, rtStats.runtime.dtoa,
  2300         "The DtoaState object, which is used for converting strings to "
  2301         "numbers and vice versa.");
  2303     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/temporary"),
  2304         KIND_HEAP, rtStats.runtime.temporary,
  2305         "Transient data (mostly parse nodes) held by the JSRuntime during "
  2306         "compilation.");
  2308     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/regexp-data"),
  2309         KIND_NONHEAP, rtStats.runtime.regexpData,
  2310         "Regexp JIT data.");
  2312     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/interpreter-stack"),
  2313         KIND_HEAP, rtStats.runtime.interpreterStack,
  2314         "JS interpreter frames.");
  2316     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/math-cache"),
  2317         KIND_HEAP, rtStats.runtime.mathCache,
  2318         "The math cache.");
  2320     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/source-data-cache"),
  2321         KIND_HEAP, rtStats.runtime.sourceDataCache,
  2322         "The source data cache, which holds decompressed script source code.");
  2324     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/script-data"),
  2325         KIND_HEAP, rtStats.runtime.scriptData,
  2326         "The table holding script data shared in the runtime.");
  2328     nsCString nonNotablePath =
  2329         rtPath + nsPrintfCString("runtime/script-sources/source(scripts=%d, <non-notable files>)/",
  2330                                  rtStats.runtime.scriptSourceInfo.numScripts);
  2332     rv = ReportScriptSourceStats(rtStats.runtime.scriptSourceInfo,
  2333                                  nonNotablePath, cb, closure, rtTotal);
  2334     NS_ENSURE_SUCCESS(rv, rv);
  2336     for (size_t i = 0; i < rtStats.runtime.notableScriptSources.length(); i++) {
  2337         const JS::NotableScriptSourceInfo& scriptSourceInfo =
  2338             rtStats.runtime.notableScriptSources[i];
  2340         // Escape / to \ before we put the filename into the memory reporter
  2341         // path, because we don't want any forward slashes in the string to
  2342         // count as path separators. Consumers of memory reporters (e.g.
  2343         // about:memory) will convert them back to / after doing path
  2344         // splitting.
  2345         nsDependentCString filename(scriptSourceInfo.filename_);
  2346         nsCString escapedFilename(filename);
  2347         escapedFilename.ReplaceSubstring("/", "\\");
  2349         nsCString notablePath = rtPath +
  2350             nsPrintfCString("runtime/script-sources/source(scripts=%d, %s)/",
  2351                             scriptSourceInfo.numScripts, escapedFilename.get());
  2353         rv = ReportScriptSourceStats(scriptSourceInfo, notablePath,
  2354                                      cb, closure, rtTotal);
  2355         NS_ENSURE_SUCCESS(rv, rv);
  2358     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/ion"),
  2359         KIND_NONHEAP, rtStats.runtime.code.ion,
  2360         "Code generated by the IonMonkey JIT.");
  2362     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/baseline"),
  2363         KIND_NONHEAP, rtStats.runtime.code.baseline,
  2364         "Code generated by the Baseline JIT.");
  2366     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/regexp"),
  2367         KIND_NONHEAP, rtStats.runtime.code.regexp,
  2368         "Code generated by the regexp JIT.");
  2370     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/other"),
  2371         KIND_NONHEAP, rtStats.runtime.code.other,
  2372         "Code generated by the JITs for wrappers and trampolines.");
  2374     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/unused"),
  2375         KIND_NONHEAP, rtStats.runtime.code.unused,
  2376         "Memory allocated by one of the JITs to hold code, but which is "
  2377         "currently unused.");
  2379     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/marker"),
  2380         KIND_HEAP, rtStats.runtime.gc.marker,
  2381         "The GC mark stack and gray roots.");
  2383     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/nursery-committed"),
  2384         KIND_NONHEAP, rtStats.runtime.gc.nurseryCommitted,
  2385         "Memory being used by the GC's nursery.");
  2387     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/nursery-huge-slots"),
  2388         KIND_NONHEAP, rtStats.runtime.gc.nurseryHugeSlots,
  2389         "Out-of-line slots and elements belonging to objects in the "
  2390         "nursery.");
  2392     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/vals"),
  2393         KIND_HEAP, rtStats.runtime.gc.storeBufferVals,
  2394         "Values in the store buffer.");
  2396     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/cells"),
  2397         KIND_HEAP, rtStats.runtime.gc.storeBufferCells,
  2398         "Cells in the store buffer.");
  2400     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/slots"),
  2401         KIND_HEAP, rtStats.runtime.gc.storeBufferSlots,
  2402         "Slots in the store buffer.");
  2404     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/whole-cells"),
  2405         KIND_HEAP, rtStats.runtime.gc.storeBufferWholeCells,
  2406         "Whole cells in the store buffer.");
  2408     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/reloc-vals"),
  2409         KIND_HEAP, rtStats.runtime.gc.storeBufferRelocVals,
  2410         "Relocatable values in the store buffer.");
  2412     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/reloc-cells"),
  2413         KIND_HEAP, rtStats.runtime.gc.storeBufferRelocCells,
  2414         "Relocatable cells in the store buffer.");
  2416     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/generics"),
  2417         KIND_HEAP, rtStats.runtime.gc.storeBufferGenerics,
  2418         "Generic things in the store buffer.");
  2420     if (rtTotalOut)
  2421         *rtTotalOut = rtTotal;
  2423     // Report GC numbers that don't belong to a compartment.
  2425     // We don't want to report decommitted memory in "explicit", so we just
  2426     // change the leading "explicit/" to "decommitted/".
  2427     nsCString rtPath2(rtPath);
  2428     rtPath2.Replace(0, strlen("explicit"), NS_LITERAL_CSTRING("decommitted"));
  2429     REPORT_GC_BYTES(rtPath2 + NS_LITERAL_CSTRING("gc-heap/decommitted-arenas"),
  2430         rtStats.gcHeapDecommittedArenas,
  2431         "GC arenas in non-empty chunks that is decommitted, i.e. it takes up "
  2432         "address space but no physical memory or swap space.");
  2434     REPORT_BYTES(rtPath2 + NS_LITERAL_CSTRING("runtime/gc/nursery-decommitted"),
  2435         KIND_NONHEAP, rtStats.runtime.gc.nurseryDecommitted,
  2436         "Memory allocated to the GC's nursery this is decommitted, i.e. it takes up "
  2437         "address space but no physical memory or swap space.");
  2439     REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/unused-chunks"),
  2440         rtStats.gcHeapUnusedChunks,
  2441         "Empty GC chunks which will soon be released unless claimed for new "
  2442         "allocations.");
  2444     REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/unused-arenas"),
  2445         rtStats.gcHeapUnusedArenas,
  2446         "Empty GC arenas within non-empty chunks.");
  2448     REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/chunk-admin"),
  2449         rtStats.gcHeapChunkAdmin,
  2450         "Bookkeeping information within GC chunks.");
  2452     // gcTotal is the sum of everything we've reported for the GC heap.  It
  2453     // should equal rtStats.gcHeapChunkTotal.
  2454     MOZ_ASSERT(gcTotal == rtStats.gcHeapChunkTotal);
  2456     return NS_OK;
  2459 nsresult
  2460 ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats,
  2461                                  const nsACString &rtPath,
  2462                                  nsIMemoryReporterCallback *cb,
  2463                                  nsISupports *closure, size_t *rtTotalOut)
  2465     nsCOMPtr<amIAddonManager> am;
  2466     if (XRE_GetProcessType() == GeckoProcessType_Default) {
  2467         // Only try to access the service from the main process.
  2468         am = do_GetService("@mozilla.org/addons/integration;1");
  2470     return ReportJSRuntimeExplicitTreeStats(rtStats, rtPath, am.get(), cb,
  2471                                             closure, rtTotalOut);
  2475 } // namespace xpc
  2477 class JSMainRuntimeCompartmentsReporter MOZ_FINAL : public nsIMemoryReporter
  2479   public:
  2480     NS_DECL_ISUPPORTS
  2482     typedef js::Vector<nsCString, 0, js::SystemAllocPolicy> Paths;
  2484     static void CompartmentCallback(JSRuntime *rt, void* data, JSCompartment *c) {
  2485         // silently ignore OOM errors
  2486         Paths *paths = static_cast<Paths *>(data);
  2487         nsCString path;
  2488         GetCompartmentName(c, path, true);
  2489         path.Insert(js::IsSystemCompartment(c)
  2490                     ? NS_LITERAL_CSTRING("js-main-runtime-compartments/system/")
  2491                     : NS_LITERAL_CSTRING("js-main-runtime-compartments/user/"),
  2492                     0);
  2493         paths->append(path);
  2496     NS_IMETHOD CollectReports(nsIMemoryReporterCallback *cb,
  2497                               nsISupports *closure)
  2499         // First we collect the compartment paths.  Then we report them.  Doing
  2500         // the two steps interleaved is a bad idea, because calling |cb|
  2501         // from within CompartmentCallback() leads to all manner of assertions.
  2503         // Collect.
  2505         Paths paths;
  2506         JS_IterateCompartments(nsXPConnect::GetRuntimeInstance()->Runtime(),
  2507                                &paths, CompartmentCallback);
  2509         // Report.
  2510         for (size_t i = 0; i < paths.length(); i++)
  2511             // These ones don't need a description, hence the "".
  2512             REPORT(nsCString(paths[i]), KIND_OTHER, UNITS_COUNT, 1,
  2513                 "A live compartment in the main JSRuntime.");
  2515         return NS_OK;
  2517 };
  2519 NS_IMPL_ISUPPORTS(JSMainRuntimeCompartmentsReporter, nsIMemoryReporter)
  2521 MOZ_DEFINE_MALLOC_SIZE_OF(OrphanMallocSizeOf)
  2523 namespace xpc {
  2525 static size_t
  2526 SizeOfTreeIncludingThis(nsINode *tree)
  2528     size_t n = tree->SizeOfIncludingThis(OrphanMallocSizeOf);
  2529     for (nsIContent* child = tree->GetFirstChild(); child; child = child->GetNextNode(tree))
  2530         n += child->SizeOfIncludingThis(OrphanMallocSizeOf);
  2532     return n;
  2535 class OrphanReporter : public JS::ObjectPrivateVisitor
  2537   public:
  2538     OrphanReporter(GetISupportsFun aGetISupports)
  2539       : JS::ObjectPrivateVisitor(aGetISupports)
  2543     virtual size_t sizeOfIncludingThis(nsISupports *aSupports) MOZ_OVERRIDE {
  2544         size_t n = 0;
  2545         nsCOMPtr<nsINode> node = do_QueryInterface(aSupports);
  2546         // https://bugzilla.mozilla.org/show_bug.cgi?id=773533#c11 explains
  2547         // that we have to skip XBL elements because they violate certain
  2548         // assumptions.  Yuk.
  2549         if (node && !node->IsInDoc() &&
  2550             !(node->IsElement() && node->AsElement()->IsInNamespace(kNameSpaceID_XBL)))
  2552             // This is an orphan node.  If we haven't already handled the
  2553             // sub-tree that this node belongs to, measure the sub-tree's size
  2554             // and then record its root so we don't measure it again.
  2555             nsCOMPtr<nsINode> orphanTree = node->SubtreeRoot();
  2556             if (!mAlreadyMeasuredOrphanTrees.Contains(orphanTree)) {
  2557                 n += SizeOfTreeIncludingThis(orphanTree);
  2558                 mAlreadyMeasuredOrphanTrees.PutEntry(orphanTree);
  2561         return n;
  2564   private:
  2565     nsTHashtable <nsISupportsHashKey> mAlreadyMeasuredOrphanTrees;
  2566 };
  2568 #ifdef DEBUG
  2569 static bool
  2570 StartsWithExplicit(nsACString& s)
  2572     const char* e = "explicit/";
  2573     return Substring(s, 0, strlen(e)).Equals(e);
  2575 #endif
  2577 class XPCJSRuntimeStats : public JS::RuntimeStats
  2579     WindowPaths *mWindowPaths;
  2580     WindowPaths *mTopWindowPaths;
  2581     bool mGetLocations;
  2583   public:
  2584     XPCJSRuntimeStats(WindowPaths *windowPaths, WindowPaths *topWindowPaths,
  2585                       bool getLocations)
  2586       : JS::RuntimeStats(JSMallocSizeOf),
  2587         mWindowPaths(windowPaths),
  2588         mTopWindowPaths(topWindowPaths),
  2589         mGetLocations(getLocations)
  2590     {}
  2592     ~XPCJSRuntimeStats() {
  2593         for (size_t i = 0; i != compartmentStatsVector.length(); ++i)
  2594             delete static_cast<xpc::CompartmentStatsExtras*>(compartmentStatsVector[i].extra);
  2597         for (size_t i = 0; i != zoneStatsVector.length(); ++i)
  2598             delete static_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra);
  2601     virtual void initExtraZoneStats(JS::Zone *zone, JS::ZoneStats *zStats) MOZ_OVERRIDE {
  2602         // Get the compartment's global.
  2603         nsXPConnect *xpc = nsXPConnect::XPConnect();
  2604         AutoSafeJSContext cx;
  2605         JSCompartment *comp = js::GetAnyCompartmentInZone(zone);
  2606         xpc::ZoneStatsExtras *extras = new xpc::ZoneStatsExtras;
  2607         extras->pathPrefix.AssignLiteral("explicit/js-non-window/zones/");
  2608         RootedObject global(cx, JS_GetGlobalForCompartmentOrNull(cx, comp));
  2609         if (global) {
  2610             // Need to enter the compartment, otherwise GetNativeOfWrapper()
  2611             // might crash.
  2612             JSAutoCompartment ac(cx, global);
  2613             nsISupports *native = xpc->GetNativeOfWrapper(cx, global);
  2614             if (nsCOMPtr<nsPIDOMWindow> piwindow = do_QueryInterface(native)) {
  2615                 // The global is a |window| object.  Use the path prefix that
  2616                 // we should have already created for it.
  2617                 if (mTopWindowPaths->Get(piwindow->WindowID(),
  2618                                          &extras->pathPrefix))
  2619                     extras->pathPrefix.AppendLiteral("/js-");
  2623         extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void *)zone);
  2625         MOZ_ASSERT(StartsWithExplicit(extras->pathPrefix));
  2627         zStats->extra = extras;
  2630     virtual void initExtraCompartmentStats(JSCompartment *c,
  2631                                            JS::CompartmentStats *cstats) MOZ_OVERRIDE
  2633         xpc::CompartmentStatsExtras *extras = new xpc::CompartmentStatsExtras;
  2634         nsCString cName;
  2635         GetCompartmentName(c, cName, true);
  2636         if (mGetLocations) {
  2637             CompartmentPrivate *cp = GetCompartmentPrivate(c);
  2638             if (cp)
  2639               cp->GetLocationURI(CompartmentPrivate::LocationHintAddon,
  2640                                  getter_AddRefs(extras->location));
  2641             // Note: cannot use amIAddonManager implementation at this point,
  2642             // as it is a JS service and the JS heap is currently not idle.
  2643             // Otherwise, we could have computed the add-on id at this point.
  2646         // Get the compartment's global.
  2647         nsXPConnect *xpc = nsXPConnect::XPConnect();
  2648         AutoSafeJSContext cx;
  2649         bool needZone = true;
  2650         RootedObject global(cx, JS_GetGlobalForCompartmentOrNull(cx, c));
  2651         if (global) {
  2652             // Need to enter the compartment, otherwise GetNativeOfWrapper()
  2653             // might crash.
  2654             JSAutoCompartment ac(cx, global);
  2655             nsISupports *native = xpc->GetNativeOfWrapper(cx, global);
  2656             if (nsCOMPtr<nsPIDOMWindow> piwindow = do_QueryInterface(native)) {
  2657                 // The global is a |window| object.  Use the path prefix that
  2658                 // we should have already created for it.
  2659                 if (mWindowPaths->Get(piwindow->WindowID(),
  2660                                       &extras->jsPathPrefix)) {
  2661                     extras->domPathPrefix.Assign(extras->jsPathPrefix);
  2662                     extras->domPathPrefix.AppendLiteral("/dom/");
  2663                     extras->jsPathPrefix.AppendLiteral("/js-");
  2664                     needZone = false;
  2665                 } else {
  2666                     extras->jsPathPrefix.AssignLiteral("explicit/js-non-window/zones/");
  2667                     extras->domPathPrefix.AssignLiteral("explicit/dom/unknown-window-global?!/");
  2669             } else {
  2670                 extras->jsPathPrefix.AssignLiteral("explicit/js-non-window/zones/");
  2671                 extras->domPathPrefix.AssignLiteral("explicit/dom/non-window-global?!/");
  2673         } else {
  2674             extras->jsPathPrefix.AssignLiteral("explicit/js-non-window/zones/");
  2675             extras->domPathPrefix.AssignLiteral("explicit/dom/no-global?!/");
  2678         if (needZone)
  2679             extras->jsPathPrefix += nsPrintfCString("zone(0x%p)/", (void *)js::GetCompartmentZone(c));
  2681         extras->jsPathPrefix += NS_LITERAL_CSTRING("compartment(") + cName + NS_LITERAL_CSTRING(")/");
  2683         // extras->jsPathPrefix is used for almost all the compartment-specific
  2684         // reports. At this point it has the form
  2685         // "<something>compartment(<cname>)/".
  2686         //
  2687         // extras->domPathPrefix is used for DOM orphan nodes, which are
  2688         // counted by the JS reporter but reported as part of the DOM
  2689         // measurements. At this point it has the form "<something>/dom/" if
  2690         // this compartment belongs to an nsGlobalWindow, and
  2691         // "explicit/dom/<something>?!/" otherwise (in which case it shouldn't
  2692         // be used, because non-nsGlobalWindow compartments shouldn't have
  2693         // orphan DOM nodes).
  2695         MOZ_ASSERT(StartsWithExplicit(extras->jsPathPrefix));
  2696         MOZ_ASSERT(StartsWithExplicit(extras->domPathPrefix));
  2698         cstats->extra = extras;
  2700 };
  2702 nsresult
  2703 JSReporter::CollectReports(WindowPaths *windowPaths,
  2704                            WindowPaths *topWindowPaths,
  2705                            nsIMemoryReporterCallback *cb,
  2706                            nsISupports *closure)
  2708     XPCJSRuntime *xpcrt = nsXPConnect::GetRuntimeInstance();
  2710     // In the first step we get all the stats and stash them in a local
  2711     // data structure.  In the second step we pass all the stashed stats to
  2712     // the callback.  Separating these steps is important because the
  2713     // callback may be a JS function, and executing JS while getting these
  2714     // stats seems like a bad idea.
  2716     nsCOMPtr<amIAddonManager> addonManager;
  2717     if (XRE_GetProcessType() == GeckoProcessType_Default) {
  2718         // Only try to access the service from the main process.
  2719         addonManager = do_GetService("@mozilla.org/addons/integration;1");
  2721     bool getLocations = !!addonManager;
  2722     XPCJSRuntimeStats rtStats(windowPaths, topWindowPaths, getLocations);
  2723     OrphanReporter orphanReporter(XPCConvert::GetISupportsFromJSObject);
  2724     if (!JS::CollectRuntimeStats(xpcrt->Runtime(), &rtStats, &orphanReporter))
  2725         return NS_ERROR_FAILURE;
  2727     size_t xpconnect = xpcrt->SizeOfIncludingThis(JSMallocSizeOf);
  2729     XPCWrappedNativeScope::ScopeSizeInfo sizeInfo(JSMallocSizeOf);
  2730     XPCWrappedNativeScope::AddSizeOfAllScopesIncludingThis(&sizeInfo);
  2732     mozJSComponentLoader* loader = mozJSComponentLoader::Get();
  2733     size_t jsComponentLoaderSize = loader ? loader->SizeOfIncludingThis(JSMallocSizeOf) : 0;
  2735     // This is the second step (see above).  First we report stuff in the
  2736     // "explicit" tree, then we report other stuff.
  2738     nsresult rv;
  2739     size_t rtTotal = 0;
  2740     rv = xpc::ReportJSRuntimeExplicitTreeStats(rtStats,
  2741                                                NS_LITERAL_CSTRING("explicit/js-non-window/"),
  2742                                                addonManager, cb, closure,
  2743                                                &rtTotal);
  2744     NS_ENSURE_SUCCESS(rv, rv);
  2746     // Report the sums of the compartment numbers.
  2747     xpc::CompartmentStatsExtras cExtrasTotal;
  2748     cExtrasTotal.jsPathPrefix.AssignLiteral("js-main-runtime/compartments/");
  2749     cExtrasTotal.domPathPrefix.AssignLiteral("window-objects/dom/");
  2750     rv = ReportCompartmentStats(rtStats.cTotals, cExtrasTotal, addonManager,
  2751                                 cb, closure);
  2752     NS_ENSURE_SUCCESS(rv, rv);
  2754     xpc::ZoneStatsExtras zExtrasTotal;
  2755     zExtrasTotal.pathPrefix.AssignLiteral("js-main-runtime/zones/");
  2756     rv = ReportZoneStats(rtStats.zTotals, zExtrasTotal, cb, closure);
  2757     NS_ENSURE_SUCCESS(rv, rv);
  2759     // Report the sum of the runtime/ numbers.
  2760     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/runtime"),
  2761         KIND_OTHER, rtTotal,
  2762         "The sum of all measurements under 'explicit/js-non-window/runtime/'.");
  2764     // Report the numbers for memory outside of compartments.
  2766     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/unused-chunks"),
  2767         KIND_OTHER, rtStats.gcHeapUnusedChunks,
  2768         "The same as 'explicit/js-non-window/gc-heap/unused-chunks'.");
  2770     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/unused-arenas"),
  2771         KIND_OTHER, rtStats.gcHeapUnusedArenas,
  2772         "The same as 'explicit/js-non-window/gc-heap/unused-arenas'.");
  2774     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/chunk-admin"),
  2775         KIND_OTHER, rtStats.gcHeapChunkAdmin,
  2776         "The same as 'explicit/js-non-window/gc-heap/chunk-admin'.");
  2778     // Report a breakdown of the committed GC space.
  2780     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/chunks"),
  2781         KIND_OTHER, rtStats.gcHeapUnusedChunks,
  2782         "The same as 'explicit/js-non-window/gc-heap/unused-chunks'.");
  2784     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/arenas"),
  2785         KIND_OTHER, rtStats.gcHeapUnusedArenas,
  2786         "The same as 'explicit/js-non-window/gc-heap/unused-arenas'.");
  2788     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things"),
  2789         KIND_OTHER, rtStats.zTotals.unusedGCThings,
  2790         "The same as 'js-main-runtime/zones/unused-gc-things'.");
  2792     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/chunk-admin"),
  2793         KIND_OTHER, rtStats.gcHeapChunkAdmin,
  2794         "The same as 'explicit/js-non-window/gc-heap/chunk-admin'.");
  2796     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/arena-admin"),
  2797         KIND_OTHER, rtStats.zTotals.gcHeapArenaAdmin,
  2798         "The same as 'js-main-runtime/zones/gc-heap-arena-admin'.");
  2800     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things"),
  2801         KIND_OTHER, rtStats.gcHeapGCThings,
  2802         "GC things: objects, strings, scripts, etc.");
  2804     // Report xpconnect.
  2806     REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/runtime"),
  2807         KIND_HEAP, xpconnect,
  2808         "The XPConnect runtime.");
  2810     REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/scopes"),
  2811         KIND_HEAP, sizeInfo.mScopeAndMapSize,
  2812         "XPConnect scopes.");
  2814     REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/proto-iface-cache"),
  2815         KIND_HEAP, sizeInfo.mProtoAndIfaceCacheSize,
  2816         "Prototype and interface binding caches.");
  2818     REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/js-component-loader"),
  2819         KIND_HEAP, jsComponentLoaderSize,
  2820         "XPConnect's JS component loader.");
  2822     return NS_OK;
  2825 static nsresult
  2826 JSSizeOfTab(JSObject *objArg, size_t *jsObjectsSize, size_t *jsStringsSize,
  2827             size_t *jsPrivateSize, size_t *jsOtherSize)
  2829     JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
  2830     JS::RootedObject obj(rt, objArg);
  2832     TabSizes sizes;
  2833     OrphanReporter orphanReporter(XPCConvert::GetISupportsFromJSObject);
  2834     NS_ENSURE_TRUE(JS::AddSizeOfTab(rt, obj, moz_malloc_size_of,
  2835                                     &orphanReporter, &sizes),
  2836                    NS_ERROR_OUT_OF_MEMORY);
  2838     *jsObjectsSize = sizes.objects;
  2839     *jsStringsSize = sizes.strings;
  2840     *jsPrivateSize = sizes.private_;
  2841     *jsOtherSize   = sizes.other;
  2842     return NS_OK;
  2845 } // namespace xpc
  2847 #ifdef MOZ_CRASHREPORTER
  2848 static bool
  2849 DiagnosticMemoryCallback(void *ptr, size_t size)
  2851     return CrashReporter::RegisterAppMemory(ptr, size) == NS_OK;
  2853 #endif
  2855 static void
  2856 AccumulateTelemetryCallback(int id, uint32_t sample)
  2858     switch (id) {
  2859       case JS_TELEMETRY_GC_REASON:
  2860         Telemetry::Accumulate(Telemetry::GC_REASON_2, sample);
  2861         break;
  2862       case JS_TELEMETRY_GC_IS_COMPARTMENTAL:
  2863         Telemetry::Accumulate(Telemetry::GC_IS_COMPARTMENTAL, sample);
  2864         break;
  2865       case JS_TELEMETRY_GC_MS:
  2866         Telemetry::Accumulate(Telemetry::GC_MS, sample);
  2867         break;
  2868       case JS_TELEMETRY_GC_MAX_PAUSE_MS:
  2869         Telemetry::Accumulate(Telemetry::GC_MAX_PAUSE_MS, sample);
  2870         break;
  2871       case JS_TELEMETRY_GC_MARK_MS:
  2872         Telemetry::Accumulate(Telemetry::GC_MARK_MS, sample);
  2873         break;
  2874       case JS_TELEMETRY_GC_SWEEP_MS:
  2875         Telemetry::Accumulate(Telemetry::GC_SWEEP_MS, sample);
  2876         break;
  2877       case JS_TELEMETRY_GC_MARK_ROOTS_MS:
  2878         Telemetry::Accumulate(Telemetry::GC_MARK_ROOTS_MS, sample);
  2879         break;
  2880       case JS_TELEMETRY_GC_MARK_GRAY_MS:
  2881         Telemetry::Accumulate(Telemetry::GC_MARK_GRAY_MS, sample);
  2882         break;
  2883       case JS_TELEMETRY_GC_SLICE_MS:
  2884         Telemetry::Accumulate(Telemetry::GC_SLICE_MS, sample);
  2885         break;
  2886       case JS_TELEMETRY_GC_MMU_50:
  2887         Telemetry::Accumulate(Telemetry::GC_MMU_50, sample);
  2888         break;
  2889       case JS_TELEMETRY_GC_RESET:
  2890         Telemetry::Accumulate(Telemetry::GC_RESET, sample);
  2891         break;
  2892       case JS_TELEMETRY_GC_INCREMENTAL_DISABLED:
  2893         Telemetry::Accumulate(Telemetry::GC_INCREMENTAL_DISABLED, sample);
  2894         break;
  2895       case JS_TELEMETRY_GC_NON_INCREMENTAL:
  2896         Telemetry::Accumulate(Telemetry::GC_NON_INCREMENTAL, sample);
  2897         break;
  2898       case JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS:
  2899         Telemetry::Accumulate(Telemetry::GC_SCC_SWEEP_TOTAL_MS, sample);
  2900         break;
  2901       case JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS:
  2902         Telemetry::Accumulate(Telemetry::GC_SCC_SWEEP_MAX_PAUSE_MS, sample);
  2903         break;
  2907 static void
  2908 CompartmentNameCallback(JSRuntime *rt, JSCompartment *comp,
  2909                         char *buf, size_t bufsize)
  2911     nsCString name;
  2912     GetCompartmentName(comp, name, false);
  2913     if (name.Length() >= bufsize)
  2914         name.Truncate(bufsize - 1);
  2915     memcpy(buf, name.get(), name.Length() + 1);
  2918 static bool
  2919 PreserveWrapper(JSContext *cx, JSObject *obj)
  2921     MOZ_ASSERT(cx);
  2922     MOZ_ASSERT(obj);
  2923     MOZ_ASSERT(IS_WN_REFLECTOR(obj) || mozilla::dom::IsDOMObject(obj));
  2925     return mozilla::dom::IsDOMObject(obj) && mozilla::dom::TryPreserveWrapper(obj);
  2928 static nsresult
  2929 ReadSourceFromFilename(JSContext *cx, const char *filename, jschar **src, size_t *len)
  2931     nsresult rv;
  2933     // mozJSSubScriptLoader prefixes the filenames of the scripts it loads with
  2934     // the filename of its caller. Axe that if present.
  2935     const char *arrow;
  2936     while ((arrow = strstr(filename, " -> ")))
  2937         filename = arrow + strlen(" -> ");
  2939     // Get the URI.
  2940     nsCOMPtr<nsIURI> uri;
  2941     rv = NS_NewURI(getter_AddRefs(uri), filename);
  2942     NS_ENSURE_SUCCESS(rv, rv);
  2944     nsCOMPtr<nsIChannel> scriptChannel;
  2945     rv = NS_NewChannel(getter_AddRefs(scriptChannel), uri);
  2946     NS_ENSURE_SUCCESS(rv, rv);
  2948     // Only allow local reading.
  2949     nsCOMPtr<nsIURI> actualUri;
  2950     rv = scriptChannel->GetURI(getter_AddRefs(actualUri));
  2951     NS_ENSURE_SUCCESS(rv, rv);
  2952     nsCString scheme;
  2953     rv = actualUri->GetScheme(scheme);
  2954     NS_ENSURE_SUCCESS(rv, rv);
  2955     if (!scheme.EqualsLiteral("file") && !scheme.EqualsLiteral("jar"))
  2956         return NS_OK;
  2958     nsCOMPtr<nsIInputStream> scriptStream;
  2959     rv = scriptChannel->Open(getter_AddRefs(scriptStream));
  2960     NS_ENSURE_SUCCESS(rv, rv);
  2962     uint64_t rawLen;
  2963     rv = scriptStream->Available(&rawLen);
  2964     NS_ENSURE_SUCCESS(rv, rv);
  2965     if (!rawLen)
  2966         return NS_ERROR_FAILURE;
  2968     // Technically, this should be SIZE_MAX, but we don't run on machines
  2969     // where that would be less than UINT32_MAX, and the latter is already
  2970     // well beyond a reasonable limit.
  2971     if (rawLen > UINT32_MAX)
  2972         return NS_ERROR_FILE_TOO_BIG;
  2974     // Allocate an internal buf the size of the file.
  2975     nsAutoArrayPtr<unsigned char> buf(new unsigned char[rawLen]);
  2976     if (!buf)
  2977         return NS_ERROR_OUT_OF_MEMORY;
  2979     unsigned char *ptr = buf, *end = ptr + rawLen;
  2980     while (ptr < end) {
  2981         uint32_t bytesRead;
  2982         rv = scriptStream->Read(reinterpret_cast<char *>(ptr), end - ptr, &bytesRead);
  2983         if (NS_FAILED(rv))
  2984             return rv;
  2985         MOZ_ASSERT(bytesRead > 0, "stream promised more bytes before EOF");
  2986         ptr += bytesRead;
  2989     rv = nsScriptLoader::ConvertToUTF16(scriptChannel, buf, rawLen, EmptyString(),
  2990                                         nullptr, *src, *len);
  2991     NS_ENSURE_SUCCESS(rv, rv);
  2993     if (!*src)
  2994         return NS_ERROR_FAILURE;
  2996     // Historically this method used JS_malloc() which updates the GC memory
  2997     // accounting.  Since ConvertToUTF16() now uses js_malloc() instead we
  2998     // update the accounting manually after the fact.
  2999     JS_updateMallocCounter(cx, *len);
  3001     return NS_OK;
  3004 // The JS engine calls this object's 'load' member function when it needs
  3005 // the source for a chrome JS function. See the comment in the XPCJSRuntime
  3006 // constructor.
  3007 class XPCJSSourceHook: public js::SourceHook {
  3008     bool load(JSContext *cx, const char *filename, jschar **src, size_t *length) {
  3009         *src = nullptr;
  3010         *length = 0;
  3012         if (!nsContentUtils::IsCallerChrome())
  3013             return true;
  3015         if (!filename)
  3016             return true;
  3018         nsresult rv = ReadSourceFromFilename(cx, filename, src, length);
  3019         if (NS_FAILED(rv)) {
  3020             xpc::Throw(cx, rv);
  3021             return false;
  3024         return true;
  3026 };
  3028 static const JSWrapObjectCallbacks WrapObjectCallbacks = {
  3029     xpc::WrapperFactory::Rewrap,
  3030     xpc::WrapperFactory::PrepareForWrapping
  3031 };
  3033 XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
  3034    : CycleCollectedJSRuntime(nullptr, 32L * 1024L * 1024L, JS_USE_HELPER_THREADS),
  3035    mJSContextStack(new XPCJSContextStack(MOZ_THIS_IN_INITIALIZER_LIST())),
  3036    mCallContext(nullptr),
  3037    mAutoRoots(nullptr),
  3038    mResolveName(JSID_VOID),
  3039    mResolvingWrapper(nullptr),
  3040    mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_SIZE)),
  3041    mWrappedJSClassMap(IID2WrappedJSClassMap::newMap(XPC_JS_CLASS_MAP_SIZE)),
  3042    mIID2NativeInterfaceMap(IID2NativeInterfaceMap::newMap(XPC_NATIVE_INTERFACE_MAP_SIZE)),
  3043    mClassInfo2NativeSetMap(ClassInfo2NativeSetMap::newMap(XPC_NATIVE_SET_MAP_SIZE)),
  3044    mNativeSetMap(NativeSetMap::newMap(XPC_NATIVE_SET_MAP_SIZE)),
  3045    mThisTranslatorMap(IID2ThisTranslatorMap::newMap(XPC_THIS_TRANSLATOR_MAP_SIZE)),
  3046    mNativeScriptableSharedMap(XPCNativeScriptableSharedMap::newMap(XPC_NATIVE_JSCLASS_MAP_SIZE)),
  3047    mDyingWrappedNativeProtoMap(XPCWrappedNativeProtoMap::newMap(XPC_DYING_NATIVE_PROTO_MAP_SIZE)),
  3048    mDetachedWrappedNativeProtoMap(XPCWrappedNativeProtoMap::newMap(XPC_DETACHED_NATIVE_PROTO_MAP_SIZE)),
  3049    mGCIsRunning(false),
  3050    mWrappedJSToReleaseArray(),
  3051    mNativesToReleaseArray(),
  3052    mDoingFinalization(false),
  3053    mVariantRoots(nullptr),
  3054    mWrappedJSRoots(nullptr),
  3055    mObjectHolderRoots(nullptr),
  3056    mWatchdogManager(new WatchdogManager(MOZ_THIS_IN_INITIALIZER_LIST())),
  3057    mJunkScope(MOZ_THIS_IN_INITIALIZER_LIST()->Runtime(), nullptr),
  3058    mCompilationScope(MOZ_THIS_IN_INITIALIZER_LIST()->Runtime(), nullptr),
  3059    mAsyncSnowWhiteFreer(new AsyncFreeSnowWhite())
  3061     DOM_InitInterfaces();
  3063     // these jsids filled in later when we have a JSContext to work with.
  3064     mStrIDs[0] = JSID_VOID;
  3066     MOZ_ASSERT(Runtime());
  3067     JSRuntime* runtime = Runtime();
  3069     auto rtPrivate = new PerThreadAtomCache();
  3070     memset(rtPrivate, 0, sizeof(PerThreadAtomCache));
  3071     JS_SetRuntimePrivate(runtime, rtPrivate);
  3073     // Unconstrain the runtime's threshold on nominal heap size, to avoid
  3074     // triggering GC too often if operating continuously near an arbitrary
  3075     // finite threshold (0xffffffff is infinity for uint32_t parameters).
  3076     // This leaves the maximum-JS_malloc-bytes threshold still in effect
  3077     // to cause period, and we hope hygienic, last-ditch GCs from within
  3078     // the GC's allocator.
  3079     JS_SetGCParameter(runtime, JSGC_MAX_BYTES, 0xffffffff);
  3081     // The JS engine permits us to set different stack limits for system code,
  3082     // trusted script, and untrusted script. We have tests that ensure that
  3083     // we can always execute 10 "heavy" (eval+with) stack frames deeper in
  3084     // privileged code. Our stack sizes vary greatly in different configurations,
  3085     // so satisfying those tests requires some care. Manual measurements of the
  3086     // number of heavy stack frames achievable gives us the following rough data,
  3087     // ordered by the effective categories in which they are grouped in the
  3088     // JS_SetNativeStackQuota call (which predates this analysis).
  3089     //
  3090     // (NB: These numbers may have drifted recently - see bug 938429)
  3091     // OSX 64-bit Debug: 7MB stack, 636 stack frames => ~11.3k per stack frame
  3092     // OSX64 Opt: 7MB stack, 2440 stack frames => ~3k per stack frame
  3093     //
  3094     // Linux 32-bit Debug: 2MB stack, 447 stack frames => ~4.6k per stack frame
  3095     // Linux 64-bit Debug: 4MB stack, 501 stack frames => ~8.2k per stack frame
  3096     //
  3097     // Windows (Opt+Debug): 900K stack, 235 stack frames => ~3.4k per stack frame
  3098     //
  3099     // Linux 32-bit Opt: 1MB stack, 272 stack frames => ~3.8k per stack frame
  3100     // Linux 64-bit Opt: 2MB stack, 316 stack frames => ~6.5k per stack frame
  3101     //
  3102     // We tune the trusted/untrusted quotas for each configuration to achieve our
  3103     // invariants while attempting to minimize overhead. In contrast, our buffer
  3104     // between system code and trusted script is a very unscientific 10k.
  3105     const size_t kSystemCodeBuffer = 10 * 1024;
  3107     // Our "default" stack is what we use in configurations where we don't have
  3108     // a compelling reason to do things differently. This is effectively 1MB on
  3109     // 32-bit platforms and 2MB on 64-bit platforms.
  3110     const size_t kDefaultStackQuota = 128 * sizeof(size_t) * 1024;
  3112     // Set stack sizes for different configurations. It's probably not great for
  3113     // the web to base this decision primarily on the default stack size that the
  3114     // underlying platform makes available, but that seems to be what we do. :-(
  3116 #if defined(XP_MACOSX) || defined(DARWIN)
  3117     // MacOS has a gargantuan default stack size of 8MB. Go wild with 7MB,
  3118     // and give trusted script 140k extra. The stack is huge on mac anyway.
  3119     const size_t kStackQuota = 7 * 1024 * 1024;
  3120     const size_t kTrustedScriptBuffer = 140 * 1024;
  3121 #elif defined(MOZ_ASAN)
  3122     // ASan requires more stack space due to red-zones, so give it double the
  3123     // default (2MB on 32-bit, 4MB on 64-bit). ASAN stack frame measurements
  3124     // were not taken at the time of this writing, so we hazard a guess that
  3125     // ASAN builds have roughly thrice the stack overhead as normal builds.
  3126     // On normal builds, the largest stack frame size we might encounter is
  3127     // 8.2k, so let's use a buffer of 8.2 * 3 * 10 = 246k.
  3128     const size_t kStackQuota =  2 * kDefaultStackQuota;
  3129     const size_t kTrustedScriptBuffer = 246 * 1024;
  3130 #elif defined(XP_WIN)
  3131     // 1MB is the default stack size on Windows, so the default 1MB stack quota
  3132     // we'd get on win32 is slightly too large. Use 900k instead. And since
  3133     // windows stack frames are 3.4k each, let's use a buffer of 50k.
  3134     const size_t kStackQuota = 900 * 1024;
  3135     const size_t kTrustedScriptBuffer = 50 * 1024;
  3136     // The following two configurations are linux-only. Given the numbers above,
  3137     // we use 50k and 100k trusted buffers on 32-bit and 64-bit respectively.
  3138 #elif defined(DEBUG)
  3139     // Bug 803182: account for the 4x difference in the size of js::Interpret
  3140     // between optimized and debug builds.
  3141     // XXXbholley - Then why do we only account for 2x of difference?
  3142     const size_t kStackQuota = 2 * kDefaultStackQuota;
  3143     const size_t kTrustedScriptBuffer = sizeof(size_t) * 12800;
  3144 #else
  3145     const size_t kStackQuota = kDefaultStackQuota;
  3146     const size_t kTrustedScriptBuffer = sizeof(size_t) * 12800;
  3147 #endif
  3149     // Avoid an unused variable warning on platforms where we don't use the
  3150     // default.
  3151     (void) kDefaultStackQuota;
  3153     JS_SetNativeStackQuota(runtime,
  3154                            kStackQuota,
  3155                            kStackQuota - kSystemCodeBuffer,
  3156                            kStackQuota - kSystemCodeBuffer - kTrustedScriptBuffer);
  3158     JS_SetDestroyCompartmentCallback(runtime, CompartmentDestroyedCallback);
  3159     JS_SetCompartmentNameCallback(runtime, CompartmentNameCallback);
  3160     mPrevGCSliceCallback = JS::SetGCSliceCallback(runtime, GCSliceCallback);
  3161     JS_SetFinalizeCallback(runtime, FinalizeCallback);
  3162     JS_SetWrapObjectCallbacks(runtime, &WrapObjectCallbacks);
  3163     js::SetPreserveWrapperCallback(runtime, PreserveWrapper);
  3164 #ifdef MOZ_CRASHREPORTER
  3165     JS_EnumerateDiagnosticMemoryRegions(DiagnosticMemoryCallback);
  3166 #endif
  3167 #ifdef MOZ_ENABLE_PROFILER_SPS
  3168     if (PseudoStack *stack = mozilla_get_pseudo_stack())
  3169         stack->sampleRuntime(runtime);
  3170 #endif
  3171     JS_SetAccumulateTelemetryCallback(runtime, AccumulateTelemetryCallback);
  3172     js::SetDefaultJSContextCallback(runtime, DefaultJSContextCallback);
  3173     js::SetActivityCallback(runtime, ActivityCallback, this);
  3174     js::SetCTypesActivityCallback(runtime, CTypesActivityCallback);
  3175     JS_SetInterruptCallback(runtime, InterruptCallback);
  3176     JS::SetOutOfMemoryCallback(runtime, OutOfMemoryCallback);
  3178     // The JS engine needs to keep the source code around in order to implement
  3179     // Function.prototype.toSource(). It'd be nice to not have to do this for
  3180     // chrome code and simply stub out requests for source on it. Life is not so
  3181     // easy, unfortunately. Nobody relies on chrome toSource() working in core
  3182     // browser code, but chrome tests use it. The worst offenders are addons,
  3183     // which like to monkeypatch chrome functions by calling toSource() on them
  3184     // and using regular expressions to modify them. We avoid keeping most browser
  3185     // JS source code in memory by setting LAZY_SOURCE on JS::CompileOptions when
  3186     // compiling some chrome code. This causes the JS engine not save the source
  3187     // code in memory. When the JS engine is asked to provide the source for a
  3188     // function compiled with LAZY_SOURCE, it calls SourceHook to load it.
  3189     ///
  3190     // Note we do have to retain the source code in memory for scripts compiled in
  3191     // compileAndGo mode and compiled function bodies (from
  3192     // JS_CompileFunction*). In practice, this means content scripts and event
  3193     // handlers.
  3194     js::SetSourceHook(runtime, new XPCJSSourceHook);
  3196     // Set up locale information and callbacks for the newly-created runtime so
  3197     // that the various toLocaleString() methods, localeCompare(), and other
  3198     // internationalization APIs work as desired.
  3199     if (!xpc_LocalizeRuntime(runtime))
  3200         NS_RUNTIMEABORT("xpc_LocalizeRuntime failed.");
  3202     // Register memory reporters and distinguished amount functions.
  3203     RegisterStrongMemoryReporter(new JSMainRuntimeCompartmentsReporter());
  3204     RegisterStrongMemoryReporter(new JSMainRuntimeTemporaryPeakReporter());
  3205     RegisterJSMainRuntimeGCHeapDistinguishedAmount(JSMainRuntimeGCHeapDistinguishedAmount);
  3206     RegisterJSMainRuntimeTemporaryPeakDistinguishedAmount(JSMainRuntimeTemporaryPeakDistinguishedAmount);
  3207     RegisterJSMainRuntimeCompartmentsSystemDistinguishedAmount(JSMainRuntimeCompartmentsSystemDistinguishedAmount);
  3208     RegisterJSMainRuntimeCompartmentsUserDistinguishedAmount(JSMainRuntimeCompartmentsUserDistinguishedAmount);
  3209     mozilla::RegisterJSSizeOfTab(JSSizeOfTab);
  3211     // Install a JavaScript 'debugger' keyword handler in debug builds only
  3212 #ifdef DEBUG
  3213     if (!JS_GetGlobalDebugHooks(runtime)->debuggerHandler)
  3214         xpc_InstallJSDebuggerKeywordHandler(runtime);
  3215 #endif
  3217     // Watch for the JS boolean options.
  3218     ReloadPrefsCallback(nullptr, this);
  3219     Preferences::RegisterCallback(ReloadPrefsCallback, JS_OPTIONS_DOT_STR, this);
  3222 // static
  3223 XPCJSRuntime*
  3224 XPCJSRuntime::newXPCJSRuntime(nsXPConnect* aXPConnect)
  3226     NS_PRECONDITION(aXPConnect,"bad param");
  3228     XPCJSRuntime* self = new XPCJSRuntime(aXPConnect);
  3230     if (self                                    &&
  3231         self->Runtime()                         &&
  3232         self->GetWrappedJSMap()                 &&
  3233         self->GetWrappedJSClassMap()            &&
  3234         self->GetIID2NativeInterfaceMap()       &&
  3235         self->GetClassInfo2NativeSetMap()       &&
  3236         self->GetNativeSetMap()                 &&
  3237         self->GetThisTranslatorMap()            &&
  3238         self->GetNativeScriptableSharedMap()    &&
  3239         self->GetDyingWrappedNativeProtoMap()   &&
  3240         self->mWatchdogManager) {
  3241         return self;
  3244     NS_RUNTIMEABORT("new XPCJSRuntime failed to initialize.");
  3246     delete self;
  3247     return nullptr;
  3250 bool
  3251 XPCJSRuntime::OnJSContextNew(JSContext *cx)
  3253     // If we were the first cx ever created (like the SafeJSContext), the caller
  3254     // would have had no way to enter a request. Enter one now before doing the
  3255     // rest of the cx setup.
  3256     JSAutoRequest ar(cx);
  3258     // if it is our first context then we need to generate our string ids
  3259     if (JSID_IS_VOID(mStrIDs[0])) {
  3260         RootedString str(cx);
  3261         for (unsigned i = 0; i < IDX_TOTAL_COUNT; i++) {
  3262             str = JS_InternString(cx, mStrings[i]);
  3263             if (!str) {
  3264                 mStrIDs[0] = JSID_VOID;
  3265                 return false;
  3267             mStrIDs[i] = INTERNED_STRING_TO_JSID(cx, str);
  3268             mStrJSVals[i] = STRING_TO_JSVAL(str);
  3271         if (!mozilla::dom::DefineStaticJSVals(cx)) {
  3272             return false;
  3276     XPCContext* xpc = new XPCContext(this, cx);
  3277     if (!xpc)
  3278         return false;
  3280     return true;
  3283 bool
  3284 XPCJSRuntime::DescribeCustomObjects(JSObject* obj, const js::Class* clasp,
  3285                                     char (&name)[72]) const
  3287     XPCNativeScriptableInfo *si = nullptr;
  3289     if (!IS_PROTO_CLASS(clasp)) {
  3290         return false;
  3293     XPCWrappedNativeProto *p =
  3294         static_cast<XPCWrappedNativeProto*>(xpc_GetJSPrivate(obj));
  3295     si = p->GetScriptableInfo();
  3297     if (!si) {
  3298         return false;
  3301     JS_snprintf(name, sizeof(name), "JS Object (%s - %s)",
  3302                 clasp->name, si->GetJSClass()->name);
  3303     return true;
  3306 bool
  3307 XPCJSRuntime::NoteCustomGCThingXPCOMChildren(const js::Class* clasp, JSObject* obj,
  3308                                              nsCycleCollectionTraversalCallback& cb) const
  3310     if (clasp != &XPC_WN_Tearoff_JSClass) {
  3311         return false;
  3314     // A tearoff holds a strong reference to its native object
  3315     // (see XPCWrappedNative::FlatJSObjectFinalized). Its XPCWrappedNative
  3316     // will be held alive through the parent of the JSObject of the tearoff.
  3317     XPCWrappedNativeTearOff *to =
  3318         static_cast<XPCWrappedNativeTearOff*>(xpc_GetJSPrivate(obj));
  3319     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "xpc_GetJSPrivate(obj)->mNative");
  3320     cb.NoteXPCOMChild(to->GetNative());
  3321     return true;
  3324 /***************************************************************************/
  3326 #ifdef DEBUG
  3327 static PLDHashOperator
  3328 WrappedJSClassMapDumpEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr,
  3329                                 uint32_t number, void *arg)
  3331     ((IID2WrappedJSClassMap::Entry*)hdr)->value->DebugDump(*(int16_t*)arg);
  3332     return PL_DHASH_NEXT;
  3334 static PLDHashOperator
  3335 NativeSetDumpEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr,
  3336                         uint32_t number, void *arg)
  3338     ((NativeSetMap::Entry*)hdr)->key_value->DebugDump(*(int16_t*)arg);
  3339     return PL_DHASH_NEXT;
  3341 #endif
  3343 void
  3344 XPCJSRuntime::DebugDump(int16_t depth)
  3346 #ifdef DEBUG
  3347     depth--;
  3348     XPC_LOG_ALWAYS(("XPCJSRuntime @ %x", this));
  3349         XPC_LOG_INDENT();
  3350         XPC_LOG_ALWAYS(("mJSRuntime @ %x", Runtime()));
  3352         XPC_LOG_ALWAYS(("mWrappedJSToReleaseArray @ %x with %d wrappers(s)", \
  3353                         &mWrappedJSToReleaseArray,
  3354                         mWrappedJSToReleaseArray.Length()));
  3356         int cxCount = 0;
  3357         JSContext* iter = nullptr;
  3358         while (JS_ContextIterator(Runtime(), &iter))
  3359             ++cxCount;
  3360         XPC_LOG_ALWAYS(("%d JS context(s)", cxCount));
  3362         iter = nullptr;
  3363         while (JS_ContextIterator(Runtime(), &iter)) {
  3364             XPCContext *xpc = XPCContext::GetXPCContext(iter);
  3365             XPC_LOG_INDENT();
  3366             xpc->DebugDump(depth);
  3367             XPC_LOG_OUTDENT();
  3370         XPC_LOG_ALWAYS(("mWrappedJSClassMap @ %x with %d wrapperclasses(s)",  \
  3371                         mWrappedJSClassMap, mWrappedJSClassMap ?              \
  3372                         mWrappedJSClassMap->Count() : 0));
  3373         // iterate wrappersclasses...
  3374         if (depth && mWrappedJSClassMap && mWrappedJSClassMap->Count()) {
  3375             XPC_LOG_INDENT();
  3376             mWrappedJSClassMap->Enumerate(WrappedJSClassMapDumpEnumerator, &depth);
  3377             XPC_LOG_OUTDENT();
  3379         XPC_LOG_ALWAYS(("mWrappedJSMap @ %x with %d wrappers(s)",             \
  3380                         mWrappedJSMap, mWrappedJSMap ?                        \
  3381                         mWrappedJSMap->Count() : 0));
  3382         // iterate wrappers...
  3383         if (depth && mWrappedJSMap && mWrappedJSMap->Count()) {
  3384             XPC_LOG_INDENT();
  3385             mWrappedJSMap->Dump(depth);
  3386             XPC_LOG_OUTDENT();
  3389         XPC_LOG_ALWAYS(("mIID2NativeInterfaceMap @ %x with %d interface(s)",  \
  3390                         mIID2NativeInterfaceMap, mIID2NativeInterfaceMap ?    \
  3391                         mIID2NativeInterfaceMap->Count() : 0));
  3393         XPC_LOG_ALWAYS(("mClassInfo2NativeSetMap @ %x with %d sets(s)",       \
  3394                         mClassInfo2NativeSetMap, mClassInfo2NativeSetMap ?    \
  3395                         mClassInfo2NativeSetMap->Count() : 0));
  3397         XPC_LOG_ALWAYS(("mThisTranslatorMap @ %x with %d translator(s)",      \
  3398                         mThisTranslatorMap, mThisTranslatorMap ?              \
  3399                         mThisTranslatorMap->Count() : 0));
  3401         XPC_LOG_ALWAYS(("mNativeSetMap @ %x with %d sets(s)",                 \
  3402                         mNativeSetMap, mNativeSetMap ?                        \
  3403                         mNativeSetMap->Count() : 0));
  3405         // iterate sets...
  3406         if (depth && mNativeSetMap && mNativeSetMap->Count()) {
  3407             XPC_LOG_INDENT();
  3408             mNativeSetMap->Enumerate(NativeSetDumpEnumerator, &depth);
  3409             XPC_LOG_OUTDENT();
  3412         XPC_LOG_OUTDENT();
  3413 #endif
  3416 /***************************************************************************/
  3418 void
  3419 XPCRootSetElem::AddToRootSet(XPCRootSetElem **listHead)
  3421     MOZ_ASSERT(!mSelfp, "Must be not linked");
  3423     mSelfp = listHead;
  3424     mNext = *listHead;
  3425     if (mNext) {
  3426         MOZ_ASSERT(mNext->mSelfp == listHead, "Must be list start");
  3427         mNext->mSelfp = &mNext;
  3429     *listHead = this;
  3432 void
  3433 XPCRootSetElem::RemoveFromRootSet()
  3435     nsXPConnect *xpc = nsXPConnect::XPConnect();
  3436     JS::PokeGC(xpc->GetRuntime()->Runtime());
  3438     MOZ_ASSERT(mSelfp, "Must be linked");
  3440     MOZ_ASSERT(*mSelfp == this, "Link invariant");
  3441     *mSelfp = mNext;
  3442     if (mNext)
  3443         mNext->mSelfp = mSelfp;
  3444 #ifdef DEBUG
  3445     mSelfp = nullptr;
  3446     mNext = nullptr;
  3447 #endif
  3450 void
  3451 XPCJSRuntime::AddGCCallback(xpcGCCallback cb)
  3453     MOZ_ASSERT(cb, "null callback");
  3454     extraGCCallbacks.AppendElement(cb);
  3457 void
  3458 XPCJSRuntime::RemoveGCCallback(xpcGCCallback cb)
  3460     MOZ_ASSERT(cb, "null callback");
  3461     bool found = extraGCCallbacks.RemoveElement(cb);
  3462     if (!found) {
  3463         NS_ERROR("Removing a callback which was never added.");
  3467 void
  3468 XPCJSRuntime::AddContextCallback(xpcContextCallback cb)
  3470     MOZ_ASSERT(cb, "null callback");
  3471     extraContextCallbacks.AppendElement(cb);
  3474 void
  3475 XPCJSRuntime::RemoveContextCallback(xpcContextCallback cb)
  3477     MOZ_ASSERT(cb, "null callback");
  3478     bool found = extraContextCallbacks.RemoveElement(cb);
  3479     if (!found) {
  3480         NS_ERROR("Removing a callback which was never added.");
  3484 JSObject *
  3485 XPCJSRuntime::GetJunkScope()
  3487     if (!mJunkScope) {
  3488         AutoSafeJSContext cx;
  3489         SandboxOptions options;
  3490         options.sandboxName.AssignLiteral("XPConnect Junk Compartment");
  3491         RootedValue v(cx);
  3492         nsresult rv = CreateSandboxObject(cx, &v, nsContentUtils::GetSystemPrincipal(), options);
  3493         NS_ENSURE_SUCCESS(rv, nullptr);
  3495         mJunkScope = js::UncheckedUnwrap(&v.toObject());
  3497     return mJunkScope;
  3500 JSObject *
  3501 XPCJSRuntime::GetCompilationScope()
  3503     if (!mCompilationScope) {
  3504         AutoSafeJSContext cx;
  3505         SandboxOptions options;
  3506         options.sandboxName.AssignLiteral("XPConnect Compilation Compartment");
  3507         options.invisibleToDebugger = true;
  3508         options.discardSource = ShouldDiscardSystemSource();
  3509         RootedValue v(cx);
  3510         nsresult rv = CreateSandboxObject(cx, &v, /* principal = */ nullptr, options);
  3511         NS_ENSURE_SUCCESS(rv, nullptr);
  3513         mCompilationScope = js::UncheckedUnwrap(&v.toObject());
  3515     return mCompilationScope;
  3519 void
  3520 XPCJSRuntime::DeleteSingletonScopes()
  3522     mJunkScope = nullptr;
  3523     mCompilationScope = nullptr;

mercurial