summaryrefslogtreecommitdiffstats
path: root/js/src/vm/Realm.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /js/src/vm/Realm.cpp
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/vm/Realm.cpp')
-rw-r--r--js/src/vm/Realm.cpp774
1 files changed, 774 insertions, 0 deletions
diff --git a/js/src/vm/Realm.cpp b/js/src/vm/Realm.cpp
new file mode 100644
index 0000000000..f27ddc8f06
--- /dev/null
+++ b/js/src/vm/Realm.cpp
@@ -0,0 +1,774 @@
+/* -*- 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 "builtin/WrappedFunctionObject.h"
+#include "debugger/DebugAPI.h"
+#include "debugger/Debugger.h"
+#include "gc/GC.h"
+#include "jit/JitRealm.h"
+#include "jit/JitRuntime.h"
+#include "js/CallAndConstruct.h" // JS::IsCallable
+#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
+#include "js/GCVariant.h"
+#include "js/Proxy.h"
+#include "js/RootingAPI.h"
+#include "js/Wrapper.h"
+#include "vm/Compartment.h"
+#include "vm/DateTime.h"
+#include "vm/Iteration.h"
+#include "vm/JSContext.h"
+#include "vm/PIC.h"
+
+#include "gc/Marking-inl.h"
+#include "vm/JSObject-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) {}
+
+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_),
+ randomKeyGenerator_(runtime_->forkRandomKeyGenerator()),
+ debuggers_(zone_),
+ allocatedDuringIncrementalGC_(zone_->isGCMarkingOrSweeping() ||
+ zone_->isGCFinished()),
+ wasm(runtime_) {
+ 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--;
+}
+
+void 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 (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;
+ }
+}
+
+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;
+ }
+
+ jitRealm->initialize(zone()->allocNurseryStrings());
+
+ jitRealm_ = std::move(jitRealm);
+ return true;
+}
+
+#ifdef JSGC_HASH_TABLE_CHECKS
+
+void js::DtoaCache::checkCacheAfterMovingGC() {
+ MOZ_ASSERT(!str || !IsForwarded(str));
+}
+
+#endif // JSGC_HASH_TABLE_CHECKS
+
+NonSyntacticLexicalEnvironmentObject*
+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 =
+ NonSyntacticLexicalEnvironmentObject::create(cx, enclosing, thisv);
+ if (!lexicalEnv) {
+ return nullptr;
+ }
+ if (!nonSyntacticLexicalEnvironments_->add(cx, key, lexicalEnv)) {
+ return nullptr;
+ }
+ }
+
+ return &lexicalEnv->as<NonSyntacticLexicalEnvironmentObject>();
+}
+
+NonSyntacticLexicalEnvironmentObject*
+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);
+}
+
+NonSyntacticLexicalEnvironmentObject*
+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<NonSyntacticLexicalEnvironmentObject>();
+}
+
+void Realm::traceGlobalData(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.
+
+ savedStacks_.trace(trc);
+
+ DebugAPI::traceFromRealm(trc, this);
+}
+
+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) {
+ // It's not possible to trigger a GC between allocating the pending object and
+ // setting its meta data in ~AutoSetNewObjectMetadata.
+ MOZ_RELEASE_ASSERT(!objectPendingMetadata_);
+
+ 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(JSTracer* trc) {
+ InnerViewTable& table = innerViews.get();
+ if (table.needsSweepAfterMinorGC()) {
+ table.sweepAfterMinorGC(trc);
+ }
+}
+
+void Realm::sweepAfterMinorGC(JSTracer* trc) {
+ globalWriteBarriered = 0;
+ dtoaCache.purge();
+ objects_.sweepAfterMinorGC(trc);
+}
+
+void Realm::traceWeakSavedStacks(JSTracer* trc) { savedStacks_.traceWeak(trc); }
+
+void Realm::traceWeakGlobalEdge(JSTracer* trc) {
+ // If the global is dead, free its GlobalObjectData.
+ auto result = TraceWeakEdge(trc, &global_, "Realm::global_");
+ if (result.isDead()) {
+ result.initialTarget()->releaseData(runtime_->gcContext());
+ }
+}
+
+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::traceWeakDebugEnvironmentEdges(JSTracer* trc) {
+ if (debugEnvs_) {
+ debugEnvs_->traceWeak(trc);
+ }
+}
+
+void Realm::fixupAfterMovingGC(JSTracer* trc) {
+ purge();
+ traceWeakGlobalEdge(trc);
+}
+
+void Realm::purge() {
+ dtoaCache.purge();
+ newProxyCache.purge();
+ newPlainObjectWithPropsCache.purge();
+ objects_.iteratorCache.clearAndCompact();
+ arraySpeciesLookup.purge();
+ promiseLookup.purge();
+
+ if (zone()->isGCPreparing()) {
+ purgeForOfPicChain();
+ }
+}
+
+void Realm::purgeForOfPicChain() {
+ if (GlobalObject* global = global_.unbarrieredGet()) {
+ if (NativeObject* object = global->getForOfPICObject()) {
+ ForOfPIC::Chain* chain = ForOfPIC::fromJSObject(object);
+ chain->freeAllStubs(runtime_->gcContext());
+ }
+ }
+}
+
+// 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_->gcContext());
+
+ 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 || flag == DebuggerObservesWasm);
+
+ 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);
+ } else if (flag == DebuggerObservesWasm) {
+ observes = DebugAPI::debuggerObservesWasm(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* nonSyntacticLexicalEnvironmentsArg,
+ size_t* jitRealm) {
+ *realmObject += mallocSizeOf(this);
+ wasm.addSizeOfExcludingThis(mallocSizeOf, realmTables);
+
+ objects_.addSizeOfExcludingThis(mallocSizeOf, innerViewsArg,
+ objectMetadataTablesArg,
+ nonSyntacticLexicalEnvironmentsArg);
+
+ *savedStacksSet += savedStacks_.sizeOfExcludingThis(mallocSizeOf);
+
+ if (jitRealm_) {
+ *jitRealm += jitRealm_->sizeOfIncludingThis(mallocSizeOf);
+ }
+}
+
+bool Realm::shouldCaptureStackForThrow() {
+ // Determine whether a stack trace should be captured for throw-statements (or
+ // similar) in JS code in this realm. We don't want to do this unconditionally
+ // because capturing stacks is slow and some scripts throw a lot of
+ // exceptions.
+ //
+ // Note: this is unrelated to Error.stack! That property is observable from
+ // JS code so we can't use these heuristics there. The code here is mostly
+ // relevant for uncaught exceptions that are not Error objects.
+
+ // To match other browsers, we always capture a stack trace if the realm is a
+ // debuggee (this includes the devtools console being open) or if unlimited
+ // stack traces have been enabled for this realm (used in automation).
+ if (isDebuggee() || isUnlimitedStacksCapturingEnabled) {
+ return true;
+ }
+
+ // Also always capture for chrome code. This is code we control and this helps
+ // debugging.
+ if (principals() &&
+ principals() == runtimeFromMainThread()->trustedPrincipals()) {
+ return true;
+ }
+
+ // Else, capture the stack only for the first N exceptions so that we can
+ // still show stack traces for scripts that don't throw a lot of exceptions
+ // (if the console is opened later).
+ static constexpr uint16_t MaxStacksCapturedForThrow = 50;
+ if (numStacksCapturedForThrow_ > MaxStacksCapturedForThrow) {
+ return false;
+ }
+ numStacksCapturedForThrow_++;
+ return true;
+}
+
+mozilla::HashCodeScrambler Realm::randomHashCodeScrambler() {
+ return mozilla::HashCodeScrambler(randomKeyGenerator_.next(),
+ randomKeyGenerator_.next());
+}
+
+void AutoSetNewObjectMetadata::setPendingMetadata() {
+ JSObject* obj = cx_->realm()->getAndClearObjectPendingMetadata();
+ if (!obj) {
+ return;
+ }
+
+ MOZ_ASSERT(obj->getClass()->shouldDelayMetadataBuilder());
+
+ if (cx_->isExceptionPending()) {
+ return;
+ }
+
+ // This function is called from a destructor that 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_);
+
+ (void)SetNewObjectMetadata(cx_, obj);
+}
+
+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->traceGlobalData(trc);
+}
+
+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 bool JS::MaybeFreezeCtorAndPrototype(JSContext* cx,
+ HandleObject ctor,
+ HandleObject maybeProto) {
+ if (MOZ_LIKELY(!cx->realm()->creationOptions().freezeBuiltins())) {
+ return true;
+ }
+ if (!SetIntegrityLevel(cx, ctor, IntegrityLevel::Frozen)) {
+ return false;
+ }
+ if (maybeProto) {
+ if (!SetIntegrityLevel(cx, maybeProto, IntegrityLevel::Sealed)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+JS_PUBLIC_API JSObject* JS::GetRealmObjectPrototype(JSContext* cx) {
+ CHECK_THREAD(cx);
+ return &cx->global()->getObjectPrototype();
+}
+
+JS_PUBLIC_API JS::Handle<JSObject*> JS::GetRealmObjectPrototypeHandle(
+ JSContext* cx) {
+ return cx->global()->getObjectPrototypeHandle();
+}
+
+JS_PUBLIC_API JSObject* JS::GetRealmFunctionPrototype(JSContext* cx) {
+ CHECK_THREAD(cx);
+ return &cx->global()->getFunctionPrototype();
+}
+
+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 JSObject* JS::GetRealmAsyncIteratorPrototype(JSContext* cx) {
+ CHECK_THREAD(cx);
+ return GlobalObject::getOrCreateAsyncIteratorPrototype(cx, cx->global());
+}
+
+JS_PUBLIC_API JSObject* JS::GetRealmKeyObject(JSContext* cx) {
+ return GlobalObject::getOrCreateRealmKeyObject(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>()) {
+ return obj->as<JSFunction>().realm();
+ }
+ if (obj->is<BoundFunctionObject>()) {
+ obj = obj->as<BoundFunctionObject>().getTarget();
+ continue;
+ }
+
+ // WrappedFunctionObjects also have a [[Realm]] internal slot,
+ // which is the nonCCWRealm by construction.
+ if (obj->is<WrappedFunctionObject>()) {
+ return obj->nonCCWRealm();
+ }
+
+ // 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();
+ }
+}