1763 lines
53 KiB
C++
1763 lines
53 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
* 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_Runtime_h
|
|
#define vm_Runtime_h
|
|
|
|
#include "mozilla/Atomics.h"
|
|
#include "mozilla/Attributes.h"
|
|
#include "mozilla/LinkedList.h"
|
|
#include "mozilla/MemoryReporting.h"
|
|
#include "mozilla/Scoped.h"
|
|
#include "mozilla/ThreadLocal.h"
|
|
#include "mozilla/Vector.h"
|
|
|
|
#include <algorithm>
|
|
#include <setjmp.h>
|
|
|
|
#include "jsatom.h"
|
|
#include "jsclist.h"
|
|
#include "jsscript.h"
|
|
|
|
#ifdef XP_DARWIN
|
|
# include "wasm/WasmSignalHandlers.h"
|
|
#endif
|
|
#include "builtin/AtomicsObject.h"
|
|
#include "builtin/Intl.h"
|
|
#include "builtin/Promise.h"
|
|
#include "ds/FixedSizeHash.h"
|
|
#include "frontend/NameCollections.h"
|
|
#include "gc/GCRuntime.h"
|
|
#include "gc/Tracer.h"
|
|
#include "irregexp/RegExpStack.h"
|
|
#include "js/Debug.h"
|
|
#include "js/GCVector.h"
|
|
#include "js/HashTable.h"
|
|
#ifdef DEBUG
|
|
# include "js/Proxy.h" // For AutoEnterPolicy
|
|
#endif
|
|
#include "js/UniquePtr.h"
|
|
#include "js/Vector.h"
|
|
#include "threading/Thread.h"
|
|
#include "vm/CodeCoverage.h"
|
|
#include "vm/CommonPropertyNames.h"
|
|
#include "vm/DateTime.h"
|
|
#include "vm/MallocProvider.h"
|
|
#include "vm/Scope.h"
|
|
#include "vm/SharedImmutableStringsCache.h"
|
|
#include "vm/SPSProfiler.h"
|
|
#include "vm/Stack.h"
|
|
#include "vm/Stopwatch.h"
|
|
#include "vm/Symbol.h"
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
|
|
#endif
|
|
|
|
namespace js {
|
|
|
|
class PerThreadData;
|
|
class ExclusiveContext;
|
|
class AutoKeepAtoms;
|
|
class EnterDebuggeeNoExecute;
|
|
#ifdef JS_TRACE_LOGGING
|
|
class TraceLoggerThread;
|
|
#endif
|
|
|
|
typedef Vector<UniquePtr<PromiseTask>, 0, SystemAllocPolicy> PromiseTaskPtrVector;
|
|
|
|
/* Thread Local Storage slot for storing the runtime for a thread. */
|
|
extern MOZ_THREAD_LOCAL(PerThreadData*) TlsPerThreadData;
|
|
|
|
} // namespace js
|
|
|
|
struct DtoaState;
|
|
|
|
#ifdef JS_SIMULATOR_ARM64
|
|
namespace vixl {
|
|
class Simulator;
|
|
}
|
|
#endif
|
|
|
|
namespace js {
|
|
|
|
extern MOZ_COLD void
|
|
ReportOutOfMemory(ExclusiveContext* cx);
|
|
|
|
extern MOZ_COLD void
|
|
ReportAllocationOverflow(ExclusiveContext* maybecx);
|
|
|
|
extern MOZ_COLD void
|
|
ReportOverRecursed(ExclusiveContext* cx);
|
|
|
|
class Activation;
|
|
class ActivationIterator;
|
|
class WasmActivation;
|
|
|
|
namespace jit {
|
|
class JitRuntime;
|
|
class JitActivation;
|
|
struct PcScriptCache;
|
|
struct AutoFlushICache;
|
|
class CompileRuntime;
|
|
|
|
#ifdef JS_SIMULATOR_ARM64
|
|
typedef vixl::Simulator Simulator;
|
|
#elif defined(JS_SIMULATOR)
|
|
class Simulator;
|
|
#endif
|
|
} // namespace jit
|
|
|
|
/*
|
|
* A FreeOp can do one thing: free memory. For convenience, it has delete_
|
|
* convenience methods that also call destructors.
|
|
*
|
|
* FreeOp is passed to finalizers and other sweep-phase hooks so that we do not
|
|
* need to pass a JSContext to those hooks.
|
|
*/
|
|
class FreeOp : public JSFreeOp
|
|
{
|
|
Vector<void*, 0, SystemAllocPolicy> freeLaterList;
|
|
jit::JitPoisonRangeVector jitPoisonRanges;
|
|
|
|
public:
|
|
static FreeOp* get(JSFreeOp* fop) {
|
|
return static_cast<FreeOp*>(fop);
|
|
}
|
|
|
|
explicit FreeOp(JSRuntime* maybeRuntime);
|
|
~FreeOp();
|
|
|
|
bool onMainThread() const {
|
|
return runtime_ != nullptr;
|
|
}
|
|
|
|
bool maybeOffMainThread() const {
|
|
// Sometimes background finalization happens on the main thread so
|
|
// runtime_ being null doesn't always mean we are off the main thread.
|
|
return !runtime_;
|
|
}
|
|
|
|
bool isDefaultFreeOp() const;
|
|
|
|
inline void free_(void* p);
|
|
inline void freeLater(void* p);
|
|
|
|
inline bool appendJitPoisonRange(const jit::JitPoisonRange& range);
|
|
|
|
template <class T>
|
|
inline void delete_(T* p) {
|
|
if (p) {
|
|
p->~T();
|
|
free_(p);
|
|
}
|
|
}
|
|
};
|
|
|
|
} /* namespace js */
|
|
|
|
namespace JS {
|
|
struct RuntimeSizes;
|
|
} // namespace JS
|
|
|
|
/* Various built-in or commonly-used names pinned on first context. */
|
|
struct JSAtomState
|
|
{
|
|
#define PROPERTYNAME_FIELD(idpart, id, text) js::ImmutablePropertyNamePtr id;
|
|
FOR_EACH_COMMON_PROPERTYNAME(PROPERTYNAME_FIELD)
|
|
#undef PROPERTYNAME_FIELD
|
|
#define PROPERTYNAME_FIELD(name, code, init, clasp) js::ImmutablePropertyNamePtr name;
|
|
JS_FOR_EACH_PROTOTYPE(PROPERTYNAME_FIELD)
|
|
#undef PROPERTYNAME_FIELD
|
|
#define PROPERTYNAME_FIELD(name) js::ImmutablePropertyNamePtr name;
|
|
JS_FOR_EACH_WELL_KNOWN_SYMBOL(PROPERTYNAME_FIELD)
|
|
#undef PROPERTYNAME_FIELD
|
|
#define PROPERTYNAME_FIELD(name) js::ImmutablePropertyNamePtr Symbol_##name;
|
|
JS_FOR_EACH_WELL_KNOWN_SYMBOL(PROPERTYNAME_FIELD)
|
|
#undef PROPERTYNAME_FIELD
|
|
|
|
js::ImmutablePropertyNamePtr* wellKnownSymbolNames() {
|
|
#define FIRST_PROPERTYNAME_FIELD(name) return &name;
|
|
JS_FOR_EACH_WELL_KNOWN_SYMBOL(FIRST_PROPERTYNAME_FIELD)
|
|
#undef FIRST_PROPERTYNAME_FIELD
|
|
}
|
|
|
|
js::ImmutablePropertyNamePtr* wellKnownSymbolDescriptions() {
|
|
#define FIRST_PROPERTYNAME_FIELD(name) return &Symbol_ ##name;
|
|
JS_FOR_EACH_WELL_KNOWN_SYMBOL(FIRST_PROPERTYNAME_FIELD)
|
|
#undef FIRST_PROPERTYNAME_FIELD
|
|
}
|
|
};
|
|
|
|
namespace js {
|
|
|
|
/*
|
|
* Storage for well-known symbols. It's a separate struct from the Runtime so
|
|
* that it can be shared across multiple runtimes. As in JSAtomState, each
|
|
* field is a smart pointer that's immutable once initialized.
|
|
* `rt->wellKnownSymbols->iterator` is convertible to Handle<Symbol*>.
|
|
*
|
|
* Well-known symbols are never GC'd. The description() of each well-known
|
|
* symbol is a permanent atom.
|
|
*/
|
|
struct WellKnownSymbols
|
|
{
|
|
#define DECLARE_SYMBOL(name) js::ImmutableSymbolPtr name;
|
|
JS_FOR_EACH_WELL_KNOWN_SYMBOL(DECLARE_SYMBOL)
|
|
#undef DECLARE_SYMBOL
|
|
|
|
const ImmutableSymbolPtr& get(size_t u) const {
|
|
MOZ_ASSERT(u < JS::WellKnownSymbolLimit);
|
|
const ImmutableSymbolPtr* symbols = reinterpret_cast<const ImmutableSymbolPtr*>(this);
|
|
return symbols[u];
|
|
}
|
|
|
|
const ImmutableSymbolPtr& get(JS::SymbolCode code) const {
|
|
return get(size_t(code));
|
|
}
|
|
|
|
WellKnownSymbols() {}
|
|
WellKnownSymbols(const WellKnownSymbols&) = delete;
|
|
WellKnownSymbols& operator=(const WellKnownSymbols&) = delete;
|
|
};
|
|
|
|
#define NAME_OFFSET(name) offsetof(JSAtomState, name)
|
|
|
|
inline HandlePropertyName
|
|
AtomStateOffsetToName(const JSAtomState& atomState, size_t offset)
|
|
{
|
|
return *reinterpret_cast<js::ImmutablePropertyNamePtr*>((char*)&atomState + offset);
|
|
}
|
|
|
|
// There are several coarse locks in the enum below. These may be either
|
|
// per-runtime or per-process. When acquiring more than one of these locks,
|
|
// the acquisition must be done in the order below to avoid deadlocks.
|
|
enum RuntimeLock {
|
|
ExclusiveAccessLock,
|
|
HelperThreadStateLock,
|
|
GCLock
|
|
};
|
|
|
|
inline bool
|
|
CanUseExtraThreads()
|
|
{
|
|
extern bool gCanUseExtraThreads;
|
|
return gCanUseExtraThreads;
|
|
}
|
|
|
|
void DisableExtraThreads();
|
|
|
|
/*
|
|
* Encapsulates portions of the runtime/context that are tied to a
|
|
* single active thread. Instances of this structure can occur for
|
|
* the main thread as |JSRuntime::mainThread|, for select operations
|
|
* performed off thread, such as parsing.
|
|
*/
|
|
class PerThreadData
|
|
{
|
|
/*
|
|
* Backpointer to the full shared JSRuntime* with which this
|
|
* thread is associated. This is private because accessing the
|
|
* fields of this runtime can provoke race conditions, so the
|
|
* intention is that access will be mediated through safe
|
|
* functions like |runtimeFromMainThread| and |associatedWith()| below.
|
|
*/
|
|
JSRuntime* runtime_;
|
|
|
|
public:
|
|
#ifdef JS_TRACE_LOGGING
|
|
TraceLoggerMainThread* traceLogger;
|
|
#endif
|
|
|
|
/* Pointer to the current AutoFlushICache. */
|
|
js::jit::AutoFlushICache* autoFlushICache_;
|
|
|
|
public:
|
|
/* State used by jsdtoa.cpp. */
|
|
DtoaState* dtoaState;
|
|
|
|
/*
|
|
* When this flag is non-zero, any attempt to GC will be skipped. It is used
|
|
* to suppress GC when reporting an OOM (see ReportOutOfMemory) and in
|
|
* debugging facilities that cannot tolerate a GC and would rather OOM
|
|
* immediately, such as utilities exposed to GDB. Setting this flag is
|
|
* extremely dangerous and should only be used when in an OOM situation or
|
|
* in non-exposed debugging facilities.
|
|
*/
|
|
int32_t suppressGC;
|
|
|
|
#ifdef DEBUG
|
|
// Whether this thread is actively Ion compiling.
|
|
bool ionCompiling;
|
|
|
|
// Whether this thread is actively Ion compiling in a context where a minor
|
|
// GC could happen simultaneously. If this is true, this thread cannot use
|
|
// any pointers into the nursery.
|
|
bool ionCompilingSafeForMinorGC;
|
|
|
|
// Whether this thread is currently performing GC. This thread could be the
|
|
// main thread or a helper thread while the main thread is running the
|
|
// collector.
|
|
bool performingGC;
|
|
|
|
// Whether this thread is currently sweeping GC things. This thread could
|
|
// be the main thread or a helper thread while the main thread is running
|
|
// the mutator. This is used to assert that destruction of GCPtr only
|
|
// happens when we are sweeping.
|
|
bool gcSweeping;
|
|
#endif
|
|
|
|
// Pools used for recycling name maps and vectors when parsing and
|
|
// emitting bytecode. Purged on GC when there are no active script
|
|
// compilations.
|
|
frontend::NameCollectionPool frontendCollectionPool;
|
|
|
|
explicit PerThreadData(JSRuntime* runtime);
|
|
~PerThreadData();
|
|
|
|
bool init();
|
|
|
|
bool associatedWith(const JSRuntime* rt) { return runtime_ == rt; }
|
|
inline JSRuntime* runtimeFromMainThread();
|
|
inline JSRuntime* runtimeIfOnOwnerThread();
|
|
|
|
JSContext* contextFromMainThread();
|
|
|
|
inline bool exclusiveThreadsPresent();
|
|
|
|
// For threads which may be associated with different runtimes, depending
|
|
// on the work they are doing.
|
|
class MOZ_STACK_CLASS AutoEnterRuntime
|
|
{
|
|
PerThreadData* pt;
|
|
|
|
public:
|
|
AutoEnterRuntime(PerThreadData* pt, JSRuntime* rt)
|
|
: pt(pt)
|
|
{
|
|
MOZ_ASSERT(!pt->runtime_);
|
|
pt->runtime_ = rt;
|
|
}
|
|
|
|
~AutoEnterRuntime() {
|
|
pt->runtime_ = nullptr;
|
|
}
|
|
};
|
|
|
|
js::jit::AutoFlushICache* autoFlushICache() const;
|
|
void setAutoFlushICache(js::jit::AutoFlushICache* afc);
|
|
|
|
#ifdef JS_SIMULATOR
|
|
js::jit::Simulator* simulator() const;
|
|
#endif
|
|
};
|
|
|
|
using ScriptAndCountsVector = GCVector<ScriptAndCounts, 0, SystemAllocPolicy>;
|
|
|
|
class AutoLockForExclusiveAccess;
|
|
} // namespace js
|
|
|
|
struct JSRuntime : public JS::shadow::Runtime,
|
|
public js::MallocProvider<JSRuntime>
|
|
{
|
|
/*
|
|
* Per-thread data for the main thread that is associated with
|
|
* this JSRuntime, as opposed to any worker threads used in
|
|
* parallel sections. See definition of |PerThreadData| struct
|
|
* above for more details.
|
|
*
|
|
* NB: This field is statically asserted to be at offset
|
|
* sizeof(js::shadow::Runtime). See
|
|
* PerThreadDataFriendFields::getMainThread.
|
|
*/
|
|
js::PerThreadData mainThread;
|
|
|
|
/*
|
|
* If Baseline or Ion code is on the stack, and has called into C++, this
|
|
* will be aligned to an exit frame.
|
|
*/
|
|
uint8_t* jitTop;
|
|
|
|
/*
|
|
* Points to the most recent JitActivation pushed on the thread.
|
|
* See JitActivation constructor in vm/Stack.cpp
|
|
*/
|
|
js::jit::JitActivation* jitActivation;
|
|
|
|
/* See comment for JSRuntime::interrupt_. */
|
|
protected:
|
|
mozilla::Atomic<uintptr_t, mozilla::Relaxed> jitStackLimit_;
|
|
|
|
// Like jitStackLimit_, but not reset to trigger interrupts.
|
|
uintptr_t jitStackLimitNoInterrupt_;
|
|
|
|
public:
|
|
uintptr_t jitStackLimit() const { return jitStackLimit_; }
|
|
|
|
// For read-only JIT use:
|
|
void* addressOfJitStackLimit() { return &jitStackLimit_; }
|
|
static size_t offsetOfJitStackLimit() { return offsetof(JSRuntime, jitStackLimit_); }
|
|
|
|
void* addressOfJitStackLimitNoInterrupt() { return &jitStackLimitNoInterrupt_; }
|
|
|
|
// Information about the heap allocated backtrack stack used by RegExp JIT code.
|
|
js::irregexp::RegExpStack regexpStack;
|
|
|
|
#ifdef DEBUG
|
|
private:
|
|
// The number of possible bailing places encounters before forcefully bailing
|
|
// in that place. Zero means inactive.
|
|
uint32_t ionBailAfter_;
|
|
|
|
public:
|
|
void* addressOfIonBailAfter() { return &ionBailAfter_; }
|
|
|
|
// Set after how many bailing places we should forcefully bail.
|
|
// Zero disables this feature.
|
|
void setIonBailAfter(uint32_t after) {
|
|
ionBailAfter_ = after;
|
|
}
|
|
#endif
|
|
|
|
private:
|
|
friend class js::Activation;
|
|
friend class js::ActivationIterator;
|
|
friend class js::jit::JitActivation;
|
|
friend class js::WasmActivation;
|
|
friend class js::jit::CompileRuntime;
|
|
|
|
protected:
|
|
/*
|
|
* Points to the most recent activation running on the thread.
|
|
* See Activation comment in vm/Stack.h.
|
|
*/
|
|
js::Activation* activation_;
|
|
|
|
/*
|
|
* Points to the most recent profiling activation running on the
|
|
* thread.
|
|
*/
|
|
js::Activation * volatile profilingActivation_;
|
|
|
|
/*
|
|
* The profiler sampler generation after the latest sample.
|
|
*
|
|
* The lapCount indicates the number of largest number of 'laps'
|
|
* (wrapping from high to low) that occurred when writing entries
|
|
* into the sample buffer. All JitcodeGlobalMap entries referenced
|
|
* from a given sample are assigned the generation of the sample buffer
|
|
* at the START of the run. If multiple laps occur, then some entries
|
|
* (towards the end) will be written out with the "wrong" generation.
|
|
* The lapCount indicates the required fudge factor to use to compare
|
|
* entry generations with the sample buffer generation.
|
|
*/
|
|
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> profilerSampleBufferGen_;
|
|
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> profilerSampleBufferLapCount_;
|
|
|
|
/* See WasmActivation comment. */
|
|
js::WasmActivation * volatile wasmActivationStack_;
|
|
|
|
public:
|
|
/* If non-null, report JavaScript entry points to this monitor. */
|
|
JS::dbg::AutoEntryMonitor* entryMonitor;
|
|
|
|
/*
|
|
* Stack of debuggers that currently disallow debuggee execution.
|
|
*
|
|
* When we check for NX we are inside the debuggee compartment, and thus a
|
|
* stack of Debuggers that have prevented execution need to be tracked to
|
|
* enter the correct Debugger compartment to report the error.
|
|
*/
|
|
js::EnterDebuggeeNoExecute* noExecuteDebuggerTop;
|
|
|
|
js::Activation* const* addressOfActivation() const {
|
|
return &activation_;
|
|
}
|
|
static unsigned offsetOfActivation() {
|
|
return offsetof(JSRuntime, activation_);
|
|
}
|
|
|
|
js::Activation* profilingActivation() const {
|
|
return profilingActivation_;
|
|
}
|
|
void* addressOfProfilingActivation() {
|
|
return (void*) &profilingActivation_;
|
|
}
|
|
static unsigned offsetOfProfilingActivation() {
|
|
return offsetof(JSRuntime, profilingActivation_);
|
|
}
|
|
|
|
uint32_t profilerSampleBufferGen() {
|
|
return profilerSampleBufferGen_;
|
|
}
|
|
void resetProfilerSampleBufferGen() {
|
|
profilerSampleBufferGen_ = 0;
|
|
}
|
|
void setProfilerSampleBufferGen(uint32_t gen) {
|
|
// May be called from sampler thread or signal handler; use
|
|
// compareExchange to make sure we have monotonic increase.
|
|
for (;;) {
|
|
uint32_t curGen = profilerSampleBufferGen_;
|
|
if (curGen >= gen)
|
|
break;
|
|
|
|
if (profilerSampleBufferGen_.compareExchange(curGen, gen))
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint32_t profilerSampleBufferLapCount() {
|
|
MOZ_ASSERT(profilerSampleBufferLapCount_ > 0);
|
|
return profilerSampleBufferLapCount_;
|
|
}
|
|
void resetProfilerSampleBufferLapCount() {
|
|
profilerSampleBufferLapCount_ = 1;
|
|
}
|
|
void updateProfilerSampleBufferLapCount(uint32_t lapCount) {
|
|
MOZ_ASSERT(profilerSampleBufferLapCount_ > 0);
|
|
|
|
// May be called from sampler thread or signal handler; use
|
|
// compareExchange to make sure we have monotonic increase.
|
|
for (;;) {
|
|
uint32_t curLapCount = profilerSampleBufferLapCount_;
|
|
if (curLapCount >= lapCount)
|
|
break;
|
|
|
|
if (profilerSampleBufferLapCount_.compareExchange(curLapCount, lapCount))
|
|
break;
|
|
}
|
|
}
|
|
|
|
js::WasmActivation* wasmActivationStack() const {
|
|
return wasmActivationStack_;
|
|
}
|
|
static js::WasmActivation* innermostWasmActivation() {
|
|
js::PerThreadData* ptd = js::TlsPerThreadData.get();
|
|
return ptd ? ptd->runtimeFromMainThread()->wasmActivationStack_ : nullptr;
|
|
}
|
|
|
|
js::Activation* activation() const {
|
|
return activation_;
|
|
}
|
|
|
|
/*
|
|
* If non-null, another runtime guaranteed to outlive this one and whose
|
|
* permanent data may be used by this one where possible.
|
|
*/
|
|
JSRuntime* parentRuntime;
|
|
|
|
private:
|
|
#ifdef DEBUG
|
|
/* The number of child runtimes that have this runtime as their parent. */
|
|
mozilla::Atomic<size_t> childRuntimeCount;
|
|
|
|
class AutoUpdateChildRuntimeCount
|
|
{
|
|
JSRuntime* parent_;
|
|
|
|
public:
|
|
explicit AutoUpdateChildRuntimeCount(JSRuntime* parent)
|
|
: parent_(parent)
|
|
{
|
|
if (parent_)
|
|
parent_->childRuntimeCount++;
|
|
}
|
|
|
|
~AutoUpdateChildRuntimeCount() {
|
|
if (parent_)
|
|
parent_->childRuntimeCount--;
|
|
}
|
|
};
|
|
|
|
AutoUpdateChildRuntimeCount updateChildRuntimeCount;
|
|
#endif
|
|
|
|
mozilla::Atomic<uint32_t, mozilla::Relaxed> interrupt_;
|
|
public:
|
|
enum InterruptMode {
|
|
RequestInterruptUrgent,
|
|
RequestInterruptCanWait
|
|
};
|
|
|
|
// Any thread can call requestInterrupt() to request that the main JS thread
|
|
// stop running and call the interrupt callback (allowing the interrupt
|
|
// callback to halt execution). To stop the main JS thread, requestInterrupt
|
|
// sets two fields: interrupt_ (set to true) and jitStackLimit_ (set to
|
|
// UINTPTR_MAX). The JS engine must continually poll one of these fields
|
|
// and call handleInterrupt if either field has the interrupt value. (The
|
|
// point of setting jitStackLimit_ to UINTPTR_MAX is that JIT code already
|
|
// needs to guard on jitStackLimit_ in every function prologue to avoid
|
|
// stack overflow, so we avoid a second branch on interrupt_ by setting
|
|
// jitStackLimit_ to a value that is guaranteed to fail the guard.)
|
|
//
|
|
// Note that the writes to interrupt_ and jitStackLimit_ use a Relaxed
|
|
// Atomic so, while the writes are guaranteed to eventually be visible to
|
|
// the main thread, it can happen in any order. handleInterrupt calls the
|
|
// interrupt callback if either is set, so it really doesn't matter as long
|
|
// as the JS engine is continually polling at least one field. In corner
|
|
// cases, this relaxed ordering could lead to an interrupt handler being
|
|
// called twice in succession after a single requestInterrupt call, but
|
|
// that's fine.
|
|
void requestInterrupt(InterruptMode mode);
|
|
bool handleInterrupt(JSContext* cx);
|
|
|
|
MOZ_ALWAYS_INLINE bool hasPendingInterrupt() const {
|
|
return interrupt_;
|
|
}
|
|
|
|
// For read-only JIT use:
|
|
void* addressOfInterruptUint32() {
|
|
static_assert(sizeof(interrupt_) == sizeof(uint32_t), "Assumed by JIT callers");
|
|
return &interrupt_;
|
|
}
|
|
|
|
// Set when handling a segfault in the wasm signal handler.
|
|
bool handlingSegFault;
|
|
|
|
private:
|
|
// Set when we're handling an interrupt of JIT/wasm code in
|
|
// InterruptRunningJitCode.
|
|
mozilla::Atomic<bool> handlingJitInterrupt_;
|
|
|
|
public:
|
|
bool startHandlingJitInterrupt() {
|
|
// Return true if we changed handlingJitInterrupt_ from
|
|
// false to true.
|
|
return handlingJitInterrupt_.compareExchange(false, true);
|
|
}
|
|
void finishHandlingJitInterrupt() {
|
|
MOZ_ASSERT(handlingJitInterrupt_);
|
|
handlingJitInterrupt_ = false;
|
|
}
|
|
bool handlingJitInterrupt() const {
|
|
return handlingJitInterrupt_;
|
|
}
|
|
|
|
using InterruptCallbackVector = js::Vector<JSInterruptCallback, 2, js::SystemAllocPolicy>;
|
|
InterruptCallbackVector interruptCallbacks;
|
|
bool interruptCallbackDisabled;
|
|
|
|
JSGetIncumbentGlobalCallback getIncumbentGlobalCallback;
|
|
JSEnqueuePromiseJobCallback enqueuePromiseJobCallback;
|
|
void* enqueuePromiseJobCallbackData;
|
|
|
|
JSPromiseRejectionTrackerCallback promiseRejectionTrackerCallback;
|
|
void* promiseRejectionTrackerCallbackData;
|
|
|
|
JS::StartAsyncTaskCallback startAsyncTaskCallback;
|
|
JS::FinishAsyncTaskCallback finishAsyncTaskCallback;
|
|
js::ExclusiveData<js::PromiseTaskPtrVector> promiseTasksToDestroy;
|
|
|
|
private:
|
|
/*
|
|
* Lock taken when using per-runtime or per-zone data that could otherwise
|
|
* be accessed simultaneously by both the main thread and another thread
|
|
* with an ExclusiveContext.
|
|
*
|
|
* Locking this only occurs if there is actually a thread other than the
|
|
* main thread with an ExclusiveContext which could access such data.
|
|
*/
|
|
js::Mutex exclusiveAccessLock;
|
|
#ifdef DEBUG
|
|
bool mainThreadHasExclusiveAccess;
|
|
#endif
|
|
|
|
/* Number of non-main threads with an ExclusiveContext. */
|
|
size_t numExclusiveThreads;
|
|
|
|
friend class js::AutoLockForExclusiveAccess;
|
|
|
|
public:
|
|
void setUsedByExclusiveThread(JS::Zone* zone);
|
|
void clearUsedByExclusiveThread(JS::Zone* zone);
|
|
|
|
bool exclusiveThreadsPresent() const {
|
|
return numExclusiveThreads > 0;
|
|
}
|
|
|
|
// How many compartments there are across all zones. This number includes
|
|
// ExclusiveContext compartments, so it isn't necessarily equal to the
|
|
// number of compartments visited by CompartmentsIter.
|
|
size_t numCompartments;
|
|
|
|
/* Locale-specific callbacks for string conversion. */
|
|
const JSLocaleCallbacks* localeCallbacks;
|
|
|
|
/* Default locale for Internationalization API */
|
|
char* defaultLocale;
|
|
|
|
/* Default JSVersion. */
|
|
JSVersion defaultVersion_;
|
|
|
|
/* Futex state, used by Atomics.wait() and Atomics.wake() on the Atomics object */
|
|
js::FutexRuntime fx;
|
|
|
|
private:
|
|
/* See comment for JS_AbortIfWrongThread in jsapi.h. */
|
|
js::Thread::Id ownerThread_;
|
|
size_t ownerThreadNative_;
|
|
friend bool js::CurrentThreadCanAccessRuntime(const JSRuntime* rt);
|
|
public:
|
|
|
|
size_t ownerThreadNative() const {
|
|
return ownerThreadNative_;
|
|
}
|
|
|
|
/* Temporary arena pool used while compiling and decompiling. */
|
|
static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 4 * 1024;
|
|
js::LifoAlloc tempLifoAlloc;
|
|
|
|
private:
|
|
js::jit::JitRuntime* jitRuntime_;
|
|
|
|
/*
|
|
* Self-hosting state cloned on demand into other compartments. Shared with the parent
|
|
* runtime if there is one.
|
|
*/
|
|
js::NativeObject* selfHostingGlobal_;
|
|
|
|
static js::GlobalObject*
|
|
createSelfHostingGlobal(JSContext* cx);
|
|
|
|
bool getUnclonedSelfHostedValue(JSContext* cx, js::HandlePropertyName name,
|
|
js::MutableHandleValue vp);
|
|
JSFunction* getUnclonedSelfHostedFunction(JSContext* cx, js::HandlePropertyName name);
|
|
|
|
/* Space for interpreter frames. */
|
|
js::InterpreterStack interpreterStack_;
|
|
|
|
js::jit::JitRuntime* createJitRuntime(JSContext* cx);
|
|
|
|
public:
|
|
js::jit::JitRuntime* getJitRuntime(JSContext* cx) {
|
|
return jitRuntime_ ? jitRuntime_ : createJitRuntime(cx);
|
|
}
|
|
js::jit::JitRuntime* jitRuntime() const {
|
|
return jitRuntime_;
|
|
}
|
|
bool hasJitRuntime() const {
|
|
return !!jitRuntime_;
|
|
}
|
|
js::InterpreterStack& interpreterStack() {
|
|
return interpreterStack_;
|
|
}
|
|
|
|
inline JSContext* unsafeContextFromAnyThread();
|
|
inline JSContext* contextFromMainThread();
|
|
|
|
JSObject* getIncumbentGlobal(JSContext* cx);
|
|
bool enqueuePromiseJob(JSContext* cx, js::HandleFunction job, js::HandleObject promise,
|
|
js::HandleObject incumbentGlobal);
|
|
void addUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise);
|
|
void removeUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise);
|
|
|
|
private:
|
|
// Used to generate random keys for hash tables.
|
|
mozilla::Maybe<mozilla::non_crypto::XorShift128PlusRNG> randomKeyGenerator_;
|
|
mozilla::non_crypto::XorShift128PlusRNG& randomKeyGenerator();
|
|
|
|
public:
|
|
mozilla::HashCodeScrambler randomHashCodeScrambler();
|
|
mozilla::non_crypto::XorShift128PlusRNG forkRandomKeyGenerator();
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Self-hosting support
|
|
//-------------------------------------------------------------------------
|
|
|
|
bool hasInitializedSelfHosting() const {
|
|
return selfHostingGlobal_;
|
|
}
|
|
|
|
bool initSelfHosting(JSContext* cx);
|
|
void finishSelfHosting();
|
|
void markSelfHostingGlobal(JSTracer* trc);
|
|
bool isSelfHostingGlobal(JSObject* global) {
|
|
return global == selfHostingGlobal_;
|
|
}
|
|
bool isSelfHostingCompartment(JSCompartment* comp) const;
|
|
bool isSelfHostingZone(const JS::Zone* zone) const;
|
|
bool createLazySelfHostedFunctionClone(JSContext* cx, js::HandlePropertyName selfHostedName,
|
|
js::HandleAtom name, unsigned nargs,
|
|
js::HandleObject proto,
|
|
js::NewObjectKind newKind,
|
|
js::MutableHandleFunction fun);
|
|
bool cloneSelfHostedFunctionScript(JSContext* cx, js::Handle<js::PropertyName*> name,
|
|
js::Handle<JSFunction*> targetFun);
|
|
bool cloneSelfHostedValue(JSContext* cx, js::Handle<js::PropertyName*> name,
|
|
js::MutableHandleValue vp);
|
|
void assertSelfHostedFunctionHasCanonicalName(JSContext* cx, js::HandlePropertyName name);
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Locale information
|
|
//-------------------------------------------------------------------------
|
|
|
|
/*
|
|
* Set the default locale for the ECMAScript Internationalization API
|
|
* (Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat).
|
|
* Note that the Internationalization API encourages clients to
|
|
* specify their own locales.
|
|
* The locale string remains owned by the caller.
|
|
*/
|
|
bool setDefaultLocale(const char* locale);
|
|
|
|
/* Reset the default locale to OS defaults. */
|
|
void resetDefaultLocale();
|
|
|
|
/* Gets current default locale. String remains owned by context. */
|
|
const char* getDefaultLocale();
|
|
|
|
/* Shared Intl data for this runtime. */
|
|
js::SharedIntlData sharedIntlData;
|
|
|
|
void traceSharedIntlData(JSTracer* trc);
|
|
|
|
JSVersion defaultVersion() const { return defaultVersion_; }
|
|
void setDefaultVersion(JSVersion v) { defaultVersion_ = v; }
|
|
|
|
/* Base address of the native stack for the current thread. */
|
|
const uintptr_t nativeStackBase;
|
|
|
|
/* The native stack size limit that runtime should not exceed. */
|
|
size_t nativeStackQuota[js::StackKindCount];
|
|
|
|
/* Compartment destroy callback. */
|
|
JSDestroyCompartmentCallback destroyCompartmentCallback;
|
|
|
|
/* Compartment memory reporting callback. */
|
|
JSSizeOfIncludingThisCompartmentCallback sizeOfIncludingThisCompartmentCallback;
|
|
|
|
/* Zone destroy callback. */
|
|
JSZoneCallback destroyZoneCallback;
|
|
|
|
/* Zone sweep callback. */
|
|
JSZoneCallback sweepZoneCallback;
|
|
|
|
/* Call this to get the name of a compartment. */
|
|
JSCompartmentNameCallback compartmentNameCallback;
|
|
|
|
js::ActivityCallback activityCallback;
|
|
void* activityCallbackArg;
|
|
void triggerActivityCallback(bool active);
|
|
|
|
/* The request depth for this thread. */
|
|
unsigned requestDepth;
|
|
|
|
#ifdef DEBUG
|
|
unsigned checkRequestDepth;
|
|
#endif
|
|
|
|
/* Garbage collector state, used by jsgc.c. */
|
|
js::gc::GCRuntime gc;
|
|
|
|
/* Garbage collector state has been successfully initialized. */
|
|
bool gcInitialized;
|
|
|
|
void lockGC() {
|
|
gc.lockGC();
|
|
}
|
|
|
|
void unlockGC() {
|
|
gc.unlockGC();
|
|
}
|
|
|
|
#ifdef JS_SIMULATOR
|
|
js::jit::Simulator* simulator_;
|
|
#endif
|
|
|
|
public:
|
|
#ifdef JS_SIMULATOR
|
|
js::jit::Simulator* simulator() const;
|
|
uintptr_t* addressOfSimulatorStackLimit();
|
|
#endif
|
|
|
|
/* Strong references on scripts held for PCCount profiling API. */
|
|
JS::PersistentRooted<js::ScriptAndCountsVector>* scriptAndCountsVector;
|
|
|
|
/* Code coverage output. */
|
|
js::coverage::LCovRuntime lcovOutput;
|
|
|
|
/* Well-known numbers. */
|
|
const js::Value NaNValue;
|
|
const js::Value negativeInfinityValue;
|
|
const js::Value positiveInfinityValue;
|
|
|
|
js::PropertyName* emptyString;
|
|
|
|
mozilla::UniquePtr<js::SourceHook> sourceHook;
|
|
|
|
/* SPS profiling metadata */
|
|
js::SPSProfiler spsProfiler;
|
|
|
|
/* If true, new scripts must be created with PC counter information. */
|
|
bool profilingScripts;
|
|
|
|
/* Whether sampling should be enabled or not. */
|
|
private:
|
|
mozilla::Atomic<bool, mozilla::SequentiallyConsistent> suppressProfilerSampling;
|
|
|
|
public:
|
|
bool isProfilerSamplingEnabled() const {
|
|
return !suppressProfilerSampling;
|
|
}
|
|
void disableProfilerSampling() {
|
|
suppressProfilerSampling = true;
|
|
}
|
|
void enableProfilerSampling() {
|
|
suppressProfilerSampling = false;
|
|
}
|
|
|
|
/* Had an out-of-memory error which did not populate an exception. */
|
|
bool hadOutOfMemory;
|
|
|
|
#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
|
|
/* We are currently running a simulated OOM test. */
|
|
bool runningOOMTest;
|
|
#endif
|
|
|
|
/*
|
|
* Allow relazifying functions in compartments that are active. This is
|
|
* only used by the relazifyFunctions() testing function.
|
|
*/
|
|
bool allowRelazificationForTesting;
|
|
|
|
/* Linked list of all Debugger objects in the runtime. */
|
|
mozilla::LinkedList<js::Debugger> debuggerList;
|
|
|
|
/*
|
|
* Head of circular list of all enabled Debuggers that have
|
|
* onNewGlobalObject handler methods established.
|
|
*/
|
|
JSCList onNewGlobalObjectWatchers;
|
|
|
|
#if defined(XP_DARWIN)
|
|
js::wasm::MachExceptionHandler wasmMachExceptionHandler;
|
|
#endif
|
|
|
|
private:
|
|
js::FreeOp* defaultFreeOp_;
|
|
|
|
public:
|
|
js::FreeOp* defaultFreeOp() {
|
|
MOZ_ASSERT(defaultFreeOp_);
|
|
return defaultFreeOp_;
|
|
}
|
|
|
|
uint32_t debuggerMutations;
|
|
|
|
const JSSecurityCallbacks* securityCallbacks;
|
|
const js::DOMCallbacks* DOMcallbacks;
|
|
JSDestroyPrincipalsOp destroyPrincipals;
|
|
JSReadPrincipalsOp readPrincipals;
|
|
|
|
/* Optional warning reporter. */
|
|
JS::WarningReporter warningReporter;
|
|
|
|
JS::BuildIdOp buildIdOp;
|
|
|
|
/* AsmJSCache callbacks are runtime-wide. */
|
|
JS::AsmJSCacheOps asmJSCacheOps;
|
|
|
|
/*
|
|
* The propertyRemovals counter is incremented for every JSObject::clear,
|
|
* and for each JSObject::remove method call that frees a slot in the given
|
|
* object. See js_NativeGet and js_NativeSet in jsobj.cpp.
|
|
*/
|
|
uint32_t propertyRemovals;
|
|
|
|
private:
|
|
mozilla::Maybe<js::SharedImmutableStringsCache> sharedImmutableStrings_;
|
|
|
|
public:
|
|
// If this particular JSRuntime has a SharedImmutableStringsCache, return a
|
|
// pointer to it, otherwise return nullptr.
|
|
js::SharedImmutableStringsCache* maybeThisRuntimeSharedImmutableStrings() {
|
|
return sharedImmutableStrings_.isSome() ? &*sharedImmutableStrings_ : nullptr;
|
|
}
|
|
|
|
// Get a reference to this JSRuntime's or its parent's
|
|
// SharedImmutableStringsCache.
|
|
js::SharedImmutableStringsCache& sharedImmutableStrings() {
|
|
MOZ_ASSERT_IF(parentRuntime, !sharedImmutableStrings_);
|
|
MOZ_ASSERT_IF(!parentRuntime, sharedImmutableStrings_);
|
|
return parentRuntime ? parentRuntime->sharedImmutableStrings() : *sharedImmutableStrings_;
|
|
}
|
|
|
|
// Count of AutoKeepAtoms instances on the main thread's stack. When any
|
|
// instances exist, atoms in the runtime will not be collected. Threads
|
|
// with an ExclusiveContext do not increment this value, but the presence
|
|
// of any such threads also inhibits collection of atoms. We don't scan the
|
|
// stacks of exclusive threads, so we need to avoid collecting their
|
|
// objects in another way. The only GC thing pointers they have are to
|
|
// their exclusive compartment (which is not collected) or to the atoms
|
|
// compartment. Therefore, we avoid collecting the atoms compartment when
|
|
// exclusive threads are running.
|
|
private:
|
|
unsigned keepAtoms_;
|
|
friend class js::AutoKeepAtoms;
|
|
public:
|
|
bool keepAtoms() {
|
|
return keepAtoms_ != 0 || exclusiveThreadsPresent();
|
|
}
|
|
|
|
private:
|
|
const JSPrincipals* trustedPrincipals_;
|
|
public:
|
|
void setTrustedPrincipals(const JSPrincipals* p) { trustedPrincipals_ = p; }
|
|
const JSPrincipals* trustedPrincipals() const { return trustedPrincipals_; }
|
|
|
|
private:
|
|
bool beingDestroyed_;
|
|
public:
|
|
bool isBeingDestroyed() const {
|
|
return beingDestroyed_;
|
|
}
|
|
|
|
private:
|
|
// Set of all atoms other than those in permanentAtoms and staticStrings.
|
|
// Reading or writing this set requires the calling thread to have an
|
|
// ExclusiveContext and hold a lock. Use AutoLockForExclusiveAccess.
|
|
js::AtomSet* atoms_;
|
|
|
|
// Compartment and associated zone containing all atoms in the runtime, as
|
|
// well as runtime wide IonCode stubs. Modifying the contents of this
|
|
// compartment requires the calling thread to have an ExclusiveContext and
|
|
// hold a lock. Use AutoLockForExclusiveAccess.
|
|
JSCompartment* atomsCompartment_;
|
|
|
|
// Set of all live symbols produced by Symbol.for(). All such symbols are
|
|
// allocated in the atomsCompartment. Reading or writing the symbol
|
|
// registry requires the calling thread to have an ExclusiveContext and
|
|
// hold a lock. Use AutoLockForExclusiveAccess.
|
|
js::SymbolRegistry symbolRegistry_;
|
|
|
|
public:
|
|
bool initializeAtoms(JSContext* cx);
|
|
void finishAtoms();
|
|
bool atomsAreFinished() const { return !atoms_; }
|
|
|
|
void sweepAtoms();
|
|
|
|
js::AtomSet& atoms(js::AutoLockForExclusiveAccess& lock) {
|
|
return *atoms_;
|
|
}
|
|
JSCompartment* atomsCompartment(js::AutoLockForExclusiveAccess& lock) {
|
|
return atomsCompartment_;
|
|
}
|
|
|
|
bool isAtomsCompartment(JSCompartment* comp) {
|
|
return comp == atomsCompartment_;
|
|
}
|
|
|
|
// The atoms compartment is the only one in its zone.
|
|
inline bool isAtomsZone(const JS::Zone* zone) const;
|
|
|
|
bool activeGCInAtomsZone();
|
|
|
|
js::SymbolRegistry& symbolRegistry(js::AutoLockForExclusiveAccess& lock) {
|
|
return symbolRegistry_;
|
|
}
|
|
|
|
// Permanent atoms are fixed during initialization of the runtime and are
|
|
// not modified or collected until the runtime is destroyed. These may be
|
|
// shared with another, longer living runtime through |parentRuntime| and
|
|
// can be freely accessed with no locking necessary.
|
|
|
|
// Permanent atoms pre-allocated for general use.
|
|
js::StaticStrings* staticStrings;
|
|
|
|
// Cached pointers to various permanent property names.
|
|
JSAtomState* commonNames;
|
|
|
|
// All permanent atoms in the runtime, other than those in staticStrings.
|
|
// Unlike |atoms_|, access to this does not require
|
|
// AutoLockForExclusiveAccess because it is frozen and thus read-only.
|
|
js::FrozenAtomSet* permanentAtoms;
|
|
|
|
bool transformToPermanentAtoms(JSContext* cx);
|
|
|
|
// Cached well-known symbols (ES6 rev 24 6.1.5.1). Like permanent atoms,
|
|
// these are shared with the parentRuntime, if any.
|
|
js::WellKnownSymbols* wellKnownSymbols;
|
|
|
|
const JSWrapObjectCallbacks* wrapObjectCallbacks;
|
|
js::PreserveWrapperCallback preserveWrapperCallback;
|
|
|
|
// Table of bytecode and other data that may be shared across scripts
|
|
// within the runtime. This may be modified by threads with an
|
|
// ExclusiveContext and requires a lock.
|
|
private:
|
|
js::ScriptDataTable scriptDataTable_;
|
|
public:
|
|
js::ScriptDataTable& scriptDataTable(js::AutoLockForExclusiveAccess& lock) {
|
|
return scriptDataTable_;
|
|
}
|
|
|
|
bool jitSupportsFloatingPoint;
|
|
bool jitSupportsUnalignedAccesses;
|
|
bool jitSupportsSimd;
|
|
|
|
// Cache for jit::GetPcScript().
|
|
js::jit::PcScriptCache* ionPcScriptCache;
|
|
|
|
js::ScriptEnvironmentPreparer* scriptEnvironmentPreparer;
|
|
|
|
js::CTypesActivityCallback ctypesActivityCallback;
|
|
|
|
private:
|
|
static mozilla::Atomic<size_t> liveRuntimesCount;
|
|
|
|
public:
|
|
static bool hasLiveRuntimes() {
|
|
return liveRuntimesCount > 0;
|
|
}
|
|
|
|
protected:
|
|
explicit JSRuntime(JSRuntime* parentRuntime);
|
|
|
|
// destroyRuntime is used instead of a destructor, to ensure the downcast
|
|
// to JSContext remains valid. The final GC triggered here depends on this.
|
|
void destroyRuntime();
|
|
|
|
bool init(uint32_t maxbytes, uint32_t maxNurseryBytes);
|
|
|
|
JSRuntime* thisFromCtor() { return this; }
|
|
|
|
public:
|
|
/*
|
|
* Call this after allocating memory held by GC things, to update memory
|
|
* pressure counters or report the OOM error if necessary. If oomError and
|
|
* cx is not null the function also reports OOM error.
|
|
*
|
|
* The function must be called outside the GC lock and in case of OOM error
|
|
* the caller must ensure that no deadlock possible during OOM reporting.
|
|
*/
|
|
void updateMallocCounter(size_t nbytes);
|
|
void updateMallocCounter(JS::Zone* zone, size_t nbytes);
|
|
|
|
void reportAllocationOverflow() { js::ReportAllocationOverflow(nullptr); }
|
|
|
|
/*
|
|
* This should be called after system malloc/calloc/realloc returns nullptr
|
|
* to try to recove some memory or to report an error. For realloc, the
|
|
* original pointer must be passed as reallocPtr.
|
|
*
|
|
* The function must be called outside the GC lock.
|
|
*/
|
|
JS_FRIEND_API(void*) onOutOfMemory(js::AllocFunction allocator, size_t nbytes,
|
|
void* reallocPtr = nullptr, JSContext* maybecx = nullptr);
|
|
|
|
/* onOutOfMemory but can call the largeAllocationFailureCallback. */
|
|
JS_FRIEND_API(void*) onOutOfMemoryCanGC(js::AllocFunction allocator, size_t nbytes,
|
|
void* reallocPtr = nullptr);
|
|
|
|
void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes* runtime);
|
|
|
|
private:
|
|
const js::Class* windowProxyClass_;
|
|
|
|
// Settings for how helper threads can be used.
|
|
bool offthreadIonCompilationEnabled_;
|
|
bool parallelParsingEnabled_;
|
|
|
|
bool autoWritableJitCodeActive_;
|
|
|
|
public:
|
|
|
|
// Note: these values may be toggled dynamically (in response to about:config
|
|
// prefs changing).
|
|
void setOffthreadIonCompilationEnabled(bool value) {
|
|
offthreadIonCompilationEnabled_ = value;
|
|
}
|
|
bool canUseOffthreadIonCompilation() const {
|
|
return offthreadIonCompilationEnabled_;
|
|
}
|
|
void setParallelParsingEnabled(bool value) {
|
|
parallelParsingEnabled_ = value;
|
|
}
|
|
bool canUseParallelParsing() const {
|
|
return parallelParsingEnabled_;
|
|
}
|
|
|
|
void toggleAutoWritableJitCodeActive(bool b) {
|
|
MOZ_ASSERT(autoWritableJitCodeActive_ != b, "AutoWritableJitCode should not be nested.");
|
|
MOZ_ASSERT(CurrentThreadCanAccessRuntime(this));
|
|
autoWritableJitCodeActive_ = b;
|
|
}
|
|
|
|
const js::Class* maybeWindowProxyClass() const {
|
|
return windowProxyClass_;
|
|
}
|
|
void setWindowProxyClass(const js::Class* clasp) {
|
|
windowProxyClass_ = clasp;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
public:
|
|
js::AutoEnterPolicy* enteredPolicy;
|
|
#endif
|
|
|
|
/* See comment for JS::SetLargeAllocationFailureCallback in jsapi.h. */
|
|
JS::LargeAllocationFailureCallback largeAllocationFailureCallback;
|
|
void* largeAllocationFailureCallbackData;
|
|
|
|
/* See comment for JS::SetOutOfMemoryCallback in jsapi.h. */
|
|
JS::OutOfMemoryCallback oomCallback;
|
|
void* oomCallbackData;
|
|
|
|
/*
|
|
* These variations of malloc/calloc/realloc will call the
|
|
* large-allocation-failure callback on OOM and retry the allocation.
|
|
*/
|
|
|
|
static const unsigned LARGE_ALLOCATION = 25 * 1024 * 1024;
|
|
|
|
template <typename T>
|
|
T* pod_callocCanGC(size_t numElems) {
|
|
T* p = pod_calloc<T>(numElems);
|
|
if (MOZ_LIKELY(!!p))
|
|
return p;
|
|
size_t bytes;
|
|
if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes))) {
|
|
reportAllocationOverflow();
|
|
return nullptr;
|
|
}
|
|
return static_cast<T*>(onOutOfMemoryCanGC(js::AllocFunction::Calloc, bytes));
|
|
}
|
|
|
|
template <typename T>
|
|
T* pod_reallocCanGC(T* p, size_t oldSize, size_t newSize) {
|
|
T* p2 = pod_realloc<T>(p, oldSize, newSize);
|
|
if (MOZ_LIKELY(!!p2))
|
|
return p2;
|
|
size_t bytes;
|
|
if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(newSize, &bytes))) {
|
|
reportAllocationOverflow();
|
|
return nullptr;
|
|
}
|
|
return static_cast<T*>(onOutOfMemoryCanGC(js::AllocFunction::Realloc, bytes, p));
|
|
}
|
|
|
|
/*
|
|
* Debugger.Memory functions like takeCensus use this embedding-provided
|
|
* function to assess the size of malloc'd blocks of memory.
|
|
*/
|
|
mozilla::MallocSizeOf debuggerMallocSizeOf;
|
|
|
|
/* Last time at which an animation was played for this runtime. */
|
|
int64_t lastAnimationTime;
|
|
|
|
public:
|
|
js::PerformanceMonitoring performanceMonitoring;
|
|
|
|
private:
|
|
/* List of Ion compilation waiting to get linked. */
|
|
typedef mozilla::LinkedList<js::jit::IonBuilder> IonBuilderList;
|
|
|
|
IonBuilderList ionLazyLinkList_;
|
|
size_t ionLazyLinkListSize_;
|
|
|
|
public:
|
|
IonBuilderList& ionLazyLinkList();
|
|
|
|
size_t ionLazyLinkListSize() {
|
|
return ionLazyLinkListSize_;
|
|
}
|
|
|
|
void ionLazyLinkListRemove(js::jit::IonBuilder* builder);
|
|
void ionLazyLinkListAdd(js::jit::IonBuilder* builder);
|
|
|
|
private:
|
|
/* The stack format for the current runtime. Only valid on non-child
|
|
* runtimes. */
|
|
mozilla::Atomic<js::StackFormat, mozilla::ReleaseAcquire> stackFormat_;
|
|
|
|
public:
|
|
js::StackFormat stackFormat() const {
|
|
const JSRuntime* rt = this;
|
|
while (rt->parentRuntime) {
|
|
MOZ_ASSERT(rt->stackFormat_ == js::StackFormat::Default);
|
|
rt = rt->parentRuntime;
|
|
}
|
|
MOZ_ASSERT(rt->stackFormat_ != js::StackFormat::Default);
|
|
return rt->stackFormat_;
|
|
}
|
|
void setStackFormat(js::StackFormat format) {
|
|
MOZ_ASSERT(!parentRuntime);
|
|
MOZ_ASSERT(format != js::StackFormat::Default);
|
|
stackFormat_ = format;
|
|
}
|
|
|
|
// For inherited heap state accessors.
|
|
friend class js::gc::AutoTraceSession;
|
|
friend class JS::AutoEnterCycleCollection;
|
|
|
|
// The implementation-defined abstract operation HostResolveImportedModule.
|
|
JS::ModuleResolveHook moduleResolveHook;
|
|
};
|
|
|
|
namespace js {
|
|
|
|
static inline JSContext*
|
|
GetJSContextFromMainThread()
|
|
{
|
|
return js::TlsPerThreadData.get()->contextFromMainThread();
|
|
}
|
|
|
|
/*
|
|
* Flags accompany script version data so that a) dynamically created scripts
|
|
* can inherit their caller's compile-time properties and b) scripts can be
|
|
* appropriately compared in the eval cache across global option changes. An
|
|
* example of the latter is enabling the top-level-anonymous-function-is-error
|
|
* option: subsequent evals of the same, previously-valid script text may have
|
|
* become invalid.
|
|
*/
|
|
namespace VersionFlags {
|
|
static const unsigned MASK = 0x0FFF; /* see JSVersion in jspubtd.h */
|
|
} /* namespace VersionFlags */
|
|
|
|
static inline JSVersion
|
|
VersionNumber(JSVersion version)
|
|
{
|
|
return JSVersion(uint32_t(version) & VersionFlags::MASK);
|
|
}
|
|
|
|
static inline JSVersion
|
|
VersionExtractFlags(JSVersion version)
|
|
{
|
|
return JSVersion(uint32_t(version) & ~VersionFlags::MASK);
|
|
}
|
|
|
|
static inline void
|
|
VersionCopyFlags(JSVersion* version, JSVersion from)
|
|
{
|
|
*version = JSVersion(VersionNumber(*version) | VersionExtractFlags(from));
|
|
}
|
|
|
|
static inline bool
|
|
VersionHasFlags(JSVersion version)
|
|
{
|
|
return !!VersionExtractFlags(version);
|
|
}
|
|
|
|
static inline bool
|
|
VersionIsKnown(JSVersion version)
|
|
{
|
|
return VersionNumber(version) != JSVERSION_UNKNOWN;
|
|
}
|
|
|
|
inline void
|
|
FreeOp::free_(void* p)
|
|
{
|
|
js_free(p);
|
|
}
|
|
|
|
inline void
|
|
FreeOp::freeLater(void* p)
|
|
{
|
|
// FreeOps other than the defaultFreeOp() are constructed on the stack,
|
|
// and won't hold onto the pointers to free indefinitely.
|
|
MOZ_ASSERT(!isDefaultFreeOp());
|
|
|
|
AutoEnterOOMUnsafeRegion oomUnsafe;
|
|
if (!freeLaterList.append(p))
|
|
oomUnsafe.crash("FreeOp::freeLater");
|
|
}
|
|
|
|
inline bool
|
|
FreeOp::appendJitPoisonRange(const jit::JitPoisonRange& range)
|
|
{
|
|
// FreeOps other than the defaultFreeOp() are constructed on the stack,
|
|
// and won't hold onto the pointers to free indefinitely.
|
|
MOZ_ASSERT(!isDefaultFreeOp());
|
|
|
|
return jitPoisonRanges.append(range);
|
|
}
|
|
|
|
/*
|
|
* RAII class that takes the GC lock while it is live.
|
|
*
|
|
* Note that the lock may be temporarily released by use of AutoUnlockGC when
|
|
* passed a non-const reference to this class.
|
|
*/
|
|
class MOZ_RAII AutoLockGC
|
|
{
|
|
public:
|
|
explicit AutoLockGC(JSRuntime* rt
|
|
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
|
: runtime_(rt)
|
|
{
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
lock();
|
|
}
|
|
|
|
~AutoLockGC() {
|
|
unlock();
|
|
}
|
|
|
|
void lock() {
|
|
MOZ_ASSERT(lockGuard_.isNothing());
|
|
lockGuard_.emplace(runtime_->gc.lock);
|
|
}
|
|
|
|
void unlock() {
|
|
MOZ_ASSERT(lockGuard_.isSome());
|
|
lockGuard_.reset();
|
|
}
|
|
|
|
js::LockGuard<js::Mutex>& guard() {
|
|
return lockGuard_.ref();
|
|
}
|
|
|
|
private:
|
|
JSRuntime* runtime_;
|
|
mozilla::Maybe<js::LockGuard<js::Mutex>> lockGuard_;
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
|
|
AutoLockGC(const AutoLockGC&) = delete;
|
|
AutoLockGC& operator=(const AutoLockGC&) = delete;
|
|
};
|
|
|
|
class MOZ_RAII AutoUnlockGC
|
|
{
|
|
public:
|
|
explicit AutoUnlockGC(AutoLockGC& lock
|
|
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
|
: lock(lock)
|
|
{
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
lock.unlock();
|
|
}
|
|
|
|
~AutoUnlockGC() {
|
|
lock.lock();
|
|
}
|
|
|
|
private:
|
|
AutoLockGC& lock;
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
|
|
AutoUnlockGC(const AutoUnlockGC&) = delete;
|
|
AutoUnlockGC& operator=(const AutoUnlockGC&) = delete;
|
|
};
|
|
|
|
class MOZ_RAII AutoKeepAtoms
|
|
{
|
|
PerThreadData* pt;
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
|
|
public:
|
|
explicit AutoKeepAtoms(PerThreadData* pt
|
|
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
|
: pt(pt)
|
|
{
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
if (JSRuntime* rt = pt->runtimeIfOnOwnerThread()) {
|
|
rt->keepAtoms_++;
|
|
} else {
|
|
// This should be a thread with an exclusive context, which will
|
|
// always inhibit collection of atoms.
|
|
MOZ_ASSERT(pt->exclusiveThreadsPresent());
|
|
}
|
|
}
|
|
~AutoKeepAtoms() {
|
|
if (JSRuntime* rt = pt->runtimeIfOnOwnerThread()) {
|
|
MOZ_ASSERT(rt->keepAtoms_);
|
|
rt->keepAtoms_--;
|
|
if (rt->gc.fullGCForAtomsRequested() && !rt->keepAtoms())
|
|
rt->gc.triggerFullGCForAtoms();
|
|
}
|
|
}
|
|
};
|
|
|
|
inline JSRuntime*
|
|
PerThreadData::runtimeFromMainThread()
|
|
{
|
|
MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
|
|
return runtime_;
|
|
}
|
|
|
|
inline JSRuntime*
|
|
PerThreadData::runtimeIfOnOwnerThread()
|
|
{
|
|
return (runtime_ && CurrentThreadCanAccessRuntime(runtime_)) ? runtime_ : nullptr;
|
|
}
|
|
|
|
inline bool
|
|
PerThreadData::exclusiveThreadsPresent()
|
|
{
|
|
return runtime_->exclusiveThreadsPresent();
|
|
}
|
|
|
|
/************************************************************************/
|
|
|
|
static MOZ_ALWAYS_INLINE void
|
|
MakeRangeGCSafe(Value* vec, size_t len)
|
|
{
|
|
// Don't PodZero here because JS::Value is non-trivial.
|
|
for (size_t i = 0; i < len; i++)
|
|
vec[i].setDouble(+0.0);
|
|
}
|
|
|
|
static MOZ_ALWAYS_INLINE void
|
|
MakeRangeGCSafe(Value* beg, Value* end)
|
|
{
|
|
MakeRangeGCSafe(beg, end - beg);
|
|
}
|
|
|
|
static MOZ_ALWAYS_INLINE void
|
|
MakeRangeGCSafe(jsid* beg, jsid* end)
|
|
{
|
|
std::fill(beg, end, INT_TO_JSID(0));
|
|
}
|
|
|
|
static MOZ_ALWAYS_INLINE void
|
|
MakeRangeGCSafe(jsid* vec, size_t len)
|
|
{
|
|
MakeRangeGCSafe(vec, vec + len);
|
|
}
|
|
|
|
static MOZ_ALWAYS_INLINE void
|
|
MakeRangeGCSafe(Shape** beg, Shape** end)
|
|
{
|
|
std::fill(beg, end, nullptr);
|
|
}
|
|
|
|
static MOZ_ALWAYS_INLINE void
|
|
MakeRangeGCSafe(Shape** vec, size_t len)
|
|
{
|
|
MakeRangeGCSafe(vec, vec + len);
|
|
}
|
|
|
|
static MOZ_ALWAYS_INLINE void
|
|
SetValueRangeToUndefined(Value* beg, Value* end)
|
|
{
|
|
for (Value* v = beg; v != end; ++v)
|
|
v->setUndefined();
|
|
}
|
|
|
|
static MOZ_ALWAYS_INLINE void
|
|
SetValueRangeToUndefined(Value* vec, size_t len)
|
|
{
|
|
SetValueRangeToUndefined(vec, vec + len);
|
|
}
|
|
|
|
static MOZ_ALWAYS_INLINE void
|
|
SetValueRangeToNull(Value* beg, Value* end)
|
|
{
|
|
for (Value* v = beg; v != end; ++v)
|
|
v->setNull();
|
|
}
|
|
|
|
static MOZ_ALWAYS_INLINE void
|
|
SetValueRangeToNull(Value* vec, size_t len)
|
|
{
|
|
SetValueRangeToNull(vec, vec + len);
|
|
}
|
|
|
|
/*
|
|
* Allocation policy that uses JSRuntime::pod_malloc and friends, so that
|
|
* memory pressure is properly accounted for. This is suitable for
|
|
* long-lived objects owned by the JSRuntime.
|
|
*
|
|
* Since it doesn't hold a JSContext (those may not live long enough), it
|
|
* can't report out-of-memory conditions itself; the caller must check for
|
|
* OOM and take the appropriate action.
|
|
*
|
|
* FIXME bug 647103 - replace these *AllocPolicy names.
|
|
*/
|
|
class RuntimeAllocPolicy
|
|
{
|
|
JSRuntime* const runtime;
|
|
|
|
public:
|
|
MOZ_IMPLICIT RuntimeAllocPolicy(JSRuntime* rt) : runtime(rt) {}
|
|
|
|
template <typename T>
|
|
T* maybe_pod_malloc(size_t numElems) {
|
|
return runtime->maybe_pod_malloc<T>(numElems);
|
|
}
|
|
|
|
template <typename T>
|
|
T* maybe_pod_calloc(size_t numElems) {
|
|
return runtime->maybe_pod_calloc<T>(numElems);
|
|
}
|
|
|
|
template <typename T>
|
|
T* maybe_pod_realloc(T* p, size_t oldSize, size_t newSize) {
|
|
return runtime->maybe_pod_realloc<T>(p, oldSize, newSize);
|
|
}
|
|
|
|
template <typename T>
|
|
T* pod_malloc(size_t numElems) {
|
|
return runtime->pod_malloc<T>(numElems);
|
|
}
|
|
|
|
template <typename T>
|
|
T* pod_calloc(size_t numElems) {
|
|
return runtime->pod_calloc<T>(numElems);
|
|
}
|
|
|
|
template <typename T>
|
|
T* pod_realloc(T* p, size_t oldSize, size_t newSize) {
|
|
return runtime->pod_realloc<T>(p, oldSize, newSize);
|
|
}
|
|
|
|
void free_(void* p) { js_free(p); }
|
|
void reportAllocOverflow() const {}
|
|
|
|
bool checkSimulatedOOM() const {
|
|
return !js::oom::ShouldFailWithOOM();
|
|
}
|
|
};
|
|
|
|
extern const JSSecurityCallbacks NullSecurityCallbacks;
|
|
|
|
// Debugging RAII class which marks the current thread as performing an Ion
|
|
// compilation, for use by CurrentThreadCan{Read,Write}CompilationData
|
|
class MOZ_RAII AutoEnterIonCompilation
|
|
{
|
|
public:
|
|
explicit AutoEnterIonCompilation(bool safeForMinorGC
|
|
MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
|
|
#ifdef DEBUG
|
|
PerThreadData* pt = js::TlsPerThreadData.get();
|
|
MOZ_ASSERT(!pt->ionCompiling);
|
|
MOZ_ASSERT(!pt->ionCompilingSafeForMinorGC);
|
|
pt->ionCompiling = true;
|
|
pt->ionCompilingSafeForMinorGC = safeForMinorGC;
|
|
#endif
|
|
}
|
|
|
|
~AutoEnterIonCompilation() {
|
|
#ifdef DEBUG
|
|
PerThreadData* pt = js::TlsPerThreadData.get();
|
|
MOZ_ASSERT(pt->ionCompiling);
|
|
pt->ionCompiling = false;
|
|
pt->ionCompilingSafeForMinorGC = false;
|
|
#endif
|
|
}
|
|
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
};
|
|
|
|
namespace gc {
|
|
|
|
// In debug builds, set/unset the performing GC flag for the current thread.
|
|
struct MOZ_RAII AutoSetThreadIsPerformingGC
|
|
{
|
|
#ifdef DEBUG
|
|
AutoSetThreadIsPerformingGC()
|
|
: threadData_(js::TlsPerThreadData.get())
|
|
{
|
|
MOZ_ASSERT(!threadData_->performingGC);
|
|
threadData_->performingGC = true;
|
|
}
|
|
|
|
~AutoSetThreadIsPerformingGC() {
|
|
MOZ_ASSERT(threadData_->performingGC);
|
|
threadData_->performingGC = false;
|
|
}
|
|
|
|
private:
|
|
PerThreadData* threadData_;
|
|
#else
|
|
AutoSetThreadIsPerformingGC() {}
|
|
#endif
|
|
};
|
|
|
|
// In debug builds, set/unset the GC sweeping flag for the current thread.
|
|
struct MOZ_RAII AutoSetThreadIsSweeping
|
|
{
|
|
#ifdef DEBUG
|
|
AutoSetThreadIsSweeping()
|
|
: threadData_(js::TlsPerThreadData.get())
|
|
{
|
|
MOZ_ASSERT(!threadData_->gcSweeping);
|
|
threadData_->gcSweeping = true;
|
|
}
|
|
|
|
~AutoSetThreadIsSweeping() {
|
|
MOZ_ASSERT(threadData_->gcSweeping);
|
|
threadData_->gcSweeping = false;
|
|
}
|
|
|
|
private:
|
|
PerThreadData* threadData_;
|
|
#else
|
|
AutoSetThreadIsSweeping() {}
|
|
#endif
|
|
};
|
|
|
|
} // namespace gc
|
|
|
|
/*
|
|
* Provides a delete policy that can be used for objects which have their
|
|
* lifetime managed by the GC and can only safely be destroyed while the nursery
|
|
* is empty.
|
|
*
|
|
* This is necessary when initializing such an object may fail after the initial
|
|
* allocation. The partially-initialized object must be destroyed, but it may
|
|
* not be safe to do so at the current time. This policy puts the object on a
|
|
* queue to be destroyed at a safe time.
|
|
*/
|
|
template <typename T>
|
|
struct GCManagedDeletePolicy
|
|
{
|
|
void operator()(const T* ptr) {
|
|
if (ptr) {
|
|
JSRuntime* rt = TlsPerThreadData.get()->runtimeIfOnOwnerThread();
|
|
if (rt && rt->gc.nursery.isEnabled()) {
|
|
// The object may contain nursery pointers and must only be
|
|
// destroyed after a minor GC.
|
|
rt->gc.callAfterMinorGC(deletePtr, const_cast<T*>(ptr));
|
|
} else {
|
|
// The object cannot contain nursery pointers so can be
|
|
// destroyed immediately.
|
|
gc::AutoSetThreadIsSweeping threadIsSweeping;
|
|
js_delete(const_cast<T*>(ptr));
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
static void deletePtr(void* data) {
|
|
js_delete(reinterpret_cast<T*>(data));
|
|
}
|
|
};
|
|
|
|
} /* namespace js */
|
|
|
|
namespace JS {
|
|
|
|
template <typename T>
|
|
struct DeletePolicy<js::GCPtr<T>> : public js::GCManagedDeletePolicy<js::GCPtr<T>>
|
|
{};
|
|
|
|
// Scope data that contain GCPtrs must use the correct DeletePolicy.
|
|
//
|
|
// This is defined here because vm/Scope.h cannot #include "vm/Runtime.h"
|
|
|
|
template <>
|
|
struct DeletePolicy<js::FunctionScope::Data>
|
|
: public js::GCManagedDeletePolicy<js::FunctionScope::Data>
|
|
{ };
|
|
|
|
template <>
|
|
struct DeletePolicy<js::ModuleScope::Data>
|
|
: public js::GCManagedDeletePolicy<js::ModuleScope::Data>
|
|
{ };
|
|
|
|
} /* namespace JS */
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(pop)
|
|
#endif
|
|
|
|
#endif /* vm_Runtime_h */
|