summaryrefslogtreecommitdiffstats
path: root/js/src/vm/Realm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/vm/Realm.cpp')
-rw-r--r--js/src/vm/Realm.cpp841
1 files changed, 841 insertions, 0 deletions
diff --git a/js/src/vm/Realm.cpp b/js/src/vm/Realm.cpp
new file mode 100644
index 0000000000..cb08e90c0f
--- /dev/null
+++ b/js/src/vm/Realm.cpp
@@ -0,0 +1,841 @@
+/* -*- 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 "js/shadow/Realm.h" // JS::shadow::Realm
+#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 "jit/JitRuntime.h"
+#include "js/Date.h"
+#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
+#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_) {
+ TraceRoot(trc, global_.unbarrieredAddress(), "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_.unbarrieredGet();
+ if (global) {
+ global_.unbarrieredSet(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");
+ }
+ }
+}
+
+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_ = 0;
+ 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 (collectCoverageForDebug()) {
+ return;
+ }
+
+ clearScriptCounts();
+ clearScriptLCov();
+}
+
+coverage::LCovRealm* Realm::lcovRealm() {
+ if (!lcovRealm_) {
+ lcovRealm_ = js::MakeUnique<coverage::LCovRealm>(this);
+ }
+ return lcovRealm_.get();
+}
+
+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* 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, 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)
+ : cx_(cx->isHelperThreadContext() ? nullptr : cx),
+ prevState_(cx, cx->realm()->objectMetadataState_) {
+ 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(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();
+ }
+}