js/public/Value.h
author Masayuki Nakano <masayuki@d-toybox.com>
Fri, 19 May 2017 17:49:41 +0900
changeset 359318 b75c111837a802ceb953dba50a3c5a193d53ca22
parent 354994 0203cc1f2d2f017f58bf00c676d9f8f819546287
child 381081 5c1007b062e3a2b3d2571983603ccc67a144f8b6
permissions -rw-r--r--
Bug 1339543 part 4 Change nsIWidget::ExecuteNativeKeyBinding() to nsIWidget::GetEditCommands() which just retrieves edit commands for the type r=smaug Now, nsIWidget::ExecuteNativeKeyBinding() isn't used by anybody for executing edit commands. Instead, they need array of edit commands for the key combination. So, the method should be renamed to GetEditCommands() and just return edit commands as an array. MozReview-Commit-ID: 4G0B1lJ8Lbe

/* -*- 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::Value implementation. */

#ifndef js_Value_h
#define js_Value_h

#include "mozilla/Attributes.h"
#include "mozilla/Casting.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/Likely.h"

#include <limits> /* for std::numeric_limits */

#include "js-config.h"
#include "jstypes.h"

#include "js/GCAPI.h"
#include "js/RootingAPI.h"
#include "js/Utility.h"

namespace JS { class Value; }

/* JS::Value can store a full int32_t. */
#define JSVAL_INT_BITS          32
#define JSVAL_INT_MIN           ((int32_t)0x80000000)
#define JSVAL_INT_MAX           ((int32_t)0x7fffffff)

#if defined(JS_PUNBOX64)
# define JSVAL_TAG_SHIFT 47
#endif

// Use enums so that printing a JS::Value in the debugger shows nice
// symbolic type tags.

#if defined(_MSC_VER)
# define JS_ENUM_HEADER(id, type)              enum id : type
# define JS_ENUM_FOOTER(id)
#else
# define JS_ENUM_HEADER(id, type)              enum id
# define JS_ENUM_FOOTER(id)                    __attribute__((packed))
#endif

JS_ENUM_HEADER(JSValueType, uint8_t)
{
    JSVAL_TYPE_DOUBLE              = 0x00,
    JSVAL_TYPE_INT32               = 0x01,
    JSVAL_TYPE_UNDEFINED           = 0x02,
    JSVAL_TYPE_NULL                = 0x03,
    JSVAL_TYPE_BOOLEAN             = 0x04,
    JSVAL_TYPE_MAGIC               = 0x05,
    JSVAL_TYPE_STRING              = 0x06,
    JSVAL_TYPE_SYMBOL              = 0x07,
    JSVAL_TYPE_PRIVATE_GCTHING     = 0x08,
    JSVAL_TYPE_OBJECT              = 0x0c,

    /* These never appear in a jsval; they are only provided as an out-of-band value. */
    JSVAL_TYPE_UNKNOWN             = 0x20,
    JSVAL_TYPE_MISSING             = 0x21
} JS_ENUM_FOOTER(JSValueType);

static_assert(sizeof(JSValueType) == 1,
              "compiler typed enum support is apparently buggy");

#if defined(JS_NUNBOX32)

JS_ENUM_HEADER(JSValueTag, uint32_t)
{
    JSVAL_TAG_CLEAR                = 0xFFFFFF80,
    JSVAL_TAG_INT32                = JSVAL_TAG_CLEAR | JSVAL_TYPE_INT32,
    JSVAL_TAG_UNDEFINED            = JSVAL_TAG_CLEAR | JSVAL_TYPE_UNDEFINED,
    JSVAL_TAG_NULL                 = JSVAL_TAG_CLEAR | JSVAL_TYPE_NULL,
    JSVAL_TAG_BOOLEAN              = JSVAL_TAG_CLEAR | JSVAL_TYPE_BOOLEAN,
    JSVAL_TAG_MAGIC                = JSVAL_TAG_CLEAR | JSVAL_TYPE_MAGIC,
    JSVAL_TAG_STRING               = JSVAL_TAG_CLEAR | JSVAL_TYPE_STRING,
    JSVAL_TAG_SYMBOL               = JSVAL_TAG_CLEAR | JSVAL_TYPE_SYMBOL,
    JSVAL_TAG_PRIVATE_GCTHING      = JSVAL_TAG_CLEAR | JSVAL_TYPE_PRIVATE_GCTHING,
    JSVAL_TAG_OBJECT               = JSVAL_TAG_CLEAR | JSVAL_TYPE_OBJECT
} JS_ENUM_FOOTER(JSValueTag);

static_assert(sizeof(JSValueTag) == sizeof(uint32_t),
              "compiler typed enum support is apparently buggy");

#elif defined(JS_PUNBOX64)

JS_ENUM_HEADER(JSValueTag, uint32_t)
{
    JSVAL_TAG_MAX_DOUBLE           = 0x1FFF0,
    JSVAL_TAG_INT32                = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32,
    JSVAL_TAG_UNDEFINED            = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_UNDEFINED,
    JSVAL_TAG_NULL                 = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_NULL,
    JSVAL_TAG_BOOLEAN              = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN,
    JSVAL_TAG_MAGIC                = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_MAGIC,
    JSVAL_TAG_STRING               = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_STRING,
    JSVAL_TAG_SYMBOL               = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_SYMBOL,
    JSVAL_TAG_PRIVATE_GCTHING      = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_PRIVATE_GCTHING,
    JSVAL_TAG_OBJECT               = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_OBJECT
} JS_ENUM_FOOTER(JSValueTag);

static_assert(sizeof(JSValueTag) == sizeof(uint32_t),
              "compiler typed enum support is apparently buggy");

