/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "vm/Realm-inl.h"

#include "mozilla/MemoryReporting.h"

#include <stddef.h>

#include "jsfriendapi.h"

#include "debugger/DebugAPI.h"
#include "debugger/Debugger.h"
#include "gc/Policy.h"
#include "gc/PublicIterators.h"
#include "jit/JitOptions.h"
#include "jit/JitRealm.h"
#include "js/Date.h"
#include "js/Proxy.h"
#include "js/RootingAPI.h"
#include "js/Wrapper.h"
#include "proxy/DeadObjectProxy.h"
#include "vm/DateTime.h"
#include "vm/Iteration.h"
#include "vm/JSContext.h"
#include "vm/WrapperObject.h"

#include "gc/GC-inl.h"
#include "gc/Marking-inl.h"
#include "vm/JSAtom-inl.h"
#include "vm/JSFunction-inl.h"
#include "vm/JSObject-inl.h"
#include "vm/JSScript-inl.h"
#include "vm/NativeObject-inl.h"

using namespace js;

Realm::DebuggerVectorEntry::DebuggerVectorEntry(js::Debugger* dbg_,
                                                JSObject* link)
    : dbg(dbg_), debuggerLink(link) {}

ObjectRealm::ObjectRealm(JS::Zone* zone)
    : innerViews(zone, zone), iteratorCache(zone) {}

ObjectRealm::~ObjectRealm() {
  MOZ_ASSERT(enumerators == iteratorSentinel_.get());
}

Realm::Realm(Compartment* comp, const JS::RealmOptions& options)
    : JS::shadow::Realm(comp),
      zone_(comp->zone()),
      runtime_(comp->runtimeFromMainThread()),
      creationOptions_(options.creationOptions()),
      behaviors_(options.behaviors()),
      objects_(zone_),
      varNames_(zone_),
      randomKeyGenerator_(runtime_->forkRandomKeyGenerator()),
      debuggers_(zone_),
      wasm(runtime_) {
  MOZ_ASSERT_IF(creationOptions_.mergeable(),
                creationOptions_.invisibleToDebugger());

  runtime_->numRealms++;
}

Realm::~Realm() {
  MOZ_ASSERT(!hasBeenEnteredIgnoringJit());
  MOZ_ASSERT(!isDebuggee());

  // Write the code coverage information in a file.
  if (lcovRealm_) {
    runtime_->lcovOutput().writeLCovResult(*lcovRealm_);
  }

  MOZ_ASSERT(runtime_->numRealms > 0);
  runtime_->numRealms--;
}

bool ObjectRealm::init(JSContext* cx) {
  NativeIteratorSentinel sentinel(NativeIterator::allocateSentinel(cx));
  if (!sentinel) {
    return false;
  }

  iteratorSentinel_ = std::move(sentinel);
  enumerators = iteratorSentinel_.get();
  return true;
}

bool Realm::init(JSContext* cx, JSPrincipals* principals) {
  /*
   * As a hack, we clear our timezone cache every time we create a new realm.
   * This ensures that the cache is always relatively fresh, but shouldn't
   * interfere with benchmarks that create tons of date objects (unless they
   * also create tons of iframes, which seems unlikely).
   */
  js::ResetTimeZoneInternal(ResetTimeZoneMode::DontResetIfOffsetUnchanged);

  if (!objects_.init(cx)) {
    return false;
  }

  if (principals) {
    // Any realm with the trusted principals -- and there can be
    // multiple -- is a system realm.
    isSystem_ = (principals == cx->runtime()->trustedPrincipals());
    JS_HoldPrincipals(principals);
    principals_ = principals;
  }

  return true;
}

void Realm::setIsSelfHostingRealm() {
  MOZ_ASSERT(!isSelfHostingRealm_);
  MOZ_ASSERT(zone()->isSelfHostingZone());
  isSelfHostingRealm_ = true;
  isSystem_ = true;
}

