/* 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/. */

#ifndef vm_Realm_h
#define vm_Realm_h

#include "mozilla/Array.h"
#include "mozilla/Maybe.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/XorShift128PlusRNG.h"

#include <stddef.h>

#include "builtin/Array.h"
#include "ds/IdValuePair.h"
#include "gc/Barrier.h"
#include "gc/WeakMap.h"
#include "jit/BaselineCompileQueue.h"
#include "js/GCVariant.h"
#include "js/RealmOptions.h"
#include "js/TelemetryTimers.h"
#include "js/UniquePtr.h"
#include "util/LanguageId.h"
#include "vm/ArrayBufferObject.h"
#include "vm/GuardFuse.h"
#include "vm/InvalidatingFuse.h"
#include "vm/JSContext.h"
#include "vm/RealmFuses.h"
#include "vm/SavedStacks.h"
#include "wasm/WasmRealm.h"

namespace js {

namespace coverage {
class LCovRealm;
}  // namespace coverage

namespace jit {
class BaselineCompileQueue;
}  // namespace jit

class AutoRestoreRealmDebugMode;
class DateTimeInfo;
class Debugger;
class GlobalObject;
class GlobalObjectData;
class GlobalLexicalEnvironmentObject;
class NonSyntacticLexicalEnvironmentObject;
struct IdValuePair;
struct NativeIterator;

/*
 * A single-entry cache for some base-10 double-to-string conversions. This
 * helps date-format-xparb.js.  It also avoids skewing the results for
 * v8-splay.js when measured by the SunSpider harness, where the splay tree
 * initialization (which includes many repeated double-to-string conversions)
 * is erroneously included in the measurement; see bug 562553.
 */
class DtoaCache {
  double dbl = 0.0;
  int base = 0;
  JSLinearString* str;  // if str==nullptr, dbl and base are not valid

 public:
  DtoaCache() : str(nullptr) {}
  void purge() { str = nullptr; }

  JSLinearString* lookup(int b, double d) {
    return str && b == base && d == dbl ? str : nullptr;
  }

  void cache(int b, double d, JSLinearString* s) {
    base = b;
    dbl = d;
    str = s;
  }

#ifdef JSGC_HASH_TABLE_CHECKS
  void checkCacheAfterMovingGC();
#endif
};

// Cache to speed up the group/shape lookup in ProxyObject::create. A proxy's
// shape is only determined by the Class + proto, so a small cache for this is
// very effective in practice.
class NewProxyCache {
  struct Entry {
    Shape* shape;
  };
  static const size_t NumEntries = 4;
  mozilla::UniquePtr<Entry[], JS::FreePolicy> entries_;

 public:
  MOZ_ALWAYS_INLINE bool lookup(const JSClass* clasp, TaggedProto proto,
                                Shape** shape) const {
    if (!entries_) {
      return false;
    }
    for (size_t i = 0; i < NumEntries; i++) {
      const Entry& entry = entries_[i];
      if (entry.shape && entry.shape->getObjectClass() == clasp &&
          entry.shape->proto() == proto) {
        *shape = entry.shape;
        return true;
      }
    }
    return false;
  }
  void add(Shape* shape) {
    MOZ_ASSERT(shape);
    if (!entries_) {
      entries_.reset(js_pod_calloc<Entry>(NumEntries));
      if (!entries_) {
        return;
      }
    } else {
      for (size_t i = NumEntries - 1; i > 0; i--) {
        entries_[i] = entries_[i - 1];
      }
    }
    entries_[0].shape = shape;
  }
  void purge() { entries_.reset(); }
};

// Cache for NewPlainObjectWithProperties. When the list of properties matches
// a recently created object's shape, we can use this shape directly.
class NewPlainObjectWithPropsCache {
  static const size_t NumEntries = 4;
  mozilla::Array<SharedShape*, NumEntries> entries_;

 public:
  NewPlainObjectWithPropsCache() { purge(); }

  SharedShape* lookup(Handle<IdValueVector> properties) const;
  void add(SharedShape* shape);

  void purge() {
    for (size_t i = 0; i < NumEntries; i++) {
      entries_[i] = nullptr;
    }
  }
};

// Cache for Object.assign's fast path for two plain objects. It's used to
// optimize:
//
//   Object.assign(to, from)
//
// If the |to| object has shape |emptyToShape_| (shape with no properties) and
// the |from| object has shape |fromShape_|, we can use |newToShape_| for |to|
// and copy all (data)) properties from the |from| object.
//
// This is a one-entry cache for now. It has a hit rate of > 90% on both
// Speedometer 2 and Speedometer 3.
class MOZ_NON_TEMPORARY_CLASS PlainObjectAssignCache {
  SharedShape* emptyToShape_ = nullptr;
  SharedShape* fromShape_ = nullptr;
  SharedShape* newToShape_ = nullptr;

#ifdef DEBUG
  void assertValid() const;
#else
  void assertValid() const {}
#endif

