diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /js/public/Value.h | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/public/Value.h')
-rw-r--r-- | js/public/Value.h | 1509 |
1 files changed, 1509 insertions, 0 deletions
diff --git a/js/public/Value.h b/js/public/Value.h new file mode 100644 index 000000000..00fdad586 --- /dev/null +++ b/js/public/Value.h @@ -0,0 +1,1509 @@ +/* -*- 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_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_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() = 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) +{ + 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 */ |