1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/xpconnect/src/XPCJSRuntime.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,3524 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* vim: set ts=8 sts=4 et sw=4 tw=99: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* Per JSRuntime object */ 1.11 + 1.12 +#include "mozilla/MemoryReporting.h" 1.13 + 1.14 +#include "xpcprivate.h" 1.15 +#include "xpcpublic.h" 1.16 +#include "XPCWrapper.h" 1.17 +#include "XPCJSMemoryReporter.h" 1.18 +#include "WrapperFactory.h" 1.19 +#include "dom_quickstubs.h" 1.20 +#include "mozJSComponentLoader.h" 1.21 + 1.22 +#include "nsIMemoryInfoDumper.h" 1.23 +#include "nsIMemoryReporter.h" 1.24 +#include "nsIObserverService.h" 1.25 +#include "nsIDebug2.h" 1.26 +#include "amIAddonManager.h" 1.27 +#include "nsPIDOMWindow.h" 1.28 +#include "nsPrintfCString.h" 1.29 +#include "mozilla/Preferences.h" 1.30 +#include "mozilla/Telemetry.h" 1.31 +#include "mozilla/Services.h" 1.32 + 1.33 +#include "nsContentUtils.h" 1.34 +#include "nsCxPusher.h" 1.35 +#include "nsCCUncollectableMarker.h" 1.36 +#include "nsCycleCollectionNoteRootCallback.h" 1.37 +#include "nsScriptLoader.h" 1.38 +#include "jsfriendapi.h" 1.39 +#include "jsprf.h" 1.40 +#include "js/MemoryMetrics.h" 1.41 +#include "js/OldDebugAPI.h" 1.42 +#include "mozilla/dom/GeneratedAtomList.h" 1.43 +#include "mozilla/dom/BindingUtils.h" 1.44 +#include "mozilla/dom/Element.h" 1.45 +#include "mozilla/dom/WindowBinding.h" 1.46 +#include "mozilla/Atomics.h" 1.47 +#include "mozilla/Attributes.h" 1.48 +#include "AccessCheck.h" 1.49 +#include "nsGlobalWindow.h" 1.50 +#include "nsAboutProtocolUtils.h" 1.51 + 1.52 +#include "GeckoProfiler.h" 1.53 +#include "nsIXULRuntime.h" 1.54 +#include "nsJSPrincipals.h" 1.55 + 1.56 +#ifdef MOZ_CRASHREPORTER 1.57 +#include "nsExceptionHandler.h" 1.58 +#endif 1.59 + 1.60 +using namespace mozilla; 1.61 +using namespace xpc; 1.62 +using namespace JS; 1.63 +using mozilla::dom::PerThreadAtomCache; 1.64 + 1.65 +/***************************************************************************/ 1.66 + 1.67 +const char* const XPCJSRuntime::mStrings[] = { 1.68 + "constructor", // IDX_CONSTRUCTOR 1.69 + "toString", // IDX_TO_STRING 1.70 + "toSource", // IDX_TO_SOURCE 1.71 + "lastResult", // IDX_LAST_RESULT 1.72 + "returnCode", // IDX_RETURN_CODE 1.73 + "value", // IDX_VALUE 1.74 + "QueryInterface", // IDX_QUERY_INTERFACE 1.75 + "Components", // IDX_COMPONENTS 1.76 + "wrappedJSObject", // IDX_WRAPPED_JSOBJECT 1.77 + "Object", // IDX_OBJECT 1.78 + "Function", // IDX_FUNCTION 1.79 + "prototype", // IDX_PROTOTYPE 1.80 + "createInstance", // IDX_CREATE_INSTANCE 1.81 + "item", // IDX_ITEM 1.82 + "__proto__", // IDX_PROTO 1.83 + "__iterator__", // IDX_ITERATOR 1.84 + "__exposedProps__", // IDX_EXPOSEDPROPS 1.85 + "eval", // IDX_EVAL 1.86 + "controllers", // IDX_CONTROLLERS 1.87 +}; 1.88 + 1.89 +/***************************************************************************/ 1.90 + 1.91 +static mozilla::Atomic<bool> sDiscardSystemSource(false); 1.92 + 1.93 +bool 1.94 +xpc::ShouldDiscardSystemSource() { return sDiscardSystemSource; } 1.95 + 1.96 +static void * const UNMARK_ONLY = nullptr; 1.97 +static void * const UNMARK_AND_SWEEP = (void *)1; 1.98 + 1.99 +static PLDHashOperator 1.100 +NativeInterfaceSweeper(PLDHashTable *table, PLDHashEntryHdr *hdr, 1.101 + uint32_t number, void *arg) 1.102 +{ 1.103 + XPCNativeInterface* iface = ((IID2NativeInterfaceMap::Entry*)hdr)->value; 1.104 + if (iface->IsMarked()) { 1.105 + iface->Unmark(); 1.106 + return PL_DHASH_NEXT; 1.107 + } 1.108 + 1.109 + if (arg == UNMARK_ONLY) 1.110 + return PL_DHASH_NEXT; 1.111 + 1.112 + XPCNativeInterface::DestroyInstance(iface); 1.113 + return PL_DHASH_REMOVE; 1.114 +} 1.115 + 1.116 +// *Some* NativeSets are referenced from mClassInfo2NativeSetMap. 1.117 +// *All* NativeSets are referenced from mNativeSetMap. 1.118 +// So, in mClassInfo2NativeSetMap we just clear references to the unmarked. 1.119 +// In mNativeSetMap we clear the references to the unmarked *and* delete them. 1.120 + 1.121 +static PLDHashOperator 1.122 +NativeUnMarkedSetRemover(PLDHashTable *table, PLDHashEntryHdr *hdr, 1.123 + uint32_t number, void *arg) 1.124 +{ 1.125 + XPCNativeSet* set = ((ClassInfo2NativeSetMap::Entry*)hdr)->value; 1.126 + if (set->IsMarked()) 1.127 + return PL_DHASH_NEXT; 1.128 + return PL_DHASH_REMOVE; 1.129 +} 1.130 + 1.131 +static PLDHashOperator 1.132 +NativeSetSweeper(PLDHashTable *table, PLDHashEntryHdr *hdr, 1.133 + uint32_t number, void *arg) 1.134 +{ 1.135 + XPCNativeSet* set = ((NativeSetMap::Entry*)hdr)->key_value; 1.136 + if (set->IsMarked()) { 1.137 + set->Unmark(); 1.138 + return PL_DHASH_NEXT; 1.139 + } 1.140 + 1.141 + if (arg == UNMARK_ONLY) 1.142 + return PL_DHASH_NEXT; 1.143 + 1.144 + XPCNativeSet::DestroyInstance(set); 1.145 + return PL_DHASH_REMOVE; 1.146 +} 1.147 + 1.148 +static PLDHashOperator 1.149 +JSClassSweeper(PLDHashTable *table, PLDHashEntryHdr *hdr, 1.150 + uint32_t number, void *arg) 1.151 +{ 1.152 + XPCNativeScriptableShared* shared = 1.153 + ((XPCNativeScriptableSharedMap::Entry*) hdr)->key; 1.154 + if (shared->IsMarked()) { 1.155 + shared->Unmark(); 1.156 + return PL_DHASH_NEXT; 1.157 + } 1.158 + 1.159 + if (arg == UNMARK_ONLY) 1.160 + return PL_DHASH_NEXT; 1.161 + 1.162 + delete shared; 1.163 + return PL_DHASH_REMOVE; 1.164 +} 1.165 + 1.166 +static PLDHashOperator 1.167 +DyingProtoKiller(PLDHashTable *table, PLDHashEntryHdr *hdr, 1.168 + uint32_t number, void *arg) 1.169 +{ 1.170 + XPCWrappedNativeProto* proto = 1.171 + (XPCWrappedNativeProto*)((PLDHashEntryStub*)hdr)->key; 1.172 + delete proto; 1.173 + return PL_DHASH_REMOVE; 1.174 +} 1.175 + 1.176 +static PLDHashOperator 1.177 +DetachedWrappedNativeProtoMarker(PLDHashTable *table, PLDHashEntryHdr *hdr, 1.178 + uint32_t number, void *arg) 1.179 +{ 1.180 + XPCWrappedNativeProto* proto = 1.181 + (XPCWrappedNativeProto*)((PLDHashEntryStub*)hdr)->key; 1.182 + 1.183 + proto->Mark(); 1.184 + return PL_DHASH_NEXT; 1.185 +} 1.186 + 1.187 +bool 1.188 +XPCJSRuntime::CustomContextCallback(JSContext *cx, unsigned operation) 1.189 +{ 1.190 + if (operation == JSCONTEXT_NEW) { 1.191 + if (!OnJSContextNew(cx)) { 1.192 + return false; 1.193 + } 1.194 + } else if (operation == JSCONTEXT_DESTROY) { 1.195 + delete XPCContext::GetXPCContext(cx); 1.196 + } 1.197 + 1.198 + nsTArray<xpcContextCallback> callbacks(extraContextCallbacks); 1.199 + for (uint32_t i = 0; i < callbacks.Length(); ++i) { 1.200 + if (!callbacks[i](cx, operation)) { 1.201 + return false; 1.202 + } 1.203 + } 1.204 + 1.205 + return true; 1.206 +} 1.207 + 1.208 +class AsyncFreeSnowWhite : public nsRunnable 1.209 +{ 1.210 +public: 1.211 + NS_IMETHOD Run() 1.212 + { 1.213 + TimeStamp start = TimeStamp::Now(); 1.214 + bool hadSnowWhiteObjects = nsCycleCollector_doDeferredDeletion(); 1.215 + Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_ASYNC_SNOW_WHITE_FREEING, 1.216 + uint32_t((TimeStamp::Now() - start).ToMilliseconds())); 1.217 + if (hadSnowWhiteObjects && !mContinuation) { 1.218 + mContinuation = true; 1.219 + if (NS_FAILED(NS_DispatchToCurrentThread(this))) { 1.220 + mActive = false; 1.221 + } 1.222 + } else { 1.223 + mActive = false; 1.224 + } 1.225 + return NS_OK; 1.226 + } 1.227 + 1.228 + void Dispatch(bool aContinuation = false) 1.229 + { 1.230 + if (mContinuation) { 1.231 + mContinuation = aContinuation; 1.232 + } 1.233 + if (!mActive && NS_SUCCEEDED(NS_DispatchToCurrentThread(this))) { 1.234 + mActive = true; 1.235 + } 1.236 + } 1.237 + 1.238 + AsyncFreeSnowWhite() : mContinuation(false), mActive(false) {} 1.239 + 1.240 +public: 1.241 + bool mContinuation; 1.242 + bool mActive; 1.243 +}; 1.244 + 1.245 +namespace xpc { 1.246 + 1.247 +static uint32_t kLivingAdopters = 0; 1.248 + 1.249 +void 1.250 +RecordAdoptedNode(JSCompartment *c) 1.251 +{ 1.252 + CompartmentPrivate *priv = EnsureCompartmentPrivate(c); 1.253 + if (!priv->adoptedNode) { 1.254 + priv->adoptedNode = true; 1.255 + ++kLivingAdopters; 1.256 + } 1.257 +} 1.258 + 1.259 +void 1.260 +RecordDonatedNode(JSCompartment *c) 1.261 +{ 1.262 + EnsureCompartmentPrivate(c)->donatedNode = true; 1.263 +} 1.264 + 1.265 +CompartmentPrivate::~CompartmentPrivate() 1.266 +{ 1.267 + MOZ_COUNT_DTOR(xpc::CompartmentPrivate); 1.268 + 1.269 + Telemetry::Accumulate(Telemetry::COMPARTMENT_ADOPTED_NODE, adoptedNode); 1.270 + Telemetry::Accumulate(Telemetry::COMPARTMENT_DONATED_NODE, donatedNode); 1.271 + if (adoptedNode) 1.272 + --kLivingAdopters; 1.273 +} 1.274 + 1.275 +static bool 1.276 +TryParseLocationURICandidate(const nsACString& uristr, 1.277 + CompartmentPrivate::LocationHint aLocationHint, 1.278 + nsIURI** aURI) 1.279 +{ 1.280 + static NS_NAMED_LITERAL_CSTRING(kGRE, "resource://gre/"); 1.281 + static NS_NAMED_LITERAL_CSTRING(kToolkit, "chrome://global/"); 1.282 + static NS_NAMED_LITERAL_CSTRING(kBrowser, "chrome://browser/"); 1.283 + 1.284 + if (aLocationHint == CompartmentPrivate::LocationHintAddon) { 1.285 + // Blacklist some known locations which are clearly not add-on related. 1.286 + if (StringBeginsWith(uristr, kGRE) || 1.287 + StringBeginsWith(uristr, kToolkit) || 1.288 + StringBeginsWith(uristr, kBrowser)) 1.289 + return false; 1.290 + } 1.291 + 1.292 + nsCOMPtr<nsIURI> uri; 1.293 + if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), uristr))) 1.294 + return false; 1.295 + 1.296 + nsAutoCString scheme; 1.297 + if (NS_FAILED(uri->GetScheme(scheme))) 1.298 + return false; 1.299 + 1.300 + // Cannot really map data: and blob:. 1.301 + // Also, data: URIs are pretty memory hungry, which is kinda bad 1.302 + // for memory reporter use. 1.303 + if (scheme.EqualsLiteral("data") || scheme.EqualsLiteral("blob")) 1.304 + return false; 1.305 + 1.306 + uri.forget(aURI); 1.307 + return true; 1.308 +} 1.309 + 1.310 +bool CompartmentPrivate::TryParseLocationURI(CompartmentPrivate::LocationHint aLocationHint, 1.311 + nsIURI **aURI) 1.312 +{ 1.313 + if (!aURI) 1.314 + return false; 1.315 + 1.316 + // Need to parse the URI. 1.317 + if (location.IsEmpty()) 1.318 + return false; 1.319 + 1.320 + // Handle Sandbox location strings. 1.321 + // A sandbox string looks like this: 1.322 + // <sandboxName> (from: <js-stack-frame-filename>:<lineno>) 1.323 + // where <sandboxName> is user-provided via Cu.Sandbox() 1.324 + // and <js-stack-frame-filename> and <lineno> is the stack frame location 1.325 + // from where Cu.Sandbox was called. 1.326 + // <js-stack-frame-filename> furthermore is "free form", often using a 1.327 + // "uri -> uri -> ..." chain. The following code will and must handle this 1.328 + // common case. 1.329 + // It should be noted that other parts of the code may already rely on the 1.330 + // "format" of these strings, such as the add-on SDK. 1.331 + 1.332 + static const nsDependentCString from("(from: "); 1.333 + static const nsDependentCString arrow(" -> "); 1.334 + static const size_t fromLength = from.Length(); 1.335 + static const size_t arrowLength = arrow.Length(); 1.336 + 1.337 + // See: XPCComponents.cpp#AssembleSandboxMemoryReporterName 1.338 + int32_t idx = location.Find(from); 1.339 + if (idx < 0) 1.340 + return TryParseLocationURICandidate(location, aLocationHint, aURI); 1.341 + 1.342 + 1.343 + // When parsing we're looking for the right-most URI. This URI may be in 1.344 + // <sandboxName>, so we try this first. 1.345 + if (TryParseLocationURICandidate(Substring(location, 0, idx), aLocationHint, 1.346 + aURI)) 1.347 + return true; 1.348 + 1.349 + // Not in <sandboxName> so we need to inspect <js-stack-frame-filename> and 1.350 + // the chain that is potentially contained within and grab the rightmost 1.351 + // item that is actually a URI. 1.352 + 1.353 + // First, hack off the :<lineno>) part as well 1.354 + int32_t ridx = location.RFind(NS_LITERAL_CSTRING(":")); 1.355 + nsAutoCString chain(Substring(location, idx + fromLength, 1.356 + ridx - idx - fromLength)); 1.357 + 1.358 + // Loop over the "->" chain. This loop also works for non-chains, or more 1.359 + // correctly chains with only one item. 1.360 + for (;;) { 1.361 + idx = chain.RFind(arrow); 1.362 + if (idx < 0) { 1.363 + // This is the last chain item. Try to parse what is left. 1.364 + return TryParseLocationURICandidate(chain, aLocationHint, aURI); 1.365 + } 1.366 + 1.367 + // Try to parse current chain item 1.368 + if (TryParseLocationURICandidate(Substring(chain, idx + arrowLength), 1.369 + aLocationHint, aURI)) 1.370 + return true; 1.371 + 1.372 + // Current chain item couldn't be parsed. 1.373 + // Strip current item and continue. 1.374 + chain = Substring(chain, 0, idx); 1.375 + } 1.376 + MOZ_ASSUME_UNREACHABLE("Chain parser loop does not terminate"); 1.377 +} 1.378 + 1.379 +CompartmentPrivate* 1.380 +EnsureCompartmentPrivate(JSObject *obj) 1.381 +{ 1.382 + return EnsureCompartmentPrivate(js::GetObjectCompartment(obj)); 1.383 +} 1.384 + 1.385 +CompartmentPrivate* 1.386 +EnsureCompartmentPrivate(JSCompartment *c) 1.387 +{ 1.388 + CompartmentPrivate *priv = GetCompartmentPrivate(c); 1.389 + if (priv) 1.390 + return priv; 1.391 + priv = new CompartmentPrivate(c); 1.392 + JS_SetCompartmentPrivate(c, priv); 1.393 + return priv; 1.394 +} 1.395 + 1.396 +XPCWrappedNativeScope* 1.397 +MaybeGetObjectScope(JSObject *obj) 1.398 +{ 1.399 + MOZ_ASSERT(obj); 1.400 + JSCompartment *compartment = js::GetObjectCompartment(obj); 1.401 + 1.402 + MOZ_ASSERT(compartment); 1.403 + CompartmentPrivate *priv = GetCompartmentPrivate(compartment); 1.404 + if (!priv) 1.405 + return nullptr; 1.406 + 1.407 + return priv->scope; 1.408 +} 1.409 + 1.410 +static bool 1.411 +PrincipalImmuneToScriptPolicy(nsIPrincipal* aPrincipal) 1.412 +{ 1.413 + // System principal gets a free pass. 1.414 + if (XPCWrapper::GetSecurityManager()->IsSystemPrincipal(aPrincipal)) 1.415 + return true; 1.416 + 1.417 + // nsExpandedPrincipal gets a free pass. 1.418 + nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal); 1.419 + if (ep) 1.420 + return true; 1.421 + 1.422 + // Check whether our URI is an "about:" URI that allows scripts. If it is, 1.423 + // we need to allow JS to run. 1.424 + nsCOMPtr<nsIURI> principalURI; 1.425 + aPrincipal->GetURI(getter_AddRefs(principalURI)); 1.426 + MOZ_ASSERT(principalURI); 1.427 + bool isAbout; 1.428 + nsresult rv = principalURI->SchemeIs("about", &isAbout); 1.429 + if (NS_SUCCEEDED(rv) && isAbout) { 1.430 + nsCOMPtr<nsIAboutModule> module; 1.431 + rv = NS_GetAboutModule(principalURI, getter_AddRefs(module)); 1.432 + if (NS_SUCCEEDED(rv)) { 1.433 + uint32_t flags; 1.434 + rv = module->GetURIFlags(principalURI, &flags); 1.435 + if (NS_SUCCEEDED(rv) && 1.436 + (flags & nsIAboutModule::ALLOW_SCRIPT)) { 1.437 + return true; 1.438 + } 1.439 + } 1.440 + } 1.441 + 1.442 + return false; 1.443 +} 1.444 + 1.445 +Scriptability::Scriptability(JSCompartment *c) : mScriptBlocks(0) 1.446 + , mDocShellAllowsScript(true) 1.447 + , mScriptBlockedByPolicy(false) 1.448 +{ 1.449 + nsIPrincipal *prin = nsJSPrincipals::get(JS_GetCompartmentPrincipals(c)); 1.450 + mImmuneToScriptPolicy = PrincipalImmuneToScriptPolicy(prin); 1.451 + 1.452 + // If we're not immune, we should have a real principal with a codebase URI. 1.453 + // Check the URI against the new-style domain policy. 1.454 + if (!mImmuneToScriptPolicy) { 1.455 + nsIScriptSecurityManager* ssm = XPCWrapper::GetSecurityManager(); 1.456 + nsCOMPtr<nsIURI> codebase; 1.457 + nsresult rv = prin->GetURI(getter_AddRefs(codebase)); 1.458 + bool policyAllows; 1.459 + if (NS_SUCCEEDED(rv) && codebase && 1.460 + NS_SUCCEEDED(ssm->PolicyAllowsScript(codebase, &policyAllows))) 1.461 + { 1.462 + mScriptBlockedByPolicy = !policyAllows; 1.463 + } else { 1.464 + // Something went wrong - be safe and block script. 1.465 + mScriptBlockedByPolicy = true; 1.466 + } 1.467 + } 1.468 +} 1.469 + 1.470 +bool 1.471 +Scriptability::Allowed() 1.472 +{ 1.473 + return mDocShellAllowsScript && !mScriptBlockedByPolicy && 1.474 + mScriptBlocks == 0; 1.475 +} 1.476 + 1.477 +bool 1.478 +Scriptability::IsImmuneToScriptPolicy() 1.479 +{ 1.480 + return mImmuneToScriptPolicy; 1.481 +} 1.482 + 1.483 +void 1.484 +Scriptability::Block() 1.485 +{ 1.486 + ++mScriptBlocks; 1.487 +} 1.488 + 1.489 +void 1.490 +Scriptability::Unblock() 1.491 +{ 1.492 + MOZ_ASSERT(mScriptBlocks > 0); 1.493 + --mScriptBlocks; 1.494 +} 1.495 + 1.496 +void 1.497 +Scriptability::SetDocShellAllowsScript(bool aAllowed) 1.498 +{ 1.499 + mDocShellAllowsScript = aAllowed || mImmuneToScriptPolicy; 1.500 +} 1.501 + 1.502 +/* static */ 1.503 +Scriptability& 1.504 +Scriptability::Get(JSObject *aScope) 1.505 +{ 1.506 + return EnsureCompartmentPrivate(aScope)->scriptability; 1.507 +} 1.508 + 1.509 +bool 1.510 +IsXBLScope(JSCompartment *compartment) 1.511 +{ 1.512 + // We always eagerly create compartment privates for XBL scopes. 1.513 + CompartmentPrivate *priv = GetCompartmentPrivate(compartment); 1.514 + if (!priv || !priv->scope) 1.515 + return false; 1.516 + return priv->scope->IsXBLScope(); 1.517 +} 1.518 + 1.519 +bool 1.520 +IsInXBLScope(JSObject *obj) 1.521 +{ 1.522 + return IsXBLScope(js::GetObjectCompartment(obj)); 1.523 +} 1.524 + 1.525 +bool 1.526 +IsUniversalXPConnectEnabled(JSCompartment *compartment) 1.527 +{ 1.528 + CompartmentPrivate *priv = GetCompartmentPrivate(compartment); 1.529 + if (!priv) 1.530 + return false; 1.531 + return priv->universalXPConnectEnabled; 1.532 +} 1.533 + 1.534 +bool 1.535 +IsUniversalXPConnectEnabled(JSContext *cx) 1.536 +{ 1.537 + JSCompartment *compartment = js::GetContextCompartment(cx); 1.538 + if (!compartment) 1.539 + return false; 1.540 + return IsUniversalXPConnectEnabled(compartment); 1.541 +} 1.542 + 1.543 +bool 1.544 +EnableUniversalXPConnect(JSContext *cx) 1.545 +{ 1.546 + JSCompartment *compartment = js::GetContextCompartment(cx); 1.547 + if (!compartment) 1.548 + return true; 1.549 + // Never set universalXPConnectEnabled on a chrome compartment - it confuses 1.550 + // the security wrapping code. 1.551 + if (AccessCheck::isChrome(compartment)) 1.552 + return true; 1.553 + CompartmentPrivate *priv = GetCompartmentPrivate(compartment); 1.554 + if (!priv) 1.555 + return true; 1.556 + priv->universalXPConnectEnabled = true; 1.557 + 1.558 + // Recompute all the cross-compartment wrappers leaving the newly-privileged 1.559 + // compartment. 1.560 + bool ok = js::RecomputeWrappers(cx, js::SingleCompartment(compartment), 1.561 + js::AllCompartments()); 1.562 + NS_ENSURE_TRUE(ok, false); 1.563 + 1.564 + // The Components object normally isn't defined for unprivileged web content, 1.565 + // but we define it when UniversalXPConnect is enabled to support legacy 1.566 + // tests. 1.567 + XPCWrappedNativeScope *scope = priv->scope; 1.568 + if (!scope) 1.569 + return true; 1.570 + scope->ForcePrivilegedComponents(); 1.571 + return scope->AttachComponentsObject(cx); 1.572 +} 1.573 + 1.574 +JSObject * 1.575 +GetJunkScope() 1.576 +{ 1.577 + XPCJSRuntime *self = nsXPConnect::GetRuntimeInstance(); 1.578 + NS_ENSURE_TRUE(self, nullptr); 1.579 + return self->GetJunkScope(); 1.580 +} 1.581 + 1.582 +nsIGlobalObject * 1.583 +GetJunkScopeGlobal() 1.584 +{ 1.585 + JSObject *junkScope = GetJunkScope(); 1.586 + // GetJunkScope would ideally never fail, currently it is not yet the case 1.587 + // unfortunately...(see Bug 874158) 1.588 + if (!junkScope) 1.589 + return nullptr; 1.590 + return GetNativeForGlobal(junkScope); 1.591 +} 1.592 + 1.593 +JSObject * 1.594 +GetCompilationScope() 1.595 +{ 1.596 + XPCJSRuntime *self = nsXPConnect::GetRuntimeInstance(); 1.597 + NS_ENSURE_TRUE(self, nullptr); 1.598 + return self->GetCompilationScope(); 1.599 +} 1.600 + 1.601 +JSObject * 1.602 +GetSafeJSContextGlobal() 1.603 +{ 1.604 + return XPCJSRuntime::Get()->GetJSContextStack()->GetSafeJSContextGlobal(); 1.605 +} 1.606 + 1.607 +nsGlobalWindow* 1.608 +WindowOrNull(JSObject *aObj) 1.609 +{ 1.610 + MOZ_ASSERT(aObj); 1.611 + MOZ_ASSERT(!js::IsWrapper(aObj)); 1.612 + 1.613 + // This will always return null until we have Window on WebIDL bindings, 1.614 + // at which point it will do the right thing. 1.615 + if (!IS_WN_CLASS(js::GetObjectClass(aObj))) { 1.616 + nsGlobalWindow* win = nullptr; 1.617 + UNWRAP_OBJECT(Window, aObj, win); 1.618 + return win; 1.619 + } 1.620 + 1.621 + nsISupports* supports = XPCWrappedNative::Get(aObj)->GetIdentityObject(); 1.622 + nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(supports); 1.623 + if (!piWin) 1.624 + return nullptr; 1.625 + return static_cast<nsGlobalWindow*>(piWin.get()); 1.626 +} 1.627 + 1.628 +nsGlobalWindow* 1.629 +WindowGlobalOrNull(JSObject *aObj) 1.630 +{ 1.631 + MOZ_ASSERT(aObj); 1.632 + JSObject *glob = js::GetGlobalForObjectCrossCompartment(aObj); 1.633 + 1.634 + return WindowOrNull(glob); 1.635 +} 1.636 + 1.637 +} 1.638 + 1.639 +static void 1.640 +CompartmentDestroyedCallback(JSFreeOp *fop, JSCompartment *compartment) 1.641 +{ 1.642 + // NB - This callback may be called in JS_DestroyRuntime, which happens 1.643 + // after the XPCJSRuntime has been torn down. 1.644 + 1.645 + // Get the current compartment private into an AutoPtr (which will do the 1.646 + // cleanup for us), and null out the private (which may already be null). 1.647 + nsAutoPtr<CompartmentPrivate> priv(GetCompartmentPrivate(compartment)); 1.648 + JS_SetCompartmentPrivate(compartment, nullptr); 1.649 +} 1.650 + 1.651 +void XPCJSRuntime::TraceNativeBlackRoots(JSTracer* trc) 1.652 +{ 1.653 + // Skip this part if XPConnect is shutting down. We get into 1.654 + // bad locking problems with the thread iteration otherwise. 1.655 + if (!nsXPConnect::XPConnect()->IsShuttingDown()) { 1.656 + // Trace those AutoMarkingPtr lists! 1.657 + if (AutoMarkingPtr *roots = Get()->mAutoRoots) 1.658 + roots->TraceJSAll(trc); 1.659 + } 1.660 + 1.661 + // XPCJSObjectHolders don't participate in cycle collection, so always 1.662 + // trace them here. 1.663 + XPCRootSetElem *e; 1.664 + for (e = mObjectHolderRoots; e; e = e->GetNextRoot()) 1.665 + static_cast<XPCJSObjectHolder*>(e)->TraceJS(trc); 1.666 + 1.667 + dom::TraceBlackJS(trc, JS_GetGCParameter(Runtime(), JSGC_NUMBER), 1.668 + nsXPConnect::XPConnect()->IsShuttingDown()); 1.669 +} 1.670 + 1.671 +void XPCJSRuntime::TraceAdditionalNativeGrayRoots(JSTracer *trc) 1.672 +{ 1.673 + XPCWrappedNativeScope::TraceWrappedNativesInAllScopes(trc, this); 1.674 + 1.675 + for (XPCRootSetElem *e = mVariantRoots; e ; e = e->GetNextRoot()) 1.676 + static_cast<XPCTraceableVariant*>(e)->TraceJS(trc); 1.677 + 1.678 + for (XPCRootSetElem *e = mWrappedJSRoots; e ; e = e->GetNextRoot()) 1.679 + static_cast<nsXPCWrappedJS*>(e)->TraceJS(trc); 1.680 +} 1.681 + 1.682 +// static 1.683 +void 1.684 +XPCJSRuntime::SuspectWrappedNative(XPCWrappedNative *wrapper, 1.685 + nsCycleCollectionNoteRootCallback &cb) 1.686 +{ 1.687 + if (!wrapper->IsValid() || wrapper->IsWrapperExpired()) 1.688 + return; 1.689 + 1.690 + MOZ_ASSERT(NS_IsMainThread(), 1.691 + "Suspecting wrapped natives from non-main thread"); 1.692 + 1.693 + // Only record objects that might be part of a cycle as roots, unless 1.694 + // the callback wants all traces (a debug feature). 1.695 + JSObject* obj = wrapper->GetFlatJSObjectPreserveColor(); 1.696 + if (xpc_IsGrayGCThing(obj) || cb.WantAllTraces()) 1.697 + cb.NoteJSRoot(obj); 1.698 +} 1.699 + 1.700 +void 1.701 +XPCJSRuntime::TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback &cb) 1.702 +{ 1.703 + XPCWrappedNativeScope::SuspectAllWrappers(this, cb); 1.704 + 1.705 + for (XPCRootSetElem *e = mVariantRoots; e ; e = e->GetNextRoot()) { 1.706 + XPCTraceableVariant* v = static_cast<XPCTraceableVariant*>(e); 1.707 + if (nsCCUncollectableMarker::InGeneration(cb, 1.708 + v->CCGeneration())) { 1.709 + jsval val = v->GetJSValPreserveColor(); 1.710 + if (val.isObject() && !xpc_IsGrayGCThing(&val.toObject())) 1.711 + continue; 1.712 + } 1.713 + cb.NoteXPCOMRoot(v); 1.714 + } 1.715 + 1.716 + for (XPCRootSetElem *e = mWrappedJSRoots; e ; e = e->GetNextRoot()) { 1.717 + cb.NoteXPCOMRoot(ToSupports(static_cast<nsXPCWrappedJS*>(e))); 1.718 + } 1.719 +} 1.720 + 1.721 +void 1.722 +XPCJSRuntime::UnmarkSkippableJSHolders() 1.723 +{ 1.724 + CycleCollectedJSRuntime::UnmarkSkippableJSHolders(); 1.725 +} 1.726 + 1.727 +void 1.728 +XPCJSRuntime::PrepareForForgetSkippable() 1.729 +{ 1.730 + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 1.731 + if (obs) { 1.732 + obs->NotifyObservers(nullptr, "cycle-collector-forget-skippable", nullptr); 1.733 + } 1.734 +} 1.735 + 1.736 +void 1.737 +XPCJSRuntime::BeginCycleCollectionCallback() 1.738 +{ 1.739 + nsJSContext::BeginCycleCollectionCallback(); 1.740 + 1.741 + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 1.742 + if (obs) { 1.743 + obs->NotifyObservers(nullptr, "cycle-collector-begin", nullptr); 1.744 + } 1.745 +} 1.746 + 1.747 +void 1.748 +XPCJSRuntime::EndCycleCollectionCallback(CycleCollectorResults &aResults) 1.749 +{ 1.750 + nsJSContext::EndCycleCollectionCallback(aResults); 1.751 + 1.752 + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 1.753 + if (obs) { 1.754 + obs->NotifyObservers(nullptr, "cycle-collector-end", nullptr); 1.755 + } 1.756 +} 1.757 + 1.758 +void 1.759 +XPCJSRuntime::DispatchDeferredDeletion(bool aContinuation) 1.760 +{ 1.761 + mAsyncSnowWhiteFreer->Dispatch(aContinuation); 1.762 +} 1.763 + 1.764 +void 1.765 +xpc_UnmarkSkippableJSHolders() 1.766 +{ 1.767 + if (nsXPConnect::XPConnect()->GetRuntime()) { 1.768 + nsXPConnect::XPConnect()->GetRuntime()->UnmarkSkippableJSHolders(); 1.769 + } 1.770 +} 1.771 + 1.772 +template<class T> static void 1.773 +DoDeferredRelease(nsTArray<T> &array) 1.774 +{ 1.775 + while (1) { 1.776 + uint32_t count = array.Length(); 1.777 + if (!count) { 1.778 + array.Compact(); 1.779 + break; 1.780 + } 1.781 + T wrapper = array[count-1]; 1.782 + array.RemoveElementAt(count-1); 1.783 + NS_RELEASE(wrapper); 1.784 + } 1.785 +} 1.786 + 1.787 +/* static */ void 1.788 +XPCJSRuntime::GCSliceCallback(JSRuntime *rt, 1.789 + JS::GCProgress progress, 1.790 + const JS::GCDescription &desc) 1.791 +{ 1.792 + XPCJSRuntime *self = nsXPConnect::GetRuntimeInstance(); 1.793 + if (!self) 1.794 + return; 1.795 + 1.796 +#ifdef MOZ_CRASHREPORTER 1.797 + CrashReporter::SetGarbageCollecting(progress == JS::GC_CYCLE_BEGIN || 1.798 + progress == JS::GC_SLICE_BEGIN); 1.799 +#endif 1.800 + 1.801 + if (self->mPrevGCSliceCallback) 1.802 + (*self->mPrevGCSliceCallback)(rt, progress, desc); 1.803 +} 1.804 + 1.805 +void 1.806 +XPCJSRuntime::CustomGCCallback(JSGCStatus status) 1.807 +{ 1.808 + // Record kLivingAdopters once per GC to approximate time sampling during 1.809 + // periods of activity. 1.810 + if (status == JSGC_BEGIN) { 1.811 + Telemetry::Accumulate(Telemetry::COMPARTMENT_LIVING_ADOPTERS, 1.812 + kLivingAdopters); 1.813 + } 1.814 + 1.815 + nsTArray<xpcGCCallback> callbacks(extraGCCallbacks); 1.816 + for (uint32_t i = 0; i < callbacks.Length(); ++i) 1.817 + callbacks[i](status); 1.818 +} 1.819 + 1.820 +/* static */ void 1.821 +XPCJSRuntime::FinalizeCallback(JSFreeOp *fop, JSFinalizeStatus status, bool isCompartmentGC) 1.822 +{ 1.823 + XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance(); 1.824 + if (!self) 1.825 + return; 1.826 + 1.827 + switch (status) { 1.828 + case JSFINALIZE_GROUP_START: 1.829 + { 1.830 + MOZ_ASSERT(!self->mDoingFinalization, "bad state"); 1.831 + 1.832 + MOZ_ASSERT(!self->mGCIsRunning, "bad state"); 1.833 + self->mGCIsRunning = true; 1.834 + 1.835 + nsTArray<nsXPCWrappedJS*>* dyingWrappedJSArray = 1.836 + &self->mWrappedJSToReleaseArray; 1.837 + 1.838 + // Add any wrappers whose JSObjects are to be finalized to 1.839 + // this array. Note that we do not want to be changing the 1.840 + // refcount of these wrappers. 1.841 + // We add them to the array now and Release the array members 1.842 + // later to avoid the posibility of doing any JS GCThing 1.843 + // allocations during the gc cycle. 1.844 + self->mWrappedJSMap->FindDyingJSObjects(dyingWrappedJSArray); 1.845 + 1.846 + // Find dying scopes. 1.847 + XPCWrappedNativeScope::StartFinalizationPhaseOfGC(fop, self); 1.848 + 1.849 + self->mDoingFinalization = true; 1.850 + break; 1.851 + } 1.852 + case JSFINALIZE_GROUP_END: 1.853 + { 1.854 + MOZ_ASSERT(self->mDoingFinalization, "bad state"); 1.855 + self->mDoingFinalization = false; 1.856 + 1.857 + // Release all the members whose JSObjects are now known 1.858 + // to be dead. 1.859 + DoDeferredRelease(self->mWrappedJSToReleaseArray); 1.860 + 1.861 + // Sweep scopes needing cleanup 1.862 + XPCWrappedNativeScope::FinishedFinalizationPhaseOfGC(); 1.863 + 1.864 + MOZ_ASSERT(self->mGCIsRunning, "bad state"); 1.865 + self->mGCIsRunning = false; 1.866 + 1.867 + break; 1.868 + } 1.869 + case JSFINALIZE_COLLECTION_END: 1.870 + { 1.871 + MOZ_ASSERT(!self->mGCIsRunning, "bad state"); 1.872 + self->mGCIsRunning = true; 1.873 + 1.874 + // We use this occasion to mark and sweep NativeInterfaces, 1.875 + // NativeSets, and the WrappedNativeJSClasses... 1.876 + 1.877 + // Do the marking... 1.878 + XPCWrappedNativeScope::MarkAllWrappedNativesAndProtos(); 1.879 + 1.880 + self->mDetachedWrappedNativeProtoMap-> 1.881 + Enumerate(DetachedWrappedNativeProtoMarker, nullptr); 1.882 + 1.883 + DOM_MarkInterfaces(); 1.884 + 1.885 + // Mark the sets used in the call contexts. There is a small 1.886 + // chance that a wrapper's set will change *while* a call is 1.887 + // happening which uses that wrapper's old interfface set. So, 1.888 + // we need to do this marking to avoid collecting those sets 1.889 + // that might no longer be otherwise reachable from the wrappers 1.890 + // or the wrapperprotos. 1.891 + 1.892 + // Skip this part if XPConnect is shutting down. We get into 1.893 + // bad locking problems with the thread iteration otherwise. 1.894 + if (!nsXPConnect::XPConnect()->IsShuttingDown()) { 1.895 + 1.896 + // Mark those AutoMarkingPtr lists! 1.897 + if (AutoMarkingPtr *roots = Get()->mAutoRoots) 1.898 + roots->MarkAfterJSFinalizeAll(); 1.899 + 1.900 + XPCCallContext* ccxp = XPCJSRuntime::Get()->GetCallContext(); 1.901 + while (ccxp) { 1.902 + // Deal with the strictness of callcontext that 1.903 + // complains if you ask for a set when 1.904 + // it is in a state where the set could not 1.905 + // possibly be valid. 1.906 + if (ccxp->CanGetSet()) { 1.907 + XPCNativeSet* set = ccxp->GetSet(); 1.908 + if (set) 1.909 + set->Mark(); 1.910 + } 1.911 + if (ccxp->CanGetInterface()) { 1.912 + XPCNativeInterface* iface = ccxp->GetInterface(); 1.913 + if (iface) 1.914 + iface->Mark(); 1.915 + } 1.916 + ccxp = ccxp->GetPrevCallContext(); 1.917 + } 1.918 + } 1.919 + 1.920 + // Do the sweeping. During a compartment GC, only 1.921 + // WrappedNativeProtos in collected compartments will be 1.922 + // marked. Therefore, some reachable NativeInterfaces will not be 1.923 + // marked, so it is not safe to sweep them. We still need to unmark 1.924 + // them, since the ones pointed to by WrappedNativeProtos in a 1.925 + // compartment being collected will be marked. 1.926 + // 1.927 + // Ideally, if NativeInterfaces from different compartments were 1.928 + // kept separate, we could sweep only the ones belonging to 1.929 + // compartments being collected. Currently, though, NativeInterfaces 1.930 + // are shared between compartments. This ought to be fixed. 1.931 + void *sweepArg = isCompartmentGC ? UNMARK_ONLY : UNMARK_AND_SWEEP; 1.932 + 1.933 + // We don't want to sweep the JSClasses at shutdown time. 1.934 + // At this point there may be JSObjects using them that have 1.935 + // been removed from the other maps. 1.936 + if (!nsXPConnect::XPConnect()->IsShuttingDown()) { 1.937 + self->mNativeScriptableSharedMap-> 1.938 + Enumerate(JSClassSweeper, sweepArg); 1.939 + } 1.940 + 1.941 + if (!isCompartmentGC) { 1.942 + self->mClassInfo2NativeSetMap-> 1.943 + Enumerate(NativeUnMarkedSetRemover, nullptr); 1.944 + } 1.945 + 1.946 + self->mNativeSetMap-> 1.947 + Enumerate(NativeSetSweeper, sweepArg); 1.948 + 1.949 + self->mIID2NativeInterfaceMap-> 1.950 + Enumerate(NativeInterfaceSweeper, sweepArg); 1.951 + 1.952 +#ifdef DEBUG 1.953 + XPCWrappedNativeScope::ASSERT_NoInterfaceSetsAreMarked(); 1.954 +#endif 1.955 + 1.956 + // Now we are going to recycle any unused WrappedNativeTearoffs. 1.957 + // We do this by iterating all the live callcontexts 1.958 + // and marking the tearoffs in use. And then we 1.959 + // iterate over all the WrappedNative wrappers and sweep their 1.960 + // tearoffs. 1.961 + // 1.962 + // This allows us to perhaps minimize the growth of the 1.963 + // tearoffs. And also makes us not hold references to interfaces 1.964 + // on our wrapped natives that we are not actually using. 1.965 + // 1.966 + // XXX We may decide to not do this on *every* gc cycle. 1.967 + 1.968 + // Skip this part if XPConnect is shutting down. We get into 1.969 + // bad locking problems with the thread iteration otherwise. 1.970 + if (!nsXPConnect::XPConnect()->IsShuttingDown()) { 1.971 + // Do the marking... 1.972 + 1.973 + XPCCallContext* ccxp = XPCJSRuntime::Get()->GetCallContext(); 1.974 + while (ccxp) { 1.975 + // Deal with the strictness of callcontext that 1.976 + // complains if you ask for a tearoff when 1.977 + // it is in a state where the tearoff could not 1.978 + // possibly be valid. 1.979 + if (ccxp->CanGetTearOff()) { 1.980 + XPCWrappedNativeTearOff* to = 1.981 + ccxp->GetTearOff(); 1.982 + if (to) 1.983 + to->Mark(); 1.984 + } 1.985 + ccxp = ccxp->GetPrevCallContext(); 1.986 + } 1.987 + 1.988 + // Do the sweeping... 1.989 + XPCWrappedNativeScope::SweepAllWrappedNativeTearOffs(); 1.990 + } 1.991 + 1.992 + // Now we need to kill the 'Dying' XPCWrappedNativeProtos. 1.993 + // We transfered these native objects to this table when their 1.994 + // JSObject's were finalized. We did not destroy them immediately 1.995 + // at that point because the ordering of JS finalization is not 1.996 + // deterministic and we did not yet know if any wrappers that 1.997 + // might still be referencing the protos where still yet to be 1.998 + // finalized and destroyed. We *do* know that the protos' 1.999 + // JSObjects would not have been finalized if there were any 1.1000 + // wrappers that referenced the proto but where not themselves 1.1001 + // slated for finalization in this gc cycle. So... at this point 1.1002 + // we know that any and all wrappers that might have been 1.1003 + // referencing the protos in the dying list are themselves dead. 1.1004 + // So, we can safely delete all the protos in the list. 1.1005 + 1.1006 + self->mDyingWrappedNativeProtoMap-> 1.1007 + Enumerate(DyingProtoKiller, nullptr); 1.1008 + 1.1009 + MOZ_ASSERT(self->mGCIsRunning, "bad state"); 1.1010 + self->mGCIsRunning = false; 1.1011 + 1.1012 + break; 1.1013 + } 1.1014 + } 1.1015 +} 1.1016 + 1.1017 +static void WatchdogMain(void *arg); 1.1018 +class Watchdog; 1.1019 +class WatchdogManager; 1.1020 +class AutoLockWatchdog { 1.1021 + Watchdog* const mWatchdog; 1.1022 + public: 1.1023 + AutoLockWatchdog(Watchdog* aWatchdog); 1.1024 + ~AutoLockWatchdog(); 1.1025 +}; 1.1026 + 1.1027 +class Watchdog 1.1028 +{ 1.1029 + public: 1.1030 + Watchdog(WatchdogManager *aManager) 1.1031 + : mManager(aManager) 1.1032 + , mLock(nullptr) 1.1033 + , mWakeup(nullptr) 1.1034 + , mThread(nullptr) 1.1035 + , mHibernating(false) 1.1036 + , mInitialized(false) 1.1037 + , mShuttingDown(false) 1.1038 + {} 1.1039 + ~Watchdog() { MOZ_ASSERT(!Initialized()); } 1.1040 + 1.1041 + WatchdogManager* Manager() { return mManager; } 1.1042 + bool Initialized() { return mInitialized; } 1.1043 + bool ShuttingDown() { return mShuttingDown; } 1.1044 + PRLock *GetLock() { return mLock; } 1.1045 + bool Hibernating() { return mHibernating; } 1.1046 + void WakeUp() 1.1047 + { 1.1048 + MOZ_ASSERT(Initialized()); 1.1049 + MOZ_ASSERT(Hibernating()); 1.1050 + mHibernating = false; 1.1051 + PR_NotifyCondVar(mWakeup); 1.1052 + } 1.1053 + 1.1054 + // 1.1055 + // Invoked by the main thread only. 1.1056 + // 1.1057 + 1.1058 + void Init() 1.1059 + { 1.1060 + MOZ_ASSERT(NS_IsMainThread()); 1.1061 + mLock = PR_NewLock(); 1.1062 + if (!mLock) 1.1063 + NS_RUNTIMEABORT("PR_NewLock failed."); 1.1064 + mWakeup = PR_NewCondVar(mLock); 1.1065 + if (!mWakeup) 1.1066 + NS_RUNTIMEABORT("PR_NewCondVar failed."); 1.1067 + 1.1068 + { 1.1069 + AutoLockWatchdog lock(this); 1.1070 + 1.1071 + mThread = PR_CreateThread(PR_USER_THREAD, WatchdogMain, this, 1.1072 + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, 1.1073 + PR_UNJOINABLE_THREAD, 0); 1.1074 + if (!mThread) 1.1075 + NS_RUNTIMEABORT("PR_CreateThread failed!"); 1.1076 + 1.1077 + // WatchdogMain acquires the lock and then asserts mInitialized. So 1.1078 + // make sure to set mInitialized before releasing the lock here so 1.1079 + // that it's atomic with the creation of the thread. 1.1080 + mInitialized = true; 1.1081 + } 1.1082 + } 1.1083 + 1.1084 + void Shutdown() 1.1085 + { 1.1086 + MOZ_ASSERT(NS_IsMainThread()); 1.1087 + MOZ_ASSERT(Initialized()); 1.1088 + { // Scoped lock. 1.1089 + AutoLockWatchdog lock(this); 1.1090 + 1.1091 + // Signal to the watchdog thread that it's time to shut down. 1.1092 + mShuttingDown = true; 1.1093 + 1.1094 + // Wake up the watchdog, and wait for it to call us back. 1.1095 + PR_NotifyCondVar(mWakeup); 1.1096 + PR_WaitCondVar(mWakeup, PR_INTERVAL_NO_TIMEOUT); 1.1097 + MOZ_ASSERT(!mShuttingDown); 1.1098 + } 1.1099 + 1.1100 + // Destroy state. 1.1101 + mThread = nullptr; 1.1102 + PR_DestroyCondVar(mWakeup); 1.1103 + mWakeup = nullptr; 1.1104 + PR_DestroyLock(mLock); 1.1105 + mLock = nullptr; 1.1106 + 1.1107 + // All done. 1.1108 + mInitialized = false; 1.1109 + } 1.1110 + 1.1111 + // 1.1112 + // Invoked by the watchdog thread only. 1.1113 + // 1.1114 + 1.1115 + void Hibernate() 1.1116 + { 1.1117 + MOZ_ASSERT(!NS_IsMainThread()); 1.1118 + mHibernating = true; 1.1119 + Sleep(PR_INTERVAL_NO_TIMEOUT); 1.1120 + } 1.1121 + void Sleep(PRIntervalTime timeout) 1.1122 + { 1.1123 + MOZ_ASSERT(!NS_IsMainThread()); 1.1124 + MOZ_ALWAYS_TRUE(PR_WaitCondVar(mWakeup, timeout) == PR_SUCCESS); 1.1125 + } 1.1126 + void Finished() 1.1127 + { 1.1128 + MOZ_ASSERT(!NS_IsMainThread()); 1.1129 + mShuttingDown = false; 1.1130 + PR_NotifyCondVar(mWakeup); 1.1131 + } 1.1132 + 1.1133 + private: 1.1134 + WatchdogManager *mManager; 1.1135 + 1.1136 + PRLock *mLock; 1.1137 + PRCondVar *mWakeup; 1.1138 + PRThread *mThread; 1.1139 + bool mHibernating; 1.1140 + bool mInitialized; 1.1141 + bool mShuttingDown; 1.1142 +}; 1.1143 + 1.1144 +#ifdef MOZ_NUWA_PROCESS 1.1145 +#include "ipc/Nuwa.h" 1.1146 +#endif 1.1147 + 1.1148 +class WatchdogManager : public nsIObserver 1.1149 +{ 1.1150 + public: 1.1151 + 1.1152 + NS_DECL_ISUPPORTS 1.1153 + WatchdogManager(XPCJSRuntime *aRuntime) : mRuntime(aRuntime) 1.1154 + , mRuntimeState(RUNTIME_INACTIVE) 1.1155 + { 1.1156 + // All the timestamps start at zero except for runtime state change. 1.1157 + PodArrayZero(mTimestamps); 1.1158 + mTimestamps[TimestampRuntimeStateChange] = PR_Now(); 1.1159 + 1.1160 + // Enable the watchdog, if appropriate. 1.1161 + RefreshWatchdog(); 1.1162 + 1.1163 + // Register ourselves as an observer to get updates on the pref. 1.1164 + mozilla::Preferences::AddStrongObserver(this, "dom.use_watchdog"); 1.1165 + } 1.1166 + virtual ~WatchdogManager() 1.1167 + { 1.1168 + // Shutting down the watchdog requires context-switching to the watchdog 1.1169 + // thread, which isn't great to do in a destructor. So we require 1.1170 + // consumers to shut it down manually before releasing it. 1.1171 + MOZ_ASSERT(!mWatchdog); 1.1172 + mozilla::Preferences::RemoveObserver(this, "dom.use_watchdog"); 1.1173 + } 1.1174 + 1.1175 + NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, 1.1176 + const char16_t* aData) 1.1177 + { 1.1178 + RefreshWatchdog(); 1.1179 + return NS_OK; 1.1180 + } 1.1181 + 1.1182 + // Runtime statistics. These live on the watchdog manager, are written 1.1183 + // from the main thread, and are read from the watchdog thread (holding 1.1184 + // the lock in each case). 1.1185 + void 1.1186 + RecordRuntimeActivity(bool active) 1.1187 + { 1.1188 + // The watchdog reads this state, so acquire the lock before writing it. 1.1189 + MOZ_ASSERT(NS_IsMainThread()); 1.1190 + Maybe<AutoLockWatchdog> lock; 1.1191 + if (mWatchdog) 1.1192 + lock.construct(mWatchdog); 1.1193 + 1.1194 + // Write state. 1.1195 + mTimestamps[TimestampRuntimeStateChange] = PR_Now(); 1.1196 + mRuntimeState = active ? RUNTIME_ACTIVE : RUNTIME_INACTIVE; 1.1197 + 1.1198 + // The watchdog may be hibernating, waiting for the runtime to go 1.1199 + // active. Wake it up if necessary. 1.1200 + if (active && mWatchdog && mWatchdog->Hibernating()) 1.1201 + mWatchdog->WakeUp(); 1.1202 + } 1.1203 + bool IsRuntimeActive() { return mRuntimeState == RUNTIME_ACTIVE; } 1.1204 + PRTime TimeSinceLastRuntimeStateChange() 1.1205 + { 1.1206 + return PR_Now() - GetTimestamp(TimestampRuntimeStateChange); 1.1207 + } 1.1208 + 1.1209 + // Note - Because of the runtime activity timestamp, these are read and 1.1210 + // written from both threads. 1.1211 + void RecordTimestamp(WatchdogTimestampCategory aCategory) 1.1212 + { 1.1213 + // The watchdog thread always holds the lock when it runs. 1.1214 + Maybe<AutoLockWatchdog> maybeLock; 1.1215 + if (NS_IsMainThread() && mWatchdog) 1.1216 + maybeLock.construct(mWatchdog); 1.1217 + mTimestamps[aCategory] = PR_Now(); 1.1218 + } 1.1219 + PRTime GetTimestamp(WatchdogTimestampCategory aCategory) 1.1220 + { 1.1221 + // The watchdog thread always holds the lock when it runs. 1.1222 + Maybe<AutoLockWatchdog> maybeLock; 1.1223 + if (NS_IsMainThread() && mWatchdog) 1.1224 + maybeLock.construct(mWatchdog); 1.1225 + return mTimestamps[aCategory]; 1.1226 + } 1.1227 + 1.1228 + XPCJSRuntime* Runtime() { return mRuntime; } 1.1229 + Watchdog* GetWatchdog() { return mWatchdog; } 1.1230 + 1.1231 + void RefreshWatchdog() 1.1232 + { 1.1233 + bool wantWatchdog = Preferences::GetBool("dom.use_watchdog", true); 1.1234 + if (wantWatchdog == !!mWatchdog) 1.1235 + return; 1.1236 + if (wantWatchdog) 1.1237 + StartWatchdog(); 1.1238 + else 1.1239 + StopWatchdog(); 1.1240 + } 1.1241 + 1.1242 + void StartWatchdog() 1.1243 + { 1.1244 + MOZ_ASSERT(!mWatchdog); 1.1245 + mWatchdog = new Watchdog(this); 1.1246 + mWatchdog->Init(); 1.1247 + } 1.1248 + 1.1249 + void StopWatchdog() 1.1250 + { 1.1251 + MOZ_ASSERT(mWatchdog); 1.1252 + mWatchdog->Shutdown(); 1.1253 + mWatchdog = nullptr; 1.1254 + } 1.1255 + 1.1256 + private: 1.1257 + XPCJSRuntime *mRuntime; 1.1258 + nsAutoPtr<Watchdog> mWatchdog; 1.1259 + 1.1260 + enum { RUNTIME_ACTIVE, RUNTIME_INACTIVE } mRuntimeState; 1.1261 + PRTime mTimestamps[TimestampCount]; 1.1262 +}; 1.1263 + 1.1264 +NS_IMPL_ISUPPORTS(WatchdogManager, nsIObserver) 1.1265 + 1.1266 +AutoLockWatchdog::AutoLockWatchdog(Watchdog *aWatchdog) : mWatchdog(aWatchdog) 1.1267 +{ 1.1268 + PR_Lock(mWatchdog->GetLock()); 1.1269 +} 1.1270 + 1.1271 +AutoLockWatchdog::~AutoLockWatchdog() 1.1272 +{ 1.1273 + PR_Unlock(mWatchdog->GetLock()); 1.1274 +} 1.1275 + 1.1276 +static void 1.1277 +WatchdogMain(void *arg) 1.1278 +{ 1.1279 + PR_SetCurrentThreadName("JS Watchdog"); 1.1280 + 1.1281 +#ifdef MOZ_NUWA_PROCESS 1.1282 + if (IsNuwaProcess()) { 1.1283 + NS_ASSERTION(NuwaMarkCurrentThread != nullptr, 1.1284 + "NuwaMarkCurrentThread is undefined!"); 1.1285 + NuwaMarkCurrentThread(nullptr, nullptr); 1.1286 + NuwaFreezeCurrentThread(); 1.1287 + } 1.1288 +#endif 1.1289 + 1.1290 + Watchdog* self = static_cast<Watchdog*>(arg); 1.1291 + WatchdogManager* manager = self->Manager(); 1.1292 + 1.1293 + // Lock lasts until we return 1.1294 + AutoLockWatchdog lock(self); 1.1295 + 1.1296 + MOZ_ASSERT(self->Initialized()); 1.1297 + MOZ_ASSERT(!self->ShuttingDown()); 1.1298 + while (!self->ShuttingDown()) { 1.1299 + // Sleep only 1 second if recently (or currently) active; otherwise, hibernate 1.1300 + if (manager->IsRuntimeActive() || 1.1301 + manager->TimeSinceLastRuntimeStateChange() <= PRTime(2*PR_USEC_PER_SEC)) 1.1302 + { 1.1303 + self->Sleep(PR_TicksPerSecond()); 1.1304 + } else { 1.1305 + manager->RecordTimestamp(TimestampWatchdogHibernateStart); 1.1306 + self->Hibernate(); 1.1307 + manager->RecordTimestamp(TimestampWatchdogHibernateStop); 1.1308 + } 1.1309 + 1.1310 + // Rise and shine. 1.1311 + manager->RecordTimestamp(TimestampWatchdogWakeup); 1.1312 + 1.1313 + // Don't request an interrupt callback if activity started less than one second ago. 1.1314 + // The callback is only used for detecting long running scripts, and triggering the 1.1315 + // callback from off the main thread can be expensive. 1.1316 + if (manager->IsRuntimeActive() && 1.1317 + manager->TimeSinceLastRuntimeStateChange() >= PRTime(PR_USEC_PER_SEC)) 1.1318 + { 1.1319 + bool debuggerAttached = false; 1.1320 + nsCOMPtr<nsIDebug2> dbg = do_GetService("@mozilla.org/xpcom/debug;1"); 1.1321 + if (dbg) 1.1322 + dbg->GetIsDebuggerAttached(&debuggerAttached); 1.1323 + if (!debuggerAttached) 1.1324 + JS_RequestInterruptCallback(manager->Runtime()->Runtime()); 1.1325 + } 1.1326 + } 1.1327 + 1.1328 + // Tell the manager that we've shut down. 1.1329 + self->Finished(); 1.1330 +} 1.1331 + 1.1332 +PRTime 1.1333 +XPCJSRuntime::GetWatchdogTimestamp(WatchdogTimestampCategory aCategory) 1.1334 +{ 1.1335 + return mWatchdogManager->GetTimestamp(aCategory); 1.1336 +} 1.1337 + 1.1338 +void 1.1339 +xpc::SimulateActivityCallback(bool aActive) 1.1340 +{ 1.1341 + XPCJSRuntime::ActivityCallback(XPCJSRuntime::Get(), aActive); 1.1342 +} 1.1343 + 1.1344 +// static 1.1345 +JSContext* 1.1346 +XPCJSRuntime::DefaultJSContextCallback(JSRuntime *rt) 1.1347 +{ 1.1348 + MOZ_ASSERT(rt == Get()->Runtime()); 1.1349 + return Get()->GetJSContextStack()->GetSafeJSContext(); 1.1350 +} 1.1351 + 1.1352 +// static 1.1353 +void 1.1354 +XPCJSRuntime::ActivityCallback(void *arg, bool active) 1.1355 +{ 1.1356 + XPCJSRuntime* self = static_cast<XPCJSRuntime*>(arg); 1.1357 + self->mWatchdogManager->RecordRuntimeActivity(active); 1.1358 +} 1.1359 + 1.1360 +// static 1.1361 +// 1.1362 +// JS-CTypes creates and caches a JSContext that it uses when executing JS 1.1363 +// callbacks. When we're notified that ctypes is about to call into some JS, 1.1364 +// push the cx to maintain the integrity of the context stack. 1.1365 +void 1.1366 +XPCJSRuntime::CTypesActivityCallback(JSContext *cx, js::CTypesActivityType type) 1.1367 +{ 1.1368 + if (type == js::CTYPES_CALLBACK_BEGIN) { 1.1369 + if (!xpc::PushJSContextNoScriptContext(cx)) 1.1370 + MOZ_CRASH(); 1.1371 + } else if (type == js::CTYPES_CALLBACK_END) { 1.1372 + xpc::PopJSContextNoScriptContext(); 1.1373 + } 1.1374 +} 1.1375 + 1.1376 +// static 1.1377 +bool 1.1378 +XPCJSRuntime::InterruptCallback(JSContext *cx) 1.1379 +{ 1.1380 + XPCJSRuntime *self = XPCJSRuntime::Get(); 1.1381 + 1.1382 + // If this is the first time the interrupt callback has fired since we last 1.1383 + // returned to the event loop, mark the checkpoint. 1.1384 + if (self->mSlowScriptCheckpoint.IsNull()) { 1.1385 + self->mSlowScriptCheckpoint = TimeStamp::NowLoRes(); 1.1386 + return true; 1.1387 + } 1.1388 + 1.1389 + // This is at least the second interrupt callback we've received since 1.1390 + // returning to the event loop. See how long it's been, and what the limit 1.1391 + // is. 1.1392 + TimeDuration duration = TimeStamp::NowLoRes() - self->mSlowScriptCheckpoint; 1.1393 + bool chrome = 1.1394 + nsContentUtils::IsSystemPrincipal(nsContentUtils::GetSubjectPrincipal()); 1.1395 + const char *prefName = chrome ? "dom.max_chrome_script_run_time" 1.1396 + : "dom.max_script_run_time"; 1.1397 + int32_t limit = Preferences::GetInt(prefName, chrome ? 20 : 10); 1.1398 + 1.1399 + // If there's no limit, or we're within the limit, let it go. 1.1400 + if (limit == 0 || duration.ToSeconds() < limit) 1.1401 + return true; 1.1402 + 1.1403 + // 1.1404 + // This has gone on long enough! Time to take action. ;-) 1.1405 + // 1.1406 + 1.1407 + // Get the DOM window associated with the running script. If the script is 1.1408 + // running in a non-DOM scope, we have to just let it keep running. 1.1409 + RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); 1.1410 + nsRefPtr<nsGlobalWindow> win = WindowOrNull(global); 1.1411 + if (!win && IsSandbox(global)) { 1.1412 + // If this is a sandbox associated with a DOMWindow via a 1.1413 + // sandboxPrototype, use that DOMWindow. This supports GreaseMonkey 1.1414 + // and JetPack content scripts. 1.1415 + JS::Rooted<JSObject*> proto(cx); 1.1416 + if (!JS_GetPrototype(cx, global, &proto)) 1.1417 + return false; 1.1418 + if (proto && IsSandboxPrototypeProxy(proto) && 1.1419 + (proto = js::CheckedUnwrap(proto, /* stopAtOuter = */ false))) 1.1420 + { 1.1421 + win = WindowGlobalOrNull(proto); 1.1422 + } 1.1423 + } 1.1424 + if (!win) 1.1425 + return true; 1.1426 + 1.1427 + // Show the prompt to the user, and kill if requested. 1.1428 + nsGlobalWindow::SlowScriptResponse response = win->ShowSlowScriptDialog(); 1.1429 + if (response == nsGlobalWindow::KillSlowScript) 1.1430 + return false; 1.1431 + 1.1432 + // The user chose to continue the script. Reset the timer, and disable this 1.1433 + // machinery with a pref of the user opted out of future slow-script dialogs. 1.1434 + self->mSlowScriptCheckpoint = TimeStamp::NowLoRes(); 1.1435 + if (response == nsGlobalWindow::AlwaysContinueSlowScript) 1.1436 + Preferences::SetInt(prefName, 0); 1.1437 + 1.1438 + return true; 1.1439 +} 1.1440 + 1.1441 +/* static */ void 1.1442 +XPCJSRuntime::OutOfMemoryCallback(JSContext *cx) 1.1443 +{ 1.1444 + if (!Preferences::GetBool("memory.dump_reports_on_oom")) { 1.1445 + return; 1.1446 + } 1.1447 + 1.1448 + nsCOMPtr<nsIMemoryInfoDumper> dumper = 1.1449 + do_GetService("@mozilla.org/memory-info-dumper;1"); 1.1450 + if (!dumper) { 1.1451 + return; 1.1452 + } 1.1453 + 1.1454 + // If this fails, it fails silently. 1.1455 + dumper->DumpMemoryInfoToTempDir(NS_LITERAL_STRING("due-to-JS-OOM"), 1.1456 + /* minimizeMemoryUsage = */ false); 1.1457 +} 1.1458 + 1.1459 +size_t 1.1460 +XPCJSRuntime::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) 1.1461 +{ 1.1462 + size_t n = 0; 1.1463 + n += mallocSizeOf(this); 1.1464 + n += mWrappedJSMap->SizeOfIncludingThis(mallocSizeOf); 1.1465 + n += mIID2NativeInterfaceMap->SizeOfIncludingThis(mallocSizeOf); 1.1466 + n += mClassInfo2NativeSetMap->ShallowSizeOfIncludingThis(mallocSizeOf); 1.1467 + n += mNativeSetMap->SizeOfIncludingThis(mallocSizeOf); 1.1468 + 1.1469 + n += CycleCollectedJSRuntime::SizeOfExcludingThis(mallocSizeOf); 1.1470 + 1.1471 + // There are other XPCJSRuntime members that could be measured; the above 1.1472 + // ones have been seen by DMD to be worth measuring. More stuff may be 1.1473 + // added later. 1.1474 + 1.1475 + return n; 1.1476 +} 1.1477 + 1.1478 +nsString* 1.1479 +XPCJSRuntime::NewShortLivedString() 1.1480 +{ 1.1481 + for (uint32_t i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) { 1.1482 + if (mScratchStrings[i].empty()) { 1.1483 + mScratchStrings[i].construct(); 1.1484 + return mScratchStrings[i].addr(); 1.1485 + } 1.1486 + } 1.1487 + 1.1488 + // All our internal string wrappers are used, allocate a new string. 1.1489 + return new nsString(); 1.1490 +} 1.1491 + 1.1492 +void 1.1493 +XPCJSRuntime::DeleteShortLivedString(nsString *string) 1.1494 +{ 1.1495 + if (string == &EmptyString() || string == &NullString()) 1.1496 + return; 1.1497 + 1.1498 + for (uint32_t i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) { 1.1499 + if (!mScratchStrings[i].empty() && 1.1500 + mScratchStrings[i].addr() == string) { 1.1501 + // One of our internal strings is no longer in use, mark 1.1502 + // it as such and free its data. 1.1503 + mScratchStrings[i].destroy(); 1.1504 + return; 1.1505 + } 1.1506 + } 1.1507 + 1.1508 + // We're done with a string that's not one of our internal 1.1509 + // strings, delete it. 1.1510 + delete string; 1.1511 +} 1.1512 + 1.1513 +/***************************************************************************/ 1.1514 + 1.1515 +static PLDHashOperator 1.1516 +DetachedWrappedNativeProtoShutdownMarker(PLDHashTable *table, PLDHashEntryHdr *hdr, 1.1517 + uint32_t number, void *arg) 1.1518 +{ 1.1519 + XPCWrappedNativeProto* proto = 1.1520 + (XPCWrappedNativeProto*)((PLDHashEntryStub*)hdr)->key; 1.1521 + 1.1522 + proto->SystemIsBeingShutDown(); 1.1523 + return PL_DHASH_NEXT; 1.1524 +} 1.1525 + 1.1526 +void XPCJSRuntime::DestroyJSContextStack() 1.1527 +{ 1.1528 + delete mJSContextStack; 1.1529 + mJSContextStack = nullptr; 1.1530 +} 1.1531 + 1.1532 +void XPCJSRuntime::SystemIsBeingShutDown() 1.1533 +{ 1.1534 + DOM_ClearInterfaces(); 1.1535 + 1.1536 + if (mDetachedWrappedNativeProtoMap) 1.1537 + mDetachedWrappedNativeProtoMap-> 1.1538 + Enumerate(DetachedWrappedNativeProtoShutdownMarker, nullptr); 1.1539 +} 1.1540 + 1.1541 +#define JS_OPTIONS_DOT_STR "javascript.options." 1.1542 + 1.1543 +static void 1.1544 +ReloadPrefsCallback(const char *pref, void *data) 1.1545 +{ 1.1546 + XPCJSRuntime *runtime = reinterpret_cast<XPCJSRuntime *>(data); 1.1547 + JSRuntime *rt = runtime->Runtime(); 1.1548 + 1.1549 + bool safeMode = false; 1.1550 + nsCOMPtr<nsIXULRuntime> xr = do_GetService("@mozilla.org/xre/runtime;1"); 1.1551 + if (xr) { 1.1552 + xr->GetInSafeMode(&safeMode); 1.1553 + } 1.1554 + 1.1555 + bool useBaseline = Preferences::GetBool(JS_OPTIONS_DOT_STR "baselinejit") && !safeMode; 1.1556 + bool useIon = Preferences::GetBool(JS_OPTIONS_DOT_STR "ion") && !safeMode; 1.1557 + bool useAsmJS = Preferences::GetBool(JS_OPTIONS_DOT_STR "asmjs") && !safeMode; 1.1558 + 1.1559 + bool parallelParsing = Preferences::GetBool(JS_OPTIONS_DOT_STR "parallel_parsing"); 1.1560 + bool parallelIonCompilation = Preferences::GetBool(JS_OPTIONS_DOT_STR 1.1561 + "ion.parallel_compilation"); 1.1562 + bool useBaselineEager = Preferences::GetBool(JS_OPTIONS_DOT_STR 1.1563 + "baselinejit.unsafe_eager_compilation"); 1.1564 + bool useIonEager = Preferences::GetBool(JS_OPTIONS_DOT_STR "ion.unsafe_eager_compilation"); 1.1565 + 1.1566 + sDiscardSystemSource = Preferences::GetBool(JS_OPTIONS_DOT_STR "discardSystemSource"); 1.1567 + 1.1568 + JS::RuntimeOptionsRef(rt).setBaseline(useBaseline) 1.1569 + .setIon(useIon) 1.1570 + . setAsmJS(useAsmJS); 1.1571 + 1.1572 + JS_SetParallelParsingEnabled(rt, parallelParsing); 1.1573 + JS_SetParallelIonCompilationEnabled(rt, parallelIonCompilation); 1.1574 + JS_SetGlobalJitCompilerOption(rt, JSJITCOMPILER_BASELINE_USECOUNT_TRIGGER, 1.1575 + useBaselineEager ? 0 : -1); 1.1576 + JS_SetGlobalJitCompilerOption(rt, JSJITCOMPILER_ION_USECOUNT_TRIGGER, 1.1577 + useIonEager ? 0 : -1); 1.1578 +} 1.1579 + 1.1580 +XPCJSRuntime::~XPCJSRuntime() 1.1581 +{ 1.1582 + // This destructor runs before ~CycleCollectedJSRuntime, which does the 1.1583 + // actual JS_DestroyRuntime() call. But destroying the runtime triggers 1.1584 + // one final GC, which can call back into the runtime with various 1.1585 + // callback if we aren't careful. Null out the relevant callbacks. 1.1586 + js::SetActivityCallback(Runtime(), nullptr, nullptr); 1.1587 + JS_SetFinalizeCallback(Runtime(), nullptr); 1.1588 + 1.1589 + // Clear any pending exception. It might be an XPCWrappedJS, and if we try 1.1590 + // to destroy it later we will crash. 1.1591 + SetPendingException(nullptr); 1.1592 + 1.1593 + JS::SetGCSliceCallback(Runtime(), mPrevGCSliceCallback); 1.1594 + 1.1595 + xpc_DelocalizeRuntime(Runtime()); 1.1596 + 1.1597 + if (mWatchdogManager->GetWatchdog()) 1.1598 + mWatchdogManager->StopWatchdog(); 1.1599 + 1.1600 + if (mCallContext) 1.1601 + mCallContext->SystemIsBeingShutDown(); 1.1602 + 1.1603 + auto rtPrivate = static_cast<PerThreadAtomCache*>(JS_GetRuntimePrivate(Runtime())); 1.1604 + delete rtPrivate; 1.1605 + JS_SetRuntimePrivate(Runtime(), nullptr); 1.1606 + 1.1607 + // clean up and destroy maps... 1.1608 + if (mWrappedJSMap) { 1.1609 + mWrappedJSMap->ShutdownMarker(); 1.1610 + delete mWrappedJSMap; 1.1611 + mWrappedJSMap = nullptr; 1.1612 + } 1.1613 + 1.1614 + if (mWrappedJSClassMap) { 1.1615 + delete mWrappedJSClassMap; 1.1616 + mWrappedJSClassMap = nullptr; 1.1617 + } 1.1618 + 1.1619 + if (mIID2NativeInterfaceMap) { 1.1620 + delete mIID2NativeInterfaceMap; 1.1621 + mIID2NativeInterfaceMap = nullptr; 1.1622 + } 1.1623 + 1.1624 + if (mClassInfo2NativeSetMap) { 1.1625 + delete mClassInfo2NativeSetMap; 1.1626 + mClassInfo2NativeSetMap = nullptr; 1.1627 + } 1.1628 + 1.1629 + if (mNativeSetMap) { 1.1630 + delete mNativeSetMap; 1.1631 + mNativeSetMap = nullptr; 1.1632 + } 1.1633 + 1.1634 + if (mThisTranslatorMap) { 1.1635 + delete mThisTranslatorMap; 1.1636 + mThisTranslatorMap = nullptr; 1.1637 + } 1.1638 + 1.1639 + if (mNativeScriptableSharedMap) { 1.1640 + delete mNativeScriptableSharedMap; 1.1641 + mNativeScriptableSharedMap = nullptr; 1.1642 + } 1.1643 + 1.1644 + if (mDyingWrappedNativeProtoMap) { 1.1645 + delete mDyingWrappedNativeProtoMap; 1.1646 + mDyingWrappedNativeProtoMap = nullptr; 1.1647 + } 1.1648 + 1.1649 + if (mDetachedWrappedNativeProtoMap) { 1.1650 + delete mDetachedWrappedNativeProtoMap; 1.1651 + mDetachedWrappedNativeProtoMap = nullptr; 1.1652 + } 1.1653 + 1.1654 +#ifdef MOZ_ENABLE_PROFILER_SPS 1.1655 + // Tell the profiler that the runtime is gone 1.1656 + if (PseudoStack *stack = mozilla_get_pseudo_stack()) 1.1657 + stack->sampleRuntime(nullptr); 1.1658 +#endif 1.1659 + 1.1660 +#ifdef DEBUG 1.1661 + for (uint32_t i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) { 1.1662 + MOZ_ASSERT(mScratchStrings[i].empty(), "Short lived string still in use"); 1.1663 + } 1.1664 +#endif 1.1665 + 1.1666 + Preferences::UnregisterCallback(ReloadPrefsCallback, JS_OPTIONS_DOT_STR, this); 1.1667 +} 1.1668 + 1.1669 +static void 1.1670 +GetCompartmentName(JSCompartment *c, nsCString &name, bool replaceSlashes) 1.1671 +{ 1.1672 + if (js::IsAtomsCompartment(c)) { 1.1673 + name.AssignLiteral("atoms"); 1.1674 + } else if (JSPrincipals *principals = JS_GetCompartmentPrincipals(c)) { 1.1675 + nsJSPrincipals::get(principals)->GetScriptLocation(name); 1.1676 + 1.1677 + // If the compartment's location (name) differs from the principal's 1.1678 + // script location, append the compartment's location to allow 1.1679 + // differentiation of multiple compartments owned by the same principal 1.1680 + // (e.g. components owned by the system or null principal). 1.1681 + CompartmentPrivate *compartmentPrivate = GetCompartmentPrivate(c); 1.1682 + if (compartmentPrivate) { 1.1683 + const nsACString& location = compartmentPrivate->GetLocation(); 1.1684 + if (!location.IsEmpty() && !location.Equals(name)) { 1.1685 + name.AppendLiteral(", "); 1.1686 + name.Append(location); 1.1687 + } 1.1688 + } 1.1689 + 1.1690 + // A hack: replace forward slashes with '\\' so they aren't 1.1691 + // treated as path separators. Users of the reporters 1.1692 + // (such as about:memory) have to undo this change. 1.1693 + if (replaceSlashes) 1.1694 + name.ReplaceChar('/', '\\'); 1.1695 + } else { 1.1696 + name.AssignLiteral("null-principal"); 1.1697 + } 1.1698 +} 1.1699 + 1.1700 +static int64_t 1.1701 +JSMainRuntimeGCHeapDistinguishedAmount() 1.1702 +{ 1.1703 + JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime(); 1.1704 + return int64_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) * 1.1705 + js::gc::ChunkSize; 1.1706 +} 1.1707 + 1.1708 +static int64_t 1.1709 +JSMainRuntimeTemporaryPeakDistinguishedAmount() 1.1710 +{ 1.1711 + JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime(); 1.1712 + return JS::PeakSizeOfTemporary(rt); 1.1713 +} 1.1714 + 1.1715 +static int64_t 1.1716 +JSMainRuntimeCompartmentsSystemDistinguishedAmount() 1.1717 +{ 1.1718 + JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime(); 1.1719 + return JS::SystemCompartmentCount(rt); 1.1720 +} 1.1721 + 1.1722 +static int64_t 1.1723 +JSMainRuntimeCompartmentsUserDistinguishedAmount() 1.1724 +{ 1.1725 + JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime(); 1.1726 + return JS::UserCompartmentCount(rt); 1.1727 +} 1.1728 + 1.1729 +class JSMainRuntimeTemporaryPeakReporter MOZ_FINAL : public nsIMemoryReporter 1.1730 +{ 1.1731 + public: 1.1732 + NS_DECL_ISUPPORTS 1.1733 + 1.1734 + NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport, 1.1735 + nsISupports* aData) 1.1736 + { 1.1737 + return MOZ_COLLECT_REPORT("js-main-runtime-temporary-peak", 1.1738 + KIND_OTHER, UNITS_BYTES, 1.1739 + JSMainRuntimeTemporaryPeakDistinguishedAmount(), 1.1740 + "Peak transient data size in the main JSRuntime (the current size " 1.1741 + "of which is reported as " 1.1742 + "'explicit/js-non-window/runtime/temporary')."); 1.1743 + } 1.1744 +}; 1.1745 + 1.1746 +NS_IMPL_ISUPPORTS(JSMainRuntimeTemporaryPeakReporter, nsIMemoryReporter) 1.1747 + 1.1748 +// The REPORT* macros do an unconditional report. The ZCREPORT* macros are for 1.1749 +// compartments and zones; they aggregate any entries smaller than 1.1750 +// SUNDRIES_THRESHOLD into the "sundries/gc-heap" and "sundries/malloc-heap" 1.1751 +// entries for the compartment. 1.1752 + 1.1753 +#define SUNDRIES_THRESHOLD js::MemoryReportingSundriesThreshold() 1.1754 + 1.1755 +#define REPORT(_path, _kind, _units, _amount, _desc) \ 1.1756 + do { \ 1.1757 + nsresult rv; \ 1.1758 + rv = cb->Callback(EmptyCString(), _path, \ 1.1759 + nsIMemoryReporter::_kind, \ 1.1760 + nsIMemoryReporter::_units, \ 1.1761 + _amount, \ 1.1762 + NS_LITERAL_CSTRING(_desc), \ 1.1763 + closure); \ 1.1764 + NS_ENSURE_SUCCESS(rv, rv); \ 1.1765 + } while (0) 1.1766 + 1.1767 +#define REPORT_BYTES(_path, _kind, _amount, _desc) \ 1.1768 + REPORT(_path, _kind, UNITS_BYTES, _amount, _desc); 1.1769 + 1.1770 +#define REPORT_GC_BYTES(_path, _amount, _desc) \ 1.1771 + do { \ 1.1772 + size_t amount = _amount; /* evaluate _amount only once */ \ 1.1773 + nsresult rv; \ 1.1774 + rv = cb->Callback(EmptyCString(), _path, \ 1.1775 + nsIMemoryReporter::KIND_NONHEAP, \ 1.1776 + nsIMemoryReporter::UNITS_BYTES, amount, \ 1.1777 + NS_LITERAL_CSTRING(_desc), closure); \ 1.1778 + NS_ENSURE_SUCCESS(rv, rv); \ 1.1779 + gcTotal += amount; \ 1.1780 + } while (0) 1.1781 + 1.1782 +// Report compartment/zone non-GC (KIND_HEAP) bytes. 1.1783 +#define ZCREPORT_BYTES(_path, _amount, _desc) \ 1.1784 + do { \ 1.1785 + /* Assign _descLiteral plus "" into a char* to prove that it's */ \ 1.1786 + /* actually a literal. */ \ 1.1787 + size_t amount = _amount; /* evaluate _amount only once */ \ 1.1788 + if (amount >= SUNDRIES_THRESHOLD) { \ 1.1789 + nsresult rv; \ 1.1790 + rv = cb->Callback(EmptyCString(), _path, \ 1.1791 + nsIMemoryReporter::KIND_HEAP, \ 1.1792 + nsIMemoryReporter::UNITS_BYTES, amount, \ 1.1793 + NS_LITERAL_CSTRING(_desc), closure); \ 1.1794 + NS_ENSURE_SUCCESS(rv, rv); \ 1.1795 + } else { \ 1.1796 + sundriesMallocHeap += amount; \ 1.1797 + } \ 1.1798 + } while (0) 1.1799 + 1.1800 +// Report compartment/zone GC bytes. 1.1801 +#define ZCREPORT_GC_BYTES(_path, _amount, _desc) \ 1.1802 + do { \ 1.1803 + size_t amount = _amount; /* evaluate _amount only once */ \ 1.1804 + if (amount >= SUNDRIES_THRESHOLD) { \ 1.1805 + nsresult rv; \ 1.1806 + rv = cb->Callback(EmptyCString(), _path, \ 1.1807 + nsIMemoryReporter::KIND_NONHEAP, \ 1.1808 + nsIMemoryReporter::UNITS_BYTES, amount, \ 1.1809 + NS_LITERAL_CSTRING(_desc), closure); \ 1.1810 + NS_ENSURE_SUCCESS(rv, rv); \ 1.1811 + gcTotal += amount; \ 1.1812 + } else { \ 1.1813 + sundriesGCHeap += amount; \ 1.1814 + } \ 1.1815 + } while (0) 1.1816 + 1.1817 +// Report runtime bytes. 1.1818 +#define RREPORT_BYTES(_path, _kind, _amount, _desc) \ 1.1819 + do { \ 1.1820 + size_t amount = _amount; /* evaluate _amount only once */ \ 1.1821 + nsresult rv; \ 1.1822 + rv = cb->Callback(EmptyCString(), _path, \ 1.1823 + nsIMemoryReporter::_kind, \ 1.1824 + nsIMemoryReporter::UNITS_BYTES, amount, \ 1.1825 + NS_LITERAL_CSTRING(_desc), closure); \ 1.1826 + NS_ENSURE_SUCCESS(rv, rv); \ 1.1827 + rtTotal += amount; \ 1.1828 + } while (0) 1.1829 + 1.1830 +MOZ_DEFINE_MALLOC_SIZE_OF(JSMallocSizeOf) 1.1831 + 1.1832 +namespace xpc { 1.1833 + 1.1834 +static nsresult 1.1835 +ReportZoneStats(const JS::ZoneStats &zStats, 1.1836 + const xpc::ZoneStatsExtras &extras, 1.1837 + nsIMemoryReporterCallback *cb, 1.1838 + nsISupports *closure, size_t *gcTotalOut = nullptr) 1.1839 +{ 1.1840 + const nsAutoCString& pathPrefix = extras.pathPrefix; 1.1841 + size_t gcTotal = 0, sundriesGCHeap = 0, sundriesMallocHeap = 0; 1.1842 + 1.1843 + MOZ_ASSERT(!gcTotalOut == zStats.isTotals); 1.1844 + 1.1845 + ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap-arena-admin"), 1.1846 + zStats.gcHeapArenaAdmin, 1.1847 + "Bookkeeping information and alignment padding within GC arenas."); 1.1848 + 1.1849 + ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("unused-gc-things"), 1.1850 + zStats.unusedGCThings, 1.1851 + "Empty GC thing cells within non-empty arenas."); 1.1852 + 1.1853 + ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts/gc-heap"), 1.1854 + zStats.lazyScriptsGCHeap, 1.1855 + "Scripts that haven't executed yet."); 1.1856 + 1.1857 + ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts/malloc-heap"), 1.1858 + zStats.lazyScriptsMallocHeap, 1.1859 + "Lazy script tables containing free variables or inner functions."); 1.1860 + 1.1861 + ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("jit-codes-gc-heap"), 1.1862 + zStats.jitCodesGCHeap, 1.1863 + "References to executable code pools used by the JITs."); 1.1864 + 1.1865 + ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-objects/gc-heap"), 1.1866 + zStats.typeObjectsGCHeap, 1.1867 + "Type inference information about objects."); 1.1868 + 1.1869 + ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-objects/malloc-heap"), 1.1870 + zStats.typeObjectsMallocHeap, 1.1871 + "Type object addenda."); 1.1872 + 1.1873 + ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-pool"), 1.1874 + zStats.typePool, 1.1875 + "Type sets and related data."); 1.1876 + 1.1877 + ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("baseline/optimized-stubs"), 1.1878 + zStats.baselineStubsOptimized, 1.1879 + "The Baseline JIT's optimized IC stubs (excluding code)."); 1.1880 + 1.1881 + size_t stringsNotableAboutMemoryGCHeap = 0; 1.1882 + size_t stringsNotableAboutMemoryMallocHeap = 0; 1.1883 + 1.1884 + #define MAYBE_INLINE \ 1.1885 + "The characters may be inline or on the malloc heap." 1.1886 + #define MAYBE_OVERALLOCATED \ 1.1887 + "Sometimes over-allocated to simplify string concatenation." 1.1888 + 1.1889 + for (size_t i = 0; i < zStats.notableStrings.length(); i++) { 1.1890 + const JS::NotableStringInfo& info = zStats.notableStrings[i]; 1.1891 + 1.1892 + MOZ_ASSERT(!zStats.isTotals); 1.1893 + 1.1894 + nsDependentCString notableString(info.buffer); 1.1895 + 1.1896 + // Viewing about:memory generates many notable strings which contain 1.1897 + // "string(length=". If we report these as notable, then we'll create 1.1898 + // even more notable strings the next time we open about:memory (unless 1.1899 + // there's a GC in the meantime), and so on ad infinitum. 1.1900 + // 1.1901 + // To avoid cluttering up about:memory like this, we stick notable 1.1902 + // strings which contain "string(length=" into their own bucket. 1.1903 +# define STRING_LENGTH "string(length=" 1.1904 + if (FindInReadable(NS_LITERAL_CSTRING(STRING_LENGTH), notableString)) { 1.1905 + stringsNotableAboutMemoryGCHeap += info.gcHeap; 1.1906 + stringsNotableAboutMemoryMallocHeap += info.mallocHeap; 1.1907 + continue; 1.1908 + } 1.1909 + 1.1910 + // Escape / to \ before we put notableString into the memory reporter 1.1911 + // path, because we don't want any forward slashes in the string to 1.1912 + // count as path separators. 1.1913 + nsCString escapedString(notableString); 1.1914 + escapedString.ReplaceSubstring("/", "\\"); 1.1915 + 1.1916 + bool truncated = notableString.Length() < info.length; 1.1917 + 1.1918 + nsCString path = pathPrefix + 1.1919 + nsPrintfCString("strings/" STRING_LENGTH "%d, copies=%d, \"%s\"%s)/", 1.1920 + info.length, info.numCopies, escapedString.get(), 1.1921 + truncated ? " (truncated)" : ""); 1.1922 + 1.1923 + if (info.gcHeap > 0) { 1.1924 + REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("gc-heap"), 1.1925 + info.gcHeap, 1.1926 + "Strings. " MAYBE_INLINE); 1.1927 + } 1.1928 + 1.1929 + if (info.mallocHeap > 0) { 1.1930 + REPORT_BYTES(path + NS_LITERAL_CSTRING("malloc-heap"), 1.1931 + KIND_HEAP, info.mallocHeap, 1.1932 + "Non-inline string characters. " MAYBE_OVERALLOCATED); 1.1933 + } 1.1934 + } 1.1935 + 1.1936 + nsCString nonNotablePath = pathPrefix; 1.1937 + nonNotablePath += zStats.isTotals 1.1938 + ? NS_LITERAL_CSTRING("strings/") 1.1939 + : NS_LITERAL_CSTRING("strings/string(<non-notable strings>)/"); 1.1940 + 1.1941 + if (zStats.stringInfo.gcHeap > 0) { 1.1942 + REPORT_GC_BYTES(nonNotablePath + NS_LITERAL_CSTRING("gc-heap"), 1.1943 + zStats.stringInfo.gcHeap, 1.1944 + "Strings. " MAYBE_INLINE); 1.1945 + } 1.1946 + 1.1947 + if (zStats.stringInfo.mallocHeap > 0) { 1.1948 + REPORT_BYTES(nonNotablePath + NS_LITERAL_CSTRING("malloc-heap"), 1.1949 + KIND_HEAP, zStats.stringInfo.mallocHeap, 1.1950 + "Non-inline string characters. " MAYBE_OVERALLOCATED); 1.1951 + } 1.1952 + 1.1953 + if (stringsNotableAboutMemoryGCHeap > 0) { 1.1954 + MOZ_ASSERT(!zStats.isTotals); 1.1955 + REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/string(<about-memory>)/gc-heap"), 1.1956 + stringsNotableAboutMemoryGCHeap, 1.1957 + "Strings that contain the characters '" STRING_LENGTH "', which " 1.1958 + "are probably from about:memory itself." MAYBE_INLINE 1.1959 + " We filter them out rather than display them, because displaying " 1.1960 + "them would create even more such strings every time about:memory " 1.1961 + "is refreshed."); 1.1962 + } 1.1963 + 1.1964 + if (stringsNotableAboutMemoryMallocHeap > 0) { 1.1965 + MOZ_ASSERT(!zStats.isTotals); 1.1966 + REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/string(<about-memory>)/malloc-heap"), 1.1967 + KIND_HEAP, stringsNotableAboutMemoryMallocHeap, 1.1968 + "Non-inline string characters of strings that contain the " 1.1969 + "characters '" STRING_LENGTH "', which are probably from " 1.1970 + "about:memory itself. " MAYBE_OVERALLOCATED 1.1971 + " We filter them out rather than display them, because displaying " 1.1972 + "them would create even more such strings every time about:memory " 1.1973 + "is refreshed."); 1.1974 + } 1.1975 + 1.1976 + if (sundriesGCHeap > 0) { 1.1977 + // We deliberately don't use ZCREPORT_GC_BYTES here. 1.1978 + REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("sundries/gc-heap"), 1.1979 + sundriesGCHeap, 1.1980 + "The sum of all 'gc-heap' measurements that are too small to be " 1.1981 + "worth showing individually."); 1.1982 + } 1.1983 + 1.1984 + if (sundriesMallocHeap > 0) { 1.1985 + // We deliberately don't use ZCREPORT_BYTES here. 1.1986 + REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("sundries/malloc-heap"), 1.1987 + KIND_HEAP, sundriesMallocHeap, 1.1988 + "The sum of all 'malloc-heap' measurements that are too small to " 1.1989 + "be worth showing individually."); 1.1990 + } 1.1991 + 1.1992 + if (gcTotalOut) 1.1993 + *gcTotalOut += gcTotal; 1.1994 + 1.1995 + return NS_OK; 1.1996 + 1.1997 +# undef STRING_LENGTH 1.1998 +} 1.1999 + 1.2000 +static nsresult 1.2001 +ReportCompartmentStats(const JS::CompartmentStats &cStats, 1.2002 + const xpc::CompartmentStatsExtras &extras, 1.2003 + amIAddonManager *addonManager, 1.2004 + nsIMemoryReporterCallback *cb, 1.2005 + nsISupports *closure, size_t *gcTotalOut = nullptr) 1.2006 +{ 1.2007 + static const nsDependentCString addonPrefix("explicit/add-ons/"); 1.2008 + 1.2009 + size_t gcTotal = 0, sundriesGCHeap = 0, sundriesMallocHeap = 0; 1.2010 + nsAutoCString cJSPathPrefix = extras.jsPathPrefix; 1.2011 + nsAutoCString cDOMPathPrefix = extras.domPathPrefix; 1.2012 + 1.2013 + // Only attempt to prefix if we got a location and the path wasn't already 1.2014 + // prefixed. 1.2015 + if (extras.location && addonManager && 1.2016 + cJSPathPrefix.Find(addonPrefix, false, 0, 0) != 0) { 1.2017 + nsAutoCString addonId; 1.2018 + bool ok; 1.2019 + if (NS_SUCCEEDED(addonManager->MapURIToAddonID(extras.location, 1.2020 + addonId, &ok)) 1.2021 + && ok) { 1.2022 + // Insert the add-on id as "add-ons/@id@/" after "explicit/" to 1.2023 + // aggregate add-on compartments. 1.2024 + static const size_t explicitLength = strlen("explicit/"); 1.2025 + addonId.Insert(NS_LITERAL_CSTRING("add-ons/"), 0); 1.2026 + addonId += "/"; 1.2027 + cJSPathPrefix.Insert(addonId, explicitLength); 1.2028 + cDOMPathPrefix.Insert(addonId, explicitLength); 1.2029 + } 1.2030 + } 1.2031 + 1.2032 + ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/ordinary"), 1.2033 + cStats.objectsGCHeapOrdinary, 1.2034 + "Ordinary objects, i.e. not otherwise distinguished by memory " 1.2035 + "reporters."); 1.2036 + 1.2037 + ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/function"), 1.2038 + cStats.objectsGCHeapFunction, 1.2039 + "Function objects."); 1.2040 + 1.2041 + ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/dense-array"), 1.2042 + cStats.objectsGCHeapDenseArray, 1.2043 + "Dense array objects."); 1.2044 + 1.2045 + ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/slow-array"), 1.2046 + cStats.objectsGCHeapSlowArray, 1.2047 + "Slow array objects."); 1.2048 + 1.2049 + ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/cross-compartment-wrapper"), 1.2050 + cStats.objectsGCHeapCrossCompartmentWrapper, 1.2051 + "Cross-compartment wrapper objects."); 1.2052 + 1.2053 + // Note that we use cDOMPathPrefix here. This is because we measure orphan 1.2054 + // DOM nodes in the JS reporter, but we want to report them in a "dom" 1.2055 + // sub-tree rather than a "js" sub-tree. 1.2056 + ZCREPORT_BYTES(cDOMPathPrefix + NS_LITERAL_CSTRING("orphan-nodes"), 1.2057 + cStats.objectsPrivate, 1.2058 + "Orphan DOM nodes, i.e. those that are only reachable from JavaScript " 1.2059 + "objects."); 1.2060 + 1.2061 + ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/tree/global-parented"), 1.2062 + cStats.shapesGCHeapTreeGlobalParented, 1.2063 + "Shapes that (a) are in a property tree, and (b) represent an object " 1.2064 + "whose parent is the global object."); 1.2065 + 1.2066 + ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/tree/non-global-parented"), 1.2067 + cStats.shapesGCHeapTreeNonGlobalParented, 1.2068 + "Shapes that (a) are in a property tree, and (b) represent an object " 1.2069 + "whose parent is not the global object."); 1.2070 + 1.2071 + ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/dict"), 1.2072 + cStats.shapesGCHeapDict, 1.2073 + "Shapes that are in dictionary mode."); 1.2074 + 1.2075 + ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/base"), 1.2076 + cStats.shapesGCHeapBase, 1.2077 + "Base shapes, which collate data common to many shapes."); 1.2078 + 1.2079 + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/tree-tables"), 1.2080 + cStats.shapesMallocHeapTreeTables, 1.2081 + "Property tables belonging to shapes that are in a property tree."); 1.2082 + 1.2083 + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/dict-tables"), 1.2084 + cStats.shapesMallocHeapDictTables, 1.2085 + "Property tables that belong to shapes that are in dictionary mode."); 1.2086 + 1.2087 + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/tree-shape-kids"), 1.2088 + cStats.shapesMallocHeapTreeShapeKids, 1.2089 + "Kid hashes that belong to shapes that are in a property tree."); 1.2090 + 1.2091 + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/compartment-tables"), 1.2092 + cStats.shapesMallocHeapCompartmentTables, 1.2093 + "Compartment-wide tables storing shape information used during object " 1.2094 + "construction."); 1.2095 + 1.2096 + ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("scripts/gc-heap"), 1.2097 + cStats.scriptsGCHeap, 1.2098 + "JSScript instances. There is one per user-defined function in a " 1.2099 + "script, and one for the top-level code in a script."); 1.2100 + 1.2101 + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("scripts/malloc-heap/data"), 1.2102 + cStats.scriptsMallocHeapData, 1.2103 + "Various variable-length tables in JSScripts."); 1.2104 + 1.2105 + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("baseline/data"), 1.2106 + cStats.baselineData, 1.2107 + "The Baseline JIT's compilation data (BaselineScripts)."); 1.2108 + 1.2109 + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("baseline/fallback-stubs"), 1.2110 + cStats.baselineStubsFallback, 1.2111 + "The Baseline JIT's fallback IC stubs (excluding code)."); 1.2112 + 1.2113 + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("ion-data"), 1.2114 + cStats.ionData, 1.2115 + "The IonMonkey JIT's compilation data (IonScripts)."); 1.2116 + 1.2117 + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/type-scripts"), 1.2118 + cStats.typeInferenceTypeScripts, 1.2119 + "Type sets associated with scripts."); 1.2120 + 1.2121 + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/allocation-site-tables"), 1.2122 + cStats.typeInferenceAllocationSiteTables, 1.2123 + "Tables of type objects associated with allocation sites."); 1.2124 + 1.2125 + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/array-type-tables"), 1.2126 + cStats.typeInferenceArrayTypeTables, 1.2127 + "Tables of type objects associated with array literals."); 1.2128 + 1.2129 + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/object-type-tables"), 1.2130 + cStats.typeInferenceObjectTypeTables, 1.2131 + "Tables of type objects associated with object literals."); 1.2132 + 1.2133 + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("compartment-object"), 1.2134 + cStats.compartmentObject, 1.2135 + "The JSCompartment object itself."); 1.2136 + 1.2137 + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("cross-compartment-wrapper-table"), 1.2138 + cStats.crossCompartmentWrappersTable, 1.2139 + "The cross-compartment wrapper table."); 1.2140 + 1.2141 + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("regexp-compartment"), 1.2142 + cStats.regexpCompartment, 1.2143 + "The regexp compartment."); 1.2144 + 1.2145 + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("debuggees-set"), 1.2146 + cStats.debuggeesSet, 1.2147 + "The debuggees set."); 1.2148 + 1.2149 + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/slots"), 1.2150 + cStats.objectsExtra.mallocHeapSlots, 1.2151 + "Non-fixed object slot arrays, which represent object properties."); 1.2152 + 1.2153 + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/elements/non-asm.js"), 1.2154 + cStats.objectsExtra.mallocHeapElementsNonAsmJS, 1.2155 + "Non-asm.js indexed elements."); 1.2156 + 1.2157 + // asm.js arrays are heap-allocated on some platforms and 1.2158 + // non-heap-allocated on others. We never put them under sundries, 1.2159 + // because (a) in practice they're almost always larger than the sundries 1.2160 + // threshold, and (b) we'd need a third category of sundries ("non-heap"), 1.2161 + // which would be a pain. 1.2162 + size_t mallocHeapElementsAsmJS = cStats.objectsExtra.mallocHeapElementsAsmJS; 1.2163 + size_t nonHeapElementsAsmJS = cStats.objectsExtra.nonHeapElementsAsmJS; 1.2164 + MOZ_ASSERT(mallocHeapElementsAsmJS == 0 || nonHeapElementsAsmJS == 0); 1.2165 + if (mallocHeapElementsAsmJS > 0) { 1.2166 + REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/elements/asm.js"), 1.2167 + KIND_HEAP, mallocHeapElementsAsmJS, 1.2168 + "asm.js array buffer elements on the malloc heap."); 1.2169 + } 1.2170 + if (nonHeapElementsAsmJS > 0) { 1.2171 + REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/non-heap/elements/asm.js"), 1.2172 + KIND_NONHEAP, nonHeapElementsAsmJS, 1.2173 + "asm.js array buffer elements outside both the malloc heap and " 1.2174 + "the GC heap."); 1.2175 + } 1.2176 + 1.2177 + if (cStats.objectsExtra.nonHeapElementsMapped > 0) { 1.2178 + REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/non-heap/elements/mapped"), 1.2179 + KIND_NONHEAP, cStats.objectsExtra.nonHeapElementsMapped, 1.2180 + "Memory-mapped array buffer elements."); 1.2181 + } 1.2182 + 1.2183 + REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/non-heap/code/asm.js"), 1.2184 + KIND_NONHEAP, cStats.objectsExtra.nonHeapCodeAsmJS, 1.2185 + "AOT-compiled asm.js code."); 1.2186 + 1.2187 + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/asm.js-module-data"), 1.2188 + cStats.objectsExtra.mallocHeapAsmJSModuleData, 1.2189 + "asm.js module data."); 1.2190 + 1.2191 + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/arguments-data"), 1.2192 + cStats.objectsExtra.mallocHeapArgumentsData, 1.2193 + "Data belonging to Arguments objects."); 1.2194 + 1.2195 + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/regexp-statics"), 1.2196 + cStats.objectsExtra.mallocHeapRegExpStatics, 1.2197 + "Data belonging to the RegExpStatics object."); 1.2198 + 1.2199 + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/property-iterator-data"), 1.2200 + cStats.objectsExtra.mallocHeapPropertyIteratorData, 1.2201 + "Data belonging to property iterator objects."); 1.2202 + 1.2203 + ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/ctypes-data"), 1.2204 + cStats.objectsExtra.mallocHeapCtypesData, 1.2205 + "Data belonging to ctypes objects."); 1.2206 + 1.2207 + if (sundriesGCHeap > 0) { 1.2208 + // We deliberately don't use ZCREPORT_GC_BYTES here. 1.2209 + REPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("sundries/gc-heap"), 1.2210 + sundriesGCHeap, 1.2211 + "The sum of all 'gc-heap' measurements that are too small to be " 1.2212 + "worth showing individually."); 1.2213 + } 1.2214 + 1.2215 + if (sundriesMallocHeap > 0) { 1.2216 + // We deliberately don't use ZCREPORT_BYTES here. 1.2217 + REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("sundries/malloc-heap"), 1.2218 + KIND_HEAP, sundriesMallocHeap, 1.2219 + "The sum of all 'malloc-heap' measurements that are too small to " 1.2220 + "be worth showing individually."); 1.2221 + } 1.2222 + 1.2223 + if (gcTotalOut) 1.2224 + *gcTotalOut += gcTotal; 1.2225 + 1.2226 + return NS_OK; 1.2227 +} 1.2228 + 1.2229 +static nsresult 1.2230 +ReportScriptSourceStats(const ScriptSourceInfo &scriptSourceInfo, 1.2231 + const nsACString &path, 1.2232 + nsIHandleReportCallback *cb, nsISupports *closure, 1.2233 + size_t &rtTotal) 1.2234 +{ 1.2235 + if (scriptSourceInfo.compressed > 0) { 1.2236 + RREPORT_BYTES(path + NS_LITERAL_CSTRING("compressed"), 1.2237 + KIND_HEAP, scriptSourceInfo.compressed, 1.2238 + "Compressed JavaScript source code."); 1.2239 + } 1.2240 + 1.2241 + if (scriptSourceInfo.uncompressed > 0) { 1.2242 + RREPORT_BYTES(path + NS_LITERAL_CSTRING("uncompressed"), 1.2243 + KIND_HEAP, scriptSourceInfo.uncompressed, 1.2244 + "Uncompressed JavaScript source code."); 1.2245 + } 1.2246 + 1.2247 + if (scriptSourceInfo.misc > 0) { 1.2248 + RREPORT_BYTES(path + NS_LITERAL_CSTRING("misc"), 1.2249 + KIND_HEAP, scriptSourceInfo.misc, 1.2250 + "Miscellaneous data relating to JavaScript source code."); 1.2251 + } 1.2252 + 1.2253 + return NS_OK; 1.2254 +} 1.2255 + 1.2256 +static nsresult 1.2257 +ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats, 1.2258 + const nsACString &rtPath, 1.2259 + amIAddonManager* addonManager, 1.2260 + nsIMemoryReporterCallback *cb, 1.2261 + nsISupports *closure, size_t *rtTotalOut) 1.2262 +{ 1.2263 + nsresult rv; 1.2264 + 1.2265 + size_t gcTotal = 0; 1.2266 + 1.2267 + for (size_t i = 0; i < rtStats.zoneStatsVector.length(); i++) { 1.2268 + const JS::ZoneStats &zStats = rtStats.zoneStatsVector[i]; 1.2269 + const xpc::ZoneStatsExtras *extras = 1.2270 + static_cast<const xpc::ZoneStatsExtras*>(zStats.extra); 1.2271 + rv = ReportZoneStats(zStats, *extras, cb, closure, &gcTotal); 1.2272 + NS_ENSURE_SUCCESS(rv, rv); 1.2273 + } 1.2274 + 1.2275 + for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++) { 1.2276 + JS::CompartmentStats cStats = rtStats.compartmentStatsVector[i]; 1.2277 + const xpc::CompartmentStatsExtras *extras = 1.2278 + static_cast<const xpc::CompartmentStatsExtras*>(cStats.extra); 1.2279 + rv = ReportCompartmentStats(cStats, *extras, addonManager, cb, closure, 1.2280 + &gcTotal); 1.2281 + NS_ENSURE_SUCCESS(rv, rv); 1.2282 + } 1.2283 + 1.2284 + // Report the rtStats.runtime numbers under "runtime/", and compute their 1.2285 + // total for later. 1.2286 + 1.2287 + size_t rtTotal = 0; 1.2288 + 1.2289 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/runtime-object"), 1.2290 + KIND_HEAP, rtStats.runtime.object, 1.2291 + "The JSRuntime object."); 1.2292 + 1.2293 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/atoms-table"), 1.2294 + KIND_HEAP, rtStats.runtime.atomsTable, 1.2295 + "The atoms table."); 1.2296 + 1.2297 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/contexts"), 1.2298 + KIND_HEAP, rtStats.runtime.contexts, 1.2299 + "JSContext objects and structures that belong to them."); 1.2300 + 1.2301 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/dtoa"), 1.2302 + KIND_HEAP, rtStats.runtime.dtoa, 1.2303 + "The DtoaState object, which is used for converting strings to " 1.2304 + "numbers and vice versa."); 1.2305 + 1.2306 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/temporary"), 1.2307 + KIND_HEAP, rtStats.runtime.temporary, 1.2308 + "Transient data (mostly parse nodes) held by the JSRuntime during " 1.2309 + "compilation."); 1.2310 + 1.2311 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/regexp-data"), 1.2312 + KIND_NONHEAP, rtStats.runtime.regexpData, 1.2313 + "Regexp JIT data."); 1.2314 + 1.2315 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/interpreter-stack"), 1.2316 + KIND_HEAP, rtStats.runtime.interpreterStack, 1.2317 + "JS interpreter frames."); 1.2318 + 1.2319 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/math-cache"), 1.2320 + KIND_HEAP, rtStats.runtime.mathCache, 1.2321 + "The math cache."); 1.2322 + 1.2323 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/source-data-cache"), 1.2324 + KIND_HEAP, rtStats.runtime.sourceDataCache, 1.2325 + "The source data cache, which holds decompressed script source code."); 1.2326 + 1.2327 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/script-data"), 1.2328 + KIND_HEAP, rtStats.runtime.scriptData, 1.2329 + "The table holding script data shared in the runtime."); 1.2330 + 1.2331 + nsCString nonNotablePath = 1.2332 + rtPath + nsPrintfCString("runtime/script-sources/source(scripts=%d, <non-notable files>)/", 1.2333 + rtStats.runtime.scriptSourceInfo.numScripts); 1.2334 + 1.2335 + rv = ReportScriptSourceStats(rtStats.runtime.scriptSourceInfo, 1.2336 + nonNotablePath, cb, closure, rtTotal); 1.2337 + NS_ENSURE_SUCCESS(rv, rv); 1.2338 + 1.2339 + for (size_t i = 0; i < rtStats.runtime.notableScriptSources.length(); i++) { 1.2340 + const JS::NotableScriptSourceInfo& scriptSourceInfo = 1.2341 + rtStats.runtime.notableScriptSources[i]; 1.2342 + 1.2343 + // Escape / to \ before we put the filename into the memory reporter 1.2344 + // path, because we don't want any forward slashes in the string to 1.2345 + // count as path separators. Consumers of memory reporters (e.g. 1.2346 + // about:memory) will convert them back to / after doing path 1.2347 + // splitting. 1.2348 + nsDependentCString filename(scriptSourceInfo.filename_); 1.2349 + nsCString escapedFilename(filename); 1.2350 + escapedFilename.ReplaceSubstring("/", "\\"); 1.2351 + 1.2352 + nsCString notablePath = rtPath + 1.2353 + nsPrintfCString("runtime/script-sources/source(scripts=%d, %s)/", 1.2354 + scriptSourceInfo.numScripts, escapedFilename.get()); 1.2355 + 1.2356 + rv = ReportScriptSourceStats(scriptSourceInfo, notablePath, 1.2357 + cb, closure, rtTotal); 1.2358 + NS_ENSURE_SUCCESS(rv, rv); 1.2359 + } 1.2360 + 1.2361 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/ion"), 1.2362 + KIND_NONHEAP, rtStats.runtime.code.ion, 1.2363 + "Code generated by the IonMonkey JIT."); 1.2364 + 1.2365 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/baseline"), 1.2366 + KIND_NONHEAP, rtStats.runtime.code.baseline, 1.2367 + "Code generated by the Baseline JIT."); 1.2368 + 1.2369 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/regexp"), 1.2370 + KIND_NONHEAP, rtStats.runtime.code.regexp, 1.2371 + "Code generated by the regexp JIT."); 1.2372 + 1.2373 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/other"), 1.2374 + KIND_NONHEAP, rtStats.runtime.code.other, 1.2375 + "Code generated by the JITs for wrappers and trampolines."); 1.2376 + 1.2377 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/unused"), 1.2378 + KIND_NONHEAP, rtStats.runtime.code.unused, 1.2379 + "Memory allocated by one of the JITs to hold code, but which is " 1.2380 + "currently unused."); 1.2381 + 1.2382 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/marker"), 1.2383 + KIND_HEAP, rtStats.runtime.gc.marker, 1.2384 + "The GC mark stack and gray roots."); 1.2385 + 1.2386 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/nursery-committed"), 1.2387 + KIND_NONHEAP, rtStats.runtime.gc.nurseryCommitted, 1.2388 + "Memory being used by the GC's nursery."); 1.2389 + 1.2390 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/nursery-huge-slots"), 1.2391 + KIND_NONHEAP, rtStats.runtime.gc.nurseryHugeSlots, 1.2392 + "Out-of-line slots and elements belonging to objects in the " 1.2393 + "nursery."); 1.2394 + 1.2395 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/vals"), 1.2396 + KIND_HEAP, rtStats.runtime.gc.storeBufferVals, 1.2397 + "Values in the store buffer."); 1.2398 + 1.2399 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/cells"), 1.2400 + KIND_HEAP, rtStats.runtime.gc.storeBufferCells, 1.2401 + "Cells in the store buffer."); 1.2402 + 1.2403 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/slots"), 1.2404 + KIND_HEAP, rtStats.runtime.gc.storeBufferSlots, 1.2405 + "Slots in the store buffer."); 1.2406 + 1.2407 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/whole-cells"), 1.2408 + KIND_HEAP, rtStats.runtime.gc.storeBufferWholeCells, 1.2409 + "Whole cells in the store buffer."); 1.2410 + 1.2411 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/reloc-vals"), 1.2412 + KIND_HEAP, rtStats.runtime.gc.storeBufferRelocVals, 1.2413 + "Relocatable values in the store buffer."); 1.2414 + 1.2415 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/reloc-cells"), 1.2416 + KIND_HEAP, rtStats.runtime.gc.storeBufferRelocCells, 1.2417 + "Relocatable cells in the store buffer."); 1.2418 + 1.2419 + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/generics"), 1.2420 + KIND_HEAP, rtStats.runtime.gc.storeBufferGenerics, 1.2421 + "Generic things in the store buffer."); 1.2422 + 1.2423 + if (rtTotalOut) 1.2424 + *rtTotalOut = rtTotal; 1.2425 + 1.2426 + // Report GC numbers that don't belong to a compartment. 1.2427 + 1.2428 + // We don't want to report decommitted memory in "explicit", so we just 1.2429 + // change the leading "explicit/" to "decommitted/". 1.2430 + nsCString rtPath2(rtPath); 1.2431 + rtPath2.Replace(0, strlen("explicit"), NS_LITERAL_CSTRING("decommitted")); 1.2432 + REPORT_GC_BYTES(rtPath2 + NS_LITERAL_CSTRING("gc-heap/decommitted-arenas"), 1.2433 + rtStats.gcHeapDecommittedArenas, 1.2434 + "GC arenas in non-empty chunks that is decommitted, i.e. it takes up " 1.2435 + "address space but no physical memory or swap space."); 1.2436 + 1.2437 + REPORT_BYTES(rtPath2 + NS_LITERAL_CSTRING("runtime/gc/nursery-decommitted"), 1.2438 + KIND_NONHEAP, rtStats.runtime.gc.nurseryDecommitted, 1.2439 + "Memory allocated to the GC's nursery this is decommitted, i.e. it takes up " 1.2440 + "address space but no physical memory or swap space."); 1.2441 + 1.2442 + REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/unused-chunks"), 1.2443 + rtStats.gcHeapUnusedChunks, 1.2444 + "Empty GC chunks which will soon be released unless claimed for new " 1.2445 + "allocations."); 1.2446 + 1.2447 + REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/unused-arenas"), 1.2448 + rtStats.gcHeapUnusedArenas, 1.2449 + "Empty GC arenas within non-empty chunks."); 1.2450 + 1.2451 + REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/chunk-admin"), 1.2452 + rtStats.gcHeapChunkAdmin, 1.2453 + "Bookkeeping information within GC chunks."); 1.2454 + 1.2455 + // gcTotal is the sum of everything we've reported for the GC heap. It 1.2456 + // should equal rtStats.gcHeapChunkTotal. 1.2457 + MOZ_ASSERT(gcTotal == rtStats.gcHeapChunkTotal); 1.2458 + 1.2459 + return NS_OK; 1.2460 +} 1.2461 + 1.2462 +nsresult 1.2463 +ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats, 1.2464 + const nsACString &rtPath, 1.2465 + nsIMemoryReporterCallback *cb, 1.2466 + nsISupports *closure, size_t *rtTotalOut) 1.2467 +{ 1.2468 + nsCOMPtr<amIAddonManager> am; 1.2469 + if (XRE_GetProcessType() == GeckoProcessType_Default) { 1.2470 + // Only try to access the service from the main process. 1.2471 + am = do_GetService("@mozilla.org/addons/integration;1"); 1.2472 + } 1.2473 + return ReportJSRuntimeExplicitTreeStats(rtStats, rtPath, am.get(), cb, 1.2474 + closure, rtTotalOut); 1.2475 +} 1.2476 + 1.2477 + 1.2478 +} // namespace xpc 1.2479 + 1.2480 +class JSMainRuntimeCompartmentsReporter MOZ_FINAL : public nsIMemoryReporter 1.2481 +{ 1.2482 + public: 1.2483 + NS_DECL_ISUPPORTS 1.2484 + 1.2485 + typedef js::Vector<nsCString, 0, js::SystemAllocPolicy> Paths; 1.2486 + 1.2487 + static void CompartmentCallback(JSRuntime *rt, void* data, JSCompartment *c) { 1.2488 + // silently ignore OOM errors 1.2489 + Paths *paths = static_cast<Paths *>(data); 1.2490 + nsCString path; 1.2491 + GetCompartmentName(c, path, true); 1.2492 + path.Insert(js::IsSystemCompartment(c) 1.2493 + ? NS_LITERAL_CSTRING("js-main-runtime-compartments/system/") 1.2494 + : NS_LITERAL_CSTRING("js-main-runtime-compartments/user/"), 1.2495 + 0); 1.2496 + paths->append(path); 1.2497 + } 1.2498 + 1.2499 + NS_IMETHOD CollectReports(nsIMemoryReporterCallback *cb, 1.2500 + nsISupports *closure) 1.2501 + { 1.2502 + // First we collect the compartment paths. Then we report them. Doing 1.2503 + // the two steps interleaved is a bad idea, because calling |cb| 1.2504 + // from within CompartmentCallback() leads to all manner of assertions. 1.2505 + 1.2506 + // Collect. 1.2507 + 1.2508 + Paths paths; 1.2509 + JS_IterateCompartments(nsXPConnect::GetRuntimeInstance()->Runtime(), 1.2510 + &paths, CompartmentCallback); 1.2511 + 1.2512 + // Report. 1.2513 + for (size_t i = 0; i < paths.length(); i++) 1.2514 + // These ones don't need a description, hence the "". 1.2515 + REPORT(nsCString(paths[i]), KIND_OTHER, UNITS_COUNT, 1, 1.2516 + "A live compartment in the main JSRuntime."); 1.2517 + 1.2518 + return NS_OK; 1.2519 + } 1.2520 +}; 1.2521 + 1.2522 +NS_IMPL_ISUPPORTS(JSMainRuntimeCompartmentsReporter, nsIMemoryReporter) 1.2523 + 1.2524 +MOZ_DEFINE_MALLOC_SIZE_OF(OrphanMallocSizeOf) 1.2525 + 1.2526 +namespace xpc { 1.2527 + 1.2528 +static size_t 1.2529 +SizeOfTreeIncludingThis(nsINode *tree) 1.2530 +{ 1.2531 + size_t n = tree->SizeOfIncludingThis(OrphanMallocSizeOf); 1.2532 + for (nsIContent* child = tree->GetFirstChild(); child; child = child->GetNextNode(tree)) 1.2533 + n += child->SizeOfIncludingThis(OrphanMallocSizeOf); 1.2534 + 1.2535 + return n; 1.2536 +} 1.2537 + 1.2538 +class OrphanReporter : public JS::ObjectPrivateVisitor 1.2539 +{ 1.2540 + public: 1.2541 + OrphanReporter(GetISupportsFun aGetISupports) 1.2542 + : JS::ObjectPrivateVisitor(aGetISupports) 1.2543 + { 1.2544 + } 1.2545 + 1.2546 + virtual size_t sizeOfIncludingThis(nsISupports *aSupports) MOZ_OVERRIDE { 1.2547 + size_t n = 0; 1.2548 + nsCOMPtr<nsINode> node = do_QueryInterface(aSupports); 1.2549 + // https://bugzilla.mozilla.org/show_bug.cgi?id=773533#c11 explains 1.2550 + // that we have to skip XBL elements because they violate certain 1.2551 + // assumptions. Yuk. 1.2552 + if (node && !node->IsInDoc() && 1.2553 + !(node->IsElement() && node->AsElement()->IsInNamespace(kNameSpaceID_XBL))) 1.2554 + { 1.2555 + // This is an orphan node. If we haven't already handled the 1.2556 + // sub-tree that this node belongs to, measure the sub-tree's size 1.2557 + // and then record its root so we don't measure it again. 1.2558 + nsCOMPtr<nsINode> orphanTree = node->SubtreeRoot(); 1.2559 + if (!mAlreadyMeasuredOrphanTrees.Contains(orphanTree)) { 1.2560 + n += SizeOfTreeIncludingThis(orphanTree); 1.2561 + mAlreadyMeasuredOrphanTrees.PutEntry(orphanTree); 1.2562 + } 1.2563 + } 1.2564 + return n; 1.2565 + } 1.2566 + 1.2567 + private: 1.2568 + nsTHashtable <nsISupportsHashKey> mAlreadyMeasuredOrphanTrees; 1.2569 +}; 1.2570 + 1.2571 +#ifdef DEBUG 1.2572 +static bool 1.2573 +StartsWithExplicit(nsACString& s) 1.2574 +{ 1.2575 + const char* e = "explicit/"; 1.2576 + return Substring(s, 0, strlen(e)).Equals(e); 1.2577 +} 1.2578 +#endif 1.2579 + 1.2580 +class XPCJSRuntimeStats : public JS::RuntimeStats 1.2581 +{ 1.2582 + WindowPaths *mWindowPaths; 1.2583 + WindowPaths *mTopWindowPaths; 1.2584 + bool mGetLocations; 1.2585 + 1.2586 + public: 1.2587 + XPCJSRuntimeStats(WindowPaths *windowPaths, WindowPaths *topWindowPaths, 1.2588 + bool getLocations) 1.2589 + : JS::RuntimeStats(JSMallocSizeOf), 1.2590 + mWindowPaths(windowPaths), 1.2591 + mTopWindowPaths(topWindowPaths), 1.2592 + mGetLocations(getLocations) 1.2593 + {} 1.2594 + 1.2595 + ~XPCJSRuntimeStats() { 1.2596 + for (size_t i = 0; i != compartmentStatsVector.length(); ++i) 1.2597 + delete static_cast<xpc::CompartmentStatsExtras*>(compartmentStatsVector[i].extra); 1.2598 + 1.2599 + 1.2600 + for (size_t i = 0; i != zoneStatsVector.length(); ++i) 1.2601 + delete static_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra); 1.2602 + } 1.2603 + 1.2604 + virtual void initExtraZoneStats(JS::Zone *zone, JS::ZoneStats *zStats) MOZ_OVERRIDE { 1.2605 + // Get the compartment's global. 1.2606 + nsXPConnect *xpc = nsXPConnect::XPConnect(); 1.2607 + AutoSafeJSContext cx; 1.2608 + JSCompartment *comp = js::GetAnyCompartmentInZone(zone); 1.2609 + xpc::ZoneStatsExtras *extras = new xpc::ZoneStatsExtras; 1.2610 + extras->pathPrefix.AssignLiteral("explicit/js-non-window/zones/"); 1.2611 + RootedObject global(cx, JS_GetGlobalForCompartmentOrNull(cx, comp)); 1.2612 + if (global) { 1.2613 + // Need to enter the compartment, otherwise GetNativeOfWrapper() 1.2614 + // might crash. 1.2615 + JSAutoCompartment ac(cx, global); 1.2616 + nsISupports *native = xpc->GetNativeOfWrapper(cx, global); 1.2617 + if (nsCOMPtr<nsPIDOMWindow> piwindow = do_QueryInterface(native)) { 1.2618 + // The global is a |window| object. Use the path prefix that 1.2619 + // we should have already created for it. 1.2620 + if (mTopWindowPaths->Get(piwindow->WindowID(), 1.2621 + &extras->pathPrefix)) 1.2622 + extras->pathPrefix.AppendLiteral("/js-"); 1.2623 + } 1.2624 + } 1.2625 + 1.2626 + extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void *)zone); 1.2627 + 1.2628 + MOZ_ASSERT(StartsWithExplicit(extras->pathPrefix)); 1.2629 + 1.2630 + zStats->extra = extras; 1.2631 + } 1.2632 + 1.2633 + virtual void initExtraCompartmentStats(JSCompartment *c, 1.2634 + JS::CompartmentStats *cstats) MOZ_OVERRIDE 1.2635 + { 1.2636 + xpc::CompartmentStatsExtras *extras = new xpc::CompartmentStatsExtras; 1.2637 + nsCString cName; 1.2638 + GetCompartmentName(c, cName, true); 1.2639 + if (mGetLocations) { 1.2640 + CompartmentPrivate *cp = GetCompartmentPrivate(c); 1.2641 + if (cp) 1.2642 + cp->GetLocationURI(CompartmentPrivate::LocationHintAddon, 1.2643 + getter_AddRefs(extras->location)); 1.2644 + // Note: cannot use amIAddonManager implementation at this point, 1.2645 + // as it is a JS service and the JS heap is currently not idle. 1.2646 + // Otherwise, we could have computed the add-on id at this point. 1.2647 + } 1.2648 + 1.2649 + // Get the compartment's global. 1.2650 + nsXPConnect *xpc = nsXPConnect::XPConnect(); 1.2651 + AutoSafeJSContext cx; 1.2652 + bool needZone = true; 1.2653 + RootedObject global(cx, JS_GetGlobalForCompartmentOrNull(cx, c)); 1.2654 + if (global) { 1.2655 + // Need to enter the compartment, otherwise GetNativeOfWrapper() 1.2656 + // might crash. 1.2657 + JSAutoCompartment ac(cx, global); 1.2658 + nsISupports *native = xpc->GetNativeOfWrapper(cx, global); 1.2659 + if (nsCOMPtr<nsPIDOMWindow> piwindow = do_QueryInterface(native)) { 1.2660 + // The global is a |window| object. Use the path prefix that 1.2661 + // we should have already created for it. 1.2662 + if (mWindowPaths->Get(piwindow->WindowID(), 1.2663 + &extras->jsPathPrefix)) { 1.2664 + extras->domPathPrefix.Assign(extras->jsPathPrefix); 1.2665 + extras->domPathPrefix.AppendLiteral("/dom/"); 1.2666 + extras->jsPathPrefix.AppendLiteral("/js-"); 1.2667 + needZone = false; 1.2668 + } else { 1.2669 + extras->jsPathPrefix.AssignLiteral("explicit/js-non-window/zones/"); 1.2670 + extras->domPathPrefix.AssignLiteral("explicit/dom/unknown-window-global?!/"); 1.2671 + } 1.2672 + } else { 1.2673 + extras->jsPathPrefix.AssignLiteral("explicit/js-non-window/zones/"); 1.2674 + extras->domPathPrefix.AssignLiteral("explicit/dom/non-window-global?!/"); 1.2675 + } 1.2676 + } else { 1.2677 + extras->jsPathPrefix.AssignLiteral("explicit/js-non-window/zones/"); 1.2678 + extras->domPathPrefix.AssignLiteral("explicit/dom/no-global?!/"); 1.2679 + } 1.2680 + 1.2681 + if (needZone) 1.2682 + extras->jsPathPrefix += nsPrintfCString("zone(0x%p)/", (void *)js::GetCompartmentZone(c)); 1.2683 + 1.2684 + extras->jsPathPrefix += NS_LITERAL_CSTRING("compartment(") + cName + NS_LITERAL_CSTRING(")/"); 1.2685 + 1.2686 + // extras->jsPathPrefix is used for almost all the compartment-specific 1.2687 + // reports. At this point it has the form 1.2688 + // "<something>compartment(<cname>)/". 1.2689 + // 1.2690 + // extras->domPathPrefix is used for DOM orphan nodes, which are 1.2691 + // counted by the JS reporter but reported as part of the DOM 1.2692 + // measurements. At this point it has the form "<something>/dom/" if 1.2693 + // this compartment belongs to an nsGlobalWindow, and 1.2694 + // "explicit/dom/<something>?!/" otherwise (in which case it shouldn't 1.2695 + // be used, because non-nsGlobalWindow compartments shouldn't have 1.2696 + // orphan DOM nodes). 1.2697 + 1.2698 + MOZ_ASSERT(StartsWithExplicit(extras->jsPathPrefix)); 1.2699 + MOZ_ASSERT(StartsWithExplicit(extras->domPathPrefix)); 1.2700 + 1.2701 + cstats->extra = extras; 1.2702 + } 1.2703 +}; 1.2704 + 1.2705 +nsresult 1.2706 +JSReporter::CollectReports(WindowPaths *windowPaths, 1.2707 + WindowPaths *topWindowPaths, 1.2708 + nsIMemoryReporterCallback *cb, 1.2709 + nsISupports *closure) 1.2710 +{ 1.2711 + XPCJSRuntime *xpcrt = nsXPConnect::GetRuntimeInstance(); 1.2712 + 1.2713 + // In the first step we get all the stats and stash them in a local 1.2714 + // data structure. In the second step we pass all the stashed stats to 1.2715 + // the callback. Separating these steps is important because the 1.2716 + // callback may be a JS function, and executing JS while getting these 1.2717 + // stats seems like a bad idea. 1.2718 + 1.2719 + nsCOMPtr<amIAddonManager> addonManager; 1.2720 + if (XRE_GetProcessType() == GeckoProcessType_Default) { 1.2721 + // Only try to access the service from the main process. 1.2722 + addonManager = do_GetService("@mozilla.org/addons/integration;1"); 1.2723 + } 1.2724 + bool getLocations = !!addonManager; 1.2725 + XPCJSRuntimeStats rtStats(windowPaths, topWindowPaths, getLocations); 1.2726 + OrphanReporter orphanReporter(XPCConvert::GetISupportsFromJSObject); 1.2727 + if (!JS::CollectRuntimeStats(xpcrt->Runtime(), &rtStats, &orphanReporter)) 1.2728 + return NS_ERROR_FAILURE; 1.2729 + 1.2730 + size_t xpconnect = xpcrt->SizeOfIncludingThis(JSMallocSizeOf); 1.2731 + 1.2732 + XPCWrappedNativeScope::ScopeSizeInfo sizeInfo(JSMallocSizeOf); 1.2733 + XPCWrappedNativeScope::AddSizeOfAllScopesIncludingThis(&sizeInfo); 1.2734 + 1.2735 + mozJSComponentLoader* loader = mozJSComponentLoader::Get(); 1.2736 + size_t jsComponentLoaderSize = loader ? loader->SizeOfIncludingThis(JSMallocSizeOf) : 0; 1.2737 + 1.2738 + // This is the second step (see above). First we report stuff in the 1.2739 + // "explicit" tree, then we report other stuff. 1.2740 + 1.2741 + nsresult rv; 1.2742 + size_t rtTotal = 0; 1.2743 + rv = xpc::ReportJSRuntimeExplicitTreeStats(rtStats, 1.2744 + NS_LITERAL_CSTRING("explicit/js-non-window/"), 1.2745 + addonManager, cb, closure, 1.2746 + &rtTotal); 1.2747 + NS_ENSURE_SUCCESS(rv, rv); 1.2748 + 1.2749 + // Report the sums of the compartment numbers. 1.2750 + xpc::CompartmentStatsExtras cExtrasTotal; 1.2751 + cExtrasTotal.jsPathPrefix.AssignLiteral("js-main-runtime/compartments/"); 1.2752 + cExtrasTotal.domPathPrefix.AssignLiteral("window-objects/dom/"); 1.2753 + rv = ReportCompartmentStats(rtStats.cTotals, cExtrasTotal, addonManager, 1.2754 + cb, closure); 1.2755 + NS_ENSURE_SUCCESS(rv, rv); 1.2756 + 1.2757 + xpc::ZoneStatsExtras zExtrasTotal; 1.2758 + zExtrasTotal.pathPrefix.AssignLiteral("js-main-runtime/zones/"); 1.2759 + rv = ReportZoneStats(rtStats.zTotals, zExtrasTotal, cb, closure); 1.2760 + NS_ENSURE_SUCCESS(rv, rv); 1.2761 + 1.2762 + // Report the sum of the runtime/ numbers. 1.2763 + REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/runtime"), 1.2764 + KIND_OTHER, rtTotal, 1.2765 + "The sum of all measurements under 'explicit/js-non-window/runtime/'."); 1.2766 + 1.2767 + // Report the numbers for memory outside of compartments. 1.2768 + 1.2769 + REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/unused-chunks"), 1.2770 + KIND_OTHER, rtStats.gcHeapUnusedChunks, 1.2771 + "The same as 'explicit/js-non-window/gc-heap/unused-chunks'."); 1.2772 + 1.2773 + REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/unused-arenas"), 1.2774 + KIND_OTHER, rtStats.gcHeapUnusedArenas, 1.2775 + "The same as 'explicit/js-non-window/gc-heap/unused-arenas'."); 1.2776 + 1.2777 + REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/chunk-admin"), 1.2778 + KIND_OTHER, rtStats.gcHeapChunkAdmin, 1.2779 + "The same as 'explicit/js-non-window/gc-heap/chunk-admin'."); 1.2780 + 1.2781 + // Report a breakdown of the committed GC space. 1.2782 + 1.2783 + REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/chunks"), 1.2784 + KIND_OTHER, rtStats.gcHeapUnusedChunks, 1.2785 + "The same as 'explicit/js-non-window/gc-heap/unused-chunks'."); 1.2786 + 1.2787 + REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/arenas"), 1.2788 + KIND_OTHER, rtStats.gcHeapUnusedArenas, 1.2789 + "The same as 'explicit/js-non-window/gc-heap/unused-arenas'."); 1.2790 + 1.2791 + REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things"), 1.2792 + KIND_OTHER, rtStats.zTotals.unusedGCThings, 1.2793 + "The same as 'js-main-runtime/zones/unused-gc-things'."); 1.2794 + 1.2795 + REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/chunk-admin"), 1.2796 + KIND_OTHER, rtStats.gcHeapChunkAdmin, 1.2797 + "The same as 'explicit/js-non-window/gc-heap/chunk-admin'."); 1.2798 + 1.2799 + REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/arena-admin"), 1.2800 + KIND_OTHER, rtStats.zTotals.gcHeapArenaAdmin, 1.2801 + "The same as 'js-main-runtime/zones/gc-heap-arena-admin'."); 1.2802 + 1.2803 + REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things"), 1.2804 + KIND_OTHER, rtStats.gcHeapGCThings, 1.2805 + "GC things: objects, strings, scripts, etc."); 1.2806 + 1.2807 + // Report xpconnect. 1.2808 + 1.2809 + REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/runtime"), 1.2810 + KIND_HEAP, xpconnect, 1.2811 + "The XPConnect runtime."); 1.2812 + 1.2813 + REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/scopes"), 1.2814 + KIND_HEAP, sizeInfo.mScopeAndMapSize, 1.2815 + "XPConnect scopes."); 1.2816 + 1.2817 + REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/proto-iface-cache"), 1.2818 + KIND_HEAP, sizeInfo.mProtoAndIfaceCacheSize, 1.2819 + "Prototype and interface binding caches."); 1.2820 + 1.2821 + REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/js-component-loader"), 1.2822 + KIND_HEAP, jsComponentLoaderSize, 1.2823 + "XPConnect's JS component loader."); 1.2824 + 1.2825 + return NS_OK; 1.2826 +} 1.2827 + 1.2828 +static nsresult 1.2829 +JSSizeOfTab(JSObject *objArg, size_t *jsObjectsSize, size_t *jsStringsSize, 1.2830 + size_t *jsPrivateSize, size_t *jsOtherSize) 1.2831 +{ 1.2832 + JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime(); 1.2833 + JS::RootedObject obj(rt, objArg); 1.2834 + 1.2835 + TabSizes sizes; 1.2836 + OrphanReporter orphanReporter(XPCConvert::GetISupportsFromJSObject); 1.2837 + NS_ENSURE_TRUE(JS::AddSizeOfTab(rt, obj, moz_malloc_size_of, 1.2838 + &orphanReporter, &sizes), 1.2839 + NS_ERROR_OUT_OF_MEMORY); 1.2840 + 1.2841 + *jsObjectsSize = sizes.objects; 1.2842 + *jsStringsSize = sizes.strings; 1.2843 + *jsPrivateSize = sizes.private_; 1.2844 + *jsOtherSize = sizes.other; 1.2845 + return NS_OK; 1.2846 +} 1.2847 + 1.2848 +} // namespace xpc 1.2849 + 1.2850 +#ifdef MOZ_CRASHREPORTER 1.2851 +static bool 1.2852 +DiagnosticMemoryCallback(void *ptr, size_t size) 1.2853 +{ 1.2854 + return CrashReporter::RegisterAppMemory(ptr, size) == NS_OK; 1.2855 +} 1.2856 +#endif 1.2857 + 1.2858 +static void 1.2859 +AccumulateTelemetryCallback(int id, uint32_t sample) 1.2860 +{ 1.2861 + switch (id) { 1.2862 + case JS_TELEMETRY_GC_REASON: 1.2863 + Telemetry::Accumulate(Telemetry::GC_REASON_2, sample); 1.2864 + break; 1.2865 + case JS_TELEMETRY_GC_IS_COMPARTMENTAL: 1.2866 + Telemetry::Accumulate(Telemetry::GC_IS_COMPARTMENTAL, sample); 1.2867 + break; 1.2868 + case JS_TELEMETRY_GC_MS: 1.2869 + Telemetry::Accumulate(Telemetry::GC_MS, sample); 1.2870 + break; 1.2871 + case JS_TELEMETRY_GC_MAX_PAUSE_MS: 1.2872 + Telemetry::Accumulate(Telemetry::GC_MAX_PAUSE_MS, sample); 1.2873 + break; 1.2874 + case JS_TELEMETRY_GC_MARK_MS: 1.2875 + Telemetry::Accumulate(Telemetry::GC_MARK_MS, sample); 1.2876 + break; 1.2877 + case JS_TELEMETRY_GC_SWEEP_MS: 1.2878 + Telemetry::Accumulate(Telemetry::GC_SWEEP_MS, sample); 1.2879 + break; 1.2880 + case JS_TELEMETRY_GC_MARK_ROOTS_MS: 1.2881 + Telemetry::Accumulate(Telemetry::GC_MARK_ROOTS_MS, sample); 1.2882 + break; 1.2883 + case JS_TELEMETRY_GC_MARK_GRAY_MS: 1.2884 + Telemetry::Accumulate(Telemetry::GC_MARK_GRAY_MS, sample); 1.2885 + break; 1.2886 + case JS_TELEMETRY_GC_SLICE_MS: 1.2887 + Telemetry::Accumulate(Telemetry::GC_SLICE_MS, sample); 1.2888 + break; 1.2889 + case JS_TELEMETRY_GC_MMU_50: 1.2890 + Telemetry::Accumulate(Telemetry::GC_MMU_50, sample); 1.2891 + break; 1.2892 + case JS_TELEMETRY_GC_RESET: 1.2893 + Telemetry::Accumulate(Telemetry::GC_RESET, sample); 1.2894 + break; 1.2895 + case JS_TELEMETRY_GC_INCREMENTAL_DISABLED: 1.2896 + Telemetry::Accumulate(Telemetry::GC_INCREMENTAL_DISABLED, sample); 1.2897 + break; 1.2898 + case JS_TELEMETRY_GC_NON_INCREMENTAL: 1.2899 + Telemetry::Accumulate(Telemetry::GC_NON_INCREMENTAL, sample); 1.2900 + break; 1.2901 + case JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS: 1.2902 + Telemetry::Accumulate(Telemetry::GC_SCC_SWEEP_TOTAL_MS, sample); 1.2903 + break; 1.2904 + case JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS: 1.2905 + Telemetry::Accumulate(Telemetry::GC_SCC_SWEEP_MAX_PAUSE_MS, sample); 1.2906 + break; 1.2907 + } 1.2908 +} 1.2909 + 1.2910 +static void 1.2911 +CompartmentNameCallback(JSRuntime *rt, JSCompartment *comp, 1.2912 + char *buf, size_t bufsize) 1.2913 +{ 1.2914 + nsCString name; 1.2915 + GetCompartmentName(comp, name, false); 1.2916 + if (name.Length() >= bufsize) 1.2917 + name.Truncate(bufsize - 1); 1.2918 + memcpy(buf, name.get(), name.Length() + 1); 1.2919 +} 1.2920 + 1.2921 +static bool 1.2922 +PreserveWrapper(JSContext *cx, JSObject *obj) 1.2923 +{ 1.2924 + MOZ_ASSERT(cx); 1.2925 + MOZ_ASSERT(obj); 1.2926 + MOZ_ASSERT(IS_WN_REFLECTOR(obj) || mozilla::dom::IsDOMObject(obj)); 1.2927 + 1.2928 + return mozilla::dom::IsDOMObject(obj) && mozilla::dom::TryPreserveWrapper(obj); 1.2929 +} 1.2930 + 1.2931 +static nsresult 1.2932 +ReadSourceFromFilename(JSContext *cx, const char *filename, jschar **src, size_t *len) 1.2933 +{ 1.2934 + nsresult rv; 1.2935 + 1.2936 + // mozJSSubScriptLoader prefixes the filenames of the scripts it loads with 1.2937 + // the filename of its caller. Axe that if present. 1.2938 + const char *arrow; 1.2939 + while ((arrow = strstr(filename, " -> "))) 1.2940 + filename = arrow + strlen(" -> "); 1.2941 + 1.2942 + // Get the URI. 1.2943 + nsCOMPtr<nsIURI> uri; 1.2944 + rv = NS_NewURI(getter_AddRefs(uri), filename); 1.2945 + NS_ENSURE_SUCCESS(rv, rv); 1.2946 + 1.2947 + nsCOMPtr<nsIChannel> scriptChannel; 1.2948 + rv = NS_NewChannel(getter_AddRefs(scriptChannel), uri); 1.2949 + NS_ENSURE_SUCCESS(rv, rv); 1.2950 + 1.2951 + // Only allow local reading. 1.2952 + nsCOMPtr<nsIURI> actualUri; 1.2953 + rv = scriptChannel->GetURI(getter_AddRefs(actualUri)); 1.2954 + NS_ENSURE_SUCCESS(rv, rv); 1.2955 + nsCString scheme; 1.2956 + rv = actualUri->GetScheme(scheme); 1.2957 + NS_ENSURE_SUCCESS(rv, rv); 1.2958 + if (!scheme.EqualsLiteral("file") && !scheme.EqualsLiteral("jar")) 1.2959 + return NS_OK; 1.2960 + 1.2961 + nsCOMPtr<nsIInputStream> scriptStream; 1.2962 + rv = scriptChannel->Open(getter_AddRefs(scriptStream)); 1.2963 + NS_ENSURE_SUCCESS(rv, rv); 1.2964 + 1.2965 + uint64_t rawLen; 1.2966 + rv = scriptStream->Available(&rawLen); 1.2967 + NS_ENSURE_SUCCESS(rv, rv); 1.2968 + if (!rawLen) 1.2969 + return NS_ERROR_FAILURE; 1.2970 + 1.2971 + // Technically, this should be SIZE_MAX, but we don't run on machines 1.2972 + // where that would be less than UINT32_MAX, and the latter is already 1.2973 + // well beyond a reasonable limit. 1.2974 + if (rawLen > UINT32_MAX) 1.2975 + return NS_ERROR_FILE_TOO_BIG; 1.2976 + 1.2977 + // Allocate an internal buf the size of the file. 1.2978 + nsAutoArrayPtr<unsigned char> buf(new unsigned char[rawLen]); 1.2979 + if (!buf) 1.2980 + return NS_ERROR_OUT_OF_MEMORY; 1.2981 + 1.2982 + unsigned char *ptr = buf, *end = ptr + rawLen; 1.2983 + while (ptr < end) { 1.2984 + uint32_t bytesRead; 1.2985 + rv = scriptStream->Read(reinterpret_cast<char *>(ptr), end - ptr, &bytesRead); 1.2986 + if (NS_FAILED(rv)) 1.2987 + return rv; 1.2988 + MOZ_ASSERT(bytesRead > 0, "stream promised more bytes before EOF"); 1.2989 + ptr += bytesRead; 1.2990 + } 1.2991 + 1.2992 + rv = nsScriptLoader::ConvertToUTF16(scriptChannel, buf, rawLen, EmptyString(), 1.2993 + nullptr, *src, *len); 1.2994 + NS_ENSURE_SUCCESS(rv, rv); 1.2995 + 1.2996 + if (!*src) 1.2997 + return NS_ERROR_FAILURE; 1.2998 + 1.2999 + // Historically this method used JS_malloc() which updates the GC memory 1.3000 + // accounting. Since ConvertToUTF16() now uses js_malloc() instead we 1.3001 + // update the accounting manually after the fact. 1.3002 + JS_updateMallocCounter(cx, *len); 1.3003 + 1.3004 + return NS_OK; 1.3005 +} 1.3006 + 1.3007 +// The JS engine calls this object's 'load' member function when it needs 1.3008 +// the source for a chrome JS function. See the comment in the XPCJSRuntime 1.3009 +// constructor. 1.3010 +class XPCJSSourceHook: public js::SourceHook { 1.3011 + bool load(JSContext *cx, const char *filename, jschar **src, size_t *length) { 1.3012 + *src = nullptr; 1.3013 + *length = 0; 1.3014 + 1.3015 + if (!nsContentUtils::IsCallerChrome()) 1.3016 + return true; 1.3017 + 1.3018 + if (!filename) 1.3019 + return true; 1.3020 + 1.3021 + nsresult rv = ReadSourceFromFilename(cx, filename, src, length); 1.3022 + if (NS_FAILED(rv)) { 1.3023 + xpc::Throw(cx, rv); 1.3024 + return false; 1.3025 + } 1.3026 + 1.3027 + return true; 1.3028 + } 1.3029 +}; 1.3030 + 1.3031 +static const JSWrapObjectCallbacks WrapObjectCallbacks = { 1.3032 + xpc::WrapperFactory::Rewrap, 1.3033 + xpc::WrapperFactory::PrepareForWrapping 1.3034 +}; 1.3035 + 1.3036 +XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect) 1.3037 + : CycleCollectedJSRuntime(nullptr, 32L * 1024L * 1024L, JS_USE_HELPER_THREADS), 1.3038 + mJSContextStack(new XPCJSContextStack(MOZ_THIS_IN_INITIALIZER_LIST())), 1.3039 + mCallContext(nullptr), 1.3040 + mAutoRoots(nullptr), 1.3041 + mResolveName(JSID_VOID), 1.3042 + mResolvingWrapper(nullptr), 1.3043 + mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_SIZE)), 1.3044 + mWrappedJSClassMap(IID2WrappedJSClassMap::newMap(XPC_JS_CLASS_MAP_SIZE)), 1.3045 + mIID2NativeInterfaceMap(IID2NativeInterfaceMap::newMap(XPC_NATIVE_INTERFACE_MAP_SIZE)), 1.3046 + mClassInfo2NativeSetMap(ClassInfo2NativeSetMap::newMap(XPC_NATIVE_SET_MAP_SIZE)), 1.3047 + mNativeSetMap(NativeSetMap::newMap(XPC_NATIVE_SET_MAP_SIZE)), 1.3048 + mThisTranslatorMap(IID2ThisTranslatorMap::newMap(XPC_THIS_TRANSLATOR_MAP_SIZE)), 1.3049 + mNativeScriptableSharedMap(XPCNativeScriptableSharedMap::newMap(XPC_NATIVE_JSCLASS_MAP_SIZE)), 1.3050 + mDyingWrappedNativeProtoMap(XPCWrappedNativeProtoMap::newMap(XPC_DYING_NATIVE_PROTO_MAP_SIZE)), 1.3051 + mDetachedWrappedNativeProtoMap(XPCWrappedNativeProtoMap::newMap(XPC_DETACHED_NATIVE_PROTO_MAP_SIZE)), 1.3052 + mGCIsRunning(false), 1.3053 + mWrappedJSToReleaseArray(), 1.3054 + mNativesToReleaseArray(), 1.3055 + mDoingFinalization(false), 1.3056 + mVariantRoots(nullptr), 1.3057 + mWrappedJSRoots(nullptr), 1.3058 + mObjectHolderRoots(nullptr), 1.3059 + mWatchdogManager(new WatchdogManager(MOZ_THIS_IN_INITIALIZER_LIST())), 1.3060 + mJunkScope(MOZ_THIS_IN_INITIALIZER_LIST()->Runtime(), nullptr), 1.3061 + mCompilationScope(MOZ_THIS_IN_INITIALIZER_LIST()->Runtime(), nullptr), 1.3062 + mAsyncSnowWhiteFreer(new AsyncFreeSnowWhite()) 1.3063 +{ 1.3064 + DOM_InitInterfaces(); 1.3065 + 1.3066 + // these jsids filled in later when we have a JSContext to work with. 1.3067 + mStrIDs[0] = JSID_VOID; 1.3068 + 1.3069 + MOZ_ASSERT(Runtime()); 1.3070 + JSRuntime* runtime = Runtime(); 1.3071 + 1.3072 + auto rtPrivate = new PerThreadAtomCache(); 1.3073 + memset(rtPrivate, 0, sizeof(PerThreadAtomCache)); 1.3074 + JS_SetRuntimePrivate(runtime, rtPrivate); 1.3075 + 1.3076 + // Unconstrain the runtime's threshold on nominal heap size, to avoid 1.3077 + // triggering GC too often if operating continuously near an arbitrary 1.3078 + // finite threshold (0xffffffff is infinity for uint32_t parameters). 1.3079 + // This leaves the maximum-JS_malloc-bytes threshold still in effect 1.3080 + // to cause period, and we hope hygienic, last-ditch GCs from within 1.3081 + // the GC's allocator. 1.3082 + JS_SetGCParameter(runtime, JSGC_MAX_BYTES, 0xffffffff); 1.3083 + 1.3084 + // The JS engine permits us to set different stack limits for system code, 1.3085 + // trusted script, and untrusted script. We have tests that ensure that 1.3086 + // we can always execute 10 "heavy" (eval+with) stack frames deeper in 1.3087 + // privileged code. Our stack sizes vary greatly in different configurations, 1.3088 + // so satisfying those tests requires some care. Manual measurements of the 1.3089 + // number of heavy stack frames achievable gives us the following rough data, 1.3090 + // ordered by the effective categories in which they are grouped in the 1.3091 + // JS_SetNativeStackQuota call (which predates this analysis). 1.3092 + // 1.3093 + // (NB: These numbers may have drifted recently - see bug 938429) 1.3094 + // OSX 64-bit Debug: 7MB stack, 636 stack frames => ~11.3k per stack frame 1.3095 + // OSX64 Opt: 7MB stack, 2440 stack frames => ~3k per stack frame 1.3096 + // 1.3097 + // Linux 32-bit Debug: 2MB stack, 447 stack frames => ~4.6k per stack frame 1.3098 + // Linux 64-bit Debug: 4MB stack, 501 stack frames => ~8.2k per stack frame 1.3099 + // 1.3100 + // Windows (Opt+Debug): 900K stack, 235 stack frames => ~3.4k per stack frame 1.3101 + // 1.3102 + // Linux 32-bit Opt: 1MB stack, 272 stack frames => ~3.8k per stack frame 1.3103 + // Linux 64-bit Opt: 2MB stack, 316 stack frames => ~6.5k per stack frame 1.3104 + // 1.3105 + // We tune the trusted/untrusted quotas for each configuration to achieve our 1.3106 + // invariants while attempting to minimize overhead. In contrast, our buffer 1.3107 + // between system code and trusted script is a very unscientific 10k. 1.3108 + const size_t kSystemCodeBuffer = 10 * 1024; 1.3109 + 1.3110 + // Our "default" stack is what we use in configurations where we don't have 1.3111 + // a compelling reason to do things differently. This is effectively 1MB on 1.3112 + // 32-bit platforms and 2MB on 64-bit platforms. 1.3113 + const size_t kDefaultStackQuota = 128 * sizeof(size_t) * 1024; 1.3114 + 1.3115 + // Set stack sizes for different configurations. It's probably not great for 1.3116 + // the web to base this decision primarily on the default stack size that the 1.3117 + // underlying platform makes available, but that seems to be what we do. :-( 1.3118 + 1.3119 +#if defined(XP_MACOSX) || defined(DARWIN) 1.3120 + // MacOS has a gargantuan default stack size of 8MB. Go wild with 7MB, 1.3121 + // and give trusted script 140k extra. The stack is huge on mac anyway. 1.3122 + const size_t kStackQuota = 7 * 1024 * 1024; 1.3123 + const size_t kTrustedScriptBuffer = 140 * 1024; 1.3124 +#elif defined(MOZ_ASAN) 1.3125 + // ASan requires more stack space due to red-zones, so give it double the 1.3126 + // default (2MB on 32-bit, 4MB on 64-bit). ASAN stack frame measurements 1.3127 + // were not taken at the time of this writing, so we hazard a guess that 1.3128 + // ASAN builds have roughly thrice the stack overhead as normal builds. 1.3129 + // On normal builds, the largest stack frame size we might encounter is 1.3130 + // 8.2k, so let's use a buffer of 8.2 * 3 * 10 = 246k. 1.3131 + const size_t kStackQuota = 2 * kDefaultStackQuota; 1.3132 + const size_t kTrustedScriptBuffer = 246 * 1024; 1.3133 +#elif defined(XP_WIN) 1.3134 + // 1MB is the default stack size on Windows, so the default 1MB stack quota 1.3135 + // we'd get on win32 is slightly too large. Use 900k instead. And since 1.3136 + // windows stack frames are 3.4k each, let's use a buffer of 50k. 1.3137 + const size_t kStackQuota = 900 * 1024; 1.3138 + const size_t kTrustedScriptBuffer = 50 * 1024; 1.3139 + // The following two configurations are linux-only. Given the numbers above, 1.3140 + // we use 50k and 100k trusted buffers on 32-bit and 64-bit respectively. 1.3141 +#elif defined(DEBUG) 1.3142 + // Bug 803182: account for the 4x difference in the size of js::Interpret 1.3143 + // between optimized and debug builds. 1.3144 + // XXXbholley - Then why do we only account for 2x of difference? 1.3145 + const size_t kStackQuota = 2 * kDefaultStackQuota; 1.3146 + const size_t kTrustedScriptBuffer = sizeof(size_t) * 12800; 1.3147 +#else 1.3148 + const size_t kStackQuota = kDefaultStackQuota; 1.3149 + const size_t kTrustedScriptBuffer = sizeof(size_t) * 12800; 1.3150 +#endif 1.3151 + 1.3152 + // Avoid an unused variable warning on platforms where we don't use the 1.3153 + // default. 1.3154 + (void) kDefaultStackQuota; 1.3155 + 1.3156 + JS_SetNativeStackQuota(runtime, 1.3157 + kStackQuota, 1.3158 + kStackQuota - kSystemCodeBuffer, 1.3159 + kStackQuota - kSystemCodeBuffer - kTrustedScriptBuffer); 1.3160 + 1.3161 + JS_SetDestroyCompartmentCallback(runtime, CompartmentDestroyedCallback); 1.3162 + JS_SetCompartmentNameCallback(runtime, CompartmentNameCallback); 1.3163 + mPrevGCSliceCallback = JS::SetGCSliceCallback(runtime, GCSliceCallback); 1.3164 + JS_SetFinalizeCallback(runtime, FinalizeCallback); 1.3165 + JS_SetWrapObjectCallbacks(runtime, &WrapObjectCallbacks); 1.3166 + js::SetPreserveWrapperCallback(runtime, PreserveWrapper); 1.3167 +#ifdef MOZ_CRASHREPORTER 1.3168 + JS_EnumerateDiagnosticMemoryRegions(DiagnosticMemoryCallback); 1.3169 +#endif 1.3170 +#ifdef MOZ_ENABLE_PROFILER_SPS 1.3171 + if (PseudoStack *stack = mozilla_get_pseudo_stack()) 1.3172 + stack->sampleRuntime(runtime); 1.3173 +#endif 1.3174 + JS_SetAccumulateTelemetryCallback(runtime, AccumulateTelemetryCallback); 1.3175 + js::SetDefaultJSContextCallback(runtime, DefaultJSContextCallback); 1.3176 + js::SetActivityCallback(runtime, ActivityCallback, this); 1.3177 + js::SetCTypesActivityCallback(runtime, CTypesActivityCallback); 1.3178 + JS_SetInterruptCallback(runtime, InterruptCallback); 1.3179 + JS::SetOutOfMemoryCallback(runtime, OutOfMemoryCallback); 1.3180 + 1.3181 + // The JS engine needs to keep the source code around in order to implement 1.3182 + // Function.prototype.toSource(). It'd be nice to not have to do this for 1.3183 + // chrome code and simply stub out requests for source on it. Life is not so 1.3184 + // easy, unfortunately. Nobody relies on chrome toSource() working in core 1.3185 + // browser code, but chrome tests use it. The worst offenders are addons, 1.3186 + // which like to monkeypatch chrome functions by calling toSource() on them 1.3187 + // and using regular expressions to modify them. We avoid keeping most browser 1.3188 + // JS source code in memory by setting LAZY_SOURCE on JS::CompileOptions when 1.3189 + // compiling some chrome code. This causes the JS engine not save the source 1.3190 + // code in memory. When the JS engine is asked to provide the source for a 1.3191 + // function compiled with LAZY_SOURCE, it calls SourceHook to load it. 1.3192 + /// 1.3193 + // Note we do have to retain the source code in memory for scripts compiled in 1.3194 + // compileAndGo mode and compiled function bodies (from 1.3195 + // JS_CompileFunction*). In practice, this means content scripts and event 1.3196 + // handlers. 1.3197 + js::SetSourceHook(runtime, new XPCJSSourceHook); 1.3198 + 1.3199 + // Set up locale information and callbacks for the newly-created runtime so 1.3200 + // that the various toLocaleString() methods, localeCompare(), and other 1.3201 + // internationalization APIs work as desired. 1.3202 + if (!xpc_LocalizeRuntime(runtime)) 1.3203 + NS_RUNTIMEABORT("xpc_LocalizeRuntime failed."); 1.3204 + 1.3205 + // Register memory reporters and distinguished amount functions. 1.3206 + RegisterStrongMemoryReporter(new JSMainRuntimeCompartmentsReporter()); 1.3207 + RegisterStrongMemoryReporter(new JSMainRuntimeTemporaryPeakReporter()); 1.3208 + RegisterJSMainRuntimeGCHeapDistinguishedAmount(JSMainRuntimeGCHeapDistinguishedAmount); 1.3209 + RegisterJSMainRuntimeTemporaryPeakDistinguishedAmount(JSMainRuntimeTemporaryPeakDistinguishedAmount); 1.3210 + RegisterJSMainRuntimeCompartmentsSystemDistinguishedAmount(JSMainRuntimeCompartmentsSystemDistinguishedAmount); 1.3211 + RegisterJSMainRuntimeCompartmentsUserDistinguishedAmount(JSMainRuntimeCompartmentsUserDistinguishedAmount); 1.3212 + mozilla::RegisterJSSizeOfTab(JSSizeOfTab); 1.3213 + 1.3214 + // Install a JavaScript 'debugger' keyword handler in debug builds only 1.3215 +#ifdef DEBUG 1.3216 + if (!JS_GetGlobalDebugHooks(runtime)->debuggerHandler) 1.3217 + xpc_InstallJSDebuggerKeywordHandler(runtime); 1.3218 +#endif 1.3219 + 1.3220 + // Watch for the JS boolean options. 1.3221 + ReloadPrefsCallback(nullptr, this); 1.3222 + Preferences::RegisterCallback(ReloadPrefsCallback, JS_OPTIONS_DOT_STR, this); 1.3223 +} 1.3224 + 1.3225 +// static 1.3226 +XPCJSRuntime* 1.3227 +XPCJSRuntime::newXPCJSRuntime(nsXPConnect* aXPConnect) 1.3228 +{ 1.3229 + NS_PRECONDITION(aXPConnect,"bad param"); 1.3230 + 1.3231 + XPCJSRuntime* self = new XPCJSRuntime(aXPConnect); 1.3232 + 1.3233 + if (self && 1.3234 + self->Runtime() && 1.3235 + self->GetWrappedJSMap() && 1.3236 + self->GetWrappedJSClassMap() && 1.3237 + self->GetIID2NativeInterfaceMap() && 1.3238 + self->GetClassInfo2NativeSetMap() && 1.3239 + self->GetNativeSetMap() && 1.3240 + self->GetThisTranslatorMap() && 1.3241 + self->GetNativeScriptableSharedMap() && 1.3242 + self->GetDyingWrappedNativeProtoMap() && 1.3243 + self->mWatchdogManager) { 1.3244 + return self; 1.3245 + } 1.3246 + 1.3247 + NS_RUNTIMEABORT("new XPCJSRuntime failed to initialize."); 1.3248 + 1.3249 + delete self; 1.3250 + return nullptr; 1.3251 +} 1.3252 + 1.3253 +bool 1.3254 +XPCJSRuntime::OnJSContextNew(JSContext *cx) 1.3255 +{ 1.3256 + // If we were the first cx ever created (like the SafeJSContext), the caller 1.3257 + // would have had no way to enter a request. Enter one now before doing the 1.3258 + // rest of the cx setup. 1.3259 + JSAutoRequest ar(cx); 1.3260 + 1.3261 + // if it is our first context then we need to generate our string ids 1.3262 + if (JSID_IS_VOID(mStrIDs[0])) { 1.3263 + RootedString str(cx); 1.3264 + for (unsigned i = 0; i < IDX_TOTAL_COUNT; i++) { 1.3265 + str = JS_InternString(cx, mStrings[i]); 1.3266 + if (!str) { 1.3267 + mStrIDs[0] = JSID_VOID; 1.3268 + return false; 1.3269 + } 1.3270 + mStrIDs[i] = INTERNED_STRING_TO_JSID(cx, str); 1.3271 + mStrJSVals[i] = STRING_TO_JSVAL(str); 1.3272 + } 1.3273 + 1.3274 + if (!mozilla::dom::DefineStaticJSVals(cx)) { 1.3275 + return false; 1.3276 + } 1.3277 + } 1.3278 + 1.3279 + XPCContext* xpc = new XPCContext(this, cx); 1.3280 + if (!xpc) 1.3281 + return false; 1.3282 + 1.3283 + return true; 1.3284 +} 1.3285 + 1.3286 +bool 1.3287 +XPCJSRuntime::DescribeCustomObjects(JSObject* obj, const js::Class* clasp, 1.3288 + char (&name)[72]) const 1.3289 +{ 1.3290 + XPCNativeScriptableInfo *si = nullptr; 1.3291 + 1.3292 + if (!IS_PROTO_CLASS(clasp)) { 1.3293 + return false; 1.3294 + } 1.3295 + 1.3296 + XPCWrappedNativeProto *p = 1.3297 + static_cast<XPCWrappedNativeProto*>(xpc_GetJSPrivate(obj)); 1.3298 + si = p->GetScriptableInfo(); 1.3299 + 1.3300 + if (!si) { 1.3301 + return false; 1.3302 + } 1.3303 + 1.3304 + JS_snprintf(name, sizeof(name), "JS Object (%s - %s)", 1.3305 + clasp->name, si->GetJSClass()->name); 1.3306 + return true; 1.3307 +} 1.3308 + 1.3309 +bool 1.3310 +XPCJSRuntime::NoteCustomGCThingXPCOMChildren(const js::Class* clasp, JSObject* obj, 1.3311 + nsCycleCollectionTraversalCallback& cb) const 1.3312 +{ 1.3313 + if (clasp != &XPC_WN_Tearoff_JSClass) { 1.3314 + return false; 1.3315 + } 1.3316 + 1.3317 + // A tearoff holds a strong reference to its native object 1.3318 + // (see XPCWrappedNative::FlatJSObjectFinalized). Its XPCWrappedNative 1.3319 + // will be held alive through the parent of the JSObject of the tearoff. 1.3320 + XPCWrappedNativeTearOff *to = 1.3321 + static_cast<XPCWrappedNativeTearOff*>(xpc_GetJSPrivate(obj)); 1.3322 + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "xpc_GetJSPrivate(obj)->mNative"); 1.3323 + cb.NoteXPCOMChild(to->GetNative()); 1.3324 + return true; 1.3325 +} 1.3326 + 1.3327 +/***************************************************************************/ 1.3328 + 1.3329 +#ifdef DEBUG 1.3330 +static PLDHashOperator 1.3331 +WrappedJSClassMapDumpEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr, 1.3332 + uint32_t number, void *arg) 1.3333 +{ 1.3334 + ((IID2WrappedJSClassMap::Entry*)hdr)->value->DebugDump(*(int16_t*)arg); 1.3335 + return PL_DHASH_NEXT; 1.3336 +} 1.3337 +static PLDHashOperator 1.3338 +NativeSetDumpEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr, 1.3339 + uint32_t number, void *arg) 1.3340 +{ 1.3341 + ((NativeSetMap::Entry*)hdr)->key_value->DebugDump(*(int16_t*)arg); 1.3342 + return PL_DHASH_NEXT; 1.3343 +} 1.3344 +#endif 1.3345 + 1.3346 +void 1.3347 +XPCJSRuntime::DebugDump(int16_t depth) 1.3348 +{ 1.3349 +#ifdef DEBUG 1.3350 + depth--; 1.3351 + XPC_LOG_ALWAYS(("XPCJSRuntime @ %x", this)); 1.3352 + XPC_LOG_INDENT(); 1.3353 + XPC_LOG_ALWAYS(("mJSRuntime @ %x", Runtime())); 1.3354 + 1.3355 + XPC_LOG_ALWAYS(("mWrappedJSToReleaseArray @ %x with %d wrappers(s)", \ 1.3356 + &mWrappedJSToReleaseArray, 1.3357 + mWrappedJSToReleaseArray.Length())); 1.3358 + 1.3359 + int cxCount = 0; 1.3360 + JSContext* iter = nullptr; 1.3361 + while (JS_ContextIterator(Runtime(), &iter)) 1.3362 + ++cxCount; 1.3363 + XPC_LOG_ALWAYS(("%d JS context(s)", cxCount)); 1.3364 + 1.3365 + iter = nullptr; 1.3366 + while (JS_ContextIterator(Runtime(), &iter)) { 1.3367 + XPCContext *xpc = XPCContext::GetXPCContext(iter); 1.3368 + XPC_LOG_INDENT(); 1.3369 + xpc->DebugDump(depth); 1.3370 + XPC_LOG_OUTDENT(); 1.3371 + } 1.3372 + 1.3373 + XPC_LOG_ALWAYS(("mWrappedJSClassMap @ %x with %d wrapperclasses(s)", \ 1.3374 + mWrappedJSClassMap, mWrappedJSClassMap ? \ 1.3375 + mWrappedJSClassMap->Count() : 0)); 1.3376 + // iterate wrappersclasses... 1.3377 + if (depth && mWrappedJSClassMap && mWrappedJSClassMap->Count()) { 1.3378 + XPC_LOG_INDENT(); 1.3379 + mWrappedJSClassMap->Enumerate(WrappedJSClassMapDumpEnumerator, &depth); 1.3380 + XPC_LOG_OUTDENT(); 1.3381 + } 1.3382 + XPC_LOG_ALWAYS(("mWrappedJSMap @ %x with %d wrappers(s)", \ 1.3383 + mWrappedJSMap, mWrappedJSMap ? \ 1.3384 + mWrappedJSMap->Count() : 0)); 1.3385 + // iterate wrappers... 1.3386 + if (depth && mWrappedJSMap && mWrappedJSMap->Count()) { 1.3387 + XPC_LOG_INDENT(); 1.3388 + mWrappedJSMap->Dump(depth); 1.3389 + XPC_LOG_OUTDENT(); 1.3390 + } 1.3391 + 1.3392 + XPC_LOG_ALWAYS(("mIID2NativeInterfaceMap @ %x with %d interface(s)", \ 1.3393 + mIID2NativeInterfaceMap, mIID2NativeInterfaceMap ? \ 1.3394 + mIID2NativeInterfaceMap->Count() : 0)); 1.3395 + 1.3396 + XPC_LOG_ALWAYS(("mClassInfo2NativeSetMap @ %x with %d sets(s)", \ 1.3397 + mClassInfo2NativeSetMap, mClassInfo2NativeSetMap ? \ 1.3398 + mClassInfo2NativeSetMap->Count() : 0)); 1.3399 + 1.3400 + XPC_LOG_ALWAYS(("mThisTranslatorMap @ %x with %d translator(s)", \ 1.3401 + mThisTranslatorMap, mThisTranslatorMap ? \ 1.3402 + mThisTranslatorMap->Count() : 0)); 1.3403 + 1.3404 + XPC_LOG_ALWAYS(("mNativeSetMap @ %x with %d sets(s)", \ 1.3405 + mNativeSetMap, mNativeSetMap ? \ 1.3406 + mNativeSetMap->Count() : 0)); 1.3407 + 1.3408 + // iterate sets... 1.3409 + if (depth && mNativeSetMap && mNativeSetMap->Count()) { 1.3410 + XPC_LOG_INDENT(); 1.3411 + mNativeSetMap->Enumerate(NativeSetDumpEnumerator, &depth); 1.3412 + XPC_LOG_OUTDENT(); 1.3413 + } 1.3414 + 1.3415 + XPC_LOG_OUTDENT(); 1.3416 +#endif 1.3417 +} 1.3418 + 1.3419 +/***************************************************************************/ 1.3420 + 1.3421 +void 1.3422 +XPCRootSetElem::AddToRootSet(XPCRootSetElem **listHead) 1.3423 +{ 1.3424 + MOZ_ASSERT(!mSelfp, "Must be not linked"); 1.3425 + 1.3426 + mSelfp = listHead; 1.3427 + mNext = *listHead; 1.3428 + if (mNext) { 1.3429 + MOZ_ASSERT(mNext->mSelfp == listHead, "Must be list start"); 1.3430 + mNext->mSelfp = &mNext; 1.3431 + } 1.3432 + *listHead = this; 1.3433 +} 1.3434 + 1.3435 +void 1.3436 +XPCRootSetElem::RemoveFromRootSet() 1.3437 +{ 1.3438 + nsXPConnect *xpc = nsXPConnect::XPConnect(); 1.3439 + JS::PokeGC(xpc->GetRuntime()->Runtime()); 1.3440 + 1.3441 + MOZ_ASSERT(mSelfp, "Must be linked"); 1.3442 + 1.3443 + MOZ_ASSERT(*mSelfp == this, "Link invariant"); 1.3444 + *mSelfp = mNext; 1.3445 + if (mNext) 1.3446 + mNext->mSelfp = mSelfp; 1.3447 +#ifdef DEBUG 1.3448 + mSelfp = nullptr; 1.3449 + mNext = nullptr; 1.3450 +#endif 1.3451 +} 1.3452 + 1.3453 +void 1.3454 +XPCJSRuntime::AddGCCallback(xpcGCCallback cb) 1.3455 +{ 1.3456 + MOZ_ASSERT(cb, "null callback"); 1.3457 + extraGCCallbacks.AppendElement(cb); 1.3458 +} 1.3459 + 1.3460 +void 1.3461 +XPCJSRuntime::RemoveGCCallback(xpcGCCallback cb) 1.3462 +{ 1.3463 + MOZ_ASSERT(cb, "null callback"); 1.3464 + bool found = extraGCCallbacks.RemoveElement(cb); 1.3465 + if (!found) { 1.3466 + NS_ERROR("Removing a callback which was never added."); 1.3467 + } 1.3468 +} 1.3469 + 1.3470 +void 1.3471 +XPCJSRuntime::AddContextCallback(xpcContextCallback cb) 1.3472 +{ 1.3473 + MOZ_ASSERT(cb, "null callback"); 1.3474 + extraContextCallbacks.AppendElement(cb); 1.3475 +} 1.3476 + 1.3477 +void 1.3478 +XPCJSRuntime::RemoveContextCallback(xpcContextCallback cb) 1.3479 +{ 1.3480 + MOZ_ASSERT(cb, "null callback"); 1.3481 + bool found = extraContextCallbacks.RemoveElement(cb); 1.3482 + if (!found) { 1.3483 + NS_ERROR("Removing a callback which was never added."); 1.3484 + } 1.3485 +} 1.3486 + 1.3487 +JSObject * 1.3488 +XPCJSRuntime::GetJunkScope() 1.3489 +{ 1.3490 + if (!mJunkScope) { 1.3491 + AutoSafeJSContext cx; 1.3492 + SandboxOptions options; 1.3493 + options.sandboxName.AssignLiteral("XPConnect Junk Compartment"); 1.3494 + RootedValue v(cx); 1.3495 + nsresult rv = CreateSandboxObject(cx, &v, nsContentUtils::GetSystemPrincipal(), options); 1.3496 + NS_ENSURE_SUCCESS(rv, nullptr); 1.3497 + 1.3498 + mJunkScope = js::UncheckedUnwrap(&v.toObject()); 1.3499 + } 1.3500 + return mJunkScope; 1.3501 +} 1.3502 + 1.3503 +JSObject * 1.3504 +XPCJSRuntime::GetCompilationScope() 1.3505 +{ 1.3506 + if (!mCompilationScope) { 1.3507 + AutoSafeJSContext cx; 1.3508 + SandboxOptions options; 1.3509 + options.sandboxName.AssignLiteral("XPConnect Compilation Compartment"); 1.3510 + options.invisibleToDebugger = true; 1.3511 + options.discardSource = ShouldDiscardSystemSource(); 1.3512 + RootedValue v(cx); 1.3513 + nsresult rv = CreateSandboxObject(cx, &v, /* principal = */ nullptr, options); 1.3514 + NS_ENSURE_SUCCESS(rv, nullptr); 1.3515 + 1.3516 + mCompilationScope = js::UncheckedUnwrap(&v.toObject()); 1.3517 + } 1.3518 + return mCompilationScope; 1.3519 +} 1.3520 + 1.3521 + 1.3522 +void 1.3523 +XPCJSRuntime::DeleteSingletonScopes() 1.3524 +{ 1.3525 + mJunkScope = nullptr; 1.3526 + mCompilationScope = nullptr; 1.3527 +}