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