 public:
  PlainObjectAssignCache() = default;
  PlainObjectAssignCache(const PlainObjectAssignCache&) = delete;
  void operator=(const PlainObjectAssignCache&) = delete;

  SharedShape* lookup(Shape* emptyToShape, Shape* fromShape) const {
    if (emptyToShape_ == emptyToShape && fromShape_ == fromShape) {
      assertValid();
      return newToShape_;
    }
    return nullptr;
  }
  void fill(SharedShape* emptyToShape, SharedShape* fromShape,
            SharedShape* newToShape) {
    emptyToShape_ = emptyToShape;
    fromShape_ = fromShape;
    newToShape_ = newToShape;
    assertValid();
  }
  void purge() {
    emptyToShape_ = nullptr;
    fromShape_ = nullptr;
    newToShape_ = nullptr;
  }
};

// [SMDOC] Object MetadataBuilder API
//
// We must ensure that all newly allocated JSObjects get their metadata
// set. However, metadata builders may require the new object be in a sane
// state (eg, have its reserved slots initialized so they can get the
// sizeOfExcludingThis of the object). Therefore, for objects of certain
// JSClasses (those marked with JSCLASS_DELAY_METADATA_BUILDER), it is not safe
// for the allocation paths to call the object metadata builder
// immediately. Instead, the JSClass-specific "constructor" C++ function up the
// stack makes a promise that it will ensure that the new object has its
// metadata set after the object is initialized.
//
// The js::AutoSetNewObjectMetadata RAII class provides an ergonomic way for
// constructor functions to do this.
//
// In the presence of internal errors, we do not set the new object's metadata
// (if it was even allocated).

class PropertyIteratorObject;

struct IteratorHashPolicy {
  struct Lookup {
    Shape* objShape;
    Shape** protoShapes;
    size_t numProtoShapes;
    HashNumber shapesHash;

    Lookup(Shape* objShape, Shape** protoShapes, size_t numProtoShapes,
           HashNumber shapesHash)
        : objShape(objShape),
          protoShapes(protoShapes),
          numProtoShapes(numProtoShapes),
          shapesHash(shapesHash) {
      MOZ_ASSERT(objShape);
    }
  };
  static HashNumber hash(const Lookup& lookup) { return lookup.shapesHash; }
  static bool match(PropertyIteratorObject* obj, const Lookup& lookup);
};

class DebugEnvironments;
class NonSyntacticVariablesObject;
class ScriptSourceObject;
class WithEnvironmentObject;

// ObjectRealm stores various tables and other state associated with particular
// objects in a realm. To make sure the correct ObjectRealm is used for an
// object, use of the ObjectRealm::get(obj) static method is required.
class ObjectRealm {
  // All non-syntactic lexical environments in the realm. These are kept in a
  // map because when loading scripts into a non-syntactic environment, we
  // need to use the same lexical environment to persist lexical bindings.
  using NonSyntacticLexialEnvironmentsMap =
      WeakMap<JSObject*, JSObject*, ZoneAllocPolicy>;
  js::UniquePtr<NonSyntacticLexialEnvironmentsMap>
      nonSyntacticLexicalEnvironments_;

 public:
  // Map from array buffers to views sharing that storage.
  JS::WeakCache<js::InnerViewTable> innerViews;

  using ModuleScriptSourceSet =
      JS::GCHashSet<js::WeakHeapPtr<ScriptSourceObject*>,
                    js::DefaultHasher<js::WeakHeapPtr<ScriptSourceObject*>>,
                    js::ZoneAllocPolicy>;
  JS::WeakCache<ModuleScriptSourceSet> moduleScriptSources;

  // Keep track of the metadata objects which can be associated with each JS
  // object. Both keys and values are in this realm.
  using ObjectMetadataTable = WeakMap<JSObject*, JSObject*, ZoneAllocPolicy>;
  js::UniquePtr<ObjectMetadataTable> objectMetadataTable;

  using IteratorCache =
      js::HashSet<js::PropertyIteratorObject*, js::IteratorHashPolicy,
                  js::ZoneAllocPolicy>;
  IteratorCache iteratorCache;

  static inline ObjectRealm& get(const JSObject* obj);

  explicit ObjectRealm(JS::Zone* zone);

  ObjectRealm(const ObjectRealm&) = delete;
  void operator=(const ObjectRealm&) = delete;

  void finishRoots();
  void trace(JSTracer* trc);
  void sweepAfterMinorGC(JSTracer* trc);

  void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
                              size_t* innerViewsArg,
                              size_t* objectMetadataTablesArg,
                              size_t* nonSyntacticLexicalEnvironmentsArg);

