gecko-dev/js/public/Value.h
Jon Coppeard b425876903 Bug 1306008 - Give JS::Heap<T> pointer wrappers a pre-barrier r=sfink
This adds a pre write barrier to Heap<T> so that these can be uses as non-roots in the heap without breaking our snapshot at the beginning invariant if they are written to during an incremental GC. This makes it harder to misuse and allows us to take out manual barriers in at least one place.

Differential Revision: https://phabricator.services.mozilla.com/D25083
2019-03-27 16:26:09 +00:00

1402 lines
41 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* 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 "mozilla/Maybe.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_BIGINT = 0x09,
JSVAL_TYPE_OBJECT = 0x0c,
// This type never appears in a Value; it's only an out-of-band value.
JSVAL_TYPE_UNKNOWN = 0x20
};
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,
"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_BIGINT = JSVAL_TAG_CLEAR | JSVAL_TYPE_BIGINT,
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_BIGINT = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BIGINT,
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_BIGINT = (((uint64_t)JSVAL_TAG_BIGINT) << 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,
/** used in jit::TrySkipAwait */
JS_CANNOT_SKIP_AWAIT,
/** 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;
}
/**
* [SMDOC] JS::Value type
*
* 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, BigInt, 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 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:
#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 setBigInt(JS::BigInt* bi) {
MOZ_ASSERT(js::gc::IsCellPointerValid(bi));
asBits_ = bitsFromTagAndPayload(JSVAL_TAG_BIGINT, PayloadType(bi));
}
void setObject(JSObject& obj) {
MOZ_ASSERT(js::gc::IsCellPointerValid(&obj));
#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::FloatingPoint<double>::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 isBigInt() const { return toTag() == JSVAL_TAG_BIGINT; }
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 {
if (!isMagic()) {
return false;
}
MOZ_RELEASE_ASSERT(s_.payload_.why_ == why);
return true;
}
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());
}
if (MOZ_UNLIKELY(isBigInt())) {
return JS::TraceKind::BigInt;
}
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
}
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
}
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);
}
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 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::BigInt,
"Private GC thing Values must not be BigInts. Make a "
"BigIntValue 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 MOZ_NON_PARAM;
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::FloatingPoint<double>::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 BigIntValue(JS::BigInt* bi) {
Value v;
v.setBigInt(bi);
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 HeapValueWriteBarriers(Value* valuep, const Value& prev,
const Value& next);
template <>
struct GCPolicy<JS::Value> {
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 writeBarriers(JS::Value* v, const JS::Value& prev,
const JS::Value& next) {
JS::HeapValueWriteBarriers(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 isBigInt() const { return value().isBigInt(); }
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(); }
JS::BigInt* toBigInt() const { return value().toBigInt(); }
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();
}
JS::ValueType type() const { return value().type(); }
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 setBigInt(JS::BigInt* bi) { this->value().setBigInt(bi); }
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 setBigInt(JS::BigInt* bi) { setBarriered(JS::BigIntValue(bi)); }
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, call |f| with the pointer cast to that
// type and return the result wrapped in a Maybe, otherwise return None().
template <typename F>
auto MapGCThingTyped(const JS::Value& val, F&& f) {
switch (val.type()) {
case JS::ValueType::String: {
JSString* str = val.toString();
MOZ_ASSERT(gc::IsCellPointerValid(str));
return mozilla::Some(f(str));
}
case JS::ValueType::Object: {
JSObject* obj = &val.toObject();
MOZ_ASSERT(gc::IsCellPointerValid(obj));
return mozilla::Some(f(obj));
}
case JS::ValueType::Symbol: {
JS::Symbol* sym = val.toSymbol();
MOZ_ASSERT(gc::IsCellPointerValid(sym));
return mozilla::Some(f(sym));
}
case JS::ValueType::BigInt: {
JS::BigInt* bi = val.toBigInt();
MOZ_ASSERT(gc::IsCellPointerValid(bi));
return mozilla::Some(f(bi));
}
case JS::ValueType::PrivateGCThing: {
MOZ_ASSERT(gc::IsCellPointerValid(val.toGCThing()));
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());
using ReturnType = decltype(f(static_cast<JSObject*>(nullptr)));
return mozilla::Maybe<ReturnType>();
}
}
MOZ_CRASH("no missing return");
}
// If the Value is a GC pointer type, call |f| with the pointer cast to that
// type. Return whether this happened.
template <typename F>
bool ApplyGCThingTyped(const JS::Value& val, F&& f) {
return MapGCThingTyped(val,
[&f](auto t) {
f(t);
return true;
})
.isSome();
}
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 void AssertValueIsNotGray(const Value& value) {
if (value.isGCThing()) {
AssertCellIsNotGray(value.toGCThing());
}
}
MOZ_ALWAYS_INLINE void AssertValueIsNotGray(const Heap<Value>& value) {
AssertValueIsNotGray(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 */