/* -*- 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

/* Remember to propagate changes to the C defines below. */
JS_ENUM_HEADER(JSValueType, uint8_t)
{
    JSVAL_TYPE_DOUBLE              = 0x00,
    JSVAL_TYPE_INT32               = 0x01,
    JSVAL_TYPE_UNDEFINED           = 0x02,
    JSVAL_TYPE_BOOLEAN             = 0x03,
    JSVAL_TYPE_MAGIC               = 0x04,
    JSVAL_TYPE_STRING              = 0x05,
    JSVAL_TYPE_SYMBOL              = 0x06,
    JSVAL_TYPE_PRIVATE_GCTHING     = 0x07,
    JSVAL_TYPE_NULL                = 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)

/* Remember to propagate changes to the C defines below. */
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_STRING               = JSVAL_TAG_CLEAR | JSVAL_TYPE_STRING,
    JSVAL_TAG_SYMBOL               = JSVAL_TAG_CLEAR | JSVAL_TYPE_SYMBOL,
    JSVAL_TAG_BOOLEAN              = JSVAL_TAG_CLEAR | JSVAL_TYPE_BOOLEAN,
    JSVAL_TAG_MAGIC                = JSVAL_TAG_CLEAR | JSVAL_TYPE_MAGIC,
    JSVAL_TAG_NULL                 = JSVAL_TAG_CLEAR | JSVAL_TYPE_NULL,
    JSVAL_TAG_OBJECT               = JSVAL_TAG_CLEAR | JSVAL_TYPE_OBJECT,
    JSVAL_TAG_PRIVATE_GCTHING      = JSVAL_TAG_CLEAR | JSVAL_TYPE_PRIVATE_GCTHING
} JS_ENUM_FOOTER(JSValueTag);

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

#elif defined(JS_PUNBOX64)

/* Remember to propagate changes to the C defines below. */
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_STRING               = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_STRING,
    JSVAL_TAG_SYMBOL               = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_SYMBOL,
    JSVAL_TAG_BOOLEAN              = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN,
    JSVAL_TAG_MAGIC                = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_MAGIC,
    JSVAL_TAG_NULL                 = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_NULL,
    JSVAL_TAG_OBJECT               = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_OBJECT,
    JSVAL_TAG_PRIVATE_GCTHING      = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_PRIVATE_GCTHING
} 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_STRING          = (((uint64_t)JSVAL_TAG_STRING)          << JSVAL_TAG_SHIFT),
    JSVAL_SHIFTED_TAG_SYMBOL          = (((uint64_t)JSVAL_TAG_SYMBOL)          << 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_NULL            = (((uint64_t)JSVAL_TAG_NULL)            << JSVAL_TAG_SHIFT),
    JSVAL_SHIFTED_TAG_OBJECT          = (((uint64_t)JSVAL_TAG_OBJECT)          << JSVAL_TAG_SHIFT),
    JSVAL_SHIFTED_TAG_PRIVATE_GCTHING = (((uint64_t)JSVAL_TAG_PRIVATE_GCTHING) << 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_RAW64_UNDEFINED        (uint64_t(JSVAL_TAG_UNDEFINED) << 32)

#define JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET         JSVAL_TAG_NULL
#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_RAW64_UNDEFINED        (uint64_t(JSVAL_TAG_UNDEFINED) << JSVAL_TAG_SHIFT)

#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_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET         JSVAL_TAG_NULL
#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_LOWER_INCL_SHIFTED_TAG_OF_OBJ_OR_NULL_SET  JSVAL_SHIFTED_TAG_NULL
#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 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 {
        MOZ_ASSERT(uint32_t(toTag()) <= uint32_t(JSVAL_TAG_OBJECT));
#if defined(JS_NUNBOX32)
        return uint32_t(toTag()) >= uint32_t(JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET);
#elif defined(JS_PUNBOX64)
        return data.asBits >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_OBJ_OR_NULL_SET;
#endif
    }

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

    bool isMarkable() const {
        return isGCThing() && !isNull();
    }

    JS::TraceKind traceKind() const {
        MOZ_ASSERT(isMarkable());
        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
    }

    js::gc::Cell* toMarkablePointer() const {
        MOZ_ASSERT(isMarkable());
        return toGCThing();
    }

    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 isMarkable()
     * and 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() : asBits(JSVAL_RAW64_UNDEFINED) {}
        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() : asBits(JSVAL_RAW64_UNDEFINED) {}
        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() : asBits(JSVAL_RAW64_UNDEFINED) {}
        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() : asBits(JSVAL_RAW64_UNDEFINED) {}
        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;

/**
 * This is a null-constructible structure that can convert to and from
 * a Value, allowing UninitializedValue to be stored in unions.
 */
struct MOZ_NON_PARAM alignas(8) UninitializedValue
{
  private:
    uint64_t bits;

  public:
    UninitializedValue() = default;
    UninitializedValue(const UninitializedValue&) = default;
    MOZ_IMPLICIT UninitializedValue(const Value& val) : bits(val.asRawBits()) {}

    inline uint64_t asRawBits() const {
        return bits;
    }

    inline Value& asValueRef() {
        return *reinterpret_cast<Value*>(this);
    }
    inline const Value& asValueRef() const {
        return *reinterpret_cast<const Value*>(this);
    }

    inline operator Value&() {
        return asValueRef();
    }
    inline operator Value const&() const {
        return asValueRef();
    }
    inline operator Value() const {
        return asValueRef();
    }

    inline void operator=(Value const& other) {
        asValueRef() = other;
    }
};

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

static_assert(sizeof(UninitializedValue) == sizeof(Value), "Value and UninitializedValue must be the same size");
static_assert(alignof(UninitializedValue) == alignof(Value), "Value and UninitializedValue must have same alignment");

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)
{
    if (v.isMarkable())
        js::gc::ExposeGCThingToActiveJS(GCCellPtr(v));
}

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

static inline 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.isMarkable() ? 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 Outer> class MutableValueOperations;

/**
 * A class designed for CRTP use in implementing the non-mutating parts of the
 * Value interface in Value-like classes.  Outer must be a class inheriting
 * ValueOperations<Outer> with a visible get() method returning a const
 * reference to the Value abstracted by Outer.
 */
template <class Outer>
class ValueOperations
{
    friend class MutableValueOperations<Outer>;

    const JS::Value& value() const { return static_cast<const Outer*>(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 isMarkable() const { return value().isMarkable(); }
    bool isPrimitive() const { return value().isPrimitive(); }
    bool isGCThing() const { return value().isGCThing(); }

    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.  Outer must be a class inheriting
 * MutableValueOperations<Outer> with visible get() methods returning const and
 * non-const references to the Value abstracted by Outer.
 */
template <class Outer>
class MutableValueOperations : public ValueOperations<Outer>
{
    JS::Value& value() { return static_cast<Outer*>(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 <>
class HeapBase<JS::Value> : public ValueOperations<JS::Heap<JS::Value> >
{
    typedef JS::Heap<JS::Value> Outer;

    friend class ValueOperations<Outer>;

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

template <>
class HandleBase<JS::Value> : public ValueOperations<JS::Handle<JS::Value> >
{};

template <>
class MutableHandleBase<JS::Value> : public MutableValueOperations<JS::MutableHandle<JS::Value> >
{};

template <>
class RootedBase<JS::Value> : public MutableValueOperations<JS::Rooted<JS::Value> >
{};

template <>
class PersistentRootedBase<JS::Value> : public MutableValueOperations<JS::PersistentRooted<JS::Value>>
{};

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

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

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 */