  NonSyntacticLexicalEnvironmentObject*
  getOrCreateNonSyntacticLexicalEnvironment(
      JSContext* cx, Handle<NonSyntacticVariablesObject*> enclosing);

  NonSyntacticLexicalEnvironmentObject*
  getOrCreateNonSyntacticLexicalEnvironment(
      JSContext* cx, Handle<WithEnvironmentObject*> enclosing);

  NonSyntacticLexicalEnvironmentObject*
  getOrCreateNonSyntacticLexicalEnvironment(
      JSContext* cx, Handle<WithEnvironmentObject*> enclosing,
      Handle<NonSyntacticVariablesObject*> key);

 private:
  NonSyntacticLexicalEnvironmentObject*
  getOrCreateNonSyntacticLexicalEnvironment(JSContext* cx,
                                            HandleObject enclosing,
                                            HandleObject key,
                                            HandleObject thisv);

 public:
  NonSyntacticLexicalEnvironmentObject* getNonSyntacticLexicalEnvironment(
      JSObject* key) const;
};

}  // namespace js

class JS::Realm : public JS::shadow::Realm {
  JS::Zone* zone_;
  JSRuntime* runtime_;

  const JS::RealmCreationOptions creationOptions_;
  JS::RealmBehaviors behaviors_;

  friend struct ::JSContext;
  js::WeakHeapPtr<js::GlobalObject*> global_;

  // Note: this is private to enforce use of ObjectRealm::get(obj).
  js::ObjectRealm objects_;
  friend js::ObjectRealm& js::ObjectRealm::get(const JSObject*);

  // See the "Object MetadataBuilder API" comment.
  JSObject* objectPendingMetadata_ = nullptr;
#ifdef DEBUG
  uint32_t numActiveAutoSetNewObjectMetadata_ = 0;
#endif

  // Random number generator for Math.random().
  mozilla::Maybe<mozilla::non_crypto::XorShift128PlusRNG>
      randomNumberGenerator_;

  // Random number generator for randomHashCodeScrambler().
  mozilla::non_crypto::XorShift128PlusRNG randomKeyGenerator_;

  JSPrincipals* principals_ = nullptr;

  js::jit::BaselineCompileQueue baselineCompileQueue_;

  // Bookkeeping information for debug scope objects.
  js::UniquePtr<js::DebugEnvironments> debugEnvs_;

  js::SavedStacks savedStacks_;

  // Used by memory reporters and invalid otherwise.
  JS::RealmStats* realmStats_ = nullptr;

  const js::AllocationMetadataBuilder* allocationMetadataBuilder_ = nullptr;
  void* realmPrivate_ = nullptr;

  // Default locale for realms with non-default locales.
  js::LanguageId localeId_ = js::LanguageId::und();

#if JS_HAS_INTL_API
  // Date-time info for realms with non-default time zones.
  js::UniquePtr<js::DateTimeInfo> dateTimeInfo_;
#endif

  // There are two ways to enter a realm:
  //
  // (1) AutoRealm (and JSAutoRealm, JS::EnterRealm)
  // (2) When calling a cross-realm (but same-compartment) function in JIT
  //     code.
  //
  // This field only accounts for (1), to keep the JIT code as simple as
  // possible.
  //
  // An important invariant is that the JIT can only switch to a different
  // realm within the same compartment, so whenever that happens there must
  // always be a same-compartment realm with enterRealmDepthIgnoringJit_ > 0.
  // This lets us set Compartment::hasEnteredRealm without walking the
  // stack.
  unsigned enterRealmDepthIgnoringJit_ = 0;

 public:
  // Various timers for collecting time spent delazifying, jit compiling,
  // executing, etc
  JS::JSTimers timers;

  struct DebuggerVectorEntry {
    // The debugger relies on iterating through the DebuggerVector to know what
    // debuggers to notify about certain actions, which it does using this
    // pointer. We need an explicit Debugger* because the JSObject* from
    // the DebuggerDebuggeeLink to the Debugger is only set some of the time.
    // This `Debugger*` pointer itself could also live on the
    // DebuggerDebuggeeLink itself, but that would then require all of the
    // places that iterate over the realm's DebuggerVector to also traverse
    // the CCW which seems like it would be needlessly complicated.
    js::WeakHeapPtr<js::Debugger*> dbg;

    // This links to the debugger's DebuggerDebuggeeLink object, via a CCW.
    // Tracing this link from the realm allows the debugger to define
    // whether pieces of the debugger should be held live by a given realm.
    js::HeapPtr<JSObject*> debuggerLink;

    DebuggerVectorEntry(js::Debugger* dbg_, JSObject* link);
  };
  using DebuggerVector =
      js::Vector<DebuggerVectorEntry, 0, js::ZoneAllocPolicy>;