bool JSRuntime::createJitRuntime(JSContext* cx) {
  using namespace js::jit;

  MOZ_ASSERT(!jitRuntime_);

  if (!CanLikelyAllocateMoreExecutableMemory()) {
    // Try to release memory first instead of potentially reporting OOM below.
    if (OnLargeAllocationFailure) {
      OnLargeAllocationFailure();
    }
  }

  jit::JitRuntime* jrt = cx->new_<jit::JitRuntime>();
  if (!jrt) {
    return false;
  }

  // Unfortunately, initialization depends on jitRuntime_ being non-null, so
  // we can't just wait to assign jitRuntime_.
  jitRuntime_ = jrt;

  if (!jitRuntime_->initialize(cx)) {
    js_delete(jitRuntime_.ref());
    jitRuntime_ = nullptr;
    return false;
  }

  return true;
}

bool Realm::ensureJitRealmExists(JSContext* cx) {
  using namespace js::jit;

  if (jitRealm_) {
    return true;
  }

  if (!zone()->getJitZone(cx)) {
    return false;
  }

  UniquePtr<JitRealm> jitRealm = cx->make_unique<JitRealm>();
  if (!jitRealm) {
    return false;
  }

  if (!jitRealm->initialize(cx, zone()->allocNurseryStrings)) {
    return false;
  }

  jitRealm_ = std::move(jitRealm);
  return true;
}

#ifdef JSGC_HASH_TABLE_CHECKS

void js::DtoaCache::checkCacheAfterMovingGC() {
  MOZ_ASSERT(!s || !IsForwarded(s));
}

#endif  // JSGC_HASH_TABLE_CHECKS

LexicalEnvironmentObject*
ObjectRealm::getOrCreateNonSyntacticLexicalEnvironment(JSContext* cx,
                                                       HandleObject enclosing,
                                                       HandleObject key,
                                                       HandleObject thisv) {
  MOZ_ASSERT(&ObjectRealm::get(enclosing) == this);

  if (!nonSyntacticLexicalEnvironments_) {
    auto map = cx->make_unique<ObjectWeakMap>(cx);
    if (!map) {
      return nullptr;
    }

    nonSyntacticLexicalEnvironments_ = std::move(map);
  }

  RootedObject lexicalEnv(cx, nonSyntacticLexicalEnvironments_->lookup(key));

  if (!lexicalEnv) {
    MOZ_ASSERT(key->is<NonSyntacticVariablesObject>() ||
               !key->is<EnvironmentObject>());
    lexicalEnv =
        LexicalEnvironmentObject::createNonSyntactic(cx, enclosing, thisv);
    if (!lexicalEnv) {
      return nullptr;
    }
    if (!nonSyntacticLexicalEnvironments_->add(cx, key, lexicalEnv)) {
      return nullptr;
    }
  }

  return &lexicalEnv->as<LexicalEnvironmentObject>();
}

LexicalEnvironmentObject*
ObjectRealm::getOrCreateNonSyntacticLexicalEnvironment(JSContext* cx,
                                                       HandleObject enclosing) {
  // If a wrapped WithEnvironmentObject was passed in, unwrap it, as we may
  // be creating different WithEnvironmentObject wrappers each time.
  RootedObject key(cx, enclosing);
  if (enclosing->is<WithEnvironmentObject>()) {
    MOZ_ASSERT(!enclosing->as<WithEnvironmentObject>().isSyntactic());
    key = &enclosing->as<WithEnvironmentObject>().object();
  }

  // NOTE: The default global |this| value is set to key for compatibility
  // with existing users of the lexical environment cache.
  //  - When used by shared-global JSM loader, |this| must be the
  //    NonSyntacticVariablesObject passed as enclosing.
  //  - When used by SubscriptLoader, |this| must be the target object of
  //    the WithEnvironmentObject wrapper.
  //  - When used by XBL/DOM Events, we execute directly as a function and
  //    do not access the |this| value.
  // See js::GetFunctionThis / js::GetNonSyntacticGlobalThis
  return getOrCreateNonSyntacticLexicalEnvironment(cx, enclosing, key,
                                                   /*thisv = */ key);
}

