Backed out changeset a5ff95602119 (bug 1905021) as requested for causing accessibilit...
[gecko.git] / js / public / Value.h
blob6c6d82f1b86a4f41e147662321c3fa52061cec50
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. */
9 #ifndef js_Value_h
10 #define js_Value_h
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>
21 #include "jstypes.h"
23 #include "js/HeapAPI.h"
24 #include "js/RootingAPI.h"
25 #include "js/TypeDecls.h"
27 namespace JS {
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:
35 // - Object Pointers
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
79 // below.)
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
85 // ECMAScript number.
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
110 // NaN.
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
114 // the exponent.
116 // Tag values are carefully ordered to support a set of micro-optimizations. In
117 // particular:
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.
145 // [1]:
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
157 #endif
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,
175 #endif
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
182 namespace JS {
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,
196 #endif
197 Object = JSVAL_TYPE_OBJECT,
199 } // namespace JS
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,
220 # endif
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,
243 # endif
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),
268 # endif
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");
275 #endif
277 namespace JS {
278 namespace detail {
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;
332 static_assert(
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
345 } // namespace JS
347 #define JSVAL_TYPE_TO_TAG(type) (JS::detail::ValueTypeToTag(type))
349 enum JSWhyMagic {
350 /** a hole in a native object's elements */
351 JS_ELEMENTS_HOLE,
353 /** there is not a pending iterator value */
354 JS_NO_ITER_VALUE,
356 /** exception value thrown when closing a generator */
357 JS_GENERATOR_CLOSING,
359 /** used in debug builds to catch tracing errors */
360 JS_ARG_POISON,
362 /** an empty subnode in the AST serializer */
363 JS_SERIALIZE_NO_NODE,
365 /** magic value passed to natives to indicate construction */
366 JS_IS_CONSTRUCTING,
368 /** see class js::HashableValue */
369 JS_HASH_KEY_EMPTY,
371 /** error while running Ion code */
372 JS_ION_ERROR,
374 /** missing recover instruction result */
375 JS_ION_BAILOUT,
377 /** optimized out slot */
378 JS_OPTIMIZED_OUT,
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 */
387 JS_INTERRUPT_REGEXP,
389 /** for local use */
390 JS_GENERIC_MAGIC,
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,
398 JS_WHY_MAGIC_COUNT
401 namespace js {
402 class JS_PUBLIC_API GenericPrinter;
403 class JSONPrinter;
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);
410 namespace gc {
411 bool MaybeForwardedIsExtendedPrimitive(const JSObject& obj);
412 } // namespace gc
413 #endif
414 } // namespace js
416 namespace JS {
418 namespace detail {
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
430 // platforms.
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
439 #endif
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
451 #endif
453 #if defined(JS_RUNTIME_CANONICAL_NAN)
454 extern uint64_t CanonicalizedNaNBits;
455 #else
456 constexpr uint64_t CanonicalizedNaNBits =
457 mozilla::SpecificNaNBits<double, detail::CanonicalizedNaNSignBit,
458 detail::CanonicalizedNaNSignificand>::value;
459 #endif
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");
467 #endif
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))) {
481 return GenericNaN();
483 return 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,
495 * Object, Magic }
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
526 * tag.
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
530 * doesn't match.
532 class alignas(8) Value {
533 private:
534 uint64_t asBits_;
536 public:
537 constexpr Value() : asBits_(bitsFromTagAndPayload(JSVAL_TAG_UNDEFINED, 0)) {}
539 private:
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);
545 #endif
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");
558 public:
559 #if defined(JS_NUNBOX32)
560 using PayloadType = uint32_t;
561 #elif defined(JS_PUNBOX64)
562 using PayloadType = uint64_t;
563 #endif
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;
592 /*** Mutators ***/
594 void setNull() {
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));
636 #endif
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));
645 asBits_ =
646 bitsFromTagAndPayload(JSVAL_TAG_EXTENDED_PRIMITIVE, PayloadType(&obj));
647 MOZ_ASSERT(&toExtendedPrimitive() == &obj);
649 #endif
651 void changeGCThingPayload(js::gc::Cell* cell) {
652 MOZ_ASSERT(js::gc::IsCellPointerValid(cell));
653 #ifdef DEBUG
654 assertTraceKindMatches(cell);
655 #endif
656 asBits_ = bitsFromTagAndPayload(toTag(), PayloadType(cell));
657 MOZ_ASSERT(toGCThing() == cell);
660 private:
661 #ifdef DEBUG
662 void assertTraceKindMatches(js::gc::Cell* cell) const;
663 #endif
665 void setObjectNoCheck(JSObject* obj) {
666 asBits_ = bitsFromTagAndPayload(JSVAL_TAG_OBJECT, PayloadType(obj));
669 friend inline Value js::PoisonedObjectValue(uintptr_t poison);
671 public:
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) {
690 int32_t i;
691 if (mozilla::NumberIsInt32(f, &i)) {
692 setInt32(i);
693 return;
696 setDouble(double(f));
699 void setNumber(double d) {
700 int32_t i;
701 if (mozilla::NumberIsInt32(d, &i)) {
702 setInt32(i);
703 return;
706 setDouble(d);
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));
717 } else {
718 if (JSVAL_INT_MIN <= t && t <= JSVAL_INT_MAX) {
719 setInt32(int32_t(t));
720 } else {
721 setDouble(double(t));
724 } else {
725 if constexpr (sizeof(t) <= sizeof(uint16_t)) {
726 setInt32(int32_t(t));
727 } else {
728 if (t <= JSVAL_INT_MAX) {
729 setInt32(int32_t(t));
730 } else {
731 setDouble(double(t));
737 void setObjectOrNull(JSObject* arg) {
738 if (arg) {
739 setObject(*arg);
740 } else {
741 setNull();
745 void swap(Value& rhs) {
746 uint64_t tmp = rhs.asBits_;
747 rhs.asBits_ = asBits_;
748 asBits_ = tmp;
751 private:
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));
766 #endif
769 public:
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_);
779 #endif
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
787 * uint32_t.
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;
795 #endif
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;
803 #endif
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;
822 #endif
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;
837 #endif
840 #ifdef ENABLE_RECORD_TUPLE
841 bool isExtendedPrimitive() const {
842 return toTag() == JSVAL_TAG_EXTENDED_PRIMITIVE;
844 #endif
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;
855 #endif
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;
868 #endif
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 {
884 if (!isMagic()) {
885 return false;
887 MOZ_RELEASE_ASSERT(whyMagic() == why);
888 return true;
891 // Like isMagic, but without the release assertion.
892 bool isMagicNoReleaseCheck(JSWhyMagic why) const {
893 if (!isMagic()) {
894 return false;
896 MOZ_ASSERT(whyMagic() == why);
897 return true;
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;
917 #endif
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_);
931 /*** Comparison ***/
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);
975 #endif
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.
986 uint64_t ptrBits =
987 (asBits_ ^ JSVAL_SHIFTED_TAG_OBJECT) & ~detail::ValueObjectOrNullBit;
988 MOZ_ASSERT((ptrBits & 0x7) == 0);
989 return reinterpret_cast<JSObject*>(ptrBits);
990 #endif
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);
998 # endif
999 return *unboxGCPointer<JSObject, JSVAL_TAG_EXTENDED_PRIMITIVE>();
1001 #endif
1003 JSObject& getObjectPayload() const {
1004 #ifdef ENABLE_RECORD_TUPLE
1005 return isExtendedPrimitive() ? toExtendedPrimitive() : toObject();
1006 #else
1007 return toObject();
1008 #endif
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);
1019 #endif
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);
1030 #endif
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 {
1042 if (isDouble()) {
1043 return JS::ValueType::Double;
1046 JSValueType type = extractNonDoubleType();
1047 MOZ_ASSERT(type <= JSVAL_TYPE_OBJECT);
1048 return JS::ValueType(type);
1052 * Private API
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)));
1064 #endif
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_));
1073 #endif
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
1094 * cells.
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);
1118 #endif
1119 asBits_ =
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)
1126 void dump() const;
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;
1132 #endif
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) {
1140 #ifdef DEBUG
1141 Value tmp = v;
1142 MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarrieredSlow(&tmp));
1143 #endif
1144 if (v.isGCThing()) {
1145 js::gc::ExposeGCThingToActiveJS(v.toGCCellPtr());
1149 /************************************************************************/
1151 static inline MOZ_MAY_CALL_AFTER_MUST_RETURN Value NullValue() {
1152 Value v;
1153 v.setNull();
1154 return v;
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) {
1162 Value v;
1163 v.setDouble(dbl);
1164 return v;
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) {
1180 Value v;
1181 v.setDouble(f);
1182 return v;
1185 static inline Value StringValue(JSString* str) {
1186 Value v;
1187 v.setString(str);
1188 return v;
1191 static inline Value SymbolValue(JS::Symbol* sym) {
1192 Value v;
1193 v.setSymbol(sym);
1194 return v;
1197 static inline Value BigIntValue(JS::BigInt* bi) {
1198 Value v;
1199 v.setBigInt(bi);
1200 return v;
1203 static inline Value BooleanValue(bool boo) {
1204 Value v;
1205 v.setBoolean(boo);
1206 return v;
1209 static inline Value TrueValue() {
1210 Value v;
1211 v.setBoolean(true);
1212 return v;
1215 static inline Value FalseValue() {
1216 Value v;
1217 v.setBoolean(false);
1218 return v;
1221 static inline Value ObjectValue(JSObject& obj) {
1222 Value v;
1223 v.setObject(obj);
1224 return v;
1227 #ifdef ENABLE_RECORD_TUPLE
1228 static inline Value ExtendedPrimitiveValue(JSObject& obj) {
1229 Value v;
1230 v.setExtendedPrimitive(obj);
1231 return v;
1233 #endif
1235 static inline Value MagicValue(JSWhyMagic why) {
1236 Value v;
1237 v.setMagic(why);
1238 return v;
1241 static inline Value MagicValueUint32(uint32_t payload) {
1242 Value v;
1243 v.setMagicUint32(payload);
1244 return v;
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) {
1254 Value v;
1255 v.setNumber(t);
1256 return v;
1259 static inline Value ObjectOrNullValue(JSObject* obj) {
1260 Value v;
1261 v.setObjectOrNull(obj);
1262 return v;
1265 static inline Value PrivateValue(void* ptr) {
1266 Value v;
1267 v.setPrivate(ptr);
1268 return v;
1271 static inline Value PrivateValue(uintptr_t ptr) {
1272 return PrivateValue(reinterpret_cast<void*>(ptr));
1275 static inline Value PrivateUint32Value(uint32_t ui) {
1276 Value v;
1277 v.setPrivateUint32(ui);
1278 return v;
1281 static inline Value PrivateGCThingValue(js::gc::Cell* cell) {
1282 Value v;
1283 v.setPrivateGCThing(cell);
1284 return v;
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);
1294 #endif
1297 } // namespace JS
1299 /************************************************************************/
1301 namespace JS {
1302 JS_PUBLIC_API void HeapValuePostWriteBarrier(Value* valuep, const Value& prev,
1303 const Value& next);
1304 JS_PUBLIC_API void HeapValueWriteBarriers(Value* valuep, const Value& prev,
1305 const Value& next);
1307 template <>
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());
1323 } // namespace JS
1325 namespace js {
1327 template <>
1328 struct BarrierMethods<JS::Value> {
1329 static gc::Cell* asGCThingOrNull(const JS::Value& v) {
1330 return v.isGCThing() ? v.toGCThing() : nullptr;
1332 static void writeBarriers(JS::Value* v, const JS::Value& prev,
1333 const JS::Value& next) {
1334 JS::HeapValueWriteBarriers(v, prev, next);
1336 static void postWriteBarrier(JS::Value* v, const JS::Value& prev,
1337 const JS::Value& next) {
1338 JS::HeapValuePostWriteBarrier(v, prev, next);
1340 static void exposeToJS(const JS::Value& v) { JS::ExposeValueToActiveJS(v); }
1341 static void readBarrier(const JS::Value& v) {
1342 if (v.isGCThing()) {
1343 js::gc::IncrementalReadBarrier(v.toGCCellPtr());
1348 template <class Wrapper>
1349 class MutableValueOperations;
1352 * A class designed for CRTP use in implementing the non-mutating parts of the
1353 * Value interface in Value-like classes. Wrapper must be a class inheriting
1354 * ValueOperations<Wrapper> with a visible get() method returning a const
1355 * reference to the Value abstracted by Wrapper.
1357 template <class Wrapper>
1358 class WrappedPtrOperations<JS::Value, Wrapper> {
1359 const JS::Value& value() const {
1360 return static_cast<const Wrapper*>(this)->get();
1363 public:
1364 bool isUndefined() const { return value().isUndefined(); }
1365 bool isNull() const { return value().isNull(); }
1366 bool isBoolean() const { return value().isBoolean(); }
1367 bool isTrue() const { return value().isTrue(); }
1368 bool isFalse() const { return value().isFalse(); }
1369 bool isNumber() const { return value().isNumber(); }
1370 bool isInt32() const { return value().isInt32(); }
1371 bool isInt32(int32_t i32) const { return value().isInt32(i32); }
1372 bool isDouble() const { return value().isDouble(); }
1373 bool isString() const { return value().isString(); }
1374 bool isSymbol() const { return value().isSymbol(); }
1375 bool isBigInt() const { return value().isBigInt(); }
1376 bool isObject() const { return value().isObject(); }
1377 #ifdef ENABLE_RECORD_TUPLE
1378 bool isExtendedPrimitive() const { return value().isExtendedPrimitive(); }
1379 #endif
1380 bool hasObjectPayload() const { return value().hasObjectPayload(); }
1381 bool isMagic() const { return value().isMagic(); }
1382 bool isMagic(JSWhyMagic why) const { return value().isMagic(why); }
1383 bool isGCThing() const { return value().isGCThing(); }
1384 bool isPrivateGCThing() const { return value().isPrivateGCThing(); }
1385 bool isPrimitive() const { return value().isPrimitive(); }
1387 bool isNullOrUndefined() const { return value().isNullOrUndefined(); }
1388 bool isObjectOrNull() const { return value().isObjectOrNull(); }
1389 bool isNumeric() const { return value().isNumeric(); }
1391 bool toBoolean() const { return value().toBoolean(); }
1392 double toNumber() const { return value().toNumber(); }
1393 int32_t toInt32() const { return value().toInt32(); }
1394 double toDouble() const { return value().toDouble(); }
1395 JSString* toString() const { return value().toString(); }
1396 JS::Symbol* toSymbol() const { return value().toSymbol(); }
1397 JS::BigInt* toBigInt() const { return value().toBigInt(); }
1398 JSObject& toObject() const { return value().toObject(); }
1399 JSObject* toObjectOrNull() const { return value().toObjectOrNull(); }
1400 #ifdef ENABLE_RECORD_TUPLE
1401 JSObject& toExtendedPrimitive() const {
1402 return value().toExtendedPrimitive();
1404 #endif
1405 JSObject& getObjectPayload() const { return value().getObjectPayload(); }
1406 JS::GCCellPtr toGCCellPtr() const { return value().toGCCellPtr(); }
1407 gc::Cell* toGCThing() const { return value().toGCThing(); }
1408 JS::TraceKind traceKind() const { return value().traceKind(); }
1409 void* toPrivate() const { return value().toPrivate(); }
1410 uint32_t toPrivateUint32() const { return value().toPrivateUint32(); }
1412 uint64_t asRawBits() const { return value().asRawBits(); }
1413 JSValueType extractNonDoubleType() const {
1414 return value().extractNonDoubleType();
1416 JS::ValueType type() const { return value().type(); }
1418 JSWhyMagic whyMagic() const { return value().whyMagic(); }
1419 uint32_t magicUint32() const { return value().magicUint32(); }
1423 * A class designed for CRTP use in implementing all the mutating parts of the
1424 * Value interface in Value-like classes. Wrapper must be a class inheriting
1425 * MutableWrappedPtrOperations<Wrapper> with visible get() methods returning
1426 * const and non-const references to the Value abstracted by Wrapper.
1428 template <class Wrapper>
1429 class MutableWrappedPtrOperations<JS::Value, Wrapper>
1430 : public WrappedPtrOperations<JS::Value, Wrapper> {
1431 protected:
1432 void set(const JS::Value& v) {
1433 // Call Wrapper::set to trigger any barriers.
1434 static_cast<Wrapper*>(this)->set(v);
1437 public:
1438 void setNull() { set(JS::NullValue()); }
1439 void setUndefined() { set(JS::UndefinedValue()); }
1440 void setInt32(int32_t i) { set(JS::Int32Value(i)); }
1441 void setDouble(double d) { set(JS::DoubleValue(d)); }
1442 void setNaN() { set(JS::NaNValue()); }
1443 void setInfinity() { set(JS::InfinityValue()); }
1444 void setBoolean(bool b) { set(JS::BooleanValue(b)); }
1445 void setMagic(JSWhyMagic why) { set(JS::MagicValue(why)); }
1446 template <typename T>
1447 void setNumber(T t) {
1448 set(JS::NumberValue(t));
1450 void setString(JSString* str) { set(JS::StringValue(str)); }
1451 void setSymbol(JS::Symbol* sym) { set(JS::SymbolValue(sym)); }
1452 void setBigInt(JS::BigInt* bi) { set(JS::BigIntValue(bi)); }
1453 void setObject(JSObject& obj) { set(JS::ObjectValue(obj)); }
1454 void setObjectOrNull(JSObject* arg) { set(JS::ObjectOrNullValue(arg)); }
1455 #ifdef ENABLE_RECORD_TUPLE
1456 void setExtendedPrimitive(JSObject& obj) {
1457 return set(JS::ExtendedPrimitiveValue(obj));
1459 #endif
1460 void setPrivate(void* ptr) { set(JS::PrivateValue(ptr)); }
1461 void setPrivateUint32(uint32_t ui) { set(JS::PrivateUint32Value(ui)); }
1462 void setPrivateGCThing(js::gc::Cell* cell) {
1463 set(JS::PrivateGCThingValue(cell));
1468 * Augment the generic Heap<T> interface when T = Value with
1469 * type-querying, value-extracting, and mutating operations.
1471 template <typename Wrapper>
1472 class HeapOperations<JS::Value, Wrapper>
1473 : public MutableWrappedPtrOperations<JS::Value, Wrapper> {};
1475 MOZ_HAVE_NORETURN MOZ_COLD MOZ_NEVER_INLINE void ReportBadValueTypeAndCrash(
1476 const JS::Value& val);
1478 // If the Value is a GC pointer type, call |f| with the pointer cast to that
1479 // type and return the result wrapped in a Maybe, otherwise return None().
1480 template <typename F>
1481 auto MapGCThingTyped(const JS::Value& val, F&& f) {
1482 switch (val.type()) {
1483 case JS::ValueType::String: {
1484 JSString* str = val.toString();
1485 MOZ_ASSERT(gc::IsCellPointerValid(str));
1486 return mozilla::Some(f(str));
1488 #ifdef ENABLE_RECORD_TUPLE
1489 case JS::ValueType::ExtendedPrimitive:
1490 #endif
1491 case JS::ValueType::Object: {
1492 JSObject* obj = &val.getObjectPayload();
1493 MOZ_ASSERT(gc::IsCellPointerValid(obj));
1494 return mozilla::Some(f(obj));
1496 case JS::ValueType::Symbol: {
1497 JS::Symbol* sym = val.toSymbol();
1498 MOZ_ASSERT(gc::IsCellPointerValid(sym));
1499 return mozilla::Some(f(sym));
1501 case JS::ValueType::BigInt: {
1502 JS::BigInt* bi = val.toBigInt();
1503 MOZ_ASSERT(gc::IsCellPointerValid(bi));
1504 return mozilla::Some(f(bi));
1506 case JS::ValueType::PrivateGCThing: {
1507 MOZ_ASSERT(gc::IsCellPointerValid(val.toGCThing()));
1508 return mozilla::Some(MapGCThingTyped(val.toGCCellPtr(), std::move(f)));
1510 case JS::ValueType::Double:
1511 case JS::ValueType::Int32:
1512 case JS::ValueType::Boolean:
1513 case JS::ValueType::Undefined:
1514 case JS::ValueType::Null:
1515 case JS::ValueType::Magic: {
1516 MOZ_ASSERT(!val.isGCThing());
1517 using ReturnType = decltype(f(static_cast<JSObject*>(nullptr)));
1518 return mozilla::Maybe<ReturnType>();
1522 ReportBadValueTypeAndCrash(val);
1525 // If the Value is a GC pointer type, call |f| with the pointer cast to that
1526 // type. Return whether this happened.
1527 template <typename F>
1528 bool ApplyGCThingTyped(const JS::Value& val, F&& f) {
1529 return MapGCThingTyped(val,
1530 [&f](auto t) {
1531 f(t);
1532 return true;
1534 .isSome();
1537 static inline JS::Value PoisonedObjectValue(uintptr_t poison) {
1538 JS::Value v;
1539 v.setObjectNoCheck(reinterpret_cast<JSObject*>(poison));
1540 return v;
1543 } // namespace js
1545 #ifdef DEBUG
1546 namespace JS {
1548 MOZ_ALWAYS_INLINE void AssertValueIsNotGray(const Value& value) {
1549 if (value.isGCThing()) {
1550 AssertCellIsNotGray(value.toGCThing());
1554 MOZ_ALWAYS_INLINE void AssertValueIsNotGray(const Heap<Value>& value) {
1555 AssertValueIsNotGray(value.unbarrieredGet());
1558 } // namespace JS
1559 #endif
1561 /************************************************************************/
1563 namespace JS {
1565 extern JS_PUBLIC_DATA const HandleValue NullHandleValue;
1566 extern JS_PUBLIC_DATA const HandleValue UndefinedHandleValue;
1567 extern JS_PUBLIC_DATA const HandleValue TrueHandleValue;
1568 extern JS_PUBLIC_DATA const HandleValue FalseHandleValue;
1569 extern JS_PUBLIC_DATA const Handle<mozilla::Maybe<Value>> NothingHandleValue;
1571 } // namespace JS
1573 #endif /* js_Value_h */