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