LexicalEnvironmentObject* ObjectRealm::getNonSyntacticLexicalEnvironment(
    JSObject* key) const {
  MOZ_ASSERT(&ObjectRealm::get(key) == this);

  if (!nonSyntacticLexicalEnvironments_) {
    return nullptr;
  }
  // If a wrapped WithEnvironmentObject was passed in, unwrap it as in
  // getOrCreateNonSyntacticLexicalEnvironment.
  if (key->is<WithEnvironmentObject>()) {
    MOZ_ASSERT(!key->as<WithEnvironmentObject>().isSyntactic());
    key = &key->as<WithEnvironmentObject>().object();
  }
  JSObject* lexicalEnv = nonSyntacticLexicalEnvironments_->lookup(key);
  if (!lexicalEnv) {
    return nullptr;
  }
  return &lexicalEnv->as<LexicalEnvironmentObject>();
}

bool Realm::addToVarNames(JSContext* cx, JS::Handle<JSAtom*> name) {
  MOZ_ASSERT(name);

  if (varNames_.put(name)) {
    return true;
  }

  ReportOutOfMemory(cx);
  return false;
}

void Realm::traceGlobal(JSTracer* trc) {
  // Trace things reachable from the realm's global. Note that these edges
  // must be swept too in case the realm is live but the global is not.

  TraceEdge(trc, &lexicalEnv_, "realm-global-lexical");

  savedStacks_.trace(trc);

  DebugAPI::traceFromRealm(trc, this);

  // Atoms are always tenured.
  if (!JS::RuntimeHeapIsMinorCollecting()) {
    varNames_.trace(trc);
  }
}

void ObjectRealm::trace(JSTracer* trc) {
  if (objectMetadataTable) {
    objectMetadataTable->trace(trc);
  }

  if (nonSyntacticLexicalEnvironments_) {
    nonSyntacticLexicalEnvironments_->trace(trc);
  }
}

void Realm::traceRoots(JSTracer* trc,
                       js::gc::GCRuntime::TraceOrMarkRuntime traceOrMark) {
  if (objectMetadataState_.is<PendingMetadata>()) {
    GCPolicy<NewObjectMetadataState>::trace(trc, &objectMetadataState_,
                                            "on-stack object pending metadata");
  }

  if (!JS::RuntimeHeapIsMinorCollecting()) {
    // The global is never nursery allocated, so we don't need to
    // trace it when doing a minor collection.
    //
    // If a realm is on-stack, we mark its global so that
    // JSContext::global() remains valid.
    if (shouldTraceGlobal() && global_.unbarrieredGet()) {
      TraceRoot(trc, global_.unsafeUnbarrieredForTracing(),
                "on-stack realm global");
    }
  }

  // Nothing below here needs to be treated as a root if we aren't marking
  // this zone for a collection.
  if (traceOrMark == js::gc::GCRuntime::MarkRuntime &&
      !zone()->isCollectingFromAnyThread()) {
    return;
  }

  /* Mark debug scopes, if present */
  if (debugEnvs_) {
    debugEnvs_->trace(trc);
  }

  objects_.trace(trc);
}

void ObjectRealm::finishRoots() {
  if (objectMetadataTable) {
    objectMetadataTable->clear();
  }

  if (nonSyntacticLexicalEnvironments_) {
    nonSyntacticLexicalEnvironments_->clear();
  }
}

void Realm::finishRoots() {
  if (debugEnvs_) {
    debugEnvs_->finish();
  }

  objects_.finishRoots();
}

void ObjectRealm::sweepAfterMinorGC() {
  InnerViewTable& table = innerViews.get();
  if (table.needsSweepAfterMinorGC()) {
    table.sweepAfterMinorGC();
  }
}

void Realm::sweepAfterMinorGC() {
  globalWriteBarriered = 0;
  dtoaCache.purge();
  objects_.sweepAfterMinorGC();
}

void Realm::traceWeakSavedStacks(JSTracer* trc) { savedStacks_.traceWeak(trc); }

void Realm::traceWeakObjects(JSTracer* trc) {
  if (global_) {
    TraceWeakEdge(trc, &global_, "Realm::global_");
  }
  if (lexicalEnv_) {
    TraceWeakEdge(trc, &lexicalEnv_, "Realm::lexicalEnv_");
  }
}

void Realm::traceWeakSelfHostingScriptSource(JSTracer* trc) {
  if (selfHostingScriptSource.unbarrieredGet()) {
    TraceWeakEdge(trc, &selfHostingScriptSource,
                  "Realm::selfHostingScriptSource");
  }
}

