gecko-dev/js/public/Value.h
Ciure Andrei a7fbf22348 Backed out 11 changesets (bug 1457560, bug 1366287) for causing Linux build bustages CLOSED TREE
Backed out changeset e71da1f3c048
Backed out changeset 971159738904 (bug 1366287)
Backed out changeset 68fae6784ebe (bug 1366287)
Backed out changeset a18120245eb7 (bug 1366287)
Backed out changeset 3bbd03d726e5 (bug 1366287)
Backed out changeset f80aac2c702a (bug 1366287)
Backed out changeset c834c0c12a7f (bug 1366287)
Backed out changeset dd19d38a2b1c (bug 1366287)
Backed out changeset 76fdbe405fbd (bug 1366287)
Backed out changeset b424782cd5d1 (bug 1366287)
Backed out changeset e4fd5393d398 (bug 1457560)
2018-05-12 08:00:13 +03:00

1436 lines
42 KiB
C++

/* -*- 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/Compiler.h"
#include "mozilla/EndianUtils.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 { union 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.
// Work around a GCC bug. See comment above #undef JS_ENUM_HEADER.
#if MOZ_IS_GCC
# define JS_ENUM_HEADER(id, type) enum id
# define JS_ENUM_FOOTER(id) __attribute__((packed))
#else
# define JS_ENUM_HEADER(id, type) enum id : type
# define JS_ENUM_FOOTER(id)
#endif
enum JSValueType : uint8_t
{
JSVAL_TYPE_DOUBLE = 0x00,
JSVAL_TYPE_INT32 = 0x01,
JSVAL_TYPE_BOOLEAN = 0x02,
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_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
};
static_assert(sizeof(JSValueType) == 1,
"compiler typed enum support is apparently buggy");
#if defined(JS_NUNBOX32)
JS_ENUM_HEADER(JSValueTag, uint32_t)
{
JSVAL_TAG_CLEAR = 0xFFFFFF80,
JSVAL_TAG_INT32 = JSVAL_TAG_CLEAR | JSVAL_TYPE_INT32,
JSVAL_TAG_UNDEFINED = JSVAL_TAG_CLEAR | JSVAL_TYPE_UNDEFINED,
JSVAL_TAG_NULL = JSVAL_TAG_CLEAR | JSVAL_TYPE_NULL,
JSVAL_TAG_BOOLEAN = JSVAL_TAG_CLEAR | JSVAL_TYPE_BOOLEAN,
JSVAL_TAG_MAGIC = JSVAL_TAG_CLEAR | JSVAL_TYPE_MAGIC,
JSVAL_TAG_STRING = JSVAL_TAG_CLEAR | JSVAL_TYPE_STRING,
JSVAL_TAG_SYMBOL = JSVAL_TAG_CLEAR | JSVAL_TYPE_SYMBOL,
JSVAL_TAG_PRIVATE_GCTHING = JSVAL_TAG_CLEAR | JSVAL_TYPE_PRIVATE_GCTHING,
JSVAL_TAG_OBJECT = JSVAL_TAG_CLEAR | JSVAL_TYPE_OBJECT
} JS_ENUM_FOOTER(JSValueTag);
static_assert(sizeof(JSValueTag) == sizeof(uint32_t),
"compiler typed enum support is apparently buggy");
#elif defined(JS_PUNBOX64)
JS_ENUM_HEADER(JSValueTag, uint32_t)
{
JSVAL_TAG_MAX_DOUBLE = 0x1FFF0,
JSVAL_TAG_INT32 = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32,
JSVAL_TAG_UNDEFINED = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_UNDEFINED,
JSVAL_TAG_NULL = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_NULL,
JSVAL_TAG_BOOLEAN = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN,
JSVAL_TAG_MAGIC = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_MAGIC,
JSVAL_TAG_STRING = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_STRING,
JSVAL_TAG_SYMBOL = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_SYMBOL,
JSVAL_TAG_PRIVATE_GCTHING = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_PRIVATE_GCTHING,
JSVAL_TAG_OBJECT = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_OBJECT
} JS_ENUM_FOOTER(JSValueTag);
static_assert(sizeof(JSValueTag) == sizeof(uint32_t),
"compiler typed enum support is apparently buggy");
enum JSValueShiftedTag : uint64_t
{
JSVAL_SHIFTED_TAG_MAX_DOUBLE = ((((uint64_t)JSVAL_TAG_MAX_DOUBLE) << JSVAL_TAG_SHIFT) | 0xFFFFFFFF),
JSVAL_SHIFTED_TAG_INT32 = (((uint64_t)JSVAL_TAG_INT32) << JSVAL_TAG_SHIFT),
JSVAL_SHIFTED_TAG_UNDEFINED = (((uint64_t)JSVAL_TAG_UNDEFINED) << JSVAL_TAG_SHIFT),
JSVAL_SHIFTED_TAG_NULL = (((uint64_t)JSVAL_TAG_NULL) << JSVAL_TAG_SHIFT),
JSVAL_SHIFTED_TAG_BOOLEAN = (((uint64_t)JSVAL_TAG_BOOLEAN) << JSVAL_TAG_SHIFT),
JSVAL_SHIFTED_TAG_MAGIC = (((uint64_t)JSVAL_TAG_MAGIC) << JSVAL_TAG_SHIFT),
JSVAL_SHIFTED_TAG_STRING = (((uint64_t)JSVAL_TAG_STRING) << JSVAL_TAG_SHIFT),
JSVAL_SHIFTED_TAG_SYMBOL = (((uint64_t)JSVAL_TAG_SYMBOL) << JSVAL_TAG_SHIFT),
JSVAL_SHIFTED_TAG_PRIVATE_GCTHING = (((uint64_t)JSVAL_TAG_PRIVATE_GCTHING) << JSVAL_TAG_SHIFT),
JSVAL_SHIFTED_TAG_OBJECT = (((uint64_t)JSVAL_TAG_OBJECT) << JSVAL_TAG_SHIFT)
};
static_assert(sizeof(JSValueShiftedTag) == sizeof(uint64_t),
"compiler typed enum support is apparently buggy");
#endif
/*
* All our supported compilers implement C++11 |enum Foo : T| syntax, so don't
* expose these macros. (This macro exists *only* because gcc bug 51242
* <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51242> makes bit-fields of
* typed enums trigger a warning that can't be turned off. Don't expose it
* beyond this file!)
*/
#undef JS_ENUM_HEADER
#undef JS_ENUM_FOOTER
#if defined(JS_NUNBOX32)
#define JSVAL_TYPE_TO_TAG(type) ((JSValueTag)(JSVAL_TAG_CLEAR | (type)))
#define JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET JSVAL_TAG_OBJECT
#define JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET JSVAL_TAG_INT32
#define JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET JSVAL_TAG_STRING
#elif defined(JS_PUNBOX64)
// This should only be used in toGCThing, see the 'Spectre mitigations' comment.
#define JSVAL_PAYLOAD_MASK_GCTHING 0x00007FFFFFFFFFFFLL
#define JSVAL_TAG_MASK 0xFFFF800000000000LL
#define JSVAL_TYPE_TO_TAG(type) ((JSValueTag)(JSVAL_TAG_MAX_DOUBLE | (type)))
#define JSVAL_TYPE_TO_SHIFTED_TAG(type) (((uint64_t)JSVAL_TYPE_TO_TAG(type)) << JSVAL_TAG_SHIFT)
#define JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET JSVAL_TAG_OBJECT
#define JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET JSVAL_TAG_INT32
#define JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET JSVAL_TAG_STRING
#define JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET JSVAL_SHIFTED_TAG_OBJECT
#define JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET JSVAL_SHIFTED_TAG_BOOLEAN
#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
// implement toObjectOrNull more efficiently.
#define JSVAL_OBJECT_OR_NULL_BIT (uint64_t(0x8) << JSVAL_TAG_SHIFT)
static_assert((JSVAL_SHIFTED_TAG_NULL ^ JSVAL_SHIFTED_TAG_OBJECT) == JSVAL_OBJECT_OR_NULL_BIT,
"JSVAL_OBJECT_OR_NULL_BIT must be consistent with object and null tags");
#endif /* JS_PUNBOX64 */
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,
/** used in debug builds to catch tracing errors */
JS_ARG_POISON,
/** an empty subnode in the AST serializer */
JS_SERIALIZE_NO_NODE,
/** optimized-away 'arguments' value */
JS_OPTIMIZED_ARGUMENTS,
/** magic value passed to natives to indicate construction */
JS_IS_CONSTRUCTING,
/** 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,
/** standard constructors are not created for off-thread parsing. */
JS_OFF_THREAD_CONSTRUCTOR,
/** for local use */
JS_GENERIC_MAGIC,
JS_WHY_MAGIC_COUNT
};
namespace js {
static inline JS::Value PoisonedObjectValue(uintptr_t poison);
} // namespace js
namespace JS {
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);
}
static inline double
CanonicalizeNaN(double d)
{
if (MOZ_UNLIKELY(mozilla::IsNaN(d)))
return GenericNaN();
return d;
}
/**
* 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.
*
* - 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&.
*
* Spectre mitigations
* ===================
* To mitigate Spectre attacks, we do the following:
*
* - On 64-bit platforms, when unboxing a Value, we XOR the bits with the
* expected type tag (instead of masking the payload bits). This guarantees
* 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
* tag.
*
* - 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
* doesn't match.
*/
union MOZ_NON_PARAM alignas(8) Value
{
#if !defined(_MSC_VER) && !defined(__sparc)
// Don't expose Value's fields unless we have to: MSVC (bug 689101) and SPARC
// (bug 737344) require Value be POD to pass it by value and not in memory.
// More precisely, we don't want Value return values compiled as outparams.
private:
#endif
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_;
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:
#if defined(JS_NUNBOX32)
using PayloadType = uint32_t;
#elif defined(JS_PUNBOX64)
using PayloadType = uint64_t;
#endif
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);
}
public:
/**
* 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() {
asBits_ = bitsFromTagAndPayload(JSVAL_TAG_NULL, 0);
}
void setUndefined() {
asBits_ = bitsFromTagAndPayload(JSVAL_TAG_UNDEFINED, 0);
}
void setInt32(int32_t i) {
asBits_ = bitsFromTagAndPayload(JSVAL_TAG_INT32, uint32_t(i));
}
void setDouble(double d) {
// Don't assign to asDouble_ to fix a miscompilation with GCC 5.2.1 and
// 5.3.1. See bug 1312488.
*this = Value(d);
MOZ_ASSERT(isDouble());
}
void setNaN() {
setDouble(GenericNaN());
}
void setString(JSString* str) {
MOZ_ASSERT(js::gc::IsCellPointerValid(str));
asBits_ = bitsFromTagAndPayload(JSVAL_TAG_STRING, PayloadType(str));
}
void setSymbol(JS::Symbol* sym) {
MOZ_ASSERT(js::gc::IsCellPointerValid(sym));
asBits_ = bitsFromTagAndPayload(JSVAL_TAG_SYMBOL, PayloadType(sym));
}
void setObject(JSObject& 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)
// 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) {
asBits_ = bitsFromTagAndPayload(JSVAL_TAG_OBJECT, PayloadType(obj));
}
friend inline Value js::PoisonedObjectValue(uintptr_t poison);
public:
void setBoolean(bool b) {
asBits_ = bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN, uint32_t(b));
}
void setMagic(JSWhyMagic why) {
asBits_ = bitsFromTagAndPayload(JSVAL_TAG_MAGIC, uint32_t(why));
}
void setMagicUint32(uint32_t payload) {
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.asBits_;
rhs.asBits_ = asBits_;
asBits_ = tmp;
}
private:
JSValueTag toTag() const {
#if defined(JS_NUNBOX32)
return s_.tag_;
#elif defined(JS_PUNBOX64)
return JSValueTag(asBits_ >> JSVAL_TAG_SHIFT);
#endif
}
public:
/*** JIT-only interfaces to interact with and create raw Values ***/
#if defined(JS_NUNBOX32)
PayloadType toNunboxPayload() const {
return static_cast<PayloadType>(s_.payload_.i32_);
}
JSValueTag toNunboxTag() const {
return s_.tag_;
}
#elif defined(JS_PUNBOX64)
const void* bitsAsPunboxPointer() const {
return reinterpret_cast<void*>(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 asBits_ == JSVAL_SHIFTED_TAG_UNDEFINED;
#endif
}
bool isNull() const {
#if defined(JS_NUNBOX32)
return toTag() == JSVAL_TAG_NULL;
#elif defined(JS_PUNBOX64)
return 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 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 (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 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((asBits_ >> JSVAL_TAG_SHIFT) <= JSVAL_TAG_OBJECT);
return 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 asBits_ < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET;
#endif
}
bool isObjectOrNull() const {
return isObject() || isNull();
}
bool isGCThing() const {
#if defined(JS_NUNBOX32)
/* gcc sometimes generates signed < without explicit casts. */
return uint32_t(toTag()) >= uint32_t(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET);
#elif defined(JS_PUNBOX64)
return asBits_ >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET;
#endif
}
bool isBoolean() const {
return toTag() == JSVAL_TAG_BOOLEAN;
}
bool isTrue() const {
return asBits_ == bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN, uint32_t(true));
}
bool isFalse() const {
return 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(), s_.payload_.why_ == why);
return isMagic();
}
JS::TraceKind traceKind() const {
MOZ_ASSERT(isGCThing());
static_assert((JSVAL_TAG_STRING & 0x03) == size_t(JS::TraceKind::String),
"Value type tags must correspond with JS::TraceKinds.");
static_assert((JSVAL_TAG_SYMBOL & 0x03) == size_t(JS::TraceKind::Symbol),
"Value type tags must correspond with JS::TraceKinds.");
static_assert((JSVAL_TAG_OBJECT & 0x03) == size_t(JS::TraceKind::Object),
"Value type tags must correspond with JS::TraceKinds.");
if (MOZ_UNLIKELY(isPrivateGCThing()))
return JS::GCThingTraceKind(toGCThing());
return JS::TraceKind(toTag() & 0x03);
}
JSWhyMagic whyMagic() const {
MOZ_ASSERT(isMagic());
return s_.payload_.why_;
}
uint32_t magicUint32() const {
MOZ_ASSERT(isMagic());
return s_.payload_.u32_;
}
/*** Comparison ***/
bool operator==(const Value& rhs) const {
return asBits_ == rhs.asBits_;
}
bool operator!=(const Value& rhs) const {
return asBits_ != rhs.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 s_.payload_.i32_;
#elif defined(JS_PUNBOX64)
return int32_t(asBits_);
#endif
}
double toDouble() const {
MOZ_ASSERT(isDouble());
return asDouble_;
}
double toNumber() const {
MOZ_ASSERT(isNumber());
return isDouble() ? toDouble() : double(toInt32());
}
JSString* toString() const {
MOZ_ASSERT(isString());
#if defined(JS_NUNBOX32)
return s_.payload_.str_;
#elif defined(JS_PUNBOX64)
return reinterpret_cast<JSString*>(asBits_ ^ JSVAL_SHIFTED_TAG_STRING);
#endif
}
JS::Symbol* toSymbol() const {
MOZ_ASSERT(isSymbol());
#if defined(JS_NUNBOX32)
return s_.payload_.sym_;
#elif defined(JS_PUNBOX64)
return reinterpret_cast<JS::Symbol*>(asBits_ ^ JSVAL_SHIFTED_TAG_SYMBOL);
#endif
}
JSObject& toObject() const {
MOZ_ASSERT(isObject());
#if defined(JS_NUNBOX32)
return *s_.payload_.obj_;
#elif defined(JS_PUNBOX64)
uint64_t ptrBits = asBits_ ^ JSVAL_SHIFTED_TAG_OBJECT;
MOZ_ASSERT(ptrBits);
MOZ_ASSERT((ptrBits & 0x7) == 0);
return *reinterpret_cast<JSObject*>(ptrBits);
#endif
}
JSObject* toObjectOrNull() const {
MOZ_ASSERT(isObjectOrNull());
#if defined(JS_NUNBOX32)
return s_.payload_.obj_;
#elif defined(JS_PUNBOX64)
// Note: the 'Spectre mitigations' comment at the top of this class
// explains why we use XOR here and in other to* methods.
uint64_t ptrBits = (asBits_ ^ JSVAL_SHIFTED_TAG_OBJECT) & ~JSVAL_OBJECT_OR_NULL_BIT;
MOZ_ASSERT((ptrBits & 0x7) == 0);
return reinterpret_cast<JSObject*>(ptrBits);
#endif
}
js::gc::Cell* toGCThing() const {
MOZ_ASSERT(isGCThing());
#if defined(JS_NUNBOX32)
return s_.payload_.cell_;
#elif defined(JS_PUNBOX64)
uint64_t ptrBits = asBits_ & JSVAL_PAYLOAD_MASK_GCTHING;
MOZ_ASSERT((ptrBits & 0x7) == 0);
return reinterpret_cast<js::gc::Cell*>(ptrBits);
#endif
}
GCCellPtr toGCCellPtr() const {
return GCCellPtr(toGCThing(), traceKind());
}
bool toBoolean() const {
MOZ_ASSERT(isBoolean());
#if defined(JS_NUNBOX32)
return bool(s_.payload_.boo_);
#elif defined(JS_PUNBOX64)
return bool(int32_t(asBits_));
#endif
}
uint32_t payloadAsRawUint32() const {
MOZ_ASSERT(!isDouble());
return s_.payload_.u32_;
}
uint64_t asRawBits() const {
return 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)
s_.tag_ = JSValueTag(0);
s_.payload_.ptr_ = ptr;
#elif defined(JS_PUNBOX64)
asBits_ = uintptr_t(ptr) >> 1;
#endif
MOZ_ASSERT(isDouble());
}
void* toPrivate() const {
MOZ_ASSERT(isDouble());
#if defined(JS_NUNBOX32)
return s_.payload_.ptr_;
#elif defined(JS_PUNBOX64)
MOZ_ASSERT((asBits_ & 0x8000000000000000ULL) == 0);
return reinterpret_cast<void*>(asBits_ << 1);
#endif
}
void setPrivateUint32(uint32_t ui) {
MOZ_ASSERT(uint32_t(int32_t(ui)) == ui);
setInt32(int32_t(ui));
}
uint32_t toPrivateUint32() const {
return uint32_t(toInt32());
}
/*
* Private GC Thing API
*
* Non-JSObject, JSString, and JS::Symbol cells may be put into the 64-bit
* payload as private GC things. Such Values are considered isGCThing(), and
* as such, automatically marked. Their traceKind() is gotten via their
* cells.
*/
void setPrivateGCThing(js::gc::Cell* cell) {
MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::String,
"Private GC thing Values must not be strings. Make a StringValue instead.");
MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Symbol,
"Private GC thing Values must not be symbols. Make a SymbolValue instead.");
MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Object,
"Private GC thing Values must not be objects. Make an ObjectValue instead.");
MOZ_ASSERT(js::gc::IsCellPointerValid(cell));
#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
asBits_ = bitsFromTagAndPayload(JSVAL_TAG_PRIVATE_GCTHING, PayloadType(cell));
}
bool isPrivateGCThing() const {
return toTag() == JSVAL_TAG_PRIVATE_GCTHING;
}
} JS_HAZ_GC_POINTER;
static_assert(sizeof(Value) == 8,
"Value size must leave three tag bits, be a binary power, and "
"is ubiquitously depended upon everywhere");
inline bool
IsOptimizedPlaceholderMagicValue(const Value& v)
{
if (v.isMagic()) {
MOZ_ASSERT(v.whyMagic() == JS_OPTIMIZED_ARGUMENTS || v.whyMagic() == JS_OPTIMIZED_OUT);
return true;
}
return false;
}
static MOZ_ALWAYS_INLINE void
ExposeValueToActiveJS(const Value& v)
{
#ifdef DEBUG
Value tmp = v;
MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarrieredSlow(&tmp));
#endif
if (v.isGCThing())
js::gc::ExposeGCThingToActiveJS(GCCellPtr(v));
}
/************************************************************************/
static inline MOZ_MAY_CALL_AFTER_MUST_RETURN Value
NullValue()
{
Value v;
v.setNull();
return v;
}
static inline constexpr Value
UndefinedValue()
{
return Value();
}
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
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;
}
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.asBits_ ^ rhs.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());
}
static bool isValid(const Value& value) {
return !value.isGCThing() || js::gc::IsCellPointerValid(value.toGCThing());
}
};
} // namespace JS
namespace js {
template <>
struct BarrierMethods<JS::Value>
{
static gc::Cell* asGCThingOrNull(const JS::Value& v) {
return v.isGCThing() ? v.toGCThing() : nullptr;
}
static void postBarrier(JS::Value* v, const JS::Value& prev, const JS::Value& next) {
JS::HeapValuePostBarrier(v, prev, next);
}
static void exposeToJS(const JS::Value& v) {
JS::ExposeValueToActiveJS(v);
}
};
template <class Wrapper> class MutableValueOperations;
/**
* A class designed for CRTP use in implementing the non-mutating parts of the
* Value interface in Value-like classes. Wrapper must be a class inheriting
* ValueOperations<Wrapper> with a visible get() method returning a const
* reference to the Value abstracted by Wrapper.
*/
template <class Wrapper>
class WrappedPtrOperations<JS::Value, Wrapper>
{
const JS::Value& value() const { return static_cast<const Wrapper*>(this)->get(); }
public:
bool isUndefined() const { return value().isUndefined(); }
bool isNull() const { return value().isNull(); }
bool isBoolean() const { return value().isBoolean(); }
bool isTrue() const { return value().isTrue(); }
bool isFalse() const { return value().isFalse(); }
bool isNumber() const { return value().isNumber(); }
bool isInt32() const { return value().isInt32(); }
bool isInt32(int32_t i32) const { return value().isInt32(i32); }
bool isDouble() const { return value().isDouble(); }
bool isString() const { return value().isString(); }
bool isSymbol() const { return value().isSymbol(); }
bool isObject() const { return value().isObject(); }
bool isMagic() const { return value().isMagic(); }
bool isMagic(JSWhyMagic why) const { return value().isMagic(why); }
bool isGCThing() const { return value().isGCThing(); }
bool isPrimitive() const { return value().isPrimitive(); }
bool isNullOrUndefined() const { return value().isNullOrUndefined(); }
bool isObjectOrNull() const { return value().isObjectOrNull(); }
bool toBoolean() const { return value().toBoolean(); }
double toNumber() const { return value().toNumber(); }
int32_t toInt32() const { return value().toInt32(); }
double toDouble() const { return value().toDouble(); }
JSString* toString() const { return value().toString(); }
JS::Symbol* toSymbol() const { return value().toSymbol(); }
JSObject& toObject() const { return value().toObject(); }
JSObject* toObjectOrNull() const { return value().toObjectOrNull(); }
gc::Cell* toGCThing() const { return value().toGCThing(); }
JS::TraceKind traceKind() const { return value().traceKind(); }
void* toPrivate() const { return value().toPrivate(); }
uint32_t toPrivateUint32() const { return value().toPrivateUint32(); }
uint64_t asRawBits() const { return value().asRawBits(); }
JSValueType extractNonDoubleType() const { return value().extractNonDoubleType(); }
JSWhyMagic whyMagic() const { return value().whyMagic(); }
uint32_t magicUint32() const { return value().magicUint32(); }
};
/**
* A class designed for CRTP use in implementing all the mutating parts of the
* Value interface in Value-like classes. Wrapper must be a class inheriting
* MutableWrappedPtrOperations<Wrapper> with visible get() methods returning const and
* non-const references to the Value abstracted by Wrapper.
*/
template <class Wrapper>
class MutableWrappedPtrOperations<JS::Value, Wrapper> : public WrappedPtrOperations<JS::Value, Wrapper>
{
JS::Value& value() { return static_cast<Wrapper*>(this)->get(); }
public:
void setNull() { value().setNull(); }
void setUndefined() { value().setUndefined(); }
void setInt32(int32_t i) { value().setInt32(i); }
void setDouble(double d) { value().setDouble(d); }
void setNaN() { setDouble(JS::GenericNaN()); }
void setBoolean(bool b) { value().setBoolean(b); }
void setMagic(JSWhyMagic why) { value().setMagic(why); }
bool setNumber(uint32_t ui) { return value().setNumber(ui); }
bool setNumber(double d) { return value().setNumber(d); }
void setString(JSString* str) { this->value().setString(str); }
void setSymbol(JS::Symbol* sym) { this->value().setSymbol(sym); }
void setObject(JSObject& obj) { this->value().setObject(obj); }
void setObjectOrNull(JSObject* arg) { this->value().setObjectOrNull(arg); }
void setPrivate(void* ptr) { this->value().setPrivate(ptr); }
void setPrivateUint32(uint32_t ui) { this->value().setPrivateUint32(ui); }
void setPrivateGCThing(js::gc::Cell* cell) { this->value().setPrivateGCThing(cell); }
};
/*
* Augment the generic Heap<T> interface when T = Value with
* type-querying, value-extracting, and mutating operations.
*/
template <typename Wrapper>
class HeapBase<JS::Value, Wrapper> : public WrappedPtrOperations<JS::Value, Wrapper>
{
void setBarriered(const JS::Value& v) {
*static_cast<JS::Heap<JS::Value>*>(this) = v;
}
public:
void setNull() { setBarriered(JS::NullValue()); }
void setUndefined() { setBarriered(JS::UndefinedValue()); }
void setInt32(int32_t i) { setBarriered(JS::Int32Value(i)); }
void setDouble(double d) { setBarriered(JS::DoubleValue(d)); }
void setNaN() { setDouble(JS::GenericNaN()); }
void setBoolean(bool b) { setBarriered(JS::BooleanValue(b)); }
void setMagic(JSWhyMagic why) { setBarriered(JS::MagicValue(why)); }
void setString(JSString* str) { setBarriered(JS::StringValue(str)); }
void setSymbol(JS::Symbol* sym) { setBarriered(JS::SymbolValue(sym)); }
void setObject(JSObject& obj) { setBarriered(JS::ObjectValue(obj)); }
void setPrivateGCThing(js::gc::Cell* cell) { setBarriered(JS::PrivateGCThingValue(cell)); }
bool setNumber(uint32_t ui) {
if (ui > JSVAL_INT_MAX) {
setDouble((double)ui);
return false;
} else {
setInt32((int32_t)ui);
return true;
}
}
bool setNumber(double d) {
int32_t i;
if (mozilla::NumberIsInt32(d, &i)) {
setInt32(i);
return true;
}
setDouble(d);
return false;
}
void setObjectOrNull(JSObject* arg) {
if (arg)
setObject(*arg);
else
setNull();
}
};
/*
* If the Value is a GC pointer type, convert to that type and call |f| with
* the pointer. If the Value is not a GC type, calls F::defaultValue.
*/
template <typename F, typename... Args>
auto
DispatchTyped(F f, const JS::Value& val, Args&&... args)
-> decltype(f(static_cast<JSObject*>(nullptr), mozilla::Forward<Args>(args)...))
{
if (val.isString()) {
JSString* str = val.toString();
MOZ_ASSERT(gc::IsCellPointerValid(str));
return f(str, mozilla::Forward<Args>(args)...);
}
if (val.isObject()) {
JSObject* obj = &val.toObject();
MOZ_ASSERT(gc::IsCellPointerValid(obj));
return f(obj, mozilla::Forward<Args>(args)...);
}
if (val.isSymbol()) {
JS::Symbol* sym = val.toSymbol();
MOZ_ASSERT(gc::IsCellPointerValid(sym));
return f(sym, mozilla::Forward<Args>(args)...);
}
if (MOZ_UNLIKELY(val.isPrivateGCThing())) {
MOZ_ASSERT(gc::IsCellPointerValid(val.toGCThing()));
return DispatchTyped(f, val.toGCCellPtr(), mozilla::Forward<Args>(args)...);
}
MOZ_ASSERT(!val.isGCThing());
return F::defaultValue(val);
}
template <class S> struct VoidDefaultAdaptor { static void defaultValue(const S&) {} };
template <class S> struct IdentityDefaultAdaptor { static S defaultValue(const S& v) {return v;} };
template <class S, bool v> struct BoolDefaultAdaptor { static bool defaultValue(const S&) { return v; } };
static inline JS::Value
PoisonedObjectValue(uintptr_t poison)
{
JS::Value v;
v.setObjectNoCheck(reinterpret_cast<JSObject*>(poison));
return v;
}
} // namespace js
#ifdef DEBUG
namespace JS {
MOZ_ALWAYS_INLINE bool
ValueIsNotGray(const Value& value)
{
if (!value.isGCThing())
return true;
return CellIsNotGray(value.toGCThing());
}
MOZ_ALWAYS_INLINE bool
ValueIsNotGray(const Heap<Value>& value)
{
return ValueIsNotGray(value.unbarrieredGet());
}
} // namespace JS
#endif
/************************************************************************/
namespace JS {
extern JS_PUBLIC_DATA(const HandleValue) NullHandleValue;
extern JS_PUBLIC_DATA(const HandleValue) UndefinedHandleValue;
extern JS_PUBLIC_DATA(const HandleValue) TrueHandleValue;
extern JS_PUBLIC_DATA(const HandleValue) FalseHandleValue;
} // namespace JS
#endif /* js_Value_h */