JS_ENUM_HEADER(JSValueShiftedTag, uint64_t)
{
    JSVAL_SHIFTED_TAG_MAX_DOUBLE      = ((((uint64_t)JSVAL_TAG_MAX_DOUBLE)     << JSVAL_TAG_SHIFT) | 0xFFFFFFFF),
    JSVAL_SHIFTED_TAG_INT32           = (((uint64_t)JSVAL_TAG_INT32)           << JSVAL_TAG_SHIFT),
    JSVAL_SHIFTED_TAG_UNDEFINED       = (((uint64_t)JSVAL_TAG_UNDEFINED)       << JSVAL_TAG_SHIFT),
    JSVAL_SHIFTED_TAG_NULL            = (((uint64_t)JSVAL_TAG_NULL)            << JSVAL_TAG_SHIFT),
    JSVAL_SHIFTED_TAG_BOOLEAN         = (((uint64_t)JSVAL_TAG_BOOLEAN)         << JSVAL_TAG_SHIFT),
    JSVAL_SHIFTED_TAG_MAGIC           = (((uint64_t)JSVAL_TAG_MAGIC)           << JSVAL_TAG_SHIFT),
    JSVAL_SHIFTED_TAG_STRING          = (((uint64_t)JSVAL_TAG_STRING)          << JSVAL_TAG_SHIFT),
    JSVAL_SHIFTED_TAG_SYMBOL          = (((uint64_t)JSVAL_TAG_SYMBOL)          << JSVAL_TAG_SHIFT),
    JSVAL_SHIFTED_TAG_PRIVATE_GCTHING = (((uint64_t)JSVAL_TAG_PRIVATE_GCTHING) << JSVAL_TAG_SHIFT),
    JSVAL_SHIFTED_TAG_OBJECT          = (((uint64_t)JSVAL_TAG_OBJECT)          << JSVAL_TAG_SHIFT)
} JS_ENUM_FOOTER(JSValueShiftedTag);

static_assert(sizeof(JSValueShiftedTag) == sizeof(uint64_t),
              "compiler typed enum support is apparently buggy");

#endif

/*
 * All our supported compilers implement C++11 |enum Foo : T| syntax, so don't
 * expose these macros. (This macro exists *only* because gcc bug 51242
 * <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51242> makes bit-fields of
 * typed enums trigger a warning that can't be turned off. Don't expose it
 * beyond this file!)
 */
#undef JS_ENUM_HEADER
#undef JS_ENUM_FOOTER

#if defined(JS_NUNBOX32)

#define JSVAL_TYPE_TO_TAG(type)      ((JSValueTag)(JSVAL_TAG_CLEAR | (type)))

#define JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET           JSVAL_TAG_OBJECT
#define JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET              JSVAL_TAG_INT32
#define JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET             JSVAL_TAG_STRING

#elif defined(JS_PUNBOX64)

#define JSVAL_PAYLOAD_MASK           0x00007FFFFFFFFFFFLL
#define JSVAL_TAG_MASK               0xFFFF800000000000LL
#define JSVAL_TYPE_TO_TAG(type)      ((JSValueTag)(JSVAL_TAG_MAX_DOUBLE | (type)))
#define JSVAL_TYPE_TO_SHIFTED_TAG(type) (((uint64_t)JSVAL_TYPE_TO_TAG(type)) << JSVAL_TAG_SHIFT)

#define JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET           JSVAL_TAG_OBJECT
#define JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET              JSVAL_TAG_INT32
#define JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET             JSVAL_TAG_STRING

#define JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET    JSVAL_SHIFTED_TAG_OBJECT
#define JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET       JSVAL_SHIFTED_TAG_UNDEFINED
#define JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET      JSVAL_SHIFTED_TAG_STRING

#endif /* JS_PUNBOX64 */

typedef enum JSWhyMagic
{
    /** a hole in a native object's elements */
    JS_ELEMENTS_HOLE,

    /** there is not a pending iterator value */
    JS_NO_ITER_VALUE,

    /** exception value thrown when closing a generator */
    JS_GENERATOR_CLOSING,

    /** compiler sentinel value */
    JS_NO_CONSTANT,

    /** used in debug builds to catch tracing errors */
    JS_THIS_POISON,

    /** used in debug builds to catch tracing errors */
    JS_ARG_POISON,

    /** an empty subnode in the AST serializer */
    JS_SERIALIZE_NO_NODE,

    /** lazy arguments value on the stack */
    JS_LAZY_ARGUMENTS,

    /** optimized-away 'arguments' value */
    JS_OPTIMIZED_ARGUMENTS,

    /** magic value passed to natives to indicate construction */
    JS_IS_CONSTRUCTING,

    /** value of static block object slot */
    JS_BLOCK_NEEDS_CLONE,

    /** see class js::HashableValue */
    JS_HASH_KEY_EMPTY,

    /** error while running Ion code */
    JS_ION_ERROR,

    /** missing recover instruction result */
    JS_ION_BAILOUT,

    /** optimized out slot */
    JS_OPTIMIZED_OUT,

    /** uninitialized lexical bindings that produce ReferenceError on touch. */
    JS_UNINITIALIZED_LEXICAL,

    /** for local use */
    JS_GENERIC_MAGIC,

    JS_WHY_MAGIC_COUNT
} JSWhyMagic;

namespace JS {

static inline constexpr JS::Value UndefinedValue();
static inline JS::Value PoisonedObjectValue(JSObject* obj);

namespace detail {

constexpr int CanonicalizedNaNSignBit = 0;
constexpr uint64_t CanonicalizedNaNSignificand = 0x8000000000000ULL;

constexpr uint64_t CanonicalizedNaNBits =
    mozilla::SpecificNaNBits<double,
                             detail::CanonicalizedNaNSignBit,
                             detail::CanonicalizedNaNSignificand>::value;

} // namespace detail

/**
 * Returns a generic quiet NaN value, with all payload bits set to zero.
 *
 * Among other properties, this NaN's bit pattern conforms to JS::Value's
 * bit pattern restrictions.
 */
static MOZ_ALWAYS_INLINE double
GenericNaN()
{
  return mozilla::SpecificNaN<double>(detail::CanonicalizedNaNSignBit,
                                      detail::CanonicalizedNaNSignificand);
}

/* MSVC with PGO miscompiles this function. */
#if defined(_MSC_VER)
# pragma optimize("g", off)
#endif
static inline double
CanonicalizeNaN(double d)
{
    if (MOZ_UNLIKELY(mozilla::IsNaN(d)))
        return GenericNaN();
    return d;
}
#if defined(_MSC_VER)
# pragma optimize("", on)
#endif

/**
 * JS::Value is the interface for a single JavaScript Engine value.  A few
 * general notes on JS::Value:
 *
 * - JS::Value has setX() and isX() members for X in
 *
 *     { Int32, Double, String, Symbol, Boolean, Undefined, Null, Object, Magic }
 *
 *   JS::Value also contains toX() for each of the non-singleton types.
 *
 * - Magic is a singleton type whose payload contains either a JSWhyMagic "reason" for
 *   the magic value or a uint32_t value. By providing JSWhyMagic values when
 *   creating and checking for magic values, it is possible to assert, at
 *   runtime, that only magic values with the expected reason flow through a
 *   particular value. For example, if cx->exception has a magic value, the
 *   reason must be JS_GENERATOR_CLOSING.
 *
 * - The JS::Value operations are preferred.  The JSVAL_* operations remain for
 *   compatibility; they may be removed at some point.  These operations mostly
 *   provide similar functionality.  But there are a few key differences.  One
 *   is that JS::Value gives null a separate type.
 *   Also, to help prevent mistakenly boxing a nullable JSObject* as an object,
 *   Value::setObject takes a JSObject&. (Conversely, Value::toObject returns a
 *   JSObject&.)  A convenience member Value::setObjectOrNull is provided.
 *
 * - JSVAL_VOID is the same as the singleton value of the Undefined type.
 *
 * - Note that JS::Value is 8 bytes on 32 and 64-bit architectures. Thus, on
 *   32-bit user code should avoid copying jsval/JS::Value as much as possible,
 *   preferring to pass by const Value&.
 */
class MOZ_NON_PARAM alignas(8) Value
{
  public:
#if defined(JS_NUNBOX32)
    using PayloadType = uint32_t;
#elif defined(JS_PUNBOX64)
    using PayloadType = uint64_t;
#endif

