1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
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/ArrayUtils.h"
8 #include "mozilla/Atomics.h"
9 #include "mozilla/DebugOnly.h"
10 #include "mozilla/MemoryReporting.h"
11 #include "mozilla/ThreadLocal.h"
12 #include "mozilla/Unused.h"
13 
14 #if defined(XP_DARWIN)
15 #include <mach/mach.h>
16 #elif defined(XP_UNIX)
17 #include <sys/resource.h>
18 #endif  // defined(XP_DARWIN) || defined(XP_UNIX) || defined(XP_WIN)
19 #include <locale.h>
20 #include <string.h>
21 #ifdef JS_CAN_CHECK_THREADSAFE_ACCESSES
22 #include <sys/mman.h>
23 #endif
24 
25 #include "jsmath.h"
26 
27 #include "builtin/Promise.h"
28 #include "gc/FreeOp.h"
29 #include "gc/GCInternals.h"
30 #include "gc/PublicIterators.h"
31 #include "jit/arm/Simulator-arm.h"
32 #include "jit/arm64/vixl/Simulator-vixl.h"
33 #include "jit/JitCompartment.h"
34 #include "jit/mips32/Simulator-mips32.h"
35 #include "jit/mips64/Simulator-mips64.h"
36 #include "js/Date.h"
37 #include "js/MemoryMetrics.h"
38 #include "js/SliceBudget.h"
39 #include "js/Wrapper.h"
40 #include "util/Windows.h"
41 #include "vm/Debugger.h"
42 #include "vm/JSAtom.h"
43 #include "vm/JSObject.h"
44 #include "vm/JSScript.h"
45 #include "vm/TraceLogging.h"
46 #include "vm/TraceLoggingGraph.h"
47 #include "wasm/WasmSignalHandlers.h"
48 
49 #include "gc/GC-inl.h"
50 #include "vm/JSContext-inl.h"
51 
52 using namespace js;
53 using namespace js::gc;
54 
55 using JS::DoubleNaNValue;
56 using mozilla::Atomic;
57 using mozilla::DebugOnly;
58 using mozilla::NegativeInfinity;
59 using mozilla::PodZero;
60 using mozilla::PositiveInfinity;
61 
62 /* static */ MOZ_THREAD_LOCAL(JSContext*) js::TlsContext;
63 /* static */ Atomic<size_t> JSRuntime::liveRuntimesCount;
64 Atomic<JS::LargeAllocationFailureCallback> js::OnLargeAllocationFailure;
65 
66 namespace js {
67 bool gCanUseExtraThreads = true;
68 }  // namespace js
69 
DisableExtraThreads()70 void js::DisableExtraThreads() { gCanUseExtraThreads = false; }
71 
72 const JSSecurityCallbacks js::NullSecurityCallbacks = {};
73 
74 static const JSWrapObjectCallbacks DefaultWrapObjectCallbacks = {
75     TransparentObjectWrapper, nullptr};
76 
ReturnZeroSize(const void * p)77 static size_t ReturnZeroSize(const void* p) { return 0; }
78 
JSRuntime(JSRuntime * parentRuntime)79 JSRuntime::JSRuntime(JSRuntime* parentRuntime)
80     : parentRuntime(parentRuntime),
81 #ifdef DEBUG
82       updateChildRuntimeCount(parentRuntime),
83       initialized_(false),
84 #endif
85       activeContext_(nullptr),
86       activeContextChangeProhibited_(0),
87       singleThreadedExecutionRequired_(0),
88       startingSingleThreadedExecution_(false),
89       beginSingleThreadedExecutionCallback(nullptr),
90       endSingleThreadedExecutionCallback(nullptr),
91       profilerSampleBufferRangeStart_(0),
92       telemetryCallback(nullptr),
93       consumeStreamCallback(nullptr),
94       readableStreamDataRequestCallback(nullptr),
95       readableStreamWriteIntoReadRequestCallback(nullptr),
96       readableStreamCancelCallback(nullptr),
97       readableStreamClosedCallback(nullptr),
98       readableStreamErroredCallback(nullptr),
99       readableStreamFinalizeCallback(nullptr),
100       hadOutOfMemory(false),
101       allowRelazificationForTesting(false),
102       destroyCompartmentCallback(nullptr),
103       sizeOfIncludingThisCompartmentCallback(nullptr),
104       compartmentNameCallback(nullptr),
105       destroyRealmCallback(nullptr),
106       realmNameCallback(nullptr),
107       externalStringSizeofCallback(nullptr),
108       securityCallbacks(&NullSecurityCallbacks),
109       DOMcallbacks(nullptr),
110       destroyPrincipals(nullptr),
111       readPrincipals(nullptr),
112       warningReporter(nullptr),
113       geckoProfiler_(thisFromCtor()),
114       buildIdOp(nullptr),
115       trustedPrincipals_(nullptr),
116       wrapObjectCallbacks(&DefaultWrapObjectCallbacks),
117       preserveWrapperCallback(nullptr),
118       scriptEnvironmentPreparer(nullptr),
119       ctypesActivityCallback(nullptr),
120       windowProxyClass_(nullptr),
121       exclusiveAccessLock(mutexid::RuntimeExclusiveAccess),
122 #ifdef DEBUG
123       activeThreadHasExclusiveAccess(false),
124 #endif
125       scriptDataLock(mutexid::RuntimeScriptData),
126 #ifdef DEBUG
127       activeThreadHasScriptDataAccess(false),
128 #endif
129       numActiveHelperThreadZones(0),
130       numCompartments(0),
131       localeCallbacks(nullptr),
132       defaultLocale(nullptr),
133       profilingScripts(false),
134       scriptAndCountsVector(nullptr),
135       lcovOutput_(),
136       jitRuntime_(nullptr),
137       selfHostingGlobal_(nullptr),
138       gc(thisFromCtor()),
139       gcInitialized(false),
140       NaNValue(DoubleNaNValue()),
141       negativeInfinityValue(DoubleValue(NegativeInfinity<double>())),
142       positiveInfinityValue(DoubleValue(PositiveInfinity<double>())),
143       emptyString(nullptr),
144       defaultFreeOp_(nullptr),
145 #if !EXPOSE_INTL_API
146       thousandsSeparator(nullptr),
147       decimalSeparator(nullptr),
148       numGrouping(nullptr),
149 #endif
150       beingDestroyed_(false),
151       allowContentJS_(true),
152       atoms_(nullptr),
153       atomsAddedWhileSweeping_(nullptr),
154       atomsCompartment_(nullptr),
155       staticStrings(nullptr),
156       commonNames(nullptr),
157       permanentAtoms(nullptr),
158       wellKnownSymbols(nullptr),
159       jitSupportsFloatingPoint(false),
160       jitSupportsUnalignedAccesses(false),
161       jitSupportsSimd(false),
162       offthreadIonCompilationEnabled_(true),
163       parallelParsingEnabled_(true),
164       autoWritableJitCodeActive_(false),
165       oomCallback(nullptr),
166       debuggerMallocSizeOf(ReturnZeroSize),
167       lastAnimationTime(0),
168       performanceMonitoring_(),
169       stackFormat_(parentRuntime ? js::StackFormat::Default
170                                  : js::StackFormat::SpiderMonkey) {
171   liveRuntimesCount++;
172 
173   /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
174 
175   PodZero(&asmJSCacheOps);
176   lcovOutput().init();
177 }
178 
~JSRuntime()179 JSRuntime::~JSRuntime() {
180   MOZ_ASSERT(!initialized_);
181 
182   DebugOnly<size_t> oldCount = liveRuntimesCount--;
183   MOZ_ASSERT(oldCount > 0);
184 }
185 
init(JSContext * cx,uint32_t maxbytes,uint32_t maxNurseryBytes)186 bool JSRuntime::init(JSContext* cx, uint32_t maxbytes,
187                      uint32_t maxNurseryBytes) {
188 #ifdef DEBUG
189   MOZ_ASSERT(!initialized_);
190   initialized_ = true;
191 #endif
192 
193   if (CanUseExtraThreads() && !EnsureHelperThreadsInitialized()) return false;
194 
195   activeContext_ = cx;
196   if (!cooperatingContexts().append(cx)) return false;
197 
198   defaultFreeOp_ = js_new<js::FreeOp>(this);
199   if (!defaultFreeOp_) return false;
200 
201   if (!gc.init(maxbytes, maxNurseryBytes)) return false;
202 
203   ScopedJSDeletePtr<Zone> atomsZone(js_new<Zone>(this, nullptr));
204   if (!atomsZone || !atomsZone->init(true)) return false;
205 
206   JS::CompartmentOptions options;
207   ScopedJSDeletePtr<JSCompartment> atomsCompartment(
208       js_new<JSCompartment>(atomsZone.get(), options));
209   if (!atomsCompartment || !atomsCompartment->init(nullptr)) return false;
210 
211   gc.atomsZone = atomsZone.get();
212   if (!atomsZone->compartments().append(atomsCompartment.get())) return false;
213 
214   atomsCompartment->setIsSystem(true);
215   atomsCompartment->setIsAtomsCompartment();
216 
217   atomsZone.forget();
218   this->atomsCompartment_ = atomsCompartment.forget();
219 
220   if (!symbolRegistry_.ref().init()) return false;
221 
222   if (!scriptDataTable_.ref().init()) return false;
223 
224   /* The garbage collector depends on everything before this point being
225    * initialized. */
226   gcInitialized = true;
227 
228   if (!InitRuntimeNumberState(this)) return false;
229 
230   JS::ResetTimeZone();
231 
232   jitSupportsFloatingPoint = js::jit::JitSupportsFloatingPoint();
233   jitSupportsUnalignedAccesses = js::jit::JitSupportsUnalignedAccesses();
234   jitSupportsSimd = js::jit::JitSupportsSimd();
235 
236   if (!geckoProfiler().init()) return false;
237 
238   if (!parentRuntime) {
239     sharedImmutableStrings_ = js::SharedImmutableStringsCache::Create();
240     if (!sharedImmutableStrings_) return false;
241   }
242 
243   if (!caches().init()) return false;
244 
245   return true;
246 }
247 
destroyRuntime()248 void JSRuntime::destroyRuntime() {
249   MOZ_ASSERT(!JS::CurrentThreadIsHeapBusy());
250   MOZ_ASSERT(childRuntimeCount == 0);
251   MOZ_ASSERT(initialized_);
252 
253   sharedIntlData.ref().destroyInstance();
254 
255   if (gcInitialized) {
256     /*
257      * Finish any in-progress GCs first. This ensures the parseWaitingOnGC
258      * list is empty in CancelOffThreadParses.
259      */
260     JSContext* cx = TlsContext.get();
261     if (JS::IsIncrementalGCInProgress(cx)) FinishGC(cx);
262 
263     /* Free source hook early, as its destructor may want to delete roots. */
264     sourceHook = nullptr;
265 
266     /*
267      * Cancel any pending, in progress or completed Ion compilations and
268      * parse tasks. Waiting for wasm and compression tasks is done
269      * synchronously (on the active thread or during parse tasks), so no
270      * explicit canceling is needed for these.
271      */
272     CancelOffThreadIonCompile(this);
273     CancelOffThreadParses(this);
274     CancelOffThreadCompressions(this);
275 
276     /* Remove persistent GC roots. */
277     gc.finishRoots();
278 
279     /*
280      * Flag us as being destroyed. This allows the GC to free things like
281      * interned atoms and Ion trampolines.
282      */
283     beingDestroyed_ = true;
284 
285     /* Allow the GC to release scripts that were being profiled. */
286     profilingScripts = false;
287 
288     JS::PrepareForFullGC(cx);
289     gc.gc(GC_NORMAL, JS::gcreason::DESTROY_RUNTIME);
290   }
291 
292   AutoNoteSingleThreadedRegion anstr;
293 
294   MOZ_ASSERT(!hasHelperThreadZones());
295 
296   /*
297    * Even though all objects in the compartment are dead, we may have keep
298    * some filenames around because of gcKeepAtoms.
299    */
300   FreeScriptData(this);
301 
302 #if !EXPOSE_INTL_API
303   FinishRuntimeNumberState(this);
304 #endif
305 
306   gc.finish();
307   atomsCompartment_ = nullptr;
308 
309   js_delete(defaultFreeOp_.ref());
310 
311   js_free(defaultLocale);
312   js_delete(jitRuntime_.ref());
313 
314 #ifdef DEBUG
315   initialized_ = false;
316 #endif
317 }
318 
CheckCanChangeActiveContext(JSRuntime * rt)319 static void CheckCanChangeActiveContext(JSRuntime* rt) {
320   // The runtime might not currently have an active context, in which case
321   // the accesses below to ActiveThreadData data would not normally be
322   // allowed. Suppress protected data checks so these accesses will be
323   // tolerated --- if the active context is null then we're about to set it
324   // to the current thread.
325   AutoNoteSingleThreadedRegion anstr;
326 
327   MOZ_RELEASE_ASSERT(!rt->activeContextChangeProhibited());
328   MOZ_RELEASE_ASSERT(!rt->activeContext() ||
329                      rt->gc.canChangeActiveContext(rt->activeContext()));
330 
331   if (rt->singleThreadedExecutionRequired()) {
332     for (ZoneGroupsIter group(rt); !group.done(); group.next())
333       MOZ_RELEASE_ASSERT(group->ownerContext().context() == nullptr);
334   }
335 }
336 
setActiveContext(JSContext * cx)337 void JSRuntime::setActiveContext(JSContext* cx) {
338   CheckCanChangeActiveContext(this);
339   MOZ_ASSERT_IF(cx, cx->isCooperativelyScheduled());
340 
341   activeContext_ = cx;
342 }
343 
setNewbornActiveContext(JSContext * cx)344 void JSRuntime::setNewbornActiveContext(JSContext* cx) {
345   CheckCanChangeActiveContext(this);
346 
347   activeContext_ = cx;
348 
349   AutoEnterOOMUnsafeRegion oomUnsafe;
350   if (!cooperatingContexts().append(cx))
351     oomUnsafe.crash("Add cooperating context");
352 }
353 
deleteActiveContext(JSContext * cx)354 void JSRuntime::deleteActiveContext(JSContext* cx) {
355   CheckCanChangeActiveContext(this);
356   MOZ_ASSERT(cx == activeContext());
357 
358   js_delete_poison(cx);
359   activeContext_ = nullptr;
360 }
361 
beginSingleThreadedExecution(JSContext * cx)362 bool JSRuntime::beginSingleThreadedExecution(JSContext* cx) {
363   if (singleThreadedExecutionRequired_ == 0) {
364     if (startingSingleThreadedExecution_) return false;
365     startingSingleThreadedExecution_ = true;
366     if (beginSingleThreadedExecutionCallback)
367       beginSingleThreadedExecutionCallback(cx);
368     MOZ_ASSERT(startingSingleThreadedExecution_);
369     startingSingleThreadedExecution_ = false;
370   }
371 
372   singleThreadedExecutionRequired_++;
373 
374   for (ZoneGroupsIter group(this); !group.done(); group.next()) {
375     MOZ_RELEASE_ASSERT(group->ownedByCurrentThread() ||
376                        group->ownerContext().context() == nullptr);
377   }
378 
379   return true;
380 }
381 
endSingleThreadedExecution(JSContext * cx)382 void JSRuntime::endSingleThreadedExecution(JSContext* cx) {
383   MOZ_ASSERT(singleThreadedExecutionRequired_);
384   if (--singleThreadedExecutionRequired_ == 0) {
385     if (endSingleThreadedExecutionCallback)
386       endSingleThreadedExecutionCallback(cx);
387   }
388 }
389 
addTelemetry(int id,uint32_t sample,const char * key)390 void JSRuntime::addTelemetry(int id, uint32_t sample, const char* key) {
391   if (telemetryCallback) (*telemetryCallback)(id, sample, key);
392 }
393 
setTelemetryCallback(JSRuntime * rt,JSAccumulateTelemetryDataCallback callback)394 void JSRuntime::setTelemetryCallback(
395     JSRuntime* rt, JSAccumulateTelemetryDataCallback callback) {
396   rt->telemetryCallback = callback;
397 }
398 
setUseCounter(JSObject * obj,JSUseCounter counter)399 void JSRuntime::setUseCounter(JSObject* obj, JSUseCounter counter) {
400   if (useCounterCallback) (*useCounterCallback)(obj, counter);
401 }
402 
setUseCounterCallback(JSRuntime * rt,JSSetUseCounterCallback callback)403 void JSRuntime::setUseCounterCallback(JSRuntime* rt,
404                                       JSSetUseCounterCallback callback) {
405   rt->useCounterCallback = callback;
406 }
407 
addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,JS::RuntimeSizes * rtSizes)408 void JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
409                                        JS::RuntimeSizes* rtSizes) {
410   rtSizes->object += mallocSizeOf(this);
411 
412   {
413     AutoLockForExclusiveAccess lock(this);
414     rtSizes->atomsTable += atoms(lock).sizeOfIncludingThis(mallocSizeOf);
415     rtSizes->gc.marker += gc.marker.sizeOfExcludingThis(mallocSizeOf, lock);
416   }
417 
418   if (!parentRuntime) {
419     rtSizes->atomsTable += mallocSizeOf(staticStrings);
420     rtSizes->atomsTable += mallocSizeOf(commonNames);
421     rtSizes->atomsTable += permanentAtoms->sizeOfIncludingThis(mallocSizeOf);
422   }
423 
424   for (const CooperatingContext& target : cooperatingContexts()) {
425     JSContext* cx = target.context();
426     rtSizes->contexts += mallocSizeOf(cx);
427     rtSizes->contexts += cx->sizeOfExcludingThis(mallocSizeOf);
428     rtSizes->temporary += cx->tempLifoAlloc().sizeOfExcludingThis(mallocSizeOf);
429     rtSizes->interpreterStack +=
430         cx->interpreterStack().sizeOfExcludingThis(mallocSizeOf);
431 #ifdef JS_TRACE_LOGGING
432     if (cx->traceLogger)
433       rtSizes->tracelogger +=
434           cx->traceLogger->sizeOfIncludingThis(mallocSizeOf);
435 #endif
436   }
437 
438   if (MathCache* cache = caches().maybeGetMathCache())
439     rtSizes->mathCache += cache->sizeOfIncludingThis(mallocSizeOf);
440 
441   rtSizes->uncompressedSourceCache +=
442       caches().uncompressedSourceCache.sizeOfExcludingThis(mallocSizeOf);
443 
444   rtSizes->gc.nurseryCommitted += gc.nursery().sizeOfHeapCommitted();
445   rtSizes->gc.nurseryMallocedBuffers +=
446       gc.nursery().sizeOfMallocedBuffers(mallocSizeOf);
447   gc.storeBuffer().addSizeOfExcludingThis(mallocSizeOf, &rtSizes->gc);
448 
449   if (sharedImmutableStrings_) {
450     rtSizes->sharedImmutableStringsCache +=
451         sharedImmutableStrings_->sizeOfExcludingThis(mallocSizeOf);
452   }
453 
454   rtSizes->sharedIntlData +=
455       sharedIntlData.ref().sizeOfExcludingThis(mallocSizeOf);
456 
457   {
458     AutoLockScriptData lock(this);
459     rtSizes->scriptData +=
460         scriptDataTable(lock).sizeOfExcludingThis(mallocSizeOf);
461     for (ScriptDataTable::Range r = scriptDataTable(lock).all(); !r.empty();
462          r.popFront())
463       rtSizes->scriptData += mallocSizeOf(r.front());
464   }
465 
466   if (jitRuntime_) {
467     jitRuntime_->execAlloc().addSizeOfCode(&rtSizes->code);
468     jitRuntime_->backedgeExecAlloc().addSizeOfCode(&rtSizes->code);
469   }
470 }
471 
InvokeInterruptCallback(JSContext * cx)472 static bool InvokeInterruptCallback(JSContext* cx) {
473   MOZ_ASSERT(cx->requestDepth >= 1);
474   MOZ_ASSERT(!cx->compartment()->isAtomsCompartment());
475 
476   cx->runtime()->gc.gcIfRequested();
477 
478   // A worker thread may have requested an interrupt after finishing an Ion
479   // compilation.
480   jit::AttachFinishedCompilations(cx->zone()->group(), cx);
481 
482   // Important: Additional callbacks can occur inside the callback handler
483   // if it re-enters the JS engine. The embedding must ensure that the
484   // callback is disconnected before attempting such re-entry.
485   if (cx->interruptCallbackDisabled) return true;
486 
487   bool stop = false;
488   for (JSInterruptCallback cb : cx->interruptCallbacks()) {
489     if (!cb(cx)) stop = true;
490   }
491 
492   if (!stop) {
493     // Debugger treats invoking the interrupt callback as a "step", so
494     // invoke the onStep handler.
495     if (cx->compartment()->isDebuggee()) {
496       ScriptFrameIter iter(cx);
497       if (!iter.done() && cx->compartment() == iter.compartment() &&
498           iter.script()->stepModeEnabled()) {
499         RootedValue rval(cx);
500         switch (Debugger::onSingleStep(cx, &rval)) {
501           case JSTRAP_ERROR:
502             return false;
503           case JSTRAP_CONTINUE:
504             return true;
505           case JSTRAP_RETURN:
506             // See note in Debugger::propagateForcedReturn.
507             Debugger::propagateForcedReturn(cx, iter.abstractFramePtr(), rval);
508             return false;
509           case JSTRAP_THROW:
510             cx->setPendingException(rval);
511             return false;
512           default:;
513         }
514       }
515     }
516 
517     return true;
518   }
519 
520   // No need to set aside any pending exception here: ComputeStackString
521   // already does that.
522   JSString* stack = ComputeStackString(cx);
523   JSFlatString* flat = stack ? stack->ensureFlat(cx) : nullptr;
524 
525   const char16_t* chars;
526   AutoStableStringChars stableChars(cx);
527   if (flat && stableChars.initTwoByte(cx, flat))
528     chars = stableChars.twoByteRange().begin().get();
529   else
530     chars = u"(stack not available)";
531   JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_WARNING, GetErrorMessage, nullptr,
532                                  JSMSG_TERMINATED, chars);
533 
534   return false;
535 }
536 
requestInterrupt(InterruptMode mode)537 void JSContext::requestInterrupt(InterruptMode mode) {
538   interrupt_ = true;
539   jitStackLimit = UINTPTR_MAX;
540 
541   if (mode == JSContext::RequestInterruptUrgent) {
542     // If this interrupt is urgent (slow script dialog for instance), take
543     // additional steps to interrupt corner cases where the above fields are
544     // not regularly polled. Wake ilooping Ion code, irregexp JIT code and
545     // Atomics.wait()
546     interruptRegExpJit_ = true;
547     fx.lock();
548     if (fx.isWaiting()) fx.wake(FutexThread::WakeForJSInterrupt);
549     fx.unlock();
550     InterruptRunningJitCode(this);
551   }
552 }
553 
handleInterrupt()554 bool JSContext::handleInterrupt() {
555   MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime()));
556   if (interrupt_ || jitStackLimit == UINTPTR_MAX) {
557     interrupt_ = false;
558     interruptRegExpJit_ = false;
559     resetJitStackLimit();
560     return InvokeInterruptCallback(this);
561   }
562   return true;
563 }
564 
setDefaultLocale(const char * locale)565 bool JSRuntime::setDefaultLocale(const char* locale) {
566   if (!locale) return false;
567   resetDefaultLocale();
568   defaultLocale = JS_strdup(activeContextFromOwnThread(), locale);
569   return defaultLocale != nullptr;
570 }
571 
resetDefaultLocale()572 void JSRuntime::resetDefaultLocale() {
573   js_free(defaultLocale);
574   defaultLocale = nullptr;
575 }
576 
getDefaultLocale()577 const char* JSRuntime::getDefaultLocale() {
578   if (defaultLocale) return defaultLocale;
579 
580   const char* locale = setlocale(LC_ALL, nullptr);
581 
582   // convert to a well-formed BCP 47 language tag
583   if (!locale || !strcmp(locale, "C")) locale = "und";
584 
585   char* lang = JS_strdup(activeContextFromOwnThread(), locale);
586   if (!lang) return nullptr;
587 
588   char* p;
589   if ((p = strchr(lang, '.'))) *p = '\0';
590   while ((p = strchr(lang, '_'))) *p = '-';
591 
592   defaultLocale = lang;
593   return defaultLocale;
594 }
595 
traceSharedIntlData(JSTracer * trc)596 void JSRuntime::traceSharedIntlData(JSTracer* trc) {
597   sharedIntlData.ref().trace(trc);
598 }
599 
triggerActivityCallback(bool active)600 void JSContext::triggerActivityCallback(bool active) {
601   if (!activityCallback) return;
602 
603   /*
604    * The activity callback must not trigger a GC: it would create a cirular
605    * dependency between entering a request and Rooted's requirement of being
606    * in a request. In practice this callback already cannot trigger GC. The
607    * suppression serves to inform the exact rooting hazard analysis of this
608    * property and ensures that it remains true in the future.
609    */
610   AutoSuppressGC suppress(this);
611 
612   activityCallback(activityCallbackArg, active);
613 }
614 
FreeOp(JSRuntime * maybeRuntime)615 FreeOp::FreeOp(JSRuntime* maybeRuntime) : JSFreeOp(maybeRuntime) {
616   MOZ_ASSERT_IF(maybeRuntime, CurrentThreadCanAccessRuntime(maybeRuntime));
617 }
618 
~FreeOp()619 FreeOp::~FreeOp() {
620   for (size_t i = 0; i < freeLaterList.length(); i++) free_(freeLaterList[i]);
621 
622   if (!jitPoisonRanges.empty())
623     jit::ExecutableAllocator::poisonCode(runtime(), jitPoisonRanges);
624 }
625 
isDefaultFreeOp() const626 bool FreeOp::isDefaultFreeOp() const {
627   return runtime_ && runtime_->defaultFreeOp() == this;
628 }
629 
getIncumbentGlobal(JSContext * cx)630 JSObject* JSRuntime::getIncumbentGlobal(JSContext* cx) {
631   // If the embedding didn't set a callback for getting the incumbent
632   // global, the currently active global is used.
633   if (!cx->getIncumbentGlobalCallback) {
634     if (!cx->compartment()) return nullptr;
635     return cx->global();
636   }
637 
638   return cx->getIncumbentGlobalCallback(cx);
639 }
640 
enqueuePromiseJob(JSContext * cx,HandleFunction job,HandleObject promise,HandleObject incumbentGlobal)641 bool JSRuntime::enqueuePromiseJob(JSContext* cx, HandleFunction job,
642                                   HandleObject promise,
643                                   HandleObject incumbentGlobal) {
644   MOZ_ASSERT(cx->enqueuePromiseJobCallback,
645              "Must set a callback using JS::SetEnqueuePromiseJobCallback "
646              "before using Promises");
647   MOZ_ASSERT_IF(incumbentGlobal,
648                 !IsWrapper(incumbentGlobal) && !IsWindowProxy(incumbentGlobal));
649 
650   void* data = cx->enqueuePromiseJobCallbackData;
651   RootedObject allocationSite(cx);
652   if (promise) {
653     RootedObject unwrappedPromise(cx, promise);
654     // While the job object is guaranteed to be unwrapped, the promise
655     // might be wrapped. See the comments in
656     // intrinsic_EnqueuePromiseReactionJob for details.
657     if (IsWrapper(promise)) unwrappedPromise = UncheckedUnwrap(promise);
658     if (unwrappedPromise->is<PromiseObject>())
659       allocationSite = JS::GetPromiseAllocationSite(unwrappedPromise);
660   }
661   return cx->enqueuePromiseJobCallback(cx, job, allocationSite, incumbentGlobal,
662                                        data);
663 }
664 
addUnhandledRejectedPromise(JSContext * cx,js::HandleObject promise)665 void JSRuntime::addUnhandledRejectedPromise(JSContext* cx,
666                                             js::HandleObject promise) {
667   MOZ_ASSERT(promise->is<PromiseObject>());
668   if (!cx->promiseRejectionTrackerCallback) return;
669 
670   void* data = cx->promiseRejectionTrackerCallbackData;
671   cx->promiseRejectionTrackerCallback(
672       cx, promise, JS::PromiseRejectionHandlingState::Unhandled, data);
673 }
674 
removeUnhandledRejectedPromise(JSContext * cx,js::HandleObject promise)675 void JSRuntime::removeUnhandledRejectedPromise(JSContext* cx,
676                                                js::HandleObject promise) {
677   MOZ_ASSERT(promise->is<PromiseObject>());
678   if (!cx->promiseRejectionTrackerCallback) return;
679 
680   void* data = cx->promiseRejectionTrackerCallbackData;
681   cx->promiseRejectionTrackerCallback(
682       cx, promise, JS::PromiseRejectionHandlingState::Handled, data);
683 }
684 
randomKeyGenerator()685 mozilla::non_crypto::XorShift128PlusRNG& JSRuntime::randomKeyGenerator() {
686   MOZ_ASSERT(CurrentThreadCanAccessRuntime(this));
687   if (randomKeyGenerator_.isNothing()) {
688     mozilla::Array<uint64_t, 2> seed;
689     GenerateXorShift128PlusSeed(seed);
690     randomKeyGenerator_.emplace(seed[0], seed[1]);
691   }
692   return randomKeyGenerator_.ref();
693 }
694 
randomHashCodeScrambler()695 mozilla::HashCodeScrambler JSRuntime::randomHashCodeScrambler() {
696   auto& rng = randomKeyGenerator();
697   return mozilla::HashCodeScrambler(rng.next(), rng.next());
698 }
699 
forkRandomKeyGenerator()700 mozilla::non_crypto::XorShift128PlusRNG JSRuntime::forkRandomKeyGenerator() {
701   auto& rng = randomKeyGenerator();
702   return mozilla::non_crypto::XorShift128PlusRNG(rng.next(), rng.next());
703 }
704 
updateMallocCounter(size_t nbytes)705 void JSRuntime::updateMallocCounter(size_t nbytes) {
706   gc.updateMallocCounter(nbytes);
707 }
708 
onOutOfMemory(AllocFunction allocFunc,size_t nbytes,void * reallocPtr,JSContext * maybecx)709 JS_FRIEND_API void* JSRuntime::onOutOfMemory(AllocFunction allocFunc,
710                                              size_t nbytes, void* reallocPtr,
711                                              JSContext* maybecx) {
712   MOZ_ASSERT_IF(allocFunc != AllocFunction::Realloc, !reallocPtr);
713 
714   if (JS::CurrentThreadIsHeapBusy()) return nullptr;
715 
716   if (!oom::IsSimulatedOOMAllocation()) {
717     /*
718      * Retry when we are done with the background sweeping and have stopped
719      * all the allocations and released the empty GC chunks.
720      */
721     gc.onOutOfMallocMemory();
722     void* p;
723     switch (allocFunc) {
724       case AllocFunction::Malloc:
725         p = js_malloc(nbytes);
726         break;
727       case AllocFunction::Calloc:
728         p = js_calloc(nbytes);
729         break;
730       case AllocFunction::Realloc:
731         p = js_realloc(reallocPtr, nbytes);
732         break;
733       default:
734         MOZ_CRASH();
735     }
736     if (p) return p;
737   }
738 
739   if (maybecx) ReportOutOfMemory(maybecx);
740   return nullptr;
741 }
742 
onOutOfMemoryCanGC(AllocFunction allocFunc,size_t bytes,void * reallocPtr)743 void* JSRuntime::onOutOfMemoryCanGC(AllocFunction allocFunc, size_t bytes,
744                                     void* reallocPtr) {
745   if (OnLargeAllocationFailure && bytes >= LARGE_ALLOCATION)
746     OnLargeAllocationFailure();
747   return onOutOfMemory(allocFunc, bytes, reallocPtr);
748 }
749 
activeGCInAtomsZone()750 bool JSRuntime::activeGCInAtomsZone() {
751   Zone* zone = atomsCompartment_->zone();
752   return (zone->needsIncrementalBarrier() &&
753           !gc.isVerifyPreBarriersEnabled()) ||
754          zone->wasGCStarted();
755 }
756 
createAtomsAddedWhileSweepingTable()757 bool JSRuntime::createAtomsAddedWhileSweepingTable() {
758   MOZ_ASSERT(JS::CurrentThreadIsHeapCollecting());
759   MOZ_ASSERT(!atomsAddedWhileSweeping_);
760 
761   atomsAddedWhileSweeping_ = js_new<AtomSet>();
762   if (!atomsAddedWhileSweeping_) return false;
763 
764   if (!atomsAddedWhileSweeping_->init()) {
765     destroyAtomsAddedWhileSweepingTable();
766     return false;
767   }
768 
769   return true;
770 }
771 
destroyAtomsAddedWhileSweepingTable()772 void JSRuntime::destroyAtomsAddedWhileSweepingTable() {
773   MOZ_ASSERT(JS::CurrentThreadIsHeapCollecting());
774   MOZ_ASSERT(atomsAddedWhileSweeping_);
775 
776   js_delete(atomsAddedWhileSweeping_.ref());
777   atomsAddedWhileSweeping_ = nullptr;
778 }
779 
setUsedByHelperThread(Zone * zone)780 void JSRuntime::setUsedByHelperThread(Zone* zone) {
781   MOZ_ASSERT(!zone->group()->usedByHelperThread());
782   MOZ_ASSERT(!zone->wasGCStarted());
783   zone->group()->setUsedByHelperThread();
784   numActiveHelperThreadZones++;
785 }
786 
clearUsedByHelperThread(Zone * zone)787 void JSRuntime::clearUsedByHelperThread(Zone* zone) {
788   MOZ_ASSERT(zone->group()->usedByHelperThread());
789   zone->group()->clearUsedByHelperThread();
790   numActiveHelperThreadZones--;
791   JSContext* cx = TlsContext.get();
792   if (gc.fullGCForAtomsRequested() && cx->canCollectAtoms())
793     gc.triggerFullGCForAtoms(cx);
794 }
795 
CurrentThreadCanAccessRuntime(const JSRuntime * rt)796 bool js::CurrentThreadCanAccessRuntime(const JSRuntime* rt) {
797   return rt->activeContext() == TlsContext.get();
798 }
799 
CurrentThreadCanAccessZone(Zone * zone)800 bool js::CurrentThreadCanAccessZone(Zone* zone) {
801   if (CurrentThreadCanAccessRuntime(zone->runtime_)) return true;
802 
803   // Only zones marked for use by a helper thread can be used off thread.
804   return zone->usedByHelperThread() && zone->group()->ownedByCurrentThread();
805 }
806 
807 #ifdef DEBUG
CurrentThreadIsPerformingGC()808 bool js::CurrentThreadIsPerformingGC() {
809   return TlsContext.get()->performingGC;
810 }
811 #endif
812 
SetJSContextProfilerSampleBufferRangeStart(JSContext * cx,uint64_t rangeStart)813 JS_FRIEND_API void JS::SetJSContextProfilerSampleBufferRangeStart(
814     JSContext* cx, uint64_t rangeStart) {
815   cx->runtime()->setProfilerSampleBufferRangeStart(rangeStart);
816 }
817 
IsProfilingEnabledForContext(JSContext * cx)818 JS_FRIEND_API bool JS::IsProfilingEnabledForContext(JSContext* cx) {
819   MOZ_ASSERT(cx);
820   return cx->runtime()->geckoProfiler().enabled();
821 }
822 
RegisterWeakCache(JSRuntime * rt,detail::WeakCacheBase * cachep)823 JS_PUBLIC_API void JS::shadow::RegisterWeakCache(
824     JSRuntime* rt, detail::WeakCacheBase* cachep) {
825   rt->registerWeakCache(cachep);
826 }
827