diff --git a/js/src/gc/GCParallelTask.h b/js/src/gc/GCParallelTask.h index 56987ee266..bcb5ea93b0 100644 --- a/js/src/gc/GCParallelTask.h +++ b/js/src/gc/GCParallelTask.h @@ -87,5 +87,24 @@ class GCParallelTask void runFromHelperThread(AutoLockHelperThreadState& locked); }; +// A CRTP helper to create a GCParallelTask from a derived class +// that already has a run() method. +template +class GCParallelTaskHelper : public GCParallelTask +{ + public: + explicit GCParallelTaskHelper(JSRuntime* runtime) + : GCParallelTask(runtime) + {} + + GCParallelTaskHelper(GCParallelTaskHelper&& other) + : GCParallelTask(mozilla::Move(other)) + {} + + void run() override { + static_cast(this)->run(); + } +}; + } /* namespace js */ #endif /* gc_GCParallelTask_h */ diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index 00041e3348..2af53d62ef 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -41,6 +41,9 @@ class MarkingValidator; class AutoTraceSession; struct MovingTracer; enum class ShouldCheckThresholds; + +template +struct SweepAction; class SweepGroupsIter; class WeakCacheSweepIterator; diff --git a/js/src/gc/Nursery.h b/js/src/gc/Nursery.h index 0c53f7e66c..dbcd3ef2fc 100644 --- a/js/src/gc/Nursery.h +++ b/js/src/gc/Nursery.h @@ -411,6 +411,7 @@ class Nursery }; using ProfileTimes = mozilla::EnumeratedArray; + using ProfileDurations = mozilla::EnumeratedArray; ProfileTimes startTimes_; ProfileTimes profileTimes_; diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index 2b6175e24b..124bac0224 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -18,6 +18,12 @@ struct JSContext; +namespace JS { +namespace detail { +class WeakCacheBase; +} // namespace detail +} // namespace JS + namespace js { class Debugger; diff --git a/js/src/gc/ZoneGroup.h b/js/src/gc/ZoneGroup.h index e695be6dc5..42bfa61915 100644 --- a/js/src/gc/ZoneGroup.h +++ b/js/src/gc/ZoneGroup.h @@ -18,6 +18,20 @@ namespace jit { class JitZoneGroup; } class AutoKeepAtoms; typedef Vector ZoneVector; +typedef Vector ZoneGroupVector; + +// In UXP there is only one cooperating context per thread, represented +// as a simple wrapper around JSContext*. +class CooperatingContext +{ + JSContext* cx_; + public: + explicit CooperatingContext(JSContext* cx) : cx_(cx) {} + JSContext* operator*() const { return cx_; } + JSContext* operator->() const { return cx_; } + explicit operator bool() const { return !!cx_; } + JSContext* get() const { return cx_; } +}; // Zone groups encapsulate data about a group of zones that are logically // related in some way. diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 4a04857040..18e954513b 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -104,70 +104,31 @@ void ReportOverRecursed(JSContext* cx, unsigned errorNumber); /* Thread Local Storage slot for storing the context for a thread. */ extern MOZ_THREAD_LOCAL(JSContext*) TlsContext; +} /* namespace js */ + +namespace js { + enum class ContextKind { - friend class gc::ArenaLists; - friend class AutoCompartment; - friend class AutoLockForExclusiveAccess; - friend struct StackBaseShape; - friend void JSScript::initCompartment(ExclusiveContext* cx); - friend class jit::JitContext; + Context_JS, + Context_Exclusive +}; - // runtime_ is private to hide it from JSContext. JSContext inherits from - // JSRuntime, so it's more efficient to use the base class. - JSRuntime* const runtime_; - -/* - * A JSContext encapsulates the thread local state used when using the JS - * runtime. - */ -struct JSContext : public JS::RootingContext, - public js::MallocProvider +// ExclusiveContext provides a base for JSContext with context-kind tracking. +class ExclusiveContext : public ContextFriendFields, + public MallocProvider { - JSContext(JSRuntime* runtime, const JS::ContextOptions& options); - ~JSContext(); - - bool init(js::ContextKind kind); - - private: - js::UnprotectedData runtime_; - js::WriteOnceData kind_; - - // System handle for the thread this context is associated with. - js::WriteOnceData threadNative_; - - // The thread on which this context is running, if this is performing a parse task. - js::ThreadLocalData helperThread_; - - friend class js::gc::AutoSuppressNurseryCellAlloc; - js::ThreadLocalData nurserySuppressions_; - - js::ThreadLocalData options_; - - js::ThreadLocalData arenas_; - - public: - enum ContextKind { - Context_JS, - Context_Exclusive - }; - - private: - ContextKind contextKind_; - protected: - // Background threads get a read-only copy of the main thread's - // ContextOptions. + JSRuntime* const runtime_; + ContextKind contextKind_; JS::ContextOptions options_; public: - PerThreadData* perThreadData; - ExclusiveContext(JSRuntime* rt, PerThreadData* pt, ContextKind kind, const JS::ContextOptions& options); bool isJSContext() const { - return contextKind_ == Context_JS; + return contextKind_ == ContextKind::Context_JS; } JSContext* maybeJSContext() const { @@ -177,21 +138,10 @@ struct JSContext : public JS::RootingContext, } JSContext* asJSContext() const { - // Note: there is no way to perform an unchecked coercion from a - // ThreadSafeContext to a JSContext. This ensures that trying to use - // the context as a JSContext off the main thread will nullptr crash - // rather than race. MOZ_ASSERT(isJSContext()); return maybeJSContext(); } - // In some cases we could potentially want to do operations that require a - // JSContext while running off the main thread. While this should never - // actually happen, the wide enough API for working off the main thread - // makes such operations impossible to rule out. Rather than blindly using - // asJSContext() and crashing afterwards, this method may be used to watch - // for such cases and produce either a soft failure in release builds or - // an assertion failure in debug builds. bool shouldBeJSContext() const { MOZ_ASSERT(isJSContext()); return isJSContext(); @@ -205,187 +155,33 @@ struct JSContext : public JS::RootingContext, return runtime_ == rt; } - protected: - js::gc::ArenaLists* arenas_; + PerThreadData* perThreadData; public: - inline js::gc::ArenaLists* arenas() const { return arenas_; } + gc::ArenaLists* arenas_; - template - bool isInsideCurrentZone(T thing) const { - return thing->zoneFromAnyThread() == zone_; - } + // Error reporting + static JS::Error reportedError; + static JS::OOM reportedOOM; - template - inline bool isInsideCurrentCompartment(T thing) const { - return thing->compartment() == compartment_; - } + inline JS::Result<> boolToResult(bool ok); - void* onOutOfMemory(js::AllocFunction allocFunc, size_t nbytes, void* reallocPtr = nullptr) { - if (!isJSContext()) { - addPendingOutOfMemory(); - return nullptr; - } - return runtime_->onOutOfMemory(allocFunc, nbytes, reallocPtr, asJSContext()); - } + mozilla::GenericErrorResult alreadyReportedOOM(); + mozilla::GenericErrorResult alreadyReportedError(); - /* Clear the pending exception (if any) due to OOM. */ - void recoverFromOutOfMemory(); - - inline void updateMallocCounter(size_t nbytes) { - // Note: this is racy. - runtime_->updateMallocCounter(zone_, nbytes); - } - - void reportAllocationOverflow() { - js::ReportAllocationOverflow(this); - } - - // Accessors for immutable runtime data. + // Forwarding methods for JSRuntime members JSAtomState& names() { return *runtime_->commonNames; } StaticStrings& staticStrings() { return *runtime_->staticStrings; } - SharedImmutableStringsCache& sharedImmutableStrings() { - return runtime_->sharedImmutableStrings(); - } bool isPermanentAtomsInitialized() { return !!runtime_->permanentAtoms; } FrozenAtomSet& permanentAtoms() { return *runtime_->permanentAtoms; } WellKnownSymbols& wellKnownSymbols() { return *runtime_->wellKnownSymbols; } JS::BuildIdOp buildIdOp() { return runtime_->buildIdOp; } - const JS::AsmJSCacheOps& asmJSCacheOps() { return runtime_->asmJSCacheOps; } PropertyName* emptyString() { return runtime_->emptyString; } - FreeOp* defaultFreeOp() { return runtime_->defaultFreeOp(); } - void* contextAddressForJit() { return runtime_->unsafeContextFromAnyThread(); } - void* runtimeAddressOfInterruptUint32() { return runtime_->addressOfInterruptUint32(); } - void* stackLimitAddress(StackKind kind) { return &nativeStackLimit[kind]; } - void* stackLimitAddressForJitCode(StackKind kind); - uintptr_t stackLimit(StackKind kind) { return nativeStackLimit[kind]; } - uintptr_t stackLimitForJitCode(StackKind kind); - size_t gcSystemPageSize() { return gc::SystemPageSize(); } bool jitSupportsFloatingPoint() const { return runtime_->jitSupportsFloatingPoint; } - bool jitSupportsUnalignedAccesses() const { return runtime_->jitSupportsUnalignedAccesses; } bool jitSupportsSimd() const { return runtime_->jitSupportsSimd; } - bool lcovEnabled() const { return runtime_->lcovOutput.isEnabled(); } - - // Thread local data that may be accessed freely. - DtoaState* dtoaState() { - return perThreadData->dtoaState; - } - - frontend::NameCollectionPool& frontendCollectionPool() { - return perThreadData->frontendCollectionPool; - } - - /* - * "Entering" a compartment changes cx->compartment (which changes - * cx->global). Note that this does not push any InterpreterFrame which means - * that it is possible for cx->fp()->compartment() != cx->compartment. - * This is not a problem since, in general, most places in the VM cannot - * know that they were called from script (e.g., they may have been called - * through the JSAPI via JS_CallFunction) and thus cannot expect fp. - * - * Compartments should be entered/left in a LIFO fasion. The depth of this - * enter/leave stack is maintained by enterCompartmentDepth_ and queried by - * hasEnteredCompartment. - * - * To enter a compartment, code should prefer using AutoCompartment over - * manually calling cx->enterCompartment/leaveCompartment. - */ - protected: - unsigned enterCompartmentDepth_; - - inline void setCompartment(JSCompartment* comp, - const js::AutoLockForExclusiveAccess* maybeLock = nullptr); - public: - bool hasEnteredCompartment() const { - return enterCompartmentDepth_ > 0; - } -#ifdef DEBUG - unsigned getEnterCompartmentDepth() const { - return enterCompartmentDepth_; - } -#endif - - // If |c| or |oldCompartment| is the atoms compartment, the - // |exclusiveAccessLock| must be held. - inline void enterCompartment(JSCompartment* c, - const js::AutoLockForExclusiveAccess* maybeLock = nullptr); - inline void enterNullCompartment(); - inline void leaveCompartment(JSCompartment* oldCompartment, - const js::AutoLockForExclusiveAccess* maybeLock = nullptr); - - void setHelperThread(HelperThread* helperThread); - HelperThread* helperThread() const { return helperThread_; } - - void setHelperThread(js::HelperThread* helperThread); - js::HelperThread* helperThread() const { return helperThread_; } - - bool isNurseryAllocSuppressed() const { - return nurserySuppressions_; - } - - // Threads may freely access any data in their compartment and zone. - JSCompartment* compartment() const { - return compartment_; - } - JS::Zone* zone() const { - MOZ_ASSERT_IF(!compartment(), !zone_); - MOZ_ASSERT_IF(compartment(), js::GetCompartmentZone(compartment()) == zone_); - return zone_; - } - - // Zone local methods that can be used freely from an ExclusiveContext. - inline js::LifoAlloc& typeLifoAlloc(); - - // Current global. This is only safe to use within the scope of the - // AutoCompartment from which it's called. - inline js::Handle global() const; - - // Methods to access runtime data that must be protected by locks. - AtomSet& atoms(js::AutoLockForExclusiveAccess& lock) { - return runtime_->atoms(lock); - } - JSCompartment* atomsCompartment(js::AutoLockForExclusiveAccess& lock) { - return runtime_->atomsCompartment(lock); - } - SymbolRegistry& symbolRegistry(js::AutoLockForExclusiveAccess& lock) { - return runtime_->symbolRegistry(lock); - } - js::ScriptDataTable& scriptDataTable(js::AutoLockScriptData& lock) { - return runtime_->scriptDataTable(lock); - } - - // Methods specific to any HelperThread for the context. - bool addPendingCompileError(frontend::CompileError** err); - void addPendingOverRecursed(); - void addPendingOutOfMemory(); - - private: - static JS::Error reportedError; - static JS::OOM reportedOOM; - - public: - inline JS::Result<> boolToResult(bool ok); - - /** - * Intentionally awkward signpost method that is stationed on the - * boundary between Result-using and non-Result-using code. - */ - template - bool resultToBool(JS::Result result) { - return result.isOk(); - } - - template - V* resultToPtr(JS::Result result) { - return result.isOk() ? result.unwrap() : nullptr; - } - - mozilla::GenericErrorResult alreadyReportedOOM(); - mozilla::GenericErrorResult alreadyReportedError(); + JSCompartment* atomsCompartment(AutoLockForExclusiveAccess& lock) { return runtime_->atomsCompartment(lock); } }; -void ReportOverRecursed(JSContext* cx, unsigned errorNumber); - } /* namespace js */ struct JSContext : public js::ExclusiveContext, @@ -396,23 +192,6 @@ struct JSContext : public js::ExclusiveContext, bool init(uint32_t maxBytes, uint32_t maxNurseryBytes); - // For names that exist in both ExclusiveContext and JSRuntime, pick the - // ExclusiveContext version. - using ExclusiveContext::atomsCompartment; - using ExclusiveContext::buildIdOp; - using ExclusiveContext::emptyString; - using ExclusiveContext::jitSupportsSimd; - using ExclusiveContext::make_pod_array; - using ExclusiveContext::make_unique; - using ExclusiveContext::new_; - using ExclusiveContext::permanentAtoms; - using ExclusiveContext::pod_calloc; - using ExclusiveContext::pod_malloc; - using ExclusiveContext::pod_realloc; - using ExclusiveContext::staticStrings; - using ExclusiveContext::updateMallocCounter; - using ExclusiveContext::wellKnownSymbols; - JSRuntime* runtime() { return this; } js::PerThreadData& mainThread() { return this->JSRuntime::mainThread; } @@ -429,9 +208,7 @@ struct JSContext : public js::ExclusiveContext, return offsetof(JSContext, compartment_); } - friend class js::ExclusiveContext; friend class JS::AutoSaveExceptionState; - friend class js::jit::DebugModeOSRVolatileJitFrameIterator; friend void js::ReportOverRecursed(JSContext*, unsigned errorNumber); private: @@ -950,6 +727,7 @@ class MOZ_RAII AutoKeepAtoms { JSContext* cx; MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; extern JS::TwoByteCharsZ LossyUTF8CharsToNewTwoByteCharsZ(ExclusiveContext* cx, const JS::ConstUTF8CharsZ& utf8, size_t* outlen); diff --git a/js/src/vm/NativeObject.h b/js/src/vm/NativeObject.h index 7d7d9fab96..92b752bca6 100644 --- a/js/src/vm/NativeObject.h +++ b/js/src/vm/NativeObject.h @@ -1030,7 +1030,7 @@ class NativeObject : public ShapedObject for (size_t i = 0; i < count; i++) { const Value& v = elements_[start + i]; if (v.isObject() && IsInsideNursery(&v.toObject())) { - JS::shadow::Runtime* shadowRuntime = JS::shadow::Runtime::asShadowRuntime(runtimeFromMainThread()); + JS::shadow::Runtime* shadowRuntime = JS::shadow::Runtime::asShadowRuntime(zone()->runtimeFromMainThread()); shadowRuntime->gcStoreBufferPtr()->putSlot(this, HeapSlot::Element, start + i, count - i); return; diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 3a2fc8b44c..6235ddae44 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -20,6 +20,7 @@ #include "jsatom.h" #include "jsclist.h" #include "jsscript.h" +#include "irregexp/RegExpStack.h" #ifdef XP_DARWIN # include "wasm/WasmSignalHandlers.h" @@ -1042,6 +1043,8 @@ struct JSRuntime : public JS::shadow::Runtime, return keepAtoms_ != 0 || exclusiveThreadsPresent(); } + bool exclusiveThreadsPresent() const { return false; } + private: const JSPrincipals* trustedPrincipals_; public: diff --git a/js/src/vm/Scope.h b/js/src/vm/Scope.h index a480a8c1e9..115cbb6173 100644 --- a/js/src/vm/Scope.h +++ b/js/src/vm/Scope.h @@ -1524,18 +1524,6 @@ DEFINE_SCOPE_DATA_GCPOLICY(js::WasmFunctionScope::Data); #undef DEFINE_SCOPE_DATA_GCPOLICY -// Scope data that contain GCPtrs must use the correct DeletePolicy. - -template <> -struct DeletePolicy - : public js::GCManagedDeletePolicy -{}; - -template <> -struct DeletePolicy - : public js::GCManagedDeletePolicy -{}; - namespace ubi { template <>