Runtime.cpp (28109B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "vm/Runtime.h" 8 9 #include "mozilla/Atomics.h" 10 #include "mozilla/DebugOnly.h" 11 #if JS_HAS_INTL_API 12 # include "mozilla/intl/Locale.h" 13 #endif 14 #include "mozilla/MemoryReporting.h" 15 #include "mozilla/ThreadLocal.h" 16 17 #include <locale.h> 18 #include <string.h> 19 20 #include "jsfriendapi.h" 21 #include "jsmath.h" 22 23 #include "builtin/String.h" 24 #include "frontend/CompilationStencil.h" 25 #include "frontend/ParserAtom.h" // frontend::WellKnownParserAtoms 26 #include "gc/GC.h" 27 #include "gc/PublicIterators.h" 28 #include "jit/IonCompileTask.h" 29 #include "jit/JitRuntime.h" 30 #include "jit/Simulator.h" 31 #include "js/AllocationLogging.h" // JS_COUNT_CTOR, JS_COUNT_DTOR 32 #include "js/experimental/JSStencil.h" 33 #include "js/experimental/SourceHook.h" 34 #include "js/friend/ErrorMessages.h" // JSMSG_* 35 #include "js/Interrupt.h" 36 #include "js/MemoryMetrics.h" 37 #include "js/Stack.h" // JS::NativeStackLimitMin 38 #include "js/Wrapper.h" 39 #include "js/WrapperCallbacks.h" 40 #include "vm/DateTime.h" 41 #include "vm/JSFunction.h" 42 #include "vm/JSObject.h" 43 #include "vm/JSScript.h" 44 #include "vm/PromiseObject.h" // js::PromiseObject 45 #include "vm/SharedImmutableStringsCache.h" 46 #include "vm/Warnings.h" // js::WarnNumberUC 47 #include "wasm/WasmPI.h" 48 #include "wasm/WasmSignalHandlers.h" 49 50 #include "debugger/DebugAPI-inl.h" 51 #include "gc/ArenaList-inl.h" 52 #include "vm/JSContext-inl.h" 53 #include "vm/Realm-inl.h" 54 55 using namespace js; 56 57 using mozilla::Atomic; 58 using mozilla::DebugOnly; 59 60 /* static */ MOZ_THREAD_LOCAL(JSContext*) js::TlsContext; 61 /* static */ 62 Atomic<size_t> JSRuntime::liveRuntimesCount; 63 Atomic<JS::LargeAllocationFailureCallback> js::OnLargeAllocationFailure; 64 65 JS::FilenameValidationCallback js::gFilenameValidationCallback = nullptr; 66 67 namespace js { 68 69 #ifndef __wasi__ 70 bool gCanUseExtraThreads = true; 71 #else 72 bool gCanUseExtraThreads = false; 73 #endif 74 } // namespace js 75 76 void js::DisableExtraThreads() { gCanUseExtraThreads = false; } 77 78 const JSSecurityCallbacks js::NullSecurityCallbacks = {}; 79 80 static const JSWrapObjectCallbacks DefaultWrapObjectCallbacks = { 81 TransparentObjectWrapper, nullptr}; 82 83 extern bool DefaultHostEnsureCanAddPrivateElementCallback(JSContext* cx, 84 HandleValue val); 85 86 static size_t ReturnZeroSize(const void* p) { return 0; } 87 88 JSRuntime::JSRuntime(JSRuntime* parentRuntime) 89 : parentRuntime(parentRuntime), 90 #ifdef DEBUG 91 updateChildRuntimeCount(parentRuntime), 92 initialized_(false), 93 #endif 94 mainContext_(nullptr), 95 profilerSampleBufferRangeStart_(0), 96 telemetryCallback(nullptr), 97 consumeStreamCallback(nullptr), 98 reportStreamErrorCallback(nullptr), 99 hadOutOfMemory(false), 100 allowRelazificationForTesting(false), 101 destroyCompartmentCallback(nullptr), 102 sizeOfIncludingThisCompartmentCallback(nullptr), 103 destroyRealmCallback(nullptr), 104 realmNameCallback(nullptr), 105 securityCallbacks(&NullSecurityCallbacks), 106 DOMcallbacks(nullptr), 107 destroyPrincipals(nullptr), 108 readPrincipals(nullptr), 109 canAddPrivateElement(&DefaultHostEnsureCanAddPrivateElementCallback), 110 warningReporter(nullptr), 111 geckoProfiler_(thisFromCtor()), 112 trustedPrincipals_(nullptr), 113 wrapObjectCallbacks(&DefaultWrapObjectCallbacks), 114 preserveWrapperCallback(nullptr), 115 scriptEnvironmentPreparer(nullptr), 116 ctypesActivityCallback(nullptr), 117 windowProxyClass_(nullptr), 118 numRealms(0), 119 numDebuggeeRealms_(0), 120 numDebuggeeRealmsObservingCoverage_(0), 121 localeCallbacks(nullptr), 122 defaultLocale(nullptr), 123 profilingScripts(false), 124 scriptAndCountsVector(nullptr), 125 watchtowerTestingLog(nullptr), 126 jitRuntime_(nullptr), 127 gc(thisFromCtor()), 128 emptyString(nullptr), 129 #if !JS_HAS_INTL_API 130 thousandsSeparator(nullptr), 131 decimalSeparator(nullptr), 132 numGrouping(nullptr), 133 #endif 134 beingDestroyed_(false), 135 allowContentJS_(true), 136 atoms_(nullptr), 137 permanentAtoms_(nullptr), 138 staticStrings(nullptr), 139 commonNames(nullptr), 140 wellKnownSymbols(nullptr), 141 scriptDataTableHolder_(SharedScriptDataTableHolder::NeedsLock::No), 142 liveSABs(0), 143 beforeWaitCallback(nullptr), 144 afterWaitCallback(nullptr), 145 offthreadBaselineCompilationEnabled_(false), 146 offthreadIonCompilationEnabled_(true), 147 autoWritableJitCodeActive_(false), 148 oomCallback(nullptr), 149 debuggerMallocSizeOf(ReturnZeroSize), 150 stackFormat_(parentRuntime ? js::StackFormat::Default 151 : js::StackFormat::SpiderMonkey), 152 wasmInstances(mutexid::WasmRuntimeInstances), 153 moduleAsyncEvaluatingPostOrder(0), 154 pendingAsyncModuleEvaluations(0) { 155 JS_COUNT_CTOR(JSRuntime); 156 liveRuntimesCount++; 157 158 #ifndef __wasi__ 159 // See function comment for why we call this now, not in JS_Init(). 160 wasm::EnsureEagerProcessSignalHandlers(); 161 #endif // __wasi__ 162 } 163 164 JSRuntime::~JSRuntime() { 165 JS_COUNT_DTOR(JSRuntime); 166 MOZ_ASSERT(!initialized_); 167 168 DebugOnly<size_t> oldCount = liveRuntimesCount--; 169 MOZ_ASSERT(oldCount > 0); 170 171 MOZ_ASSERT(wasmInstances.lock()->empty()); 172 173 MOZ_ASSERT(numRealms == 0); 174 MOZ_ASSERT(numDebuggeeRealms_ == 0); 175 MOZ_ASSERT(numDebuggeeRealmsObservingCoverage_ == 0); 176 } 177 178 bool JSRuntime::init(JSContext* cx, uint32_t maxbytes) { 179 #ifdef DEBUG 180 MOZ_ASSERT(!initialized_); 181 initialized_ = true; 182 #endif 183 184 if (CanUseExtraThreads() && !EnsureHelperThreadsInitialized()) { 185 return false; 186 } 187 188 mainContext_ = cx; 189 190 if (!gc.init(maxbytes)) { 191 return false; 192 } 193 194 if (!InitRuntimeNumberState(this)) { 195 return false; 196 } 197 198 // As a hack, we clear our timezone cache every time we create a new runtime. 199 // Also see the comment in JS::Realm::init(). 200 js::ResetTimeZoneInternal(ResetTimeZoneMode::DontResetIfOffsetUnchanged); 201 202 caches().megamorphicSetPropCache = MakeUnique<MegamorphicSetPropCache>(); 203 if (!caches().megamorphicSetPropCache) { 204 return false; 205 } 206 207 return true; 208 } 209 210 void JSRuntime::destroyRuntime() { 211 MOZ_ASSERT(!JS::RuntimeHeapIsBusy()); 212 MOZ_ASSERT(childRuntimeCount == 0); 213 MOZ_ASSERT(initialized_); 214 215 #ifdef JS_HAS_INTL_API 216 sharedIntlData.ref().destroyInstance(); 217 #endif 218 219 watchtowerTestingLog.ref().reset(); 220 221 if (gc.wasInitialized()) { 222 /* 223 * Finish any in-progress GCs first. 224 */ 225 JSContext* cx = mainContextFromOwnThread(); 226 if (JS::IsIncrementalGCInProgress(cx)) { 227 gc::FinishGC(cx); 228 } 229 230 /* Free source hook early, as its destructor may want to delete roots. */ 231 sourceHook = nullptr; 232 233 /* 234 * Cancel any pending, in progress or completed baseline/Ion compilations 235 * and parse tasks. Waiting for wasm and compression tasks is done 236 * synchronously (on the main thread or during parse tasks), so no 237 * explicit canceling is needed for these. 238 */ 239 CancelOffThreadCompile(this); 240 CancelOffThreadDelazify(this); 241 CancelOffThreadCompressions(this); 242 243 /* 244 * Flag us as being destroyed. This allows the GC to free things like 245 * interned atoms and Ion trampolines. 246 */ 247 beingDestroyed_ = true; 248 249 /* Remove persistent GC roots. */ 250 gc.finishRoots(); 251 252 /* Allow the GC to release scripts that were being profiled. */ 253 profilingScripts = false; 254 255 JS::PrepareForFullGC(cx); 256 gc.gc(JS::GCOptions::Shutdown, JS::GCReason::DESTROY_RUNTIME); 257 } 258 259 AutoNoteSingleThreadedRegion anstr; 260 261 MOZ_ASSERT(scriptDataTableHolder().getWithoutLock().empty()); 262 263 #if !JS_HAS_INTL_API 264 FinishRuntimeNumberState(this); 265 #endif 266 267 gc.finish(); 268 269 for (auto [f, data] : cleanupClosures.ref()) { 270 f(data); 271 } 272 cleanupClosures.ref().clear(); 273 274 defaultLocale = nullptr; 275 js_delete(jitRuntime_.ref()); 276 277 #ifdef DEBUG 278 initialized_ = false; 279 #endif 280 } 281 282 void JSRuntime::addTelemetry(JSMetric id, uint32_t sample) { 283 if (telemetryCallback) { 284 (*telemetryCallback)(id, sample); 285 } 286 } 287 288 void JSRuntime::setTelemetryCallback( 289 JSRuntime* rt, JSAccumulateTelemetryDataCallback callback) { 290 rt->telemetryCallback = callback; 291 } 292 293 void JSRuntime::setUseCounter(JSObject* obj, JSUseCounter counter) { 294 if (useCounterCallback) { 295 // A use counter callback cannot GC. 296 JS::AutoSuppressGCAnalysis suppress; 297 (*useCounterCallback)(obj, counter); 298 } 299 } 300 301 void JSRuntime::setUseCounterCallback(JSRuntime* rt, 302 JSSetUseCounterCallback callback) { 303 rt->useCounterCallback = callback; 304 } 305 306 void JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, 307 JS::RuntimeSizes* rtSizes) { 308 rtSizes->object += mallocSizeOf(this); 309 310 rtSizes->atomsTable += atoms().sizeOfIncludingThis(mallocSizeOf); 311 rtSizes->gc.marker += gc.markers.sizeOfExcludingThis(mallocSizeOf); 312 for (auto& marker : gc.markers) { 313 rtSizes->gc.marker += marker->sizeOfIncludingThis(mallocSizeOf); 314 } 315 316 if (!parentRuntime) { 317 rtSizes->atomsTable += mallocSizeOf(staticStrings); 318 rtSizes->atomsTable += mallocSizeOf(commonNames); 319 rtSizes->atomsTable += permanentAtoms()->sizeOfIncludingThis(mallocSizeOf); 320 321 rtSizes->selfHostStencil = 322 selfHostStencilInput_->sizeOfIncludingThis(mallocSizeOf) + 323 selfHostStencil_->sizeOfIncludingThis(mallocSizeOf) + 324 selfHostScriptMap.ref().shallowSizeOfExcludingThis(mallocSizeOf); 325 } 326 327 JSContext* cx = mainContextFromAnyThread(); 328 rtSizes->contexts += cx->sizeOfIncludingThis(mallocSizeOf); 329 rtSizes->temporary += cx->tempLifoAlloc().sizeOfExcludingThis(mallocSizeOf); 330 rtSizes->interpreterStack += 331 cx->interpreterStack().sizeOfExcludingThis(mallocSizeOf); 332 rtSizes->uncompressedSourceCache += 333 caches().uncompressedSourceCache.sizeOfExcludingThis(mallocSizeOf); 334 335 rtSizes->gc.nurseryCommitted += gc.nursery().totalCommitted(); 336 rtSizes->gc.nurseryMallocedBuffers += 337 gc.nursery().sizeOfMallocedBuffers(mallocSizeOf); 338 gc.storeBuffer().addSizeOfExcludingThis(mallocSizeOf, &rtSizes->gc); 339 340 if (isMainRuntime()) { 341 rtSizes->sharedImmutableStringsCache += 342 js::SharedImmutableStringsCache::getSingleton().sizeOfExcludingThis( 343 mallocSizeOf); 344 rtSizes->atomsTable += 345 js::frontend::WellKnownParserAtoms::getSingleton().sizeOfExcludingThis( 346 mallocSizeOf); 347 } 348 349 #ifdef JS_HAS_INTL_API 350 rtSizes->sharedIntlData += 351 sharedIntlData.ref().sizeOfExcludingThis(mallocSizeOf); 352 #endif 353 354 { 355 auto& table = scriptDataTableHolder().getWithoutLock(); 356 357 rtSizes->scriptData += table.shallowSizeOfExcludingThis(mallocSizeOf); 358 for (SharedImmutableScriptDataTable::Range r = table.all(); !r.empty(); 359 r.popFront()) { 360 rtSizes->scriptData += r.front()->sizeOfIncludingThis(mallocSizeOf); 361 } 362 } 363 364 if (isMainRuntime()) { 365 AutoLockGlobalScriptData lock; 366 367 auto& table = js::globalSharedScriptDataTableHolder.get(lock); 368 369 rtSizes->scriptData += table.shallowSizeOfExcludingThis(mallocSizeOf); 370 for (SharedImmutableScriptDataTable::Range r = table.all(); !r.empty(); 371 r.popFront()) { 372 rtSizes->scriptData += r.front()->sizeOfIncludingThis(mallocSizeOf); 373 } 374 } 375 376 if (jitRuntime_) { 377 // Sizes of the IonCompileTasks we are holding for lazy linking 378 for (auto* task : jitRuntime_->ionLazyLinkList(this)) { 379 rtSizes->jitLazyLink += task->sizeOfExcludingThis(mallocSizeOf); 380 } 381 } 382 383 rtSizes->wasmRuntime += 384 wasmInstances.lock()->sizeOfExcludingThis(mallocSizeOf); 385 } 386 387 static bool InvokeInterruptCallbacks(JSContext* cx) { 388 bool stop = false; 389 for (JSInterruptCallback cb : cx->interruptCallbacks()) { 390 if (!cb(cx)) { 391 stop = true; 392 } 393 } 394 return stop; 395 } 396 397 static bool HandleInterrupt(JSContext* cx, bool invokeCallback, 398 bool oomStackTrace) { 399 MOZ_ASSERT(!cx->zone()->isAtomsZone()); 400 401 cx->runtime()->gc.gcIfRequested(); 402 403 if (oomStackTrace) { 404 // Capture OOM stack trace this way because we don't have memory to handle 405 // it the way ComputeStackString does. 406 cx->captureOOMStackTrace(); 407 } else { 408 // We can handle OOM interrupts while handling exceptions, when it isn't 409 // safe to attach finished compilations 410 411 // A worker thread may have requested an interrupt after finishing an 412 // offthread compilation. 413 jit::AttachFinishedCompilations(cx); 414 } 415 416 // Don't call the interrupt callback if we only interrupted for GC, Ion, or 417 // OOM. 418 if (!invokeCallback) { 419 return true; 420 } 421 422 // Important: Additional callbacks can occur inside the callback handler 423 // if it re-enters the JS engine. The embedding must ensure that the 424 // callback is disconnected before attempting such re-entry. 425 if (cx->interruptCallbackDisabled) { 426 return true; 427 } 428 429 if (!InvokeInterruptCallbacks(cx)) { 430 // Debugger treats invoking the interrupt callback as a "step", so 431 // invoke the onStep handler. 432 if (cx->realm()->isDebuggee()) { 433 ScriptFrameIter iter(cx); 434 if (!iter.done() && cx->compartment() == iter.compartment() && 435 DebugAPI::stepModeEnabled(iter.script())) { 436 if (!DebugAPI::onSingleStep(cx)) { 437 return false; 438 } 439 } 440 } 441 442 return true; 443 } 444 445 // No need to set aside any pending exception here: ComputeStackString 446 // already does that. 447 JSString* stack = ComputeStackString(cx); 448 449 UniqueTwoByteChars stringChars; 450 if (stack) { 451 stringChars = JS_CopyStringCharsZ(cx, stack); 452 if (!stringChars) { 453 cx->recoverFromOutOfMemory(); 454 } 455 } 456 457 const char16_t* chars; 458 if (stringChars) { 459 chars = stringChars.get(); 460 } else { 461 chars = u"(stack not available)"; 462 } 463 WarnNumberUC(cx, JSMSG_TERMINATED, chars); 464 cx->reportUncatchableException(); 465 return false; 466 } 467 468 void JSContext::requestInterrupt(InterruptReason reason) { 469 interruptBits_ |= uint32_t(reason); 470 jitStackLimit = JS::NativeStackLimitMin; 471 472 if (reason == InterruptReason::CallbackUrgent) { 473 // If this interrupt is urgent (slow script dialog for instance), take 474 // additional steps to interrupt corner cases where the above fields are 475 // not regularly polled. 476 FutexThread::lock(); 477 if (fx.isWaiting()) { 478 fx.notify(FutexThread::NotifyForJSInterrupt); 479 } 480 fx.unlock(); 481 } 482 483 if (reason == InterruptReason::CallbackUrgent || 484 reason == InterruptReason::MajorGC || 485 reason == InterruptReason::MinorGC) { 486 wasm::InterruptRunningCode(this); 487 } 488 } 489 490 bool JSContext::handleInterrupt() { 491 MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime())); 492 if (hasAnyPendingInterrupt() || jitStackLimit == JS::NativeStackLimitMin) { 493 bool invokeCallback = 494 hasPendingInterrupt(InterruptReason::CallbackUrgent) || 495 hasPendingInterrupt(InterruptReason::CallbackCanWait); 496 bool oomStackTrace = hasPendingInterrupt(InterruptReason::OOMStackTrace); 497 interruptBits_ = 0; 498 resetJitStackLimit(); 499 return HandleInterrupt(this, invokeCallback, oomStackTrace); 500 } 501 return true; 502 } 503 504 bool JSContext::handleInterruptNoCallbacks() { 505 MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime())); 506 if (hasAnyPendingInterrupt() || jitStackLimit == JS::NativeStackLimitMin) { 507 bool oomStackTrace = hasPendingInterrupt(InterruptReason::OOMStackTrace); 508 clearPendingInterrupt(js::InterruptReason::OOMStackTrace); 509 if (!hasAnyPendingInterrupt()) { 510 resetJitStackLimit(); 511 } 512 return HandleInterrupt(this, false, oomStackTrace); 513 } 514 return true; 515 } 516 517 void JSContext::clearPendingInterrupt(js::InterruptReason reason) { 518 // Interrupt bit have already been cleared. 519 interruptBits_ &= ~uint32_t(reason); 520 } 521 522 bool JSRuntime::setDefaultLocale(const char* locale) { 523 if (!locale) { 524 return false; 525 } 526 527 UniqueChars newLocale = DuplicateString(mainContextFromOwnThread(), locale); 528 if (!newLocale) { 529 return false; 530 } 531 532 #if JS_HAS_INTL_API 533 if (!LocaleHasDefaultCaseMapping(newLocale.get())) { 534 runtimeFuses.ref().defaultLocaleHasDefaultCaseMappingFuse.popFuse( 535 mainContextFromOwnThread()); 536 } 537 #endif 538 539 defaultLocale.ref() = std::move(newLocale); 540 return true; 541 } 542 543 void JSRuntime::resetDefaultLocale() { defaultLocale = nullptr; } 544 545 const char* JSRuntime::getDefaultLocale() { 546 if (defaultLocale.ref()) { 547 return defaultLocale.ref().get(); 548 } 549 550 // Use ICU if available to retrieve the default locale, this ensures ICU's 551 // default locale matches our default locale. 552 #if JS_HAS_INTL_API 553 const char* locale = mozilla::intl::Locale::GetDefaultLocale(); 554 #else 555 const char* locale = setlocale(LC_ALL, nullptr); 556 #endif 557 558 // convert to a well-formed BCP 47 language tag 559 if (!locale || !strcmp(locale, "C")) { 560 locale = "und"; 561 } 562 563 UniqueChars lang = DuplicateString(mainContextFromOwnThread(), locale); 564 if (!lang) { 565 return nullptr; 566 } 567 568 char* p; 569 if ((p = strchr(lang.get(), '.'))) { 570 *p = '\0'; 571 } 572 while ((p = strchr(lang.get(), '_'))) { 573 *p = '-'; 574 } 575 576 #if JS_HAS_INTL_API 577 if (!LocaleHasDefaultCaseMapping(lang.get())) { 578 runtimeFuses.ref().defaultLocaleHasDefaultCaseMappingFuse.popFuse( 579 mainContextFromOwnThread()); 580 } 581 #endif 582 583 defaultLocale.ref() = std::move(lang); 584 return defaultLocale.ref().get(); 585 } 586 587 #ifdef JS_HAS_INTL_API 588 void JSRuntime::traceSharedIntlData(JSTracer* trc) { 589 sharedIntlData.ref().trace(trc); 590 } 591 #endif 592 593 SharedScriptDataTableHolder& JSRuntime::scriptDataTableHolder() { 594 return scriptDataTableHolder_; 595 } 596 597 bool JSRuntime::getHostDefinedData(JSContext* cx, 598 JS::MutableHandle<JSObject*> data) const { 599 MOZ_ASSERT(cx->jobQueue); 600 601 return cx->jobQueue->getHostDefinedData(cx, data); 602 } 603 604 JS_PUBLIC_API JSObject* 605 JS::MaybeGetPromiseAllocationSiteFromPossiblyWrappedPromise( 606 HandleObject promise) { 607 if (!promise) { 608 return nullptr; 609 } 610 611 JSObject* unwrappedPromise = promise; 612 // While the job object is guaranteed to be unwrapped, the promise 613 // might be wrapped. See the comments in EnqueuePromiseReactionJob in 614 // builtin/Promise.cpp for details. 615 if (IsWrapper(promise)) { 616 unwrappedPromise = UncheckedUnwrap(promise); 617 } 618 if (unwrappedPromise->is<PromiseObject>()) { 619 return unwrappedPromise->as<PromiseObject>().allocationSite(); 620 } 621 return nullptr; 622 } 623 624 bool JSRuntime::enqueuePromiseJob(JSContext* cx, HandleFunction job, 625 HandleObject promise, 626 HandleObject hostDefinedData) { 627 MOZ_ASSERT(cx->jobQueue, 628 "Must select a JobQueue implementation using JS::JobQueue " 629 "or js::UseInternalJobQueues before using Promises"); 630 631 if (promise) { 632 #ifdef DEBUG 633 AssertSameCompartment(job, promise); 634 #endif 635 } 636 637 RootedObject allocationSite( 638 cx, JS::MaybeGetPromiseAllocationSiteFromPossiblyWrappedPromise(promise)); 639 return cx->jobQueue->enqueuePromiseJob(cx, promise, job, allocationSite, 640 hostDefinedData); 641 } 642 643 void JSRuntime::addUnhandledRejectedPromise(JSContext* cx, 644 js::HandleObject promise) { 645 MOZ_ASSERT(promise->is<PromiseObject>()); 646 if (!cx->promiseRejectionTrackerCallback) { 647 return; 648 } 649 650 bool mutedErrors = false; 651 if (JSScript* script = cx->currentScript()) { 652 mutedErrors = script->mutedErrors(); 653 } 654 655 void* data = cx->promiseRejectionTrackerCallbackData; 656 cx->promiseRejectionTrackerCallback( 657 cx, mutedErrors, promise, JS::PromiseRejectionHandlingState::Unhandled, 658 data); 659 } 660 661 void JSRuntime::removeUnhandledRejectedPromise(JSContext* cx, 662 js::HandleObject promise) { 663 MOZ_ASSERT(promise->is<PromiseObject>()); 664 if (!cx->promiseRejectionTrackerCallback) { 665 return; 666 } 667 668 bool mutedErrors = false; 669 if (JSScript* script = cx->currentScript()) { 670 mutedErrors = script->mutedErrors(); 671 } 672 673 void* data = cx->promiseRejectionTrackerCallbackData; 674 cx->promiseRejectionTrackerCallback( 675 cx, mutedErrors, promise, JS::PromiseRejectionHandlingState::Handled, 676 data); 677 } 678 679 mozilla::non_crypto::XorShift128PlusRNG& JSRuntime::randomKeyGenerator() { 680 MOZ_ASSERT(CurrentThreadCanAccessRuntime(this)); 681 if (randomKeyGenerator_.isNothing()) { 682 mozilla::Array<uint64_t, 2> seed; 683 GenerateXorShift128PlusSeed(seed); 684 randomKeyGenerator_.emplace(seed[0], seed[1]); 685 } 686 return randomKeyGenerator_.ref(); 687 } 688 689 mozilla::HashCodeScrambler JSRuntime::randomHashCodeScrambler() { 690 auto& rng = randomKeyGenerator(); 691 return mozilla::HashCodeScrambler(rng.next(), rng.next()); 692 } 693 694 mozilla::non_crypto::XorShift128PlusRNG JSRuntime::forkRandomKeyGenerator() { 695 auto& rng = randomKeyGenerator(); 696 return mozilla::non_crypto::XorShift128PlusRNG(rng.next(), rng.next()); 697 } 698 699 js::HashNumber JSRuntime::randomHashCode() { 700 MOZ_ASSERT(CurrentThreadCanAccessRuntime(this)); 701 702 if (randomHashCodeGenerator_.isNothing()) { 703 mozilla::Array<uint64_t, 2> seed; 704 GenerateXorShift128PlusSeed(seed); 705 randomHashCodeGenerator_.emplace(seed[0], seed[1]); 706 } 707 708 return HashNumber(randomHashCodeGenerator_->next()); 709 } 710 711 JS_PUBLIC_API void* JSRuntime::onOutOfMemory(AllocFunction allocFunc, 712 arena_id_t arena, size_t nbytes, 713 void* reallocPtr, 714 JSContext* maybecx) { 715 MOZ_ASSERT_IF(allocFunc != AllocFunction::Realloc, !reallocPtr); 716 717 if (JS::RuntimeHeapIsBusy()) { 718 return nullptr; 719 } 720 721 if (!oom::IsSimulatedOOMAllocation()) { 722 /* 723 * Retry when we are done with the background sweeping and have stopped 724 * all the allocations and released the empty GC chunks. 725 */ 726 gc.onOutOfMallocMemory(); 727 void* p; 728 switch (allocFunc) { 729 case AllocFunction::Malloc: 730 p = js_arena_malloc(arena, nbytes); 731 break; 732 case AllocFunction::Calloc: 733 p = js_arena_calloc(arena, nbytes, 1); 734 break; 735 case AllocFunction::Realloc: 736 p = js_arena_realloc(arena, reallocPtr, nbytes); 737 break; 738 default: 739 MOZ_CRASH(); 740 } 741 if (p) { 742 return p; 743 } 744 } 745 746 if (maybecx) { 747 ReportOutOfMemory(maybecx); 748 } 749 return nullptr; 750 } 751 752 void* JSRuntime::onOutOfMemoryCanGC(AllocFunction allocFunc, arena_id_t arena, 753 size_t bytes, void* reallocPtr) { 754 if (OnLargeAllocationFailure && bytes >= LARGE_ALLOCATION) { 755 OnLargeAllocationFailure(); 756 } 757 return onOutOfMemory(allocFunc, arena, bytes, reallocPtr); 758 } 759 760 bool JSRuntime::activeGCInAtomsZone() { 761 Zone* zone = unsafeAtomsZone(); 762 return (zone->needsIncrementalBarrier() && 763 !gc.isVerifyPreBarriersEnabled()) || 764 zone->wasGCStarted(); 765 } 766 767 void JSRuntime::commitPendingWrapperPreservations() { 768 for (NonAtomZonesIter zone(this); !zone.done(); zone.next()) { 769 commitPendingWrapperPreservations(zone); 770 } 771 } 772 773 void JSRuntime::commitPendingWrapperPreservations(JS::Zone* zone) { 774 for (JSObject* wrapper : zone->slurpPendingWrapperPreservations()) { 775 JS::Value objectWrapperSlot = 776 JS::GetReservedSlot(wrapper, JS_OBJECT_WRAPPER_SLOT); 777 // This mirrors logic in MaybePreserveDOMWrapper, and should be kept in 778 // sync with that. 779 if (objectWrapperSlot.isUndefined() || !objectWrapperSlot.toPrivate()) { 780 continue; 781 } 782 783 if (IsWrapper(wrapper)) { 784 wrapper = UncheckedUnwrap(wrapper); 785 } 786 787 Rooted<JSObject*> rooted(mainContextFromOwnThread(), wrapper); 788 bool success = preserveWrapperCallback(mainContextFromOwnThread(), rooted); 789 MOZ_RELEASE_ASSERT(success); 790 } 791 } 792 793 void JSRuntime::incrementNumDebuggeeRealms() { 794 if (numDebuggeeRealms_ == 0) { 795 jitRuntime()->baselineInterpreter().toggleDebuggerInstrumentation(true); 796 } 797 798 numDebuggeeRealms_++; 799 MOZ_ASSERT(numDebuggeeRealms_ <= numRealms); 800 } 801 802 void JSRuntime::decrementNumDebuggeeRealms() { 803 MOZ_ASSERT(numDebuggeeRealms_ > 0); 804 numDebuggeeRealms_--; 805 806 // Note: if we had shutdown leaks we can end up here while destroying the 807 // runtime. It's not safe to access JitRuntime trampolines because they're no 808 // longer traced. 809 if (numDebuggeeRealms_ == 0 && !isBeingDestroyed()) { 810 jitRuntime()->baselineInterpreter().toggleDebuggerInstrumentation(false); 811 } 812 } 813 814 void JSRuntime::incrementNumDebuggeeRealmsObservingCoverage() { 815 if (numDebuggeeRealmsObservingCoverage_ == 0) { 816 jit::BaselineInterpreter& interp = jitRuntime()->baselineInterpreter(); 817 interp.toggleCodeCoverageInstrumentation(true); 818 } 819 820 numDebuggeeRealmsObservingCoverage_++; 821 MOZ_ASSERT(numDebuggeeRealmsObservingCoverage_ <= numRealms); 822 } 823 824 void JSRuntime::decrementNumDebuggeeRealmsObservingCoverage() { 825 MOZ_ASSERT(numDebuggeeRealmsObservingCoverage_ > 0); 826 numDebuggeeRealmsObservingCoverage_--; 827 828 // Note: if we had shutdown leaks we can end up here while destroying the 829 // runtime. It's not safe to access JitRuntime trampolines because they're no 830 // longer traced. 831 if (numDebuggeeRealmsObservingCoverage_ == 0 && !isBeingDestroyed()) { 832 jit::BaselineInterpreter& interp = jitRuntime()->baselineInterpreter(); 833 interp.toggleCodeCoverageInstrumentation(false); 834 } 835 } 836 837 bool js::CurrentThreadCanAccessRuntime(const JSRuntime* rt) { 838 return rt->mainContextFromAnyThread() == TlsContext.get(); 839 } 840 841 bool js::CurrentThreadCanAccessZone(Zone* zone) { 842 return CurrentThreadCanAccessRuntime(zone->runtime_); 843 } 844 845 #ifdef DEBUG 846 bool js::CurrentThreadIsMainThread() { return !!TlsContext.get(); } 847 #endif 848 849 JS_PUBLIC_API void JS::SetJSContextProfilerSampleBufferRangeStart( 850 JSContext* cx, uint64_t rangeStart) { 851 cx->runtime()->setProfilerSampleBufferRangeStart(rangeStart); 852 } 853 854 JS_PUBLIC_API bool JS::IsProfilingEnabledForContext(JSContext* cx) { 855 MOZ_ASSERT(cx); 856 return cx->runtime()->geckoProfiler().enabled(); 857 } 858 859 JS_PUBLIC_API void JS::EnableRecordingAllocations( 860 JSContext* cx, JS::RecordAllocationsCallback callback, double probability) { 861 MOZ_ASSERT(cx); 862 cx->runtime()->startRecordingAllocations(probability, callback); 863 } 864 865 JS_PUBLIC_API void JS::DisableRecordingAllocations(JSContext* cx) { 866 MOZ_ASSERT(cx); 867 cx->runtime()->stopRecordingAllocations(); 868 } 869 870 JS_PUBLIC_API void JS::shadow::RegisterWeakCache( 871 JSRuntime* rt, detail::WeakCacheBase* cachep) { 872 rt->registerWeakCache(cachep); 873 } 874 875 void JSRuntime::startRecordingAllocations( 876 double probability, JS::RecordAllocationsCallback callback) { 877 allocationSamplingProbability = probability; 878 recordAllocationCallback = callback; 879 880 // Go through all of the existing realms, and turn on allocation tracking. 881 for (RealmsIter realm(this); !realm.done(); realm.next()) { 882 realm->setAllocationMetadataBuilder(&SavedStacks::metadataBuilder); 883 realm->chooseAllocationSamplingProbability(); 884 } 885 } 886 887 void JSRuntime::stopRecordingAllocations() { 888 recordAllocationCallback = nullptr; 889 // Go through all of the existing realms, and turn on allocation tracking. 890 for (RealmsIter realm(this); !realm.done(); realm.next()) { 891 js::GlobalObject* global = realm->maybeGlobal(); 892 if (!realm->isDebuggee() || !global || 893 !DebugAPI::isObservedByDebuggerTrackingAllocations(*global)) { 894 // Only remove the allocation metadata builder if no Debuggers are 895 // tracking allocations. 896 realm->forgetAllocationMetadataBuilder(); 897 } 898 } 899 } 900 901 // This function can run to ensure that when new realms are created 902 // they have allocation logging turned on. 903 void JSRuntime::ensureRealmIsRecordingAllocations( 904 Handle<GlobalObject*> global) { 905 if (recordAllocationCallback) { 906 if (!global->realm()->isRecordingAllocations()) { 907 // This is a new realm, turn on allocations for it. 908 global->realm()->setAllocationMetadataBuilder( 909 &SavedStacks::metadataBuilder); 910 } 911 // Ensure the probability is up to date with the current combination of 912 // debuggers and runtime profiling. 913 global->realm()->chooseAllocationSamplingProbability(); 914 } 915 }