void Realm::traceWeakEdgesInJitRealm(JSTracer* trc) {
  if (jitRealm_) {
    jitRealm_->traceWeak(trc, this);
  }
}

void Realm::traceWeakRegExps(JSTracer* trc) {
  /*
   * JIT code increments activeWarmUpCounter for any RegExpShared used by jit
   * code for the lifetime of the JIT script. Thus, we must perform
   * sweeping after clearing jit code.
   */
  regExps.traceWeak(trc);
}

void Realm::sweepDebugEnvironments() {
  if (debugEnvs_) {
    debugEnvs_->sweep();
  }
}

void ObjectRealm::traceWeakNativeIterators(JSTracer* trc) {
  /* Sweep list of native iterators. */
  NativeIterator* ni = enumerators->next();
  while (ni != enumerators) {
    JSObject* iterObj = ni->iterObj();
    NativeIterator* next = ni->next();
    if (!TraceManuallyBarrieredWeakEdge(trc, &iterObj,
                                        "ObjectRealm::enumerators")) {
      ni->unlink();
    }
    MOZ_ASSERT_IF(ni->objectBeingIterated(),
                  &ObjectRealm::get(ni->objectBeingIterated()) == this);
    ni = next;
  }
}

void Realm::traceWeakObjectRealm(JSTracer* trc) {
  objects_.traceWeakNativeIterators(trc);
}

void Realm::tracekWeakVarNames(JSTracer* trc) { varNames_.traceWeak(trc); }

void Realm::traceWeakTemplateObjects(JSTracer* trc) {
  if (mappedArgumentsTemplate_) {
    TraceWeakEdge(trc, &mappedArgumentsTemplate_,
                  "Realm::mappedArgumentsTemplate_");
  }

  if (unmappedArgumentsTemplate_) {
    TraceWeakEdge(trc, &unmappedArgumentsTemplate_,
                  "Realm::unmappedArgumentsTemplate_");
  }

  if (iterResultTemplate_) {
    TraceWeakEdge(trc, &iterResultTemplate_, "Realm::iterResultTemplate_");
  }

  if (iterResultWithoutPrototypeTemplate_) {
    TraceWeakEdge(trc, &iterResultWithoutPrototypeTemplate_,
                  "Realm::iterResultWithoutPrototypeTemplate_");
  }
}

void Realm::fixupAfterMovingGC(JSTracer* trc) {
  purge();
  fixupGlobal();
  objectGroups_.fixupTablesAfterMovingGC();
}

void Realm::fixupGlobal() {
  GlobalObject* global = *global_.unsafeGet();
  if (global) {
    global_.set(MaybeForwarded(global));
  }
}

void Realm::purge() {
  dtoaCache.purge();
  newProxyCache.purge();
  objectGroups_.purge();
  objects_.iteratorCache.clearAndCompact();
  arraySpeciesLookup.purge();
  promiseLookup.purge();
}

void Realm::clearTables() {
  global_.set(nullptr);
  lexicalEnv_.set(nullptr);

  // No scripts should have run in this realm. This is used when merging
  // a realm that has been used off thread into another realm and zone.
  compartment()->assertNoCrossCompartmentWrappers();
  MOZ_ASSERT(!jitRealm_);
  MOZ_ASSERT(!debugEnvs_);
  MOZ_ASSERT(objects_.enumerators->next() == objects_.enumerators);

  objectGroups_.clearTables();
  savedStacks_.clear();
  varNames_.clear();
}

// Check to see if this individual realm is recording allocations. Debuggers or
// runtimes can try and record allocations, so this method can check to see if
// any initialization is needed.
bool Realm::isRecordingAllocations() { return !!allocationMetadataBuilder_; }

void Realm::setAllocationMetadataBuilder(
    const js::AllocationMetadataBuilder* builder) {
  // Clear any jitcode in the runtime, which behaves differently depending on
  // whether there is a creation callback.
  ReleaseAllJITCode(runtime_->defaultFreeOp());

  allocationMetadataBuilder_ = builder;
}