 private:
  DebuggerVector debuggers_;

  enum {
    IsDebuggee = 1 << 0,
    DebuggerObservesAllExecution = 1 << 1,
    DebuggerObservesCoverage = 1 << 2,
    DebuggerObservesWasm = 1 << 3,
    DebuggerObservesNativeCall = 1 << 4,
  };
  uint32_t debugModeBits_ = 0;
  friend class js::AutoRestoreRealmDebugMode;

  bool isSystem_ = false;
  bool allocatedDuringIncrementalGC_;
  bool initializingGlobal_ = true;

  // Indicates that we are tracing all execution within this realm, i.e.,
  // recording every entrance into exit from each function, among other
  // things. See ExecutionTracer.h for where the bulk of this work
  // happens.
  bool isTracingExecution_ = false;

  js::UniquePtr<js::coverage::LCovRealm> lcovRealm_ = nullptr;

 public:
  // WebAssembly state for the realm.
  js::wasm::Realm wasm;

  js::DtoaCache dtoaCache;
  js::NewProxyCache newProxyCache;
  js::NewPlainObjectWithPropsCache newPlainObjectWithPropsCache;
  js::PlainObjectAssignCache plainObjectAssignCache;

  // Last time at which an animation was played for this realm.
  js::MainThreadData<mozilla::TimeStamp> lastAnimationTime;

  /*
   * For generational GC, record whether a write barrier has added this
   * realm's global to the store buffer since the last minor GC.
   *
   * This is used to avoid calling into the VM every time a nursery object is
   * written to a property of the global.
   */
  uint32_t globalWriteBarriered = 0;

  // Counter for shouldCaptureStackForThrow.
  uint16_t numStacksCapturedForThrow_ = 0;

  // Count the number of allocation sites pretenured, for testing purposes.
  uint16_t numAllocSitesPretenured = 0;

#ifdef DEBUG
  bool firedOnNewGlobalObject = false;
#endif

  // True if all incoming wrappers have been nuked. This happens when
  // NukeCrossCompartmentWrappers is called with the NukeAllReferences option.
  // This prevents us from creating new wrappers for the compartment.
  bool nukedIncomingWrappers = false;

  // Enable async stack capturing for this realm even if
  // JS::ContextOptions::asyncStackCaptureDebuggeeOnly_ is true.
  //
  // No-op when JS::ContextOptions::asyncStack_ is false, or
  // JS::ContextOptions::asyncStackCaptureDebuggeeOnly_ is false.
  //
  // This can be used as a lightweight alternative for making the global
  // debuggee, if the async stack capturing is necessary but no other debugging
  // features are used.
  bool isAsyncStackCapturingEnabled = false;

  // Allow to collect more than 50 stack traces for throw even if the global is
  // not a debuggee.
  //
  // Similarly to isAsyncStackCapturingEnabled, this is a lightweight
  // alternative for making the global a debuggee, when no actual debugging
  // features are required.
  bool isUnlimitedStacksCapturingEnabled = false;

 private:
  void updateDebuggerObservesFlag(unsigned flag);
  void restoreDebugModeBitsOnOOM(uint32_t bits);

 public:
  Realm(JS::Compartment* comp, const JS::RealmOptions& options);
  ~Realm();

  Realm(const Realm&) = delete;
  void operator=(const Realm&) = delete;

  void init(JSContext* cx, JSPrincipals* principals);
  void destroy(JS::GCContext* gcx);

