tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }