1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* JS::Value implementation. */
12 #include "mozilla/Attributes.h"
13 #include "mozilla/Casting.h"
14 #include "mozilla/FloatingPoint.h"
15 #include "mozilla/Likely.h"
16 #include "mozilla/Maybe.h"
18 #include <limits> /* for std::numeric_limits */
19 #include <type_traits>
23 #include "js/HeapAPI.h"
24 #include "js/RootingAPI.h"
25 #include "js/TypeDecls.h"
28 class JS_PUBLIC_API Value
;
31 // [SMDOC] JS::Value Boxing Formats
33 // JS::Value is a 64-bit value, on all architectures. It is conceptually a
34 // discriminated union of all the types of values that can be represented in SM:
36 // - 64 bit IEEE 754 floats
37 // - 32-bit integer values
38 // - and quite a few more (see JSValueType)
40 // The ECMAScript standard specifies that ECMAScript numbers are IEEE 64-bit
41 // floating-point values. A JS::Value can represent any JavaScript number
42 // value directly, without referring to additional storage, or represent an
43 // object, string, or other ECMAScript value, and remember which type it is.
45 // This may seem surprising: how can a 64-bit type hold all the 64-bit IEEE
46 // values, and still distinguish them from objects, strings, and so on,
47 // which have 64-bit addresses ?
49 // This is possible for two reasons:
51 // - First, ECMAScript implementations aren't required to distinguish all
52 // the values the IEEE 64-bit format can represent.
54 // The IEEE 754 format for floating point numbers specifies that every
55 // floating-point value whose 11-bit exponent field is all ones, and whose
56 // 52-bit fraction field is non-zero, has the value NaN. EMCAScript requires
57 // only one NaN value. This means we can use one IEEE NaN to represent
58 // ECMAScript's NaN, and use all the other 2^52-2 NaN bitstrings to
59 // represent the other ECMAScript values.
61 // - Second, on the 64 bit architectures we suppport, only the
62 // lower 48 bits of an address are currently significant. The upper sixteen
63 // bits are required to be the sign-extension of bit 48. Furthermore, user
64 // code always runs in "positive addresses": those in which bit 48 is zero. So
65 // we only actually need 47 bits to store all possible object or string
66 // addresses, even on 64-bit platforms.
68 // Our memory initialization system ensures that all pointers we will store in
69 // objects use only 47 bits. See js::gc::MapAlignedPagesRandom.
71 // The introduction of 5-level page tables, supporting 57-bit virtual
72 // addresses, is a potential complication. For now, large addresses are
73 // opt-in, and we simply don't use them.
75 // With a 52-bit fraction field, and 47 bits needed for the 'payload', we
76 // have up to five bits left to store a 'tag' value, to indicate which
77 // branch of our discriminated union is live. (In practice, one of those
78 // bits is used up to simplify NaN representation; see micro-optimization 5
81 // Thus, we define JS::Value representations in terms of the IEEE 64-bit
82 // floating-point format:
84 // - Any bitstring that IEEE calls a number or an infinity represents that
87 // - Any bitstring that IEEE calls a NaN represents either an ECMAScript NaN
88 // or a non-number ECMAScript value, as determined by a tag field stored
89 // towards the most significant end of the fraction field (exactly where
90 // depends on the address size). If the tag field indicates that this
91 // JS::Value is an object, the fraction field's least significant end
92 // holds the address of a JSObject; if a string, the address of a
93 // JSString; and so on.
95 // To enforce this invariant, anywhere that may provide a numerical value
96 // which may have a non-canonical NaN value (NaN, but not the one we've chosen
97 // for ECMAScript) we must convert that to the canonical NaN. See
98 // JS::CanonicalizeNaN.
100 // We have two boxing modes defined: NUNBOX32 and PUNBOX64.The first is
101 // "NaN unboxed boxing" (or Nunboxing), as non-Number payload are stored
102 // unaltered in the lower bits. The second is "Packed NaN boxing" (or
103 // punboxing), which is 'logically like nunboxing, but with all the unused bits
104 // sucked out' [1], as we rely on unused bits of the payload to pack the
105 // payload in the lower bits using Nunboxing.
107 // - In NUNBOX32 the tag is stored in the least-significant bits of the high
108 // word of the NaN. Since it's used on 32-bit systems, this has the nice
109 // property that boxed values are simply stored in the low-word of the 8-byte
112 // - In PUNBOX64, since we need to store more pointer bits (47, see above), the
113 // tag is stored in the 5 most significant bits of the fraction adjacent to
116 // Tag values are carefully ordered to support a set of micro-optimizations. In
119 // 1. Object is the highest tag, to simplify isPrimitive checks. (See
120 // ValueUpperExclPrimitiveTag)
121 // 2. Numbers (Double and Int32) are the lowest tags, to simplify isNumber
122 // checks. (See ValueUpperInclNumberTag)
123 // 3. Non-GC tags are ordered before GC-tags, to simplify isGCThing checks. (See
124 // ValueLowerInclGCThingTag)
125 // 4. The tags for Object and Null differ by a single flipped bit, to simplify
126 // toObjectOrNull. (See ValueObjectOrNullBit)
127 // 5. In PUNBOX64, the most significant bit of every non-Double tag is always
128 // set. This is to simplify isDouble checks. Note that the highest bitstring
129 // that corresponds to a non-NaN double is -Infinity:
130 // 0xfff0_0000_0000_0000
131 // But the canonical hardware NaN (produced by, for example, 0/0) is:
132 // 0x?ff8_0000_0000_0000
133 // on all platforms with JIT support*. (The most significant bit is the sign
134 // bit; it is 1 on x86, but 0 on ARM.) The most significant bit of the
135 // fraction field is set, which corresponds to the most significant of the 5
136 // tag bits. Because we only use tags that have the high bit set, any Value
137 // represented by a bitstring less than or equal to 0xfff8_..._0000 is a
138 // Double. (If we wanted to use all five bits, we could define 0x10 as
139 // JSVAL_TYPE_NAN, and mask off the most significant bit of the tag for
140 // IsDouble checks. This is not yet necessary, because we still have room
141 // left to allocate new tags.)
143 // * But see JS_NONCANONICAL_HARDWARE_NAN below.
146 // https://wingolog.org/archives/2011/05/18/value-representation-in-javascript-implementations#969f63bbe4eb912778c9da85feb0f5763e7a7862
148 /* JS::Value can store a full int32_t. */
149 #define JSVAL_INT_BITS 32
150 #define JSVAL_INT_MIN ((int32_t)0x80000000)
151 #define JSVAL_INT_MAX ((int32_t)0x7fffffff)
153 #if defined(JS_NUNBOX32)
154 # define JSVAL_TAG_SHIFT 32
155 #elif defined(JS_PUNBOX64)
156 # define JSVAL_TAG_SHIFT 47
159 // Use enums so that printing a JS::Value in the debugger shows nice
160 // symbolic type tags.
162 enum JSValueType
: uint8_t {
163 JSVAL_TYPE_DOUBLE
= 0x00,
164 JSVAL_TYPE_INT32
= 0x01,
165 JSVAL_TYPE_BOOLEAN
= 0x02,
166 JSVAL_TYPE_UNDEFINED
= 0x03,
167 JSVAL_TYPE_NULL
= 0x04,
168 JSVAL_TYPE_MAGIC
= 0x05,
169 JSVAL_TYPE_STRING
= 0x06,
170 JSVAL_TYPE_SYMBOL
= 0x07,
171 JSVAL_TYPE_PRIVATE_GCTHING
= 0x08,
172 JSVAL_TYPE_BIGINT
= 0x09,
173 #ifdef ENABLE_RECORD_TUPLE
174 JSVAL_TYPE_EXTENDED_PRIMITIVE
= 0x0b,
176 JSVAL_TYPE_OBJECT
= 0x0c,
178 // This type never appears in a Value; it's only an out-of-band value.
179 JSVAL_TYPE_UNKNOWN
= 0x20
183 enum class ValueType
: uint8_t {
184 Double
= JSVAL_TYPE_DOUBLE
,
185 Int32
= JSVAL_TYPE_INT32
,
186 Boolean
= JSVAL_TYPE_BOOLEAN
,
187 Undefined
= JSVAL_TYPE_UNDEFINED
,
188 Null
= JSVAL_TYPE_NULL
,
189 Magic
= JSVAL_TYPE_MAGIC
,
190 String
= JSVAL_TYPE_STRING
,
191 Symbol
= JSVAL_TYPE_SYMBOL
,
192 PrivateGCThing
= JSVAL_TYPE_PRIVATE_GCTHING
,
193 BigInt
= JSVAL_TYPE_BIGINT
,
194 #ifdef ENABLE_RECORD_TUPLE
195 ExtendedPrimitive
= JSVAL_TYPE_EXTENDED_PRIMITIVE
,
197 Object
= JSVAL_TYPE_OBJECT
,
201 static_assert(sizeof(JSValueType
) == 1,
202 "compiler typed enum support is apparently buggy");
204 #if defined(JS_NUNBOX32)
206 enum JSValueTag
: uint32_t {
207 JSVAL_TAG_CLEAR
= 0xFFFFFF80,
208 JSVAL_TAG_INT32
= JSVAL_TAG_CLEAR
| JSVAL_TYPE_INT32
,
209 JSVAL_TAG_UNDEFINED
= JSVAL_TAG_CLEAR
| JSVAL_TYPE_UNDEFINED
,
210 JSVAL_TAG_NULL
= JSVAL_TAG_CLEAR
| JSVAL_TYPE_NULL
,
211 JSVAL_TAG_BOOLEAN
= JSVAL_TAG_CLEAR
| JSVAL_TYPE_BOOLEAN
,
212 JSVAL_TAG_MAGIC
= JSVAL_TAG_CLEAR
| JSVAL_TYPE_MAGIC
,
213 JSVAL_TAG_STRING
= JSVAL_TAG_CLEAR
| JSVAL_TYPE_STRING
,
214 JSVAL_TAG_SYMBOL
= JSVAL_TAG_CLEAR
| JSVAL_TYPE_SYMBOL
,
215 JSVAL_TAG_PRIVATE_GCTHING
= JSVAL_TAG_CLEAR
| JSVAL_TYPE_PRIVATE_GCTHING
,
216 JSVAL_TAG_BIGINT
= JSVAL_TAG_CLEAR
| JSVAL_TYPE_BIGINT
,
217 # ifdef ENABLE_RECORD_TUPLE
218 JSVAL_TAG_EXTENDED_PRIMITIVE
=
219 JSVAL_TAG_CLEAR
| JSVAL_TYPE_EXTENDED_PRIMITIVE
,
221 JSVAL_TAG_OBJECT
= JSVAL_TAG_CLEAR
| JSVAL_TYPE_OBJECT
224 static_assert(sizeof(JSValueTag
) == sizeof(uint32_t),
225 "compiler typed enum support is apparently buggy");
227 #elif defined(JS_PUNBOX64)
229 enum JSValueTag
: uint32_t {
230 JSVAL_TAG_MAX_DOUBLE
= 0x1FFF0,
231 JSVAL_TAG_INT32
= JSVAL_TAG_MAX_DOUBLE
| JSVAL_TYPE_INT32
,
232 JSVAL_TAG_UNDEFINED
= JSVAL_TAG_MAX_DOUBLE
| JSVAL_TYPE_UNDEFINED
,
233 JSVAL_TAG_NULL
= JSVAL_TAG_MAX_DOUBLE
| JSVAL_TYPE_NULL
,
234 JSVAL_TAG_BOOLEAN
= JSVAL_TAG_MAX_DOUBLE
| JSVAL_TYPE_BOOLEAN
,
235 JSVAL_TAG_MAGIC
= JSVAL_TAG_MAX_DOUBLE
| JSVAL_TYPE_MAGIC
,
236 JSVAL_TAG_STRING
= JSVAL_TAG_MAX_DOUBLE
| JSVAL_TYPE_STRING
,
237 JSVAL_TAG_SYMBOL
= JSVAL_TAG_MAX_DOUBLE
| JSVAL_TYPE_SYMBOL
,
238 JSVAL_TAG_PRIVATE_GCTHING
= JSVAL_TAG_MAX_DOUBLE
| JSVAL_TYPE_PRIVATE_GCTHING
,
239 JSVAL_TAG_BIGINT
= JSVAL_TAG_MAX_DOUBLE
| JSVAL_TYPE_BIGINT
,
240 # ifdef ENABLE_RECORD_TUPLE
241 JSVAL_TAG_EXTENDED_PRIMITIVE
=
242 JSVAL_TAG_MAX_DOUBLE
| JSVAL_TYPE_EXTENDED_PRIMITIVE
,
244 JSVAL_TAG_OBJECT
= JSVAL_TAG_MAX_DOUBLE
| JSVAL_TYPE_OBJECT
247 static_assert(sizeof(JSValueTag
) == sizeof(uint32_t),
248 "compiler typed enum support is apparently buggy");
250 enum JSValueShiftedTag
: uint64_t {
251 // See Bug 584653 for why we include 0xFFFFFFFF.
252 JSVAL_SHIFTED_TAG_MAX_DOUBLE
=
253 ((uint64_t(JSVAL_TAG_MAX_DOUBLE
) << JSVAL_TAG_SHIFT
) | 0xFFFFFFFF),
254 JSVAL_SHIFTED_TAG_INT32
= (uint64_t(JSVAL_TAG_INT32
) << JSVAL_TAG_SHIFT
),
255 JSVAL_SHIFTED_TAG_UNDEFINED
=
256 (uint64_t(JSVAL_TAG_UNDEFINED
) << JSVAL_TAG_SHIFT
),
257 JSVAL_SHIFTED_TAG_NULL
= (uint64_t(JSVAL_TAG_NULL
) << JSVAL_TAG_SHIFT
),
258 JSVAL_SHIFTED_TAG_BOOLEAN
= (uint64_t(JSVAL_TAG_BOOLEAN
) << JSVAL_TAG_SHIFT
),
259 JSVAL_SHIFTED_TAG_MAGIC
= (uint64_t(JSVAL_TAG_MAGIC
) << JSVAL_TAG_SHIFT
),
260 JSVAL_SHIFTED_TAG_STRING
= (uint64_t(JSVAL_TAG_STRING
) << JSVAL_TAG_SHIFT
),
261 JSVAL_SHIFTED_TAG_SYMBOL
= (uint64_t(JSVAL_TAG_SYMBOL
) << JSVAL_TAG_SHIFT
),
262 JSVAL_SHIFTED_TAG_PRIVATE_GCTHING
=
263 (uint64_t(JSVAL_TAG_PRIVATE_GCTHING
) << JSVAL_TAG_SHIFT
),
264 JSVAL_SHIFTED_TAG_BIGINT
= (uint64_t(JSVAL_TAG_BIGINT
) << JSVAL_TAG_SHIFT
),
265 # ifdef ENABLE_RECORD_TUPLE
266 JSVAL_SHIFTED_TAG_EXTENDED_PRIMITIVE
=
267 (uint64_t(JSVAL_TYPE_EXTENDED_PRIMITIVE
) << JSVAL_TAG_SHIFT
),
269 JSVAL_SHIFTED_TAG_OBJECT
= (uint64_t(JSVAL_TAG_OBJECT
) << JSVAL_TAG_SHIFT
)
272 static_assert(sizeof(JSValueShiftedTag
) == sizeof(uint64_t),
273 "compiler typed enum support is apparently buggy");
280 #if defined(JS_NUNBOX32)
282 constexpr JSValueTag
ValueTypeToTag(JSValueType type
) {
283 return static_cast<JSValueTag
>(JSVAL_TAG_CLEAR
|
284 std::underlying_type_t
<JSValueType
>(type
));
287 constexpr bool ValueIsDouble(uint64_t bits
) {
288 return uint32_t(bits
>> JSVAL_TAG_SHIFT
) <= uint32_t(JSVAL_TAG_CLEAR
);
291 constexpr JSValueTag ValueUpperExclPrimitiveTag
= JSVAL_TAG_OBJECT
;
292 constexpr JSValueTag ValueUpperInclNumberTag
= JSVAL_TAG_INT32
;
293 constexpr JSValueTag ValueLowerInclGCThingTag
= JSVAL_TAG_STRING
;
295 #elif defined(JS_PUNBOX64)
297 constexpr JSValueTag
ValueTypeToTag(JSValueType type
) {
298 return static_cast<JSValueTag
>(JSVAL_TAG_MAX_DOUBLE
|
299 std::underlying_type_t
<JSValueType
>(type
));
302 constexpr bool ValueIsDouble(uint64_t bits
) {
303 return bits
<= JSVAL_SHIFTED_TAG_MAX_DOUBLE
;
306 constexpr uint64_t ValueTagMask
= 0xFFFF'8000'0000'0000;
308 // This should only be used in toGCThing. See the 'Spectre mitigations' comment.
309 constexpr uint64_t ValueGCThingPayloadMask
= 0x0000'7FFF'FFFF'FFFF;
311 // Mask used to combine an unbox operation with getting the chunk base.
312 constexpr uint64_t ValueGCThingPayloadChunkMask
=
313 ValueGCThingPayloadMask
& ~js::gc::ChunkMask
;
315 constexpr uint64_t ValueTypeToShiftedTag(JSValueType type
) {
316 return static_cast<uint64_t>(ValueTypeToTag(type
)) << JSVAL_TAG_SHIFT
;
318 # define JSVAL_TYPE_TO_SHIFTED_TAG(type) \
319 (JS::detail::ValueTypeToShiftedTag(type))
321 constexpr JSValueTag ValueUpperExclPrimitiveTag
= JSVAL_TAG_OBJECT
;
322 constexpr JSValueTag ValueUpperInclNumberTag
= JSVAL_TAG_INT32
;
323 constexpr JSValueTag ValueLowerInclGCThingTag
= JSVAL_TAG_STRING
;
325 constexpr uint64_t ValueUpperExclShiftedPrimitiveTag
= JSVAL_SHIFTED_TAG_OBJECT
;
326 constexpr uint64_t ValueUpperExclShiftedNumberTag
= JSVAL_SHIFTED_TAG_BOOLEAN
;
327 constexpr uint64_t ValueLowerInclShiftedGCThingTag
= JSVAL_SHIFTED_TAG_STRING
;
329 // JSVAL_TYPE_OBJECT and JSVAL_TYPE_NULL differ by one bit. We can use this to
330 // implement toObjectOrNull more efficiently.
331 constexpr uint64_t ValueObjectOrNullBit
= 0x8ULL
<< JSVAL_TAG_SHIFT
;
333 (JSVAL_SHIFTED_TAG_NULL
^ JSVAL_SHIFTED_TAG_OBJECT
) == ValueObjectOrNullBit
,
334 "ValueObjectOrNullBit must be consistent with object and null tags");
336 constexpr uint64_t IsValidUserModePointer(uint64_t bits
) {
337 // All 64-bit platforms that we support actually have a 48-bit address space
338 // for user-mode pointers, with the top 16 bits all set to zero.
339 return (bits
& 0xFFFF'0000'0000'0000) == 0;
342 #endif /* JS_PUNBOX64 */
344 } // namespace detail
347 #define JSVAL_TYPE_TO_TAG(type) (JS::detail::ValueTypeToTag(type))
350 /** a hole in a native object's elements */
353 /** there is not a pending iterator value */
356 /** exception value thrown when closing a generator */
357 JS_GENERATOR_CLOSING
,
359 /** used in debug builds to catch tracing errors */
362 /** an empty subnode in the AST serializer */
363 JS_SERIALIZE_NO_NODE
,
365 /** magic value passed to natives to indicate construction */
368 /** see class js::HashableValue */
371 /** error while running Ion code */
374 /** missing recover instruction result */
377 /** optimized out slot */
380 /** uninitialized lexical bindings that produce ReferenceError on touch. */
381 JS_UNINITIALIZED_LEXICAL
,
383 /** arguments object can't be created because environment is dead. */
384 JS_MISSING_ARGUMENTS
,
386 /** exception value thrown when interrupting irregexp */
393 * When an error object is created without the error cause argument, we set
394 * the error's cause slot to this magic value.
396 JS_ERROR_WITHOUT_CAUSE
,
402 class JS_PUBLIC_API GenericPrinter
;
405 static inline JS::Value
PoisonedObjectValue(uintptr_t poison
);
406 #ifdef ENABLE_RECORD_TUPLE
407 // Re-defined in vm/RecordTupleBoxShared.h. We cannot include that
408 // file because it circularly includes this one.
409 bool IsExtendedPrimitive(const JSObject
& obj
);
411 bool MaybeForwardedIsExtendedPrimitive(const JSObject
& obj
);
420 // IEEE-754 bit pattern for double-precision positive infinity.
421 constexpr int InfinitySignBit
= 0;
422 constexpr uint64_t InfinityBits
=
423 mozilla::InfinityBits
<double, detail::InfinitySignBit
>::value
;
425 // This is a quiet NaN on IEEE-754[2008] compatible platforms, including X86,
426 // ARM, SPARC, RISC-V and modern MIPS.
428 // Note: The default sign bit for a hardware synthesized NaN differs between X86
429 // and ARM. Both values are considered compatible values on both
431 constexpr int CanonicalizedNaNSignBit
= 0;
432 constexpr uint64_t CanonicalizedNaNSignificand
= 0x8000000000000;
434 #if defined(__sparc__)
435 // Some architectures (not to name names) generate NaNs with bit patterns that
436 // are incompatible with JS::Value's bit pattern restrictions. Instead we must
437 // canonicalize all hardware values before storing in JS::Value.
438 # define JS_NONCANONICAL_HARDWARE_NAN
441 #if defined(__mips__) && !defined(__mips_nan_2008)
442 // These builds may run on hardware that has differing polarity of the signaling
443 // NaN bit. While the kernel may handle the trap for us, it is a performance
444 // issue so instead we compute the NaN to use on startup. The runtime value must
445 // still meet `ValueIsDouble` requirements which are checked on startup.
447 // In particular, we expect one of the following values on MIPS:
448 // - 0x7FF7FFFFFFFFFFFF Legacy
449 // - 0x7FF8000000000000 IEEE-754[2008]
450 # define JS_RUNTIME_CANONICAL_NAN
453 #if defined(JS_RUNTIME_CANONICAL_NAN)
454 extern uint64_t CanonicalizedNaNBits
;
456 constexpr uint64_t CanonicalizedNaNBits
=
457 mozilla::SpecificNaNBits
<double, detail::CanonicalizedNaNSignBit
,
458 detail::CanonicalizedNaNSignificand
>::value
;
460 } // namespace detail
462 // Return a quiet NaN that is compatible with JS::Value restrictions.
463 static MOZ_ALWAYS_INLINE
double GenericNaN() {
464 #if !defined(JS_RUNTIME_CANONICAL_NAN)
465 static_assert(detail::ValueIsDouble(detail::CanonicalizedNaNBits
),
466 "Canonical NaN must be compatible with JS::Value");
469 return mozilla::BitwiseCast
<double>(detail::CanonicalizedNaNBits
);
472 // Return the infinity the engine uses
473 static MOZ_ALWAYS_INLINE
double Infinity() {
474 return mozilla::BitwiseCast
<double>(detail::InfinityBits
);
477 // Convert an arbitrary double to one compatible with JS::Value representation
478 // by replacing any NaN value with a canonical one.
479 static MOZ_ALWAYS_INLINE
double CanonicalizeNaN(double d
) {
480 if (MOZ_UNLIKELY(std::isnan(d
))) {
487 * [SMDOC] JS::Value type
489 * JS::Value is the interface for a single JavaScript Engine value. A few
490 * general notes on JS::Value:
492 * - JS::Value has setX() and isX() members for X in
494 * { Int32, Double, String, Symbol, BigInt, Boolean, Undefined, Null,
497 * JS::Value also contains toX() for each of the non-singleton types.
499 * - Magic is a singleton type whose payload contains either a JSWhyMagic
500 * "reason" for the magic value or a uint32_t value. By providing JSWhyMagic
501 * values when creating and checking for magic values, it is possible to
502 * assert, at runtime, that only magic values with the expected reason flow
503 * through a particular value. For example, if cx->exception has a magic
504 * value, the reason must be JS_GENERATOR_CLOSING.
506 * - The JS::Value operations are preferred. The JSVAL_* operations remain for
507 * compatibility; they may be removed at some point. These operations mostly
508 * provide similar functionality. But there are a few key differences. One
509 * is that JS::Value gives null a separate type.
510 * Also, to help prevent mistakenly boxing a nullable JSObject* as an object,
511 * Value::setObject takes a JSObject&. (Conversely, Value::toObject returns a
512 * JSObject&.) A convenience member Value::setObjectOrNull is provided.
514 * - Note that JS::Value is 8 bytes on 32 and 64-bit architectures. Thus, on
515 * 32-bit user code should avoid copying jsval/JS::Value as much as possible,
516 * preferring to pass by const Value&.
518 * Spectre mitigations
519 * ===================
520 * To mitigate Spectre attacks, we do the following:
522 * - On 64-bit platforms, when unboxing a Value, we XOR the bits with the
523 * expected type tag (instead of masking the payload bits). This guarantees
524 * that toString, toObject, toSymbol will return an invalid pointer (because
525 * some high bits will be set) when called on a Value with a different type
528 * - On 32-bit platforms,when unboxing an object/string/symbol Value, we use a
529 * conditional move (not speculated) to zero the payload register if the type
532 class alignas(8) Value
{
537 constexpr Value() : asBits_(bitsFromTagAndPayload(JSVAL_TAG_UNDEFINED
, 0)) {}
540 explicit constexpr Value(uint64_t asBits
) : asBits_(asBits
) {}
542 static uint64_t bitsFromDouble(double d
) {
543 #if defined(JS_NONCANONICAL_HARDWARE_NAN)
544 d
= CanonicalizeNaN(d
);
546 return mozilla::BitwiseCast
<uint64_t>(d
);
549 static_assert(sizeof(JSValueType
) == 1,
550 "type bits must fit in a single byte");
551 static_assert(sizeof(JSValueTag
) == 4,
552 "32-bit Value's tag_ must have size 4 to complement the "
553 "payload union's size 4");
554 static_assert(sizeof(JSWhyMagic
) <= 4,
555 "32-bit Value's JSWhyMagic payload field must not inflate "
556 "the payload beyond 4 bytes");
559 #if defined(JS_NUNBOX32)
560 using PayloadType
= uint32_t;
561 #elif defined(JS_PUNBOX64)
562 using PayloadType
= uint64_t;
565 static constexpr uint64_t bitsFromTagAndPayload(JSValueTag tag
,
566 PayloadType payload
) {
567 return (uint64_t(tag
) << JSVAL_TAG_SHIFT
) | payload
;
570 static constexpr Value
fromTagAndPayload(JSValueTag tag
,
571 PayloadType payload
) {
572 return fromRawBits(bitsFromTagAndPayload(tag
, payload
));
575 static constexpr Value
fromRawBits(uint64_t asBits
) { return Value(asBits
); }
577 static constexpr Value
fromInt32(int32_t i
) {
578 return fromTagAndPayload(JSVAL_TAG_INT32
, uint32_t(i
));
581 static Value
fromDouble(double d
) { return fromRawBits(bitsFromDouble(d
)); }
584 * Returns false if creating a NumberValue containing the given type would
585 * be lossy, true otherwise.
587 template <typename T
>
588 static bool isNumberRepresentable(const T t
) {
589 return T(double(t
)) == t
;
595 asBits_
= bitsFromTagAndPayload(JSVAL_TAG_NULL
, 0);
596 MOZ_ASSERT(isNull());
599 void setUndefined() {
600 asBits_
= bitsFromTagAndPayload(JSVAL_TAG_UNDEFINED
, 0);
601 MOZ_ASSERT(isUndefined());
604 void setInt32(int32_t i
) {
605 asBits_
= bitsFromTagAndPayload(JSVAL_TAG_INT32
, uint32_t(i
));
606 MOZ_ASSERT(toInt32() == i
);
609 void setDouble(double d
) {
610 asBits_
= bitsFromDouble(d
);
611 MOZ_ASSERT(isDouble());
614 void setString(JSString
* str
) {
615 MOZ_ASSERT(js::gc::IsCellPointerValid(str
));
616 asBits_
= bitsFromTagAndPayload(JSVAL_TAG_STRING
, PayloadType(str
));
617 MOZ_ASSERT(toString() == str
);
620 void setSymbol(JS::Symbol
* sym
) {
621 MOZ_ASSERT(js::gc::IsCellPointerValid(sym
));
622 asBits_
= bitsFromTagAndPayload(JSVAL_TAG_SYMBOL
, PayloadType(sym
));
623 MOZ_ASSERT(toSymbol() == sym
);
626 void setBigInt(JS::BigInt
* bi
) {
627 MOZ_ASSERT(js::gc::IsCellPointerValid(bi
));
628 asBits_
= bitsFromTagAndPayload(JSVAL_TAG_BIGINT
, PayloadType(bi
));
629 MOZ_ASSERT(toBigInt() == bi
);
632 void setObject(JSObject
& obj
) {
633 MOZ_ASSERT(js::gc::IsCellPointerValid(&obj
));
634 #ifdef ENABLE_RECORD_TUPLE
635 MOZ_ASSERT(!js::gc::MaybeForwardedIsExtendedPrimitive(obj
));
637 setObjectNoCheck(&obj
);
638 MOZ_ASSERT(&toObject() == &obj
);
641 #ifdef ENABLE_RECORD_TUPLE
642 void setExtendedPrimitive(JSObject
& obj
) {
643 MOZ_ASSERT(js::gc::IsCellPointerValid(&obj
));
644 MOZ_ASSERT(js::gc::MaybeForwardedIsExtendedPrimitive(obj
));
646 bitsFromTagAndPayload(JSVAL_TAG_EXTENDED_PRIMITIVE
, PayloadType(&obj
));
647 MOZ_ASSERT(&toExtendedPrimitive() == &obj
);
651 void changeGCThingPayload(js::gc::Cell
* cell
) {
652 MOZ_ASSERT(js::gc::IsCellPointerValid(cell
));
654 assertTraceKindMatches(cell
);
656 asBits_
= bitsFromTagAndPayload(toTag(), PayloadType(cell
));
657 MOZ_ASSERT(toGCThing() == cell
);
662 void assertTraceKindMatches(js::gc::Cell
* cell
) const;
665 void setObjectNoCheck(JSObject
* obj
) {
666 asBits_
= bitsFromTagAndPayload(JSVAL_TAG_OBJECT
, PayloadType(obj
));
669 friend inline Value
js::PoisonedObjectValue(uintptr_t poison
);
672 void setBoolean(bool b
) {
673 asBits_
= bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN
, uint32_t(b
));
674 MOZ_ASSERT(toBoolean() == b
);
677 void setMagic(JSWhyMagic why
) {
678 asBits_
= bitsFromTagAndPayload(JSVAL_TAG_MAGIC
, uint32_t(why
));
679 MOZ_ASSERT(whyMagic() == why
);
682 void setMagicUint32(uint32_t payload
) {
683 MOZ_ASSERT(payload
>= JS_WHY_MAGIC_COUNT
,
684 "This should only be used for non-standard magic values");
685 asBits_
= bitsFromTagAndPayload(JSVAL_TAG_MAGIC
, payload
);
686 MOZ_ASSERT(magicUint32() == payload
);
689 void setNumber(float f
) {
691 if (mozilla::NumberIsInt32(f
, &i
)) {
696 setDouble(double(f
));
699 void setNumber(double d
) {
701 if (mozilla::NumberIsInt32(d
, &i
)) {
709 template <typename T
>
710 void setNumber(const T t
) {
711 static_assert(std::is_integral
<T
>::value
, "must be integral type");
712 MOZ_ASSERT(isNumberRepresentable(t
), "value creation would be lossy");
714 if constexpr (std::numeric_limits
<T
>::is_signed
) {
715 if constexpr (sizeof(t
) <= sizeof(int32_t)) {
716 setInt32(int32_t(t
));
718 if (JSVAL_INT_MIN
<= t
&& t
<= JSVAL_INT_MAX
) {
719 setInt32(int32_t(t
));
721 setDouble(double(t
));
725 if constexpr (sizeof(t
) <= sizeof(uint16_t)) {
726 setInt32(int32_t(t
));
728 if (t
<= JSVAL_INT_MAX
) {
729 setInt32(int32_t(t
));
731 setDouble(double(t
));
737 void setObjectOrNull(JSObject
* arg
) {
745 void swap(Value
& rhs
) {
746 uint64_t tmp
= rhs
.asBits_
;
747 rhs
.asBits_
= asBits_
;
752 JSValueTag
toTag() const { return JSValueTag(asBits_
>> JSVAL_TAG_SHIFT
); }
754 template <typename T
, JSValueTag Tag
>
755 T
* unboxGCPointer() const {
756 MOZ_ASSERT((asBits_
& js::gc::CellAlignMask
) == 0,
757 "GC pointer is not aligned. Is this memory corruption?");
758 #if defined(JS_NUNBOX32)
759 uintptr_t payload
= uint32_t(asBits_
);
760 return reinterpret_cast<T
*>(payload
);
761 #elif defined(JS_PUNBOX64)
762 // Note: the 'Spectre mitigations' comment at the top of this class
763 // explains why we use XOR here.
764 constexpr uint64_t shiftedTag
= uint64_t(Tag
) << JSVAL_TAG_SHIFT
;
765 return reinterpret_cast<T
*>(uintptr_t(asBits_
^ shiftedTag
));
770 /*** JIT-only interfaces to interact with and create raw Values ***/
771 #if defined(JS_NUNBOX32)
772 PayloadType
toNunboxPayload() const { return uint32_t(asBits_
); }
774 JSValueTag
toNunboxTag() const { return toTag(); }
775 #elif defined(JS_PUNBOX64)
776 const void* bitsAsPunboxPointer() const {
777 return reinterpret_cast<void*>(asBits_
);
781 /*** Value type queries ***/
784 * N.B. GCC, in some but not all cases, chooses to emit signed comparison
785 * of JSValueTag even though its underlying type has been forced to be
786 * uint32_t. Thus, all comparisons should explicitly cast operands to
790 bool isUndefined() const {
791 #if defined(JS_NUNBOX32)
792 return toTag() == JSVAL_TAG_UNDEFINED
;
793 #elif defined(JS_PUNBOX64)
794 return asBits_
== JSVAL_SHIFTED_TAG_UNDEFINED
;
798 bool isNull() const {
799 #if defined(JS_NUNBOX32)
800 return toTag() == JSVAL_TAG_NULL
;
801 #elif defined(JS_PUNBOX64)
802 return asBits_
== JSVAL_SHIFTED_TAG_NULL
;
806 bool isNullOrUndefined() const { return isNull() || isUndefined(); }
808 bool isInt32() const { return toTag() == JSVAL_TAG_INT32
; }
810 bool isInt32(int32_t i32
) const {
811 return asBits_
== bitsFromTagAndPayload(JSVAL_TAG_INT32
, uint32_t(i32
));
814 bool isDouble() const { return detail::ValueIsDouble(asBits_
); }
816 bool isNumber() const {
817 #if defined(JS_NUNBOX32)
818 MOZ_ASSERT(toTag() != JSVAL_TAG_CLEAR
);
819 return uint32_t(toTag()) <= uint32_t(detail::ValueUpperInclNumberTag
);
820 #elif defined(JS_PUNBOX64)
821 return asBits_
< detail::ValueUpperExclShiftedNumberTag
;
825 bool isString() const { return toTag() == JSVAL_TAG_STRING
; }
827 bool isSymbol() const { return toTag() == JSVAL_TAG_SYMBOL
; }
829 bool isBigInt() const { return toTag() == JSVAL_TAG_BIGINT
; }
831 bool isObject() const {
832 #if defined(JS_NUNBOX32)
833 return toTag() == JSVAL_TAG_OBJECT
;
834 #elif defined(JS_PUNBOX64)
835 MOZ_ASSERT((asBits_
>> JSVAL_TAG_SHIFT
) <= JSVAL_TAG_OBJECT
);
836 return asBits_
>= JSVAL_SHIFTED_TAG_OBJECT
;
840 #ifdef ENABLE_RECORD_TUPLE
841 bool isExtendedPrimitive() const {
842 return toTag() == JSVAL_TAG_EXTENDED_PRIMITIVE
;
846 bool hasObjectPayload() const {
847 return isObject() || IF_RECORD_TUPLE(isExtendedPrimitive(), false);
850 bool isPrimitive() const {
851 #if defined(JS_NUNBOX32)
852 return uint32_t(toTag()) < uint32_t(detail::ValueUpperExclPrimitiveTag
);
853 #elif defined(JS_PUNBOX64)
854 return asBits_
< detail::ValueUpperExclShiftedPrimitiveTag
;
858 bool isObjectOrNull() const { return isObject() || isNull(); }
860 bool isNumeric() const { return isNumber() || isBigInt(); }
862 bool isGCThing() const {
863 #if defined(JS_NUNBOX32)
864 /* gcc sometimes generates signed < without explicit casts. */
865 return uint32_t(toTag()) >= uint32_t(detail::ValueLowerInclGCThingTag
);
866 #elif defined(JS_PUNBOX64)
867 return asBits_
>= detail::ValueLowerInclShiftedGCThingTag
;
871 bool isBoolean() const { return toTag() == JSVAL_TAG_BOOLEAN
; }
873 bool isTrue() const {
874 return asBits_
== bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN
, uint32_t(true));
877 bool isFalse() const {
878 return asBits_
== bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN
, uint32_t(false));
881 bool isMagic() const { return toTag() == JSVAL_TAG_MAGIC
; }
883 bool isMagic(JSWhyMagic why
) const {
887 MOZ_RELEASE_ASSERT(whyMagic() == why
);
891 // Like isMagic, but without the release assertion.
892 bool isMagicNoReleaseCheck(JSWhyMagic why
) const {
896 MOZ_ASSERT(whyMagic() == why
);
900 JS::TraceKind
traceKind() const {
901 MOZ_ASSERT(isGCThing());
902 static_assert((JSVAL_TAG_STRING
& 0x03) == size_t(JS::TraceKind::String
),
903 "Value type tags must correspond with JS::TraceKinds.");
904 static_assert((JSVAL_TAG_SYMBOL
& 0x03) == size_t(JS::TraceKind::Symbol
),
905 "Value type tags must correspond with JS::TraceKinds.");
906 static_assert((JSVAL_TAG_OBJECT
& 0x03) == size_t(JS::TraceKind::Object
),
907 "Value type tags must correspond with JS::TraceKinds.");
908 static_assert((JSVAL_TAG_BIGINT
& 0x03) == size_t(JS::TraceKind::BigInt
),
909 "Value type tags must correspond with JS::TraceKinds.");
910 if (MOZ_UNLIKELY(isPrivateGCThing())) {
911 return JS::GCThingTraceKind(toGCThing());
913 #ifdef ENABLE_RECORD_TUPLE
914 if (isExtendedPrimitive()) {
915 return JS::TraceKind::Object
;
918 return JS::TraceKind(toTag() & 0x03);
921 JSWhyMagic
whyMagic() const {
922 MOZ_ASSERT(magicUint32() < JS_WHY_MAGIC_COUNT
);
923 return static_cast<JSWhyMagic
>(magicUint32());
926 uint32_t magicUint32() const {
927 MOZ_ASSERT(isMagic());
928 return uint32_t(asBits_
);
933 bool operator==(const Value
& rhs
) const { return asBits_
== rhs
.asBits_
; }
935 bool operator!=(const Value
& rhs
) const { return asBits_
!= rhs
.asBits_
; }
937 friend inline bool SameType(const Value
& lhs
, const Value
& rhs
);
939 /*** Extract the value's typed payload ***/
941 int32_t toInt32() const {
942 MOZ_ASSERT(isInt32());
943 return int32_t(asBits_
);
946 double toDouble() const {
947 MOZ_ASSERT(isDouble());
948 return mozilla::BitwiseCast
<double>(asBits_
);
951 double toNumber() const {
952 MOZ_ASSERT(isNumber());
953 return isDouble() ? toDouble() : double(toInt32());
956 JSString
* toString() const {
957 MOZ_ASSERT(isString());
958 return unboxGCPointer
<JSString
, JSVAL_TAG_STRING
>();
961 JS::Symbol
* toSymbol() const {
962 MOZ_ASSERT(isSymbol());
963 return unboxGCPointer
<JS::Symbol
, JSVAL_TAG_SYMBOL
>();
966 JS::BigInt
* toBigInt() const {
967 MOZ_ASSERT(isBigInt());
968 return unboxGCPointer
<JS::BigInt
, JSVAL_TAG_BIGINT
>();
971 JSObject
& toObject() const {
972 MOZ_ASSERT(isObject());
973 #if defined(JS_PUNBOX64)
974 MOZ_ASSERT((asBits_
& detail::ValueGCThingPayloadMask
) != 0);
976 return *unboxGCPointer
<JSObject
, JSVAL_TAG_OBJECT
>();
979 JSObject
* toObjectOrNull() const {
980 MOZ_ASSERT(isObjectOrNull());
981 #if defined(JS_NUNBOX32)
982 return reinterpret_cast<JSObject
*>(uintptr_t(asBits_
));
983 #elif defined(JS_PUNBOX64)
984 // Note: the 'Spectre mitigations' comment at the top of this class
985 // explains why we use XOR here and in other to* methods.
987 (asBits_
^ JSVAL_SHIFTED_TAG_OBJECT
) & ~detail::ValueObjectOrNullBit
;
988 MOZ_ASSERT((ptrBits
& 0x7) == 0);
989 return reinterpret_cast<JSObject
*>(ptrBits
);
993 #ifdef ENABLE_RECORD_TUPLE
994 JSObject
& toExtendedPrimitive() const {
995 MOZ_ASSERT(isExtendedPrimitive());
996 # if defined(JS_PUNBOX64)
997 MOZ_ASSERT((asBits_
& detail::ValueGCThingPayloadMask
) != 0);
999 return *unboxGCPointer
<JSObject
, JSVAL_TAG_EXTENDED_PRIMITIVE
>();
1003 JSObject
& getObjectPayload() const {
1004 #ifdef ENABLE_RECORD_TUPLE
1005 return isExtendedPrimitive() ? toExtendedPrimitive() : toObject();
1011 js::gc::Cell
* toGCThing() const {
1012 MOZ_ASSERT(isGCThing());
1013 #if defined(JS_NUNBOX32)
1014 return reinterpret_cast<js::gc::Cell
*>(uintptr_t(asBits_
));
1015 #elif defined(JS_PUNBOX64)
1016 uint64_t ptrBits
= asBits_
& detail::ValueGCThingPayloadMask
;
1017 MOZ_ASSERT((ptrBits
& 0x7) == 0);
1018 return reinterpret_cast<js::gc::Cell
*>(ptrBits
);
1022 GCCellPtr
toGCCellPtr() const { return GCCellPtr(toGCThing(), traceKind()); }
1024 bool toBoolean() const {
1025 MOZ_ASSERT(isBoolean());
1026 #if defined(JS_NUNBOX32)
1027 return bool(toNunboxPayload());
1028 #elif defined(JS_PUNBOX64)
1029 return bool(asBits_
& 0x1);
1033 constexpr uint64_t asRawBits() const { return asBits_
; }
1035 JSValueType
extractNonDoubleType() const {
1036 uint32_t type
= toTag() & 0xF;
1037 MOZ_ASSERT(type
> JSVAL_TYPE_DOUBLE
);
1038 return JSValueType(type
);
1041 JS::ValueType
type() const {
1043 return JS::ValueType::Double
;
1046 JSValueType type
= extractNonDoubleType();
1047 MOZ_ASSERT(type
<= JSVAL_TYPE_OBJECT
);
1048 return JS::ValueType(type
);
1054 * Private setters/getters allow the caller to read/write arbitrary
1055 * word-size pointers or uint32s. After storing to a value with
1056 * setPrivateX, it is the caller's responsibility to only read using
1057 * toPrivateX. Private values are given a type which ensures they
1058 * aren't marked by the GC.
1061 void setPrivate(void* ptr
) {
1062 #if defined(JS_PUNBOX64)
1063 MOZ_ASSERT(detail::IsValidUserModePointer(uintptr_t(ptr
)));
1065 asBits_
= uintptr_t(ptr
);
1066 MOZ_ASSERT(isDouble());
1069 void* toPrivate() const {
1070 MOZ_ASSERT(isDouble());
1071 #if defined(JS_PUNBOX64)
1072 MOZ_ASSERT(detail::IsValidUserModePointer(asBits_
));
1074 return reinterpret_cast<void*>(uintptr_t(asBits_
));
1077 void* toPrivateUnchecked() const {
1078 return reinterpret_cast<void*>(uintptr_t(asBits_
));
1081 void setPrivateUint32(uint32_t ui
) {
1082 MOZ_ASSERT(uint32_t(int32_t(ui
)) == ui
);
1083 setInt32(int32_t(ui
));
1086 uint32_t toPrivateUint32() const { return uint32_t(toInt32()); }
1089 * Private GC Thing API
1091 * Non-JSObject, JSString, and JS::Symbol cells may be put into the 64-bit
1092 * payload as private GC things. Such Values are considered isGCThing(), and
1093 * as such, automatically marked. Their traceKind() is gotten via their
1097 void setPrivateGCThing(js::gc::Cell
* cell
) {
1098 MOZ_ASSERT(JS::GCThingTraceKind(cell
) != JS::TraceKind::String
,
1099 "Private GC thing Values must not be strings. Make a "
1100 "StringValue instead.");
1101 MOZ_ASSERT(JS::GCThingTraceKind(cell
) != JS::TraceKind::Symbol
,
1102 "Private GC thing Values must not be symbols. Make a "
1103 "SymbolValue instead.");
1104 MOZ_ASSERT(JS::GCThingTraceKind(cell
) != JS::TraceKind::BigInt
,
1105 "Private GC thing Values must not be BigInts. Make a "
1106 "BigIntValue instead.");
1107 MOZ_ASSERT(JS::GCThingTraceKind(cell
) != JS::TraceKind::Object
,
1108 "Private GC thing Values must not be objects. Make an "
1109 "ObjectValue instead.");
1111 MOZ_ASSERT(js::gc::IsCellPointerValid(cell
));
1112 #if defined(JS_PUNBOX64)
1113 // VisualStudio cannot contain parenthesized C++ style cast and shift
1114 // inside decltype in template parameter:
1115 // AssertionConditionType<decltype((uintptr_t(x) >> 1))>
1116 // It throws syntax error.
1117 MOZ_ASSERT((((uintptr_t)cell
) >> JSVAL_TAG_SHIFT
) == 0);
1120 bitsFromTagAndPayload(JSVAL_TAG_PRIVATE_GCTHING
, PayloadType(cell
));
1123 bool isPrivateGCThing() const { return toTag() == JSVAL_TAG_PRIVATE_GCTHING
; }
1125 #if defined(DEBUG) || defined(JS_JITSPEW)
1127 void dump(js::GenericPrinter
& out
) const;
1128 void dump(js::JSONPrinter
& json
) const;
1130 void dumpFields(js::JSONPrinter
& json
) const;
1131 void dumpStringContent(js::GenericPrinter
& out
) const;
1133 } JS_HAZ_GC_POINTER MOZ_NON_PARAM
;
1135 static_assert(sizeof(Value
) == 8,
1136 "Value size must leave three tag bits, be a binary power, and "
1137 "is ubiquitously depended upon everywhere");
1139 static MOZ_ALWAYS_INLINE
void ExposeValueToActiveJS(const Value
& v
) {
1142 MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarrieredSlow(&tmp
));
1144 if (v
.isGCThing()) {
1145 js::gc::ExposeGCThingToActiveJS(v
.toGCCellPtr());
1149 /************************************************************************/
1151 static inline MOZ_MAY_CALL_AFTER_MUST_RETURN Value
NullValue() {
1157 static constexpr Value
UndefinedValue() { return Value(); }
1159 static constexpr Value
Int32Value(int32_t i32
) { return Value::fromInt32(i32
); }
1161 static inline Value
DoubleValue(double dbl
) {
1167 static inline Value
CanonicalizedDoubleValue(double d
) {
1168 return Value::fromDouble(CanonicalizeNaN(d
));
1171 static inline Value
NaNValue() {
1172 return Value::fromRawBits(detail::CanonicalizedNaNBits
);
1175 static inline Value
InfinityValue() {
1176 return Value::fromRawBits(detail::InfinityBits
);
1179 static inline Value
Float32Value(float f
) {
1185 static inline Value
StringValue(JSString
* str
) {
1191 static inline Value
SymbolValue(JS::Symbol
* sym
) {
1197 static inline Value
BigIntValue(JS::BigInt
* bi
) {
1203 static inline Value
BooleanValue(bool boo
) {
1209 static inline Value
TrueValue() {
1215 static inline Value
FalseValue() {
1217 v
.setBoolean(false);
1221 static inline Value
ObjectValue(JSObject
& obj
) {
1227 #ifdef ENABLE_RECORD_TUPLE
1228 static inline Value
ExtendedPrimitiveValue(JSObject
& obj
) {
1230 v
.setExtendedPrimitive(obj
);
1235 static inline Value
MagicValue(JSWhyMagic why
) {
1241 static inline Value
MagicValueUint32(uint32_t payload
) {
1243 v
.setMagicUint32(payload
);
1247 static constexpr Value
NumberValue(uint32_t i
) {
1248 return i
<= JSVAL_INT_MAX
? Int32Value(int32_t(i
))
1249 : Value::fromDouble(double(i
));
1252 template <typename T
>
1253 static inline Value
NumberValue(const T t
) {
1259 static inline Value
ObjectOrNullValue(JSObject
* obj
) {
1261 v
.setObjectOrNull(obj
);
1265 static inline Value
PrivateValue(void* ptr
) {
1271 static inline Value
PrivateValue(uintptr_t ptr
) {
1272 return PrivateValue(reinterpret_cast<void*>(ptr
));
1275 static inline Value
PrivateUint32Value(uint32_t ui
) {
1277 v
.setPrivateUint32(ui
);
1281 static inline Value
PrivateGCThingValue(js::gc::Cell
* cell
) {
1283 v
.setPrivateGCThing(cell
);
1287 inline bool SameType(const Value
& lhs
, const Value
& rhs
) {
1288 #if defined(JS_NUNBOX32)
1289 JSValueTag ltag
= lhs
.toTag(), rtag
= rhs
.toTag();
1290 return ltag
== rtag
|| (ltag
< JSVAL_TAG_CLEAR
&& rtag
< JSVAL_TAG_CLEAR
);
1291 #elif defined(JS_PUNBOX64)
1292 return (lhs
.isDouble() && rhs
.isDouble()) ||
1293 (((lhs
.asBits_
^ rhs
.asBits_
) & 0xFFFF800000000000ULL
) == 0);
1299 /************************************************************************/
1302 JS_PUBLIC_API
void HeapValuePostWriteBarrier(Value
* valuep
, const Value
& prev
,
1304 JS_PUBLIC_API
void HeapValueWriteBarriers(Value
* valuep
, const Value
& prev
,
1308 struct GCPolicy
<JS::Value
> {
1309 static void trace(JSTracer
* trc
, Value
* v
, const char* name
) {
1310 // This should only be called as part of root marking since that's the only
1311 // time we should trace unbarriered GC thing pointers. This will assert if
1312 // called at other times.
1313 TraceRoot(trc
, v
, name
);
1315 static bool isTenured(const Value
& thing
) {
1316 return !thing
.isGCThing() || !IsInsideNursery(thing
.toGCThing());
1318 static bool isValid(const Value
& value
) {
1319 return !value
.isGCThing() || js::gc::IsCellPointerValid(value
.toGCThing());
1328 struct BarrierMethods
<JS::Value
> {
1329 static gc::Cell
* asGCThingOrNull(const JS::Value
& v
) {
1330 return v
.isGCThing() ? v
.toGCThing() : nullptr;
1332 static void postWriteBarrier(JS::Value
* v
, const JS::Value
& prev
,
1333 const JS::Value
& next
) {
1334 JS::HeapValuePostWriteBarrier(v
, prev
, next
);
1336 static void exposeToJS(const JS::Value
& v
) { JS::ExposeValueToActiveJS(v
); }
1337 static void readBarrier(const JS::Value
& v
) {
1338 if (v
.isGCThing()) {
1339 js::gc::IncrementalReadBarrier(v
.toGCCellPtr());
1344 template <class Wrapper
>
1345 class MutableValueOperations
;
1348 * A class designed for CRTP use in implementing the non-mutating parts of the
1349 * Value interface in Value-like classes. Wrapper must be a class inheriting
1350 * ValueOperations<Wrapper> with a visible get() method returning a const
1351 * reference to the Value abstracted by Wrapper.
1353 template <class Wrapper
>
1354 class WrappedPtrOperations
<JS::Value
, Wrapper
> {
1355 const JS::Value
& value() const {
1356 return static_cast<const Wrapper
*>(this)->get();
1360 bool isUndefined() const { return value().isUndefined(); }
1361 bool isNull() const { return value().isNull(); }
1362 bool isBoolean() const { return value().isBoolean(); }
1363 bool isTrue() const { return value().isTrue(); }
1364 bool isFalse() const { return value().isFalse(); }
1365 bool isNumber() const { return value().isNumber(); }
1366 bool isInt32() const { return value().isInt32(); }
1367 bool isInt32(int32_t i32
) const { return value().isInt32(i32
); }
1368 bool isDouble() const { return value().isDouble(); }
1369 bool isString() const { return value().isString(); }
1370 bool isSymbol() const { return value().isSymbol(); }
1371 bool isBigInt() const { return value().isBigInt(); }
1372 bool isObject() const { return value().isObject(); }
1373 #ifdef ENABLE_RECORD_TUPLE
1374 bool isExtendedPrimitive() const { return value().isExtendedPrimitive(); }
1376 bool hasObjectPayload() const { return value().hasObjectPayload(); }
1377 bool isMagic() const { return value().isMagic(); }
1378 bool isMagic(JSWhyMagic why
) const { return value().isMagic(why
); }
1379 bool isGCThing() const { return value().isGCThing(); }
1380 bool isPrivateGCThing() const { return value().isPrivateGCThing(); }
1381 bool isPrimitive() const { return value().isPrimitive(); }
1383 bool isNullOrUndefined() const { return value().isNullOrUndefined(); }
1384 bool isObjectOrNull() const { return value().isObjectOrNull(); }
1385 bool isNumeric() const { return value().isNumeric(); }
1387 bool toBoolean() const { return value().toBoolean(); }
1388 double toNumber() const { return value().toNumber(); }
1389 int32_t toInt32() const { return value().toInt32(); }
1390 double toDouble() const { return value().toDouble(); }
1391 JSString
* toString() const { return value().toString(); }
1392 JS::Symbol
* toSymbol() const { return value().toSymbol(); }
1393 JS::BigInt
* toBigInt() const { return value().toBigInt(); }
1394 JSObject
& toObject() const { return value().toObject(); }
1395 JSObject
* toObjectOrNull() const { return value().toObjectOrNull(); }
1396 #ifdef ENABLE_RECORD_TUPLE
1397 JSObject
& toExtendedPrimitive() const {
1398 return value().toExtendedPrimitive();
1401 JSObject
& getObjectPayload() const { return value().getObjectPayload(); }
1402 JS::GCCellPtr
toGCCellPtr() const { return value().toGCCellPtr(); }
1403 gc::Cell
* toGCThing() const { return value().toGCThing(); }
1404 JS::TraceKind
traceKind() const { return value().traceKind(); }
1405 void* toPrivate() const { return value().toPrivate(); }
1406 uint32_t toPrivateUint32() const { return value().toPrivateUint32(); }
1408 uint64_t asRawBits() const { return value().asRawBits(); }
1409 JSValueType
extractNonDoubleType() const {
1410 return value().extractNonDoubleType();
1412 JS::ValueType
type() const { return value().type(); }
1414 JSWhyMagic
whyMagic() const { return value().whyMagic(); }
1415 uint32_t magicUint32() const { return value().magicUint32(); }
1419 * A class designed for CRTP use in implementing all the mutating parts of the
1420 * Value interface in Value-like classes. Wrapper must be a class inheriting
1421 * MutableWrappedPtrOperations<Wrapper> with visible get() methods returning
1422 * const and non-const references to the Value abstracted by Wrapper.
1424 template <class Wrapper
>
1425 class MutableWrappedPtrOperations
<JS::Value
, Wrapper
>
1426 : public WrappedPtrOperations
<JS::Value
, Wrapper
> {
1428 void set(const JS::Value
& v
) {
1429 // Call Wrapper::set to trigger any barriers.
1430 static_cast<Wrapper
*>(this)->set(v
);
1434 void setNull() { set(JS::NullValue()); }
1435 void setUndefined() { set(JS::UndefinedValue()); }
1436 void setInt32(int32_t i
) { set(JS::Int32Value(i
)); }
1437 void setDouble(double d
) { set(JS::DoubleValue(d
)); }
1438 void setNaN() { set(JS::NaNValue()); }
1439 void setInfinity() { set(JS::InfinityValue()); }
1440 void setBoolean(bool b
) { set(JS::BooleanValue(b
)); }
1441 void setMagic(JSWhyMagic why
) { set(JS::MagicValue(why
)); }
1442 template <typename T
>
1443 void setNumber(T t
) {
1444 set(JS::NumberValue(t
));
1446 void setString(JSString
* str
) { set(JS::StringValue(str
)); }
1447 void setSymbol(JS::Symbol
* sym
) { set(JS::SymbolValue(sym
)); }
1448 void setBigInt(JS::BigInt
* bi
) { set(JS::BigIntValue(bi
)); }
1449 void setObject(JSObject
& obj
) { set(JS::ObjectValue(obj
)); }
1450 void setObjectOrNull(JSObject
* arg
) { set(JS::ObjectOrNullValue(arg
)); }
1451 #ifdef ENABLE_RECORD_TUPLE
1452 void setExtendedPrimitive(JSObject
& obj
) {
1453 return set(JS::ExtendedPrimitiveValue(obj
));
1456 void setPrivate(void* ptr
) { set(JS::PrivateValue(ptr
)); }
1457 void setPrivateUint32(uint32_t ui
) { set(JS::PrivateUint32Value(ui
)); }
1458 void setPrivateGCThing(js::gc::Cell
* cell
) {
1459 set(JS::PrivateGCThingValue(cell
));
1464 * Augment the generic Heap<T> interface when T = Value with
1465 * type-querying, value-extracting, and mutating operations.
1467 template <typename Wrapper
>
1468 class HeapOperations
<JS::Value
, Wrapper
>
1469 : public MutableWrappedPtrOperations
<JS::Value
, Wrapper
> {};
1471 MOZ_HAVE_NORETURN MOZ_COLD MOZ_NEVER_INLINE
void ReportBadValueTypeAndCrash(
1472 const JS::Value
& val
);
1474 // If the Value is a GC pointer type, call |f| with the pointer cast to that
1475 // type and return the result wrapped in a Maybe, otherwise return None().
1476 template <typename F
>
1477 auto MapGCThingTyped(const JS::Value
& val
, F
&& f
) {
1478 switch (val
.type()) {
1479 case JS::ValueType::String
: {
1480 JSString
* str
= val
.toString();
1481 MOZ_ASSERT(gc::IsCellPointerValid(str
));
1482 return mozilla::Some(f(str
));
1484 #ifdef ENABLE_RECORD_TUPLE
1485 case JS::ValueType::ExtendedPrimitive
:
1487 case JS::ValueType::Object
: {
1488 JSObject
* obj
= &val
.getObjectPayload();
1489 MOZ_ASSERT(gc::IsCellPointerValid(obj
));
1490 return mozilla::Some(f(obj
));
1492 case JS::ValueType::Symbol
: {
1493 JS::Symbol
* sym
= val
.toSymbol();
1494 MOZ_ASSERT(gc::IsCellPointerValid(sym
));
1495 return mozilla::Some(f(sym
));
1497 case JS::ValueType::BigInt
: {
1498 JS::BigInt
* bi
= val
.toBigInt();
1499 MOZ_ASSERT(gc::IsCellPointerValid(bi
));
1500 return mozilla::Some(f(bi
));
1502 case JS::ValueType::PrivateGCThing
: {
1503 MOZ_ASSERT(gc::IsCellPointerValid(val
.toGCThing()));
1504 return mozilla::Some(MapGCThingTyped(val
.toGCCellPtr(), std::move(f
)));
1506 case JS::ValueType::Double
:
1507 case JS::ValueType::Int32
:
1508 case JS::ValueType::Boolean
:
1509 case JS::ValueType::Undefined
:
1510 case JS::ValueType::Null
:
1511 case JS::ValueType::Magic
: {
1512 MOZ_ASSERT(!val
.isGCThing());
1513 using ReturnType
= decltype(f(static_cast<JSObject
*>(nullptr)));
1514 return mozilla::Maybe
<ReturnType
>();
1518 ReportBadValueTypeAndCrash(val
);
1521 // If the Value is a GC pointer type, call |f| with the pointer cast to that
1522 // type. Return whether this happened.
1523 template <typename F
>
1524 bool ApplyGCThingTyped(const JS::Value
& val
, F
&& f
) {
1525 return MapGCThingTyped(val
,
1533 static inline JS::Value
PoisonedObjectValue(uintptr_t poison
) {
1535 v
.setObjectNoCheck(reinterpret_cast<JSObject
*>(poison
));
1544 MOZ_ALWAYS_INLINE
void AssertValueIsNotGray(const Value
& value
) {
1545 if (value
.isGCThing()) {
1546 AssertCellIsNotGray(value
.toGCThing());
1550 MOZ_ALWAYS_INLINE
void AssertValueIsNotGray(const Heap
<Value
>& value
) {
1551 AssertValueIsNotGray(value
.unbarrieredGet());
1557 /************************************************************************/
1561 extern JS_PUBLIC_DATA
const HandleValue NullHandleValue
;
1562 extern JS_PUBLIC_DATA
const HandleValue UndefinedHandleValue
;
1563 extern JS_PUBLIC_DATA
const HandleValue TrueHandleValue
;
1564 extern JS_PUBLIC_DATA
const HandleValue FalseHandleValue
;
1565 extern JS_PUBLIC_DATA
const Handle
<mozilla::Maybe
<Value
>> NothingHandleValue
;
1569 #endif /* js_Value_h */