Bug 1732010 [wpt PR 30847] - Fix incorrect svg-document-styles tests., a=testonly
[gecko.git] / js / public / Value.h
blobfa6d8f6123e28365f4bc489f90521aedc728d4a4
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 */
20 #include "jstypes.h"
22 #include "js/HeapAPI.h"
23 #include "js/RootingAPI.h"
24 #include "js/TypeDecls.h"
26 namespace JS {
27 class JS_PUBLIC_API Value;
30 /* JS::Value can store a full int32_t. */
31 #define JSVAL_INT_BITS 32
32 #define JSVAL_INT_MIN ((int32_t)0x80000000)
33 #define JSVAL_INT_MAX ((int32_t)0x7fffffff)
35 #if defined(JS_NUNBOX32)
36 # define JSVAL_TAG_SHIFT 32
37 #elif defined(JS_PUNBOX64)
38 # define JSVAL_TAG_SHIFT 47
39 #endif
41 // Use enums so that printing a JS::Value in the debugger shows nice
42 // symbolic type tags.
44 enum JSValueType : uint8_t {
45 JSVAL_TYPE_DOUBLE = 0x00,
46 JSVAL_TYPE_INT32 = 0x01,
47 JSVAL_TYPE_BOOLEAN = 0x02,
48 JSVAL_TYPE_UNDEFINED = 0x03,
49 JSVAL_TYPE_NULL = 0x04,
50 JSVAL_TYPE_MAGIC = 0x05,
51 JSVAL_TYPE_STRING = 0x06,
52 JSVAL_TYPE_SYMBOL = 0x07,
53 JSVAL_TYPE_PRIVATE_GCTHING = 0x08,
54 JSVAL_TYPE_BIGINT = 0x09,
55 JSVAL_TYPE_OBJECT = 0x0c,
57 // This type never appears in a Value; it's only an out-of-band value.
58 JSVAL_TYPE_UNKNOWN = 0x20
61 namespace JS {
62 enum class ValueType : uint8_t {
63 Double = JSVAL_TYPE_DOUBLE,
64 Int32 = JSVAL_TYPE_INT32,
65 Boolean = JSVAL_TYPE_BOOLEAN,
66 Undefined = JSVAL_TYPE_UNDEFINED,
67 Null = JSVAL_TYPE_NULL,
68 Magic = JSVAL_TYPE_MAGIC,
69 String = JSVAL_TYPE_STRING,
70 Symbol = JSVAL_TYPE_SYMBOL,
71 PrivateGCThing = JSVAL_TYPE_PRIVATE_GCTHING,
72 BigInt = JSVAL_TYPE_BIGINT,
73 Object = JSVAL_TYPE_OBJECT,
77 static_assert(sizeof(JSValueType) == 1,
78 "compiler typed enum support is apparently buggy");
80 #if defined(JS_NUNBOX32)
82 enum JSValueTag : uint32_t {
83 JSVAL_TAG_CLEAR = 0xFFFFFF80,
84 JSVAL_TAG_INT32 = JSVAL_TAG_CLEAR | JSVAL_TYPE_INT32,
85 JSVAL_TAG_UNDEFINED = JSVAL_TAG_CLEAR | JSVAL_TYPE_UNDEFINED,
86 JSVAL_TAG_NULL = JSVAL_TAG_CLEAR | JSVAL_TYPE_NULL,
87 JSVAL_TAG_BOOLEAN = JSVAL_TAG_CLEAR | JSVAL_TYPE_BOOLEAN,
88 JSVAL_TAG_MAGIC = JSVAL_TAG_CLEAR | JSVAL_TYPE_MAGIC,
89 JSVAL_TAG_STRING = JSVAL_TAG_CLEAR | JSVAL_TYPE_STRING,
90 JSVAL_TAG_SYMBOL = JSVAL_TAG_CLEAR | JSVAL_TYPE_SYMBOL,
91 JSVAL_TAG_PRIVATE_GCTHING = JSVAL_TAG_CLEAR | JSVAL_TYPE_PRIVATE_GCTHING,
92 JSVAL_TAG_BIGINT = JSVAL_TAG_CLEAR | JSVAL_TYPE_BIGINT,
93 JSVAL_TAG_OBJECT = JSVAL_TAG_CLEAR | JSVAL_TYPE_OBJECT
96 static_assert(sizeof(JSValueTag) == sizeof(uint32_t),
97 "compiler typed enum support is apparently buggy");
99 #elif defined(JS_PUNBOX64)
101 enum JSValueTag : uint32_t {
102 JSVAL_TAG_MAX_DOUBLE = 0x1FFF0,
103 JSVAL_TAG_INT32 = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32,
104 JSVAL_TAG_UNDEFINED = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_UNDEFINED,
105 JSVAL_TAG_NULL = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_NULL,
106 JSVAL_TAG_BOOLEAN = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN,
107 JSVAL_TAG_MAGIC = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_MAGIC,
108 JSVAL_TAG_STRING = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_STRING,
109 JSVAL_TAG_SYMBOL = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_SYMBOL,
110 JSVAL_TAG_PRIVATE_GCTHING = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_PRIVATE_GCTHING,
111 JSVAL_TAG_BIGINT = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BIGINT,
112 JSVAL_TAG_OBJECT = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_OBJECT
115 static_assert(sizeof(JSValueTag) == sizeof(uint32_t),
116 "compiler typed enum support is apparently buggy");
118 enum JSValueShiftedTag : uint64_t {
119 // See Bug 584653 for why we include 0xFFFFFFFF.
120 JSVAL_SHIFTED_TAG_MAX_DOUBLE =
121 ((uint64_t(JSVAL_TAG_MAX_DOUBLE) << JSVAL_TAG_SHIFT) | 0xFFFFFFFF),
122 JSVAL_SHIFTED_TAG_INT32 = (uint64_t(JSVAL_TAG_INT32) << JSVAL_TAG_SHIFT),
123 JSVAL_SHIFTED_TAG_UNDEFINED =
124 (uint64_t(JSVAL_TAG_UNDEFINED) << JSVAL_TAG_SHIFT),
125 JSVAL_SHIFTED_TAG_NULL = (uint64_t(JSVAL_TAG_NULL) << JSVAL_TAG_SHIFT),
126 JSVAL_SHIFTED_TAG_BOOLEAN = (uint64_t(JSVAL_TAG_BOOLEAN) << JSVAL_TAG_SHIFT),
127 JSVAL_SHIFTED_TAG_MAGIC = (uint64_t(JSVAL_TAG_MAGIC) << JSVAL_TAG_SHIFT),
128 JSVAL_SHIFTED_TAG_STRING = (uint64_t(JSVAL_TAG_STRING) << JSVAL_TAG_SHIFT),
129 JSVAL_SHIFTED_TAG_SYMBOL = (uint64_t(JSVAL_TAG_SYMBOL) << JSVAL_TAG_SHIFT),
130 JSVAL_SHIFTED_TAG_PRIVATE_GCTHING =
131 (uint64_t(JSVAL_TAG_PRIVATE_GCTHING) << JSVAL_TAG_SHIFT),
132 JSVAL_SHIFTED_TAG_BIGINT = (uint64_t(JSVAL_TAG_BIGINT) << JSVAL_TAG_SHIFT),
133 JSVAL_SHIFTED_TAG_OBJECT = (uint64_t(JSVAL_TAG_OBJECT) << JSVAL_TAG_SHIFT)
136 static_assert(sizeof(JSValueShiftedTag) == sizeof(uint64_t),
137 "compiler typed enum support is apparently buggy");
139 #endif
141 namespace JS {
142 namespace detail {
144 #if defined(JS_NUNBOX32)
146 constexpr JSValueTag ValueTypeToTag(JSValueType type) {
147 return static_cast<JSValueTag>(JSVAL_TAG_CLEAR | type);
150 constexpr bool ValueIsDouble(uint64_t bits) {
151 return uint32_t(bits >> JSVAL_TAG_SHIFT) <= uint32_t(JSVAL_TAG_CLEAR);
154 constexpr JSValueTag ValueUpperExclPrimitiveTag = JSVAL_TAG_OBJECT;
155 constexpr JSValueTag ValueUpperInclNumberTag = JSVAL_TAG_INT32;
156 constexpr JSValueTag ValueLowerInclGCThingTag = JSVAL_TAG_STRING;
158 #elif defined(JS_PUNBOX64)
160 constexpr JSValueTag ValueTypeToTag(JSValueType type) {
161 return static_cast<JSValueTag>(JSVAL_TAG_MAX_DOUBLE | type);
164 constexpr bool ValueIsDouble(uint64_t bits) {
165 return bits <= JSVAL_SHIFTED_TAG_MAX_DOUBLE;
168 constexpr uint64_t ValueTagMask = 0xFFFF'8000'0000'0000;
170 // This should only be used in toGCThing. See the 'Spectre mitigations' comment.
171 constexpr uint64_t ValueGCThingPayloadMask = 0x0000'7FFF'FFFF'FFFF;
173 constexpr uint64_t ValueTypeToShiftedTag(JSValueType type) {
174 return static_cast<uint64_t>(ValueTypeToTag(type)) << JSVAL_TAG_SHIFT;
176 # define JSVAL_TYPE_TO_SHIFTED_TAG(type) \
177 (JS::detail::ValueTypeToShiftedTag(type))
179 constexpr JSValueTag ValueUpperExclPrimitiveTag = JSVAL_TAG_OBJECT;
180 constexpr JSValueTag ValueUpperInclNumberTag = JSVAL_TAG_INT32;
181 constexpr JSValueTag ValueLowerInclGCThingTag = JSVAL_TAG_STRING;
183 constexpr uint64_t ValueUpperExclShiftedPrimitiveTag = JSVAL_SHIFTED_TAG_OBJECT;
184 constexpr uint64_t ValueUpperExclShiftedNumberTag = JSVAL_SHIFTED_TAG_BOOLEAN;
185 constexpr uint64_t ValueLowerInclShiftedGCThingTag = JSVAL_SHIFTED_TAG_STRING;
187 // JSVAL_TYPE_OBJECT and JSVAL_TYPE_NULL differ by one bit. We can use this to
188 // implement toObjectOrNull more efficiently.
189 constexpr uint64_t ValueObjectOrNullBit = 0x8ULL << JSVAL_TAG_SHIFT;
190 static_assert(
191 (JSVAL_SHIFTED_TAG_NULL ^ JSVAL_SHIFTED_TAG_OBJECT) == ValueObjectOrNullBit,
192 "ValueObjectOrNullBit must be consistent with object and null tags");
194 constexpr uint64_t IsValidUserModePointer(uint64_t bits) {
195 // All 64-bit platforms that we support actually have a 48-bit address space
196 // for user-mode pointers, with the top 16 bits all set to zero.
197 return (bits & 0xFFFF'0000'0000'0000) == 0;
200 #endif /* JS_PUNBOX64 */
202 } // namespace detail
203 } // namespace JS
205 #define JSVAL_TYPE_TO_TAG(type) (JS::detail::ValueTypeToTag(type))
207 enum JSWhyMagic {
208 /** a hole in a native object's elements */
209 JS_ELEMENTS_HOLE,
211 /** there is not a pending iterator value */
212 JS_NO_ITER_VALUE,
214 /** exception value thrown when closing a generator */
215 JS_GENERATOR_CLOSING,
217 /** used in debug builds to catch tracing errors */
218 JS_ARG_POISON,
220 /** an empty subnode in the AST serializer */
221 JS_SERIALIZE_NO_NODE,
223 /** magic value passed to natives to indicate construction */
224 JS_IS_CONSTRUCTING,
226 /** see class js::HashableValue */
227 JS_HASH_KEY_EMPTY,
229 /** error while running Ion code */
230 JS_ION_ERROR,
232 /** missing recover instruction result */
233 JS_ION_BAILOUT,
235 /** optimized out slot */
236 JS_OPTIMIZED_OUT,
238 /** uninitialized lexical bindings that produce ReferenceError on touch. */
239 JS_UNINITIALIZED_LEXICAL,
241 /** arguments object can't be created because environment is dead. */
242 JS_MISSING_ARGUMENTS,
244 /** for local use */
245 JS_GENERIC_MAGIC,
248 * Write records queued up in WritableStreamDefaultController.[[queue]] in the
249 * spec are either "close" (a String) or Record { [[chunk]]: chunk }, where
250 * chunk is an arbitrary user-provided (and therefore non-magic) value.
251 * Represent "close" the String as this magic value; represent Record records
252 * as the |chunk| value within each of them.
254 JS_WRITABLESTREAM_CLOSE_RECORD,
257 * The ReadableStream pipe-to operation concludes with a "finalize" operation
258 * that accepts an optional |error| argument. In certain cases that optional
259 * |error| must be stored in a handler function, for use after a promise has
260 * settled. We represent the argument not being provided, in those cases,
261 * using this magic value.
263 JS_READABLESTREAM_PIPETO_FINALIZE_WITHOUT_ERROR,
266 * When an error object is created without the error cause argument, we set
267 * the error's cause slot to this magic value.
269 JS_ERROR_WITHOUT_CAUSE,
271 JS_WHY_MAGIC_COUNT
274 namespace js {
275 static inline JS::Value PoisonedObjectValue(uintptr_t poison);
276 } // namespace js
278 namespace JS {
280 namespace detail {
282 // IEEE-754 bit pattern for double-precision positive infinity.
283 constexpr int InfinitySignBit = 0;
284 constexpr uint64_t InfinityBits =
285 mozilla::InfinityBits<double, detail::InfinitySignBit>::value;
287 // This is a quiet NaN on IEEE-754[2008] compatible platforms, including X86,
288 // ARM, SPARC and modern MIPS.
290 // Note: The default sign bit for a hardware sythesized NaN differs between X86
291 // and ARM. Both values are considered compatible values on both
292 // platforms.
293 constexpr int CanonicalizedNaNSignBit = 0;
294 constexpr uint64_t CanonicalizedNaNSignificand = 0x8000000000000;
296 #if defined(__sparc__)
297 // Some architectures (not to name names) generate NaNs with bit patterns that
298 // are incompatible with JS::Value's bit pattern restrictions. Instead we must
299 // canonicalize all hardware values before storing in JS::Value.
300 # define JS_NONCANONICAL_HARDWARE_NAN
301 #endif
303 #if defined(__mips__) && !defined(__mips_nan_2008)
304 // These builds may run on hardware that has differing polarity of the signaling
305 // NaN bit. While the kernel may handle the trap for us, it is a performance
306 // issue so instead we compute the NaN to use on startup. The runtime value must
307 // still meet `ValueIsDouble` requirements which are checked on startup.
309 // In particular, we expect one of the following values on MIPS:
310 // - 0x7FF7FFFFFFFFFFFF Legacy
311 // - 0x7FF8000000000000 IEEE-754[2008]
312 # define JS_RUNTIME_CANONICAL_NAN
313 #endif
315 #if defined(JS_RUNTIME_CANONICAL_NAN)
316 extern uint64_t CanonicalizedNaNBits;
317 #else
318 constexpr uint64_t CanonicalizedNaNBits =
319 mozilla::SpecificNaNBits<double, detail::CanonicalizedNaNSignBit,
320 detail::CanonicalizedNaNSignificand>::value;
321 #endif
322 } // namespace detail
324 // Return a quiet NaN that is compatible with JS::Value restrictions.
325 static MOZ_ALWAYS_INLINE double GenericNaN() {
326 #if !defined(JS_RUNTIME_CANONICAL_NAN)
327 static_assert(detail::ValueIsDouble(detail::CanonicalizedNaNBits),
328 "Canonical NaN must be compatible with JS::Value");
329 #endif
331 return mozilla::BitwiseCast<double>(detail::CanonicalizedNaNBits);
334 // Convert an arbitrary double to one compatible with JS::Value representation
335 // by replacing any NaN value with a canonical one.
336 static MOZ_ALWAYS_INLINE double CanonicalizeNaN(double d) {
337 if (MOZ_UNLIKELY(mozilla::IsNaN(d))) {
338 return GenericNaN();
340 return d;
344 * [SMDOC] JS::Value type
346 * JS::Value is the interface for a single JavaScript Engine value. A few
347 * general notes on JS::Value:
349 * - JS::Value has setX() and isX() members for X in
351 * { Int32, Double, String, Symbol, BigInt, Boolean, Undefined, Null,
352 * Object, Magic }
354 * JS::Value also contains toX() for each of the non-singleton types.
356 * - Magic is a singleton type whose payload contains either a JSWhyMagic
357 * "reason" for the magic value or a uint32_t value. By providing JSWhyMagic
358 * values when creating and checking for magic values, it is possible to
359 * assert, at runtime, that only magic values with the expected reason flow
360 * through a particular value. For example, if cx->exception has a magic
361 * value, the reason must be JS_GENERATOR_CLOSING.
363 * - The JS::Value operations are preferred. The JSVAL_* operations remain for
364 * compatibility; they may be removed at some point. These operations mostly
365 * provide similar functionality. But there are a few key differences. One
366 * is that JS::Value gives null a separate type.
367 * Also, to help prevent mistakenly boxing a nullable JSObject* as an object,
368 * Value::setObject takes a JSObject&. (Conversely, Value::toObject returns a
369 * JSObject&.) A convenience member Value::setObjectOrNull is provided.
371 * - Note that JS::Value is 8 bytes on 32 and 64-bit architectures. Thus, on
372 * 32-bit user code should avoid copying jsval/JS::Value as much as possible,
373 * preferring to pass by const Value&.
375 * Spectre mitigations
376 * ===================
377 * To mitigate Spectre attacks, we do the following:
379 * - On 64-bit platforms, when unboxing a Value, we XOR the bits with the
380 * expected type tag (instead of masking the payload bits). This guarantees
381 * that toString, toObject, toSymbol will return an invalid pointer (because
382 * some high bits will be set) when called on a Value with a different type
383 * tag.
385 * - On 32-bit platforms,when unboxing an object/string/symbol Value, we use a
386 * conditional move (not speculated) to zero the payload register if the type
387 * doesn't match.
389 class alignas(8) Value {
390 private:
391 uint64_t asBits_;
393 public:
394 constexpr Value() : asBits_(bitsFromTagAndPayload(JSVAL_TAG_UNDEFINED, 0)) {}
396 private:
397 explicit constexpr Value(uint64_t asBits) : asBits_(asBits) {}
399 static uint64_t bitsFromDouble(double d) {
400 #if defined(JS_NONCANONICAL_HARDWARE_NAN)
401 d = CanonicalizeNaN(d);
402 #endif
403 return mozilla::BitwiseCast<uint64_t>(d);
406 static_assert(sizeof(JSValueType) == 1,
407 "type bits must fit in a single byte");
408 static_assert(sizeof(JSValueTag) == 4,
409 "32-bit Value's tag_ must have size 4 to complement the "
410 "payload union's size 4");
411 static_assert(sizeof(JSWhyMagic) <= 4,
412 "32-bit Value's JSWhyMagic payload field must not inflate "
413 "the payload beyond 4 bytes");
415 public:
416 #if defined(JS_NUNBOX32)
417 using PayloadType = uint32_t;
418 #elif defined(JS_PUNBOX64)
419 using PayloadType = uint64_t;
420 #endif
422 static constexpr uint64_t bitsFromTagAndPayload(JSValueTag tag,
423 PayloadType payload) {
424 return (uint64_t(tag) << JSVAL_TAG_SHIFT) | payload;
427 static constexpr Value fromTagAndPayload(JSValueTag tag,
428 PayloadType payload) {
429 return fromRawBits(bitsFromTagAndPayload(tag, payload));
432 static constexpr Value fromRawBits(uint64_t asBits) { return Value(asBits); }
434 static constexpr Value fromInt32(int32_t i) {
435 return fromTagAndPayload(JSVAL_TAG_INT32, uint32_t(i));
438 static Value fromDouble(double d) { return fromRawBits(bitsFromDouble(d)); }
441 * Returns false if creating a NumberValue containing the given type would
442 * be lossy, true otherwise.
444 template <typename T>
445 static bool isNumberRepresentable(const T t) {
446 return T(double(t)) == t;
449 /*** Mutators ***/
451 void setNull() {
452 asBits_ = bitsFromTagAndPayload(JSVAL_TAG_NULL, 0);
453 MOZ_ASSERT(isNull());
456 void setUndefined() {
457 asBits_ = bitsFromTagAndPayload(JSVAL_TAG_UNDEFINED, 0);
458 MOZ_ASSERT(isUndefined());
461 void setInt32(int32_t i) {
462 asBits_ = bitsFromTagAndPayload(JSVAL_TAG_INT32, uint32_t(i));
463 MOZ_ASSERT(toInt32() == i);
466 void setDouble(double d) {
467 asBits_ = bitsFromDouble(d);
468 MOZ_ASSERT(isDouble());
471 void setString(JSString* str) {
472 MOZ_ASSERT(js::gc::IsCellPointerValid(str));
473 asBits_ = bitsFromTagAndPayload(JSVAL_TAG_STRING, PayloadType(str));
474 MOZ_ASSERT(toString() == str);
477 void setSymbol(JS::Symbol* sym) {
478 MOZ_ASSERT(js::gc::IsCellPointerValid(sym));
479 asBits_ = bitsFromTagAndPayload(JSVAL_TAG_SYMBOL, PayloadType(sym));
480 MOZ_ASSERT(toSymbol() == sym);
483 void setBigInt(JS::BigInt* bi) {
484 MOZ_ASSERT(js::gc::IsCellPointerValid(bi));
485 asBits_ = bitsFromTagAndPayload(JSVAL_TAG_BIGINT, PayloadType(bi));
486 MOZ_ASSERT(toBigInt() == bi);
489 void setObject(JSObject& obj) {
490 MOZ_ASSERT(js::gc::IsCellPointerValid(&obj));
491 setObjectNoCheck(&obj);
492 MOZ_ASSERT(&toObject() == &obj);
495 private:
496 void setObjectNoCheck(JSObject* obj) {
497 asBits_ = bitsFromTagAndPayload(JSVAL_TAG_OBJECT, PayloadType(obj));
500 friend inline Value js::PoisonedObjectValue(uintptr_t poison);
502 public:
503 void setBoolean(bool b) {
504 asBits_ = bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN, uint32_t(b));
505 MOZ_ASSERT(toBoolean() == b);
508 void setMagic(JSWhyMagic why) {
509 asBits_ = bitsFromTagAndPayload(JSVAL_TAG_MAGIC, uint32_t(why));
510 MOZ_ASSERT(whyMagic() == why);
513 void setMagicUint32(uint32_t payload) {
514 MOZ_ASSERT(payload >= JS_WHY_MAGIC_COUNT,
515 "This should only be used for non-standard magic values");
516 asBits_ = bitsFromTagAndPayload(JSVAL_TAG_MAGIC, payload);
517 MOZ_ASSERT(magicUint32() == payload);
520 void setNumber(float f) {
521 int32_t i;
522 if (mozilla::NumberIsInt32(f, &i)) {
523 setInt32(i);
524 return;
527 setDouble(double(f));
530 void setNumber(double d) {
531 int32_t i;
532 if (mozilla::NumberIsInt32(d, &i)) {
533 setInt32(i);
534 return;
537 setDouble(d);
540 template <typename T>
541 void setNumber(const T t) {
542 static_assert(std::is_integral<T>::value, "must be integral type");
543 MOZ_ASSERT(isNumberRepresentable(t), "value creation would be lossy");
545 if constexpr (std::numeric_limits<T>::is_signed) {
546 if constexpr (sizeof(t) <= sizeof(int32_t)) {
547 setInt32(int32_t(t));
548 } else {
549 if (JSVAL_INT_MIN <= t && t <= JSVAL_INT_MAX) {
550 setInt32(int32_t(t));
551 } else {
552 setDouble(double(t));
555 } else {
556 if constexpr (sizeof(t) <= sizeof(uint16_t)) {
557 setInt32(int32_t(t));
558 } else {
559 if (t <= JSVAL_INT_MAX) {
560 setInt32(int32_t(t));
561 } else {
562 setDouble(double(t));
568 void setObjectOrNull(JSObject* arg) {
569 if (arg) {
570 setObject(*arg);
571 } else {
572 setNull();
576 void swap(Value& rhs) {
577 uint64_t tmp = rhs.asBits_;
578 rhs.asBits_ = asBits_;
579 asBits_ = tmp;
582 private:
583 JSValueTag toTag() const { return JSValueTag(asBits_ >> JSVAL_TAG_SHIFT); }
585 template <typename T, JSValueTag Tag>
586 T* unboxGCPointer() const {
587 MOZ_ASSERT((asBits_ & js::gc::CellAlignMask) == 0,
588 "GC pointer is not aligned. Is this memory corruption?");
589 #if defined(JS_NUNBOX32)
590 uintptr_t payload = uint32_t(asBits_);
591 return reinterpret_cast<T*>(payload);
592 #elif defined(JS_PUNBOX64)
593 // Note: the 'Spectre mitigations' comment at the top of this class
594 // explains why we use XOR here.
595 constexpr uint64_t shiftedTag = uint64_t(Tag) << JSVAL_TAG_SHIFT;
596 return reinterpret_cast<T*>(uintptr_t(asBits_ ^ shiftedTag));
597 #endif
600 public:
601 /*** JIT-only interfaces to interact with and create raw Values ***/
602 #if defined(JS_NUNBOX32)
603 PayloadType toNunboxPayload() const { return uint32_t(asBits_); }
605 JSValueTag toNunboxTag() const { return toTag(); }
606 #elif defined(JS_PUNBOX64)
607 const void* bitsAsPunboxPointer() const {
608 return reinterpret_cast<void*>(asBits_);
610 #endif
612 /*** Value type queries ***/
615 * N.B. GCC, in some but not all cases, chooses to emit signed comparison
616 * of JSValueTag even though its underlying type has been forced to be
617 * uint32_t. Thus, all comparisons should explicitly cast operands to
618 * uint32_t.
621 bool isUndefined() const {
622 #if defined(JS_NUNBOX32)
623 return toTag() == JSVAL_TAG_UNDEFINED;
624 #elif defined(JS_PUNBOX64)
625 return asBits_ == JSVAL_SHIFTED_TAG_UNDEFINED;
626 #endif
629 bool isNull() const {
630 #if defined(JS_NUNBOX32)
631 return toTag() == JSVAL_TAG_NULL;
632 #elif defined(JS_PUNBOX64)
633 return asBits_ == JSVAL_SHIFTED_TAG_NULL;
634 #endif
637 bool isNullOrUndefined() const { return isNull() || isUndefined(); }
639 bool isInt32() const { return toTag() == JSVAL_TAG_INT32; }
641 bool isInt32(int32_t i32) const {
642 return asBits_ == bitsFromTagAndPayload(JSVAL_TAG_INT32, uint32_t(i32));
645 bool isDouble() const { return detail::ValueIsDouble(asBits_); }
647 bool isNumber() const {
648 #if defined(JS_NUNBOX32)
649 MOZ_ASSERT(toTag() != JSVAL_TAG_CLEAR);
650 return uint32_t(toTag()) <= uint32_t(detail::ValueUpperInclNumberTag);
651 #elif defined(JS_PUNBOX64)
652 return asBits_ < detail::ValueUpperExclShiftedNumberTag;
653 #endif
656 bool isString() const { return toTag() == JSVAL_TAG_STRING; }
658 bool isSymbol() const { return toTag() == JSVAL_TAG_SYMBOL; }
660 bool isBigInt() const { return toTag() == JSVAL_TAG_BIGINT; }
662 bool isObject() const {
663 #if defined(JS_NUNBOX32)
664 return toTag() == JSVAL_TAG_OBJECT;
665 #elif defined(JS_PUNBOX64)
666 MOZ_ASSERT((asBits_ >> JSVAL_TAG_SHIFT) <= JSVAL_TAG_OBJECT);
667 return asBits_ >= JSVAL_SHIFTED_TAG_OBJECT;
668 #endif
671 bool isPrimitive() const {
672 #if defined(JS_NUNBOX32)
673 return uint32_t(toTag()) < uint32_t(detail::ValueUpperExclPrimitiveTag);
674 #elif defined(JS_PUNBOX64)
675 return asBits_ < detail::ValueUpperExclShiftedPrimitiveTag;
676 #endif
679 bool isObjectOrNull() const { return isObject() || isNull(); }
681 bool isNumeric() const { return isNumber() || isBigInt(); }
683 bool isGCThing() const {
684 #if defined(JS_NUNBOX32)
685 /* gcc sometimes generates signed < without explicit casts. */
686 return uint32_t(toTag()) >= uint32_t(detail::ValueLowerInclGCThingTag);
687 #elif defined(JS_PUNBOX64)
688 return asBits_ >= detail::ValueLowerInclShiftedGCThingTag;
689 #endif
692 bool isBoolean() const { return toTag() == JSVAL_TAG_BOOLEAN; }
694 bool isTrue() const {
695 return asBits_ == bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN, uint32_t(true));
698 bool isFalse() const {
699 return asBits_ == bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN, uint32_t(false));
702 bool isMagic() const { return toTag() == JSVAL_TAG_MAGIC; }
704 bool isMagic(JSWhyMagic why) const {
705 if (!isMagic()) {
706 return false;
708 MOZ_RELEASE_ASSERT(whyMagic() == why);
709 return true;
712 JS::TraceKind traceKind() const {
713 MOZ_ASSERT(isGCThing());
714 static_assert((JSVAL_TAG_STRING & 0x03) == size_t(JS::TraceKind::String),
715 "Value type tags must correspond with JS::TraceKinds.");
716 static_assert((JSVAL_TAG_SYMBOL & 0x03) == size_t(JS::TraceKind::Symbol),
717 "Value type tags must correspond with JS::TraceKinds.");
718 static_assert((JSVAL_TAG_OBJECT & 0x03) == size_t(JS::TraceKind::Object),
719 "Value type tags must correspond with JS::TraceKinds.");
720 static_assert((JSVAL_TAG_BIGINT & 0x03) == size_t(JS::TraceKind::BigInt),
721 "Value type tags must correspond with JS::TraceKinds.");
722 if (MOZ_UNLIKELY(isPrivateGCThing())) {
723 return JS::GCThingTraceKind(toGCThing());
725 return JS::TraceKind(toTag() & 0x03);
728 JSWhyMagic whyMagic() const {
729 MOZ_ASSERT(magicUint32() < JS_WHY_MAGIC_COUNT);
730 return static_cast<JSWhyMagic>(magicUint32());
733 uint32_t magicUint32() const {
734 MOZ_ASSERT(isMagic());
735 return uint32_t(asBits_);
738 /*** Comparison ***/
740 bool operator==(const Value& rhs) const { return asBits_ == rhs.asBits_; }
742 bool operator!=(const Value& rhs) const { return asBits_ != rhs.asBits_; }
744 friend inline bool SameType(const Value& lhs, const Value& rhs);
746 /*** Extract the value's typed payload ***/
748 int32_t toInt32() const {
749 MOZ_ASSERT(isInt32());
750 return int32_t(asBits_);
753 double toDouble() const {
754 MOZ_ASSERT(isDouble());
755 return mozilla::BitwiseCast<double>(asBits_);
758 double toNumber() const {
759 MOZ_ASSERT(isNumber());
760 return isDouble() ? toDouble() : double(toInt32());
763 JSString* toString() const {
764 MOZ_ASSERT(isString());
765 return unboxGCPointer<JSString, JSVAL_TAG_STRING>();
768 JS::Symbol* toSymbol() const {
769 MOZ_ASSERT(isSymbol());
770 return unboxGCPointer<JS::Symbol, JSVAL_TAG_SYMBOL>();
773 JS::BigInt* toBigInt() const {
774 MOZ_ASSERT(isBigInt());
775 return unboxGCPointer<JS::BigInt, JSVAL_TAG_BIGINT>();
778 JSObject& toObject() const {
779 MOZ_ASSERT(isObject());
780 #if defined(JS_PUNBOX64)
781 MOZ_ASSERT((asBits_ & detail::ValueGCThingPayloadMask) != 0);
782 #endif
783 return *unboxGCPointer<JSObject, JSVAL_TAG_OBJECT>();
786 JSObject* toObjectOrNull() const {
787 MOZ_ASSERT(isObjectOrNull());
788 #if defined(JS_NUNBOX32)
789 return reinterpret_cast<JSObject*>(uintptr_t(asBits_));
790 #elif defined(JS_PUNBOX64)
791 // Note: the 'Spectre mitigations' comment at the top of this class
792 // explains why we use XOR here and in other to* methods.
793 uint64_t ptrBits =
794 (asBits_ ^ JSVAL_SHIFTED_TAG_OBJECT) & ~detail::ValueObjectOrNullBit;
795 MOZ_ASSERT((ptrBits & 0x7) == 0);
796 return reinterpret_cast<JSObject*>(ptrBits);
797 #endif
800 js::gc::Cell* toGCThing() const {
801 MOZ_ASSERT(isGCThing());
802 #if defined(JS_NUNBOX32)
803 return reinterpret_cast<js::gc::Cell*>(uintptr_t(asBits_));
804 #elif defined(JS_PUNBOX64)
805 uint64_t ptrBits = asBits_ & detail::ValueGCThingPayloadMask;
806 MOZ_ASSERT((ptrBits & 0x7) == 0);
807 return reinterpret_cast<js::gc::Cell*>(ptrBits);
808 #endif
811 GCCellPtr toGCCellPtr() const { return GCCellPtr(toGCThing(), traceKind()); }
813 bool toBoolean() const {
814 MOZ_ASSERT(isBoolean());
815 #if defined(JS_NUNBOX32)
816 return bool(toNunboxPayload());
817 #elif defined(JS_PUNBOX64)
818 return bool(asBits_ & 0x1);
819 #endif
822 uint32_t payloadAsRawUint32() const {
823 MOZ_ASSERT(!isDouble());
824 return uint32_t(asBits_);
827 constexpr uint64_t asRawBits() const { return asBits_; }
829 JSValueType extractNonDoubleType() const {
830 uint32_t type = toTag() & 0xF;
831 MOZ_ASSERT(type > JSVAL_TYPE_DOUBLE);
832 return JSValueType(type);
835 JS::ValueType type() const {
836 if (isDouble()) {
837 return JS::ValueType::Double;
840 JSValueType type = extractNonDoubleType();
841 MOZ_ASSERT(type <= JSVAL_TYPE_OBJECT);
842 return JS::ValueType(type);
846 * Private API
848 * Private setters/getters allow the caller to read/write arbitrary
849 * word-size pointers or uint32s. After storing to a value with
850 * setPrivateX, it is the caller's responsibility to only read using
851 * toPrivateX. Private values are given a type which ensures they
852 * aren't marked by the GC.
855 void setPrivate(void* ptr) {
856 #if defined(JS_PUNBOX64)
857 MOZ_ASSERT(detail::IsValidUserModePointer(uintptr_t(ptr)));
858 #endif
859 asBits_ = uintptr_t(ptr);
860 MOZ_ASSERT(isDouble());
863 void* toPrivate() const {
864 MOZ_ASSERT(isDouble());
865 #if defined(JS_PUNBOX64)
866 MOZ_ASSERT(detail::IsValidUserModePointer(asBits_));
867 #endif
868 return reinterpret_cast<void*>(uintptr_t(asBits_));
871 void setPrivateUint32(uint32_t ui) {
872 MOZ_ASSERT(uint32_t(int32_t(ui)) == ui);
873 setInt32(int32_t(ui));
876 uint32_t toPrivateUint32() const { return uint32_t(toInt32()); }
879 * Private GC Thing API
881 * Non-JSObject, JSString, and JS::Symbol cells may be put into the 64-bit
882 * payload as private GC things. Such Values are considered isGCThing(), and
883 * as such, automatically marked. Their traceKind() is gotten via their
884 * cells.
887 void setPrivateGCThing(js::gc::Cell* cell) {
888 MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::String,
889 "Private GC thing Values must not be strings. Make a "
890 "StringValue instead.");
891 MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Symbol,
892 "Private GC thing Values must not be symbols. Make a "
893 "SymbolValue instead.");
894 MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::BigInt,
895 "Private GC thing Values must not be BigInts. Make a "
896 "BigIntValue instead.");
897 MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Object,
898 "Private GC thing Values must not be objects. Make an "
899 "ObjectValue instead.");
901 MOZ_ASSERT(js::gc::IsCellPointerValid(cell));
902 #if defined(JS_PUNBOX64)
903 // VisualStudio cannot contain parenthesized C++ style cast and shift
904 // inside decltype in template parameter:
905 // AssertionConditionType<decltype((uintptr_t(x) >> 1))>
906 // It throws syntax error.
907 MOZ_ASSERT((((uintptr_t)cell) >> JSVAL_TAG_SHIFT) == 0);
908 #endif
909 asBits_ =
910 bitsFromTagAndPayload(JSVAL_TAG_PRIVATE_GCTHING, PayloadType(cell));
913 bool isPrivateGCThing() const { return toTag() == JSVAL_TAG_PRIVATE_GCTHING; }
914 } JS_HAZ_GC_POINTER MOZ_NON_PARAM;
916 static_assert(sizeof(Value) == 8,
917 "Value size must leave three tag bits, be a binary power, and "
918 "is ubiquitously depended upon everywhere");
920 static MOZ_ALWAYS_INLINE void ExposeValueToActiveJS(const Value& v) {
921 #ifdef DEBUG
922 Value tmp = v;
923 MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarrieredSlow(&tmp));
924 #endif
925 if (v.isGCThing()) {
926 js::gc::ExposeGCThingToActiveJS(v.toGCCellPtr());
930 /************************************************************************/
932 static inline MOZ_MAY_CALL_AFTER_MUST_RETURN Value NullValue() {
933 Value v;
934 v.setNull();
935 return v;
938 static constexpr Value UndefinedValue() { return Value(); }
940 static constexpr Value Int32Value(int32_t i32) { return Value::fromInt32(i32); }
942 static inline Value DoubleValue(double dbl) {
943 Value v;
944 v.setDouble(dbl);
945 return v;
948 static inline Value CanonicalizedDoubleValue(double d) {
949 return Value::fromDouble(CanonicalizeNaN(d));
952 static inline Value NaNValue() {
953 return Value::fromRawBits(detail::CanonicalizedNaNBits);
956 static inline Value InfinityValue() {
957 return Value::fromRawBits(detail::InfinityBits);
960 static inline Value Float32Value(float f) {
961 Value v;
962 v.setDouble(f);
963 return v;
966 static inline Value StringValue(JSString* str) {
967 Value v;
968 v.setString(str);
969 return v;
972 static inline Value SymbolValue(JS::Symbol* sym) {
973 Value v;
974 v.setSymbol(sym);
975 return v;
978 static inline Value BigIntValue(JS::BigInt* bi) {
979 Value v;
980 v.setBigInt(bi);
981 return v;
984 static inline Value BooleanValue(bool boo) {
985 Value v;
986 v.setBoolean(boo);
987 return v;
990 static inline Value TrueValue() {
991 Value v;
992 v.setBoolean(true);
993 return v;
996 static inline Value FalseValue() {
997 Value v;
998 v.setBoolean(false);
999 return v;
1002 static inline Value ObjectValue(JSObject& obj) {
1003 Value v;
1004 v.setObject(obj);
1005 return v;
1008 static inline Value MagicValue(JSWhyMagic why) {
1009 Value v;
1010 v.setMagic(why);
1011 return v;
1014 static inline Value MagicValueUint32(uint32_t payload) {
1015 Value v;
1016 v.setMagicUint32(payload);
1017 return v;
1020 static constexpr Value NumberValue(uint32_t i) {
1021 return i <= JSVAL_INT_MAX ? Int32Value(int32_t(i))
1022 : Value::fromDouble(double(i));
1025 template <typename T>
1026 static inline Value NumberValue(const T t) {
1027 Value v;
1028 v.setNumber(t);
1029 return v;
1032 static inline Value ObjectOrNullValue(JSObject* obj) {
1033 Value v;
1034 v.setObjectOrNull(obj);
1035 return v;
1038 static inline Value PrivateValue(void* ptr) {
1039 Value v;
1040 v.setPrivate(ptr);
1041 return v;
1044 static inline Value PrivateValue(uintptr_t ptr) {
1045 return PrivateValue(reinterpret_cast<void*>(ptr));
1048 static inline Value PrivateUint32Value(uint32_t ui) {
1049 Value v;
1050 v.setPrivateUint32(ui);
1051 return v;
1054 static inline Value PrivateGCThingValue(js::gc::Cell* cell) {
1055 Value v;
1056 v.setPrivateGCThing(cell);
1057 return v;
1060 inline bool SameType(const Value& lhs, const Value& rhs) {
1061 #if defined(JS_NUNBOX32)
1062 JSValueTag ltag = lhs.toTag(), rtag = rhs.toTag();
1063 return ltag == rtag || (ltag < JSVAL_TAG_CLEAR && rtag < JSVAL_TAG_CLEAR);
1064 #elif defined(JS_PUNBOX64)
1065 return (lhs.isDouble() && rhs.isDouble()) ||
1066 (((lhs.asBits_ ^ rhs.asBits_) & 0xFFFF800000000000ULL) == 0);
1067 #endif
1070 } // namespace JS
1072 /************************************************************************/
1074 namespace JS {
1075 JS_PUBLIC_API void HeapValuePostWriteBarrier(Value* valuep, const Value& prev,
1076 const Value& next);
1077 JS_PUBLIC_API void HeapValueWriteBarriers(Value* valuep, const Value& prev,
1078 const Value& next);
1080 template <>
1081 struct GCPolicy<JS::Value> {
1082 static void trace(JSTracer* trc, Value* v, const char* name) {
1083 // It's not safe to trace unbarriered pointers except as part of root
1084 // marking.
1085 UnsafeTraceRoot(trc, v, name);
1087 static bool isTenured(const Value& thing) {
1088 return !thing.isGCThing() || !IsInsideNursery(thing.toGCThing());
1090 static bool isValid(const Value& value) {
1091 return !value.isGCThing() || js::gc::IsCellPointerValid(value.toGCThing());
1095 } // namespace JS
1097 namespace js {
1099 template <>
1100 struct BarrierMethods<JS::Value> {
1101 static gc::Cell* asGCThingOrNull(const JS::Value& v) {
1102 return v.isGCThing() ? v.toGCThing() : nullptr;
1104 static void postWriteBarrier(JS::Value* v, const JS::Value& prev,
1105 const JS::Value& next) {
1106 JS::HeapValuePostWriteBarrier(v, prev, next);
1108 static void exposeToJS(const JS::Value& v) { JS::ExposeValueToActiveJS(v); }
1111 template <class Wrapper>
1112 class MutableValueOperations;
1115 * A class designed for CRTP use in implementing the non-mutating parts of the
1116 * Value interface in Value-like classes. Wrapper must be a class inheriting
1117 * ValueOperations<Wrapper> with a visible get() method returning a const
1118 * reference to the Value abstracted by Wrapper.
1120 template <class Wrapper>
1121 class WrappedPtrOperations<JS::Value, Wrapper> {
1122 const JS::Value& value() const {
1123 return static_cast<const Wrapper*>(this)->get();
1126 public:
1127 bool isUndefined() const { return value().isUndefined(); }
1128 bool isNull() const { return value().isNull(); }
1129 bool isBoolean() const { return value().isBoolean(); }
1130 bool isTrue() const { return value().isTrue(); }
1131 bool isFalse() const { return value().isFalse(); }
1132 bool isNumber() const { return value().isNumber(); }
1133 bool isInt32() const { return value().isInt32(); }
1134 bool isInt32(int32_t i32) const { return value().isInt32(i32); }
1135 bool isDouble() const { return value().isDouble(); }
1136 bool isString() const { return value().isString(); }
1137 bool isSymbol() const { return value().isSymbol(); }
1138 bool isBigInt() const { return value().isBigInt(); }
1139 bool isObject() const { return value().isObject(); }
1140 bool isMagic() const { return value().isMagic(); }
1141 bool isMagic(JSWhyMagic why) const { return value().isMagic(why); }
1142 bool isGCThing() const { return value().isGCThing(); }
1143 bool isPrivateGCThing() const { return value().isPrivateGCThing(); }
1144 bool isPrimitive() const { return value().isPrimitive(); }
1146 bool isNullOrUndefined() const { return value().isNullOrUndefined(); }
1147 bool isObjectOrNull() const { return value().isObjectOrNull(); }
1148 bool isNumeric() const { return value().isNumeric(); }
1150 bool toBoolean() const { return value().toBoolean(); }
1151 double toNumber() const { return value().toNumber(); }
1152 int32_t toInt32() const { return value().toInt32(); }
1153 double toDouble() const { return value().toDouble(); }
1154 JSString* toString() const { return value().toString(); }
1155 JS::Symbol* toSymbol() const { return value().toSymbol(); }
1156 JS::BigInt* toBigInt() const { return value().toBigInt(); }
1157 JSObject& toObject() const { return value().toObject(); }
1158 JSObject* toObjectOrNull() const { return value().toObjectOrNull(); }
1159 JS::GCCellPtr toGCCellPtr() const { return value().toGCCellPtr(); }
1160 gc::Cell* toGCThing() const { return value().toGCThing(); }
1161 JS::TraceKind traceKind() const { return value().traceKind(); }
1162 void* toPrivate() const { return value().toPrivate(); }
1163 uint32_t toPrivateUint32() const { return value().toPrivateUint32(); }
1165 uint64_t asRawBits() const { return value().asRawBits(); }
1166 JSValueType extractNonDoubleType() const {
1167 return value().extractNonDoubleType();
1169 JS::ValueType type() const { return value().type(); }
1171 JSWhyMagic whyMagic() const { return value().whyMagic(); }
1172 uint32_t magicUint32() const { return value().magicUint32(); }
1176 * A class designed for CRTP use in implementing all the mutating parts of the
1177 * Value interface in Value-like classes. Wrapper must be a class inheriting
1178 * MutableWrappedPtrOperations<Wrapper> with visible get() methods returning
1179 * const and non-const references to the Value abstracted by Wrapper.
1181 template <class Wrapper>
1182 class MutableWrappedPtrOperations<JS::Value, Wrapper>
1183 : public WrappedPtrOperations<JS::Value, Wrapper> {
1184 protected:
1185 void set(const JS::Value& v) {
1186 // Call Wrapper::set to trigger any barriers.
1187 static_cast<Wrapper*>(this)->set(v);
1190 public:
1191 void setNull() { set(JS::NullValue()); }
1192 void setUndefined() { set(JS::UndefinedValue()); }
1193 void setInt32(int32_t i) { set(JS::Int32Value(i)); }
1194 void setDouble(double d) { set(JS::DoubleValue(d)); }
1195 void setNaN() { set(JS::NaNValue()); }
1196 void setInfinity() { set(JS::InfinityValue()); }
1197 void setBoolean(bool b) { set(JS::BooleanValue(b)); }
1198 void setMagic(JSWhyMagic why) { set(JS::MagicValue(why)); }
1199 template <typename T>
1200 void setNumber(T t) {
1201 set(JS::NumberValue(t));
1203 void setString(JSString* str) { set(JS::StringValue(str)); }
1204 void setSymbol(JS::Symbol* sym) { set(JS::SymbolValue(sym)); }
1205 void setBigInt(JS::BigInt* bi) { set(JS::BigIntValue(bi)); }
1206 void setObject(JSObject& obj) { set(JS::ObjectValue(obj)); }
1207 void setObjectOrNull(JSObject* arg) { set(JS::ObjectOrNullValue(arg)); }
1208 void setPrivate(void* ptr) { set(JS::PrivateValue(ptr)); }
1209 void setPrivateUint32(uint32_t ui) { set(JS::PrivateUint32Value(ui)); }
1210 void setPrivateGCThing(js::gc::Cell* cell) {
1211 set(JS::PrivateGCThingValue(cell));
1216 * Augment the generic Heap<T> interface when T = Value with
1217 * type-querying, value-extracting, and mutating operations.
1219 template <typename Wrapper>
1220 class HeapOperations<JS::Value, Wrapper>
1221 : public MutableWrappedPtrOperations<JS::Value, Wrapper> {};
1223 MOZ_HAVE_NORETURN MOZ_COLD MOZ_NEVER_INLINE void ReportBadValueTypeAndCrash(
1224 const JS::Value& val);
1226 // If the Value is a GC pointer type, call |f| with the pointer cast to that
1227 // type and return the result wrapped in a Maybe, otherwise return None().
1228 template <typename F>
1229 auto MapGCThingTyped(const JS::Value& val, F&& f) {
1230 switch (val.type()) {
1231 case JS::ValueType::String: {
1232 JSString* str = val.toString();
1233 MOZ_ASSERT(gc::IsCellPointerValid(str));
1234 return mozilla::Some(f(str));
1236 case JS::ValueType::Object: {
1237 JSObject* obj = &val.toObject();
1238 MOZ_ASSERT(gc::IsCellPointerValid(obj));
1239 return mozilla::Some(f(obj));
1241 case JS::ValueType::Symbol: {
1242 JS::Symbol* sym = val.toSymbol();
1243 MOZ_ASSERT(gc::IsCellPointerValid(sym));
1244 return mozilla::Some(f(sym));
1246 case JS::ValueType::BigInt: {
1247 JS::BigInt* bi = val.toBigInt();
1248 MOZ_ASSERT(gc::IsCellPointerValid(bi));
1249 return mozilla::Some(f(bi));
1251 case JS::ValueType::PrivateGCThing: {
1252 MOZ_ASSERT(gc::IsCellPointerValid(val.toGCThing()));
1253 return mozilla::Some(MapGCThingTyped(val.toGCCellPtr(), std::move(f)));
1255 case JS::ValueType::Double:
1256 case JS::ValueType::Int32:
1257 case JS::ValueType::Boolean:
1258 case JS::ValueType::Undefined:
1259 case JS::ValueType::Null:
1260 case JS::ValueType::Magic: {
1261 MOZ_ASSERT(!val.isGCThing());
1262 using ReturnType = decltype(f(static_cast<JSObject*>(nullptr)));
1263 return mozilla::Maybe<ReturnType>();
1267 ReportBadValueTypeAndCrash(val);
1270 // If the Value is a GC pointer type, call |f| with the pointer cast to that
1271 // type. Return whether this happened.
1272 template <typename F>
1273 bool ApplyGCThingTyped(const JS::Value& val, F&& f) {
1274 return MapGCThingTyped(val,
1275 [&f](auto t) {
1276 f(t);
1277 return true;
1279 .isSome();
1282 static inline JS::Value PoisonedObjectValue(uintptr_t poison) {
1283 JS::Value v;
1284 v.setObjectNoCheck(reinterpret_cast<JSObject*>(poison));
1285 return v;
1288 } // namespace js
1290 #ifdef DEBUG
1291 namespace JS {
1293 MOZ_ALWAYS_INLINE void AssertValueIsNotGray(const Value& value) {
1294 if (value.isGCThing()) {
1295 AssertCellIsNotGray(value.toGCThing());
1299 MOZ_ALWAYS_INLINE void AssertValueIsNotGray(const Heap<Value>& value) {
1300 AssertValueIsNotGray(value.unbarrieredGet());
1303 } // namespace JS
1304 #endif
1306 /************************************************************************/
1308 namespace JS {
1310 extern JS_PUBLIC_DATA const HandleValue NullHandleValue;
1311 extern JS_PUBLIC_DATA const HandleValue UndefinedHandleValue;
1312 extern JS_PUBLIC_DATA const HandleValue TrueHandleValue;
1313 extern JS_PUBLIC_DATA const HandleValue FalseHandleValue;
1314 extern JS_PUBLIC_DATA const Handle<mozilla::Maybe<Value>> NothingHandleValue;
1316 } // namespace JS
1318 #endif /* js_Value_h */