    /*
     * N.B. the default constructor leaves Value unitialized. Adding a default
     * constructor prevents Value from being stored in a union.
     */
    Value() = default;
    Value(const Value& v) = default;

    /**
     * Returns false if creating a NumberValue containing the given type would
     * be lossy, true otherwise.
     */
    template <typename T>
    static bool isNumberRepresentable(const T t) {
        return T(double(t)) == t;
    }

    /*** Mutators ***/

    void setNull() {
        data.asBits = bitsFromTagAndPayload(JSVAL_TAG_NULL, 0);
    }

    void setUndefined() {
        data.asBits = bitsFromTagAndPayload(JSVAL_TAG_UNDEFINED, 0);
    }

    void setInt32(int32_t i) {
        data.asBits = bitsFromTagAndPayload(JSVAL_TAG_INT32, uint32_t(i));
    }

    int32_t& getInt32Ref() {
        MOZ_ASSERT(isInt32());
        return data.s.payload.i32;
    }

    void setDouble(double d) {
        // Don't assign to data.asDouble to fix a miscompilation with
        // GCC 5.2.1 and 5.3.1. See bug 1312488.
        data = layout(d);
        MOZ_ASSERT(isDouble());
    }

    void setNaN() {
        setDouble(GenericNaN());
    }

    double& getDoubleRef() {
        MOZ_ASSERT(isDouble());
        return data.asDouble;
    }

    void setString(JSString* str) {
        MOZ_ASSERT(uintptr_t(str) > 0x1000);
        data.asBits = bitsFromTagAndPayload(JSVAL_TAG_STRING, PayloadType(str));
    }

    void setSymbol(JS::Symbol* sym) {
        MOZ_ASSERT(uintptr_t(sym) > 0x1000);
        data.asBits = bitsFromTagAndPayload(JSVAL_TAG_SYMBOL, PayloadType(sym));
    }

    void setObject(JSObject& obj) {
        MOZ_ASSERT(uintptr_t(&obj) > 0x1000 || uintptr_t(&obj) == 0x48);
#if defined(JS_PUNBOX64)
        // VisualStudio cannot contain parenthesized C++ style cast and shift
        // inside decltype in template parameter:
        //   AssertionConditionType<decltype((uintptr_t(x) >> 1))>
        // It throws syntax error.
        MOZ_ASSERT((((uintptr_t)&obj) >> JSVAL_TAG_SHIFT) == 0);
#endif
        setObjectNoCheck(&obj);
    }

  private:
    void setObjectNoCheck(JSObject* obj) {
        data.asBits = bitsFromTagAndPayload(JSVAL_TAG_OBJECT, PayloadType(obj));
    }

    friend inline Value PoisonedObjectValue(JSObject* obj);

  public:
    void setBoolean(bool b) {
        data.asBits = bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN, uint32_t(b));
    }

    void setMagic(JSWhyMagic why) {
        data.asBits = bitsFromTagAndPayload(JSVAL_TAG_MAGIC, uint32_t(why));
    }

    void setMagicUint32(uint32_t payload) {
        data.asBits = bitsFromTagAndPayload(JSVAL_TAG_MAGIC, payload);
    }

    bool setNumber(uint32_t ui) {
        if (ui > JSVAL_INT_MAX) {
            setDouble((double)ui);
            return false;
        } else {
            setInt32((int32_t)ui);
            return true;
        }
    }

    bool setNumber(double d) {
        int32_t i;
        if (mozilla::NumberIsInt32(d, &i)) {
            setInt32(i);
            return true;
        }

        setDouble(d);
        return false;
    }

    void setObjectOrNull(JSObject* arg) {
        if (arg)
            setObject(*arg);
        else
            setNull();
    }

    void swap(Value& rhs) {
        uint64_t tmp = rhs.data.asBits;
        rhs.data.asBits = data.asBits;
        data.asBits = tmp;
    }

  private:
    JSValueTag toTag() const {
#if defined(JS_NUNBOX32)
        return data.s.tag;
#elif defined(JS_PUNBOX64)
        return JSValueTag(data.asBits >> JSVAL_TAG_SHIFT);
#endif
    }

  public:
    /*** JIT-only interfaces to interact with and create raw Values ***/
#if defined(JS_NUNBOX32)
    PayloadType toNunboxPayload() const {
        return static_cast<PayloadType>(data.s.payload.i32);
    }

    JSValueTag toNunboxTag() const {
        return data.s.tag;
    }
#elif defined(JS_PUNBOX64)
    const void* bitsAsPunboxPointer() const {
        return reinterpret_cast<void*>(data.asBits);
    }
#endif

    /*** Value type queries ***/

    /*
     * N.B. GCC, in some but not all cases, chooses to emit signed comparison
     * of JSValueTag even though its underlying type has been forced to be
     * uint32_t.  Thus, all comparisons should explicitly cast operands to
     * uint32_t.
     */

    bool isUndefined() const {
#if defined(JS_NUNBOX32)
        return toTag() == JSVAL_TAG_UNDEFINED;
#elif defined(JS_PUNBOX64)
        return data.asBits == JSVAL_SHIFTED_TAG_UNDEFINED;
#endif
    }

    bool isNull() const {
#if defined(JS_NUNBOX32)
        return toTag() == JSVAL_TAG_NULL;
#elif defined(JS_PUNBOX64)
        return data.asBits == JSVAL_SHIFTED_TAG_NULL;
#endif
    }

    bool isNullOrUndefined() const {
        return isNull() || isUndefined();
    }

    bool isInt32() const {
        return toTag() == JSVAL_TAG_INT32;
    }

    bool isInt32(int32_t i32) const {
        return data.asBits == bitsFromTagAndPayload(JSVAL_TAG_INT32, uint32_t(i32));
    }

    bool isDouble() const {
#if defined(JS_NUNBOX32)
        return uint32_t(toTag()) <= uint32_t(JSVAL_TAG_CLEAR);
#elif defined(JS_PUNBOX64)
        return (data.asBits | mozilla::DoubleTypeTraits::kSignBit) <= JSVAL_SHIFTED_TAG_MAX_DOUBLE;
#endif
    }

    bool isNumber() const {
#if defined(JS_NUNBOX32)
        MOZ_ASSERT(toTag() != JSVAL_TAG_CLEAR);
        return uint32_t(toTag()) <= uint32_t(JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET);
#elif defined(JS_PUNBOX64)
        return data.asBits < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET;
#endif
    }

    bool isString() const {
        return toTag() == JSVAL_TAG_STRING;
    }

    bool isSymbol() const {
        return toTag() == JSVAL_TAG_SYMBOL;
    }

    bool isObject() const {
#if defined(JS_NUNBOX32)
        return toTag() == JSVAL_TAG_OBJECT;
#elif defined(JS_PUNBOX64)
        MOZ_ASSERT((data.asBits >> JSVAL_TAG_SHIFT) <= JSVAL_TAG_OBJECT);
        return data.asBits >= JSVAL_SHIFTED_TAG_OBJECT;
#endif
    }

    bool isPrimitive() const {
#if defined(JS_NUNBOX32)
        return uint32_t(toTag()) < uint32_t(JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET);
#elif defined(JS_PUNBOX64)
        return data.asBits < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET;
#endif
    }

    bool isObjectOrNull() const {
        return isObject() || isNull();
    }

    bool isGCThing() const {
#if defined(JS_NUNBOX32)
        /* gcc sometimes generates signed < without explicit casts. */
        return uint32_t(toTag()) >= uint32_t(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET);
#elif defined(JS_PUNBOX64)
        return data.asBits >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET;
#endif
    }

    bool isBoolean() const {
        return toTag() == JSVAL_TAG_BOOLEAN;
    }

    bool isTrue() const {
        return data.asBits == bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN, uint32_t(true));
    }

    bool isFalse() const {
        return data.asBits == bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN, uint32_t(false));
    }

    bool isMagic() const {
        return toTag() == JSVAL_TAG_MAGIC;
    }

    bool isMagic(JSWhyMagic why) const {
        MOZ_ASSERT_IF(isMagic(), data.s.payload.why == why);
        return isMagic();
    }

    JS::TraceKind traceKind() const {
        MOZ_ASSERT(isGCThing());
        static_assert((JSVAL_TAG_STRING & 0x03) == size_t(JS::TraceKind::String),
                      "Value type tags must correspond with JS::TraceKinds.");
        static_assert((JSVAL_TAG_SYMBOL & 0x03) == size_t(JS::TraceKind::Symbol),
                      "Value type tags must correspond with JS::TraceKinds.");
        static_assert((JSVAL_TAG_OBJECT & 0x03) == size_t(JS::TraceKind::Object),
                      "Value type tags must correspond with JS::TraceKinds.");
        if (MOZ_UNLIKELY(isPrivateGCThing()))
            return JS::GCThingTraceKind(toGCThing());
        return JS::TraceKind(toTag() & 0x03);
    }

    JSWhyMagic whyMagic() const {
        MOZ_ASSERT(isMagic());
        return data.s.payload.why;
    }

    uint32_t magicUint32() const {
        MOZ_ASSERT(isMagic());
        return data.s.payload.u32;
    }

    /*** Comparison ***/

    bool operator==(const Value& rhs) const {
        return data.asBits == rhs.data.asBits;
    }

    bool operator!=(const Value& rhs) const {
        return data.asBits != rhs.data.asBits;
    }

    friend inline bool SameType(const Value& lhs, const Value& rhs);

    /*** Extract the value's typed payload ***/

    int32_t toInt32() const {
        MOZ_ASSERT(isInt32());
#if defined(JS_NUNBOX32)
        return data.s.payload.i32;
#elif defined(JS_PUNBOX64)
        return int32_t(data.asBits);
#endif
    }

    double toDouble() const {
        MOZ_ASSERT(isDouble());
        return data.asDouble;
    }

    double toNumber() const {
        MOZ_ASSERT(isNumber());
        return isDouble() ? toDouble() : double(toInt32());
    }

    JSString* toString() const {
        MOZ_ASSERT(isString());
#if defined(JS_NUNBOX32)
        return data.s.payload.str;
#elif defined(JS_PUNBOX64)
        return reinterpret_cast<JSString*>(data.asBits & JSVAL_PAYLOAD_MASK);
#endif
    }

    JS::Symbol* toSymbol() const {
        MOZ_ASSERT(isSymbol());
#if defined(JS_NUNBOX32)
        return data.s.payload.sym;
#elif defined(JS_PUNBOX64)
        return reinterpret_cast<JS::Symbol*>(data.asBits & JSVAL_PAYLOAD_MASK);
#endif
    }

    JSObject& toObject() const {
        MOZ_ASSERT(isObject());
#if defined(JS_NUNBOX32)
        return *data.s.payload.obj;
#elif defined(JS_PUNBOX64)
        return *toObjectOrNull();
#endif
    }

    JSObject* toObjectOrNull() const {
        MOZ_ASSERT(isObjectOrNull());
#if defined(JS_NUNBOX32)
        return data.s.payload.obj;
#elif defined(JS_PUNBOX64)
        uint64_t ptrBits = data.asBits & JSVAL_PAYLOAD_MASK;
        MOZ_ASSERT((ptrBits & 0x7) == 0);
        return reinterpret_cast<JSObject*>(ptrBits);
#endif
    }

    js::gc::Cell* toGCThing() const {
        MOZ_ASSERT(isGCThing());
#if defined(JS_NUNBOX32)
        return data.s.payload.cell;
#elif defined(JS_PUNBOX64)
        uint64_t ptrBits = data.asBits & JSVAL_PAYLOAD_MASK;
        MOZ_ASSERT((ptrBits & 0x7) == 0);
        return reinterpret_cast<js::gc::Cell*>(ptrBits);
#endif
    }

    GCCellPtr toGCCellPtr() const {
        return GCCellPtr(toGCThing(), traceKind());
    }

    bool toBoolean() const {
        MOZ_ASSERT(isBoolean());
#if defined(JS_NUNBOX32)
        return bool(data.s.payload.boo);
#elif defined(JS_PUNBOX64)
        return bool(data.asBits & JSVAL_PAYLOAD_MASK);
#endif
    }

    uint32_t payloadAsRawUint32() const {
        MOZ_ASSERT(!isDouble());
        return data.s.payload.u32;
    }

    uint64_t asRawBits() const {
        return data.asBits;
    }

    JSValueType extractNonDoubleType() const {
        uint32_t type = toTag() & 0xF;
        MOZ_ASSERT(type > JSVAL_TYPE_DOUBLE);
        return JSValueType(type);
    }

    /*
     * Private API
     *
     * Private setters/getters allow the caller to read/write arbitrary types
     * that fit in the 64-bit payload. It is the caller's responsibility, after
     * storing to a value with setPrivateX to read only using getPrivateX.
     * Privates values are given a type which ensures they are not marked.
     */

    void setPrivate(void* ptr) {
        MOZ_ASSERT((uintptr_t(ptr) & 1) == 0);
#if defined(JS_NUNBOX32)
        data.s.tag = JSValueTag(0);
        data.s.payload.ptr = ptr;
#elif defined(JS_PUNBOX64)
        data.asBits = uintptr_t(ptr) >> 1;
#endif
        MOZ_ASSERT(isDouble());
    }

    void* toPrivate() const {
        MOZ_ASSERT(isDouble());
#if defined(JS_NUNBOX32)
        return data.s.payload.ptr;
#elif defined(JS_PUNBOX64)
        MOZ_ASSERT((data.asBits & 0x8000000000000000ULL) == 0);
        return reinterpret_cast<void*>(data.asBits << 1);
#endif
    }

    void setPrivateUint32(uint32_t ui) {
        MOZ_ASSERT(uint32_t(int32_t(ui)) == ui);
        setInt32(int32_t(ui));
    }

    uint32_t toPrivateUint32() const {
        return uint32_t(toInt32());
    }

    /*
     * Private GC Thing API
     *
     * Non-JSObject, JSString, and JS::Symbol cells may be put into the 64-bit
     * payload as private GC things. Such Values are considered isGCThing(), and
     * as such, automatically marked. Their traceKind() is gotten via their
     * cells.
     */

    void setPrivateGCThing(js::gc::Cell* cell) {
        MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::String,
                   "Private GC thing Values must not be strings. Make a StringValue instead.");
        MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Symbol,
                   "Private GC thing Values must not be symbols. Make a SymbolValue instead.");
        MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Object,
                   "Private GC thing Values must not be objects. Make an ObjectValue instead.");

        MOZ_ASSERT(uintptr_t(cell) > 0x1000);
#if defined(JS_PUNBOX64)
        // VisualStudio cannot contain parenthesized C++ style cast and shift
        // inside decltype in template parameter:
        //   AssertionConditionType<decltype((uintptr_t(x) >> 1))>
        // It throws syntax error.
        MOZ_ASSERT((((uintptr_t)cell) >> JSVAL_TAG_SHIFT) == 0);
#endif
        data.asBits = bitsFromTagAndPayload(JSVAL_TAG_PRIVATE_GCTHING, PayloadType(cell));
    }

    bool isPrivateGCThing() const {
        return toTag() == JSVAL_TAG_PRIVATE_GCTHING;
    }

    const size_t* payloadWord() const {
#if defined(JS_NUNBOX32)
        return &data.s.payload.word;
#elif defined(JS_PUNBOX64)
        return &data.asWord;
#endif
    }

    const uintptr_t* payloadUIntPtr() const {
#if defined(JS_NUNBOX32)
        return &data.s.payload.uintptr;
#elif defined(JS_PUNBOX64)
        return &data.asUIntPtr;
#endif
    }

#if !defined(_MSC_VER) && !defined(__sparc)
  // Value must be POD so that MSVC will pass it by value and not in memory
  // (bug 689101); the same is true for SPARC as well (bug 737344).  More
  // precisely, we don't want Value return values compiled as out params.
  private:
#endif

#if MOZ_LITTLE_ENDIAN
# if defined(JS_NUNBOX32)
    union layout {
        uint64_t asBits;
        struct {
            union {
                int32_t        i32;
                uint32_t       u32;
                uint32_t       boo;     // Don't use |bool| -- it must be four bytes.
                JSString*      str;
                JS::Symbol*    sym;
                JSObject*      obj;
                js::gc::Cell*  cell;
                void*          ptr;
                JSWhyMagic     why;
                size_t         word;
                uintptr_t      uintptr;
            } payload;
            JSValueTag tag;
        } s;
        double asDouble;
        void* asPtr;

        layout() = default;
        explicit constexpr layout(uint64_t bits) : asBits(bits) {}
        explicit constexpr layout(double d) : asDouble(d) {}
    } data;
# elif defined(JS_PUNBOX64)
    union layout {
        uint64_t asBits;
#if !defined(_WIN64)
        /* MSVC does not pack these correctly :-( */
        struct {
            uint64_t           payload47 : 47;
            JSValueTag         tag : 17;
        } debugView;
#endif
        struct {
            union {
                int32_t        i32;
                uint32_t       u32;
                JSWhyMagic     why;
            } payload;
        } s;
        double asDouble;
        void* asPtr;
        size_t asWord;
        uintptr_t asUIntPtr;

        layout() = default;
        explicit constexpr layout(uint64_t bits) : asBits(bits) {}
        explicit constexpr layout(double d) : asDouble(d) {}
    } data;
# endif  /* JS_PUNBOX64 */
#else   /* MOZ_LITTLE_ENDIAN */
# if defined(JS_NUNBOX32)
    union layout {
        uint64_t asBits;
        struct {
            JSValueTag tag;
            union {
                int32_t        i32;
                uint32_t       u32;
                uint32_t       boo;     // Don't use |bool| -- it must be four bytes.
                JSString*      str;
                JS::Symbol*    sym;
                JSObject*      obj;
                js::gc::Cell*  cell;
                void*          ptr;
                JSWhyMagic     why;
                size_t         word;
                uintptr_t      uintptr;
            } payload;
        } s;
        double asDouble;
        void* asPtr;

        layout() = default;
        explicit constexpr layout(uint64_t bits) : asBits(bits) {}
        explicit constexpr layout(double d) : asDouble(d) {}
    } data;
# elif defined(JS_PUNBOX64)
    union layout {
        uint64_t asBits;
        struct {
            JSValueTag         tag : 17;
            uint64_t           payload47 : 47;
        } debugView;
        struct {
            uint32_t           padding;
            union {
                int32_t        i32;
                uint32_t       u32;
                JSWhyMagic     why;
            } payload;
        } s;
        double asDouble;
        void* asPtr;
        size_t asWord;
        uintptr_t asUIntPtr;

        layout() = default;
        explicit constexpr layout(uint64_t bits) : asBits(bits) {}
        explicit constexpr layout(double d) : asDouble(d) {}
    } data;
# endif /* JS_PUNBOX64 */
#endif  /* MOZ_LITTLE_ENDIAN */

  private:
    explicit constexpr Value(uint64_t asBits) : data(asBits) {}
    explicit constexpr Value(double d) : data(d) {}

    void staticAssertions() {
        JS_STATIC_ASSERT(sizeof(JSValueType) == 1);
        JS_STATIC_ASSERT(sizeof(JSValueTag) == 4);
        JS_STATIC_ASSERT(sizeof(JSWhyMagic) <= 4);
        JS_STATIC_ASSERT(sizeof(Value) == 8);
    }

    friend constexpr Value JS::UndefinedValue();

  public:
    static constexpr uint64_t
    bitsFromTagAndPayload(JSValueTag tag, PayloadType payload)
    {
#if defined(JS_NUNBOX32)
        return (uint64_t(uint32_t(tag)) << 32) | payload;
#elif defined(JS_PUNBOX64)
        return (uint64_t(uint32_t(tag)) << JSVAL_TAG_SHIFT) | payload;
#endif
    }

    static constexpr Value
    fromTagAndPayload(JSValueTag tag, PayloadType payload)
    {
        return fromRawBits(bitsFromTagAndPayload(tag, payload));
    }

    static constexpr Value
    fromRawBits(uint64_t asBits) {
        return Value(asBits);
    }

    static constexpr Value
    fromInt32(int32_t i) {
        return fromTagAndPayload(JSVAL_TAG_INT32, uint32_t(i));
    }

    static constexpr Value
    fromDouble(double d) {
        return Value(d);
    }
} JS_HAZ_GC_POINTER;

static_assert(sizeof(Value) == 8, "Value size must leave three tag bits, be a binary power, and is ubiquitously depended upon everywhere");

inline bool
IsOptimizedPlaceholderMagicValue(const Value& v)
{
    if (v.isMagic()) {
        MOZ_ASSERT(v.whyMagic() == JS_OPTIMIZED_ARGUMENTS || v.whyMagic() == JS_OPTIMIZED_OUT);
        return true;
    }
    return false;
}

static MOZ_ALWAYS_INLINE void
ExposeValueToActiveJS(const Value& v)
{
#ifdef DEBUG
    Value tmp = v;
    MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarrieredSlow(&tmp));
#endif
    if (v.isGCThing())
        js::gc::ExposeGCThingToActiveJS(GCCellPtr(v));
}

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

static inline MOZ_MAY_CALL_AFTER_MUST_RETURN Value
NullValue()
{
    Value v;
    v.setNull();
    return v;
}

static inline constexpr Value
UndefinedValue()
{
    return Value::fromTagAndPayload(JSVAL_TAG_UNDEFINED, 0);
}

static inline constexpr Value
Int32Value(int32_t i32)
{
    return Value::fromInt32(i32);
}

static inline Value
DoubleValue(double dbl)
{
    Value v;
    v.setDouble(dbl);
    return v;
}

static inline Value
CanonicalizedDoubleValue(double d)
{
    return MOZ_UNLIKELY(mozilla::IsNaN(d))
           ? Value::fromRawBits(detail::CanonicalizedNaNBits)
           : Value::fromDouble(d);
}

static inline bool
IsCanonicalized(double d)
{
  if (mozilla::IsInfinite(d) || mozilla::IsFinite(d))
      return true;

  uint64_t bits;
  mozilla::BitwiseCast<uint64_t>(d, &bits);
  return (bits & ~mozilla::DoubleTypeTraits::kSignBit) == detail::CanonicalizedNaNBits;
}

static inline Value
DoubleNaNValue()
{
    Value v;
    v.setNaN();
    return v;
}

static inline Value
Float32Value(float f)
{
    Value v;
    v.setDouble(f);
    return v;
}

static inline Value
StringValue(JSString* str)
{
    Value v;
    v.setString(str);
    return v;
}

static inline Value
SymbolValue(JS::Symbol* sym)
{
    Value v;
    v.setSymbol(sym);
    return v;
}

static inline Value
BooleanValue(bool boo)
{
    Value v;
    v.setBoolean(boo);
    return v;
}

static inline Value
TrueValue()
{
    Value v;
    v.setBoolean(true);
    return v;
}

static inline Value
FalseValue()
{
    Value v;
    v.setBoolean(false);
    return v;
}

static inline Value
ObjectValue(JSObject& obj)
{
    Value v;
    v.setObject(obj);
    return v;
}

static inline Value
ObjectValueCrashOnTouch()
{
    Value v;
    v.setObject(*reinterpret_cast<JSObject*>(0x48));
    return v;
}

