mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
f176ca2fd6
--HG-- extra : rebase_source : 1fbb7dd0bde99abef7a6d504e08fd18163cee3ee
1103 lines
34 KiB
C++
1103 lines
34 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/Vector.h"
|
|
#include "vm/Runtime.h"
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
|
|
#endif
|
|
|
|
struct DtoaState;
|
|
|
|
extern void
|
|
js_ReportOutOfMemory(js::ThreadSafeContext *cx);
|
|
|
|
extern void
|
|
js_ReportAllocationOverflow(js::ThreadSafeContext *cx);
|
|
|
|
extern void
|
|
js_ReportOverRecursed(js::ThreadSafeContext *cx);
|
|
|
|
namespace js {
|
|
|
|
namespace jit {
|
|
class IonContext;
|
|
class CompileCompartment;
|
|
}
|
|
|
|
struct CallsiteCloneKey {
|
|
/* The original function that we are cloning. */
|
|
JSFunction *original;
|
|
|
|
/* The script of the call. */
|
|
JSScript *script;
|
|
|
|
/* The offset of the call. */
|
|
uint32_t offset;
|
|
|
|
CallsiteCloneKey(JSFunction *f, JSScript *s, uint32_t o) : original(f), script(s), offset(o) {}
|
|
|
|
typedef CallsiteCloneKey Lookup;
|
|
|
|
static inline uint32_t hash(CallsiteCloneKey key) {
|
|
return uint32_t(size_t(key.script->offsetToPC(key.offset)) ^ size_t(key.original));
|
|
}
|
|
|
|
static inline bool match(const CallsiteCloneKey &a, const CallsiteCloneKey &b) {
|
|
return a.script == b.script && a.offset == b.offset && a.original == b.original;
|
|
}
|
|
};
|
|
|
|
typedef HashMap<CallsiteCloneKey,
|
|
ReadBarriered<JSFunction>,
|
|
CallsiteCloneKey,
|
|
SystemAllocPolicy> CallsiteCloneTable;
|
|
|
|
JSFunction *
|
|
ExistingCloneFunctionAtCallsite(const CallsiteCloneTable &table, JSFunction *fun,
|
|
JSScript *script, jsbytecode *pc);
|
|
|
|
JSFunction *CloneFunctionAtCallsite(JSContext *cx, HandleFunction fun,
|
|
HandleScript script, jsbytecode *pc);
|
|
|
|
typedef HashSet<JSObject *> ObjectSet;
|
|
typedef HashSet<Shape *> ShapeSet;
|
|
|
|
/* Detects cycles when traversing an object graph. */
|
|
class AutoCycleDetector
|
|
{
|
|
JSContext *cx;
|
|
RootedObject obj;
|
|
bool cyclic;
|
|
uint32_t hashsetGenerationAtInit;
|
|
ObjectSet::AddPtr hashsetAddPointer;
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
|
|
public:
|
|
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; }
|
|
};
|
|
|
|
/* Updates references in the cycle detection set if the GC moves them. */
|
|
extern void
|
|
TraceCycleDetectionSet(JSTracer *trc, ObjectSet &set);
|
|
|
|
struct AutoResolving;
|
|
class DtoaCache;
|
|
class ForkJoinContext;
|
|
class RegExpCompartment;
|
|
class RegExpStatics;
|
|
|
|
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.
|
|
*
|
|
* - ThreadSafeContext is used by threads operating in one compartment which
|
|
* may run in parallel with other threads operating on the same or other
|
|
* compartments.
|
|
*
|
|
* - 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
|
|
* or ThreadSafeContext, and will only run in parallel with threads using such
|
|
* contexts.
|
|
*
|
|
* An ExclusiveContext coerces to a ThreadSafeContext, and a JSContext coerces
|
|
* to an ExclusiveContext or ThreadSafeContext.
|
|
*
|
|
* Contexts which are a ThreadSafeContext but not an ExclusiveContext are used
|
|
* to represent a ForkJoinContext, the per-thread parallel context used in PJS.
|
|
*/
|
|
|
|
struct ThreadSafeContext : ContextFriendFields,
|
|
public MallocProvider<ThreadSafeContext>
|
|
{
|
|
friend struct StackBaseShape;
|
|
friend UnownedBaseShape *BaseShape::lookupUnowned(ThreadSafeContext *cx,
|
|
const StackBaseShape &base);
|
|
friend Shape *JSObject::lookupChildProperty(ThreadSafeContext *cx,
|
|
JS::HandleObject obj, js::HandleShape parent,
|
|
js::StackShape &child);
|
|
|
|
public:
|
|
enum ContextKind {
|
|
Context_JS,
|
|
Context_Exclusive,
|
|
Context_ForkJoin
|
|
};
|
|
|
|
private:
|
|
ContextKind contextKind_;
|
|
|
|
public:
|
|
PerThreadData *perThreadData;
|
|
|
|
ThreadSafeContext(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.
|
|
JS_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 {
|
|
JS_ASSERT(isJSContext());
|
|
return isJSContext();
|
|
}
|
|
|
|
bool isExclusiveContext() const {
|
|
return contextKind_ == Context_JS || contextKind_ == Context_Exclusive;
|
|
}
|
|
|
|
ExclusiveContext *maybeExclusiveContext() const {
|
|
if (isExclusiveContext())
|
|
return (ExclusiveContext *) this;
|
|
return nullptr;
|
|
}
|
|
|
|
ExclusiveContext *asExclusiveContext() const {
|
|
JS_ASSERT(isExclusiveContext());
|
|
return maybeExclusiveContext();
|
|
}
|
|
|
|
bool isForkJoinContext() const;
|
|
ForkJoinContext *asForkJoinContext();
|
|
|
|
// The generational GC nursery may only be used on the main thread.
|
|
#ifdef JSGC_GENERATIONAL
|
|
inline bool hasNursery() const {
|
|
return isJSContext();
|
|
}
|
|
|
|
inline js::Nursery &nursery() {
|
|
JS_ASSERT(hasNursery());
|
|
return runtime_->gcNursery;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Allocator used when allocating GCThings on this context. If we are a
|
|
* JSContext, this is the Zone allocator of the JSContext's zone.
|
|
* Otherwise, this is a per-thread allocator.
|
|
*
|
|
* This does not live in PerThreadData because the notion of an allocator
|
|
* is only per-thread when off the main thread. The runtime (and the main
|
|
* thread) can have more than one zone, each with its own allocator, and
|
|
* it's up to the context to specify what compartment and zone we are
|
|
* operating in.
|
|
*/
|
|
protected:
|
|
Allocator *allocator_;
|
|
|
|
public:
|
|
static size_t offsetOfAllocator() { return offsetof(ThreadSafeContext, allocator_); }
|
|
|
|
inline Allocator *const allocator();
|
|
|
|
// Allocations can only trigger GC when running on the main thread.
|
|
inline AllowGC allowGC() const { return isJSContext() ? CanGC : NoGC; }
|
|
|
|
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_;
|
|
}
|
|
|
|
template <typename T>
|
|
inline bool isThreadLocal(T thing) const;
|
|
|
|
void *onOutOfMemory(void *p, size_t nbytes) {
|
|
return runtime_->onOutOfMemory(p, nbytes, maybeJSContext());
|
|
}
|
|
|
|
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; }
|
|
AtomSet &permanentAtoms() { return *runtime_->permanentAtoms; }
|
|
const JS::AsmJSCacheOps &asmJSCacheOps() { return runtime_->asmJSCacheOps; }
|
|
PropertyName *emptyString() { return runtime_->emptyString; }
|
|
FreeOp *defaultFreeOp() { return runtime_->defaultFreeOp(); }
|
|
bool useHelperThreads() { return runtime_->useHelperThreads(); }
|
|
void *runtimeAddressForJit() { return runtime_; }
|
|
void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; }
|
|
void *stackLimitAddressForJitCode(StackKind kind);
|
|
size_t gcSystemPageSize() { return runtime_->gcSystemPageSize; }
|
|
bool signalHandlersInstalled() const { return runtime_->signalHandlersInstalled(); }
|
|
bool jitSupportsFloatingPoint() const { return runtime_->jitSupportsFloatingPoint; }
|
|
|
|
// Thread local data that may be accessed freely.
|
|
DtoaState *dtoaState() {
|
|
return perThreadData->dtoaState;
|
|
}
|
|
};
|
|
|
|
struct WorkerThread;
|
|
|
|
class ExclusiveContext : public ThreadSafeContext
|
|
{
|
|
friend class gc::ArenaLists;
|
|
friend class AutoCompartment;
|
|
friend class AutoLockForExclusiveAccess;
|
|
friend struct StackBaseShape;
|
|
friend void JSScript::initCompartment(ExclusiveContext *cx);
|
|
friend class jit::IonContext;
|
|
|
|
// The worker on which this context is running, if this is not a JSContext.
|
|
WorkerThread *workerThread_;
|
|
|
|
public:
|
|
|
|
ExclusiveContext(JSRuntime *rt, PerThreadData *pt, ContextKind kind)
|
|
: ThreadSafeContext(rt, pt, kind),
|
|
workerThread_(nullptr),
|
|
enterCompartmentDepth_(0)
|
|
{}
|
|
|
|
/*
|
|
* "Entering" a compartment changes cx->compartment (which changes
|
|
* cx->global). Note that this does not push any StackFrame 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 leaveCompartment(JSCompartment *oldCompartment);
|
|
|
|
void setWorkerThread(WorkerThread *workerThread);
|
|
WorkerThread *workerThread() const { return workerThread_; }
|
|
|
|
// Threads with an ExclusiveContext may freely access any data in their
|
|
// compartment and zone.
|
|
JSCompartment *compartment() const {
|
|
JS_ASSERT_IF(runtime_->isAtomsCompartment(compartment_),
|
|
runtime_->currentThreadHasExclusiveAccess());
|
|
return compartment_;
|
|
}
|
|
JS::Zone *zone() const {
|
|
JS_ASSERT_IF(!compartment(), !zone_);
|
|
JS_ASSERT_IF(compartment(), js::GetCompartmentZone(compartment()) == zone_);
|
|
return zone_;
|
|
}
|
|
|
|
// Zone local methods that can be used freely from an ExclusiveContext.
|
|
inline bool typeInferenceEnabled() const;
|
|
types::TypeObject *getNewType(const Class *clasp, TaggedProto proto, JSFunction *fun = nullptr);
|
|
types::TypeObject *getLazyType(const Class *clasp, TaggedProto proto);
|
|
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();
|
|
}
|
|
ScriptDataTable &scriptDataTable() {
|
|
return runtime_->scriptDataTable();
|
|
}
|
|
|
|
// Methods specific to any WorkerThread for the context.
|
|
frontend::CompileError &addPendingCompileError();
|
|
void addPendingOverRecursed();
|
|
};
|
|
|
|
inline void
|
|
MaybeCheckStackRoots(ExclusiveContext *cx)
|
|
{
|
|
MaybeCheckStackRoots(cx->maybeJSContext());
|
|
}
|
|
|
|
} /* 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; }
|
|
|
|
friend class js::ExclusiveContext;
|
|
friend class JS::AutoSaveExceptionState;
|
|
|
|
private:
|
|
/* Exception state -- the exception member is a GC root by definition. */
|
|
bool throwing; /* is there a pending exception? */
|
|
js::Value unwrappedException_; /* most-recently-thrown exception */
|
|
|
|
/* Per-context options. */
|
|
JS::ContextOptions options_;
|
|
|
|
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();
|
|
|
|
/*
|
|
* When no compartments have been explicitly entered, the context's
|
|
* compartment will be set to the compartment of the "default compartment
|
|
* object".
|
|
*/
|
|
private:
|
|
JSObject *defaultCompartmentObject_;
|
|
public:
|
|
inline void setDefaultCompartmentObject(JSObject *obj);
|
|
inline void setDefaultCompartmentObjectIfUnset(JSObject *obj);
|
|
JSObject *maybeDefaultCompartmentObject() const {
|
|
JS_ASSERT(!options().noDefaultCompartmentObject());
|
|
return defaultCompartmentObject_;
|
|
}
|
|
|
|
/* State for object and array toSource conversion. */
|
|
js::ObjectSet cycleDetectorSet;
|
|
|
|
/* Per-context optional error reporter. */
|
|
JSErrorReporter errorReporter;
|
|
|
|
/* 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; }
|
|
|
|
#ifdef JS_THREADSAFE
|
|
unsigned outstandingRequests;/* number of JS_BeginRequest calls
|
|
without the corresponding
|
|
JS_EndRequest. */
|
|
#endif
|
|
|
|
/* Stored here to avoid passing it around as a parameter. */
|
|
unsigned resolveFlags;
|
|
|
|
/* Location to stash the iteration value between JSOP_MOREITER and JSOP_ITERNEXT. */
|
|
js::Value iterValue;
|
|
|
|
bool jitIsBroken;
|
|
|
|
void updateJITEnabled();
|
|
|
|
/* Whether this context has JS frames on the stack. */
|
|
bool currentlyRunning() const;
|
|
|
|
bool currentlyRunningInInterpreter() const {
|
|
return mainThread().activation()->isInterpreter();
|
|
}
|
|
bool currentlyRunningInJit() const {
|
|
return mainThread().activation()->isJit();
|
|
}
|
|
js::StackFrame *interpreterFrame() const {
|
|
return mainThread().activation()->asInterpreter()->current();
|
|
}
|
|
js::InterpreterRegs &interpreterRegs() const {
|
|
return mainThread().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;
|
|
|
|
#ifdef MOZ_TRACE_JSCALLS
|
|
/* Function entry/exit debugging callback. */
|
|
JSFunctionCallback functionCallback;
|
|
|
|
void doFunctionCallback(const JSFunction *fun,
|
|
const JSScript *scr,
|
|
int entering) const
|
|
{
|
|
if (functionCallback)
|
|
functionCallback(fun, scr, this, entering);
|
|
}
|
|
#endif
|
|
|
|
private:
|
|
/* Innermost-executing generator or null if no generator are executing. */
|
|
JSGenerator *innermostGenerator_;
|
|
public:
|
|
JSGenerator *innermostGenerator() const { return innermostGenerator_; }
|
|
void enterGenerator(JSGenerator *gen);
|
|
void leaveGenerator(JSGenerator *gen);
|
|
|
|
bool isExceptionPending() {
|
|
return throwing;
|
|
}
|
|
|
|
MOZ_WARN_UNUSED_RESULT
|
|
bool getPendingException(JS::MutableHandleValue rval);
|
|
|
|
void setPendingException(js::Value v);
|
|
|
|
void clearPendingException() {
|
|
throwing = false;
|
|
unwrappedException_.setUndefined();
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
/*
|
|
* Controls whether a quadratic-complexity assertion is performed during
|
|
* stack iteration; defaults to true.
|
|
*/
|
|
bool stackIterAssertionEnabled;
|
|
#endif
|
|
|
|
/*
|
|
* See JS_SetTrustedPrincipals in jsapi.h.
|
|
* Note: !cx->compartment is treated as trusted.
|
|
*/
|
|
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 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;
|
|
JS_ASSERT(obj);
|
|
cx->resolvingList = this;
|
|
}
|
|
|
|
~AutoResolving() {
|
|
JS_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
|
|
};
|
|
|
|
} /* namespace js */
|
|
|
|
class JSAutoResolveFlags
|
|
{
|
|
public:
|
|
JSAutoResolveFlags(JSContext *cx, unsigned flags
|
|
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
|
: mContext(cx), mSaved(cx->resolveFlags)
|
|
{
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
cx->resolveFlags = flags;
|
|
}
|
|
|
|
~JSAutoResolveFlags() { mContext->resolveFlags = mSaved; }
|
|
|
|
private:
|
|
JSContext *mContext;
|
|
unsigned mSaved;
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
};
|
|
|
|
namespace js {
|
|
|
|
/*
|
|
* 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() {
|
|
JS_ASSERT(!done());
|
|
iter = iter->getNext();
|
|
}
|
|
|
|
JSContext *get() const {
|
|
JS_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);
|
|
|
|
} /* namespace js */
|
|
|
|
#ifdef va_start
|
|
extern bool
|
|
js_ReportErrorVA(JSContext *cx, unsigned flags, const char *format, va_list ap);
|
|
|
|
extern bool
|
|
js_ReportErrorNumberVA(JSContext *cx, unsigned flags, JSErrorCallback callback,
|
|
void *userRef, const unsigned errorNumber,
|
|
js::ErrorArgumentsType argumentsType, va_list ap);
|
|
|
|
extern bool
|
|
js_ReportErrorNumberUCArray(JSContext *cx, unsigned flags, JSErrorCallback callback,
|
|
void *userRef, const unsigned errorNumber,
|
|
const jschar **args);
|
|
#endif
|
|
|
|
extern bool
|
|
js_ExpandErrorArguments(js::ExclusiveContext *cx, JSErrorCallback callback,
|
|
void *userRef, const unsigned errorNumber,
|
|
char **message, JSErrorReport *reportp,
|
|
js::ErrorArgumentsType argumentsType, va_list ap);
|
|
|
|
namespace js {
|
|
|
|
/* |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);
|
|
|
|
} /* namespace js */
|
|
|
|
extern void
|
|
js_ReportIsNotDefined(JSContext *cx, const char *name);
|
|
|
|
/*
|
|
* Report an attempt to access the property of a null or undefined value (v).
|
|
*/
|
|
extern bool
|
|
js_ReportIsNullOrUndefined(JSContext *cx, int spindex, js::HandleValue v,
|
|
js::HandleString fallback);
|
|
|
|
extern void
|
|
js_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
|
|
js_ReportValueErrorFlags(JSContext *cx, unsigned flags, const unsigned errorNumber,
|
|
int spindex, js::HandleValue v, js::HandleString fallback,
|
|
const char *arg1, const char *arg2);
|
|
|
|
#define js_ReportValueError(cx,errorNumber,spindex,v,fallback) \
|
|
((void)js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \
|
|
spindex, v, fallback, nullptr, nullptr))
|
|
|
|
#define js_ReportValueError2(cx,errorNumber,spindex,v,fallback,arg1) \
|
|
((void)js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \
|
|
spindex, v, fallback, arg1, nullptr))
|
|
|
|
#define js_ReportValueError3(cx,errorNumber,spindex,v,fallback,arg1,arg2) \
|
|
((void)js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \
|
|
spindex, v, fallback, arg1, arg2))
|
|
|
|
extern const JSErrorFormatString js_ErrorFormatString[JSErr_Limit];
|
|
|
|
char *
|
|
js_strdup(js::ExclusiveContext *cx, const char *s);
|
|
|
|
#ifdef JS_THREADSAFE
|
|
# define JS_ASSERT_REQUEST_DEPTH(cx) JS_ASSERT((cx)->runtime()->requestDepth >= 1)
|
|
#else
|
|
# define JS_ASSERT_REQUEST_DEPTH(cx) ((void) 0)
|
|
#endif
|
|
|
|
/*
|
|
* Invoke the operation callback and return false if the current execution
|
|
* is to be terminated.
|
|
*/
|
|
extern bool
|
|
js_InvokeOperationCallback(JSContext *cx);
|
|
|
|
extern bool
|
|
js_HandleExecutionInterrupt(JSContext *cx);
|
|
|
|
/*
|
|
* If the operation callback flag was set, call the operation callback.
|
|
* This macro can run the full GC. Return true if it is OK to continue and
|
|
* false otherwise.
|
|
*/
|
|
static MOZ_ALWAYS_INLINE bool
|
|
JS_CHECK_OPERATION_LIMIT(JSContext *cx)
|
|
{
|
|
JS_ASSERT_REQUEST_DEPTH(cx);
|
|
return !cx->runtime()->interrupt || js_InvokeOperationCallback(cx);
|
|
}
|
|
|
|
namespace js {
|
|
|
|
/************************************************************************/
|
|
|
|
class AutoStringVector : public AutoVectorRooter<JSString *>
|
|
{
|
|
public:
|
|
explicit AutoStringVector(JSContext *cx
|
|
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
|
: AutoVectorRooter<JSString *>(cx, STRINGVECTOR)
|
|
{
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
}
|
|
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
};
|
|
|
|
class AutoPropertyNameVector : public AutoVectorRooter<PropertyName *>
|
|
{
|
|
public:
|
|
explicit AutoPropertyNameVector(JSContext *cx
|
|
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
|
: AutoVectorRooter<PropertyName *>(cx, STRINGVECTOR)
|
|
{
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
}
|
|
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
};
|
|
|
|
class AutoShapeVector : public AutoVectorRooter<Shape *>
|
|
{
|
|
public:
|
|
explicit AutoShapeVector(JSContext *cx
|
|
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
|
: AutoVectorRooter<Shape *>(cx, SHAPEVECTOR)
|
|
{
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
}
|
|
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
};
|
|
|
|
class AutoObjectObjectHashMap : public AutoHashMapRooter<JSObject *, JSObject *>
|
|
{
|
|
public:
|
|
explicit AutoObjectObjectHashMap(JSContext *cx
|
|
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
|
: AutoHashMapRooter<JSObject *, JSObject *>(cx, OBJOBJHASHMAP)
|
|
{
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
}
|
|
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
};
|
|
|
|
class AutoObjectUnsigned32HashMap : public AutoHashMapRooter<JSObject *, uint32_t>
|
|
{
|
|
public:
|
|
explicit AutoObjectUnsigned32HashMap(JSContext *cx
|
|
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
|
: AutoHashMapRooter<JSObject *, uint32_t>(cx, OBJU32HASHMAP)
|
|
{
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
}
|
|
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
};
|
|
|
|
class AutoObjectHashSet : public AutoHashSetRooter<JSObject *>
|
|
{
|
|
public:
|
|
explicit AutoObjectHashSet(JSContext *cx
|
|
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
|
: AutoHashSetRooter<JSObject *>(cx, OBJHASHSET)
|
|
{
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
}
|
|
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
};
|
|
|
|
/* AutoArrayRooter roots an external array of Values. */
|
|
class AutoArrayRooter : private AutoGCRooter
|
|
{
|
|
public:
|
|
AutoArrayRooter(JSContext *cx, size_t len, Value *vec
|
|
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
|
: AutoGCRooter(cx, len), array(vec), skip(cx, array, len)
|
|
{
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
JS_ASSERT(tag_ >= 0);
|
|
}
|
|
|
|
void changeLength(size_t newLength) {
|
|
tag_ = ptrdiff_t(newLength);
|
|
JS_ASSERT(tag_ >= 0);
|
|
}
|
|
|
|
void changeArray(Value *newArray, size_t newLength) {
|
|
changeLength(newLength);
|
|
array = newArray;
|
|
}
|
|
|
|
Value *start() {
|
|
return array;
|
|
}
|
|
|
|
size_t length() {
|
|
JS_ASSERT(tag_ >= 0);
|
|
return size_t(tag_);
|
|
}
|
|
|
|
MutableHandleValue handleAt(size_t i) {
|
|
JS_ASSERT(i < size_t(tag_));
|
|
return MutableHandleValue::fromMarkedLocation(&array[i]);
|
|
}
|
|
HandleValue handleAt(size_t i) const {
|
|
JS_ASSERT(i < size_t(tag_));
|
|
return HandleValue::fromMarkedLocation(&array[i]);
|
|
}
|
|
MutableHandleValue operator[](size_t i) {
|
|
JS_ASSERT(i < size_t(tag_));
|
|
return MutableHandleValue::fromMarkedLocation(&array[i]);
|
|
}
|
|
HandleValue operator[](size_t i) const {
|
|
JS_ASSERT(i < size_t(tag_));
|
|
return HandleValue::fromMarkedLocation(&array[i]);
|
|
}
|
|
|
|
friend void AutoGCRooter::trace(JSTracer *trc);
|
|
|
|
private:
|
|
Value *array;
|
|
js::SkipRoot skip;
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
};
|
|
|
|
class AutoAssertNoException
|
|
{
|
|
#ifdef DEBUG
|
|
JSContext *cx;
|
|
bool hadException;
|
|
#endif
|
|
|
|
public:
|
|
AutoAssertNoException(JSContext *cx)
|
|
#ifdef DEBUG
|
|
: cx(cx),
|
|
hadException(cx->isExceptionPending())
|
|
#endif
|
|
{
|
|
}
|
|
|
|
~AutoAssertNoException()
|
|
{
|
|
JS_ASSERT_IF(!hadException, !cx->isExceptionPending());
|
|
}
|
|
};
|
|
|
|
/*
|
|
* FIXME bug 647103 - replace these *AllocPolicy names.
|
|
*/
|
|
class ContextAllocPolicy
|
|
{
|
|
ThreadSafeContext *const cx_;
|
|
|
|
public:
|
|
ContextAllocPolicy(ThreadSafeContext *cx) : cx_(cx) {}
|
|
ThreadSafeContext *context() const { return cx_; }
|
|
void *malloc_(size_t bytes) { return cx_->malloc_(bytes); }
|
|
void *calloc_(size_t bytes) { return cx_->calloc_(bytes); }
|
|
void *realloc_(void *p, size_t oldBytes, size_t bytes) { return cx_->realloc_(p, oldBytes, bytes); }
|
|
void free_(void *p) { js_free(p); }
|
|
void reportAllocOverflow() const { js_ReportAllocationOverflow(cx_); }
|
|
};
|
|
|
|
/* Exposed intrinsics so that Ion may inline them. */
|
|
bool intrinsic_ToObject(JSContext *cx, unsigned argc, Value *vp);
|
|
bool intrinsic_IsCallable(JSContext *cx, unsigned argc, Value *vp);
|
|
bool intrinsic_ThrowError(JSContext *cx, unsigned argc, Value *vp);
|
|
bool intrinsic_NewDenseArray(JSContext *cx, unsigned argc, Value *vp);
|
|
|
|
bool intrinsic_UnsafePutElements(JSContext *cx, unsigned argc, Value *vp);
|
|
bool intrinsic_UnsafeSetReservedSlot(JSContext *cx, unsigned argc, Value *vp);
|
|
bool intrinsic_UnsafeGetReservedSlot(JSContext *cx, unsigned argc, Value *vp);
|
|
bool intrinsic_HaveSameClass(JSContext *cx, unsigned argc, Value *vp);
|
|
bool intrinsic_IsPackedArray(JSContext *cx, unsigned argc, Value *vp);
|
|
|
|
bool intrinsic_ShouldForceSequential(JSContext *cx, unsigned argc, Value *vp);
|
|
bool intrinsic_NewParallelArray(JSContext *cx, unsigned argc, Value *vp);
|
|
bool intrinsic_ForkJoinGetSlice(JSContext *cx, unsigned argc, Value *vp);
|
|
bool intrinsic_InParallelSection(JSContext *cx, unsigned argc, Value *vp);
|
|
|
|
bool intrinsic_ObjectIsTransparentTypedObject(JSContext *cx, unsigned argc, Value *vp);
|
|
bool intrinsic_ObjectIsOpaqueTypedObject(JSContext *cx, unsigned argc, Value *vp);
|
|
bool intrinsic_ObjectIsTypeDescr(JSContext *cx, unsigned argc, Value *vp);
|
|
|
|
class AutoLockForExclusiveAccess
|
|
{
|
|
#ifdef JS_THREADSAFE
|
|
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 {
|
|
JS_ASSERT(!runtime->mainThreadHasExclusiveAccess);
|
|
runtime->mainThreadHasExclusiveAccess = true;
|
|
}
|
|
}
|
|
|
|
public:
|
|
AutoLockForExclusiveAccess(ExclusiveContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
init(cx->runtime_);
|
|
}
|
|
AutoLockForExclusiveAccess(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
init(rt);
|
|
}
|
|
~AutoLockForExclusiveAccess() {
|
|
if (runtime->numExclusiveThreads) {
|
|
JS_ASSERT(runtime->exclusiveAccessOwner == PR_GetCurrentThread());
|
|
runtime->exclusiveAccessOwner = nullptr;
|
|
PR_Unlock(runtime->exclusiveAccessLock);
|
|
} else {
|
|
JS_ASSERT(runtime->mainThreadHasExclusiveAccess);
|
|
runtime->mainThreadHasExclusiveAccess = false;
|
|
}
|
|
}
|
|
#else // JS_THREADSAFE
|
|
public:
|
|
AutoLockForExclusiveAccess(ExclusiveContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
}
|
|
AutoLockForExclusiveAccess(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
}
|
|
~AutoLockForExclusiveAccess() {
|
|
// An empty destructor is needed to avoid warnings from clang about
|
|
// unused local variables of this type.
|
|
}
|
|
#endif // JS_THREADSAFE
|
|
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
};
|
|
|
|
} /* namespace js */
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(pop)
|
|
#endif
|
|
|
|
#endif /* jscntxt_h */
|