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/. */
6
7 #include "mozilla/Atomics.h"
8 #include "mozilla/DebugOnly.h"
9 #include "mozilla/MemoryReporting.h"
10 #include "mozilla/ThreadLocal.h"
11
12 #if defined(XP_DARWIN)
13 # include <mach/mach.h>
14 #elif defined(XP_UNIX)
15 # include <sys/resource.h>
16 #endif // defined(XP_DARWIN) || defined(XP_UNIX) || defined(XP_WIN)
17 #include <locale.h>
18 #include <string.h>
19 #ifdef JS_CAN_CHECK_THREADSAFE_ACCESSES
20 # include <sys/mman.h>
21 #endif
22
23 #include "jsfriendapi.h"
24 #include "jsmath.h"
25
26 #include "gc/FreeOp.h"
27 #include "gc/PublicIterators.h"
28 #include "jit/IonCompileTask.h"
29 #include "jit/JitRuntime.h"
30 #include "jit/Simulator.h"
31 #include "js/AllocationLogging.h" // JS_COUNT_CTOR, JS_COUNT_DTOR
32 #include "js/Date.h"
33 #include "js/friend/ErrorMessages.h" // JSMSG_*
34 #include "js/MemoryMetrics.h"
35 #include "js/SliceBudget.h"
36 #include "js/Wrapper.h"
37 #if JS_HAS_INTL_API
38 # include "unicode/uloc.h"
39 #endif
40 #include "util/Windows.h"
41 #include "vm/DateTime.h"
42 #include "vm/JSAtom.h"
43 #include "vm/JSObject.h"
44 #include "vm/JSScript.h"
45 #include "vm/PromiseObject.h" // js::PromiseObject
46 #include "vm/TraceLogging.h"
47 #include "vm/TraceLoggingGraph.h"
48 #include "vm/Warnings.h" // js::WarnNumberUC
49 #include "wasm/WasmSignalHandlers.h"
50
51 #include "debugger/DebugAPI-inl.h"
52 #include "gc/GC-inl.h"
53 #include "vm/JSContext-inl.h"
54 #include "vm/Realm-inl.h"
55
56 using namespace js;
57
58 using mozilla::Atomic;
59 using mozilla::DebugOnly;
60 using mozilla::NegativeInfinity;
61 using mozilla::PositiveInfinity;
62
63 /* static */ MOZ_THREAD_LOCAL(JSContext*) js::TlsContext;
64 /* static */
65 Atomic<size_t> JSRuntime::liveRuntimesCount;
66 Atomic<JS::LargeAllocationFailureCallback> js::OnLargeAllocationFailure;
67
68 JS::FilenameValidationCallback js::gFilenameValidationCallback = nullptr;
69
70 namespace js {
71
72 #ifndef __wasi__
73 bool gCanUseExtraThreads = true;
74 #else
75 bool gCanUseExtraThreads = false;
76 #endif
77 } // namespace js
78
DisableExtraThreads()79 void js::DisableExtraThreads() { gCanUseExtraThreads = false; }
80
81 const JSSecurityCallbacks js::NullSecurityCallbacks = {};
82
83 static const JSWrapObjectCallbacks DefaultWrapObjectCallbacks = {
84 TransparentObjectWrapper, nullptr};
85
ReturnZeroSize(const void * p)86 static size_t ReturnZeroSize(const void* p) { return 0; }
87
JSRuntime(JSRuntime * parentRuntime)88 JSRuntime::JSRuntime(JSRuntime* parentRuntime)
89 : parentRuntime(parentRuntime),
90 #ifdef DEBUG
91 updateChildRuntimeCount(parentRuntime),
92 initialized_(false),
93 #endif
94 mainContext_(nullptr),
95 profilerSampleBufferRangeStart_(0),
96 telemetryCallback(nullptr),
97 consumeStreamCallback(nullptr),
98 reportStreamErrorCallback(nullptr),
99 hadOutOfMemory(false),
100 allowRelazificationForTesting(false),
101 destroyCompartmentCallback(nullptr),
102 sizeOfIncludingThisCompartmentCallback(nullptr),
103 destroyRealmCallback(nullptr),
104 realmNameCallback(nullptr),
105 securityCallbacks(&NullSecurityCallbacks),
106 DOMcallbacks(nullptr),
107 destroyPrincipals(nullptr),
108 readPrincipals(nullptr),
109 warningReporter(nullptr),
110 selfHostedLazyScript(),
111 geckoProfiler_(thisFromCtor()),
112 trustedPrincipals_(nullptr),
113 wrapObjectCallbacks(&DefaultWrapObjectCallbacks),
114 preserveWrapperCallback(nullptr),
115 scriptEnvironmentPreparer(nullptr),
116 ctypesActivityCallback(nullptr),
117 windowProxyClass_(nullptr),
118 scriptDataLock(mutexid::SharedImmutableScriptData),
119 #ifdef DEBUG
120 activeThreadHasScriptDataAccess(false),
121 #endif
122 numParseTasks(0),
123 numActiveHelperThreadZones(0),
124 numRealms(0),
125 numDebuggeeRealms_(0),
126 numDebuggeeRealmsObservingCoverage_(0),
127 localeCallbacks(nullptr),
128 defaultLocale(nullptr),
129 profilingScripts(false),
130 scriptAndCountsVector(nullptr),
131 lcovOutput_(),
132 jitRuntime_(nullptr),
133 selfHostingGlobal_(nullptr),
134 gc(thisFromCtor()),
135 gcInitialized(false),
136 emptyString(nullptr),
137 defaultFreeOp_(nullptr),
138 #if !JS_HAS_INTL_API
139 thousandsSeparator(nullptr),
140 decimalSeparator(nullptr),
141 numGrouping(nullptr),
142 #endif
143 beingDestroyed_(false),
144 allowContentJS_(true),
145 atoms_(nullptr),
146 permanentAtomsDuringInit_(nullptr),
147 permanentAtoms_(nullptr),
148 staticStrings(nullptr),
149 commonNames(nullptr),
150 wellKnownSymbols(nullptr),
151 liveSABs(0),
152 beforeWaitCallback(nullptr),
153 afterWaitCallback(nullptr),
154 offthreadIonCompilationEnabled_(true),
155 parallelParsingEnabled_(true),
156 #ifdef DEBUG
157 offThreadParsesRunning_(0),
158 offThreadParsingBlocked_(false),
159 #endif
160 autoWritableJitCodeActive_(false),
161 oomCallback(nullptr),
162 debuggerMallocSizeOf(ReturnZeroSize),
163 stackFormat_(parentRuntime ? js::StackFormat::Default
164 : js::StackFormat::SpiderMonkey),
165 wasmInstances(mutexid::WasmRuntimeInstances),
166 moduleResolveHook(),
167 moduleMetadataHook(),
168 moduleDynamicImportHook(),
169 scriptPrivateAddRefHook(),
170 scriptPrivateReleaseHook() {
171 JS_COUNT_CTOR(JSRuntime);
172 liveRuntimesCount++;
173
174 #ifndef __wasi__
175 // See function comment for why we call this now, not in JS_Init().
176 wasm::EnsureEagerProcessSignalHandlers();
177 #endif // __wasi__
178 }
179
~JSRuntime()180 JSRuntime::~JSRuntime() {
181 JS_COUNT_DTOR(JSRuntime);
182 MOZ_ASSERT(!initialized_);
183
184 DebugOnly<size_t> oldCount = liveRuntimesCount--;
185 MOZ_ASSERT(oldCount > 0);
186
187 MOZ_ASSERT(wasmInstances.lock()->empty());
188
189 MOZ_ASSERT(offThreadParsesRunning_ == 0);
190 MOZ_ASSERT(!offThreadParsingBlocked_);
191
192 MOZ_ASSERT(numRealms == 0);
193 MOZ_ASSERT(numDebuggeeRealms_ == 0);
194 MOZ_ASSERT(numDebuggeeRealmsObservingCoverage_ == 0);
195 }
196
init(JSContext * cx,uint32_t maxbytes)197 bool JSRuntime::init(JSContext* cx, uint32_t maxbytes) {
198 #ifdef DEBUG
199 MOZ_ASSERT(!initialized_);
200 initialized_ = true;
201 #endif
202
203 if (CanUseExtraThreads() && !EnsureHelperThreadsInitialized()) {
204 return false;
205 }
206
207 mainContext_ = cx;
208
209 defaultFreeOp_ = cx->defaultFreeOp();
210
211 if (!gc.init(maxbytes)) {
212 return false;
213 }
214
215 UniquePtr<Zone> atomsZone = MakeUnique<Zone>(this, Zone::AtomsZone);
216 if (!atomsZone || !atomsZone->init()) {
217 return false;
218 }
219
220 MOZ_ASSERT(atomsZone->isAtomsZone());
221 gc.atomsZone = atomsZone.release();
222
223 // The garbage collector depends on everything before this point being
224 // initialized.
225 gcInitialized = true;
226
227 if (!InitRuntimeNumberState(this)) {
228 return false;
229 }
230
231 // As a hack, we clear our timezone cache every time we create a new runtime.
232 // Also see the comment in JS::Realm::init().
233 js::ResetTimeZoneInternal(ResetTimeZoneMode::DontResetIfOffsetUnchanged);
234
235 if (!parentRuntime) {
236 sharedImmutableStrings_ = js::SharedImmutableStringsCache::Create();
237 if (!sharedImmutableStrings_) {
238 return false;
239 }
240 }
241
242 return true;
243 }
244
destroyRuntime()245 void JSRuntime::destroyRuntime() {
246 MOZ_ASSERT(!JS::RuntimeHeapIsBusy());
247 MOZ_ASSERT(childRuntimeCount == 0);
248 MOZ_ASSERT(initialized_);
249
250 #ifdef JS_HAS_INTL_API
251 sharedIntlData.ref().destroyInstance();
252 #endif
253
254 if (gcInitialized) {
255 /*
256 * Finish any in-progress GCs first. This ensures the parseWaitingOnGC
257 * list is empty in CancelOffThreadParses.
258 */
259 JSContext* cx = mainContextFromOwnThread();
260 if (JS::IsIncrementalGCInProgress(cx)) {
261 gc::FinishGC(cx);
262 }
263
264 /* Free source hook early, as its destructor may want to delete roots. */
265 sourceHook = nullptr;
266
267 /*
268 * Cancel any pending, in progress or completed Ion compilations and
269 * parse tasks. Waiting for wasm and compression tasks is done
270 * synchronously (on the main thread or during parse tasks), so no
271 * explicit canceling is needed for these.
272 */
273 CancelOffThreadIonCompile(this);
274 CancelOffThreadParses(this);
275 CancelOffThreadCompressions(this);
276
277 /*
278 * Flag us as being destroyed. This allows the GC to free things like
279 * interned atoms and Ion trampolines.
280 */
281 beingDestroyed_ = true;
282
283 /* Remove persistent GC roots. */
284 gc.finishRoots();
285
286 /* Allow the GC to release scripts that were being profiled. */
287 profilingScripts = false;
288
289 JS::PrepareForFullGC(cx);
290 gc.gc(JS::GCOptions::Normal, JS::GCReason::DESTROY_RUNTIME);
291 }
292
293 AutoNoteSingleThreadedRegion anstr;
294
295 MOZ_ASSERT(!hasHelperThreadZones());
296
297 #ifdef DEBUG
298 {
299 AutoLockScriptData lock(this);
300 MOZ_ASSERT(scriptDataTable(lock).empty());
301 }
302 #endif
303
304 #if !JS_HAS_INTL_API
305 FinishRuntimeNumberState(this);
306 #endif
307
308 gc.finish();
309
310 defaultLocale = nullptr;
311 js_delete(jitRuntime_.ref());
312
313 #ifdef DEBUG
314 initialized_ = false;
315 #endif
316 }
317
addTelemetry(int id,uint32_t sample,const char * key)318 void JSRuntime::addTelemetry(int id, uint32_t sample, const char* key) {
319 if (telemetryCallback) {
320 (*telemetryCallback)(id, sample, key);
321 }
322 }
323
getTelemetrySender() const324 JSTelemetrySender JSRuntime::getTelemetrySender() const {
325 return JSTelemetrySender(telemetryCallback);
326 }
327
setTelemetryCallback(JSRuntime * rt,JSAccumulateTelemetryDataCallback callback)328 void JSRuntime::setTelemetryCallback(
329 JSRuntime* rt, JSAccumulateTelemetryDataCallback callback) {
330 rt->telemetryCallback = callback;
331 }
332
setSourceElementCallback(JSRuntime * rt,JSSourceElementCallback callback)333 void JSRuntime::setSourceElementCallback(JSRuntime* rt,
334 JSSourceElementCallback callback) {
335 rt->sourceElementCallback = callback;
336 }
337
setUseCounter(JSObject * obj,JSUseCounter counter)338 void JSRuntime::setUseCounter(JSObject* obj, JSUseCounter counter) {
339 if (useCounterCallback) {
340 (*useCounterCallback)(obj, counter);
341 }
342 }
343
setUseCounterCallback(JSRuntime * rt,JSSetUseCounterCallback callback)344 void JSRuntime::setUseCounterCallback(JSRuntime* rt,
345 JSSetUseCounterCallback callback) {
346 rt->useCounterCallback = callback;
347 }
348
addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,JS::RuntimeSizes * rtSizes)349 void JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
350 JS::RuntimeSizes* rtSizes) {
351 rtSizes->object += mallocSizeOf(this);
352
353 rtSizes->atomsTable += atoms().sizeOfIncludingThis(mallocSizeOf);
354 rtSizes->gc.marker += gc.marker.sizeOfExcludingThis(mallocSizeOf);
355
356 if (!parentRuntime) {
357 rtSizes->atomsTable += mallocSizeOf(staticStrings);
358 rtSizes->atomsTable += mallocSizeOf(commonNames);
359 rtSizes->atomsTable += permanentAtoms()->sizeOfIncludingThis(mallocSizeOf);
360 rtSizes->atomsTable +=
361 commonParserNames.ref()->sizeOfIncludingThis(mallocSizeOf);
362 }
363
364 JSContext* cx = mainContextFromAnyThread();
365 rtSizes->contexts += cx->sizeOfIncludingThis(mallocSizeOf);
366 rtSizes->temporary += cx->tempLifoAlloc().sizeOfExcludingThis(mallocSizeOf);
367 rtSizes->interpreterStack +=
368 cx->interpreterStack().sizeOfExcludingThis(mallocSizeOf);
369 #ifdef JS_TRACE_LOGGING
370 if (cx->traceLogger) {
371 rtSizes->tracelogger += cx->traceLogger->sizeOfIncludingThis(mallocSizeOf);
372 }
373 #endif
374
375 rtSizes->uncompressedSourceCache +=
376 caches().uncompressedSourceCache.sizeOfExcludingThis(mallocSizeOf);
377
378 rtSizes->gc.nurseryCommitted += gc.nursery().committed();
379 rtSizes->gc.nurseryMallocedBuffers +=
380 gc.nursery().sizeOfMallocedBuffers(mallocSizeOf);
381 gc.storeBuffer().addSizeOfExcludingThis(mallocSizeOf, &rtSizes->gc);
382
383 if (sharedImmutableStrings_) {
384 rtSizes->sharedImmutableStringsCache +=
385 sharedImmutableStrings_->sizeOfExcludingThis(mallocSizeOf);
386 }
387
388 #ifdef JS_HAS_INTL_API
389 rtSizes->sharedIntlData +=
390 sharedIntlData.ref().sizeOfExcludingThis(mallocSizeOf);
391 #endif
392
393 {
394 AutoLockScriptData lock(this);
395 rtSizes->scriptData +=
396 scriptDataTable(lock).shallowSizeOfExcludingThis(mallocSizeOf);
397 for (SharedImmutableScriptDataTable::Range r = scriptDataTable(lock).all();
398 !r.empty(); r.popFront()) {
399 rtSizes->scriptData += r.front()->sizeOfIncludingThis(mallocSizeOf);
400 }
401 }
402
403 if (jitRuntime_) {
404 // Sizes of the IonCompileTasks we are holding for lazy linking
405 for (auto* task : jitRuntime_->ionLazyLinkList(this)) {
406 rtSizes->jitLazyLink += task->sizeOfExcludingThis(mallocSizeOf);
407 }
408 }
409
410 rtSizes->wasmRuntime +=
411 wasmInstances.lock()->sizeOfExcludingThis(mallocSizeOf);
412 }
413
HandleInterrupt(JSContext * cx,bool invokeCallback)414 static bool HandleInterrupt(JSContext* cx, bool invokeCallback) {
415 MOZ_ASSERT(!cx->zone()->isAtomsZone());
416
417 cx->runtime()->gc.gcIfRequested();
418
419 // A worker thread may have requested an interrupt after finishing an Ion
420 // compilation.
421 jit::AttachFinishedCompilations(cx);
422
423 // Don't call the interrupt callback if we only interrupted for GC or Ion.
424 if (!invokeCallback) {
425 return true;
426 }
427
428 // Important: Additional callbacks can occur inside the callback handler
429 // if it re-enters the JS engine. The embedding must ensure that the
430 // callback is disconnected before attempting such re-entry.
431 if (cx->interruptCallbackDisabled) {
432 return true;
433 }
434
435 bool stop = false;
436 for (JSInterruptCallback cb : cx->interruptCallbacks()) {
437 if (!cb(cx)) {
438 stop = true;
439 }
440 }
441
442 if (!stop) {
443 // Debugger treats invoking the interrupt callback as a "step", so
444 // invoke the onStep handler.
445 if (cx->realm()->isDebuggee()) {
446 ScriptFrameIter iter(cx);
447 if (!iter.done() && cx->compartment() == iter.compartment() &&
448 DebugAPI::stepModeEnabled(iter.script())) {
449 if (!DebugAPI::onSingleStep(cx)) {
450 return false;
451 }
452 }
453 }
454
455 return true;
456 }
457
458 // No need to set aside any pending exception here: ComputeStackString
459 // already does that.
460 JSString* stack = ComputeStackString(cx);
461
462 UniqueTwoByteChars stringChars;
463 if (stack) {
464 stringChars = JS_CopyStringCharsZ(cx, stack);
465 if (!stringChars) {
466 cx->recoverFromOutOfMemory();
467 }
468 }
469
470 const char16_t* chars;
471 if (stringChars) {
472 chars = stringChars.get();
473 } else {
474 chars = u"(stack not available)";
475 }
476 WarnNumberUC(cx, JSMSG_TERMINATED, chars);
477 return false;
478 }
479
requestInterrupt(InterruptReason reason)480 void JSContext::requestInterrupt(InterruptReason reason) {
481 interruptBits_ |= uint32_t(reason);
482 jitStackLimit = UINTPTR_MAX;
483
484 if (reason == InterruptReason::CallbackUrgent) {
485 // If this interrupt is urgent (slow script dialog for instance), take
486 // additional steps to interrupt corner cases where the above fields are
487 // not regularly polled.
488 FutexThread::lock();
489 if (fx.isWaiting()) {
490 fx.notify(FutexThread::NotifyForJSInterrupt);
491 }
492 fx.unlock();
493 wasm::InterruptRunningCode(this);
494 }
495 }
496
handleInterrupt()497 bool JSContext::handleInterrupt() {
498 MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime()));
499 if (hasAnyPendingInterrupt() || jitStackLimit == UINTPTR_MAX) {
500 bool invokeCallback =
501 hasPendingInterrupt(InterruptReason::CallbackUrgent) ||
502 hasPendingInterrupt(InterruptReason::CallbackCanWait);
503 interruptBits_ = 0;
504 resetJitStackLimit();
505 return HandleInterrupt(this, invokeCallback);
506 }
507 return true;
508 }
509
setDefaultLocale(const char * locale)510 bool JSRuntime::setDefaultLocale(const char* locale) {
511 if (!locale) {
512 return false;
513 }
514
515 UniqueChars newLocale = DuplicateString(mainContextFromOwnThread(), locale);
516 if (!newLocale) {
517 return false;
518 }
519
520 defaultLocale.ref() = std::move(newLocale);
521 return true;
522 }
523
resetDefaultLocale()524 void JSRuntime::resetDefaultLocale() { defaultLocale = nullptr; }
525
getDefaultLocale()526 const char* JSRuntime::getDefaultLocale() {
527 if (defaultLocale.ref()) {
528 return defaultLocale.ref().get();
529 }
530
531 // Use ICU if available to retrieve the default locale, this ensures ICU's
532 // default locale matches our default locale.
533 #if JS_HAS_INTL_API
534 const char* locale = uloc_getDefault();
535 #else
536 const char* locale = setlocale(LC_ALL, nullptr);
537 #endif
538
539 // convert to a well-formed BCP 47 language tag
540 if (!locale || !strcmp(locale, "C")) {
541 locale = "und";
542 }
543
544 UniqueChars lang = DuplicateString(mainContextFromOwnThread(), locale);
545 if (!lang) {
546 return nullptr;
547 }
548
549 char* p;
550 if ((p = strchr(lang.get(), '.'))) {
551 *p = '\0';
552 }
553 while ((p = strchr(lang.get(), '_'))) {
554 *p = '-';
555 }
556
557 defaultLocale.ref() = std::move(lang);
558 return defaultLocale.ref().get();
559 }
560
561 #ifdef JS_HAS_INTL_API
traceSharedIntlData(JSTracer * trc)562 void JSRuntime::traceSharedIntlData(JSTracer* trc) {
563 sharedIntlData.ref().trace(trc);
564 }
565 #endif
566
JSFreeOp(JSRuntime * maybeRuntime,bool isDefault)567 JSFreeOp::JSFreeOp(JSRuntime* maybeRuntime, bool isDefault)
568 : runtime_(maybeRuntime), isDefault(isDefault), isCollecting_(!isDefault) {
569 MOZ_ASSERT_IF(maybeRuntime, CurrentThreadCanAccessRuntime(maybeRuntime));
570 }
571
~JSFreeOp()572 JSFreeOp::~JSFreeOp() {
573 if (!jitPoisonRanges.empty()) {
574 jit::ExecutableAllocator::poisonCode(runtime(), jitPoisonRanges);
575 }
576 }
577
getIncumbentGlobal(JSContext * cx)578 GlobalObject* JSRuntime::getIncumbentGlobal(JSContext* cx) {
579 MOZ_ASSERT(cx->jobQueue);
580
581 JSObject* obj = cx->jobQueue->getIncumbentGlobal(cx);
582 if (!obj) {
583 return nullptr;
584 }
585
586 MOZ_ASSERT(obj->is<GlobalObject>(),
587 "getIncumbentGlobalCallback must return a global!");
588 return &obj->as<GlobalObject>();
589 }
590
enqueuePromiseJob(JSContext * cx,HandleFunction job,HandleObject promise,Handle<GlobalObject * > incumbentGlobal)591 bool JSRuntime::enqueuePromiseJob(JSContext* cx, HandleFunction job,
592 HandleObject promise,
593 Handle<GlobalObject*> incumbentGlobal) {
594 MOZ_ASSERT(cx->jobQueue,
595 "Must select a JobQueue implementation using JS::JobQueue "
596 "or js::UseInternalJobQueues before using Promises");
597
598 RootedObject allocationSite(cx);
599 if (promise) {
600 #ifdef DEBUG
601 AssertSameCompartment(job, promise);
602 #endif
603
604 RootedObject unwrappedPromise(cx, promise);
605 // While the job object is guaranteed to be unwrapped, the promise
606 // might be wrapped. See the comments in EnqueuePromiseReactionJob in
607 // builtin/Promise.cpp for details.
608 if (IsWrapper(promise)) {
609 unwrappedPromise = UncheckedUnwrap(promise);
610 }
611 if (unwrappedPromise->is<PromiseObject>()) {
612 allocationSite = JS::GetPromiseAllocationSite(unwrappedPromise);
613 }
614 }
615 return cx->jobQueue->enqueuePromiseJob(cx, promise, job, allocationSite,
616 incumbentGlobal);
617 }
618
addUnhandledRejectedPromise(JSContext * cx,js::HandleObject promise)619 void JSRuntime::addUnhandledRejectedPromise(JSContext* cx,
620 js::HandleObject promise) {
621 MOZ_ASSERT(promise->is<PromiseObject>());
622 if (!cx->promiseRejectionTrackerCallback) {
623 return;
624 }
625
626 bool mutedErrors = false;
627 if (JSScript* script = cx->currentScript()) {
628 mutedErrors = script->mutedErrors();
629 }
630
631 void* data = cx->promiseRejectionTrackerCallbackData;
632 cx->promiseRejectionTrackerCallback(
633 cx, mutedErrors, promise, JS::PromiseRejectionHandlingState::Unhandled,
634 data);
635 }
636
removeUnhandledRejectedPromise(JSContext * cx,js::HandleObject promise)637 void JSRuntime::removeUnhandledRejectedPromise(JSContext* cx,
638 js::HandleObject promise) {
639 MOZ_ASSERT(promise->is<PromiseObject>());
640 if (!cx->promiseRejectionTrackerCallback) {
641 return;
642 }
643
644 bool mutedErrors = false;
645 if (JSScript* script = cx->currentScript()) {
646 mutedErrors = script->mutedErrors();
647 }
648
649 void* data = cx->promiseRejectionTrackerCallbackData;
650 cx->promiseRejectionTrackerCallback(
651 cx, mutedErrors, promise, JS::PromiseRejectionHandlingState::Handled,
652 data);
653 }
654
randomKeyGenerator()655 mozilla::non_crypto::XorShift128PlusRNG& JSRuntime::randomKeyGenerator() {
656 MOZ_ASSERT(CurrentThreadCanAccessRuntime(this));
657 if (randomKeyGenerator_.isNothing()) {
658 mozilla::Array<uint64_t, 2> seed;
659 GenerateXorShift128PlusSeed(seed);
660 randomKeyGenerator_.emplace(seed[0], seed[1]);
661 }
662 return randomKeyGenerator_.ref();
663 }
664
randomHashCodeScrambler()665 mozilla::HashCodeScrambler JSRuntime::randomHashCodeScrambler() {
666 auto& rng = randomKeyGenerator();
667 return mozilla::HashCodeScrambler(rng.next(), rng.next());
668 }
669
forkRandomKeyGenerator()670 mozilla::non_crypto::XorShift128PlusRNG JSRuntime::forkRandomKeyGenerator() {
671 auto& rng = randomKeyGenerator();
672 return mozilla::non_crypto::XorShift128PlusRNG(rng.next(), rng.next());
673 }
674
randomHashCode()675 js::HashNumber JSRuntime::randomHashCode() {
676 MOZ_ASSERT(CurrentThreadCanAccessRuntime(this));
677
678 if (randomHashCodeGenerator_.isNothing()) {
679 mozilla::Array<uint64_t, 2> seed;
680 GenerateXorShift128PlusSeed(seed);
681 randomHashCodeGenerator_.emplace(seed[0], seed[1]);
682 }
683
684 return HashNumber(randomHashCodeGenerator_->next());
685 }
686
onOutOfMemory(AllocFunction allocFunc,arena_id_t arena,size_t nbytes,void * reallocPtr,JSContext * maybecx)687 JS_PUBLIC_API void* JSRuntime::onOutOfMemory(AllocFunction allocFunc,
688 arena_id_t arena, size_t nbytes,
689 void* reallocPtr,
690 JSContext* maybecx) {
691 MOZ_ASSERT_IF(allocFunc != AllocFunction::Realloc, !reallocPtr);
692
693 if (JS::RuntimeHeapIsBusy()) {
694 return nullptr;
695 }
696
697 if (!oom::IsSimulatedOOMAllocation()) {
698 /*
699 * Retry when we are done with the background sweeping and have stopped
700 * all the allocations and released the empty GC chunks.
701 */
702 gc.onOutOfMallocMemory();
703 void* p;
704 switch (allocFunc) {
705 case AllocFunction::Malloc:
706 p = js_arena_malloc(arena, nbytes);
707 break;
708 case AllocFunction::Calloc:
709 p = js_arena_calloc(arena, nbytes, 1);
710 break;
711 case AllocFunction::Realloc:
712 p = js_arena_realloc(arena, reallocPtr, nbytes);
713 break;
714 default:
715 MOZ_CRASH();
716 }
717 if (p) {
718 return p;
719 }
720 }
721
722 if (maybecx) {
723 ReportOutOfMemory(maybecx);
724 }
725 return nullptr;
726 }
727
onOutOfMemoryCanGC(AllocFunction allocFunc,arena_id_t arena,size_t bytes,void * reallocPtr)728 void* JSRuntime::onOutOfMemoryCanGC(AllocFunction allocFunc, arena_id_t arena,
729 size_t bytes, void* reallocPtr) {
730 if (OnLargeAllocationFailure && bytes >= LARGE_ALLOCATION) {
731 OnLargeAllocationFailure();
732 }
733 return onOutOfMemory(allocFunc, arena, bytes, reallocPtr);
734 }
735
activeGCInAtomsZone()736 bool JSRuntime::activeGCInAtomsZone() {
737 Zone* zone = unsafeAtomsZone();
738 return (zone->needsIncrementalBarrier() &&
739 !gc.isVerifyPreBarriersEnabled()) ||
740 zone->wasGCStarted();
741 }
742
setUsedByHelperThread(Zone * zone)743 void JSRuntime::setUsedByHelperThread(Zone* zone) {
744 MOZ_ASSERT(!zone->usedByHelperThread());
745 MOZ_ASSERT(!zone->wasGCStarted());
746 MOZ_ASSERT(!isOffThreadParsingBlocked());
747
748 zone->setUsedByHelperThread();
749 if (numActiveHelperThreadZones++ == 0) {
750 gc.setParallelAtomsAllocEnabled(true);
751 }
752 }
753
clearUsedByHelperThread(Zone * zone)754 void JSRuntime::clearUsedByHelperThread(Zone* zone) {
755 MOZ_ASSERT(zone->usedByHelperThread());
756
757 zone->clearUsedByHelperThread();
758 if (--numActiveHelperThreadZones == 0) {
759 gc.setParallelAtomsAllocEnabled(false);
760 }
761
762 JSContext* cx = mainContextFromOwnThread();
763 if (gc.fullGCForAtomsRequested() && cx->canCollectAtoms()) {
764 gc.triggerFullGCForAtoms(cx);
765 }
766 }
767
incrementNumDebuggeeRealms()768 void JSRuntime::incrementNumDebuggeeRealms() {
769 if (numDebuggeeRealms_ == 0) {
770 jitRuntime()->baselineInterpreter().toggleDebuggerInstrumentation(true);
771 }
772
773 numDebuggeeRealms_++;
774 MOZ_ASSERT(numDebuggeeRealms_ <= numRealms);
775 }
776
decrementNumDebuggeeRealms()777 void JSRuntime::decrementNumDebuggeeRealms() {
778 MOZ_ASSERT(numDebuggeeRealms_ > 0);
779 numDebuggeeRealms_--;
780
781 // Note: if we had shutdown leaks we can end up here while destroying the
782 // runtime. It's not safe to access JitRuntime trampolines because they're no
783 // longer traced.
784 if (numDebuggeeRealms_ == 0 && !isBeingDestroyed()) {
785 jitRuntime()->baselineInterpreter().toggleDebuggerInstrumentation(false);
786 }
787 }
788
incrementNumDebuggeeRealmsObservingCoverage()789 void JSRuntime::incrementNumDebuggeeRealmsObservingCoverage() {
790 if (numDebuggeeRealmsObservingCoverage_ == 0) {
791 jit::BaselineInterpreter& interp = jitRuntime()->baselineInterpreter();
792 interp.toggleCodeCoverageInstrumentation(true);
793 }
794
795 numDebuggeeRealmsObservingCoverage_++;
796 MOZ_ASSERT(numDebuggeeRealmsObservingCoverage_ <= numRealms);
797 }
798
decrementNumDebuggeeRealmsObservingCoverage()799 void JSRuntime::decrementNumDebuggeeRealmsObservingCoverage() {
800 MOZ_ASSERT(numDebuggeeRealmsObservingCoverage_ > 0);
801 numDebuggeeRealmsObservingCoverage_--;
802
803 // Note: if we had shutdown leaks we can end up here while destroying the
804 // runtime. It's not safe to access JitRuntime trampolines because they're no
805 // longer traced.
806 if (numDebuggeeRealmsObservingCoverage_ == 0 && !isBeingDestroyed()) {
807 jit::BaselineInterpreter& interp = jitRuntime()->baselineInterpreter();
808 interp.toggleCodeCoverageInstrumentation(false);
809 }
810 }
811
CurrentThreadCanAccessRuntime(const JSRuntime * rt)812 bool js::CurrentThreadCanAccessRuntime(const JSRuntime* rt) {
813 return rt->mainContextFromAnyThread() == TlsContext.get();
814 }
815
CurrentThreadCanAccessZone(Zone * zone)816 bool js::CurrentThreadCanAccessZone(Zone* zone) {
817 // Helper thread zones can only be used by their owning thread.
818 if (zone->usedByHelperThread()) {
819 return zone->ownedByCurrentHelperThread();
820 }
821
822 // Other zones can only be accessed by the runtime's active context.
823 return CurrentThreadCanAccessRuntime(zone->runtime_);
824 }
825
826 #ifdef DEBUG
CurrentThreadIsPerformingGC()827 bool js::CurrentThreadIsPerformingGC() {
828 JSContext* cx = TlsContext.get();
829 return cx->defaultFreeOp()->isCollecting();
830 }
831 #endif
832
SetJSContextProfilerSampleBufferRangeStart(JSContext * cx,uint64_t rangeStart)833 JS_PUBLIC_API void JS::SetJSContextProfilerSampleBufferRangeStart(
834 JSContext* cx, uint64_t rangeStart) {
835 cx->runtime()->setProfilerSampleBufferRangeStart(rangeStart);
836 }
837
IsProfilingEnabledForContext(JSContext * cx)838 JS_PUBLIC_API bool JS::IsProfilingEnabledForContext(JSContext* cx) {
839 MOZ_ASSERT(cx);
840 return cx->runtime()->geckoProfiler().enabled();
841 }
842
EnableRecordingAllocations(JSContext * cx,JS::RecordAllocationsCallback callback,double probability)843 JS_PUBLIC_API void JS::EnableRecordingAllocations(
844 JSContext* cx, JS::RecordAllocationsCallback callback, double probability) {
845 MOZ_ASSERT(cx);
846 MOZ_ASSERT(cx->isMainThreadContext());
847 cx->runtime()->startRecordingAllocations(probability, callback);
848 }
849
DisableRecordingAllocations(JSContext * cx)850 JS_PUBLIC_API void JS::DisableRecordingAllocations(JSContext* cx) {
851 MOZ_ASSERT(cx);
852 MOZ_ASSERT(cx->isMainThreadContext());
853 cx->runtime()->stopRecordingAllocations();
854 }
855
RegisterWeakCache(JSRuntime * rt,detail::WeakCacheBase * cachep)856 JS_PUBLIC_API void JS::shadow::RegisterWeakCache(
857 JSRuntime* rt, detail::WeakCacheBase* cachep) {
858 rt->registerWeakCache(cachep);
859 }
860
startRecordingAllocations(double probability,JS::RecordAllocationsCallback callback)861 void JSRuntime::startRecordingAllocations(
862 double probability, JS::RecordAllocationsCallback callback) {
863 allocationSamplingProbability = probability;
864 recordAllocationCallback = callback;
865
866 // Go through all of the existing realms, and turn on allocation tracking.
867 for (RealmsIter realm(this); !realm.done(); realm.next()) {
868 realm->setAllocationMetadataBuilder(&SavedStacks::metadataBuilder);
869 realm->chooseAllocationSamplingProbability();
870 }
871 }
872
stopRecordingAllocations()873 void JSRuntime::stopRecordingAllocations() {
874 recordAllocationCallback = nullptr;
875 // Go through all of the existing realms, and turn on allocation tracking.
876 for (RealmsIter realm(this); !realm.done(); realm.next()) {
877 js::GlobalObject* global = realm->maybeGlobal();
878 if (!realm->isDebuggee() || !global ||
879 !DebugAPI::isObservedByDebuggerTrackingAllocations(*global)) {
880 // Only remove the allocation metadata builder if no Debuggers are
881 // tracking allocations.
882 realm->forgetAllocationMetadataBuilder();
883 }
884 }
885 }
886
887 // This function can run to ensure that when new realms are created
888 // they have allocation logging turned on.
ensureRealmIsRecordingAllocations(Handle<GlobalObject * > global)889 void JSRuntime::ensureRealmIsRecordingAllocations(
890 Handle<GlobalObject*> global) {
891 if (recordAllocationCallback) {
892 if (!global->realm()->isRecordingAllocations()) {
893 // This is a new realm, turn on allocations for it.
894 global->realm()->setAllocationMetadataBuilder(
895 &SavedStacks::metadataBuilder);
896 }
897 // Ensure the probability is up to date with the current combination of
898 // debuggers and runtime profiling.
899 global->realm()->chooseAllocationSamplingProbability();
900 }
901 }
902