1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "vm/Runtime.h"
9 #include "mozilla/Atomics.h"
10 #include "mozilla/DebugOnly.h"
12 # include "mozilla/intl/Locale.h"
14 #include "mozilla/MemoryReporting.h"
15 #include "mozilla/ThreadLocal.h"
20 #include "jsfriendapi.h"
23 #include "frontend/CompilationStencil.h"
24 #include "frontend/ParserAtom.h" // frontend::WellKnownParserAtoms
26 #include "gc/PublicIterators.h"
27 #include "jit/IonCompileTask.h"
28 #include "jit/JitRuntime.h"
29 #include "jit/Simulator.h"
30 #include "js/AllocationLogging.h" // JS_COUNT_CTOR, JS_COUNT_DTOR
31 #include "js/experimental/JSStencil.h"
32 #include "js/experimental/SourceHook.h"
33 #include "js/friend/ErrorMessages.h" // JSMSG_*
34 #include "js/Interrupt.h"
35 #include "js/MemoryMetrics.h"
36 #include "js/Stack.h" // JS::NativeStackLimitMin
37 #include "js/Wrapper.h"
38 #include "js/WrapperCallbacks.h"
39 #include "vm/DateTime.h"
40 #include "vm/JSObject.h"
41 #include "vm/JSScript.h"
42 #include "vm/PromiseObject.h" // js::PromiseObject
43 #include "vm/SharedImmutableStringsCache.h"
44 #include "vm/Warnings.h" // js::WarnNumberUC
45 #include "wasm/WasmSignalHandlers.h"
47 #include "debugger/DebugAPI-inl.h"
48 #include "gc/ArenaList-inl.h"
49 #include "vm/JSContext-inl.h"
50 #include "vm/Realm-inl.h"
54 using mozilla::Atomic
;
55 using mozilla::DebugOnly
;
56 using mozilla::NegativeInfinity
;
57 using mozilla::PositiveInfinity
;
59 /* static */ MOZ_THREAD_LOCAL(JSContext
*) js::TlsContext
;
61 Atomic
<size_t> JSRuntime::liveRuntimesCount
;
62 Atomic
<JS::LargeAllocationFailureCallback
> js::OnLargeAllocationFailure
;
64 JS::FilenameValidationCallback
js::gFilenameValidationCallback
= nullptr;
69 bool gCanUseExtraThreads
= true;
71 bool gCanUseExtraThreads
= false;
75 void js::DisableExtraThreads() { gCanUseExtraThreads
= false; }
77 const JSSecurityCallbacks
js::NullSecurityCallbacks
= {};
79 static const JSWrapObjectCallbacks DefaultWrapObjectCallbacks
= {
80 TransparentObjectWrapper
, nullptr};
82 extern bool DefaultHostEnsureCanAddPrivateElementCallback(JSContext
* cx
,
85 static size_t ReturnZeroSize(const void* p
) { return 0; }
87 JSRuntime::JSRuntime(JSRuntime
* parentRuntime
)
88 : parentRuntime(parentRuntime
),
90 updateChildRuntimeCount(parentRuntime
),
93 mainContext_(nullptr),
94 profilerSampleBufferRangeStart_(0),
95 telemetryCallback(nullptr),
96 consumeStreamCallback(nullptr),
97 reportStreamErrorCallback(nullptr),
98 hadOutOfMemory(false),
99 allowRelazificationForTesting(false),
100 destroyCompartmentCallback(nullptr),
101 sizeOfIncludingThisCompartmentCallback(nullptr),
102 destroyRealmCallback(nullptr),
103 realmNameCallback(nullptr),
104 securityCallbacks(&NullSecurityCallbacks
),
105 DOMcallbacks(nullptr),
106 destroyPrincipals(nullptr),
107 readPrincipals(nullptr),
108 canAddPrivateElement(&DefaultHostEnsureCanAddPrivateElementCallback
),
109 warningReporter(nullptr),
110 geckoProfiler_(thisFromCtor()),
111 trustedPrincipals_(nullptr),
112 wrapObjectCallbacks(&DefaultWrapObjectCallbacks
),
113 preserveWrapperCallback(nullptr),
114 scriptEnvironmentPreparer(nullptr),
115 ctypesActivityCallback(nullptr),
116 windowProxyClass_(nullptr),
118 numDebuggeeRealms_(0),
119 numDebuggeeRealmsObservingCoverage_(0),
120 localeCallbacks(nullptr),
121 defaultLocale(nullptr),
122 profilingScripts(false),
123 scriptAndCountsVector(nullptr),
124 watchtowerTestingLog(nullptr),
125 jitRuntime_(nullptr),
127 emptyString(nullptr),
129 thousandsSeparator(nullptr),
130 decimalSeparator(nullptr),
131 numGrouping(nullptr),
133 beingDestroyed_(false),
134 allowContentJS_(true),
136 permanentAtoms_(nullptr),
137 staticStrings(nullptr),
138 commonNames(nullptr),
139 wellKnownSymbols(nullptr),
140 scriptDataTableHolder_(SharedScriptDataTableHolder::NeedsLock::No
),
142 beforeWaitCallback(nullptr),
143 afterWaitCallback(nullptr),
144 offthreadIonCompilationEnabled_(true),
145 parallelParsingEnabled_(true),
146 autoWritableJitCodeActive_(false),
147 oomCallback(nullptr),
148 debuggerMallocSizeOf(ReturnZeroSize
),
149 stackFormat_(parentRuntime
? js::StackFormat::Default
150 : js::StackFormat::SpiderMonkey
),
151 wasmInstances(mutexid::WasmRuntimeInstances
),
152 moduleAsyncEvaluatingPostOrder(ASYNC_EVALUATING_POST_ORDER_INIT
) {
153 JS_COUNT_CTOR(JSRuntime
);
157 // See function comment for why we call this now, not in JS_Init().
158 wasm::EnsureEagerProcessSignalHandlers();
162 JSRuntime::~JSRuntime() {
163 JS_COUNT_DTOR(JSRuntime
);
164 MOZ_ASSERT(!initialized_
);
166 DebugOnly
<size_t> oldCount
= liveRuntimesCount
--;
167 MOZ_ASSERT(oldCount
> 0);
169 MOZ_ASSERT(wasmInstances
.lock()->empty());
171 MOZ_ASSERT(numRealms
== 0);
172 MOZ_ASSERT(numDebuggeeRealms_
== 0);
173 MOZ_ASSERT(numDebuggeeRealmsObservingCoverage_
== 0);
176 bool JSRuntime::init(JSContext
* cx
, uint32_t maxbytes
) {
178 MOZ_ASSERT(!initialized_
);
182 if (CanUseExtraThreads() && !EnsureHelperThreadsInitialized()) {
188 if (!gc
.init(maxbytes
)) {
192 if (!InitRuntimeNumberState(this)) {
196 // As a hack, we clear our timezone cache every time we create a new runtime.
197 // Also see the comment in JS::Realm::init().
198 js::ResetTimeZoneInternal(ResetTimeZoneMode::DontResetIfOffsetUnchanged
);
200 caches().megamorphicSetPropCache
= MakeUnique
<MegamorphicSetPropCache
>();
201 if (!caches().megamorphicSetPropCache
) {
208 void JSRuntime::destroyRuntime() {
209 MOZ_ASSERT(!JS::RuntimeHeapIsBusy());
210 MOZ_ASSERT(childRuntimeCount
== 0);
211 MOZ_ASSERT(initialized_
);
213 #ifdef JS_HAS_INTL_API
214 sharedIntlData
.ref().destroyInstance();
217 watchtowerTestingLog
.ref().reset();
219 // Caches might hold on ScriptData which are saved in the ScriptDataTable.
220 // Clear all stencils from caches to remove ScriptDataTable entries.
221 caches().purgeStencils();
223 if (gc
.wasInitialized()) {
225 * Finish any in-progress GCs first.
227 JSContext
* cx
= mainContextFromOwnThread();
228 if (JS::IsIncrementalGCInProgress(cx
)) {
232 /* Free source hook early, as its destructor may want to delete roots. */
233 sourceHook
= nullptr;
236 * Cancel any pending, in progress or completed Ion compilations and
237 * parse tasks. Waiting for wasm and compression tasks is done
238 * synchronously (on the main thread or during parse tasks), so no
239 * explicit canceling is needed for these.
241 CancelOffThreadIonCompile(this);
242 CancelOffThreadDelazify(this);
243 CancelOffThreadCompressions(this);
246 * Flag us as being destroyed. This allows the GC to free things like
247 * interned atoms and Ion trampolines.
249 beingDestroyed_
= true;
251 /* Remove persistent GC roots. */
254 /* Allow the GC to release scripts that were being profiled. */
255 profilingScripts
= false;
257 JS::PrepareForFullGC(cx
);
258 gc
.gc(JS::GCOptions::Shutdown
, JS::GCReason::DESTROY_RUNTIME
);
261 AutoNoteSingleThreadedRegion anstr
;
263 MOZ_ASSERT(scriptDataTableHolder().getWithoutLock().empty());
266 FinishRuntimeNumberState(this);
271 defaultLocale
= nullptr;
272 js_delete(jitRuntime_
.ref());
275 initialized_
= false;
279 void JSRuntime::addTelemetry(JSMetric id
, uint32_t sample
) {
280 if (telemetryCallback
) {
281 (*telemetryCallback
)(id
, sample
);
285 void JSRuntime::setTelemetryCallback(
286 JSRuntime
* rt
, JSAccumulateTelemetryDataCallback callback
) {
287 rt
->telemetryCallback
= callback
;
290 void JSRuntime::setUseCounter(JSObject
* obj
, JSUseCounter counter
) {
291 if (useCounterCallback
) {
292 (*useCounterCallback
)(obj
, counter
);
296 void JSRuntime::setUseCounterCallback(JSRuntime
* rt
,
297 JSSetUseCounterCallback callback
) {
298 rt
->useCounterCallback
= callback
;
301 void JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
,
302 JS::RuntimeSizes
* rtSizes
) {
303 rtSizes
->object
+= mallocSizeOf(this);
305 rtSizes
->atomsTable
+= atoms().sizeOfIncludingThis(mallocSizeOf
);
306 rtSizes
->gc
.marker
+= gc
.markers
.sizeOfExcludingThis(mallocSizeOf
);
307 for (auto& marker
: gc
.markers
) {
308 rtSizes
->gc
.marker
+= marker
->sizeOfIncludingThis(mallocSizeOf
);
311 if (!parentRuntime
) {
312 rtSizes
->atomsTable
+= mallocSizeOf(staticStrings
);
313 rtSizes
->atomsTable
+= mallocSizeOf(commonNames
);
314 rtSizes
->atomsTable
+= permanentAtoms()->sizeOfIncludingThis(mallocSizeOf
);
316 rtSizes
->selfHostStencil
=
317 selfHostStencilInput_
->sizeOfIncludingThis(mallocSizeOf
) +
318 selfHostStencil_
->sizeOfIncludingThis(mallocSizeOf
) +
319 selfHostScriptMap
.ref().shallowSizeOfExcludingThis(mallocSizeOf
);
322 JSContext
* cx
= mainContextFromAnyThread();
323 rtSizes
->contexts
+= cx
->sizeOfIncludingThis(mallocSizeOf
);
324 rtSizes
->temporary
+= cx
->tempLifoAlloc().sizeOfExcludingThis(mallocSizeOf
);
325 rtSizes
->interpreterStack
+=
326 cx
->interpreterStack().sizeOfExcludingThis(mallocSizeOf
);
327 rtSizes
->uncompressedSourceCache
+=
328 caches().uncompressedSourceCache
.sizeOfExcludingThis(mallocSizeOf
);
330 rtSizes
->gc
.nurseryCommitted
+= gc
.nursery().committed();
331 rtSizes
->gc
.nurseryMallocedBuffers
+=
332 gc
.nursery().sizeOfMallocedBuffers(mallocSizeOf
);
333 gc
.storeBuffer().addSizeOfExcludingThis(mallocSizeOf
, &rtSizes
->gc
);
335 rtSizes
->gc
.nurseryMallocedBlockCache
+=
336 gc
.nursery().sizeOfMallocedBlockCache(mallocSizeOf
);
337 rtSizes
->gc
.nurseryTrailerBlockSets
+=
338 gc
.nursery().sizeOfTrailerBlockSets(mallocSizeOf
);
340 if (isMainRuntime()) {
341 rtSizes
->sharedImmutableStringsCache
+=
342 js::SharedImmutableStringsCache::getSingleton().sizeOfExcludingThis(
344 rtSizes
->atomsTable
+=
345 js::frontend::WellKnownParserAtoms::getSingleton().sizeOfExcludingThis(
349 #ifdef JS_HAS_INTL_API
350 rtSizes
->sharedIntlData
+=
351 sharedIntlData
.ref().sizeOfExcludingThis(mallocSizeOf
);
355 auto& table
= scriptDataTableHolder().getWithoutLock();
357 rtSizes
->scriptData
+= table
.shallowSizeOfExcludingThis(mallocSizeOf
);
358 for (SharedImmutableScriptDataTable::Range r
= table
.all(); !r
.empty();
360 rtSizes
->scriptData
+= r
.front()->sizeOfIncludingThis(mallocSizeOf
);
364 if (isMainRuntime()) {
365 AutoLockGlobalScriptData lock
;
367 auto& table
= js::globalSharedScriptDataTableHolder
.get(lock
);
369 rtSizes
->scriptData
+= table
.shallowSizeOfExcludingThis(mallocSizeOf
);
370 for (SharedImmutableScriptDataTable::Range r
= table
.all(); !r
.empty();
372 rtSizes
->scriptData
+= r
.front()->sizeOfIncludingThis(mallocSizeOf
);
377 // Sizes of the IonCompileTasks we are holding for lazy linking
378 for (auto* task
: jitRuntime_
->ionLazyLinkList(this)) {
379 rtSizes
->jitLazyLink
+= task
->sizeOfExcludingThis(mallocSizeOf
);
383 rtSizes
->wasmRuntime
+=
384 wasmInstances
.lock()->sizeOfExcludingThis(mallocSizeOf
);
387 static bool HandleInterrupt(JSContext
* cx
, bool invokeCallback
) {
388 MOZ_ASSERT(!cx
->zone()->isAtomsZone());
390 cx
->runtime()->gc
.gcIfRequested();
392 // A worker thread may have requested an interrupt after finishing an Ion
394 jit::AttachFinishedCompilations(cx
);
396 // Don't call the interrupt callback if we only interrupted for GC or Ion.
397 if (!invokeCallback
) {
401 // Important: Additional callbacks can occur inside the callback handler
402 // if it re-enters the JS engine. The embedding must ensure that the
403 // callback is disconnected before attempting such re-entry.
404 if (cx
->interruptCallbackDisabled
) {
409 for (JSInterruptCallback cb
: cx
->interruptCallbacks()) {
416 // Debugger treats invoking the interrupt callback as a "step", so
417 // invoke the onStep handler.
418 if (cx
->realm()->isDebuggee()) {
419 ScriptFrameIter
iter(cx
);
420 if (!iter
.done() && cx
->compartment() == iter
.compartment() &&
421 DebugAPI::stepModeEnabled(iter
.script())) {
422 if (!DebugAPI::onSingleStep(cx
)) {
431 // No need to set aside any pending exception here: ComputeStackString
432 // already does that.
433 JSString
* stack
= ComputeStackString(cx
);
435 UniqueTwoByteChars stringChars
;
437 stringChars
= JS_CopyStringCharsZ(cx
, stack
);
439 cx
->recoverFromOutOfMemory();
443 const char16_t
* chars
;
445 chars
= stringChars
.get();
447 chars
= u
"(stack not available)";
449 WarnNumberUC(cx
, JSMSG_TERMINATED
, chars
);
453 void JSContext::requestInterrupt(InterruptReason reason
) {
454 interruptBits_
|= uint32_t(reason
);
455 jitStackLimit
= JS::NativeStackLimitMin
;
457 if (reason
== InterruptReason::CallbackUrgent
) {
458 // If this interrupt is urgent (slow script dialog for instance), take
459 // additional steps to interrupt corner cases where the above fields are
460 // not regularly polled.
462 if (fx
.isWaiting()) {
463 fx
.notify(FutexThread::NotifyForJSInterrupt
);
468 if (reason
== InterruptReason::CallbackUrgent
||
469 reason
== InterruptReason::MajorGC
||
470 reason
== InterruptReason::MinorGC
) {
471 wasm::InterruptRunningCode(this);
475 bool JSContext::handleInterrupt() {
476 MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime()));
477 if (hasAnyPendingInterrupt() || jitStackLimit
== JS::NativeStackLimitMin
) {
478 bool invokeCallback
=
479 hasPendingInterrupt(InterruptReason::CallbackUrgent
) ||
480 hasPendingInterrupt(InterruptReason::CallbackCanWait
);
482 resetJitStackLimit();
483 return HandleInterrupt(this, invokeCallback
);
488 void JSContext::clearPendingInterrupt(js::InterruptReason reason
) {
489 // Interrupt bit have already been cleared.
490 interruptBits_
&= ~uint32_t(reason
);
493 bool JSRuntime::setDefaultLocale(const char* locale
) {
498 UniqueChars newLocale
= DuplicateString(mainContextFromOwnThread(), locale
);
503 defaultLocale
.ref() = std::move(newLocale
);
507 void JSRuntime::resetDefaultLocale() { defaultLocale
= nullptr; }
509 const char* JSRuntime::getDefaultLocale() {
510 if (defaultLocale
.ref()) {
511 return defaultLocale
.ref().get();
514 // Use ICU if available to retrieve the default locale, this ensures ICU's
515 // default locale matches our default locale.
517 const char* locale
= mozilla::intl::Locale::GetDefaultLocale();
519 const char* locale
= setlocale(LC_ALL
, nullptr);
522 // convert to a well-formed BCP 47 language tag
523 if (!locale
|| !strcmp(locale
, "C")) {
527 UniqueChars lang
= DuplicateString(mainContextFromOwnThread(), locale
);
533 if ((p
= strchr(lang
.get(), '.'))) {
536 while ((p
= strchr(lang
.get(), '_'))) {
540 defaultLocale
.ref() = std::move(lang
);
541 return defaultLocale
.ref().get();
544 #ifdef JS_HAS_INTL_API
545 void JSRuntime::traceSharedIntlData(JSTracer
* trc
) {
546 sharedIntlData
.ref().trace(trc
);
550 SharedScriptDataTableHolder
& JSRuntime::scriptDataTableHolder() {
551 return scriptDataTableHolder_
;
554 GlobalObject
* JSRuntime::getIncumbentGlobal(JSContext
* cx
) {
555 MOZ_ASSERT(cx
->jobQueue
);
557 JSObject
* obj
= cx
->jobQueue
->getIncumbentGlobal(cx
);
562 MOZ_ASSERT(obj
->is
<GlobalObject
>(),
563 "getIncumbentGlobalCallback must return a global!");
564 return &obj
->as
<GlobalObject
>();
567 bool JSRuntime::enqueuePromiseJob(JSContext
* cx
, HandleFunction job
,
568 HandleObject promise
,
569 Handle
<GlobalObject
*> incumbentGlobal
) {
570 MOZ_ASSERT(cx
->jobQueue
,
571 "Must select a JobQueue implementation using JS::JobQueue "
572 "or js::UseInternalJobQueues before using Promises");
574 RootedObject
allocationSite(cx
);
577 AssertSameCompartment(job
, promise
);
580 RootedObject
unwrappedPromise(cx
, promise
);
581 // While the job object is guaranteed to be unwrapped, the promise
582 // might be wrapped. See the comments in EnqueuePromiseReactionJob in
583 // builtin/Promise.cpp for details.
584 if (IsWrapper(promise
)) {
585 unwrappedPromise
= UncheckedUnwrap(promise
);
587 if (unwrappedPromise
->is
<PromiseObject
>()) {
588 allocationSite
= JS::GetPromiseAllocationSite(unwrappedPromise
);
591 return cx
->jobQueue
->enqueuePromiseJob(cx
, promise
, job
, allocationSite
,
595 void JSRuntime::addUnhandledRejectedPromise(JSContext
* cx
,
596 js::HandleObject promise
) {
597 MOZ_ASSERT(promise
->is
<PromiseObject
>());
598 if (!cx
->promiseRejectionTrackerCallback
) {
602 bool mutedErrors
= false;
603 if (JSScript
* script
= cx
->currentScript()) {
604 mutedErrors
= script
->mutedErrors();
607 void* data
= cx
->promiseRejectionTrackerCallbackData
;
608 cx
->promiseRejectionTrackerCallback(
609 cx
, mutedErrors
, promise
, JS::PromiseRejectionHandlingState::Unhandled
,
613 void JSRuntime::removeUnhandledRejectedPromise(JSContext
* cx
,
614 js::HandleObject promise
) {
615 MOZ_ASSERT(promise
->is
<PromiseObject
>());
616 if (!cx
->promiseRejectionTrackerCallback
) {
620 bool mutedErrors
= false;
621 if (JSScript
* script
= cx
->currentScript()) {
622 mutedErrors
= script
->mutedErrors();
625 void* data
= cx
->promiseRejectionTrackerCallbackData
;
626 cx
->promiseRejectionTrackerCallback(
627 cx
, mutedErrors
, promise
, JS::PromiseRejectionHandlingState::Handled
,
631 mozilla::non_crypto::XorShift128PlusRNG
& JSRuntime::randomKeyGenerator() {
632 MOZ_ASSERT(CurrentThreadCanAccessRuntime(this));
633 if (randomKeyGenerator_
.isNothing()) {
634 mozilla::Array
<uint64_t, 2> seed
;
635 GenerateXorShift128PlusSeed(seed
);
636 randomKeyGenerator_
.emplace(seed
[0], seed
[1]);
638 return randomKeyGenerator_
.ref();
641 mozilla::HashCodeScrambler
JSRuntime::randomHashCodeScrambler() {
642 auto& rng
= randomKeyGenerator();
643 return mozilla::HashCodeScrambler(rng
.next(), rng
.next());
646 mozilla::non_crypto::XorShift128PlusRNG
JSRuntime::forkRandomKeyGenerator() {
647 auto& rng
= randomKeyGenerator();
648 return mozilla::non_crypto::XorShift128PlusRNG(rng
.next(), rng
.next());
651 js::HashNumber
JSRuntime::randomHashCode() {
652 MOZ_ASSERT(CurrentThreadCanAccessRuntime(this));
654 if (randomHashCodeGenerator_
.isNothing()) {
655 mozilla::Array
<uint64_t, 2> seed
;
656 GenerateXorShift128PlusSeed(seed
);
657 randomHashCodeGenerator_
.emplace(seed
[0], seed
[1]);
660 return HashNumber(randomHashCodeGenerator_
->next());
663 JS_PUBLIC_API
void* JSRuntime::onOutOfMemory(AllocFunction allocFunc
,
664 arena_id_t arena
, size_t nbytes
,
666 JSContext
* maybecx
) {
667 MOZ_ASSERT_IF(allocFunc
!= AllocFunction::Realloc
, !reallocPtr
);
669 if (JS::RuntimeHeapIsBusy()) {
673 if (!oom::IsSimulatedOOMAllocation()) {
675 * Retry when we are done with the background sweeping and have stopped
676 * all the allocations and released the empty GC chunks.
678 gc
.onOutOfMallocMemory();
681 case AllocFunction::Malloc
:
682 p
= js_arena_malloc(arena
, nbytes
);
684 case AllocFunction::Calloc
:
685 p
= js_arena_calloc(arena
, nbytes
, 1);
687 case AllocFunction::Realloc
:
688 p
= js_arena_realloc(arena
, reallocPtr
, nbytes
);
699 ReportOutOfMemory(maybecx
);
704 void* JSRuntime::onOutOfMemoryCanGC(AllocFunction allocFunc
, arena_id_t arena
,
705 size_t bytes
, void* reallocPtr
) {
706 if (OnLargeAllocationFailure
&& bytes
>= LARGE_ALLOCATION
) {
707 OnLargeAllocationFailure();
709 return onOutOfMemory(allocFunc
, arena
, bytes
, reallocPtr
);
712 bool JSRuntime::activeGCInAtomsZone() {
713 Zone
* zone
= unsafeAtomsZone();
714 return (zone
->needsIncrementalBarrier() &&
715 !gc
.isVerifyPreBarriersEnabled()) ||
716 zone
->wasGCStarted();
719 void JSRuntime::incrementNumDebuggeeRealms() {
720 if (numDebuggeeRealms_
== 0) {
721 jitRuntime()->baselineInterpreter().toggleDebuggerInstrumentation(true);
724 numDebuggeeRealms_
++;
725 MOZ_ASSERT(numDebuggeeRealms_
<= numRealms
);
728 void JSRuntime::decrementNumDebuggeeRealms() {
729 MOZ_ASSERT(numDebuggeeRealms_
> 0);
730 numDebuggeeRealms_
--;
732 // Note: if we had shutdown leaks we can end up here while destroying the
733 // runtime. It's not safe to access JitRuntime trampolines because they're no
735 if (numDebuggeeRealms_
== 0 && !isBeingDestroyed()) {
736 jitRuntime()->baselineInterpreter().toggleDebuggerInstrumentation(false);
740 void JSRuntime::incrementNumDebuggeeRealmsObservingCoverage() {
741 if (numDebuggeeRealmsObservingCoverage_
== 0) {
742 jit::BaselineInterpreter
& interp
= jitRuntime()->baselineInterpreter();
743 interp
.toggleCodeCoverageInstrumentation(true);
746 numDebuggeeRealmsObservingCoverage_
++;
747 MOZ_ASSERT(numDebuggeeRealmsObservingCoverage_
<= numRealms
);
750 void JSRuntime::decrementNumDebuggeeRealmsObservingCoverage() {
751 MOZ_ASSERT(numDebuggeeRealmsObservingCoverage_
> 0);
752 numDebuggeeRealmsObservingCoverage_
--;
754 // Note: if we had shutdown leaks we can end up here while destroying the
755 // runtime. It's not safe to access JitRuntime trampolines because they're no
757 if (numDebuggeeRealmsObservingCoverage_
== 0 && !isBeingDestroyed()) {
758 jit::BaselineInterpreter
& interp
= jitRuntime()->baselineInterpreter();
759 interp
.toggleCodeCoverageInstrumentation(false);
763 bool js::CurrentThreadCanAccessRuntime(const JSRuntime
* rt
) {
764 return rt
->mainContextFromAnyThread() == TlsContext
.get();
767 bool js::CurrentThreadCanAccessZone(Zone
* zone
) {
768 return CurrentThreadCanAccessRuntime(zone
->runtime_
);
772 bool js::CurrentThreadIsMainThread() { return !!TlsContext
.get(); }
775 JS_PUBLIC_API
void JS::SetJSContextProfilerSampleBufferRangeStart(
776 JSContext
* cx
, uint64_t rangeStart
) {
777 cx
->runtime()->setProfilerSampleBufferRangeStart(rangeStart
);
780 JS_PUBLIC_API
bool JS::IsProfilingEnabledForContext(JSContext
* cx
) {
782 return cx
->runtime()->geckoProfiler().enabled();
785 JS_PUBLIC_API
void JS::EnableRecordingAllocations(
786 JSContext
* cx
, JS::RecordAllocationsCallback callback
, double probability
) {
788 cx
->runtime()->startRecordingAllocations(probability
, callback
);
791 JS_PUBLIC_API
void JS::DisableRecordingAllocations(JSContext
* cx
) {
793 cx
->runtime()->stopRecordingAllocations();
796 JS_PUBLIC_API
void JS::shadow::RegisterWeakCache(
797 JSRuntime
* rt
, detail::WeakCacheBase
* cachep
) {
798 rt
->registerWeakCache(cachep
);
801 void JSRuntime::startRecordingAllocations(
802 double probability
, JS::RecordAllocationsCallback callback
) {
803 allocationSamplingProbability
= probability
;
804 recordAllocationCallback
= callback
;
806 // Go through all of the existing realms, and turn on allocation tracking.
807 for (RealmsIter
realm(this); !realm
.done(); realm
.next()) {
808 realm
->setAllocationMetadataBuilder(&SavedStacks::metadataBuilder
);
809 realm
->chooseAllocationSamplingProbability();
813 void JSRuntime::stopRecordingAllocations() {
814 recordAllocationCallback
= nullptr;
815 // Go through all of the existing realms, and turn on allocation tracking.
816 for (RealmsIter
realm(this); !realm
.done(); realm
.next()) {
817 js::GlobalObject
* global
= realm
->maybeGlobal();
818 if (!realm
->isDebuggee() || !global
||
819 !DebugAPI::isObservedByDebuggerTrackingAllocations(*global
)) {
820 // Only remove the allocation metadata builder if no Debuggers are
821 // tracking allocations.
822 realm
->forgetAllocationMetadataBuilder();
827 // This function can run to ensure that when new realms are created
828 // they have allocation logging turned on.
829 void JSRuntime::ensureRealmIsRecordingAllocations(
830 Handle
<GlobalObject
*> global
) {
831 if (recordAllocationCallback
) {
832 if (!global
->realm()->isRecordingAllocations()) {
833 // This is a new realm, turn on allocations for it.
834 global
->realm()->setAllocationMetadataBuilder(
835 &SavedStacks::metadataBuilder
);
837 // Ensure the probability is up to date with the current combination of
838 // debuggers and runtime profiling.
839 global
->realm()->chooseAllocationSamplingProbability();