  void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
                              size_t* realmObject, size_t* realmTables,
                              size_t* innerViewsArg,
                              size_t* objectMetadataTablesArg,
                              size_t* savedStacksSet,
                              size_t* nonSyntacticLexicalEnvironmentsArg);

  JS::Zone* zone() { return zone_; }
  const JS::Zone* zone() const { return zone_; }

  JSRuntime* runtimeFromMainThread() const {
    MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_));
    return runtime_;
  }

  // Note: Unrestricted access to the runtime from an arbitrary thread
  // can easily lead to races. Use this method very carefully.
  JSRuntime* runtimeFromAnyThread() const { return runtime_; }

  const JS::RealmCreationOptions& creationOptions() const {
    return creationOptions_;
  }

  // NOTE: Do not provide accessor for mutable reference.
  // Modifying RealmBehaviors after creating a realm can result in
  // inconsistency.
  const JS::RealmBehaviors& behaviors() const { return behaviors_; }

  void setNonLive() { behaviors_.setNonLive(); }
  void setReduceTimerPrecisionCallerType(JS::RTPCallerTypeToken type) {
    behaviors_.setReduceTimerPrecisionCallerType(type);
  }

  /* Whether to preserve JIT code on non-shrinking GCs. */
  bool preserveJitCode() { return creationOptions_.preserveJitCode(); }

  /* The global object for this realm.
   *
   * Note: the global_ field is null briefly during GC, after the global
   * object is collected; but when that happens the Realm is destroyed during
   * the same GC.)
   *
   * In contrast, JSObject::global() is infallible because marking a JSObject
   * always marks its global as well.
   */
  inline js::GlobalObject* maybeGlobal() const;

  /* An unbarriered getter for use while tracing. */
  js::GlobalObject* unsafeUnbarrieredMaybeGlobal() const {
    return global_.unbarrieredGet();
  }

  /* True if a global exists and it's not being collected. */
  inline bool hasLiveGlobal() const;

  /* True if a global exists and has been successfully initialized. */
  inline bool hasInitializedGlobal() const;

  inline void initGlobal(js::GlobalObject& global);
  void clearInitializingGlobal() { initializingGlobal_ = false; }

  /*
   * This method traces data that is live iff we know that this realm's
   * global is still live.
   */
  void traceGlobalData(JSTracer* trc);

  void traceGlobalRoot(JSTracer* trc, const char* name);

  void traceWeakGlobalEdge(JSTracer* trc);

  /*
   * This method traces Realm-owned GC roots that are considered live
   * regardless of whether the realm's global is still live.
   */
  void traceRoots(JSTracer* trc,
                  js::gc::GCRuntime::TraceOrMarkRuntime traceOrMark);
  /*
   * This method clears out tables of roots in preparation for the final GC.
   */
  void finishRoots();

  void sweepAfterMinorGC(JSTracer* trc);
  void traceWeakDebugEnvironmentEdges(JSTracer* trc);

  void clearScriptCounts();
  void clearScriptLCov();

  void purge();

  void fixupAfterMovingGC(JSTracer* trc);

  void enter() { enterRealmDepthIgnoringJit_++; }
  void leave() {
    MOZ_ASSERT(enterRealmDepthIgnoringJit_ > 0);
    enterRealmDepthIgnoringJit_--;
  }
  bool hasBeenEnteredIgnoringJit() const {
    return enterRealmDepthIgnoringJit_ > 0;
  }
  bool shouldTraceGlobal() const {
    // If we entered this realm in JIT code, there must be a script and
    // function on the stack for this realm, so the global will definitely
    // be traced and it's safe to return false here.
    return hasBeenEnteredIgnoringJit();
  }

  bool hasAllocationMetadataBuilder() const {
    return allocationMetadataBuilder_;
  }
  const js::AllocationMetadataBuilder* getAllocationMetadataBuilder() const {
    return allocationMetadataBuilder_;
  }
  const void* addressOfMetadataBuilder() const {
    return &allocationMetadataBuilder_;
  }
  bool isRecordingAllocations();
  void setAllocationMetadataBuilder(
      const js::AllocationMetadataBuilder* builder);
  void forgetAllocationMetadataBuilder();
  void setNewObjectMetadata(JSContext* cx, JS::HandleObject obj);

  bool hasObjectPendingMetadata() const {
    MOZ_ASSERT_IF(objectPendingMetadata_, hasAllocationMetadataBuilder());
    return objectPendingMetadata_ != nullptr;
  }
  void setObjectPendingMetadata(JSObject* obj) {
    MOZ_ASSERT(numActiveAutoSetNewObjectMetadata_ > 0,
               "Must not use JSCLASS_DELAY_METADATA_BUILDER without "
               "AutoSetNewObjectMetadata");
    MOZ_ASSERT(!objectPendingMetadata_);
    MOZ_ASSERT(obj);
    if (MOZ_UNLIKELY(hasAllocationMetadataBuilder())) {
      objectPendingMetadata_ = obj;
    }
  }
  JSObject* getAndClearObjectPendingMetadata() {
    MOZ_ASSERT(hasAllocationMetadataBuilder());
    JSObject* obj = objectPendingMetadata_;
    objectPendingMetadata_ = nullptr;
    return obj;
  }

#ifdef DEBUG
  bool hasActiveAutoSetNewObjectMetadata() const {
    return numActiveAutoSetNewObjectMetadata_ > 0;
  }
  void incNumActiveAutoSetNewObjectMetadata() {
    numActiveAutoSetNewObjectMetadata_++;
  }
  void decNumActiveAutoSetNewObjectMetadata() {
    MOZ_ASSERT(numActiveAutoSetNewObjectMetadata_ > 0);
    numActiveAutoSetNewObjectMetadata_--;
  }