void Realm::forgetAllocationMetadataBuilder() {
  // Unlike setAllocationMetadataBuilder, we don't have to discard all JIT
  // code here (code is still valid, just a bit slower because it doesn't do
  // inline GC allocations when a metadata builder is present), but we do want
  // to cancel off-thread Ion compilations to avoid races when Ion calls
  // hasAllocationMetadataBuilder off-thread.
  CancelOffThreadIonCompile(this);

  allocationMetadataBuilder_ = nullptr;
}

void Realm::setNewObjectMetadata(JSContext* cx, HandleObject obj) {
  MOZ_ASSERT(obj->maybeCCWRealm() == this);
  cx->check(compartment(), obj);

  AutoEnterOOMUnsafeRegion oomUnsafe;
  if (JSObject* metadata =
          allocationMetadataBuilder_->build(cx, obj, oomUnsafe)) {
    MOZ_ASSERT(metadata->maybeCCWRealm() == obj->maybeCCWRealm());
    cx->check(metadata);

    if (!objects_.objectMetadataTable) {
      auto table = cx->make_unique<ObjectWeakMap>(cx);
      if (!table) {
        oomUnsafe.crash("setNewObjectMetadata");
      }

      objects_.objectMetadataTable = std::move(table);
    }

    if (!objects_.objectMetadataTable->add(cx, obj, metadata)) {
      oomUnsafe.crash("setNewObjectMetadata");
    }
  }
}

static bool AddInnerLazyFunctionsFromScript(
    JSScript* script, MutableHandleObjectVector lazyFunctions) {
  for (JS::GCCellPtr gcThing : script->gcthings()) {
    if (!gcThing.is<JSObject>()) {
      continue;
    }
    JSObject* obj = &gcThing.as<JSObject>();

    if (!obj->is<JSFunction>()) {
      continue;
    }
    JSFunction* fun = &obj->as<JSFunction>();

    if (!fun->hasBaseScript() || fun->hasBytecode()) {
      continue;
    }

    if (!lazyFunctions.append(obj)) {
      return false;
    }
  }
  return true;
}

static bool AddLazyFunctionsForRealm(JSContext* cx,
                                     MutableHandleObjectVector lazyFunctions,
                                     gc::AllocKind kind) {
  // Find all live root lazy functions in the realm: those which have a
  // non-lazy enclosing script, and which do not have an uncompiled enclosing
  // script. The last condition is so that we don't compile lazy scripts
  // whose enclosing scripts failed to compile, indicating that the lazy
  // script did not escape the script.

  for (auto i = cx->zone()->cellIter<JSObject>(kind); !i.done(); i.next()) {
    JSFunction* fun = &i->as<JSFunction>();

    // When iterating over the GC-heap, we may encounter function objects that
    // are incomplete (missing a BaseScript when we expect one). We must check
    // for this case before we can call JSFunction::hasBytecode().
    if (fun->isIncomplete()) {
      continue;
    }

    if (fun->realm() != cx->realm()) {
      continue;
    }

    if (!fun->hasBaseScript() || fun->hasBytecode()) {
      continue;
    }

    BaseScript* lazy = fun->baseScript();
    if (lazy->isReadyForDelazification()) {
      if (!lazyFunctions.append(fun)) {
        return false;
      }
    }
  }

  return true;
}

static bool CreateLazyScriptsForRealm(JSContext* cx) {
  RootedObjectVector lazyFunctions(cx);

  if (!AddLazyFunctionsForRealm(cx, &lazyFunctions, gc::AllocKind::FUNCTION)) {
    return false;
  }

  // Methods, for instance {get method() {}}, are extended functions that can
  // be relazified, so we need to handle those as well.
  if (!AddLazyFunctionsForRealm(cx, &lazyFunctions,
                                gc::AllocKind::FUNCTION_EXTENDED)) {
    return false;
  }

  // Create scripts for each lazy function, updating the list of functions to
  // process with any newly exposed inner functions in created scripts.
  // A function cannot be delazified until its outer script exists.
  RootedFunction fun(cx);
  for (size_t i = 0; i < lazyFunctions.length(); i++) {
    fun = &lazyFunctions[i]->as<JSFunction>();

    // lazyFunctions may have been populated with multiple functions for
    // a lazy script.
    if (!fun->isInterpreted() || fun->hasBytecode()) {
      continue;
    }

    JSScript* script = JSFunction::getOrCreateScript(cx, fun);
    if (!script) {
      return false;
    }
    if (!AddInnerLazyFunctionsFromScript(script, &lazyFunctions)) {
      return false;
    }
  }

  return true;
}