static inline Value
MagicValue(JSWhyMagic why)
{
    Value v;
    v.setMagic(why);
    return v;
}

static inline Value
MagicValueUint32(uint32_t payload)
{
    Value v;
    v.setMagicUint32(payload);
    return v;
}

static inline Value
NumberValue(float f)
{
    Value v;
    v.setNumber(f);
    return v;
}

static inline Value
NumberValue(double dbl)
{
    Value v;
    v.setNumber(dbl);
    return v;
}

static inline Value
NumberValue(int8_t i)
{
    return Int32Value(i);
}

static inline Value
NumberValue(uint8_t i)
{
    return Int32Value(i);
}

static inline Value
NumberValue(int16_t i)
{
    return Int32Value(i);
}

static inline Value
NumberValue(uint16_t i)
{
    return Int32Value(i);
}

static inline Value
NumberValue(int32_t i)
{
    return Int32Value(i);
}

static inline constexpr Value
NumberValue(uint32_t i)
{
    return i <= JSVAL_INT_MAX
           ? Int32Value(int32_t(i))
           : Value::fromDouble(double(i));
}

namespace detail {

template <bool Signed>
class MakeNumberValue
{
  public:
    template<typename T>
    static inline Value create(const T t)
    {
        Value v;
        if (JSVAL_INT_MIN <= t && t <= JSVAL_INT_MAX)
            v.setInt32(int32_t(t));
        else
            v.setDouble(double(t));
        return v;
    }
};

template <>
class MakeNumberValue<false>
{
  public:
    template<typename T>
    static inline Value create(const T t)
    {
        Value v;
        if (t <= JSVAL_INT_MAX)
            v.setInt32(int32_t(t));
        else
            v.setDouble(double(t));
        return v;
    }
};

} // namespace detail

template <typename T>
static inline Value
NumberValue(const T t)
{
    MOZ_ASSERT(Value::isNumberRepresentable(t), "value creation would be lossy");
    return detail::MakeNumberValue<std::numeric_limits<T>::is_signed>::create(t);
}

static inline Value
ObjectOrNullValue(JSObject* obj)
{
    Value v;
    v.setObjectOrNull(obj);
    return v;
}

static inline Value
PrivateValue(void* ptr)
{
    Value v;
    v.setPrivate(ptr);
    return v;
}

static inline Value
PrivateUint32Value(uint32_t ui)
{
    Value v;
    v.setPrivateUint32(ui);
    return v;
}

static inline Value
PrivateGCThingValue(js::gc::Cell* cell)
{
    Value v;
    v.setPrivateGCThing(cell);
    return v;
}

static inline Value
PoisonedObjectValue(JSObject* obj)
{
    Value v;
    v.setObjectNoCheck(obj);
    return v;
}

inline bool
SameType(const Value& lhs, const Value& rhs)
{
#if defined(JS_NUNBOX32)
    JSValueTag ltag = lhs.toTag(), rtag = rhs.toTag();
    return ltag == rtag || (ltag < JSVAL_TAG_CLEAR && rtag < JSVAL_TAG_CLEAR);
#elif defined(JS_PUNBOX64)
    return (lhs.isDouble() && rhs.isDouble()) ||
           (((lhs.data.asBits ^ rhs.data.asBits) & 0xFFFF800000000000ULL) == 0);
#endif
}

} // namespace JS

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

namespace JS {
JS_PUBLIC_API(void) HeapValuePostBarrier(Value* valuep, const Value& prev, const Value& next);

template <>
struct GCPolicy<JS::Value>
{
    static Value initial() { return UndefinedValue(); }
    static void trace(JSTracer* trc, Value* v, const char* name) {
        js::UnsafeTraceManuallyBarrieredEdge(trc, v, name);
    }
    static bool isTenured(const Value& thing) {
        return !thing.isGCThing() || !IsInsideNursery(thing.toGCThing());
    }
};

} // namespace JS

namespace js {

template <>
struct BarrierMethods<JS::Value>
{
    static gc::Cell* asGCThingOrNull(const JS::Value& v) {
        return v.isGCThing() ? v.toGCThing() : nullptr;
    }
    static void postBarrier(JS::Value* v, const JS::Value& prev, const JS::Value& next) {
        JS::HeapValuePostBarrier(v, prev, next);
    }
    static void exposeToJS(const JS::Value& v) {
        JS::ExposeValueToActiveJS(v);
    }
};

template <class Wrapper> class MutableValueOperations;

/**
 * A class designed for CRTP use in implementing the non-mutating parts of the
 * Value interface in Value-like classes.  Wrapper must be a class inheriting
 * ValueOperations<Wrapper> with a visible get() method returning a const
 * reference to the Value abstracted by Wrapper.
 */
template <class Wrapper>
class WrappedPtrOperations<JS::Value, Wrapper>
{
    const JS::Value& value() const { return static_cast<const Wrapper*>(this)->get(); }

  public:
    bool isUndefined() const { return value().isUndefined(); }
    bool isNull() const { return value().isNull(); }
    bool isBoolean() const { return value().isBoolean(); }
    bool isTrue() const { return value().isTrue(); }
    bool isFalse() const { return value().isFalse(); }
    bool isNumber() const { return value().isNumber(); }
    bool isInt32() const { return value().isInt32(); }
    bool isInt32(int32_t i32) const { return value().isInt32(i32); }
    bool isDouble() const { return value().isDouble(); }
    bool isString() const { return value().isString(); }
    bool isSymbol() const { return value().isSymbol(); }
    bool isObject() const { return value().isObject(); }
    bool isMagic() const { return value().isMagic(); }
    bool isMagic(JSWhyMagic why) const { return value().isMagic(why); }
    bool isGCThing() const { return value().isGCThing(); }
    bool isPrimitive() const { return value().isPrimitive(); }

    bool isNullOrUndefined() const { return value().isNullOrUndefined(); }
    bool isObjectOrNull() const { return value().isObjectOrNull(); }

    bool toBoolean() const { return value().toBoolean(); }
    double toNumber() const { return value().toNumber(); }
    int32_t toInt32() const { return value().toInt32(); }
    double toDouble() const { return value().toDouble(); }
    JSString* toString() const { return value().toString(); }
    JS::Symbol* toSymbol() const { return value().toSymbol(); }
    JSObject& toObject() const { return value().toObject(); }
    JSObject* toObjectOrNull() const { return value().toObjectOrNull(); }
    gc::Cell* toGCThing() const { return value().toGCThing(); }
    JS::TraceKind traceKind() const { return value().traceKind(); }
    void* toPrivate() const { return value().toPrivate(); }
    uint32_t toPrivateUint32() const { return value().toPrivateUint32(); }