#endif

  void* realmPrivate() const { return realmPrivate_; }
  void setRealmPrivate(void* p) { realmPrivate_ = p; }

  // This should only be called when it is non-null, i.e. during memory
  // reporting.
  JS::RealmStats& realmStats() {
    // We use MOZ_RELEASE_ASSERT here because in bug 1132502 there was some
    // (inconclusive) evidence that realmStats_ can be nullptr unexpectedly.
    MOZ_RELEASE_ASSERT(realmStats_);
    return *realmStats_;
  }
  void nullRealmStats() {
    MOZ_ASSERT(realmStats_);
    realmStats_ = nullptr;
  }
  void setRealmStats(JS::RealmStats* newStats) {
    MOZ_ASSERT(!realmStats_ && newStats);
    realmStats_ = newStats;
  }

  inline bool marked() const;
  void clearAllocatedDuringGC() { allocatedDuringIncrementalGC_ = false; }

  /*
   * The principals associated with this realm. Note that the same several
   * realms may share the same principals and that a realm may change
   * principals during its lifetime (e.g. in case of lazy parsing).
   */
  JSPrincipals* principals() { return principals_; }
  void setPrincipals(JSPrincipals* principals) { principals_ = principals; }

  bool isSystem() const { return isSystem_; }
  //
  // The Debugger observes execution on a frame-by-frame basis. The
  // invariants of Realm's debug mode bits, JSScript::isDebuggee,
  // InterpreterFrame::isDebuggee, and BaselineFrame::isDebuggee are
  // enumerated below.
  //
  // 1. When a realm's isDebuggee() == true, relazification and lazy
  //    parsing are disabled.
  //
  //    Whether AOT wasm is disabled is togglable by the Debugger API. By
  //    default it is disabled. See debuggerObservesWasm below.
  //
  // 2. When a realm's debuggerObservesAllExecution() == true, all of
  //    the realm's scripts are considered debuggee scripts.
  //
  // 3. A script is considered a debuggee script either when, per above, its
  //    realm is observing all execution, or if it has breakpoints set.
  //
  // 4. A debuggee script always pushes a debuggee frame.
  //
  // 5. A debuggee frame calls all slow path Debugger hooks in the
  //    Interpreter and Baseline. A debuggee frame implies that its script's
  //    BaselineScript, if extant, has been compiled with debug hook calls.
  //
  // 6. A debuggee script or a debuggee frame (i.e., during OSR) ensures
  //    that the compiled BaselineScript is compiled with debug hook calls
  //    when attempting to enter Baseline.
  //
  // 7. A debuggee script or a debuggee frame (i.e., during OSR) does not
  //    attempt to enter Ion.
  //
  // Note that a debuggee frame may exist without its script being a
  // debuggee script. e.g., Debugger.Frame.prototype.eval only marks the
  // frame in which it is evaluating as a debuggee frame.
  //

  // True if this realm's global is a debuggee of some Debugger
  // object.
  bool isDebuggee() const { return !!(debugModeBits_ & IsDebuggee); }

  void setIsDebuggee();
  void unsetIsDebuggee();

  bool isTracingExecution() { return isTracingExecution_; }

  void enableExecutionTracing() {
    MOZ_ASSERT(!debuggerObservesCoverage());

    isTracingExecution_ = true;
    setIsDebuggee();
    updateDebuggerObservesAllExecution();
  }

  void disableExecutionTracing() {
    if (!isTracingExecution_) {
      return;
    }

    isTracingExecution_ = false;
    // updateDebuggerObservesAllExecution always wants isDebuggee to be true,
    // so we just have weird ordering here to play nicely with it
    updateDebuggerObservesAllExecution();
    if (!hasDebuggers()) {
      unsetIsDebuggee();
    }
  }

  DebuggerVector& getDebuggers(const JS::AutoRequireNoGC& nogc) {
    return debuggers_;
  };
  bool hasDebuggers() const { return !debuggers_.empty(); }

  // True if this compartment's global is a debuggee of some Debugger
  // object with a live hook that observes all execution; e.g.,
  // onEnterFrame.
  bool debuggerObservesAllExecution() const {
    static const unsigned Mask = IsDebuggee | DebuggerObservesAllExecution;
    return (debugModeBits_ & Mask) == Mask;
  }
  void updateDebuggerObservesAllExecution() {
    updateDebuggerObservesFlag(DebuggerObservesAllExecution);
  }

  // True if this realm's global is a debuggee of some Debugger object
  // whose allowUnobservedWasm flag is false.
  //
  // Note that since AOT wasm functions cannot bail out, this flag really
  // means "observe wasm from this point forward". We cannot make
  // already-compiled wasm code observable to Debugger.
  bool debuggerObservesWasm() const {
    static const unsigned Mask = IsDebuggee | DebuggerObservesWasm;
    return (debugModeBits_ & Mask) == Mask;
  }
  void updateDebuggerObservesWasm() {
    updateDebuggerObservesFlag(DebuggerObservesWasm);
  }

  // True if this compartment's global is a debuggee of some Debugger
  // object with a live hook that observes native calls.
  // (has a onNativeCall function registered)
  bool debuggerObservesNativeCall() const {
    static const unsigned Mask = IsDebuggee | DebuggerObservesNativeCall;
    return (debugModeBits_ & Mask) == Mask;
  }
  void updateDebuggerObservesNativeCall() {
    updateDebuggerObservesFlag(DebuggerObservesNativeCall);
  }

  // True if this realm's global is a debuggee of some Debugger object
  // whose collectCoverageInfo flag is true.
  bool debuggerObservesCoverage() const {
    static const unsigned Mask = DebuggerObservesCoverage;
    return (debugModeBits_ & Mask) == Mask;
  }
  void updateDebuggerObservesCoverage();

  // Returns true if the Debugger API is collecting code coverage data for this
  // realm or if the process-wide LCov option is enabled.
  bool collectCoverageForDebug() const;

  // Get or allocate the associated LCovRealm.
  js::coverage::LCovRealm* lcovRealm();

  bool shouldCaptureStackForThrow();

  // Returns the locale for this realm.
  //
  // The returned locale is canonicalized, but not necessarily an available
  // locale for the ECMA-402 Intl API. `intl::GlobalIntlData::defaultLocale()`
  // returns the *actual* default locale used for `Intl` objects.
  js::LanguageId getLocale();

  // Set the locale for this realm. Reset to the system default locale when the
  // input is |nullptr|.
  void setLocaleOverride(const char* locale);

  // Returns the date-time info for this realm. Returns nullptr unless a time
  // zone override was specified in the realm creation options.
  js::DateTimeInfo* getDateTimeInfo();

  // Set the time zone for this realm. Reset to the system default time zone
  // when the input is |nullptr|.
  void setTimeZoneOverride(const char* timeZone);

  // Initializes randomNumberGenerator if needed.
  mozilla::non_crypto::XorShift128PlusRNG& getOrCreateRandomNumberGenerator();

  const mozilla::non_crypto::XorShift128PlusRNG*
  addressOfRandomNumberGenerator() const {
    return randomNumberGenerator_.ptr();
  }

  mozilla::HashCodeScrambler randomHashCodeScrambler();

  js::jit::BaselineCompileQueue& baselineCompileQueue() {
    return baselineCompileQueue_;
  }
  static constexpr size_t offsetOfBaselineCompileQueue() {
    return offsetof(Realm, baselineCompileQueue_);
  }
  void removeFromCompileQueue(JSScript* script);

  js::DebugEnvironments* debugEnvs() { return debugEnvs_.get(); }
  js::UniquePtr<js::DebugEnvironments>& debugEnvsRef() { return debugEnvs_; }

  js::SavedStacks& savedStacks() { return savedStacks_; }

  // Recompute the probability with which this realm should record
  // profiling data (stack traces, allocations log, etc.) about each
  // allocation. We first consult the JS runtime to see if it is recording
  // allocations, and if not then check the probabilities requested by the
  // Debugger instances observing us, if any.
  void chooseAllocationSamplingProbability() {
    savedStacks_.chooseSamplingProbability(this);
  }

  void traceWeakSavedStacks(JSTracer* trc);

  static constexpr size_t offsetOfCompartment() {
    return offsetof(JS::Realm, compartment_);
  }
  static constexpr size_t offsetOfAllocationMetadataBuilder() {
    return offsetof(JS::Realm, allocationMetadataBuilder_);
  }
  static constexpr size_t offsetOfDebugModeBits() {
    return offsetof(JS::Realm, debugModeBits_);
  }
  static constexpr uint32_t debugModeIsDebuggeeBit() { return IsDebuggee; }

  // Note: similar to cx->global(), JIT code can omit the read barrier for the
  // context's active global.
  static constexpr size_t offsetOfActiveGlobal() {
    static_assert(sizeof(global_) == sizeof(uintptr_t),
                  "JIT code assumes field is pointer-sized");
    return offsetof(JS::Realm, global_);
  }

  js::RealmFuses realmFuses;

  // Allocation site used by binding code to provide feedback
  // on allocation heap for DOM allocation functions.
  //
  // See  CallIRGenerator::tryAttachCallNative
  js::gc::AllocSite* localAllocSite = nullptr;

  static size_t offsetOfLocalAllocSite() {
    return offsetof(JS::Realm, localAllocSite);
  }
};

