Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/libraries/source/spidermonkey/include-win32-debug/js/Value.h
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- | ||||
* vim: set ts=8 sts=4 et sw=4 tw=99: | * vim: set ts=8 sts=2 et sw=2 tw=80: | ||||
* This Source Code Form is subject to the terms of the Mozilla Public | * 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 | * 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/. */ | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||||
/* JS::Value implementation. */ | /* JS::Value implementation. */ | ||||
#ifndef js_Value_h | #ifndef js_Value_h | ||||
#define js_Value_h | #define js_Value_h | ||||
#include "mozilla/Attributes.h" | #include "mozilla/Attributes.h" | ||||
#include "mozilla/Casting.h" | #include "mozilla/Casting.h" | ||||
#include "mozilla/Compiler.h" | |||||
#include "mozilla/EndianUtils.h" | #include "mozilla/EndianUtils.h" | ||||
#include "mozilla/FloatingPoint.h" | #include "mozilla/FloatingPoint.h" | ||||
#include "mozilla/Likely.h" | #include "mozilla/Likely.h" | ||||
#include "mozilla/Maybe.h" | |||||
#include <limits> /* for std::numeric_limits */ | #include <limits> /* for std::numeric_limits */ | ||||
#include "js-config.h" | #include "js-config.h" | ||||
#include "jstypes.h" | #include "jstypes.h" | ||||
#include "js/GCAPI.h" | #include "js/GCAPI.h" | ||||
#include "js/RootingAPI.h" | #include "js/RootingAPI.h" | ||||
#include "js/Utility.h" | #include "js/Utility.h" | ||||
namespace JS { | namespace JS { | ||||
class Value; | union JS_PUBLIC_API Value; | ||||
} | } | ||||
/* JS::Value can store a full int32_t. */ | /* JS::Value can store a full int32_t. */ | ||||
#define JSVAL_INT_BITS 32 | #define JSVAL_INT_BITS 32 | ||||
#define JSVAL_INT_MIN ((int32_t)0x80000000) | #define JSVAL_INT_MIN ((int32_t)0x80000000) | ||||
#define JSVAL_INT_MAX ((int32_t)0x7fffffff) | #define JSVAL_INT_MAX ((int32_t)0x7fffffff) | ||||
#if defined(JS_PUNBOX64) | #if defined(JS_PUNBOX64) | ||||
#define JSVAL_TAG_SHIFT 47 | # define JSVAL_TAG_SHIFT 47 | ||||
#endif | #endif | ||||
// Use enums so that printing a JS::Value in the debugger shows nice | // Use enums so that printing a JS::Value in the debugger shows nice | ||||
// symbolic type tags. | // symbolic type tags. | ||||
#if defined(_MSC_VER) | // Work around a GCC bug. See comment above #undef JS_ENUM_HEADER. | ||||
#define JS_ENUM_HEADER(id, type) enum id : type | #if MOZ_IS_GCC | ||||
#define JS_ENUM_FOOTER(id) | |||||
#else | |||||
#define JS_ENUM_HEADER(id, type) enum id | # define JS_ENUM_HEADER(id, type) enum id | ||||
#define JS_ENUM_FOOTER(id) __attribute__((packed)) | # define JS_ENUM_FOOTER(id) __attribute__((packed)) | ||||
#else | |||||
# define JS_ENUM_HEADER(id, type) enum id : type | |||||
# define JS_ENUM_FOOTER(id) | |||||
#endif | #endif | ||||
JS_ENUM_HEADER(JSValueType, uint8_t){ | enum JSValueType : uint8_t { | ||||
JSVAL_TYPE_DOUBLE = 0x00, JSVAL_TYPE_INT32 = 0x01, | JSVAL_TYPE_DOUBLE = 0x00, | ||||
JSVAL_TYPE_BOOLEAN = 0x02, JSVAL_TYPE_UNDEFINED = 0x03, | JSVAL_TYPE_INT32 = 0x01, | ||||
JSVAL_TYPE_NULL = 0x04, JSVAL_TYPE_MAGIC = 0x05, JSVAL_TYPE_STRING = 0x06, | JSVAL_TYPE_BOOLEAN = 0x02, | ||||
JSVAL_TYPE_SYMBOL = 0x07, JSVAL_TYPE_PRIVATE_GCTHING = 0x08, | JSVAL_TYPE_UNDEFINED = 0x03, | ||||
JSVAL_TYPE_NULL = 0x04, | |||||
JSVAL_TYPE_MAGIC = 0x05, | |||||
JSVAL_TYPE_STRING = 0x06, | |||||
JSVAL_TYPE_SYMBOL = 0x07, | |||||
JSVAL_TYPE_PRIVATE_GCTHING = 0x08, | |||||
JSVAL_TYPE_BIGINT = 0x09, | |||||
JSVAL_TYPE_OBJECT = 0x0c, | JSVAL_TYPE_OBJECT = 0x0c, | ||||
// These never appear in a jsval; they are only provided as an out-of-band | // This type never appears in a Value; it's only an out-of-band value. | ||||
// value. | JSVAL_TYPE_UNKNOWN = 0x20 | ||||
JSVAL_TYPE_UNKNOWN = 0x20, | }; | ||||
JSVAL_TYPE_MISSING = 0x21} JS_ENUM_FOOTER(JSValueType); | |||||
namespace JS { | |||||
enum class ValueType : uint8_t { | |||||
Double = JSVAL_TYPE_DOUBLE, | |||||
Int32 = JSVAL_TYPE_INT32, | |||||
Boolean = JSVAL_TYPE_BOOLEAN, | |||||
Undefined = JSVAL_TYPE_UNDEFINED, | |||||
Null = JSVAL_TYPE_NULL, | |||||
Magic = JSVAL_TYPE_MAGIC, | |||||
String = JSVAL_TYPE_STRING, | |||||
Symbol = JSVAL_TYPE_SYMBOL, | |||||
PrivateGCThing = JSVAL_TYPE_PRIVATE_GCTHING, | |||||
BigInt = JSVAL_TYPE_BIGINT, | |||||
Object = JSVAL_TYPE_OBJECT, | |||||
}; | |||||
} | |||||
static_assert(sizeof(JSValueType) == 1, | static_assert(sizeof(JSValueType) == 1, | ||||
"compiler typed enum support is apparently buggy"); | "compiler typed enum support is apparently buggy"); | ||||
#if defined(JS_NUNBOX32) | #if defined(JS_NUNBOX32) | ||||
JS_ENUM_HEADER(JSValueTag, uint32_t){ | JS_ENUM_HEADER(JSValueTag, uint32_t){ | ||||
JSVAL_TAG_CLEAR = 0xFFFFFF80, | JSVAL_TAG_CLEAR = 0xFFFFFF80, | ||||
JSVAL_TAG_INT32 = JSVAL_TAG_CLEAR | JSVAL_TYPE_INT32, | JSVAL_TAG_INT32 = JSVAL_TAG_CLEAR | JSVAL_TYPE_INT32, | ||||
JSVAL_TAG_UNDEFINED = JSVAL_TAG_CLEAR | JSVAL_TYPE_UNDEFINED, | JSVAL_TAG_UNDEFINED = JSVAL_TAG_CLEAR | JSVAL_TYPE_UNDEFINED, | ||||
JSVAL_TAG_NULL = JSVAL_TAG_CLEAR | JSVAL_TYPE_NULL, | JSVAL_TAG_NULL = JSVAL_TAG_CLEAR | JSVAL_TYPE_NULL, | ||||
JSVAL_TAG_BOOLEAN = JSVAL_TAG_CLEAR | JSVAL_TYPE_BOOLEAN, | JSVAL_TAG_BOOLEAN = JSVAL_TAG_CLEAR | JSVAL_TYPE_BOOLEAN, | ||||
JSVAL_TAG_MAGIC = JSVAL_TAG_CLEAR | JSVAL_TYPE_MAGIC, | JSVAL_TAG_MAGIC = JSVAL_TAG_CLEAR | JSVAL_TYPE_MAGIC, | ||||
JSVAL_TAG_STRING = JSVAL_TAG_CLEAR | JSVAL_TYPE_STRING, | JSVAL_TAG_STRING = JSVAL_TAG_CLEAR | JSVAL_TYPE_STRING, | ||||
JSVAL_TAG_SYMBOL = JSVAL_TAG_CLEAR | JSVAL_TYPE_SYMBOL, | JSVAL_TAG_SYMBOL = JSVAL_TAG_CLEAR | JSVAL_TYPE_SYMBOL, | ||||
JSVAL_TAG_PRIVATE_GCTHING = JSVAL_TAG_CLEAR | JSVAL_TYPE_PRIVATE_GCTHING, | JSVAL_TAG_PRIVATE_GCTHING = JSVAL_TAG_CLEAR | JSVAL_TYPE_PRIVATE_GCTHING, | ||||
JSVAL_TAG_BIGINT = JSVAL_TAG_CLEAR | JSVAL_TYPE_BIGINT, | |||||
JSVAL_TAG_OBJECT = JSVAL_TAG_CLEAR | | JSVAL_TAG_OBJECT = JSVAL_TAG_CLEAR | | ||||
JSVAL_TYPE_OBJECT} JS_ENUM_FOOTER(JSValueTag); | JSVAL_TYPE_OBJECT} JS_ENUM_FOOTER(JSValueTag); | ||||
static_assert(sizeof(JSValueTag) == sizeof(uint32_t), | static_assert(sizeof(JSValueTag) == sizeof(uint32_t), | ||||
"compiler typed enum support is apparently buggy"); | "compiler typed enum support is apparently buggy"); | ||||
#elif defined(JS_PUNBOX64) | #elif defined(JS_PUNBOX64) | ||||
JS_ENUM_HEADER(JSValueTag, uint32_t){ | JS_ENUM_HEADER(JSValueTag, uint32_t){ | ||||
JSVAL_TAG_MAX_DOUBLE = 0x1FFF0, | JSVAL_TAG_MAX_DOUBLE = 0x1FFF0, | ||||
JSVAL_TAG_INT32 = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32, | JSVAL_TAG_INT32 = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32, | ||||
JSVAL_TAG_UNDEFINED = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_UNDEFINED, | JSVAL_TAG_UNDEFINED = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_UNDEFINED, | ||||
JSVAL_TAG_NULL = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_NULL, | JSVAL_TAG_NULL = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_NULL, | ||||
JSVAL_TAG_BOOLEAN = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN, | JSVAL_TAG_BOOLEAN = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN, | ||||
JSVAL_TAG_MAGIC = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_MAGIC, | JSVAL_TAG_MAGIC = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_MAGIC, | ||||
JSVAL_TAG_STRING = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_STRING, | JSVAL_TAG_STRING = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_STRING, | ||||
JSVAL_TAG_SYMBOL = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_SYMBOL, | JSVAL_TAG_SYMBOL = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_SYMBOL, | ||||
JSVAL_TAG_PRIVATE_GCTHING = JSVAL_TAG_MAX_DOUBLE | | JSVAL_TAG_PRIVATE_GCTHING = JSVAL_TAG_MAX_DOUBLE | | ||||
JSVAL_TYPE_PRIVATE_GCTHING, | JSVAL_TYPE_PRIVATE_GCTHING, | ||||
JSVAL_TAG_BIGINT = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BIGINT, | |||||
JSVAL_TAG_OBJECT = JSVAL_TAG_MAX_DOUBLE | | JSVAL_TAG_OBJECT = JSVAL_TAG_MAX_DOUBLE | | ||||
JSVAL_TYPE_OBJECT} JS_ENUM_FOOTER(JSValueTag); | JSVAL_TYPE_OBJECT} JS_ENUM_FOOTER(JSValueTag); | ||||
static_assert(sizeof(JSValueTag) == sizeof(uint32_t), | static_assert(sizeof(JSValueTag) == sizeof(uint32_t), | ||||
"compiler typed enum support is apparently buggy"); | "compiler typed enum support is apparently buggy"); | ||||
JS_ENUM_HEADER(JSValueShiftedTag, uint64_t){ | enum JSValueShiftedTag : uint64_t { | ||||
JSVAL_SHIFTED_TAG_MAX_DOUBLE = | JSVAL_SHIFTED_TAG_MAX_DOUBLE = | ||||
((((uint64_t)JSVAL_TAG_MAX_DOUBLE) << JSVAL_TAG_SHIFT) | 0xFFFFFFFF), | ((((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_INT32 = (((uint64_t)JSVAL_TAG_INT32) << JSVAL_TAG_SHIFT), | ||||
JSVAL_SHIFTED_TAG_UNDEFINED = (((uint64_t)JSVAL_TAG_UNDEFINED) | JSVAL_SHIFTED_TAG_UNDEFINED = | ||||
<< JSVAL_TAG_SHIFT), | (((uint64_t)JSVAL_TAG_UNDEFINED) << JSVAL_TAG_SHIFT), | ||||
JSVAL_SHIFTED_TAG_NULL = (((uint64_t)JSVAL_TAG_NULL) << 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_SHIFTED_TAG_BOOLEAN = | ||||
<< JSVAL_TAG_SHIFT), | (((uint64_t)JSVAL_TAG_BOOLEAN) << JSVAL_TAG_SHIFT), | ||||
JSVAL_SHIFTED_TAG_MAGIC = (((uint64_t)JSVAL_TAG_MAGIC) << 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_SHIFTED_TAG_STRING = (((uint64_t)JSVAL_TAG_STRING) << JSVAL_TAG_SHIFT), | ||||
<< JSVAL_TAG_SHIFT), | JSVAL_SHIFTED_TAG_SYMBOL = (((uint64_t)JSVAL_TAG_SYMBOL) << JSVAL_TAG_SHIFT), | ||||
JSVAL_SHIFTED_TAG_SYMBOL = (((uint64_t)JSVAL_TAG_SYMBOL) | JSVAL_SHIFTED_TAG_PRIVATE_GCTHING = | ||||
<< JSVAL_TAG_SHIFT), | (((uint64_t)JSVAL_TAG_PRIVATE_GCTHING) << JSVAL_TAG_SHIFT), | ||||
JSVAL_SHIFTED_TAG_PRIVATE_GCTHING = (((uint64_t)JSVAL_TAG_PRIVATE_GCTHING) | JSVAL_SHIFTED_TAG_BIGINT = (((uint64_t)JSVAL_TAG_BIGINT) << JSVAL_TAG_SHIFT), | ||||
<< JSVAL_TAG_SHIFT), | JSVAL_SHIFTED_TAG_OBJECT = (((uint64_t)JSVAL_TAG_OBJECT) << 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), | static_assert(sizeof(JSValueShiftedTag) == sizeof(uint64_t), | ||||
"compiler typed enum support is apparently buggy"); | "compiler typed enum support is apparently buggy"); | ||||
#endif | #endif | ||||
/* | /* | ||||
* All our supported compilers implement C++11 |enum Foo : T| syntax, so don't | * 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 | * 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 | * <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 | * typed enums trigger a warning that can't be turned off. Don't expose it | ||||
* beyond this file!) | * beyond this file!) | ||||
*/ | */ | ||||
#undef JS_ENUM_HEADER | #undef JS_ENUM_HEADER | ||||
#undef JS_ENUM_FOOTER | #undef JS_ENUM_FOOTER | ||||
#if defined(JS_NUNBOX32) | #if defined(JS_NUNBOX32) | ||||
#define JSVAL_TYPE_TO_TAG(type) ((JSValueTag)(JSVAL_TAG_CLEAR | (type))) | # define JSVAL_TYPE_TO_TAG(type) ((JSValueTag)(JSVAL_TAG_CLEAR | (type))) | ||||
#define JSVAL_RAW64_UNDEFINED (uint64_t(JSVAL_TAG_UNDEFINED) << 32) | |||||
#define JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET JSVAL_TAG_OBJECT | # define JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET JSVAL_TAG_OBJECT | ||||
#define JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET JSVAL_TAG_INT32 | # 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_TAG_OF_GCTHING_SET JSVAL_TAG_STRING | ||||
#elif defined(JS_PUNBOX64) | #elif defined(JS_PUNBOX64) | ||||
#define JSVAL_RAW64_UNDEFINED (uint64_t(JSVAL_TAG_UNDEFINED) << JSVAL_TAG_SHIFT) | |||||
// This should only be used in toGCThing, see the 'Spectre mitigations' comment. | // This should only be used in toGCThing, see the 'Spectre mitigations' comment. | ||||
#define JSVAL_PAYLOAD_MASK_GCTHING 0x00007FFFFFFFFFFFLL | # define JSVAL_PAYLOAD_MASK_GCTHING 0x00007FFFFFFFFFFFLL | ||||
#define JSVAL_TAG_MASK 0xFFFF800000000000LL | # define JSVAL_TAG_MASK 0xFFFF800000000000LL | ||||
#define JSVAL_TYPE_TO_TAG(type) ((JSValueTag)(JSVAL_TAG_MAX_DOUBLE | (type))) | # define JSVAL_TYPE_TO_TAG(type) ((JSValueTag)(JSVAL_TAG_MAX_DOUBLE | (type))) | ||||
#define JSVAL_TYPE_TO_SHIFTED_TAG(type) \ | # define JSVAL_TYPE_TO_SHIFTED_TAG(type) \ | ||||
(((uint64_t)JSVAL_TYPE_TO_TAG(type)) << JSVAL_TAG_SHIFT) | (((uint64_t)JSVAL_TYPE_TO_TAG(type)) << JSVAL_TAG_SHIFT) | ||||
#define JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET JSVAL_TAG_OBJECT | # define JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET JSVAL_TAG_OBJECT | ||||
#define JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET JSVAL_TAG_INT32 | # 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_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_PRIMITIVE_SET JSVAL_SHIFTED_TAG_OBJECT | ||||
#define JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET JSVAL_SHIFTED_TAG_BOOLEAN | # define JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET JSVAL_SHIFTED_TAG_BOOLEAN | ||||
#define JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET JSVAL_SHIFTED_TAG_STRING | # define JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET JSVAL_SHIFTED_TAG_STRING | ||||
// JSVAL_TYPE_OBJECT and JSVAL_TYPE_NULL differ by one bit. We can use this to | // JSVAL_TYPE_OBJECT and JSVAL_TYPE_NULL differ by one bit. We can use this to | ||||
// implement toObjectOrNull more efficiently. | // implement toObjectOrNull more efficiently. | ||||
#define JSVAL_OBJECT_OR_NULL_BIT (uint64_t(0x8) << JSVAL_TAG_SHIFT) | # define JSVAL_OBJECT_OR_NULL_BIT (uint64_t(0x8) << JSVAL_TAG_SHIFT) | ||||
static_assert( | static_assert( | ||||
(JSVAL_SHIFTED_TAG_NULL ^ JSVAL_SHIFTED_TAG_OBJECT) == | (JSVAL_SHIFTED_TAG_NULL ^ JSVAL_SHIFTED_TAG_OBJECT) == | ||||
JSVAL_OBJECT_OR_NULL_BIT, | JSVAL_OBJECT_OR_NULL_BIT, | ||||
"JSVAL_OBJECT_OR_NULL_BIT must be consistent with object and null tags"); | "JSVAL_OBJECT_OR_NULL_BIT must be consistent with object and null tags"); | ||||
#endif /* JS_PUNBOX64 */ | #endif /* JS_PUNBOX64 */ | ||||
typedef enum JSWhyMagic { | enum JSWhyMagic { | ||||
/** a hole in a native object's elements */ | /** a hole in a native object's elements */ | ||||
JS_ELEMENTS_HOLE, | JS_ELEMENTS_HOLE, | ||||
/** there is not a pending iterator value */ | /** there is not a pending iterator value */ | ||||
JS_NO_ITER_VALUE, | JS_NO_ITER_VALUE, | ||||
/** exception value thrown when closing a generator */ | /** exception value thrown when closing a generator */ | ||||
JS_GENERATOR_CLOSING, | 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 */ | /** used in debug builds to catch tracing errors */ | ||||
JS_ARG_POISON, | JS_ARG_POISON, | ||||
/** an empty subnode in the AST serializer */ | /** an empty subnode in the AST serializer */ | ||||
JS_SERIALIZE_NO_NODE, | JS_SERIALIZE_NO_NODE, | ||||
/** lazy arguments value on the stack */ | |||||
JS_LAZY_ARGUMENTS, | |||||
/** optimized-away 'arguments' value */ | /** optimized-away 'arguments' value */ | ||||
JS_OPTIMIZED_ARGUMENTS, | JS_OPTIMIZED_ARGUMENTS, | ||||
/** magic value passed to natives to indicate construction */ | /** magic value passed to natives to indicate construction */ | ||||
JS_IS_CONSTRUCTING, | JS_IS_CONSTRUCTING, | ||||
/** value of static block object slot */ | |||||
JS_BLOCK_NEEDS_CLONE, | |||||
/** see class js::HashableValue */ | /** see class js::HashableValue */ | ||||
JS_HASH_KEY_EMPTY, | JS_HASH_KEY_EMPTY, | ||||
/** error while running Ion code */ | /** error while running Ion code */ | ||||
JS_ION_ERROR, | JS_ION_ERROR, | ||||
/** missing recover instruction result */ | /** missing recover instruction result */ | ||||
JS_ION_BAILOUT, | JS_ION_BAILOUT, | ||||
/** optimized out slot */ | /** optimized out slot */ | ||||
JS_OPTIMIZED_OUT, | JS_OPTIMIZED_OUT, | ||||
/** uninitialized lexical bindings that produce ReferenceError on touch. */ | /** uninitialized lexical bindings that produce ReferenceError on touch. */ | ||||
JS_UNINITIALIZED_LEXICAL, | JS_UNINITIALIZED_LEXICAL, | ||||
/** standard constructors are not created for off-thread parsing. */ | /** standard constructors are not created for off-thread parsing. */ | ||||
JS_OFF_THREAD_CONSTRUCTOR, | JS_OFF_THREAD_CONSTRUCTOR, | ||||
/** used in jit::TrySkipAwait */ | |||||
JS_CANNOT_SKIP_AWAIT, | |||||
/** for local use */ | /** for local use */ | ||||
JS_GENERIC_MAGIC, | JS_GENERIC_MAGIC, | ||||
JS_WHY_MAGIC_COUNT | JS_WHY_MAGIC_COUNT | ||||
} JSWhyMagic; | }; | ||||
namespace js { | namespace js { | ||||
static inline JS::Value PoisonedObjectValue(uintptr_t poison); | static inline JS::Value PoisonedObjectValue(uintptr_t poison); | ||||
} // namespace js | } // namespace js | ||||
namespace JS { | namespace JS { | ||||
static inline constexpr JS::Value UndefinedValue(); | |||||
namespace detail { | namespace detail { | ||||
constexpr int CanonicalizedNaNSignBit = 0; | constexpr int CanonicalizedNaNSignBit = 0; | ||||
constexpr uint64_t CanonicalizedNaNSignificand = 0x8000000000000ULL; | constexpr uint64_t CanonicalizedNaNSignificand = 0x8000000000000ULL; | ||||
constexpr uint64_t CanonicalizedNaNBits = | constexpr uint64_t CanonicalizedNaNBits = | ||||
mozilla::SpecificNaNBits<double, detail::CanonicalizedNaNSignBit, | mozilla::SpecificNaNBits<double, detail::CanonicalizedNaNSignBit, | ||||
detail::CanonicalizedNaNSignificand>::value; | detail::CanonicalizedNaNSignificand>::value; | ||||
} // namespace detail | } // namespace detail | ||||
/** | /** | ||||
* Returns a generic quiet NaN value, with all payload bits set to zero. | * 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 | * Among other properties, this NaN's bit pattern conforms to JS::Value's | ||||
* bit pattern restrictions. | * bit pattern restrictions. | ||||
*/ | */ | ||||
static MOZ_ALWAYS_INLINE double GenericNaN() { | static MOZ_ALWAYS_INLINE double GenericNaN() { | ||||
return mozilla::SpecificNaN<double>(detail::CanonicalizedNaNSignBit, | return mozilla::SpecificNaN<double>(detail::CanonicalizedNaNSignBit, | ||||
detail::CanonicalizedNaNSignificand); | detail::CanonicalizedNaNSignificand); | ||||
} | } | ||||
/* MSVC with PGO miscompiles this function. */ | |||||
#if defined(_MSC_VER) | |||||
#pragma optimize("g", off) | |||||
#endif | |||||
static inline double CanonicalizeNaN(double d) { | static inline double CanonicalizeNaN(double d) { | ||||
if (MOZ_UNLIKELY(mozilla::IsNaN(d))) return GenericNaN(); | if (MOZ_UNLIKELY(mozilla::IsNaN(d))) { | ||||
return GenericNaN(); | |||||
} | |||||
return d; | return d; | ||||
} | } | ||||
#if defined(_MSC_VER) | |||||
#pragma optimize("", on) | |||||
#endif | |||||
/** | /** | ||||
* [SMDOC] JS::Value type | |||||
* | |||||
* JS::Value is the interface for a single JavaScript Engine value. A few | * JS::Value is the interface for a single JavaScript Engine value. A few | ||||
* general notes on JS::Value: | * general notes on JS::Value: | ||||
* | * | ||||
* - JS::Value has setX() and isX() members for X in | * - JS::Value has setX() and isX() members for X in | ||||
* | * | ||||
* { Int32, Double, String, Symbol, Boolean, Undefined, Null, | * { Int32, Double, String, Symbol, BigInt, Boolean, Undefined, Null, | ||||
* Object, Magic } | * Object, Magic } | ||||
* | * | ||||
* JS::Value also contains toX() for each of the non-singleton types. | * JS::Value also contains toX() for each of the non-singleton types. | ||||
* | * | ||||
* - Magic is a singleton type whose payload contains either a JSWhyMagic | * - Magic is a singleton type whose payload contains either a JSWhyMagic | ||||
* "reason" for the magic value or a uint32_t value. By providing 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 | * values when creating and checking for magic values, it is possible to | ||||
* assert, at runtime, that only magic values with the expected reason flow | * assert, at runtime, that only magic values with the expected reason flow | ||||
Show All 21 Lines | |||||
* that toString, toObject, toSymbol will return an invalid pointer (because | * that toString, toObject, toSymbol will return an invalid pointer (because | ||||
* some high bits will be set) when called on a Value with a different type | * some high bits will be set) when called on a Value with a different type | ||||
* tag. | * tag. | ||||
* | * | ||||
* - On 32-bit platforms,when unboxing an object/string/symbol Value, we use a | * - On 32-bit platforms,when unboxing an object/string/symbol Value, we use a | ||||
* conditional move (not speculated) to zero the payload register if the type | * conditional move (not speculated) to zero the payload register if the type | ||||
* doesn't match. | * doesn't match. | ||||
*/ | */ | ||||
class MOZ_NON_PARAM alignas(8) Value { | union alignas(8) Value { | ||||
private: | |||||
uint64_t asBits_; | |||||
double asDouble_; | |||||
#if defined(JS_PUNBOX64) && !defined(_WIN64) | |||||
// MSVC doesn't pack these correctly :-( | |||||
struct { | |||||
# if MOZ_LITTLE_ENDIAN | |||||
uint64_t payload47_ : 47; | |||||
JSValueTag tag_ : 17; | |||||
# else | |||||
JSValueTag tag_ : 17; | |||||
uint64_t payload47_ : 47; | |||||
# endif // MOZ_LITTLE_ENDIAN | |||||
} debugView_; | |||||
#endif // defined(JS_PUNBOX64) && !defined(_WIN64) | |||||
struct { | |||||
#if defined(JS_PUNBOX64) | |||||
# if MOZ_BIG_ENDIAN | |||||
uint32_t : 32; // padding | |||||
# endif // MOZ_BIG_ENDIAN | |||||
union { | |||||
int32_t i32_; | |||||
uint32_t u32_; | |||||
JSWhyMagic why_; | |||||
} payload_; | |||||
#elif defined(JS_NUNBOX32) | |||||
# if MOZ_BIG_ENDIAN | |||||
JSValueTag tag_; | |||||
# endif // MOZ_BIG_ENDIAN | |||||
union { | |||||
int32_t i32_; | |||||
uint32_t u32_; | |||||
uint32_t boo_; // Don't use |bool| -- it must be four bytes. | |||||
JSString* str_; | |||||
JS::Symbol* sym_; | |||||
JS::BigInt* bi_; | |||||
JSObject* obj_; | |||||
js::gc::Cell* cell_; | |||||
void* ptr_; | |||||
JSWhyMagic why_; | |||||
} payload_; | |||||
# if MOZ_LITTLE_ENDIAN | |||||
JSValueTag tag_; | |||||
# endif // MOZ_LITTLE_ENDIAN | |||||
#endif // defined(JS_PUNBOX64) | |||||
} s_; | |||||
public: | |||||
constexpr Value() : asBits_(bitsFromTagAndPayload(JSVAL_TAG_UNDEFINED, 0)) {} | |||||
Value(const Value& v) = default; | |||||
private: | |||||
explicit constexpr Value(uint64_t asBits) : asBits_(asBits) {} | |||||
explicit constexpr Value(double d) : asDouble_(d) {} | |||||
static_assert(sizeof(JSValueType) == 1, | |||||
"type bits must fit in a single byte"); | |||||
static_assert(sizeof(JSValueTag) == 4, | |||||
"32-bit Value's tag_ must have size 4 to complement the " | |||||
"payload union's size 4"); | |||||
static_assert(sizeof(JSWhyMagic) <= 4, | |||||
"32-bit Value's JSWhyMagic payload field must not inflate " | |||||
"the payload beyond 4 bytes"); | |||||
public: | public: | ||||
#if defined(JS_NUNBOX32) | #if defined(JS_NUNBOX32) | ||||
using PayloadType = uint32_t; | using PayloadType = uint32_t; | ||||
#elif defined(JS_PUNBOX64) | #elif defined(JS_PUNBOX64) | ||||
using PayloadType = uint64_t; | using PayloadType = uint64_t; | ||||
#endif | #endif | ||||
/* | static constexpr uint64_t bitsFromTagAndPayload(JSValueTag tag, | ||||
* N.B. the default constructor leaves Value unitialized. Adding a default | PayloadType payload) { | ||||
* constructor prevents Value from being stored in a union. | #if defined(JS_NUNBOX32) | ||||
*/ | return (uint64_t(uint32_t(tag)) << 32) | payload; | ||||
Value() = default; | #elif defined(JS_PUNBOX64) | ||||
Value(const Value& v) = default; | 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); } | |||||
public: | |||||
/** | /** | ||||
* Returns false if creating a NumberValue containing the given type would | * Returns false if creating a NumberValue containing the given type would | ||||
* be lossy, true otherwise. | * be lossy, true otherwise. | ||||
*/ | */ | ||||
template <typename T> | template <typename T> | ||||
static bool isNumberRepresentable(const T t) { | static bool isNumberRepresentable(const T t) { | ||||
return T(double(t)) == t; | return T(double(t)) == t; | ||||
} | } | ||||
/*** Mutators ***/ | /*** Mutators ***/ | ||||
void setNull() { data.asBits = bitsFromTagAndPayload(JSVAL_TAG_NULL, 0); } | void setNull() { asBits_ = bitsFromTagAndPayload(JSVAL_TAG_NULL, 0); } | ||||
void setUndefined() { | void setUndefined() { | ||||
data.asBits = bitsFromTagAndPayload(JSVAL_TAG_UNDEFINED, 0); | asBits_ = bitsFromTagAndPayload(JSVAL_TAG_UNDEFINED, 0); | ||||
} | } | ||||
void setInt32(int32_t i) { | void setInt32(int32_t i) { | ||||
data.asBits = bitsFromTagAndPayload(JSVAL_TAG_INT32, uint32_t(i)); | asBits_ = bitsFromTagAndPayload(JSVAL_TAG_INT32, uint32_t(i)); | ||||
} | |||||
int32_t& getInt32Ref() { | |||||
MOZ_ASSERT(isInt32()); | |||||
return data.s.payload.i32; | |||||
} | } | ||||
void setDouble(double d) { | void setDouble(double d) { | ||||
// Don't assign to data.asDouble to fix a miscompilation with | // Don't assign to asDouble_ to fix a miscompilation with GCC 5.2.1 and | ||||
// GCC 5.2.1 and 5.3.1. See bug 1312488. | // 5.3.1. See bug 1312488. | ||||
data = layout(d); | *this = Value(d); | ||||
MOZ_ASSERT(isDouble()); | MOZ_ASSERT(isDouble()); | ||||
} | } | ||||
void setNaN() { setDouble(GenericNaN()); } | void setNaN() { setDouble(GenericNaN()); } | ||||
double& getDoubleRef() { | |||||
MOZ_ASSERT(isDouble()); | |||||
return data.asDouble; | |||||
} | |||||
void setString(JSString* str) { | void setString(JSString* str) { | ||||
MOZ_ASSERT(js::gc::IsCellPointerValid(str)); | MOZ_ASSERT(js::gc::IsCellPointerValid(str)); | ||||
data.asBits = bitsFromTagAndPayload(JSVAL_TAG_STRING, PayloadType(str)); | asBits_ = bitsFromTagAndPayload(JSVAL_TAG_STRING, PayloadType(str)); | ||||
} | } | ||||
void setSymbol(JS::Symbol* sym) { | void setSymbol(JS::Symbol* sym) { | ||||
MOZ_ASSERT(js::gc::IsCellPointerValid(sym)); | MOZ_ASSERT(js::gc::IsCellPointerValid(sym)); | ||||
data.asBits = bitsFromTagAndPayload(JSVAL_TAG_SYMBOL, PayloadType(sym)); | asBits_ = bitsFromTagAndPayload(JSVAL_TAG_SYMBOL, PayloadType(sym)); | ||||
} | |||||
void setBigInt(JS::BigInt* bi) { | |||||
MOZ_ASSERT(js::gc::IsCellPointerValid(bi)); | |||||
asBits_ = bitsFromTagAndPayload(JSVAL_TAG_BIGINT, PayloadType(bi)); | |||||
} | } | ||||
void setObject(JSObject& obj) { | void setObject(JSObject& obj) { | ||||
MOZ_ASSERT(js::gc::IsCellPointerValid(&obj)); | MOZ_ASSERT(js::gc::IsCellPointerValid(&obj)); | ||||
// This should not be possible and is undefined behavior, but some | |||||
// ObjectValue(nullptr) are sneaking in. Try to catch them here, if | |||||
// indeed they are going through this code. I tested gcc, and it at | |||||
// least will *not* elide the null check even though it would be | |||||
// permitted according to the spec. The temporary is necessary to | |||||
// prevent gcc from helpfully pointing out that this code makes no | |||||
// sense. | |||||
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED | |||||
JSObject* testObj = &obj; | |||||
MOZ_DIAGNOSTIC_ASSERT(testObj != nullptr); | |||||
#endif | |||||
#if defined(JS_PUNBOX64) | #if defined(JS_PUNBOX64) | ||||
// VisualStudio cannot contain parenthesized C++ style cast and shift | // VisualStudio cannot contain parenthesized C++ style cast and shift | ||||
// inside decltype in template parameter: | // inside decltype in template parameter: | ||||
// AssertionConditionType<decltype((uintptr_t(x) >> 1))> | // AssertionConditionType<decltype((uintptr_t(x) >> 1))> | ||||
// It throws syntax error. | // It throws syntax error. | ||||
MOZ_ASSERT((((uintptr_t)&obj) >> JSVAL_TAG_SHIFT) == 0); | MOZ_ASSERT((((uintptr_t)&obj) >> JSVAL_TAG_SHIFT) == 0); | ||||
#endif | #endif | ||||
setObjectNoCheck(&obj); | setObjectNoCheck(&obj); | ||||
} | } | ||||
private: | private: | ||||
void setObjectNoCheck(JSObject* obj) { | void setObjectNoCheck(JSObject* obj) { | ||||
data.asBits = bitsFromTagAndPayload(JSVAL_TAG_OBJECT, PayloadType(obj)); | asBits_ = bitsFromTagAndPayload(JSVAL_TAG_OBJECT, PayloadType(obj)); | ||||
} | } | ||||
friend inline Value js::PoisonedObjectValue(uintptr_t poison); | friend inline Value js::PoisonedObjectValue(uintptr_t poison); | ||||
public: | public: | ||||
void setBoolean(bool b) { | void setBoolean(bool b) { | ||||
data.asBits = bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN, uint32_t(b)); | asBits_ = bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN, uint32_t(b)); | ||||
} | } | ||||
void setMagic(JSWhyMagic why) { | void setMagic(JSWhyMagic why) { | ||||
data.asBits = bitsFromTagAndPayload(JSVAL_TAG_MAGIC, uint32_t(why)); | asBits_ = bitsFromTagAndPayload(JSVAL_TAG_MAGIC, uint32_t(why)); | ||||
} | } | ||||
void setMagicUint32(uint32_t payload) { | void setMagicUint32(uint32_t payload) { | ||||
data.asBits = bitsFromTagAndPayload(JSVAL_TAG_MAGIC, payload); | asBits_ = bitsFromTagAndPayload(JSVAL_TAG_MAGIC, payload); | ||||
} | } | ||||
bool setNumber(uint32_t ui) { | bool setNumber(uint32_t ui) { | ||||
if (ui > JSVAL_INT_MAX) { | if (ui > JSVAL_INT_MAX) { | ||||
setDouble((double)ui); | setDouble((double)ui); | ||||
return false; | return false; | ||||
} else { | } else { | ||||
setInt32((int32_t)ui); | setInt32((int32_t)ui); | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
bool setNumber(double d) { | bool setNumber(double d) { | ||||
int32_t i; | int32_t i; | ||||
if (mozilla::NumberIsInt32(d, &i)) { | if (mozilla::NumberIsInt32(d, &i)) { | ||||
setInt32(i); | setInt32(i); | ||||
return true; | return true; | ||||
} | } | ||||
setDouble(d); | setDouble(d); | ||||
return false; | return false; | ||||
} | } | ||||
void setObjectOrNull(JSObject* arg) { | void setObjectOrNull(JSObject* arg) { | ||||
if (arg) | if (arg) { | ||||
setObject(*arg); | setObject(*arg); | ||||
else | } else { | ||||
setNull(); | setNull(); | ||||
} | } | ||||
} | |||||
void swap(Value& rhs) { | void swap(Value& rhs) { | ||||
uint64_t tmp = rhs.data.asBits; | uint64_t tmp = rhs.asBits_; | ||||
rhs.data.asBits = data.asBits; | rhs.asBits_ = asBits_; | ||||
data.asBits = tmp; | asBits_ = tmp; | ||||
} | } | ||||
private: | private: | ||||
JSValueTag toTag() const { | JSValueTag toTag() const { | ||||
#if defined(JS_NUNBOX32) | #if defined(JS_NUNBOX32) | ||||
return data.s.tag; | return s_.tag_; | ||||
#elif defined(JS_PUNBOX64) | #elif defined(JS_PUNBOX64) | ||||
return JSValueTag(data.asBits >> JSVAL_TAG_SHIFT); | return JSValueTag(asBits_ >> JSVAL_TAG_SHIFT); | ||||
#endif | #endif | ||||
} | } | ||||
public: | public: | ||||
/*** JIT-only interfaces to interact with and create raw Values ***/ | /*** JIT-only interfaces to interact with and create raw Values ***/ | ||||
#if defined(JS_NUNBOX32) | #if defined(JS_NUNBOX32) | ||||
PayloadType toNunboxPayload() const { | PayloadType toNunboxPayload() const { | ||||
return static_cast<PayloadType>(data.s.payload.i32); | return static_cast<PayloadType>(s_.payload_.i32_); | ||||
} | } | ||||
JSValueTag toNunboxTag() const { return data.s.tag; } | JSValueTag toNunboxTag() const { return s_.tag_; } | ||||
#elif defined(JS_PUNBOX64) | #elif defined(JS_PUNBOX64) | ||||
const void* bitsAsPunboxPointer() const { | const void* bitsAsPunboxPointer() const { | ||||
return reinterpret_cast<void*>(data.asBits); | return reinterpret_cast<void*>(asBits_); | ||||
} | } | ||||
#endif | #endif | ||||
/*** Value type queries ***/ | /*** Value type queries ***/ | ||||
/* | /* | ||||
* N.B. GCC, in some but not all cases, chooses to emit signed comparison | * 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 | * of JSValueTag even though its underlying type has been forced to be | ||||
* uint32_t. Thus, all comparisons should explicitly cast operands to | * uint32_t. Thus, all comparisons should explicitly cast operands to | ||||
* uint32_t. | * uint32_t. | ||||
*/ | */ | ||||
bool isUndefined() const { | bool isUndefined() const { | ||||
#if defined(JS_NUNBOX32) | #if defined(JS_NUNBOX32) | ||||
return toTag() == JSVAL_TAG_UNDEFINED; | return toTag() == JSVAL_TAG_UNDEFINED; | ||||
#elif defined(JS_PUNBOX64) | #elif defined(JS_PUNBOX64) | ||||
return data.asBits == JSVAL_SHIFTED_TAG_UNDEFINED; | return asBits_ == JSVAL_SHIFTED_TAG_UNDEFINED; | ||||
#endif | #endif | ||||
} | } | ||||
bool isNull() const { | bool isNull() const { | ||||
#if defined(JS_NUNBOX32) | #if defined(JS_NUNBOX32) | ||||
return toTag() == JSVAL_TAG_NULL; | return toTag() == JSVAL_TAG_NULL; | ||||
#elif defined(JS_PUNBOX64) | #elif defined(JS_PUNBOX64) | ||||
return data.asBits == JSVAL_SHIFTED_TAG_NULL; | return asBits_ == JSVAL_SHIFTED_TAG_NULL; | ||||
#endif | #endif | ||||
} | } | ||||
bool isNullOrUndefined() const { return isNull() || isUndefined(); } | bool isNullOrUndefined() const { return isNull() || isUndefined(); } | ||||
bool isInt32() const { return toTag() == JSVAL_TAG_INT32; } | bool isInt32() const { return toTag() == JSVAL_TAG_INT32; } | ||||
bool isInt32(int32_t i32) const { | bool isInt32(int32_t i32) const { | ||||
return data.asBits == bitsFromTagAndPayload(JSVAL_TAG_INT32, uint32_t(i32)); | return asBits_ == bitsFromTagAndPayload(JSVAL_TAG_INT32, uint32_t(i32)); | ||||
} | } | ||||
bool isDouble() const { | bool isDouble() const { | ||||
#if defined(JS_NUNBOX32) | #if defined(JS_NUNBOX32) | ||||
return uint32_t(toTag()) <= uint32_t(JSVAL_TAG_CLEAR); | return uint32_t(toTag()) <= uint32_t(JSVAL_TAG_CLEAR); | ||||
#elif defined(JS_PUNBOX64) | #elif defined(JS_PUNBOX64) | ||||
return (data.asBits | mozilla::DoubleTypeTraits::kSignBit) <= | return (asBits_ | mozilla::FloatingPoint<double>::kSignBit) <= | ||||
JSVAL_SHIFTED_TAG_MAX_DOUBLE; | JSVAL_SHIFTED_TAG_MAX_DOUBLE; | ||||
#endif | #endif | ||||
} | } | ||||
bool isNumber() const { | bool isNumber() const { | ||||
#if defined(JS_NUNBOX32) | #if defined(JS_NUNBOX32) | ||||
MOZ_ASSERT(toTag() != JSVAL_TAG_CLEAR); | MOZ_ASSERT(toTag() != JSVAL_TAG_CLEAR); | ||||
return uint32_t(toTag()) <= uint32_t(JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET); | return uint32_t(toTag()) <= uint32_t(JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET); | ||||
#elif defined(JS_PUNBOX64) | #elif defined(JS_PUNBOX64) | ||||
return data.asBits < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET; | return asBits_ < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET; | ||||
#endif | #endif | ||||
} | } | ||||
bool isString() const { return toTag() == JSVAL_TAG_STRING; } | bool isString() const { return toTag() == JSVAL_TAG_STRING; } | ||||
bool isSymbol() const { return toTag() == JSVAL_TAG_SYMBOL; } | bool isSymbol() const { return toTag() == JSVAL_TAG_SYMBOL; } | ||||
bool isBigInt() const { return toTag() == JSVAL_TAG_BIGINT; } | |||||
bool isObject() const { | bool isObject() const { | ||||
#if defined(JS_NUNBOX32) | #if defined(JS_NUNBOX32) | ||||
return toTag() == JSVAL_TAG_OBJECT; | return toTag() == JSVAL_TAG_OBJECT; | ||||
#elif defined(JS_PUNBOX64) | #elif defined(JS_PUNBOX64) | ||||
MOZ_ASSERT((data.asBits >> JSVAL_TAG_SHIFT) <= JSVAL_TAG_OBJECT); | MOZ_ASSERT((asBits_ >> JSVAL_TAG_SHIFT) <= JSVAL_TAG_OBJECT); | ||||
return data.asBits >= JSVAL_SHIFTED_TAG_OBJECT; | return asBits_ >= JSVAL_SHIFTED_TAG_OBJECT; | ||||
#endif | #endif | ||||
} | } | ||||
bool isPrimitive() const { | bool isPrimitive() const { | ||||
#if defined(JS_NUNBOX32) | #if defined(JS_NUNBOX32) | ||||
return uint32_t(toTag()) < uint32_t(JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET); | return uint32_t(toTag()) < uint32_t(JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET); | ||||
#elif defined(JS_PUNBOX64) | #elif defined(JS_PUNBOX64) | ||||
return data.asBits < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET; | return asBits_ < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET; | ||||
#endif | #endif | ||||
} | } | ||||
bool isObjectOrNull() const { return isObject() || isNull(); } | bool isObjectOrNull() const { return isObject() || isNull(); } | ||||
bool isGCThing() const { | bool isGCThing() const { | ||||
#if defined(JS_NUNBOX32) | #if defined(JS_NUNBOX32) | ||||
/* gcc sometimes generates signed < without explicit casts. */ | /* gcc sometimes generates signed < without explicit casts. */ | ||||
return uint32_t(toTag()) >= uint32_t(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET); | return uint32_t(toTag()) >= uint32_t(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET); | ||||
#elif defined(JS_PUNBOX64) | #elif defined(JS_PUNBOX64) | ||||
return data.asBits >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET; | return asBits_ >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET; | ||||
#endif | #endif | ||||
} | } | ||||
bool isBoolean() const { return toTag() == JSVAL_TAG_BOOLEAN; } | bool isBoolean() const { return toTag() == JSVAL_TAG_BOOLEAN; } | ||||
bool isTrue() const { | bool isTrue() const { | ||||
return data.asBits == | return asBits_ == bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN, uint32_t(true)); | ||||
bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN, uint32_t(true)); | |||||
} | } | ||||
bool isFalse() const { | bool isFalse() const { | ||||
return data.asBits == | return asBits_ == bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN, uint32_t(false)); | ||||
bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN, uint32_t(false)); | |||||
} | } | ||||
bool isMagic() const { return toTag() == JSVAL_TAG_MAGIC; } | bool isMagic() const { return toTag() == JSVAL_TAG_MAGIC; } | ||||
bool isMagic(JSWhyMagic why) const { | bool isMagic(JSWhyMagic why) const { | ||||
if (!isMagic()) { | if (!isMagic()) { | ||||
return false; | return false; | ||||
} | } | ||||
MOZ_RELEASE_ASSERT(data.s.payload.why == why); | MOZ_RELEASE_ASSERT(s_.payload_.why_ == why); | ||||
return true; | return true; | ||||
} | } | ||||
JS::TraceKind traceKind() const { | JS::TraceKind traceKind() const { | ||||
MOZ_ASSERT(isGCThing()); | MOZ_ASSERT(isGCThing()); | ||||
static_assert((JSVAL_TAG_STRING & 0x03) == size_t(JS::TraceKind::String), | static_assert((JSVAL_TAG_STRING & 0x03) == size_t(JS::TraceKind::String), | ||||
"Value type tags must correspond with JS::TraceKinds."); | "Value type tags must correspond with JS::TraceKinds."); | ||||
static_assert((JSVAL_TAG_SYMBOL & 0x03) == size_t(JS::TraceKind::Symbol), | static_assert((JSVAL_TAG_SYMBOL & 0x03) == size_t(JS::TraceKind::Symbol), | ||||
"Value type tags must correspond with JS::TraceKinds."); | "Value type tags must correspond with JS::TraceKinds."); | ||||
static_assert((JSVAL_TAG_OBJECT & 0x03) == size_t(JS::TraceKind::Object), | static_assert((JSVAL_TAG_OBJECT & 0x03) == size_t(JS::TraceKind::Object), | ||||
"Value type tags must correspond with JS::TraceKinds."); | "Value type tags must correspond with JS::TraceKinds."); | ||||
if (MOZ_UNLIKELY(isPrivateGCThing())) | if (MOZ_UNLIKELY(isPrivateGCThing())) { | ||||
return JS::GCThingTraceKind(toGCThing()); | return JS::GCThingTraceKind(toGCThing()); | ||||
} | |||||
if (MOZ_UNLIKELY(isBigInt())) { | |||||
return JS::TraceKind::BigInt; | |||||
} | |||||
return JS::TraceKind(toTag() & 0x03); | return JS::TraceKind(toTag() & 0x03); | ||||
} | } | ||||
JSWhyMagic whyMagic() const { | JSWhyMagic whyMagic() const { | ||||
MOZ_ASSERT(isMagic()); | MOZ_ASSERT(isMagic()); | ||||
return data.s.payload.why; | return s_.payload_.why_; | ||||
} | } | ||||
uint32_t magicUint32() const { | uint32_t magicUint32() const { | ||||
MOZ_ASSERT(isMagic()); | MOZ_ASSERT(isMagic()); | ||||
return data.s.payload.u32; | return s_.payload_.u32_; | ||||
} | } | ||||
/*** Comparison ***/ | /*** Comparison ***/ | ||||
bool operator==(const Value& rhs) const { | bool operator==(const Value& rhs) const { return asBits_ == rhs.asBits_; } | ||||
return data.asBits == rhs.data.asBits; | |||||
} | |||||
bool operator!=(const Value& rhs) const { | bool operator!=(const Value& rhs) const { return asBits_ != rhs.asBits_; } | ||||
return data.asBits != rhs.data.asBits; | |||||
} | |||||
friend inline bool SameType(const Value& lhs, const Value& rhs); | friend inline bool SameType(const Value& lhs, const Value& rhs); | ||||
/*** Extract the value's typed payload ***/ | /*** Extract the value's typed payload ***/ | ||||
int32_t toInt32() const { | int32_t toInt32() const { | ||||
MOZ_ASSERT(isInt32()); | MOZ_ASSERT(isInt32()); | ||||
#if defined(JS_NUNBOX32) | #if defined(JS_NUNBOX32) | ||||
return data.s.payload.i32; | return s_.payload_.i32_; | ||||
#elif defined(JS_PUNBOX64) | #elif defined(JS_PUNBOX64) | ||||
return int32_t(data.asBits); | return int32_t(asBits_); | ||||
#endif | #endif | ||||
} | } | ||||
double toDouble() const { | double toDouble() const { | ||||
MOZ_ASSERT(isDouble()); | MOZ_ASSERT(isDouble()); | ||||
return data.asDouble; | return asDouble_; | ||||
} | } | ||||
double toNumber() const { | double toNumber() const { | ||||
MOZ_ASSERT(isNumber()); | MOZ_ASSERT(isNumber()); | ||||
return isDouble() ? toDouble() : double(toInt32()); | return isDouble() ? toDouble() : double(toInt32()); | ||||
} | } | ||||
JSString* toString() const { | JSString* toString() const { | ||||
MOZ_ASSERT(isString()); | MOZ_ASSERT(isString()); | ||||
#if defined(JS_NUNBOX32) | #if defined(JS_NUNBOX32) | ||||
return data.s.payload.str; | return s_.payload_.str_; | ||||
#elif defined(JS_PUNBOX64) | #elif defined(JS_PUNBOX64) | ||||
return reinterpret_cast<JSString*>(data.asBits ^ JSVAL_SHIFTED_TAG_STRING); | return reinterpret_cast<JSString*>(asBits_ ^ JSVAL_SHIFTED_TAG_STRING); | ||||
#endif | #endif | ||||
} | } | ||||
JS::Symbol* toSymbol() const { | JS::Symbol* toSymbol() const { | ||||
MOZ_ASSERT(isSymbol()); | MOZ_ASSERT(isSymbol()); | ||||
#if defined(JS_NUNBOX32) | #if defined(JS_NUNBOX32) | ||||
return data.s.payload.sym; | return s_.payload_.sym_; | ||||
#elif defined(JS_PUNBOX64) | #elif defined(JS_PUNBOX64) | ||||
return reinterpret_cast<JS::Symbol*>(data.asBits ^ | return reinterpret_cast<JS::Symbol*>(asBits_ ^ JSVAL_SHIFTED_TAG_SYMBOL); | ||||
JSVAL_SHIFTED_TAG_SYMBOL); | #endif | ||||
} | |||||
JS::BigInt* toBigInt() const { | |||||
MOZ_ASSERT(isBigInt()); | |||||
#if defined(JS_NUNBOX32) | |||||
return s_.payload_.bi_; | |||||
#elif defined(JS_PUNBOX64) | |||||
return reinterpret_cast<JS::BigInt*>(asBits_ ^ JSVAL_SHIFTED_TAG_BIGINT); | |||||
#endif | #endif | ||||
} | } | ||||
JSObject& toObject() const { | JSObject& toObject() const { | ||||
MOZ_ASSERT(isObject()); | MOZ_ASSERT(isObject()); | ||||
#if defined(JS_NUNBOX32) | #if defined(JS_NUNBOX32) | ||||
return *data.s.payload.obj; | return *s_.payload_.obj_; | ||||
#elif defined(JS_PUNBOX64) | #elif defined(JS_PUNBOX64) | ||||
uint64_t ptrBits = data.asBits ^ JSVAL_SHIFTED_TAG_OBJECT; | uint64_t ptrBits = asBits_ ^ JSVAL_SHIFTED_TAG_OBJECT; | ||||
MOZ_ASSERT(ptrBits); | MOZ_ASSERT(ptrBits); | ||||
MOZ_ASSERT((ptrBits & 0x7) == 0); | MOZ_ASSERT((ptrBits & 0x7) == 0); | ||||
return *reinterpret_cast<JSObject*>(ptrBits); | return *reinterpret_cast<JSObject*>(ptrBits); | ||||
#endif | #endif | ||||
} | } | ||||
JSObject* toObjectOrNull() const { | JSObject* toObjectOrNull() const { | ||||
MOZ_ASSERT(isObjectOrNull()); | MOZ_ASSERT(isObjectOrNull()); | ||||
#if defined(JS_NUNBOX32) | #if defined(JS_NUNBOX32) | ||||
return data.s.payload.obj; | return s_.payload_.obj_; | ||||
#elif defined(JS_PUNBOX64) | #elif defined(JS_PUNBOX64) | ||||
// Note: the 'Spectre mitigations' comment at the top of this class | // Note: the 'Spectre mitigations' comment at the top of this class | ||||
// explains why we use XOR here and in other to* methods. | // explains why we use XOR here and in other to* methods. | ||||
uint64_t ptrBits = | uint64_t ptrBits = | ||||
(data.asBits ^ JSVAL_SHIFTED_TAG_OBJECT) & ~JSVAL_OBJECT_OR_NULL_BIT; | (asBits_ ^ JSVAL_SHIFTED_TAG_OBJECT) & ~JSVAL_OBJECT_OR_NULL_BIT; | ||||
MOZ_ASSERT((ptrBits & 0x7) == 0); | MOZ_ASSERT((ptrBits & 0x7) == 0); | ||||
return reinterpret_cast<JSObject*>(ptrBits); | return reinterpret_cast<JSObject*>(ptrBits); | ||||
#endif | #endif | ||||
} | } | ||||
js::gc::Cell* toGCThing() const { | js::gc::Cell* toGCThing() const { | ||||
MOZ_ASSERT(isGCThing()); | MOZ_ASSERT(isGCThing()); | ||||
#if defined(JS_NUNBOX32) | #if defined(JS_NUNBOX32) | ||||
return data.s.payload.cell; | return s_.payload_.cell_; | ||||
#elif defined(JS_PUNBOX64) | #elif defined(JS_PUNBOX64) | ||||
uint64_t ptrBits = data.asBits & JSVAL_PAYLOAD_MASK_GCTHING; | uint64_t ptrBits = asBits_ & JSVAL_PAYLOAD_MASK_GCTHING; | ||||
MOZ_ASSERT((ptrBits & 0x7) == 0); | MOZ_ASSERT((ptrBits & 0x7) == 0); | ||||
return reinterpret_cast<js::gc::Cell*>(ptrBits); | return reinterpret_cast<js::gc::Cell*>(ptrBits); | ||||
#endif | #endif | ||||
} | } | ||||
GCCellPtr toGCCellPtr() const { return GCCellPtr(toGCThing(), traceKind()); } | GCCellPtr toGCCellPtr() const { return GCCellPtr(toGCThing(), traceKind()); } | ||||
bool toBoolean() const { | bool toBoolean() const { | ||||
MOZ_ASSERT(isBoolean()); | MOZ_ASSERT(isBoolean()); | ||||
#if defined(JS_NUNBOX32) | #if defined(JS_NUNBOX32) | ||||
return bool(data.s.payload.boo); | return bool(s_.payload_.boo_); | ||||
#elif defined(JS_PUNBOX64) | #elif defined(JS_PUNBOX64) | ||||
return bool(int32_t(data.asBits)); | return bool(int32_t(asBits_)); | ||||
#endif | #endif | ||||
} | } | ||||
uint32_t payloadAsRawUint32() const { | uint32_t payloadAsRawUint32() const { | ||||
MOZ_ASSERT(!isDouble()); | MOZ_ASSERT(!isDouble()); | ||||
return data.s.payload.u32; | return s_.payload_.u32_; | ||||
} | } | ||||
uint64_t asRawBits() const { return data.asBits; } | uint64_t asRawBits() const { return asBits_; } | ||||
JSValueType extractNonDoubleType() const { | JSValueType extractNonDoubleType() const { | ||||
uint32_t type = toTag() & 0xF; | uint32_t type = toTag() & 0xF; | ||||
MOZ_ASSERT(type > JSVAL_TYPE_DOUBLE); | MOZ_ASSERT(type > JSVAL_TYPE_DOUBLE); | ||||
return JSValueType(type); | return JSValueType(type); | ||||
} | } | ||||
JS::ValueType type() const { | |||||
if (isDouble()) { | |||||
return JS::ValueType::Double; | |||||
} | |||||
JSValueType type = extractNonDoubleType(); | |||||
MOZ_ASSERT(type <= JSVAL_TYPE_OBJECT); | |||||
return JS::ValueType(type); | |||||
} | |||||
/* | /* | ||||
* Private API | * Private API | ||||
* | * | ||||
* Private setters/getters allow the caller to read/write arbitrary types | * 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 | * 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. | * storing to a value with setPrivateX to read only using getPrivateX. | ||||
* Privates values are given a type which ensures they are not marked. | * Privates values are given a type which ensures they are not marked. | ||||
*/ | */ | ||||
void setPrivate(void* ptr) { | void setPrivate(void* ptr) { | ||||
MOZ_ASSERT((uintptr_t(ptr) & 1) == 0); | |||||
#if defined(JS_NUNBOX32) | #if defined(JS_NUNBOX32) | ||||
data.s.tag = JSValueTag(0); | s_.tag_ = JSValueTag(0); | ||||
data.s.payload.ptr = ptr; | s_.payload_.ptr_ = ptr; | ||||
#elif defined(JS_PUNBOX64) | #elif defined(JS_PUNBOX64) | ||||
data.asBits = uintptr_t(ptr) >> 1; | # if defined(JS_UNALIGNED_PRIVATE_VALUES) | ||||
// ptr must be a valid user-mode pointer, with the top 16 bits clear. | |||||
MOZ_ASSERT((uintptr_t(ptr) & 0xFFFF000000000000ULL) == 0); | |||||
asBits_ = uintptr_t(ptr); | |||||
# else | |||||
MOZ_ASSERT((uintptr_t(ptr) & 1) == 0); | |||||
asBits_ = uintptr_t(ptr) >> 1; | |||||
# endif | |||||
#endif | #endif | ||||
MOZ_ASSERT(isDouble()); | MOZ_ASSERT(isDouble()); | ||||
} | } | ||||
void* toPrivate() const { | void* toPrivate() const { | ||||
MOZ_ASSERT(isDouble()); | MOZ_ASSERT(isDouble()); | ||||
#if defined(JS_NUNBOX32) | #if defined(JS_NUNBOX32) | ||||
return data.s.payload.ptr; | return s_.payload_.ptr_; | ||||
#elif defined(JS_PUNBOX64) | #elif defined(JS_PUNBOX64) | ||||
MOZ_ASSERT((data.asBits & 0x8000000000000000ULL) == 0); | # if defined(JS_UNALIGNED_PRIVATE_VALUES) | ||||
return reinterpret_cast<void*>(data.asBits << 1); | // This must be a valid user-mode pointer, with the top 16 bits clear. | ||||
MOZ_ASSERT((asBits_ & 0xFFFF000000000000ULL) == 0); | |||||
return reinterpret_cast<void*>(asBits_); | |||||
# else | |||||
return reinterpret_cast<void*>(asBits_ << 1); | |||||
# endif | |||||
#endif | #endif | ||||
} | } | ||||
void setPrivateUint32(uint32_t ui) { | void setPrivateUint32(uint32_t ui) { | ||||
MOZ_ASSERT(uint32_t(int32_t(ui)) == ui); | MOZ_ASSERT(uint32_t(int32_t(ui)) == ui); | ||||
setInt32(int32_t(ui)); | setInt32(int32_t(ui)); | ||||
} | } | ||||
Show All 10 Lines | #endif | ||||
void setPrivateGCThing(js::gc::Cell* cell) { | void setPrivateGCThing(js::gc::Cell* cell) { | ||||
MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::String, | MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::String, | ||||
"Private GC thing Values must not be strings. Make a " | "Private GC thing Values must not be strings. Make a " | ||||
"StringValue instead."); | "StringValue instead."); | ||||
MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Symbol, | MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Symbol, | ||||
"Private GC thing Values must not be symbols. Make a " | "Private GC thing Values must not be symbols. Make a " | ||||
"SymbolValue instead."); | "SymbolValue instead."); | ||||
MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::BigInt, | |||||
"Private GC thing Values must not be BigInts. Make a " | |||||
"BigIntValue instead."); | |||||
MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Object, | MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Object, | ||||
"Private GC thing Values must not be objects. Make an " | "Private GC thing Values must not be objects. Make an " | ||||
"ObjectValue instead."); | "ObjectValue instead."); | ||||
MOZ_ASSERT(js::gc::IsCellPointerValid(cell)); | MOZ_ASSERT(js::gc::IsCellPointerValid(cell)); | ||||
#if defined(JS_PUNBOX64) | #if defined(JS_PUNBOX64) | ||||
// VisualStudio cannot contain parenthesized C++ style cast and shift | // VisualStudio cannot contain parenthesized C++ style cast and shift | ||||
// inside decltype in template parameter: | // inside decltype in template parameter: | ||||
// AssertionConditionType<decltype((uintptr_t(x) >> 1))> | // AssertionConditionType<decltype((uintptr_t(x) >> 1))> | ||||
// It throws syntax error. | // It throws syntax error. | ||||
MOZ_ASSERT((((uintptr_t)cell) >> JSVAL_TAG_SHIFT) == 0); | MOZ_ASSERT((((uintptr_t)cell) >> JSVAL_TAG_SHIFT) == 0); | ||||
#endif | #endif | ||||
data.asBits = | asBits_ = | ||||
bitsFromTagAndPayload(JSVAL_TAG_PRIVATE_GCTHING, PayloadType(cell)); | bitsFromTagAndPayload(JSVAL_TAG_PRIVATE_GCTHING, PayloadType(cell)); | ||||
} | } | ||||
bool isPrivateGCThing() const { return toTag() == JSVAL_TAG_PRIVATE_GCTHING; } | bool isPrivateGCThing() const { return toTag() == JSVAL_TAG_PRIVATE_GCTHING; } | ||||
} JS_HAZ_GC_POINTER MOZ_NON_PARAM; | |||||
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, | static_assert(sizeof(Value) == 8, | ||||
"Value size must leave three tag bits, be a binary power, and is " | "Value size must leave three tag bits, be a binary power, and " | ||||
"ubiquitously depended upon everywhere"); | "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) { | inline bool IsOptimizedPlaceholderMagicValue(const Value& v) { | ||||
if (v.isMagic()) { | if (v.isMagic()) { | ||||
MOZ_ASSERT(v.whyMagic() == JS_OPTIMIZED_ARGUMENTS || | MOZ_ASSERT(v.whyMagic() == JS_OPTIMIZED_ARGUMENTS || | ||||
v.whyMagic() == JS_OPTIMIZED_OUT); | v.whyMagic() == JS_OPTIMIZED_OUT); | ||||
return true; | return true; | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
static MOZ_ALWAYS_INLINE void ExposeValueToActiveJS(const Value& v) { | static MOZ_ALWAYS_INLINE void ExposeValueToActiveJS(const Value& v) { | ||||
#ifdef DEBUG | #ifdef DEBUG | ||||
Value tmp = v; | Value tmp = v; | ||||
MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarrieredSlow(&tmp)); | MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarrieredSlow(&tmp)); | ||||
#endif | #endif | ||||
if (v.isGCThing()) js::gc::ExposeGCThingToActiveJS(GCCellPtr(v)); | if (v.isGCThing()) { | ||||
js::gc::ExposeGCThingToActiveJS(GCCellPtr(v)); | |||||
} | |||||
} | } | ||||
/************************************************************************/ | /************************************************************************/ | ||||
static inline MOZ_MAY_CALL_AFTER_MUST_RETURN Value NullValue() { | static inline MOZ_MAY_CALL_AFTER_MUST_RETURN Value NullValue() { | ||||
Value v; | Value v; | ||||
v.setNull(); | v.setNull(); | ||||
return v; | return v; | ||||
} | } | ||||
static inline constexpr Value UndefinedValue() { | static inline constexpr Value UndefinedValue() { return Value(); } | ||||
return Value::fromTagAndPayload(JSVAL_TAG_UNDEFINED, 0); | |||||
} | |||||
static inline constexpr Value Int32Value(int32_t i32) { | static inline constexpr Value Int32Value(int32_t i32) { | ||||
return Value::fromInt32(i32); | return Value::fromInt32(i32); | ||||
} | } | ||||
static inline Value DoubleValue(double dbl) { | static inline Value DoubleValue(double dbl) { | ||||
Value v; | Value v; | ||||
v.setDouble(dbl); | v.setDouble(dbl); | ||||
return v; | return v; | ||||
} | } | ||||
static inline Value CanonicalizedDoubleValue(double d) { | static inline Value CanonicalizedDoubleValue(double d) { | ||||
return MOZ_UNLIKELY(mozilla::IsNaN(d)) | return MOZ_UNLIKELY(mozilla::IsNaN(d)) | ||||
? Value::fromRawBits(detail::CanonicalizedNaNBits) | ? Value::fromRawBits(detail::CanonicalizedNaNBits) | ||||
: Value::fromDouble(d); | : Value::fromDouble(d); | ||||
} | } | ||||
static inline bool IsCanonicalized(double d) { | static inline bool IsCanonicalized(double d) { | ||||
if (mozilla::IsInfinite(d) || mozilla::IsFinite(d)) return true; | if (mozilla::IsInfinite(d) || mozilla::IsFinite(d)) { | ||||
return true; | |||||
} | |||||
uint64_t bits; | uint64_t bits; | ||||
mozilla::BitwiseCast<uint64_t>(d, &bits); | mozilla::BitwiseCast<uint64_t>(d, &bits); | ||||
return (bits & ~mozilla::DoubleTypeTraits::kSignBit) == | return (bits & ~mozilla::FloatingPoint<double>::kSignBit) == | ||||
detail::CanonicalizedNaNBits; | detail::CanonicalizedNaNBits; | ||||
} | } | ||||
static inline Value DoubleNaNValue() { | static inline Value DoubleNaNValue() { | ||||
Value v; | Value v; | ||||
v.setNaN(); | v.setNaN(); | ||||
return v; | return v; | ||||
} | } | ||||
Show All 11 Lines | |||||
} | } | ||||
static inline Value SymbolValue(JS::Symbol* sym) { | static inline Value SymbolValue(JS::Symbol* sym) { | ||||
Value v; | Value v; | ||||
v.setSymbol(sym); | v.setSymbol(sym); | ||||
return v; | return v; | ||||
} | } | ||||
static inline Value BigIntValue(JS::BigInt* bi) { | |||||
Value v; | |||||
v.setBigInt(bi); | |||||
return v; | |||||
} | |||||
static inline Value BooleanValue(bool boo) { | static inline Value BooleanValue(bool boo) { | ||||
Value v; | Value v; | ||||
v.setBoolean(boo); | v.setBoolean(boo); | ||||
return v; | return v; | ||||
} | } | ||||
static inline Value TrueValue() { | static inline Value TrueValue() { | ||||
Value v; | Value v; | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | |||||
namespace detail { | namespace detail { | ||||
template <bool Signed> | template <bool Signed> | ||||
class MakeNumberValue { | class MakeNumberValue { | ||||
public: | public: | ||||
template <typename T> | template <typename T> | ||||
static inline Value create(const T t) { | static inline Value create(const T t) { | ||||
Value v; | Value v; | ||||
if (JSVAL_INT_MIN <= t && t <= JSVAL_INT_MAX) | if (JSVAL_INT_MIN <= t && t <= JSVAL_INT_MAX) { | ||||
v.setInt32(int32_t(t)); | v.setInt32(int32_t(t)); | ||||
else | } else { | ||||
v.setDouble(double(t)); | v.setDouble(double(t)); | ||||
} | |||||
return v; | return v; | ||||
} | } | ||||
}; | }; | ||||
template <> | template <> | ||||
class MakeNumberValue<false> { | class MakeNumberValue<false> { | ||||
public: | public: | ||||
template <typename T> | template <typename T> | ||||
static inline Value create(const T t) { | static inline Value create(const T t) { | ||||
Value v; | Value v; | ||||
if (t <= JSVAL_INT_MAX) | if (t <= JSVAL_INT_MAX) { | ||||
v.setInt32(int32_t(t)); | v.setInt32(int32_t(t)); | ||||
else | } else { | ||||
v.setDouble(double(t)); | v.setDouble(double(t)); | ||||
} | |||||
return v; | return v; | ||||
} | } | ||||
}; | }; | ||||
} // namespace detail | } // namespace detail | ||||
template <typename T> | template <typename T> | ||||
static inline Value NumberValue(const T t) { | static inline Value NumberValue(const T t) { | ||||
Show All 26 Lines | |||||
} | } | ||||
inline bool SameType(const Value& lhs, const Value& rhs) { | inline bool SameType(const Value& lhs, const Value& rhs) { | ||||
#if defined(JS_NUNBOX32) | #if defined(JS_NUNBOX32) | ||||
JSValueTag ltag = lhs.toTag(), rtag = rhs.toTag(); | JSValueTag ltag = lhs.toTag(), rtag = rhs.toTag(); | ||||
return ltag == rtag || (ltag < JSVAL_TAG_CLEAR && rtag < JSVAL_TAG_CLEAR); | return ltag == rtag || (ltag < JSVAL_TAG_CLEAR && rtag < JSVAL_TAG_CLEAR); | ||||
#elif defined(JS_PUNBOX64) | #elif defined(JS_PUNBOX64) | ||||
return (lhs.isDouble() && rhs.isDouble()) || | return (lhs.isDouble() && rhs.isDouble()) || | ||||
(((lhs.data.asBits ^ rhs.data.asBits) & 0xFFFF800000000000ULL) == 0); | (((lhs.asBits_ ^ rhs.asBits_) & 0xFFFF800000000000ULL) == 0); | ||||
#endif | #endif | ||||
} | } | ||||
} // namespace JS | } // namespace JS | ||||
/************************************************************************/ | /************************************************************************/ | ||||
namespace JS { | namespace JS { | ||||
JS_PUBLIC_API void HeapValuePostBarrier(Value* valuep, const Value& prev, | JS_PUBLIC_API void HeapValueWriteBarriers(Value* valuep, const Value& prev, | ||||
const Value& next); | const Value& next); | ||||
template <> | template <> | ||||
struct GCPolicy<JS::Value> { | struct GCPolicy<JS::Value> { | ||||
static Value initial() { return UndefinedValue(); } | |||||
static void trace(JSTracer* trc, Value* v, const char* name) { | static void trace(JSTracer* trc, Value* v, const char* name) { | ||||
js::UnsafeTraceManuallyBarrieredEdge(trc, v, name); | js::UnsafeTraceManuallyBarrieredEdge(trc, v, name); | ||||
} | } | ||||
static bool isTenured(const Value& thing) { | static bool isTenured(const Value& thing) { | ||||
return !thing.isGCThing() || !IsInsideNursery(thing.toGCThing()); | return !thing.isGCThing() || !IsInsideNursery(thing.toGCThing()); | ||||
} | } | ||||
static bool isValid(const Value& value) { | static bool isValid(const Value& value) { | ||||
return !value.isGCThing() || js::gc::IsCellPointerValid(value.toGCThing()); | return !value.isGCThing() || js::gc::IsCellPointerValid(value.toGCThing()); | ||||
} | } | ||||
}; | }; | ||||
} // namespace JS | } // namespace JS | ||||
namespace js { | namespace js { | ||||
template <> | template <> | ||||
struct BarrierMethods<JS::Value> { | struct BarrierMethods<JS::Value> { | ||||
static gc::Cell* asGCThingOrNull(const JS::Value& v) { | static gc::Cell* asGCThingOrNull(const JS::Value& v) { | ||||
return v.isGCThing() ? v.toGCThing() : nullptr; | return v.isGCThing() ? v.toGCThing() : nullptr; | ||||
} | } | ||||
static void postBarrier(JS::Value* v, const JS::Value& prev, | static void writeBarriers(JS::Value* v, const JS::Value& prev, | ||||
const JS::Value& next) { | const JS::Value& next) { | ||||
JS::HeapValuePostBarrier(v, prev, next); | JS::HeapValueWriteBarriers(v, prev, next); | ||||
} | } | ||||
static void exposeToJS(const JS::Value& v) { JS::ExposeValueToActiveJS(v); } | static void exposeToJS(const JS::Value& v) { JS::ExposeValueToActiveJS(v); } | ||||
}; | }; | ||||
template <class Wrapper> | template <class Wrapper> | ||||
class MutableValueOperations; | class MutableValueOperations; | ||||
/** | /** | ||||
Show All 15 Lines | public: | ||||
bool isTrue() const { return value().isTrue(); } | bool isTrue() const { return value().isTrue(); } | ||||
bool isFalse() const { return value().isFalse(); } | bool isFalse() const { return value().isFalse(); } | ||||
bool isNumber() const { return value().isNumber(); } | bool isNumber() const { return value().isNumber(); } | ||||
bool isInt32() const { return value().isInt32(); } | bool isInt32() const { return value().isInt32(); } | ||||
bool isInt32(int32_t i32) const { return value().isInt32(i32); } | bool isInt32(int32_t i32) const { return value().isInt32(i32); } | ||||
bool isDouble() const { return value().isDouble(); } | bool isDouble() const { return value().isDouble(); } | ||||
bool isString() const { return value().isString(); } | bool isString() const { return value().isString(); } | ||||
bool isSymbol() const { return value().isSymbol(); } | bool isSymbol() const { return value().isSymbol(); } | ||||
bool isBigInt() const { return value().isBigInt(); } | |||||
bool isObject() const { return value().isObject(); } | bool isObject() const { return value().isObject(); } | ||||
bool isMagic() const { return value().isMagic(); } | bool isMagic() const { return value().isMagic(); } | ||||
bool isMagic(JSWhyMagic why) const { return value().isMagic(why); } | bool isMagic(JSWhyMagic why) const { return value().isMagic(why); } | ||||
bool isGCThing() const { return value().isGCThing(); } | bool isGCThing() const { return value().isGCThing(); } | ||||
bool isPrimitive() const { return value().isPrimitive(); } | bool isPrimitive() const { return value().isPrimitive(); } | ||||
bool isNullOrUndefined() const { return value().isNullOrUndefined(); } | bool isNullOrUndefined() const { return value().isNullOrUndefined(); } | ||||
bool isObjectOrNull() const { return value().isObjectOrNull(); } | bool isObjectOrNull() const { return value().isObjectOrNull(); } | ||||
bool toBoolean() const { return value().toBoolean(); } | bool toBoolean() const { return value().toBoolean(); } | ||||
double toNumber() const { return value().toNumber(); } | double toNumber() const { return value().toNumber(); } | ||||
int32_t toInt32() const { return value().toInt32(); } | int32_t toInt32() const { return value().toInt32(); } | ||||
double toDouble() const { return value().toDouble(); } | double toDouble() const { return value().toDouble(); } | ||||
JSString* toString() const { return value().toString(); } | JSString* toString() const { return value().toString(); } | ||||
JS::Symbol* toSymbol() const { return value().toSymbol(); } | JS::Symbol* toSymbol() const { return value().toSymbol(); } | ||||
JS::BigInt* toBigInt() const { return value().toBigInt(); } | |||||
JSObject& toObject() const { return value().toObject(); } | JSObject& toObject() const { return value().toObject(); } | ||||
JSObject* toObjectOrNull() const { return value().toObjectOrNull(); } | JSObject* toObjectOrNull() const { return value().toObjectOrNull(); } | ||||
gc::Cell* toGCThing() const { return value().toGCThing(); } | gc::Cell* toGCThing() const { return value().toGCThing(); } | ||||
JS::TraceKind traceKind() const { return value().traceKind(); } | JS::TraceKind traceKind() const { return value().traceKind(); } | ||||
void* toPrivate() const { return value().toPrivate(); } | void* toPrivate() const { return value().toPrivate(); } | ||||
uint32_t toPrivateUint32() const { return value().toPrivateUint32(); } | uint32_t toPrivateUint32() const { return value().toPrivateUint32(); } | ||||
uint64_t asRawBits() const { return value().asRawBits(); } | uint64_t asRawBits() const { return value().asRawBits(); } | ||||
JSValueType extractNonDoubleType() const { | JSValueType extractNonDoubleType() const { | ||||
return value().extractNonDoubleType(); | return value().extractNonDoubleType(); | ||||
} | } | ||||
JS::ValueType type() const { return value().type(); } | |||||
JSWhyMagic whyMagic() const { return value().whyMagic(); } | JSWhyMagic whyMagic() const { return value().whyMagic(); } | ||||
uint32_t magicUint32() const { return value().magicUint32(); } | uint32_t magicUint32() const { return value().magicUint32(); } | ||||
}; | }; | ||||
/** | /** | ||||
* A class designed for CRTP use in implementing all the mutating parts of the | * 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 | * Value interface in Value-like classes. Wrapper must be a class inheriting | ||||
Show All 12 Lines | public: | ||||
void setDouble(double d) { value().setDouble(d); } | void setDouble(double d) { value().setDouble(d); } | ||||
void setNaN() { setDouble(JS::GenericNaN()); } | void setNaN() { setDouble(JS::GenericNaN()); } | ||||
void setBoolean(bool b) { value().setBoolean(b); } | void setBoolean(bool b) { value().setBoolean(b); } | ||||
void setMagic(JSWhyMagic why) { value().setMagic(why); } | void setMagic(JSWhyMagic why) { value().setMagic(why); } | ||||
bool setNumber(uint32_t ui) { return value().setNumber(ui); } | bool setNumber(uint32_t ui) { return value().setNumber(ui); } | ||||
bool setNumber(double d) { return value().setNumber(d); } | bool setNumber(double d) { return value().setNumber(d); } | ||||
void setString(JSString* str) { this->value().setString(str); } | void setString(JSString* str) { this->value().setString(str); } | ||||
void setSymbol(JS::Symbol* sym) { this->value().setSymbol(sym); } | void setSymbol(JS::Symbol* sym) { this->value().setSymbol(sym); } | ||||
void setBigInt(JS::BigInt* bi) { this->value().setBigInt(bi); } | |||||
void setObject(JSObject& obj) { this->value().setObject(obj); } | void setObject(JSObject& obj) { this->value().setObject(obj); } | ||||
void setObjectOrNull(JSObject* arg) { this->value().setObjectOrNull(arg); } | void setObjectOrNull(JSObject* arg) { this->value().setObjectOrNull(arg); } | ||||
void setPrivate(void* ptr) { this->value().setPrivate(ptr); } | void setPrivate(void* ptr) { this->value().setPrivate(ptr); } | ||||
void setPrivateUint32(uint32_t ui) { this->value().setPrivateUint32(ui); } | void setPrivateUint32(uint32_t ui) { this->value().setPrivateUint32(ui); } | ||||
void setPrivateGCThing(js::gc::Cell* cell) { | void setPrivateGCThing(js::gc::Cell* cell) { | ||||
this->value().setPrivateGCThing(cell); | this->value().setPrivateGCThing(cell); | ||||
} | } | ||||
}; | }; | ||||
Show All 14 Lines | public: | ||||
void setUndefined() { setBarriered(JS::UndefinedValue()); } | void setUndefined() { setBarriered(JS::UndefinedValue()); } | ||||
void setInt32(int32_t i) { setBarriered(JS::Int32Value(i)); } | void setInt32(int32_t i) { setBarriered(JS::Int32Value(i)); } | ||||
void setDouble(double d) { setBarriered(JS::DoubleValue(d)); } | void setDouble(double d) { setBarriered(JS::DoubleValue(d)); } | ||||
void setNaN() { setDouble(JS::GenericNaN()); } | void setNaN() { setDouble(JS::GenericNaN()); } | ||||
void setBoolean(bool b) { setBarriered(JS::BooleanValue(b)); } | void setBoolean(bool b) { setBarriered(JS::BooleanValue(b)); } | ||||
void setMagic(JSWhyMagic why) { setBarriered(JS::MagicValue(why)); } | void setMagic(JSWhyMagic why) { setBarriered(JS::MagicValue(why)); } | ||||
void setString(JSString* str) { setBarriered(JS::StringValue(str)); } | void setString(JSString* str) { setBarriered(JS::StringValue(str)); } | ||||
void setSymbol(JS::Symbol* sym) { setBarriered(JS::SymbolValue(sym)); } | void setSymbol(JS::Symbol* sym) { setBarriered(JS::SymbolValue(sym)); } | ||||
void setBigInt(JS::BigInt* bi) { setBarriered(JS::BigIntValue(bi)); } | |||||
void setObject(JSObject& obj) { setBarriered(JS::ObjectValue(obj)); } | void setObject(JSObject& obj) { setBarriered(JS::ObjectValue(obj)); } | ||||
void setPrivateGCThing(js::gc::Cell* cell) { | void setPrivateGCThing(js::gc::Cell* cell) { | ||||
setBarriered(JS::PrivateGCThingValue(cell)); | setBarriered(JS::PrivateGCThingValue(cell)); | ||||
} | } | ||||
bool setNumber(uint32_t ui) { | bool setNumber(uint32_t ui) { | ||||
if (ui > JSVAL_INT_MAX) { | if (ui > JSVAL_INT_MAX) { | ||||
setDouble((double)ui); | setDouble((double)ui); | ||||
Show All 11 Lines | if (mozilla::NumberIsInt32(d, &i)) { | ||||
return true; | return true; | ||||
} | } | ||||
setDouble(d); | setDouble(d); | ||||
return false; | return false; | ||||
} | } | ||||
void setObjectOrNull(JSObject* arg) { | void setObjectOrNull(JSObject* arg) { | ||||
if (arg) | if (arg) { | ||||
setObject(*arg); | setObject(*arg); | ||||
else | } else { | ||||
setNull(); | setNull(); | ||||
} | } | ||||
} | |||||
}; | }; | ||||
/* | // If the Value is a GC pointer type, call |f| with the pointer cast to that | ||||
* If the Value is a GC pointer type, convert to that type and call |f| with | // type and return the result wrapped in a Maybe, otherwise return None(). | ||||
* the pointer. If the Value is not a GC type, calls F::defaultValue. | template <typename F> | ||||
*/ | auto MapGCThingTyped(const JS::Value& val, F&& f) { | ||||
template <typename F, typename... Args> | switch (val.type()) { | ||||
auto DispatchTyped(F f, const JS::Value& val, Args&&... args) | case JS::ValueType::String: { | ||||
-> decltype(f(static_cast<JSObject*>(nullptr), | |||||
mozilla::Forward<Args>(args)...)) { | |||||
if (val.isString()) { | |||||
JSString* str = val.toString(); | JSString* str = val.toString(); | ||||
MOZ_ASSERT(gc::IsCellPointerValid(str)); | MOZ_ASSERT(gc::IsCellPointerValid(str)); | ||||
return f(str, mozilla::Forward<Args>(args)...); | return mozilla::Some(f(str)); | ||||
} | } | ||||
if (val.isObject()) { | case JS::ValueType::Object: { | ||||
JSObject* obj = &val.toObject(); | JSObject* obj = &val.toObject(); | ||||
MOZ_ASSERT(gc::IsCellPointerValid(obj)); | MOZ_ASSERT(gc::IsCellPointerValid(obj)); | ||||
return f(obj, mozilla::Forward<Args>(args)...); | return mozilla::Some(f(obj)); | ||||
} | } | ||||
if (val.isSymbol()) { | case JS::ValueType::Symbol: { | ||||
JS::Symbol* sym = val.toSymbol(); | JS::Symbol* sym = val.toSymbol(); | ||||
MOZ_ASSERT(gc::IsCellPointerValid(sym)); | MOZ_ASSERT(gc::IsCellPointerValid(sym)); | ||||
return f(sym, mozilla::Forward<Args>(args)...); | return mozilla::Some(f(sym)); | ||||
} | |||||
case JS::ValueType::BigInt: { | |||||
JS::BigInt* bi = val.toBigInt(); | |||||
MOZ_ASSERT(gc::IsCellPointerValid(bi)); | |||||
return mozilla::Some(f(bi)); | |||||
} | } | ||||
if (MOZ_UNLIKELY(val.isPrivateGCThing())) { | case JS::ValueType::PrivateGCThing: { | ||||
MOZ_ASSERT(gc::IsCellPointerValid(val.toGCThing())); | MOZ_ASSERT(gc::IsCellPointerValid(val.toGCThing())); | ||||
return DispatchTyped(f, val.toGCCellPtr(), mozilla::Forward<Args>(args)...); | return mozilla::Some(MapGCThingTyped(val.toGCCellPtr(), std::move(f))); | ||||
} | } | ||||
case JS::ValueType::Double: | |||||
case JS::ValueType::Int32: | |||||
case JS::ValueType::Boolean: | |||||
case JS::ValueType::Undefined: | |||||
case JS::ValueType::Null: | |||||
case JS::ValueType::Magic: { | |||||
MOZ_ASSERT(!val.isGCThing()); | MOZ_ASSERT(!val.isGCThing()); | ||||
return F::defaultValue(val); | using ReturnType = decltype(f(static_cast<JSObject*>(nullptr))); | ||||
return mozilla::Maybe<ReturnType>(); | |||||
} | |||||
} | } | ||||
template <class S> | MOZ_CRASH("no missing return"); | ||||
struct VoidDefaultAdaptor { | } | ||||
static void defaultValue(const S&) {} | |||||
}; | // If the Value is a GC pointer type, call |f| with the pointer cast to that | ||||
template <class S> | // type. Return whether this happened. | ||||
struct IdentityDefaultAdaptor { | template <typename F> | ||||
static S defaultValue(const S& v) { return v; } | bool ApplyGCThingTyped(const JS::Value& val, F&& f) { | ||||
}; | return MapGCThingTyped(val, | ||||
template <class S, bool v> | [&f](auto t) { | ||||
struct BoolDefaultAdaptor { | f(t); | ||||
static bool defaultValue(const S&) { return v; } | return true; | ||||
}; | }) | ||||
.isSome(); | |||||
} | |||||
static inline JS::Value PoisonedObjectValue(uintptr_t poison) { | static inline JS::Value PoisonedObjectValue(uintptr_t poison) { | ||||
JS::Value v; | JS::Value v; | ||||
v.setObjectNoCheck(reinterpret_cast<JSObject*>(poison)); | v.setObjectNoCheck(reinterpret_cast<JSObject*>(poison)); | ||||
return v; | return v; | ||||
} | } | ||||
} // namespace js | } // namespace js | ||||
#ifdef DEBUG | #ifdef DEBUG | ||||
namespace JS { | namespace JS { | ||||
MOZ_ALWAYS_INLINE bool ValueIsNotGray(const Value& value) { | MOZ_ALWAYS_INLINE void AssertValueIsNotGray(const Value& value) { | ||||
if (!value.isGCThing()) return true; | if (value.isGCThing()) { | ||||
AssertCellIsNotGray(value.toGCThing()); | |||||
return CellIsNotGray(value.toGCThing()); | } | ||||
} | } | ||||
MOZ_ALWAYS_INLINE bool ValueIsNotGray(const Heap<Value>& value) { | MOZ_ALWAYS_INLINE void AssertValueIsNotGray(const Heap<Value>& value) { | ||||
return ValueIsNotGray(value.unbarrieredGet()); | AssertValueIsNotGray(value.unbarrieredGet()); | ||||
} | } | ||||
} // namespace JS | } // namespace JS | ||||
#endif | #endif | ||||
/************************************************************************/ | /************************************************************************/ | ||||
namespace JS { | namespace JS { | ||||
Show All 9 Lines |
Wildfire Games · Phabricator