bool Realm::ensureDelazifyScriptsForDebugger(JSContext* cx) {
  AutoRealmUnchecked ar(cx, this);
  if (needsDelazificationForDebugger() && !CreateLazyScriptsForRealm(cx)) {
    return false;
  }
  debugModeBits_ &= ~DebuggerNeedsDelazification;
  return true;
}

void Realm::updateDebuggerObservesFlag(unsigned flag) {
  MOZ_ASSERT(isDebuggee());
  MOZ_ASSERT(flag == DebuggerObservesAllExecution ||
             flag == DebuggerObservesCoverage || flag == DebuggerObservesAsmJS);

  GlobalObject* global =
      zone()->runtimeFromMainThread()->gc.isForegroundSweeping()
          ? unsafeUnbarrieredMaybeGlobal()
          : maybeGlobal();
  bool observes = false;
  if (flag == DebuggerObservesAllExecution) {
    observes = DebugAPI::debuggerObservesAllExecution(global);
  } else if (flag == DebuggerObservesCoverage) {
    observes = DebugAPI::debuggerObservesCoverage(global);
  } else if (flag == DebuggerObservesAsmJS) {
    observes = DebugAPI::debuggerObservesAsmJS(global);
  }

  if (observes) {
    debugModeBits_ |= flag;
  } else {
    debugModeBits_ &= ~flag;
  }
}

void Realm::setIsDebuggee() {
  if (!isDebuggee()) {
    debugModeBits_ |= IsDebuggee;
    runtimeFromMainThread()->incrementNumDebuggeeRealms();
  }
}

void Realm::unsetIsDebuggee() {
  if (isDebuggee()) {
    if (debuggerObservesCoverage()) {
      runtime_->decrementNumDebuggeeRealmsObservingCoverage();
    }
    debugModeBits_ &= ~DebuggerObservesMask;
    DebugEnvironments::onRealmUnsetIsDebuggee(this);
    runtimeFromMainThread()->decrementNumDebuggeeRealms();
  }
}

void Realm::updateDebuggerObservesCoverage() {
  bool previousState = debuggerObservesCoverage();
  updateDebuggerObservesFlag(DebuggerObservesCoverage);
  if (previousState == debuggerObservesCoverage()) {
    return;
  }

  if (debuggerObservesCoverage()) {
    // Interrupt any running interpreter frame. The scriptCounts are
    // allocated on demand when a script resumes its execution.
    JSContext* cx = TlsContext.get();
    for (ActivationIterator iter(cx); !iter.done(); ++iter) {
      if (iter->isInterpreter()) {
        iter->asInterpreter()->enableInterruptsUnconditionally();
      }
    }
    runtime_->incrementNumDebuggeeRealmsObservingCoverage();
    return;
  }

  runtime_->decrementNumDebuggeeRealmsObservingCoverage();

  // If code coverage is enabled by any other means, keep it.
  if (collectCoverage()) {
    return;
  }

  clearScriptCounts();
  clearScriptLCov();
}

coverage::LCovRealm* Realm::lcovRealm() {
  if (!lcovRealm_) {
    lcovRealm_ = js::MakeUnique<coverage::LCovRealm>(this);
  }
  return lcovRealm_.get();
}

bool Realm::collectCoverage() const {
  return collectCoverageForPGO() || collectCoverageForDebug();
}

bool Realm::collectCoverageForPGO() const {
  return !jit::JitOptions.disablePgo;
}

bool Realm::collectCoverageForDebug() const {
  return debuggerObservesCoverage() || coverage::IsLCovEnabled();
}

void Realm::clearScriptCounts() { zone()->clearScriptCounts(this); }

void Realm::clearScriptLCov() { zone()->clearScriptLCov(this); }