inline js::Handle<js::GlobalObject*> JSContext::global() const {
  /*
   * It's safe to use |unbarrieredGet()| here because any realm that is on-stack
   * will be marked automatically, so there's no need for a read barrier on
   * it. Once the realm is popped, the handle is no longer safe to use.
   */
  MOZ_ASSERT(realm_, "Caller needs to enter a realm first");
  return js::Handle<js::GlobalObject*>::fromMarkedLocation(
      realm_->global_.unbarrieredAddress());
}

namespace js {

class MOZ_RAII AssertRealmUnchanged {
 public:
  explicit AssertRealmUnchanged(JSContext* cx)
      : cx(cx), oldRealm(cx->realm()) {}

  ~AssertRealmUnchanged() { MOZ_ASSERT(cx->realm() == oldRealm); }

 protected:
  JSContext* const cx;
  JS::Realm* const oldRealm;
};

// AutoRealm can be used to enter the realm of a JSObject or JSScript. It must
// not be used with cross-compartment wrappers, because CCWs are not associated
// with a single realm.
class AutoRealm {
  JSContext* const cx_;
  JS::Realm* const origin_;

 public:
  template <typename T>
  inline AutoRealm(JSContext* cx, const T& target);
  inline ~AutoRealm();

  AutoRealm(const AutoRealm&) = delete;
  AutoRealm& operator=(const AutoRealm&) = delete;

