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