void ObjectRealm::addSizeOfExcludingThis(
    mozilla::MallocSizeOf mallocSizeOf, size_t* innerViewsArg,
    size_t* objectMetadataTablesArg,
    size_t* nonSyntacticLexicalEnvironmentsArg) {
  *innerViewsArg += innerViews.sizeOfExcludingThis(mallocSizeOf);

  if (objectMetadataTable) {
    *objectMetadataTablesArg +=
        objectMetadataTable->sizeOfIncludingThis(mallocSizeOf);
  }

  if (auto& map = nonSyntacticLexicalEnvironments_) {
    *nonSyntacticLexicalEnvironmentsArg +=
        map->sizeOfIncludingThis(mallocSizeOf);
  }
}

void Realm::addSizeOfIncludingThis(
    mozilla::MallocSizeOf mallocSizeOf, size_t* tiAllocationSiteTables,
    size_t* tiArrayTypeTables, size_t* tiObjectTypeTables, size_t* realmObject,
    size_t* realmTables, size_t* innerViewsArg, size_t* objectMetadataTablesArg,
    size_t* savedStacksSet, size_t* varNamesSet,
    size_t* nonSyntacticLexicalEnvironmentsArg, size_t* jitRealm) {
  *realmObject += mallocSizeOf(this);
  objectGroups_.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables,
                                       tiArrayTypeTables, tiObjectTypeTables,
                                       realmTables);
  wasm.addSizeOfExcludingThis(mallocSizeOf, realmTables);

  objects_.addSizeOfExcludingThis(mallocSizeOf, innerViewsArg,
                                  objectMetadataTablesArg,
                                  nonSyntacticLexicalEnvironmentsArg);

  *savedStacksSet += savedStacks_.sizeOfExcludingThis(mallocSizeOf);
  *varNamesSet += varNames_.shallowSizeOfExcludingThis(mallocSizeOf);

  if (jitRealm_) {
    *jitRealm += jitRealm_->sizeOfIncludingThis(mallocSizeOf);
  }
}

mozilla::HashCodeScrambler Realm::randomHashCodeScrambler() {
  return mozilla::HashCodeScrambler(randomKeyGenerator_.next(),
                                    randomKeyGenerator_.next());
}