  JSContext* context() const { return cx_; }
  JS::Realm* origin() const { return origin_; }

 protected:
  inline AutoRealm(JSContext* cx, JS::Realm* target);
};

class MOZ_RAII AutoAllocInAtomsZone {
  JSContext* const cx_;
  JS::Realm* const origin_;

 public:
  inline explicit AutoAllocInAtomsZone(JSContext* cx);
  inline ~AutoAllocInAtomsZone();
  AutoAllocInAtomsZone(const AutoAllocInAtomsZone&) = delete;
  AutoAllocInAtomsZone& operator=(const AutoAllocInAtomsZone&) = delete;
};

// During GC we sometimes need to enter a realm when we may have been allocating
// in the the atoms zone. This leaves the atoms zone temporarily.
class MOZ_RAII AutoMaybeLeaveAtomsZone {
  JSContext* const cx_;
  bool wasInAtomsZone_;

 public:
  inline explicit AutoMaybeLeaveAtomsZone(JSContext* cx);
  inline ~AutoMaybeLeaveAtomsZone();
  AutoMaybeLeaveAtomsZone(const AutoMaybeLeaveAtomsZone&) = delete;
  AutoMaybeLeaveAtomsZone& operator=(const AutoMaybeLeaveAtomsZone&) = delete;
};

// Enter a realm directly. Only use this where there's no target GC thing
// to pass to AutoRealm or where you need to avoid the assertions in
// JS::Compartment::enterCompartmentOf().
class AutoRealmUnchecked : protected AutoRealm {
 public:
  inline AutoRealmUnchecked(JSContext* cx, JS::Realm* target);
};

// Similar to AutoRealm, but this uses GetFunctionRealm in the spec, and
// handles both bound functions and proxies.
//
// If GetFunctionRealm fails for the following reasons, this does nothing:
//   * `fun` is revoked proxy
//   * unwrapping failed because of a security wrapper
class AutoFunctionOrCurrentRealm {
  mozilla::Maybe<AutoRealmUnchecked> ar_;

 public:
  inline AutoFunctionOrCurrentRealm(JSContext* cx, js::HandleObject fun);
  ~AutoFunctionOrCurrentRealm() = default;

  AutoFunctionOrCurrentRealm(const AutoFunctionOrCurrentRealm&) = delete;
  AutoFunctionOrCurrentRealm& operator=(const AutoFunctionOrCurrentRealm&) =
      delete;
};

/*
 * Use this to change the behavior of an AutoRealm slightly on error. If
 * the exception happens to be an Error object, copy it to the origin
 * compartment instead of wrapping it.
 */
class ErrorCopier {
  mozilla::Maybe<AutoRealm>& ar;

 public:
  explicit ErrorCopier(mozilla::Maybe<AutoRealm>& ar) : ar(ar) {}
  ~ErrorCopier();
};

// See the "Object MetadataBuilder API" comment.
class MOZ_RAII AutoSetNewObjectMetadata {
  JSContext* cx_;

  void setPendingMetadata();

 public:
  explicit inline AutoSetNewObjectMetadata(JSContext* cx) : cx_(cx) {
#ifdef DEBUG
    MOZ_ASSERT(!cx->realm()->hasObjectPendingMetadata());
    cx_->realm()->incNumActiveAutoSetNewObjectMetadata();
#endif
  }
  inline ~AutoSetNewObjectMetadata() {
#ifdef DEBUG
    cx_->realm()->decNumActiveAutoSetNewObjectMetadata();
#endif
    if (MOZ_UNLIKELY(cx_->realm()->hasAllocationMetadataBuilder())) {
      setPendingMetadata();
    }
  }

  AutoSetNewObjectMetadata(const AutoSetNewObjectMetadata& aOther) = delete;
  void operator=(const AutoSetNewObjectMetadata& aOther) = delete;
};

} /* namespace js */

#endif /* vm_Realm_h */
