Realm.cpp (27419B)
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 "js/shadow/Realm.h" // JS::shadow::Realm 8 #include "vm/Realm-inl.h" 9 10 #include "mozilla/MemoryReporting.h" 11 12 #include <stddef.h> 13 14 #include "jsfriendapi.h" 15 #include "jsmath.h" 16 17 #include "builtin/WrappedFunctionObject.h" 18 #include "debugger/DebugAPI.h" 19 #include "debugger/Debugger.h" 20 #include "gc/GC.h" 21 #include "jit/JitRuntime.h" 22 #include "js/CallAndConstruct.h" // JS::IsCallable 23 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* 24 #include "js/GCVariant.h" 25 #include "js/Proxy.h" 26 #include "js/RootingAPI.h" 27 #include "js/Wrapper.h" 28 #include "vm/Compartment.h" 29 #include "vm/DateTime.h" 30 #include "vm/Iteration.h" 31 #include "vm/JSContext.h" 32 #include "wasm/WasmInstance.h" 33 34 #include "gc/Marking-inl.h" 35 #include "vm/JSObject-inl.h" 36 37 using namespace js; 38 39 Realm::DebuggerVectorEntry::DebuggerVectorEntry(js::Debugger* dbg_, 40 JSObject* link) 41 : dbg(dbg_), debuggerLink(link) {} 42 43 ObjectRealm::ObjectRealm(JS::Zone* zone) 44 : innerViews(zone, zone), iteratorCache(zone) {} 45 46 Realm::Realm(Compartment* comp, const JS::RealmOptions& options) 47 : JS::shadow::Realm(comp), 48 zone_(comp->zone()), 49 runtime_(comp->runtimeFromMainThread()), 50 creationOptions_(options.creationOptions()), 51 behaviors_(options.behaviors()), 52 objects_(zone_), 53 randomKeyGenerator_(runtime_->forkRandomKeyGenerator()), 54 debuggers_(zone_), 55 allocatedDuringIncrementalGC_(zone_->isGCMarkingOrSweeping() || 56 zone_->isGCFinished()), 57 wasm(runtime_) { 58 runtime_->numRealms++; 59 } 60 61 Realm::~Realm() { 62 MOZ_ASSERT(!hasBeenEnteredIgnoringJit()); 63 MOZ_ASSERT(!localAllocSite); 64 MOZ_ASSERT_IF(isDebuggee(), isTracingExecution_); 65 66 // Write the code coverage information in a file. 67 if (lcovRealm_) { 68 runtime_->lcovOutput().writeLCovResult(*lcovRealm_); 69 } 70 71 if (allocationMetadataBuilder_) { 72 forgetAllocationMetadataBuilder(); 73 } 74 75 if (isTracingExecution_) { 76 disableExecutionTracing(); 77 } 78 79 MOZ_ASSERT(runtime_->numRealms > 0); 80 runtime_->numRealms--; 81 } 82 83 void Realm::init(JSContext* cx, JSPrincipals* principals) { 84 /* 85 * As a hack, we clear our timezone cache every time we create a new realm. 86 * This ensures that the cache is always relatively fresh, but shouldn't 87 * interfere with benchmarks that create tons of date objects (unless they 88 * also create tons of iframes, which seems unlikely). 89 */ 90 js::ResetTimeZoneInternal(ResetTimeZoneMode::DontResetIfOffsetUnchanged); 91 92 if (principals) { 93 // Any realm with the trusted principals -- and there can be 94 // multiple -- is a system realm. 95 isSystem_ = (principals == cx->runtime()->trustedPrincipals()); 96 JS_HoldPrincipals(principals); 97 principals_ = principals; 98 } 99 100 if (!isSystem_ && cx->hasExecutionTracer()) { 101 enableExecutionTracing(); 102 } 103 } 104 105 bool JSRuntime::createJitRuntime(JSContext* cx) { 106 using namespace js::jit; 107 108 MOZ_ASSERT(!jitRuntime_); 109 110 if (!CanLikelyAllocateMoreExecutableMemory()) { 111 // Try to release memory first instead of potentially reporting OOM below. 112 if (OnLargeAllocationFailure) { 113 OnLargeAllocationFailure(); 114 } 115 } 116 117 jit::JitRuntime* jrt = cx->new_<jit::JitRuntime>(); 118 if (!jrt) { 119 return false; 120 } 121 122 // Unfortunately, initialization depends on jitRuntime_ being non-null, so 123 // we can't just wait to assign jitRuntime_. 124 jitRuntime_ = jrt; 125 126 if (!jitRuntime_->initialize(cx)) { 127 js_delete(jitRuntime_.ref()); 128 jitRuntime_ = nullptr; 129 return false; 130 } 131 132 return true; 133 } 134 135 #ifdef JSGC_HASH_TABLE_CHECKS 136 137 void js::DtoaCache::checkCacheAfterMovingGC() { 138 MOZ_ASSERT(!str || !IsForwarded(str)); 139 } 140 141 #endif // JSGC_HASH_TABLE_CHECKS 142 143 NonSyntacticLexicalEnvironmentObject* 144 ObjectRealm::getOrCreateNonSyntacticLexicalEnvironment(JSContext* cx, 145 HandleObject enclosing, 146 HandleObject key, 147 HandleObject thisv) { 148 MOZ_ASSERT(&ObjectRealm::get(enclosing) == this); 149 150 if (!nonSyntacticLexicalEnvironments_) { 151 auto map = cx->make_unique<NonSyntacticLexialEnvironmentsMap>(cx); 152 if (!map) { 153 return nullptr; 154 } 155 156 nonSyntacticLexicalEnvironments_ = std::move(map); 157 } 158 159 JSObject* obj = nonSyntacticLexicalEnvironments_->get(key); 160 if (obj) { 161 return &obj->as<NonSyntacticLexicalEnvironmentObject>(); 162 } 163 164 MOZ_ASSERT(key->is<NonSyntacticVariablesObject>() || 165 !key->is<EnvironmentObject>()); 166 167 NonSyntacticLexicalEnvironmentObject* lexicalEnv = 168 NonSyntacticLexicalEnvironmentObject::create(cx, enclosing, thisv); 169 if (!lexicalEnv) { 170 return nullptr; 171 } 172 173 if (!nonSyntacticLexicalEnvironments_->put(key, lexicalEnv)) { 174 ReportOutOfMemory(cx); 175 return nullptr; 176 } 177 178 return lexicalEnv; 179 } 180 181 NonSyntacticLexicalEnvironmentObject* 182 ObjectRealm::getOrCreateNonSyntacticLexicalEnvironment( 183 JSContext* cx, Handle<NonSyntacticVariablesObject*> enclosing) { 184 HandleObject key = enclosing; 185 186 // NOTE: The default global |this| value is set to key for compatibility 187 // with existing users of the lexical environment cache. 188 // - When used by shared-global JSM loader, |this| must be the 189 // NonSyntacticVariablesObject passed as enclosing. 190 // See js::GetFunctionThis / js::GetNonSyntacticGlobalThis 191 return getOrCreateNonSyntacticLexicalEnvironment(cx, enclosing, key, 192 /*thisv = */ key); 193 } 194 195 NonSyntacticLexicalEnvironmentObject* 196 ObjectRealm::getOrCreateNonSyntacticLexicalEnvironment( 197 JSContext* cx, Handle<WithEnvironmentObject*> enclosing) { 198 MOZ_ASSERT(!enclosing->isSyntactic()); 199 200 // If a wrapped WithEnvironmentObject was passed in, unwrap it, as we may 201 // be creating different WithEnvironmentObject wrappers each time. 202 RootedObject key(cx, &enclosing->as<WithEnvironmentObject>().object()); 203 204 // NOTE: The default global |this| value is set to key for compatibility 205 // with existing users of the lexical environment cache. 206 // - When used by SubscriptLoader, |this| must be the target object of 207 // the WithEnvironmentObject wrapper. 208 // - When used by DOM Events, we execute directly as a function and do not 209 // access the |this| value. 210 // See js::GetFunctionThis / js::GetNonSyntacticGlobalThis 211 return getOrCreateNonSyntacticLexicalEnvironment(cx, enclosing, key, 212 /*thisv = */ key); 213 } 214 215 NonSyntacticLexicalEnvironmentObject* 216 ObjectRealm::getOrCreateNonSyntacticLexicalEnvironment( 217 JSContext* cx, Handle<WithEnvironmentObject*> enclosing, 218 Handle<NonSyntacticVariablesObject*> key) { 219 MOZ_ASSERT(!enclosing->isSyntactic()); 220 221 RootedObject thisv(cx, &enclosing->object()); 222 return getOrCreateNonSyntacticLexicalEnvironment(cx, enclosing, key, thisv); 223 } 224 225 NonSyntacticLexicalEnvironmentObject* 226 ObjectRealm::getNonSyntacticLexicalEnvironment(JSObject* key) const { 227 MOZ_ASSERT(&ObjectRealm::get(key) == this); 228 229 if (!nonSyntacticLexicalEnvironments_) { 230 return nullptr; 231 } 232 // If a wrapped WithEnvironmentObject was passed in, unwrap it as in 233 // getOrCreateNonSyntacticLexicalEnvironment. 234 if (key->is<WithEnvironmentObject>()) { 235 MOZ_ASSERT(!key->as<WithEnvironmentObject>().isSyntactic()); 236 key = &key->as<WithEnvironmentObject>().object(); 237 } 238 JSObject* lexicalEnv = nonSyntacticLexicalEnvironments_->get(key); 239 if (!lexicalEnv) { 240 return nullptr; 241 } 242 return &lexicalEnv->as<NonSyntacticLexicalEnvironmentObject>(); 243 } 244 245 void Realm::traceGlobalData(JSTracer* trc) { 246 // Trace things reachable from the realm's global. Note that these edges 247 // must be swept too in case the realm is live but the global is not. 248 249 savedStacks_.trace(trc); 250 251 DebugAPI::traceFromRealm(trc, this); 252 } 253 254 void ObjectRealm::trace(JSTracer* trc) { 255 if (objectMetadataTable) { 256 objectMetadataTable->trace(trc); 257 } 258 259 if (nonSyntacticLexicalEnvironments_) { 260 nonSyntacticLexicalEnvironments_->trace(trc); 261 } 262 } 263 264 void Realm::traceRoots(JSTracer* trc, 265 js::gc::GCRuntime::TraceOrMarkRuntime traceOrMark) { 266 // It's not possible to trigger a GC between allocating the pending object and 267 // setting its meta data in ~AutoSetNewObjectMetadata. 268 MOZ_RELEASE_ASSERT(!objectPendingMetadata_); 269 270 if (!JS::RuntimeHeapIsMinorCollecting()) { 271 // The global is never nursery allocated, so we don't need to 272 // trace it when doing a minor collection. 273 // 274 // If a realm is on-stack, we mark its global so that JSContext::global() 275 // remains valid. 276 if (shouldTraceGlobal() && global_) { 277 TraceRoot(trc, global_.unbarrieredAddress(), "on-stack realm global"); 278 } 279 280 // If the realm is still being initialized we set a flag so that it doesn't 281 // get deleted, since there may be GC things that contain pointers to it. 282 if (shouldTraceGlobal() && initializingGlobal_) { 283 allocatedDuringIncrementalGC_ = true; 284 } 285 } 286 287 // Nothing below here needs to be treated as a root if we aren't marking 288 // this zone for a collection. 289 if (traceOrMark == js::gc::GCRuntime::MarkRuntime && 290 !zone()->isCollectingFromAnyThread()) { 291 return; 292 } 293 294 /* Mark debug scopes, if present */ 295 if (debugEnvs_) { 296 debugEnvs_->trace(trc); 297 } 298 299 objects_.trace(trc); 300 baselineCompileQueue_.trace(trc); 301 } 302 303 void ObjectRealm::finishRoots() { 304 if (objectMetadataTable) { 305 objectMetadataTable->clear(); 306 } 307 308 if (nonSyntacticLexicalEnvironments_) { 309 nonSyntacticLexicalEnvironments_->clear(); 310 } 311 } 312 313 void Realm::finishRoots() { 314 if (debugEnvs_) { 315 debugEnvs_->finish(); 316 } 317 318 objects_.finishRoots(); 319 } 320 321 void ObjectRealm::sweepAfterMinorGC(JSTracer* trc) { 322 InnerViewTable& table = innerViews.get(); 323 if (table.needsSweepAfterMinorGC()) { 324 table.sweepAfterMinorGC(trc); 325 } 326 } 327 328 void Realm::sweepAfterMinorGC(JSTracer* trc) { 329 globalWriteBarriered = 0; 330 dtoaCache.purge(); 331 objects_.sweepAfterMinorGC(trc); 332 } 333 334 void Realm::traceWeakSavedStacks(JSTracer* trc) { savedStacks_.traceWeak(trc); } 335 336 void Realm::traceWeakGlobalEdge(JSTracer* trc) { 337 // If the global is dead, free its GlobalObjectData. 338 auto result = TraceWeakEdge(trc, &global_, "Realm::global_"); 339 if (result.isDead()) { 340 result.initialTarget()->releaseData(runtime_->gcContext()); 341 } 342 } 343 344 void Realm::traceWeakDebugEnvironmentEdges(JSTracer* trc) { 345 if (debugEnvs_) { 346 debugEnvs_->traceWeak(trc); 347 } 348 } 349 350 void Realm::fixupAfterMovingGC(JSTracer* trc) { 351 purge(); 352 traceWeakGlobalEdge(trc); 353 } 354 355 void Realm::purge() { 356 dtoaCache.purge(); 357 newProxyCache.purge(); 358 newPlainObjectWithPropsCache.purge(); 359 plainObjectAssignCache.purge(); 360 objects_.iteratorCache.clearAndCompact(); 361 } 362 363 void Realm::removeFromCompileQueue(JSScript* script) { 364 baselineCompileQueue_.remove(script); 365 } 366 367 // Check to see if this individual realm is recording allocations. Debuggers or 368 // runtimes can try and record allocations, so this method can check to see if 369 // any initialization is needed. 370 bool Realm::isRecordingAllocations() { return !!allocationMetadataBuilder_; } 371 372 void Realm::setAllocationMetadataBuilder( 373 const js::AllocationMetadataBuilder* builder) { 374 // Clear any jitcode in the runtime, which behaves differently depending on 375 // whether there is a creation callback. 376 if (bool(allocationMetadataBuilder_) != bool(builder)) { 377 ReleaseAllJITCode(runtime_->gcContext()); 378 if (builder) { 379 zone()->incNumRealmsWithAllocMetadataBuilder(); 380 } else { 381 zone()->decNumRealmsWithAllocMetadataBuilder(); 382 } 383 } 384 385 for (wasm::Instance* instance : wasm.instances()) { 386 instance->setAllocationMetadataBuilder(builder); 387 } 388 allocationMetadataBuilder_ = builder; 389 } 390 391 void Realm::forgetAllocationMetadataBuilder() { 392 if (!allocationMetadataBuilder_) { 393 return; 394 } 395 396 // Unlike setAllocationMetadataBuilder, we don't have to discard all JIT 397 // code here (code is still valid, just a bit slower because it doesn't do 398 // inline GC allocations when a metadata builder is present), but we do want 399 // to cancel off-thread Ion compilations to avoid races when Ion accesses 400 // numRealmsWithAllocMetadataBuilder_ off-thread. 401 CancelOffThreadIonCompile(zone()); 402 403 zone()->decNumRealmsWithAllocMetadataBuilder(); 404 405 for (wasm::Instance* instance : wasm.instances()) { 406 instance->setAllocationMetadataBuilder(nullptr); 407 } 408 allocationMetadataBuilder_ = nullptr; 409 } 410 411 void Realm::setNewObjectMetadata(JSContext* cx, HandleObject obj) { 412 MOZ_ASSERT(obj->maybeCCWRealm() == this); 413 cx->check(compartment(), obj); 414 415 AutoEnterOOMUnsafeRegion oomUnsafe; 416 if (JSObject* metadata = 417 allocationMetadataBuilder_->build(cx, obj, oomUnsafe)) { 418 MOZ_ASSERT(metadata->maybeCCWRealm() == obj->maybeCCWRealm()); 419 cx->check(metadata); 420 421 if (!objects_.objectMetadataTable) { 422 auto table = cx->make_unique<ObjectRealm::ObjectMetadataTable>(cx); 423 if (!table) { 424 oomUnsafe.crash("setNewObjectMetadata"); 425 } 426 427 objects_.objectMetadataTable = std::move(table); 428 } 429 430 if (!objects_.objectMetadataTable->put(obj, metadata)) { 431 oomUnsafe.crash("setNewObjectMetadata"); 432 } 433 } 434 } 435 436 void Realm::updateDebuggerObservesFlag(unsigned flag) { 437 MOZ_ASSERT(isDebuggee()); 438 MOZ_ASSERT(flag == DebuggerObservesAllExecution || 439 flag == DebuggerObservesCoverage || 440 flag == DebuggerObservesAsmJS || flag == DebuggerObservesWasm || 441 flag == DebuggerObservesNativeCall); 442 443 GlobalObject* global = 444 zone()->runtimeFromMainThread()->gc.isForegroundSweeping() 445 ? unsafeUnbarrieredMaybeGlobal() 446 : maybeGlobal(); 447 bool observes = false; 448 if (flag == DebuggerObservesAllExecution) { 449 observes = (global && DebugAPI::debuggerObservesAllExecution(global)) || 450 isTracingExecution_; 451 } else if (flag == DebuggerObservesCoverage) { 452 observes = DebugAPI::debuggerObservesCoverage(global); 453 } else if (flag == DebuggerObservesAsmJS) { 454 observes = DebugAPI::debuggerObservesAsmJS(global); 455 } else if (flag == DebuggerObservesWasm) { 456 observes = DebugAPI::debuggerObservesWasm(global); 457 } else if (flag == DebuggerObservesNativeCall) { 458 observes = DebugAPI::debuggerObservesNativeCall(global); 459 } 460 461 if (observes) { 462 debugModeBits_ |= flag; 463 } else { 464 debugModeBits_ &= ~flag; 465 } 466 } 467 468 void Realm::setIsDebuggee() { 469 if (!isDebuggee()) { 470 debugModeBits_ |= IsDebuggee; 471 runtimeFromMainThread()->incrementNumDebuggeeRealms(); 472 } 473 } 474 475 void Realm::unsetIsDebuggee() { 476 if (isDebuggee()) { 477 if (debuggerObservesCoverage()) { 478 runtime_->decrementNumDebuggeeRealmsObservingCoverage(); 479 } 480 debugModeBits_ = 0; 481 DebugEnvironments::onRealmUnsetIsDebuggee(this); 482 runtimeFromMainThread()->decrementNumDebuggeeRealms(); 483 } 484 } 485 486 void Realm::restoreDebugModeBitsOnOOM(uint32_t bits) { 487 // This is called from Debugger::addDebuggeeGlobal after calling 488 // Realm::setIsDebuggee. If the realm was not a debuggee realm before, we need 489 // to call unsetIsDebuggee to update counters on the JSRuntime. 490 491 MOZ_RELEASE_ASSERT(isDebuggee()); 492 493 if (!(bits & IsDebuggee)) { 494 MOZ_ASSERT(bits == 0); 495 unsetIsDebuggee(); 496 MOZ_ASSERT(debugModeBits_ == 0); 497 } else { 498 debugModeBits_ = bits; 499 } 500 } 501 502 void Realm::updateDebuggerObservesCoverage() { 503 bool previousState = debuggerObservesCoverage(); 504 updateDebuggerObservesFlag(DebuggerObservesCoverage); 505 if (previousState == debuggerObservesCoverage()) { 506 return; 507 } 508 509 if (debuggerObservesCoverage()) { 510 // Interrupt any running interpreter frame. The scriptCounts are 511 // allocated on demand when a script resumes its execution. 512 JSContext* cx = TlsContext.get(); 513 for (ActivationIterator iter(cx); !iter.done(); ++iter) { 514 if (iter->isInterpreter()) { 515 iter->asInterpreter()->enableInterruptsUnconditionally(); 516 } 517 } 518 runtime_->incrementNumDebuggeeRealmsObservingCoverage(); 519 return; 520 } 521 522 runtime_->decrementNumDebuggeeRealmsObservingCoverage(); 523 524 // If code coverage is enabled by any other means, keep it. 525 if (collectCoverageForDebug()) { 526 return; 527 } 528 529 clearScriptCounts(); 530 clearScriptLCov(); 531 } 532 533 coverage::LCovRealm* Realm::lcovRealm() { 534 if (!lcovRealm_) { 535 lcovRealm_ = js::MakeUnique<coverage::LCovRealm>(this); 536 } 537 return lcovRealm_.get(); 538 } 539 540 bool Realm::collectCoverageForDebug() const { 541 return debuggerObservesCoverage() || coverage::IsLCovEnabled(); 542 } 543 544 void Realm::clearScriptCounts() { zone()->clearScriptCounts(this); } 545 546 void Realm::clearScriptLCov() { zone()->clearScriptLCov(this); } 547 548 const char* Realm::getLocale() const { 549 if (RefPtr<LocaleString> locale = behaviors_.localeOverride()) { 550 return locale->chars(); 551 } 552 return runtime_->getDefaultLocale(); 553 } 554 555 void Realm::setLocaleOverride(const char* locale) { 556 // Clear any jitcode in the runtime, because compiled code doesn't handle 557 // updates to a realm's locale override. 558 ReleaseAllJITCode(runtime_->gcContext()); 559 560 behaviors_.setLocaleOverride(locale); 561 } 562 563 js::DateTimeInfo* Realm::getDateTimeInfo() { 564 #if JS_HAS_INTL_API 565 if (RefPtr<TimeZoneString> timeZone = behaviors_.timeZoneOverride()) { 566 if (!dateTimeInfo_) { 567 AutoEnterOOMUnsafeRegion oomUnsafe; 568 569 // Crash on OOM because we don't have a good way to handle it here. 570 dateTimeInfo_ = js::MakeUnique<js::DateTimeInfo>(timeZone); 571 if (!dateTimeInfo_) { 572 oomUnsafe.crash("getDateTimeInfo"); 573 } 574 } else { 575 dateTimeInfo_->updateTimeZoneOverride(timeZone); 576 } 577 return dateTimeInfo_.get(); 578 } 579 #endif 580 return nullptr; 581 } 582 583 void Realm::setTimeZoneOverride(const char* timeZone) { 584 // Clear any jitcode in the runtime, because compiled code doesn't handle 585 // updates to a realm's time zone override. 586 ReleaseAllJITCode(runtime_->gcContext()); 587 588 behaviors_.setTimeZoneOverride(timeZone); 589 } 590 591 void ObjectRealm::addSizeOfExcludingThis( 592 mozilla::MallocSizeOf mallocSizeOf, size_t* innerViewsArg, 593 size_t* objectMetadataTablesArg, 594 size_t* nonSyntacticLexicalEnvironmentsArg) { 595 *innerViewsArg += innerViews.sizeOfExcludingThis(mallocSizeOf); 596 597 if (objectMetadataTable) { 598 *objectMetadataTablesArg += 599 objectMetadataTable->shallowSizeOfIncludingThis(mallocSizeOf); 600 } 601 602 if (auto& map = nonSyntacticLexicalEnvironments_) { 603 *nonSyntacticLexicalEnvironmentsArg += 604 map->shallowSizeOfIncludingThis(mallocSizeOf); 605 } 606 } 607 608 void Realm::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, 609 size_t* realmObject, size_t* realmTables, 610 size_t* innerViewsArg, 611 size_t* objectMetadataTablesArg, 612 size_t* savedStacksSet, 613 size_t* nonSyntacticLexicalEnvironmentsArg) { 614 *realmObject += mallocSizeOf(this); 615 wasm.addSizeOfExcludingThis(mallocSizeOf, realmTables); 616 617 objects_.addSizeOfExcludingThis(mallocSizeOf, innerViewsArg, 618 objectMetadataTablesArg, 619 nonSyntacticLexicalEnvironmentsArg); 620 621 *savedStacksSet += savedStacks_.sizeOfExcludingThis(mallocSizeOf); 622 } 623 624 bool Realm::shouldCaptureStackForThrow() { 625 // Determine whether a stack trace should be captured for throw-statements (or 626 // similar) in JS code in this realm. We don't want to do this unconditionally 627 // because capturing stacks is slow and some scripts throw a lot of 628 // exceptions. 629 // 630 // Note: this is unrelated to Error.stack! That property is observable from 631 // JS code so we can't use these heuristics there. The code here is mostly 632 // relevant for uncaught exceptions that are not Error objects. 633 634 // To match other browsers, we always capture a stack trace if the realm is a 635 // debuggee (this includes the devtools console being open) or if unlimited 636 // stack traces have been enabled for this realm (used in automation). 637 if (isDebuggee() || isUnlimitedStacksCapturingEnabled) { 638 return true; 639 } 640 641 // Also always capture for chrome code. This is code we control and this helps 642 // debugging. 643 if (principals() && 644 principals() == runtimeFromMainThread()->trustedPrincipals()) { 645 return true; 646 } 647 648 // Else, capture the stack only for the first N exceptions so that we can 649 // still show stack traces for scripts that don't throw a lot of exceptions 650 // (if the console is opened later). 651 static constexpr uint16_t MaxStacksCapturedForThrow = 50; 652 if (numStacksCapturedForThrow_ > MaxStacksCapturedForThrow) { 653 return false; 654 } 655 numStacksCapturedForThrow_++; 656 return true; 657 } 658 659 mozilla::HashCodeScrambler Realm::randomHashCodeScrambler() { 660 return mozilla::HashCodeScrambler(randomKeyGenerator_.next(), 661 randomKeyGenerator_.next()); 662 } 663 664 void AutoSetNewObjectMetadata::setPendingMetadata() { 665 JSObject* obj = cx_->realm()->getAndClearObjectPendingMetadata(); 666 if (!obj) { 667 return; 668 } 669 670 MOZ_ASSERT(obj->getClass()->shouldDelayMetadataBuilder()); 671 672 if (cx_->isExceptionPending()) { 673 return; 674 } 675 676 // This function is called from a destructor that often runs upon exit from 677 // a function that is returning an unrooted pointer to a Cell. The 678 // allocation metadata callback often allocates; if it causes a GC, then the 679 // Cell pointer being returned won't be traced or relocated. 680 // 681 // The only extant callbacks are those internal to SpiderMonkey that 682 // capture the JS stack. In fact, we're considering removing general 683 // callbacks altogther in bug 1236748. Since it's not running arbitrary 684 // code, it's adequate to simply suppress GC while we run the callback. 685 gc::AutoSuppressGC autoSuppressGC(cx_); 686 687 (void)SetNewObjectMetadata(cx_, obj); 688 } 689 690 JS_PUBLIC_API void gc::TraceRealm(JSTracer* trc, JS::Realm* realm, 691 const char* name) { 692 // The way GC works with compartments is basically incomprehensible. 693 // For Realms, what we want is very simple: each Realm has a strong 694 // reference to its GlobalObject, and vice versa. 695 // 696 // Here we simply trace our side of that edge. During GC, 697 // GCRuntime::traceRuntimeCommon() marks all other realm roots, for 698 // all realms. 699 realm->traceGlobalData(trc); 700 } 701 702 JS_PUBLIC_API JS::Realm* JS::GetCurrentRealmOrNull(JSContext* cx) { 703 return cx->realm(); 704 } 705 706 JS_PUBLIC_API JS::Realm* JS::GetObjectRealmOrNull(JSObject* obj) { 707 return IsCrossCompartmentWrapper(obj) ? nullptr : obj->nonCCWRealm(); 708 } 709 710 JS_PUBLIC_API void* JS::GetRealmPrivate(JS::Realm* realm) { 711 return realm->realmPrivate(); 712 } 713 714 JS_PUBLIC_API bool JS::HasRealmInitializedGlobal(JS::Realm* realm) { 715 return realm->hasInitializedGlobal(); 716 } 717 718 JS_PUBLIC_API void JS::SetRealmPrivate(JS::Realm* realm, void* data) { 719 realm->setRealmPrivate(data); 720 } 721 722 JS_PUBLIC_API void JS::SetDestroyRealmCallback( 723 JSContext* cx, JS::DestroyRealmCallback callback) { 724 cx->runtime()->destroyRealmCallback = callback; 725 } 726 727 JS_PUBLIC_API void JS::SetRealmNameCallback(JSContext* cx, 728 JS::RealmNameCallback callback) { 729 cx->runtime()->realmNameCallback = callback; 730 } 731 732 JS_PUBLIC_API JSObject* JS::GetRealmGlobalOrNull(JS::Realm* realm) { 733 return realm->maybeGlobal(); 734 } 735 736 JS_PUBLIC_API bool JS::InitRealmStandardClasses(JSContext* cx) { 737 MOZ_ASSERT(!cx->zone()->isAtomsZone()); 738 AssertHeapIsIdle(); 739 CHECK_THREAD(cx); 740 return GlobalObject::initStandardClasses(cx, cx->global()); 741 } 742 743 JS_PUBLIC_API bool JS::MaybeFreezeCtorAndPrototype(JSContext* cx, 744 HandleObject ctor, 745 HandleObject maybeProto) { 746 if (MOZ_LIKELY(!cx->realm()->creationOptions().freezeBuiltins())) { 747 return true; 748 } 749 if (!SetIntegrityLevel(cx, ctor, IntegrityLevel::Frozen)) { 750 return false; 751 } 752 if (maybeProto) { 753 if (!SetIntegrityLevel(cx, maybeProto, IntegrityLevel::Sealed)) { 754 return false; 755 } 756 } 757 return true; 758 } 759 760 JS_PUBLIC_API JSObject* JS::GetRealmObjectPrototype(JSContext* cx) { 761 CHECK_THREAD(cx); 762 return &cx->global()->getObjectPrototype(); 763 } 764 765 JS_PUBLIC_API JS::Handle<JSObject*> JS::GetRealmObjectPrototypeHandle( 766 JSContext* cx) { 767 return cx->global()->getObjectPrototypeHandle(); 768 } 769 770 JS_PUBLIC_API JSObject* JS::GetRealmFunctionPrototype(JSContext* cx) { 771 CHECK_THREAD(cx); 772 return &cx->global()->getFunctionPrototype(); 773 } 774 775 JS_PUBLIC_API JS::Handle<JSObject*> JS::GetRealmFunctionPrototypeHandle( 776 JSContext* cx) { 777 CHECK_THREAD(cx); 778 return cx->global()->getFunctionPrototypeHandle(); 779 } 780 781 JS_PUBLIC_API JSObject* JS::GetRealmArrayPrototype(JSContext* cx) { 782 CHECK_THREAD(cx); 783 return GlobalObject::getOrCreateArrayPrototype(cx, cx->global()); 784 } 785 786 JS_PUBLIC_API JSObject* JS::GetRealmErrorPrototype(JSContext* cx) { 787 CHECK_THREAD(cx); 788 return GlobalObject::getOrCreateCustomErrorPrototype(cx, cx->global(), 789 JSEXN_ERR); 790 } 791 792 JS_PUBLIC_API JSObject* JS::GetRealmIteratorPrototype(JSContext* cx) { 793 CHECK_THREAD(cx); 794 return GlobalObject::getOrCreateIteratorPrototype(cx, cx->global()); 795 } 796 797 JS_PUBLIC_API JSObject* JS::GetRealmAsyncIteratorPrototype(JSContext* cx) { 798 CHECK_THREAD(cx); 799 return GlobalObject::getOrCreateAsyncIteratorPrototype(cx, cx->global()); 800 } 801 802 JS_PUBLIC_API JSObject* JS::GetRealmKeyObject(JSContext* cx) { 803 return GlobalObject::getOrCreateRealmKeyObject(cx, cx->global()); 804 } 805 806 JS_PUBLIC_API Realm* JS::GetFunctionRealm(JSContext* cx, HandleObject objArg) { 807 // https://tc39.github.io/ecma262/#sec-getfunctionrealm 808 // 7.3.22 GetFunctionRealm ( obj ) 809 810 CHECK_THREAD(cx); 811 cx->check(objArg); 812 813 RootedObject obj(cx, objArg); 814 while (true) { 815 obj = CheckedUnwrapStatic(obj); 816 if (!obj) { 817 ReportAccessDenied(cx); 818 return nullptr; 819 } 820 821 // Step 1. 822 MOZ_ASSERT(IsCallable(obj)); 823 824 // Steps 2 and 3. We use a loop instead of recursion to unwrap bound 825 // functions. 826 if (obj->is<JSFunction>()) { 827 return obj->as<JSFunction>().realm(); 828 } 829 if (obj->is<BoundFunctionObject>()) { 830 obj = obj->as<BoundFunctionObject>().getTarget(); 831 continue; 832 } 833 834 // WrappedFunctionObjects also have a [[Realm]] internal slot, 835 // which is the nonCCWRealm by construction. 836 if (obj->is<WrappedFunctionObject>()) { 837 return obj->nonCCWRealm(); 838 } 839 840 // Step 4. 841 if (IsScriptedProxy(obj)) { 842 // Steps 4.a-b. 843 JSObject* proxyTarget = GetProxyTargetObject(obj); 844 if (!proxyTarget) { 845 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 846 JSMSG_PROXY_REVOKED); 847 return nullptr; 848 } 849 850 // Step 4.c. 851 obj = proxyTarget; 852 continue; 853 } 854 855 // Step 5. 856 return cx->realm(); 857 } 858 } 859 860 JS_PUBLIC_API void JS::ResetRealmMathRandomSeed(JSContext* cx) { 861 MOZ_ASSERT(cx->realm()); 862 auto rng = cx->realm()->getOrCreateRandomNumberGenerator(); 863 mozilla::Array<uint64_t, 2> seed; 864 GenerateXorShift128PlusSeed(seed); 865 rng.setState(seed[0], seed[1]); 866 }