mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-09-28 20:56:36 +00:00
812 lines
25 KiB
C++
812 lines
25 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/. */
|
|
|
|
/* JS execution context. */
|
|
|
|
#ifndef jscntxt_h
|
|
#define jscntxt_h
|
|
|
|
#include "mozilla/MemoryReporting.h"
|
|
|
|
#include "js/TraceableVector.h"
|
|
#include "js/Utility.h"
|
|
#include "js/Vector.h"
|
|
#include "vm/Runtime.h"
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
|
|
#endif
|
|
|
|
struct DtoaState;
|
|
|
|
namespace js {
|
|
|
|
namespace jit {
|
|
class JitContext;
|
|
class DebugModeOSRVolatileJitFrameIterator;
|
|
} // namespace jit
|
|
|
|
typedef HashSet<Shape*> ShapeSet;
|
|
|
|
/* Detects cycles when traversing an object graph. */
|
|
class MOZ_RAII AutoCycleDetector
|
|
{
|
|
public:
|
|
using Set = HashSet<JSObject*, MovableCellHasher<JSObject*>>;
|
|
|
|
AutoCycleDetector(JSContext* cx, HandleObject objArg
|
|
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
|
: cx(cx), obj(cx, objArg), cyclic(true)
|
|
{
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
}
|
|
|
|
~AutoCycleDetector();
|
|
|
|
bool init();
|
|
|
|
bool foundCycle() { return cyclic; }
|
|
|
|
private:
|
|
Generation hashsetGenerationAtInit;
|
|
JSContext* cx;
|
|
RootedObject obj;
|
|
Set::AddPtr hashsetAddPointer;
|
|
bool cyclic;
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
};
|
|
|
|
/* Updates references in the cycle detection set if the GC moves them. */
|
|
extern void
|
|
TraceCycleDetectionSet(JSTracer* trc, AutoCycleDetector::Set& set);
|
|
|
|
struct AutoResolving;
|
|
|
|
namespace frontend { struct CompileError; }
|
|
|
|
/*
|
|
* Execution Context Overview:
|
|
*
|
|
* Several different structures may be used to provide a context for operations
|
|
* on the VM. Each context is thread local, but varies in what data it can
|
|
* access and what other threads may be running.
|
|
*
|
|
* - ExclusiveContext is used by threads operating in one compartment/zone,
|
|
* where other threads may operate in other compartments, but *not* the same
|
|
* compartment or zone which the ExclusiveContext is in. A thread with an
|
|
* ExclusiveContext may enter the atoms compartment and atomize strings, in
|
|
* which case a lock is used.
|
|
*
|
|
* - JSContext is used only by the runtime's main thread. The context may
|
|
* operate in any compartment or zone which is not used by an ExclusiveContext,
|
|
* and will only run in parallel with threads using such contexts.
|
|
*
|
|
* A JSContext coerces to an ExclusiveContext.
|
|
*/
|
|
|
|
struct HelperThread;
|
|
|
|
class ExclusiveContext : public ContextFriendFields,
|
|
public MallocProvider<ExclusiveContext>
|
|
{
|
|
friend class gc::ArenaLists;
|
|
friend class AutoCompartment;
|
|
friend class AutoLockForExclusiveAccess;
|
|
friend struct StackBaseShape;
|
|
friend void JSScript::initCompartment(ExclusiveContext* cx);
|
|
friend class jit::JitContext;
|
|
friend class Activation;
|
|
|
|
// The thread on which this context is running, if this is not a JSContext.
|
|
HelperThread* helperThread_;
|
|
|
|
public:
|
|
enum ContextKind {
|
|
Context_JS,
|
|
Context_Exclusive
|
|
};
|
|
|
|
private:
|
|
ContextKind contextKind_;
|
|
|
|
public:
|
|
PerThreadData* perThreadData;
|
|
|
|
ExclusiveContext(JSRuntime* rt, PerThreadData* pt, ContextKind kind);
|
|
|
|
bool isJSContext() const {
|
|
return contextKind_ == Context_JS;
|
|
}
|
|
|
|
JSContext* maybeJSContext() const {
|
|
if (isJSContext())
|
|
return (JSContext*) this;
|
|
return nullptr;
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
JSRuntime* ecRuntime() const { return runtime_; } // TenFourFox issue 391
|
|
|
|
bool runtimeMatches(JSRuntime* rt) const {
|
|
return runtime_ == rt;
|
|
}
|
|
|
|
protected:
|
|
js::gc::ArenaLists* arenas_;
|
|
|
|
public:
|
|
inline js::gc::ArenaLists* arenas() const { return arenas_; }
|
|
|
|
template <typename T>
|
|
bool isInsideCurrentZone(T thing) const {
|
|
return thing->zoneFromAnyThread() == zone_;
|
|
}
|
|
|
|
template <typename T>
|
|
inline bool isInsideCurrentCompartment(T thing) const {
|
|
return thing->compartment() == compartment_;
|
|
}
|
|
|
|
void* onOutOfMemory(js::AllocFunction allocFunc, size_t nbytes, void* reallocPtr = nullptr) {
|
|
if (!isJSContext())
|
|
return nullptr;
|
|
return runtime_->onOutOfMemory(allocFunc, nbytes, reallocPtr, asJSContext());
|
|
}
|
|
|
|
/* 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.
|
|
JSAtomState& names() { return *runtime_->commonNames; }
|
|
StaticStrings& staticStrings() { return *runtime_->staticStrings; }
|
|
bool isPermanentAtomsInitialized() { return !!runtime_->permanentAtoms; }
|
|
FrozenAtomSet& permanentAtoms() { return *runtime_->permanentAtoms; }
|
|
WellKnownSymbols& wellKnownSymbols() { return *runtime_->wellKnownSymbols; }
|
|
const JS::AsmJSCacheOps& asmJSCacheOps() { return runtime_->asmJSCacheOps; }
|
|
PropertyName* emptyString() { return runtime_->emptyString; }
|
|
FreeOp* defaultFreeOp() { return runtime_->defaultFreeOp(); }
|
|
void* runtimeAddressForJit() { return runtime_; }
|
|
void* runtimeAddressOfInterruptUint32() { return runtime_->addressOfInterruptUint32(); }
|
|
void* stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; }
|
|
void* stackLimitAddressForJitCode(StackKind kind);
|
|
uintptr_t stackLimit(StackKind kind) { return runtime_->mainThread.nativeStackLimit[kind]; }
|
|
size_t gcSystemPageSize() { return gc::SystemPageSize(); }
|
|
bool canUseSignalHandlers() const { return runtime_->canUseSignalHandlers(); }
|
|
bool jitSupportsFloatingPoint() const { return runtime_->jitSupportsFloatingPoint; }
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* "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);
|
|
public:
|
|
bool hasEnteredCompartment() const {
|
|
return enterCompartmentDepth_ > 0;
|
|
}
|
|
#ifdef DEBUG
|
|
unsigned getEnterCompartmentDepth() const {
|
|
return enterCompartmentDepth_;
|
|
}
|
|
#endif
|
|
|
|
inline void enterCompartment(JSCompartment* c);
|
|
inline void enterNullCompartment();
|
|
inline void leaveCompartment(JSCompartment* oldCompartment);
|
|
|
|
void setHelperThread(HelperThread* helperThread);
|
|
HelperThread* helperThread() const { return helperThread_; }
|
|
|
|
// Threads with an ExclusiveContext may freely access any data in their
|
|
// compartment and zone.
|
|
JSCompartment* compartment() const {
|
|
MOZ_ASSERT_IF(runtime_->isAtomsCompartment(compartment_),
|
|
runtime_->currentThreadHasExclusiveAccess());
|
|
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<js::GlobalObject*> global() const;
|
|
|
|
// Methods to access runtime data that must be protected by locks.
|
|
frontend::ParseMapPool& parseMapPool() {
|
|
return runtime_->parseMapPool();
|
|
}
|
|
AtomSet& atoms() {
|
|
return runtime_->atoms();
|
|
}
|
|
JSCompartment* atomsCompartment() {
|
|
return runtime_->atomsCompartment();
|
|
}
|
|
SymbolRegistry& symbolRegistry() {
|
|
return runtime_->symbolRegistry();
|
|
}
|
|
ScriptDataTable& scriptDataTable() {
|
|
return runtime_->scriptDataTable();
|
|
}
|
|
|
|
// Methods specific to any HelperThread for the context.
|
|
frontend::CompileError& addPendingCompileError();
|
|
void addPendingOverRecursed();
|
|
};
|
|
|
|
} /* namespace js */
|
|
|
|
struct JSContext : public js::ExclusiveContext,
|
|
public mozilla::LinkedListElement<JSContext>
|
|
{
|
|
explicit JSContext(JSRuntime* rt);
|
|
~JSContext();
|
|
|
|
JSRuntime* runtime() const { return runtime_; }
|
|
js::PerThreadData& mainThread() const { return runtime()->mainThread; }
|
|
|
|
static size_t offsetOfRuntime() {
|
|
return offsetof(JSContext, runtime_);
|
|
}
|
|
static size_t offsetOfCompartment() {
|
|
return offsetof(JSContext, compartment_);
|
|
}
|
|
|
|
friend class js::ExclusiveContext;
|
|
friend class JS::AutoSaveExceptionState;
|
|
friend class js::jit::DebugModeOSRVolatileJitFrameIterator;
|
|
friend void js::ReportOverRecursed(JSContext*);
|
|
|
|
private:
|
|
/* Exception state -- the exception member is a GC root by definition. */
|
|
bool throwing; /* is there a pending exception? */
|
|
JS::PersistentRooted<JS::Value> unwrappedException_; /* most-recently-thrown exception */
|
|
|
|
/* Per-context options. */
|
|
JS::ContextOptions options_;
|
|
|
|
// True if the exception currently being thrown is by result of
|
|
// ReportOverRecursed. See Debugger::slowPathOnExceptionUnwind.
|
|
bool overRecursed_;
|
|
|
|
// True if propagating a forced return from an interrupt handler during
|
|
// debug mode.
|
|
bool propagatingForcedReturn_;
|
|
|
|
// A stack of live iterators that need to be updated in case of debug mode
|
|
// OSR.
|
|
js::jit::DebugModeOSRVolatileJitFrameIterator* liveVolatileJitFrameIterators_;
|
|
|
|
public:
|
|
int32_t reportGranularity; /* see vm/Probes.h */
|
|
|
|
js::AutoResolving* resolvingList;
|
|
|
|
/* True if generating an error, to prevent runaway recursion. */
|
|
bool generatingError;
|
|
|
|
/* See JS_SaveFrameChain/JS_RestoreFrameChain. */
|
|
private:
|
|
struct SavedFrameChain {
|
|
SavedFrameChain(JSCompartment* comp, unsigned count)
|
|
: compartment(comp), enterCompartmentCount(count) {}
|
|
JSCompartment* compartment;
|
|
unsigned enterCompartmentCount;
|
|
};
|
|
typedef js::Vector<SavedFrameChain, 1, js::SystemAllocPolicy> SaveStack;
|
|
SaveStack savedFrameChains_;
|
|
public:
|
|
bool saveFrameChain();
|
|
void restoreFrameChain();
|
|
|
|
public:
|
|
/* State for object and array toSource conversion. */
|
|
js::AutoCycleDetector::Set cycleDetectorSet;
|
|
|
|
/* Client opaque pointers. */
|
|
void* data;
|
|
void* data2;
|
|
|
|
public:
|
|
|
|
/*
|
|
* Return:
|
|
* - The newest scripted frame's version, if there is such a frame.
|
|
* - The version from the compartment.
|
|
* - The default version.
|
|
*
|
|
* Note: if this ever shows up in a profile, just add caching!
|
|
*/
|
|
JSVersion findVersion() const;
|
|
|
|
const JS::ContextOptions& options() const {
|
|
return options_;
|
|
}
|
|
|
|
JS::ContextOptions& options() {
|
|
return options_;
|
|
}
|
|
|
|
js::LifoAlloc& tempLifoAlloc() { return runtime()->tempLifoAlloc; }
|
|
|
|
unsigned outstandingRequests;/* number of JS_BeginRequest calls
|
|
without the corresponding
|
|
JS_EndRequest. */
|
|
|
|
bool jitIsBroken;
|
|
|
|
void updateJITEnabled();
|
|
|
|
/* Whether this context has JS frames on the stack. */
|
|
bool currentlyRunning() const;
|
|
|
|
bool currentlyRunningInInterpreter() const {
|
|
return runtime_->activation()->isInterpreter();
|
|
}
|
|
bool currentlyRunningInJit() const {
|
|
return runtime_->activation()->isJit();
|
|
}
|
|
js::InterpreterFrame* interpreterFrame() const {
|
|
return runtime_->activation()->asInterpreter()->current();
|
|
}
|
|
js::InterpreterRegs& interpreterRegs() const {
|
|
return runtime_->activation()->asInterpreter()->regs();
|
|
}
|
|
|
|
/*
|
|
* Get the topmost script and optional pc on the stack. By default, this
|
|
* function only returns a JSScript in the current compartment, returning
|
|
* nullptr if the current script is in a different compartment. This
|
|
* behavior can be overridden by passing ALLOW_CROSS_COMPARTMENT.
|
|
*/
|
|
enum MaybeAllowCrossCompartment {
|
|
DONT_ALLOW_CROSS_COMPARTMENT = false,
|
|
ALLOW_CROSS_COMPARTMENT = true
|
|
};
|
|
inline JSScript* currentScript(jsbytecode** pc = nullptr,
|
|
MaybeAllowCrossCompartment = DONT_ALLOW_CROSS_COMPARTMENT) const;
|
|
|
|
// The generational GC nursery may only be used on the main thread.
|
|
inline js::Nursery& nursery() {
|
|
return runtime_->gc.nursery;
|
|
}
|
|
|
|
void minorGC(JS::gcreason::Reason reason) {
|
|
runtime_->gc.minorGC(this, reason);
|
|
}
|
|
|
|
public:
|
|
bool isExceptionPending() {
|
|
return throwing;
|
|
}
|
|
|
|
MOZ_WARN_UNUSED_RESULT
|
|
bool getPendingException(JS::MutableHandleValue rval);
|
|
|
|
bool isThrowingOutOfMemory();
|
|
bool isClosingGenerator();
|
|
|
|
void setPendingException(js::Value v);
|
|
|
|
void clearPendingException() {
|
|
throwing = false;
|
|
overRecursed_ = false;
|
|
unwrappedException_.setUndefined();
|
|
}
|
|
|
|
bool isThrowingOverRecursed() const { return throwing && overRecursed_; }
|
|
bool isPropagatingForcedReturn() const { return propagatingForcedReturn_; }
|
|
void setPropagatingForcedReturn() { propagatingForcedReturn_ = true; }
|
|
void clearPropagatingForcedReturn() { propagatingForcedReturn_ = false; }
|
|
|
|
/*
|
|
* See JS_SetTrustedPrincipals in jsapi.h.
|
|
* Note: !cx->compartment is treated as trusted.
|
|
*/
|
|
inline bool runningWithTrustedPrincipals() const;
|
|
|
|
JS_FRIEND_API(size_t) sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
|
|
|
void mark(JSTracer* trc);
|
|
|
|
private:
|
|
/*
|
|
* The allocation code calls the function to indicate either OOM failure
|
|
* when p is null or that a memory pressure counter has reached some
|
|
* threshold when p is not null. The function takes the pointer and not
|
|
* a boolean flag to minimize the amount of code in its inlined callers.
|
|
*/
|
|
JS_FRIEND_API(void) checkMallocGCPressure(void* p);
|
|
}; /* struct JSContext */
|
|
|
|
namespace js {
|
|
|
|
struct MOZ_RAII AutoResolving {
|
|
public:
|
|
enum Kind {
|
|
LOOKUP,
|
|
WATCH
|
|
};
|
|
|
|
AutoResolving(JSContext* cx, HandleObject obj, HandleId id, Kind kind = LOOKUP
|
|
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
|
: context(cx), object(obj), id(id), kind(kind), link(cx->resolvingList)
|
|
{
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
MOZ_ASSERT(obj);
|
|
cx->resolvingList = this;
|
|
}
|
|
|
|
~AutoResolving() {
|
|
MOZ_ASSERT(context->resolvingList == this);
|
|
context->resolvingList = link;
|
|
}
|
|
|
|
bool alreadyStarted() const {
|
|
return link && alreadyStartedSlow();
|
|
}
|
|
|
|
private:
|
|
bool alreadyStartedSlow() const;
|
|
|
|
JSContext* const context;
|
|
HandleObject object;
|
|
HandleId id;
|
|
Kind const kind;
|
|
AutoResolving* const link;
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
};
|
|
|
|
/*
|
|
* Enumerate all contexts in a runtime.
|
|
*/
|
|
class ContextIter
|
|
{
|
|
JSContext* iter;
|
|
|
|
public:
|
|
explicit ContextIter(JSRuntime* rt) {
|
|
iter = rt->contextList.getFirst();
|
|
}
|
|
|
|
bool done() const {
|
|
return !iter;
|
|
}
|
|
|
|
void next() {
|
|
MOZ_ASSERT(!done());
|
|
iter = iter->getNext();
|
|
}
|
|
|
|
JSContext* get() const {
|
|
MOZ_ASSERT(!done());
|
|
return iter;
|
|
}
|
|
|
|
operator JSContext*() const {
|
|
return get();
|
|
}
|
|
|
|
JSContext* operator ->() const {
|
|
return get();
|
|
}
|
|
};
|
|
|
|
/*
|
|
* Create and destroy functions for JSContext, which is manually allocated
|
|
* and exclusively owned.
|
|
*/
|
|
extern JSContext*
|
|
NewContext(JSRuntime* rt, size_t stackChunkSize);
|
|
|
|
enum DestroyContextMode {
|
|
DCM_NO_GC,
|
|
DCM_FORCE_GC,
|
|
DCM_NEW_FAILED
|
|
};
|
|
|
|
extern void
|
|
DestroyContext(JSContext* cx, DestroyContextMode mode);
|
|
|
|
enum ErrorArgumentsType {
|
|
ArgumentsAreUnicode,
|
|
ArgumentsAreASCII
|
|
};
|
|
|
|
/*
|
|
* Loads and returns a self-hosted function by name. For performance, define
|
|
* the property name in vm/CommonPropertyNames.h.
|
|
*
|
|
* Defined in SelfHosting.cpp.
|
|
*/
|
|
JSFunction*
|
|
SelfHostedFunction(JSContext* cx, HandlePropertyName propName);
|
|
|
|
#ifdef va_start
|
|
extern bool
|
|
ReportErrorVA(JSContext* cx, unsigned flags, const char* format, va_list ap);
|
|
|
|
extern bool
|
|
ReportErrorNumberVA(JSContext* cx, unsigned flags, JSErrorCallback callback,
|
|
void* userRef, const unsigned errorNumber,
|
|
ErrorArgumentsType argumentsType, va_list ap);
|
|
|
|
extern bool
|
|
ReportErrorNumberUCArray(JSContext* cx, unsigned flags, JSErrorCallback callback,
|
|
void* userRef, const unsigned errorNumber,
|
|
const char16_t** args);
|
|
#endif
|
|
|
|
extern bool
|
|
ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback,
|
|
void* userRef, const unsigned errorNumber,
|
|
char** message, JSErrorReport* reportp,
|
|
ErrorArgumentsType argumentsType, va_list ap);
|
|
|
|
/* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
|
|
extern void
|
|
ReportUsageError(JSContext* cx, HandleObject callee, const char* msg);
|
|
|
|
/*
|
|
* Prints a full report and returns true if the given report is non-nullptr
|
|
* and the report doesn't have the JSREPORT_WARNING flag set or reportWarnings
|
|
* is true.
|
|
* Returns false otherwise, printing just the message if the report is nullptr.
|
|
*/
|
|
extern bool
|
|
PrintError(JSContext* cx, FILE* file, const char* message, JSErrorReport* report,
|
|
bool reportWarnings);
|
|
|
|
/*
|
|
* Send a JSErrorReport to the errorReporter callback.
|
|
*/
|
|
void
|
|
CallErrorReporter(JSContext* cx, const char* message, JSErrorReport* report);
|
|
|
|
extern bool
|
|
ReportIsNotDefined(JSContext* cx, HandlePropertyName name);
|
|
|
|
extern bool
|
|
ReportIsNotDefined(JSContext* cx, HandleId id);
|
|
|
|
/*
|
|
* Report an attempt to access the property of a null or undefined value (v).
|
|
*/
|
|
extern bool
|
|
ReportIsNullOrUndefined(JSContext* cx, int spindex, HandleValue v, HandleString fallback);
|
|
|
|
extern void
|
|
ReportMissingArg(JSContext* cx, js::HandleValue v, unsigned arg);
|
|
|
|
/*
|
|
* Report error using js_DecompileValueGenerator(cx, spindex, v, fallback) as
|
|
* the first argument for the error message. If the error message has less
|
|
* then 3 arguments, use null for arg1 or arg2.
|
|
*/
|
|
extern bool
|
|
ReportValueErrorFlags(JSContext* cx, unsigned flags, const unsigned errorNumber,
|
|
int spindex, HandleValue v, HandleString fallback,
|
|
const char* arg1, const char* arg2);
|
|
|
|
#define ReportValueError(cx,errorNumber,spindex,v,fallback) \
|
|
((void)ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \
|
|
spindex, v, fallback, nullptr, nullptr))
|
|
|
|
#define ReportValueError2(cx,errorNumber,spindex,v,fallback,arg1) \
|
|
((void)ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \
|
|
spindex, v, fallback, arg1, nullptr))
|
|
|
|
#define ReportValueError3(cx,errorNumber,spindex,v,fallback,arg1,arg2) \
|
|
((void)ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \
|
|
spindex, v, fallback, arg1, arg2))
|
|
|
|
} /* namespace js */
|
|
|
|
extern const JSErrorFormatString js_ErrorFormatString[JSErr_Limit];
|
|
|
|
namespace js {
|
|
|
|
MOZ_ALWAYS_INLINE bool
|
|
CheckForInterrupt(JSContext* cx)
|
|
{
|
|
// Add an inline fast-path since we have to check for interrupts in some hot
|
|
// C++ loops of library builtins.
|
|
JSRuntime* rt = cx->runtime();
|
|
if (MOZ_UNLIKELY(rt->hasPendingInterrupt()))
|
|
return rt->handleInterrupt(cx);
|
|
return true;
|
|
}
|
|
|
|
/************************************************************************/
|
|
|
|
typedef JS::AutoVectorRooter<PropertyName*> AutoPropertyNameVector;
|
|
|
|
using ShapeVector = js::TraceableVector<Shape*>;
|
|
using StringVector = js::TraceableVector<JSString*>;
|
|
|
|
/* AutoArrayRooter roots an external array of Values. */
|
|
class MOZ_RAII AutoArrayRooter : private JS::AutoGCRooter
|
|
{
|
|
public:
|
|
AutoArrayRooter(JSContext* cx, size_t len, Value* vec
|
|
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
|
: JS::AutoGCRooter(cx, len), array(vec)
|
|
{
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
MOZ_ASSERT(tag_ >= 0);
|
|
}
|
|
|
|
void changeLength(size_t newLength) {
|
|
tag_ = ptrdiff_t(newLength);
|
|
MOZ_ASSERT(tag_ >= 0);
|
|
}
|
|
|
|
void changeArray(Value* newArray, size_t newLength) {
|
|
changeLength(newLength);
|
|
array = newArray;
|
|
}
|
|
|
|
Value* start() {
|
|
return array;
|
|
}
|
|
|
|
size_t length() {
|
|
MOZ_ASSERT(tag_ >= 0);
|
|
return size_t(tag_);
|
|
}
|
|
|
|
MutableHandleValue handleAt(size_t i) {
|
|
MOZ_ASSERT(i < size_t(tag_));
|
|
return MutableHandleValue::fromMarkedLocation(&array[i]);
|
|
}
|
|
HandleValue handleAt(size_t i) const {
|
|
MOZ_ASSERT(i < size_t(tag_));
|
|
return HandleValue::fromMarkedLocation(&array[i]);
|
|
}
|
|
MutableHandleValue operator[](size_t i) {
|
|
MOZ_ASSERT(i < size_t(tag_));
|
|
return MutableHandleValue::fromMarkedLocation(&array[i]);
|
|
}
|
|
HandleValue operator[](size_t i) const {
|
|
MOZ_ASSERT(i < size_t(tag_));
|
|
return HandleValue::fromMarkedLocation(&array[i]);
|
|
}
|
|
|
|
friend void JS::AutoGCRooter::trace(JSTracer* trc);
|
|
|
|
private:
|
|
Value* array;
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
};
|
|
|
|
class AutoAssertNoException
|
|
{
|
|
#ifdef DEBUG
|
|
JSContext* cx;
|
|
bool hadException;
|
|
#endif
|
|
|
|
public:
|
|
explicit AutoAssertNoException(JSContext* cx)
|
|
#ifdef DEBUG
|
|
: cx(cx),
|
|
hadException(cx->isExceptionPending())
|
|
#endif
|
|
{
|
|
}
|
|
|
|
~AutoAssertNoException()
|
|
{
|
|
MOZ_ASSERT_IF(!hadException, !cx->isExceptionPending());
|
|
}
|
|
};
|
|
|
|
/* Exposed intrinsics for the JITs. */
|
|
bool intrinsic_IsSuspendedStarGenerator(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
class MOZ_RAII AutoLockForExclusiveAccess
|
|
{
|
|
JSRuntime* runtime;
|
|
|
|
void init(JSRuntime* rt) {
|
|
runtime = rt;
|
|
if (runtime->numExclusiveThreads) {
|
|
runtime->assertCanLock(ExclusiveAccessLock);
|
|
PR_Lock(runtime->exclusiveAccessLock);
|
|
#ifdef DEBUG
|
|
runtime->exclusiveAccessOwner = PR_GetCurrentThread();
|
|
#endif
|
|
} else {
|
|
MOZ_ASSERT(!runtime->mainThreadHasExclusiveAccess);
|
|
runtime->mainThreadHasExclusiveAccess = true;
|
|
}
|
|
}
|
|
|
|
public:
|
|
explicit AutoLockForExclusiveAccess(ExclusiveContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
init(cx->runtime_);
|
|
}
|
|
explicit AutoLockForExclusiveAccess(JSRuntime* rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
init(rt);
|
|
}
|
|
~AutoLockForExclusiveAccess() {
|
|
if (runtime->numExclusiveThreads) {
|
|
MOZ_ASSERT(runtime->exclusiveAccessOwner == PR_GetCurrentThread());
|
|
runtime->exclusiveAccessOwner = nullptr;
|
|
PR_Unlock(runtime->exclusiveAccessLock);
|
|
} else {
|
|
MOZ_ASSERT(runtime->mainThreadHasExclusiveAccess);
|
|
runtime->mainThreadHasExclusiveAccess = false;
|
|
}
|
|
}
|
|
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
};
|
|
|
|
} /* namespace js */
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(pop)
|
|
#endif
|
|
|
|
#endif /* jscntxt_h */
|