js/xpconnect/src/XPCJSRuntime.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial