https://github.com/mozilla/gecko-dev
Raw File
Tip revision: 3d619309c8d39ce22d8d411fefec2a4e99561578 authored by ffxbld on 28 May 2015, 08:47:19 UTC
Added FENNEC_38_0_5_RELEASE FENNEC_38_0_5_BUILD1 tag(s) for changeset 271bc6fc4c43. DONTBUILD CLOSED TREE a=release
Tip revision: 3d61930
jscntxt.h
/* -*- 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;

namespace js {

namespace jit {
class JitContext;
class CompileCompartment;
class DebugModeOSRVolatileJitFrameIterator;
}

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 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.
 *
 * - 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();
    }

  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(void* p, size_t nbytes) {
        return runtime_->onOutOfMemory(p, nbytes, maybeJSContext());
    }

    /* 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; }
    AtomSet& 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; }

    // 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_);
    }

    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::Value           unwrappedException_; /* most-recently-thrown exception */

    /* Per-context options. */
    JS::ContextOptions  options_;

    // True if the exception currently being thrown is by result of
    // js_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::ObjectSet       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();

    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 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);

} /* 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 char16_t** 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];

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;
}

/************************************************************************/

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 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 so that Ion may inline them. */
bool intrinsic_ToObject(JSContext* cx, unsigned argc, Value* vp);
bool intrinsic_IsObject(JSContext* cx, unsigned argc, Value* vp);
bool intrinsic_ToInteger(JSContext* cx, unsigned argc, Value* vp);
bool intrinsic_ToString(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_IsConstructing(JSContext* cx, unsigned argc, Value* vp);
bool intrinsic_SubstringKernel(JSContext* cx, unsigned argc, Value* vp);

bool intrinsic_UnsafePutElements(JSContext* cx, unsigned argc, Value* vp);
bool intrinsic_DefineDataProperty(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_UnsafeGetObjectFromReservedSlot(JSContext* cx, unsigned argc, Value* vp);
bool intrinsic_UnsafeGetInt32FromReservedSlot(JSContext* cx, unsigned argc, Value* vp);
bool intrinsic_UnsafeGetStringFromReservedSlot(JSContext* cx, unsigned argc, Value* vp);
bool intrinsic_UnsafeGetBooleanFromReservedSlot(JSContext* cx, unsigned argc, Value* vp);
bool intrinsic_IsPackedArray(JSContext* cx, unsigned argc, Value* vp);

bool intrinsic_IsSuspendedStarGenerator(JSContext* cx, unsigned argc, Value* vp);
bool intrinsic_IsArrayIterator(JSContext* cx, unsigned argc, Value* vp);
bool intrinsic_IsStringIterator(JSContext* cx, unsigned argc, Value* vp);

bool intrinsic_IsTypedArray(JSContext* cx, unsigned argc, Value* vp);
bool intrinsic_TypedArrayLength(JSContext* cx, unsigned argc, Value* vp);

class 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 */
back to top