    uint64_t asRawBits() const { return value().asRawBits(); }
    JSValueType extractNonDoubleType() const { return value().extractNonDoubleType(); }

    JSWhyMagic whyMagic() const { return value().whyMagic(); }
    uint32_t magicUint32() const { return value().magicUint32(); }
};

/**
 * A class designed for CRTP use in implementing all the mutating parts of the
 * Value interface in Value-like classes.  Wrapper must be a class inheriting
 * MutableWrappedPtrOperations<Wrapper> with visible get() methods returning const and
 * non-const references to the Value abstracted by Wrapper.
 */
template <class Wrapper>
class MutableWrappedPtrOperations<JS::Value, Wrapper> : public WrappedPtrOperations<JS::Value, Wrapper>
{
    JS::Value& value() { return static_cast<Wrapper*>(this)->get(); }

  public:
    void setNull() { value().setNull(); }
    void setUndefined() { value().setUndefined(); }
    void setInt32(int32_t i) { value().setInt32(i); }
    void setDouble(double d) { value().setDouble(d); }
    void setNaN() { setDouble(JS::GenericNaN()); }
    void setBoolean(bool b) { value().setBoolean(b); }
    void setMagic(JSWhyMagic why) { value().setMagic(why); }
    bool setNumber(uint32_t ui) { return value().setNumber(ui); }
    bool setNumber(double d) { return value().setNumber(d); }
    void setString(JSString* str) { this->value().setString(str); }
    void setSymbol(JS::Symbol* sym) { this->value().setSymbol(sym); }
    void setObject(JSObject& obj) { this->value().setObject(obj); }
    void setObjectOrNull(JSObject* arg) { this->value().setObjectOrNull(arg); }
    void setPrivate(void* ptr) { this->value().setPrivate(ptr); }
    void setPrivateUint32(uint32_t ui) { this->value().setPrivateUint32(ui); }
    void setPrivateGCThing(js::gc::Cell* cell) { this->value().setPrivateGCThing(cell); }
};

/*
 * Augment the generic Heap<T> interface when T = Value with
 * type-querying, value-extracting, and mutating operations.
 */
template <typename Wrapper>
class HeapBase<JS::Value, Wrapper> : public WrappedPtrOperations<JS::Value, Wrapper>
{
    void setBarriered(const JS::Value& v) {
        *static_cast<JS::Heap<JS::Value>*>(this) = v;
    }

  public:
    void setNull() { setBarriered(JS::NullValue()); }
    void setUndefined() { setBarriered(JS::UndefinedValue()); }
    void setInt32(int32_t i) { setBarriered(JS::Int32Value(i)); }
    void setDouble(double d) { setBarriered(JS::DoubleValue(d)); }
    void setNaN() { setDouble(JS::GenericNaN()); }
    void setBoolean(bool b) { setBarriered(JS::BooleanValue(b)); }
    void setMagic(JSWhyMagic why) { setBarriered(JS::MagicValue(why)); }
    void setString(JSString* str) { setBarriered(JS::StringValue(str)); }
    void setSymbol(JS::Symbol* sym) { setBarriered(JS::SymbolValue(sym)); }
    void setObject(JSObject& obj) { setBarriered(JS::ObjectValue(obj)); }
    void setPrivateGCThing(js::gc::Cell* cell) { setBarriered(JS::PrivateGCThingValue(cell)); }

    bool setNumber(uint32_t ui) {
        if (ui > JSVAL_INT_MAX) {
            setDouble((double)ui);
            return false;
        } else {
            setInt32((int32_t)ui);
            return true;
        }
    }

    bool setNumber(double d) {
        int32_t i;
        if (mozilla::NumberIsInt32(d, &i)) {
            setInt32(i);
            return true;
        }

        setDouble(d);
        return false;
    }

    void setObjectOrNull(JSObject* arg) {
        if (arg)
            setObject(*arg);
        else
            setNull();
    }
};

/*
 * If the Value is a GC pointer type, convert to that type and call |f| with
 * the pointer. If the Value is not a GC type, calls F::defaultValue.
 */
template <typename F, typename... Args>
auto
DispatchTyped(F f, const JS::Value& val, Args&&... args)
  -> decltype(f(static_cast<JSObject*>(nullptr), mozilla::Forward<Args>(args)...))
{
    if (val.isString())
        return f(val.toString(), mozilla::Forward<Args>(args)...);
    if (val.isObject())
        return f(&val.toObject(), mozilla::Forward<Args>(args)...);
    if (val.isSymbol())
        return f(val.toSymbol(), mozilla::Forward<Args>(args)...);
    if (MOZ_UNLIKELY(val.isPrivateGCThing()))
        return DispatchTyped(f, val.toGCCellPtr(), mozilla::Forward<Args>(args)...);
    MOZ_ASSERT(!val.isGCThing());
    return F::defaultValue(val);
}

template <class S> struct VoidDefaultAdaptor { static void defaultValue(const S&) {} };
template <class S> struct IdentityDefaultAdaptor { static S defaultValue(const S& v) {return v;} };
template <class S, bool v> struct BoolDefaultAdaptor { static bool defaultValue(const S&) { return v; } };

} // namespace js

#ifdef DEBUG
namespace JS {

MOZ_ALWAYS_INLINE bool
ValueIsNotGray(const Value& value)
{
    if (!value.isGCThing())
        return true;

    return CellIsNotGray(value.toGCThing());
}

MOZ_ALWAYS_INLINE bool
ValueIsNotGray(const Heap<Value>& value)
{
    return ValueIsNotGray(value.unbarrieredGet());
}

} // namespace JS
#endif

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

namespace JS {

extern JS_PUBLIC_DATA(const HandleValue) NullHandleValue;
extern JS_PUBLIC_DATA(const HandleValue) UndefinedHandleValue;
extern JS_PUBLIC_DATA(const HandleValue) TrueHandleValue;
extern JS_PUBLIC_DATA(const HandleValue) FalseHandleValue;

} // namespace JS

#endif /* js_Value_h */