AutoSetNewObjectMetadata::AutoSetNewObjectMetadata(
    JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
    : cx_(cx->isHelperThreadContext() ? nullptr : cx),
      prevState_(cx, cx->realm()->objectMetadataState_) {
  MOZ_GUARD_OBJECT_NOTIFIER_INIT;
  if (cx_) {
    cx_->realm()->objectMetadataState_ =
        NewObjectMetadataState(DelayMetadata());
  }
}

AutoSetNewObjectMetadata::~AutoSetNewObjectMetadata() {
  // If we don't have a cx, we didn't change the metadata state, so no need to
  // reset it here.
  if (!cx_) {
    return;
  }

  if (!cx_->isExceptionPending() && cx_->realm()->hasObjectPendingMetadata()) {
    // This destructor often runs upon exit from a function that is
    // returning an unrooted pointer to a Cell. The allocation metadata
    // callback often allocates; if it causes a GC, then the Cell pointer
    // being returned won't be traced or relocated.
    //
    // The only extant callbacks are those internal to SpiderMonkey that
    // capture the JS stack. In fact, we're considering removing general
    // callbacks altogther in bug 1236748. Since it's not running arbitrary
    // code, it's adequate to simply suppress GC while we run the callback.
    gc::AutoSuppressGC autoSuppressGC(cx_);

    JSObject* obj = cx_->realm()->objectMetadataState_.as<PendingMetadata>();

    // Make sure to restore the previous state before setting the object's
    // metadata. SetNewObjectMetadata asserts that the state is not
    // PendingMetadata in order to ensure that metadata callbacks are called
    // in order.
    cx_->realm()->objectMetadataState_ = prevState_;

    obj = SetNewObjectMetadata(cx_, obj);
  } else {
    cx_->realm()->objectMetadataState_ = prevState_;
  }
}

JS_PUBLIC_API void gc::TraceRealm(JSTracer* trc, JS::Realm* realm,
                                  const char* name) {
  // The way GC works with compartments is basically incomprehensible.
  // For Realms, what we want is very simple: each Realm has a strong
  // reference to its GlobalObject, and vice versa.
  //
  // Here we simply trace our side of that edge. During GC,
  // GCRuntime::traceRuntimeCommon() marks all other realm roots, for
  // all realms.
  realm->traceGlobal(trc);
}

JS_PUBLIC_API bool gc::RealmNeedsSweep(JS::Realm* realm) {
  return realm->globalIsAboutToBeFinalized();
}

JS_PUBLIC_API JS::Realm* JS::GetCurrentRealmOrNull(JSContext* cx) {
  return cx->realm();
}

JS_PUBLIC_API JS::Realm* JS::GetObjectRealmOrNull(JSObject* obj) {
  return IsCrossCompartmentWrapper(obj) ? nullptr : obj->nonCCWRealm();
}

JS_PUBLIC_API void* JS::GetRealmPrivate(JS::Realm* realm) {
  return realm->realmPrivate();
}

JS_PUBLIC_API void JS::SetRealmPrivate(JS::Realm* realm, void* data) {
  realm->setRealmPrivate(data);
}

JS_PUBLIC_API void JS::SetDestroyRealmCallback(
    JSContext* cx, JS::DestroyRealmCallback callback) {
  cx->runtime()->destroyRealmCallback = callback;
}

JS_PUBLIC_API void JS::SetRealmNameCallback(JSContext* cx,
                                            JS::RealmNameCallback callback) {
  cx->runtime()->realmNameCallback = callback;
}

JS_PUBLIC_API JSObject* JS::GetRealmGlobalOrNull(Handle<JS::Realm*> realm) {
  return realm->maybeGlobal();
}

JS_PUBLIC_API bool JS::InitRealmStandardClasses(JSContext* cx) {
  MOZ_ASSERT(!cx->zone()->isAtomsZone());
  AssertHeapIsIdle();
  CHECK_THREAD(cx);
  return GlobalObject::initStandardClasses(cx, cx->global());
}

JS_PUBLIC_API JSObject* JS::GetRealmObjectPrototype(JSContext* cx) {
  CHECK_THREAD(cx);
  return GlobalObject::getOrCreateObjectPrototype(cx, cx->global());
}

JS_PUBLIC_API JSObject* JS::GetRealmFunctionPrototype(JSContext* cx) {
  CHECK_THREAD(cx);
  return GlobalObject::getOrCreateFunctionPrototype(cx, cx->global());
}

JS_PUBLIC_API JSObject* JS::GetRealmArrayPrototype(JSContext* cx) {
  CHECK_THREAD(cx);
  return GlobalObject::getOrCreateArrayPrototype(cx, cx->global());
}

JS_PUBLIC_API JSObject* JS::GetRealmErrorPrototype(JSContext* cx) {
  CHECK_THREAD(cx);
  return GlobalObject::getOrCreateCustomErrorPrototype(cx, cx->global(),
                                                       JSEXN_ERR);
}

JS_PUBLIC_API JSObject* JS::GetRealmIteratorPrototype(JSContext* cx) {
  CHECK_THREAD(cx);
  return GlobalObject::getOrCreateIteratorPrototype(cx, cx->global());
}

JS_PUBLIC_API Realm* JS::GetFunctionRealm(JSContext* cx, HandleObject objArg) {
  // https://tc39.github.io/ecma262/#sec-getfunctionrealm
  // 7.3.22 GetFunctionRealm ( obj )

  CHECK_THREAD(cx);
  cx->check(objArg);

  RootedObject obj(cx, objArg);
  while (true) {
    obj = CheckedUnwrapStatic(obj);
    if (!obj) {
      ReportAccessDenied(cx);
      return nullptr;
    }

    // Step 1.
    MOZ_ASSERT(IsCallable(obj));

    // Steps 2 and 3. We use a loop instead of recursion to unwrap bound
    // functions.
    if (obj->is<JSFunction>()) {
      JSFunction* fun = &obj->as<JSFunction>();
      if (!fun->isBoundFunction()) {
        return fun->realm();
      }

      obj = fun->getBoundFunctionTarget();
      continue;
    }

    // Step 4.
    if (IsScriptedProxy(obj)) {
      // Steps 4.a-b.
      JSObject* proxyTarget = GetProxyTargetObject(obj);
      if (!proxyTarget) {
        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                  JSMSG_PROXY_REVOKED);
        return nullptr;
      }

      // Step 4.c.
      obj = proxyTarget;
      continue;
    }

    // Step 5.
    return cx->realm();
  }
}
