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 #elif defined(XP_WIN)
19 #include <processthreadsapi.h>
20 #include <windows.h>
21 #endif // defined(XP_DARWIN) || defined(XP_UNIX) || defined(XP_WIN)
22 
23 #include <locale.h>
24 #include <string.h>
25 
26 #ifdef JS_CAN_CHECK_THREADSAFE_ACCESSES
27 # include <sys/mman.h>
28 #endif
29 
30 #include "jsatom.h"
31 #include "jsdtoa.h"
32 #include "jsgc.h"
33 #include "jsmath.h"
34 #include "jsnativestack.h"
35 #include "jsobj.h"
36 #include "jsscript.h"
37 #include "jswatchpoint.h"
38 #include "jswin.h"
39 #include "jswrapper.h"
40 
41 #include "builtin/Promise.h"
42 #include "gc/GCInternals.h"
43 #include "jit/arm/Simulator-arm.h"
44 #include "jit/arm64/vixl/Simulator-vixl.h"
45 #include "jit/IonBuilder.h"
46 #include "jit/JitCompartment.h"
47 #include "jit/mips32/Simulator-mips32.h"
48 #include "jit/mips64/Simulator-mips64.h"
49 #include "jit/PcScriptCache.h"
50 #include "js/Date.h"
51 #include "js/MemoryMetrics.h"
52 #include "js/SliceBudget.h"
53 #include "vm/Debugger.h"
54 #include "wasm/WasmSignalHandlers.h"
55 
56 #include "jscntxtinlines.h"
57 #include "jsgcinlines.h"
58 
59 using namespace js;
60 using namespace js::gc;
61 
62 using mozilla::Atomic;
63 using mozilla::DebugOnly;
64 using mozilla::NegativeInfinity;
65 using mozilla::PodZero;
66 using mozilla::PodArrayZero;
67 using mozilla::PositiveInfinity;
68 using JS::GenericNaN;
69 using JS::DoubleNaNValue;
70 
71 /* static */ MOZ_THREAD_LOCAL(PerThreadData*) js::TlsPerThreadData;
72 /* static */ Atomic<size_t> JSRuntime::liveRuntimesCount;
73 
74 namespace js {
75     bool gCanUseExtraThreads = true;
76 } // namespace js
77 
78 void
DisableExtraThreads()79 js::DisableExtraThreads()
80 {
81     gCanUseExtraThreads = false;
82 }
83 
84 const JSSecurityCallbacks js::NullSecurityCallbacks = { };
85 
PerThreadData(JSRuntime * runtime)86 PerThreadData::PerThreadData(JSRuntime* runtime)
87   : runtime_(runtime)
88 #ifdef JS_TRACE_LOGGING
89   , traceLogger(nullptr)
90 #endif
91   , autoFlushICache_(nullptr)
92   , dtoaState(nullptr)
93   , suppressGC(0)
94 #ifdef DEBUG
95   , ionCompiling(false)
96   , ionCompilingSafeForMinorGC(false)
97   , performingGC(false)
98   , gcSweeping(false)
99 #endif
100 {}
101 
~PerThreadData()102 PerThreadData::~PerThreadData()
103 {
104     if (dtoaState)
105         DestroyDtoaState(dtoaState);
106 }
107 
108 bool
init()109 PerThreadData::init()
110 {
111     dtoaState = NewDtoaState();
112     if (!dtoaState)
113         return false;
114 
115     return true;
116 }
117 
118 static const JSWrapObjectCallbacks DefaultWrapObjectCallbacks = {
119     TransparentObjectWrapper,
120     nullptr
121 };
122 
123 static size_t
ReturnZeroSize(const void * p)124 ReturnZeroSize(const void* p)
125 {
126     return 0;
127 }
128 
JSRuntime(JSRuntime * parentRuntime)129 JSRuntime::JSRuntime(JSRuntime* parentRuntime)
130   : mainThread(this),
131     jitTop(nullptr),
132     jitActivation(nullptr),
133     jitStackLimit_(0xbad),
134     jitStackLimitNoInterrupt_(0xbad),
135 #ifdef DEBUG
136     ionBailAfter_(0),
137 #endif
138     activation_(nullptr),
139     profilingActivation_(nullptr),
140     profilerSampleBufferGen_(0),
141     profilerSampleBufferLapCount_(1),
142     wasmActivationStack_(nullptr),
143     entryMonitor(nullptr),
144     noExecuteDebuggerTop(nullptr),
145     parentRuntime(parentRuntime),
146 #ifdef DEBUG
147     updateChildRuntimeCount(parentRuntime),
148 #endif
149     interrupt_(false),
150     telemetryCallback(nullptr),
151     handlingSegFault(false),
152     handlingJitInterrupt_(false),
153     interruptCallbackDisabled(false),
154     getIncumbentGlobalCallback(nullptr),
155     enqueuePromiseJobCallback(nullptr),
156     enqueuePromiseJobCallbackData(nullptr),
157     promiseRejectionTrackerCallback(nullptr),
158     promiseRejectionTrackerCallbackData(nullptr),
159     startAsyncTaskCallback(nullptr),
160     finishAsyncTaskCallback(nullptr),
161     promiseTasksToDestroy(mutexid::PromiseTaskPtrVector),
162     exclusiveAccessLock(mutexid::RuntimeExclusiveAccess),
163 #ifdef DEBUG
164     mainThreadHasExclusiveAccess(false),
165 #endif
166     numExclusiveThreads(0),
167     numCompartments(0),
168     localeCallbacks(nullptr),
169     defaultLocale(nullptr),
170     defaultVersion_(JSVERSION_DEFAULT),
171     ownerThread_(js::ThisThread::GetId()),
172     ownerThreadNative_(0),
173     tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
174     jitRuntime_(nullptr),
175     selfHostingGlobal_(nullptr),
176     nativeStackBase(GetNativeStackBase()),
177     destroyCompartmentCallback(nullptr),
178     sizeOfIncludingThisCompartmentCallback(nullptr),
179     destroyZoneCallback(nullptr),
180     sweepZoneCallback(nullptr),
181     compartmentNameCallback(nullptr),
182     activityCallback(nullptr),
183     activityCallbackArg(nullptr),
184     requestDepth(0),
185 #ifdef DEBUG
186     checkRequestDepth(0),
187 #endif
188     gc(thisFromCtor()),
189     gcInitialized(false),
190 #ifdef JS_SIMULATOR
191     simulator_(nullptr),
192 #endif
193     scriptAndCountsVector(nullptr),
194     lcovOutput(),
195     NaNValue(DoubleNaNValue()),
196     negativeInfinityValue(DoubleValue(NegativeInfinity<double>())),
197     positiveInfinityValue(DoubleValue(PositiveInfinity<double>())),
198     emptyString(nullptr),
199     spsProfiler(thisFromCtor()),
200     profilingScripts(false),
201     suppressProfilerSampling(false),
202     hadOutOfMemory(false),
203 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
204     runningOOMTest(false),
205 #endif
206     allowRelazificationForTesting(false),
207     defaultFreeOp_(nullptr),
208     debuggerMutations(0),
209     securityCallbacks(&NullSecurityCallbacks),
210     DOMcallbacks(nullptr),
211     destroyPrincipals(nullptr),
212     readPrincipals(nullptr),
213     warningReporter(nullptr),
214     buildIdOp(nullptr),
215     propertyRemovals(0),
216 #if !EXPOSE_INTL_API
217     thousandsSeparator(0),
218     decimalSeparator(0),
219     numGrouping(0),
220 #endif
221     keepAtoms_(0),
222     trustedPrincipals_(nullptr),
223     beingDestroyed_(false),
224     atoms_(nullptr),
225     atomsCompartment_(nullptr),
226     staticStrings(nullptr),
227     commonNames(nullptr),
228     permanentAtoms(nullptr),
229     wellKnownSymbols(nullptr),
230     wrapObjectCallbacks(&DefaultWrapObjectCallbacks),
231     preserveWrapperCallback(nullptr),
232     jitSupportsFloatingPoint(false),
233     jitSupportsUnalignedAccesses(false),
234     jitSupportsSimd(false),
235     ionPcScriptCache(nullptr),
236     scriptEnvironmentPreparer(nullptr),
237     ctypesActivityCallback(nullptr),
238     windowProxyClass_(nullptr),
239     offthreadIonCompilationEnabled_(true),
240     parallelParsingEnabled_(true),
241     autoWritableJitCodeActive_(false),
242 #ifdef DEBUG
243     enteredPolicy(nullptr),
244 #endif
245     largeAllocationFailureCallback(nullptr),
246     oomCallback(nullptr),
247     debuggerMallocSizeOf(ReturnZeroSize),
248     lastAnimationTime(0),
249     performanceMonitoring(thisFromCtor()),
250     ionLazyLinkListSize_(0),
251     stackFormat_(parentRuntime ? js::StackFormat::Default
252                                : js::StackFormat::SpiderMonkey)
253 {
254     setGCStoreBufferPtr(&gc.storeBuffer);
255 
256     liveRuntimesCount++;
257 
258     /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
259     JS_INIT_CLIST(&onNewGlobalObjectWatchers);
260 
261     PodArrayZero(nativeStackQuota);
262     PodZero(&asmJSCacheOps);
263     lcovOutput.init();
264 }
265 
266 bool
init(uint32_t maxbytes,uint32_t maxNurseryBytes)267 JSRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes)
268 {
269     MOZ_ASSERT(ownerThread_ == js::ThisThread::GetId());
270 
271     // Get a platform-native handle for the owner thread, used by
272     // js::InterruptRunningJitCode to halt the runtime's main thread.
273 #ifdef XP_WIN
274     size_t openFlags = THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME |
275                        THREAD_QUERY_INFORMATION;
276     HANDLE self = OpenThread(openFlags, false, GetCurrentThreadId());
277     if (!self)
278         return false;
279     static_assert(sizeof(HANDLE) <= sizeof(ownerThreadNative_), "need bigger field");
280     ownerThreadNative_ = (size_t)self;
281 #else
282     static_assert(sizeof(pthread_t) <= sizeof(ownerThreadNative_), "need bigger field");
283     ownerThreadNative_ = (size_t)pthread_self();
284 #endif
285 
286     if (!mainThread.init())
287         return false;
288 
289     if (!regexpStack.init())
290         return false;
291 
292     if (CanUseExtraThreads() && !EnsureHelperThreadsInitialized())
293         return false;
294 
295     js::TlsPerThreadData.set(&mainThread);
296 
297     defaultFreeOp_ = js_new<js::FreeOp>(this);
298     if (!defaultFreeOp_)
299         return false;
300 
301     if (!gc.init(maxbytes, maxNurseryBytes))
302         return false;
303 
304     ScopedJSDeletePtr<Zone> atomsZone(new_<Zone>(this));
305     if (!atomsZone || !atomsZone->init(true))
306         return false;
307 
308     JS::CompartmentOptions options;
309     ScopedJSDeletePtr<JSCompartment> atomsCompartment(new_<JSCompartment>(atomsZone.get(), options));
310     if (!atomsCompartment || !atomsCompartment->init(nullptr))
311         return false;
312 
313     if (!gc.zones.append(atomsZone.get()))
314         return false;
315     if (!atomsZone->compartments.append(atomsCompartment.get()))
316         return false;
317 
318     atomsCompartment->setIsSystem(true);
319     atomsCompartment->setIsAtomsCompartment();
320 
321     atomsZone.forget();
322     this->atomsCompartment_ = atomsCompartment.forget();
323 
324     if (!symbolRegistry_.init())
325         return false;
326 
327     if (!scriptDataTable_.init())
328         return false;
329 
330     /* The garbage collector depends on everything before this point being initialized. */
331     gcInitialized = true;
332 
333     if (!InitRuntimeNumberState(this))
334         return false;
335 
336     JS::ResetTimeZone();
337 
338 #ifdef JS_SIMULATOR
339     simulator_ = js::jit::Simulator::Create(contextFromMainThread());
340     if (!simulator_)
341         return false;
342 #endif
343 
344     jitSupportsFloatingPoint = js::jit::JitSupportsFloatingPoint();
345     jitSupportsUnalignedAccesses = js::jit::JitSupportsUnalignedAccesses();
346     jitSupportsSimd = js::jit::JitSupportsSimd();
347 
348     if (!wasm::EnsureSignalHandlers(this))
349         return false;
350 
351     if (!spsProfiler.init())
352         return false;
353 
354     if (!fx.initInstance())
355         return false;
356 
357     if (!parentRuntime) {
358         sharedImmutableStrings_ = js::SharedImmutableStringsCache::Create();
359         if (!sharedImmutableStrings_)
360             return false;
361     }
362 
363     return true;
364 }
365 
366 void
destroyRuntime()367 JSRuntime::destroyRuntime()
368 {
369     MOZ_ASSERT(!isHeapBusy());
370     MOZ_ASSERT(childRuntimeCount == 0);
371 
372     fx.destroyInstance();
373 
374     sharedIntlData.destroyInstance();
375 
376     if (gcInitialized) {
377         /*
378          * Finish any in-progress GCs first. This ensures the parseWaitingOnGC
379          * list is empty in CancelOffThreadParses.
380          */
381         JSContext* cx = contextFromMainThread();
382         if (JS::IsIncrementalGCInProgress(cx))
383             FinishGC(cx);
384 
385         /* Free source hook early, as its destructor may want to delete roots. */
386         sourceHook = nullptr;
387 
388         /*
389          * Cancel any pending, in progress or completed Ion compilations and
390          * parse tasks. Waiting for wasm and compression tasks is done
391          * synchronously (on the main thread or during parse tasks), so no
392          * explicit canceling is needed for these.
393          */
394         CancelOffThreadIonCompile(this);
395         CancelOffThreadParses(this);
396 
397         /* Remove persistent GC roots. */
398         gc.finishRoots();
399 
400         /*
401          * Flag us as being destroyed. This allows the GC to free things like
402          * interned atoms and Ion trampolines.
403          */
404         beingDestroyed_ = true;
405 
406         /* Allow the GC to release scripts that were being profiled. */
407         profilingScripts = false;
408 
409         /* Set the profiler sampler buffer generation to invalid. */
410         profilerSampleBufferGen_ = UINT32_MAX;
411 
412         JS::PrepareForFullGC(contextFromMainThread());
413         gc.gc(GC_NORMAL, JS::gcreason::DESTROY_RUNTIME);
414     }
415 
416     MOZ_ASSERT(ionLazyLinkListSize_ == 0);
417     MOZ_ASSERT(ionLazyLinkList_.isEmpty());
418 
419     MOZ_ASSERT(!numExclusiveThreads);
420     AutoLockForExclusiveAccess lock(this);
421 
422     /*
423      * Even though all objects in the compartment are dead, we may have keep
424      * some filenames around because of gcKeepAtoms.
425      */
426     FreeScriptData(this, lock);
427 
428 #if !EXPOSE_INTL_API
429     FinishRuntimeNumberState(this);
430 #endif
431 
432     gc.finish();
433     atomsCompartment_ = nullptr;
434 
435     js_delete(defaultFreeOp_);
436 
437     js_free(defaultLocale);
438     js_delete(jitRuntime_);
439 
440     js_delete(ionPcScriptCache);
441 
442     gc.storeBuffer.disable();
443     gc.nursery.disable();
444 
445 #ifdef JS_SIMULATOR
446     js::jit::Simulator::Destroy(simulator_);
447 #endif
448 
449     DebugOnly<size_t> oldCount = liveRuntimesCount--;
450     MOZ_ASSERT(oldCount > 0);
451 
452 #ifdef JS_TRACE_LOGGING
453     DestroyTraceLoggerMainThread(this);
454 #endif
455 
456     js::TlsPerThreadData.set(nullptr);
457 
458 #ifdef XP_WIN
459     if (ownerThreadNative_)
460         CloseHandle((HANDLE)ownerThreadNative_);
461 #endif
462 }
463 
464 void
addTelemetry(int id,uint32_t sample,const char * key)465 JSRuntime::addTelemetry(int id, uint32_t sample, const char* key)
466 {
467     if (telemetryCallback)
468         (*telemetryCallback)(id, sample, key);
469 }
470 
471 void
setTelemetryCallback(JSRuntime * rt,JSAccumulateTelemetryDataCallback callback)472 JSRuntime::setTelemetryCallback(JSRuntime* rt, JSAccumulateTelemetryDataCallback callback)
473 {
474     rt->telemetryCallback = callback;
475 }
476 
477 void
addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,JS::RuntimeSizes * rtSizes)478 JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes* rtSizes)
479 {
480     // Several tables in the runtime enumerated below can be used off thread.
481     AutoLockForExclusiveAccess lock(this);
482 
483     // For now, measure the size of the derived class (JSContext).
484     // TODO (bug 1281529): make memory reporting reflect the new
485     // JSContext/JSRuntime world better.
486     JSContext* cx = unsafeContextFromAnyThread();
487     rtSizes->object += mallocSizeOf(cx);
488 
489     rtSizes->atomsTable += atoms(lock).sizeOfIncludingThis(mallocSizeOf);
490 
491     if (!parentRuntime) {
492         rtSizes->atomsTable += mallocSizeOf(staticStrings);
493         rtSizes->atomsTable += mallocSizeOf(commonNames);
494         rtSizes->atomsTable += permanentAtoms->sizeOfIncludingThis(mallocSizeOf);
495     }
496 
497     rtSizes->contexts += cx->sizeOfExcludingThis(mallocSizeOf);
498 
499     rtSizes->temporary += tempLifoAlloc.sizeOfExcludingThis(mallocSizeOf);
500 
501     rtSizes->interpreterStack += interpreterStack_.sizeOfExcludingThis(mallocSizeOf);
502 
503     if (MathCache* cache = cx->caches.maybeGetMathCache())
504         rtSizes->mathCache += cache->sizeOfIncludingThis(mallocSizeOf);
505 
506     if (sharedImmutableStrings_) {
507         rtSizes->sharedImmutableStringsCache +=
508             sharedImmutableStrings_->sizeOfExcludingThis(mallocSizeOf);
509     }
510 
511     rtSizes->sharedIntlData += sharedIntlData.sizeOfExcludingThis(mallocSizeOf);
512 
513     rtSizes->uncompressedSourceCache +=
514         cx->caches.uncompressedSourceCache.sizeOfExcludingThis(mallocSizeOf);
515 
516 
517     rtSizes->scriptData += scriptDataTable(lock).sizeOfExcludingThis(mallocSizeOf);
518     for (ScriptDataTable::Range r = scriptDataTable(lock).all(); !r.empty(); r.popFront())
519         rtSizes->scriptData += mallocSizeOf(r.front());
520 
521     if (jitRuntime_) {
522         jitRuntime_->execAlloc().addSizeOfCode(&rtSizes->code);
523         jitRuntime_->backedgeExecAlloc().addSizeOfCode(&rtSizes->code);
524     }
525 
526     rtSizes->gc.marker += gc.marker.sizeOfExcludingThis(mallocSizeOf);
527     rtSizes->gc.nurseryCommitted += gc.nursery.sizeOfHeapCommitted();
528     rtSizes->gc.nurseryMallocedBuffers += gc.nursery.sizeOfMallocedBuffers(mallocSizeOf);
529     gc.storeBuffer.addSizeOfExcludingThis(mallocSizeOf, &rtSizes->gc);
530 }
531 
532 static bool
InvokeInterruptCallback(JSContext * cx)533 InvokeInterruptCallback(JSContext* cx)
534 {
535     MOZ_ASSERT(cx->runtime()->requestDepth >= 1);
536 
537     cx->runtime()->gc.gcIfRequested();
538 
539     // A worker thread may have requested an interrupt after finishing an Ion
540     // compilation.
541     jit::AttachFinishedCompilations(cx);
542 
543     // Important: Additional callbacks can occur inside the callback handler
544     // if it re-enters the JS engine. The embedding must ensure that the
545     // callback is disconnected before attempting such re-entry.
546     if (cx->runtime()->interruptCallbackDisabled)
547         return true;
548 
549     bool stop = false;
550     for (JSInterruptCallback cb : cx->runtime()->interruptCallbacks) {
551         if (!cb(cx))
552             stop = true;
553     }
554 
555     if (!stop) {
556         // Debugger treats invoking the interrupt callback as a "step", so
557         // invoke the onStep handler.
558         if (cx->compartment()->isDebuggee()) {
559             ScriptFrameIter iter(cx);
560             if (!iter.done() &&
561                 cx->compartment() == iter.compartment() &&
562                 iter.script()->stepModeEnabled())
563             {
564                 RootedValue rval(cx);
565                 switch (Debugger::onSingleStep(cx, &rval)) {
566                   case JSTRAP_ERROR:
567                     return false;
568                   case JSTRAP_CONTINUE:
569                     return true;
570                   case JSTRAP_RETURN:
571                     // See note in Debugger::propagateForcedReturn.
572                     Debugger::propagateForcedReturn(cx, iter.abstractFramePtr(), rval);
573                     return false;
574                   case JSTRAP_THROW:
575                     cx->setPendingException(rval);
576                     return false;
577                   default:;
578                 }
579             }
580         }
581 
582         return true;
583     }
584 
585     // No need to set aside any pending exception here: ComputeStackString
586     // already does that.
587     JSString* stack = ComputeStackString(cx);
588     JSFlatString* flat = stack ? stack->ensureFlat(cx) : nullptr;
589 
590     const char16_t* chars;
591     AutoStableStringChars stableChars(cx);
592     if (flat && stableChars.initTwoByte(cx, flat))
593         chars = stableChars.twoByteRange().begin().get();
594     else
595         chars = u"(stack not available)";
596     JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_WARNING, GetErrorMessage, nullptr,
597                                    JSMSG_TERMINATED, chars);
598 
599     return false;
600 }
601 
602 void
requestInterrupt(InterruptMode mode)603 JSRuntime::requestInterrupt(InterruptMode mode)
604 {
605     interrupt_ = true;
606     jitStackLimit_ = UINTPTR_MAX;
607 
608     if (mode == JSRuntime::RequestInterruptUrgent) {
609         // If this interrupt is urgent (slow script dialog and garbage
610         // collection among others), take additional steps to
611         // interrupt corner cases where the above fields are not
612         // regularly polled.  Wake both ilooping JIT code and
613         // Atomics.wait().
614         fx.lock();
615         if (fx.isWaiting())
616             fx.wake(FutexRuntime::WakeForJSInterrupt);
617         fx.unlock();
618         InterruptRunningJitCode(this);
619     }
620 }
621 
622 bool
handleInterrupt(JSContext * cx)623 JSRuntime::handleInterrupt(JSContext* cx)
624 {
625     MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
626     if (interrupt_ || jitStackLimit_ == UINTPTR_MAX) {
627         interrupt_ = false;
628         cx->resetJitStackLimit();
629         return InvokeInterruptCallback(cx);
630     }
631     return true;
632 }
633 
634 bool
setDefaultLocale(const char * locale)635 JSRuntime::setDefaultLocale(const char* locale)
636 {
637     if (!locale)
638         return false;
639     resetDefaultLocale();
640     defaultLocale = JS_strdup(contextFromMainThread(), locale);
641     return defaultLocale != nullptr;
642 }
643 
644 void
resetDefaultLocale()645 JSRuntime::resetDefaultLocale()
646 {
647     js_free(defaultLocale);
648     defaultLocale = nullptr;
649 }
650 
651 const char*
getDefaultLocale()652 JSRuntime::getDefaultLocale()
653 {
654     if (defaultLocale)
655         return defaultLocale;
656 
657     const char* locale;
658 #ifdef HAVE_SETLOCALE
659     locale = setlocale(LC_ALL, nullptr);
660 #else
661     locale = getenv("LANG");
662 #endif
663     // convert to a well-formed BCP 47 language tag
664     if (!locale || !strcmp(locale, "C"))
665         locale = "und";
666 
667     char* lang = JS_strdup(contextFromMainThread(), locale);
668     if (!lang)
669         return nullptr;
670 
671     char* p;
672     if ((p = strchr(lang, '.')))
673         *p = '\0';
674     while ((p = strchr(lang, '_')))
675         *p = '-';
676 
677     defaultLocale = lang;
678     return defaultLocale;
679 }
680 
681 void
traceSharedIntlData(JSTracer * trc)682 JSRuntime::traceSharedIntlData(JSTracer* trc)
683 {
684     sharedIntlData.trace(trc);
685 }
686 
687 void
triggerActivityCallback(bool active)688 JSRuntime::triggerActivityCallback(bool active)
689 {
690     if (!activityCallback)
691         return;
692 
693     /*
694      * The activity callback must not trigger a GC: it would create a cirular
695      * dependency between entering a request and Rooted's requirement of being
696      * in a request. In practice this callback already cannot trigger GC. The
697      * suppression serves to inform the exact rooting hazard analysis of this
698      * property and ensures that it remains true in the future.
699      */
700     AutoSuppressGC suppress(contextFromMainThread());
701 
702     activityCallback(activityCallbackArg, active);
703 }
704 
FreeOp(JSRuntime * maybeRuntime)705 FreeOp::FreeOp(JSRuntime* maybeRuntime)
706   : JSFreeOp(maybeRuntime)
707 {
708     MOZ_ASSERT_IF(maybeRuntime, CurrentThreadCanAccessRuntime(maybeRuntime));
709 }
710 
~FreeOp()711 FreeOp::~FreeOp()
712 {
713     for (size_t i = 0; i < freeLaterList.length(); i++)
714         free_(freeLaterList[i]);
715 
716     if (!jitPoisonRanges.empty())
717         jit::ExecutableAllocator::poisonCode(runtime(), jitPoisonRanges);
718 }
719 
720 bool
isDefaultFreeOp() const721 FreeOp::isDefaultFreeOp() const
722 {
723     return runtime_ && runtime_->defaultFreeOp() == this;
724 }
725 
726 JSObject*
getIncumbentGlobal(JSContext * cx)727 JSRuntime::getIncumbentGlobal(JSContext* cx)
728 {
729     MOZ_ASSERT(cx->runtime()->getIncumbentGlobalCallback,
730                "Must set a callback using JS_SetGetIncumbentGlobalCallback before using Promises");
731 
732     return cx->runtime()->getIncumbentGlobalCallback(cx);
733 }
734 
735 bool
enqueuePromiseJob(JSContext * cx,HandleFunction job,HandleObject promise,HandleObject incumbentGlobal)736 JSRuntime::enqueuePromiseJob(JSContext* cx, HandleFunction job, HandleObject promise,
737                              HandleObject incumbentGlobal)
738 {
739     MOZ_ASSERT(cx->runtime()->enqueuePromiseJobCallback,
740                "Must set a callback using JS_SetEnqeueuPromiseJobCallback before using Promises");
741     MOZ_ASSERT_IF(incumbentGlobal, !IsWrapper(incumbentGlobal) && !IsWindowProxy(incumbentGlobal));
742 
743     void* data = cx->runtime()->enqueuePromiseJobCallbackData;
744     RootedObject allocationSite(cx);
745     if (promise) {
746         RootedObject unwrappedPromise(cx, promise);
747         // While the job object is guaranteed to be unwrapped, the promise
748         // might be wrapped. See the comments in
749         // intrinsic_EnqueuePromiseReactionJob for details.
750         if (IsWrapper(promise))
751             unwrappedPromise = UncheckedUnwrap(promise);
752         if (unwrappedPromise->is<PromiseObject>())
753             allocationSite = JS::GetPromiseAllocationSite(unwrappedPromise);
754     }
755     return cx->runtime()->enqueuePromiseJobCallback(cx, job, allocationSite, incumbentGlobal, data);
756 }
757 
758 void
addUnhandledRejectedPromise(JSContext * cx,js::HandleObject promise)759 JSRuntime::addUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise)
760 {
761     MOZ_ASSERT(promise->is<PromiseObject>());
762     if (!cx->runtime()->promiseRejectionTrackerCallback)
763         return;
764 
765     void* data = cx->runtime()->promiseRejectionTrackerCallbackData;
766     cx->runtime()->promiseRejectionTrackerCallback(cx, promise,
767                                                    PromiseRejectionHandlingState::Unhandled, data);
768 }
769 
770 void
removeUnhandledRejectedPromise(JSContext * cx,js::HandleObject promise)771 JSRuntime::removeUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise)
772 {
773     MOZ_ASSERT(promise->is<PromiseObject>());
774     if (!cx->runtime()->promiseRejectionTrackerCallback)
775         return;
776 
777     void* data = cx->runtime()->promiseRejectionTrackerCallbackData;
778     cx->runtime()->promiseRejectionTrackerCallback(cx, promise,
779                                                    PromiseRejectionHandlingState::Handled, data);
780 }
781 
782 mozilla::non_crypto::XorShift128PlusRNG&
randomKeyGenerator()783 JSRuntime::randomKeyGenerator()
784 {
785     MOZ_ASSERT(CurrentThreadCanAccessRuntime(this));
786     if (randomKeyGenerator_.isNothing()) {
787         mozilla::Array<uint64_t, 2> seed;
788         GenerateXorShift128PlusSeed(seed);
789         randomKeyGenerator_.emplace(seed[0], seed[1]);
790     }
791     return randomKeyGenerator_.ref();
792 }
793 
794 mozilla::HashCodeScrambler
randomHashCodeScrambler()795 JSRuntime::randomHashCodeScrambler()
796 {
797     auto& rng = randomKeyGenerator();
798     return mozilla::HashCodeScrambler(rng.next(), rng.next());
799 }
800 
801 mozilla::non_crypto::XorShift128PlusRNG
forkRandomKeyGenerator()802 JSRuntime::forkRandomKeyGenerator()
803 {
804     auto& rng = randomKeyGenerator();
805     return mozilla::non_crypto::XorShift128PlusRNG(rng.next(), rng.next());
806 }
807 
808 void
updateMallocCounter(size_t nbytes)809 JSRuntime::updateMallocCounter(size_t nbytes)
810 {
811     updateMallocCounter(nullptr, nbytes);
812 }
813 
814 void
updateMallocCounter(JS::Zone * zone,size_t nbytes)815 JSRuntime::updateMallocCounter(JS::Zone* zone, size_t nbytes)
816 {
817     gc.updateMallocCounter(zone, nbytes);
818 }
819 
JS_FRIEND_API(void *)820 JS_FRIEND_API(void*)
821 JSRuntime::onOutOfMemory(AllocFunction allocFunc, size_t nbytes, void* reallocPtr,
822                          JSContext* maybecx)
823 {
824     MOZ_ASSERT_IF(allocFunc != AllocFunction::Realloc, !reallocPtr);
825     MOZ_ASSERT(CurrentThreadCanAccessRuntime(this));
826 
827     if (isHeapBusy())
828         return nullptr;
829 
830     if (!oom::IsSimulatedOOMAllocation()) {
831         /*
832          * Retry when we are done with the background sweeping and have stopped
833          * all the allocations and released the empty GC chunks.
834          */
835         gc.onOutOfMallocMemory();
836         void* p;
837         switch (allocFunc) {
838           case AllocFunction::Malloc:
839             p = js_malloc(nbytes);
840             break;
841           case AllocFunction::Calloc:
842             p = js_calloc(nbytes);
843             break;
844           case AllocFunction::Realloc:
845             p = js_realloc(reallocPtr, nbytes);
846             break;
847           default:
848             MOZ_CRASH();
849         }
850         if (p)
851             return p;
852     }
853 
854     if (maybecx)
855         ReportOutOfMemory(maybecx);
856     return nullptr;
857 }
858 
859 void*
onOutOfMemoryCanGC(AllocFunction allocFunc,size_t bytes,void * reallocPtr)860 JSRuntime::onOutOfMemoryCanGC(AllocFunction allocFunc, size_t bytes, void* reallocPtr)
861 {
862     if (largeAllocationFailureCallback && bytes >= LARGE_ALLOCATION)
863         largeAllocationFailureCallback(largeAllocationFailureCallbackData);
864     return onOutOfMemory(allocFunc, bytes, reallocPtr);
865 }
866 
867 bool
activeGCInAtomsZone()868 JSRuntime::activeGCInAtomsZone()
869 {
870     Zone* zone = atomsCompartment_->zone();
871     return (zone->needsIncrementalBarrier() && !gc.isVerifyPreBarriersEnabled()) ||
872            zone->wasGCStarted();
873 }
874 
875 void
setUsedByExclusiveThread(Zone * zone)876 JSRuntime::setUsedByExclusiveThread(Zone* zone)
877 {
878     MOZ_ASSERT(!zone->usedByExclusiveThread);
879     zone->usedByExclusiveThread = true;
880     numExclusiveThreads++;
881 }
882 
883 void
clearUsedByExclusiveThread(Zone * zone)884 JSRuntime::clearUsedByExclusiveThread(Zone* zone)
885 {
886     MOZ_ASSERT(zone->usedByExclusiveThread);
887     zone->usedByExclusiveThread = false;
888     numExclusiveThreads--;
889     if (gc.fullGCForAtomsRequested() && !keepAtoms())
890         gc.triggerFullGCForAtoms();
891 }
892 
893 bool
CurrentThreadCanAccessRuntime(const JSRuntime * rt)894 js::CurrentThreadCanAccessRuntime(const JSRuntime* rt)
895 {
896     return rt->ownerThread_ == js::ThisThread::GetId();
897 }
898 
899 bool
CurrentThreadCanAccessZone(Zone * zone)900 js::CurrentThreadCanAccessZone(Zone* zone)
901 {
902     if (CurrentThreadCanAccessRuntime(zone->runtime_))
903         return true;
904 
905     // Only zones in use by an exclusive thread can be used off the main thread.
906     // We don't keep track of which thread owns such zones though, so this check
907     // is imperfect.
908     return zone->usedByExclusiveThread;
909 }
910 
911 #ifdef DEBUG
912 bool
CurrentThreadIsPerformingGC()913 js::CurrentThreadIsPerformingGC()
914 {
915     return TlsPerThreadData.get()->performingGC;
916 }
917 #endif
918 
JS_FRIEND_API(void)919 JS_FRIEND_API(void)
920 JS::UpdateJSContextProfilerSampleBufferGen(JSContext* cx, uint32_t generation,
921                                            uint32_t lapCount)
922 {
923     cx->setProfilerSampleBufferGen(generation);
924     cx->updateProfilerSampleBufferLapCount(lapCount);
925 }
926 
JS_FRIEND_API(bool)927 JS_FRIEND_API(bool)
928 JS::IsProfilingEnabledForContext(JSContext* cx)
929 {
930     MOZ_ASSERT(cx);
931     return cx->spsProfiler.enabled();
932 }
933 
934 JSRuntime::IonBuilderList&
ionLazyLinkList()935 JSRuntime::ionLazyLinkList()
936 {
937     MOZ_ASSERT(TlsPerThreadData.get()->runtimeFromMainThread(),
938                "Should only be mutated by the main thread.");
939     return ionLazyLinkList_;
940 }
941 
942 void
ionLazyLinkListRemove(jit::IonBuilder * builder)943 JSRuntime::ionLazyLinkListRemove(jit::IonBuilder* builder)
944 {
945     MOZ_ASSERT(TlsPerThreadData.get()->runtimeFromMainThread(),
946             "Should only be mutated by the main thread.");
947     MOZ_ASSERT(ionLazyLinkListSize_ > 0);
948 
949     builder->removeFrom(ionLazyLinkList());
950     ionLazyLinkListSize_--;
951 
952     MOZ_ASSERT(ionLazyLinkList().isEmpty() == (ionLazyLinkListSize_ == 0));
953 }
954 
955 void
ionLazyLinkListAdd(jit::IonBuilder * builder)956 JSRuntime::ionLazyLinkListAdd(jit::IonBuilder* builder)
957 {
958     MOZ_ASSERT(TlsPerThreadData.get()->runtimeFromMainThread(),
959             "Should only be mutated by the main thread.");
960     ionLazyLinkList().insertFront(builder);
961     ionLazyLinkListSize_++;
962 }
963 
964 JSContext*
contextFromMainThread()965 PerThreadData::contextFromMainThread()
966 {
967     return runtime_->contextFromMainThread();
968 }
969