Value.h (48668B)
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/. */ 6 7 /* JS::Value implementation. */ 8 9 #ifndef js_Value_h 10 #define js_Value_h 11 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" 17 18 #include <limits> /* for std::numeric_limits */ 19 #include <type_traits> 20 21 #include "jstypes.h" 22 23 #include "js/HeapAPI.h" 24 #include "js/RootingAPI.h" 25 #include "js/TypeDecls.h" 26 27 namespace JS { 28 class JS_PUBLIC_API Value; 29 } 30 31 // [SMDOC] JS::Value Boxing Formats 32 // 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) 39 // 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. 44 // 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 ? 48 // 49 // This is possible for two reasons: 50 // 51 // - First, ECMAScript implementations aren't required to distinguish all 52 // the values the IEEE 64-bit format can represent. 53 // 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. 60 // 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. 67 // 68 // Our memory initialization system ensures that all pointers we will store in 69 // objects use only 47 bits. See js::gc::MapAlignedPagesRandom. 70 // 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. 74 // 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.) 80 // 81 // Thus, we define JS::Value representations in terms of the IEEE 64-bit 82 // floating-point format: 83 // 84 // - Any bitstring that IEEE calls a number or an infinity represents that 85 // ECMAScript number. 86 // 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. 94 // 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. 99 // 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. 106 // 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. 111 // 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. 115 // 116 // Tag values are carefully ordered to support a set of micro-optimizations. In 117 // particular: 118 // 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.) 142 // 143 // * But see JS_NONCANONICAL_HARDWARE_NAN below. 144 // 145 // [1]: 146 // https://wingolog.org/archives/2011/05/18/value-representation-in-javascript-implementations#969f63bbe4eb912778c9da85feb0f5763e7a7862 147 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) 152 153 #if defined(JS_NUNBOX32) 154 # define JSVAL_TAG_SHIFT 32 155 #elif defined(JS_PUNBOX64) 156 # define JSVAL_TAG_SHIFT 47 157 #endif 158 159 // Use enums so that printing a JS::Value in the debugger shows nice 160 // symbolic type tags. 161 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 JSVAL_TYPE_OBJECT = 0x0c, 174 175 // This type never appears in a Value; it's only an out-of-band value. 176 JSVAL_TYPE_UNKNOWN = 0x20 177 }; 178 179 namespace JS { 180 enum class ValueType : uint8_t { 181 Double = JSVAL_TYPE_DOUBLE, 182 Int32 = JSVAL_TYPE_INT32, 183 Boolean = JSVAL_TYPE_BOOLEAN, 184 Undefined = JSVAL_TYPE_UNDEFINED, 185 Null = JSVAL_TYPE_NULL, 186 Magic = JSVAL_TYPE_MAGIC, 187 String = JSVAL_TYPE_STRING, 188 Symbol = JSVAL_TYPE_SYMBOL, 189 PrivateGCThing = JSVAL_TYPE_PRIVATE_GCTHING, 190 BigInt = JSVAL_TYPE_BIGINT, 191 Object = JSVAL_TYPE_OBJECT, 192 }; 193 } // namespace JS 194 195 static_assert(sizeof(JSValueType) == 1, 196 "compiler typed enum support is apparently buggy"); 197 198 #if defined(JS_NUNBOX32) 199 200 enum JSValueTag : uint32_t { 201 JSVAL_TAG_CLEAR = 0xFFFFFF80, 202 JSVAL_TAG_INT32 = JSVAL_TAG_CLEAR | JSVAL_TYPE_INT32, 203 JSVAL_TAG_UNDEFINED = JSVAL_TAG_CLEAR | JSVAL_TYPE_UNDEFINED, 204 JSVAL_TAG_NULL = JSVAL_TAG_CLEAR | JSVAL_TYPE_NULL, 205 JSVAL_TAG_BOOLEAN = JSVAL_TAG_CLEAR | JSVAL_TYPE_BOOLEAN, 206 JSVAL_TAG_MAGIC = JSVAL_TAG_CLEAR | JSVAL_TYPE_MAGIC, 207 JSVAL_TAG_STRING = JSVAL_TAG_CLEAR | JSVAL_TYPE_STRING, 208 JSVAL_TAG_SYMBOL = JSVAL_TAG_CLEAR | JSVAL_TYPE_SYMBOL, 209 JSVAL_TAG_PRIVATE_GCTHING = JSVAL_TAG_CLEAR | JSVAL_TYPE_PRIVATE_GCTHING, 210 JSVAL_TAG_BIGINT = JSVAL_TAG_CLEAR | JSVAL_TYPE_BIGINT, 211 JSVAL_TAG_OBJECT = JSVAL_TAG_CLEAR | JSVAL_TYPE_OBJECT 212 }; 213 214 static_assert(sizeof(JSValueTag) == sizeof(uint32_t), 215 "compiler typed enum support is apparently buggy"); 216 217 #elif defined(JS_PUNBOX64) 218 219 enum JSValueTag : uint32_t { 220 JSVAL_TAG_MAX_DOUBLE = 0x1FFF0, 221 JSVAL_TAG_INT32 = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32, 222 JSVAL_TAG_UNDEFINED = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_UNDEFINED, 223 JSVAL_TAG_NULL = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_NULL, 224 JSVAL_TAG_BOOLEAN = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN, 225 JSVAL_TAG_MAGIC = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_MAGIC, 226 JSVAL_TAG_STRING = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_STRING, 227 JSVAL_TAG_SYMBOL = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_SYMBOL, 228 JSVAL_TAG_PRIVATE_GCTHING = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_PRIVATE_GCTHING, 229 JSVAL_TAG_BIGINT = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BIGINT, 230 JSVAL_TAG_OBJECT = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_OBJECT 231 }; 232 233 static_assert(sizeof(JSValueTag) == sizeof(uint32_t), 234 "compiler typed enum support is apparently buggy"); 235 236 enum JSValueShiftedTag : uint64_t { 237 // See Bug 584653 for why we include 0xFFFFFFFF. 238 JSVAL_SHIFTED_TAG_MAX_DOUBLE = 239 ((uint64_t(JSVAL_TAG_MAX_DOUBLE) << JSVAL_TAG_SHIFT) | 0xFFFFFFFF), 240 JSVAL_SHIFTED_TAG_INT32 = (uint64_t(JSVAL_TAG_INT32) << JSVAL_TAG_SHIFT), 241 JSVAL_SHIFTED_TAG_UNDEFINED = 242 (uint64_t(JSVAL_TAG_UNDEFINED) << JSVAL_TAG_SHIFT), 243 JSVAL_SHIFTED_TAG_NULL = (uint64_t(JSVAL_TAG_NULL) << JSVAL_TAG_SHIFT), 244 JSVAL_SHIFTED_TAG_BOOLEAN = (uint64_t(JSVAL_TAG_BOOLEAN) << JSVAL_TAG_SHIFT), 245 JSVAL_SHIFTED_TAG_MAGIC = (uint64_t(JSVAL_TAG_MAGIC) << JSVAL_TAG_SHIFT), 246 JSVAL_SHIFTED_TAG_STRING = (uint64_t(JSVAL_TAG_STRING) << JSVAL_TAG_SHIFT), 247 JSVAL_SHIFTED_TAG_SYMBOL = (uint64_t(JSVAL_TAG_SYMBOL) << JSVAL_TAG_SHIFT), 248 JSVAL_SHIFTED_TAG_PRIVATE_GCTHING = 249 (uint64_t(JSVAL_TAG_PRIVATE_GCTHING) << JSVAL_TAG_SHIFT), 250 JSVAL_SHIFTED_TAG_BIGINT = (uint64_t(JSVAL_TAG_BIGINT) << JSVAL_TAG_SHIFT), 251 JSVAL_SHIFTED_TAG_OBJECT = (uint64_t(JSVAL_TAG_OBJECT) << JSVAL_TAG_SHIFT) 252 }; 253 254 static_assert(sizeof(JSValueShiftedTag) == sizeof(uint64_t), 255 "compiler typed enum support is apparently buggy"); 256 257 #endif 258 259 namespace JS { 260 namespace detail { 261 262 #if defined(JS_NUNBOX32) 263 264 constexpr JSValueTag ValueTypeToTag(JSValueType type) { 265 return static_cast<JSValueTag>(JSVAL_TAG_CLEAR | 266 std::underlying_type_t<JSValueType>(type)); 267 } 268 269 constexpr bool ValueIsDouble(uint64_t bits) { 270 return uint32_t(bits >> JSVAL_TAG_SHIFT) <= uint32_t(JSVAL_TAG_CLEAR); 271 } 272 273 constexpr JSValueTag ValueUpperExclPrimitiveTag = JSVAL_TAG_OBJECT; 274 constexpr JSValueTag ValueUpperInclNumberTag = JSVAL_TAG_INT32; 275 constexpr JSValueTag ValueLowerInclGCThingTag = JSVAL_TAG_STRING; 276 277 #elif defined(JS_PUNBOX64) 278 279 constexpr JSValueTag ValueTypeToTag(JSValueType type) { 280 return static_cast<JSValueTag>(JSVAL_TAG_MAX_DOUBLE | 281 std::underlying_type_t<JSValueType>(type)); 282 } 283 284 constexpr bool ValueIsDouble(uint64_t bits) { 285 return bits <= JSVAL_SHIFTED_TAG_MAX_DOUBLE; 286 } 287 288 constexpr uint64_t ValueTagMask = 0xFFFF'8000'0000'0000; 289 290 // This should only be used in toGCThing. See the 'Spectre mitigations' comment. 291 constexpr uint64_t ValueGCThingPayloadMask = 0x0000'7FFF'FFFF'FFFF; 292 293 // Mask used to combine an unbox operation with getting the chunk base. 294 constexpr uint64_t ValueGCThingPayloadChunkMask = 295 ValueGCThingPayloadMask & ~js::gc::ChunkMask; 296 297 constexpr uint64_t ValueTypeToShiftedTag(JSValueType type) { 298 return static_cast<uint64_t>(ValueTypeToTag(type)) << JSVAL_TAG_SHIFT; 299 } 300 # define JSVAL_TYPE_TO_SHIFTED_TAG(type) \ 301 (JS::detail::ValueTypeToShiftedTag(type)) 302 303 constexpr JSValueTag ValueUpperExclPrimitiveTag = JSVAL_TAG_OBJECT; 304 constexpr JSValueTag ValueUpperInclNumberTag = JSVAL_TAG_INT32; 305 constexpr JSValueTag ValueLowerInclGCThingTag = JSVAL_TAG_STRING; 306 307 constexpr uint64_t ValueUpperExclShiftedPrimitiveTag = JSVAL_SHIFTED_TAG_OBJECT; 308 constexpr uint64_t ValueUpperExclShiftedNumberTag = JSVAL_SHIFTED_TAG_BOOLEAN; 309 constexpr uint64_t ValueLowerInclShiftedGCThingTag = JSVAL_SHIFTED_TAG_STRING; 310 311 // JSVAL_TYPE_OBJECT and JSVAL_TYPE_NULL differ by one bit. We can use this to 312 // implement toObjectOrNull more efficiently. 313 constexpr uint64_t ValueObjectOrNullBit = 0x8ULL << JSVAL_TAG_SHIFT; 314 static_assert( 315 (JSVAL_SHIFTED_TAG_NULL ^ JSVAL_SHIFTED_TAG_OBJECT) == ValueObjectOrNullBit, 316 "ValueObjectOrNullBit must be consistent with object and null tags"); 317 318 constexpr uint64_t IsValidUserModePointer(uint64_t bits) { 319 // All 64-bit platforms that we support actually have a 48-bit address space 320 // for user-mode pointers, with the top 16 bits all set to zero. 321 return (bits & 0xFFFF'0000'0000'0000) == 0; 322 } 323 324 #endif /* JS_PUNBOX64 */ 325 326 } // namespace detail 327 } // namespace JS 328 329 #define JSVAL_TYPE_TO_TAG(type) (JS::detail::ValueTypeToTag(type)) 330 331 enum JSWhyMagic { 332 /** a hole in a native object's elements */ 333 JS_ELEMENTS_HOLE, 334 335 /** there is not a pending iterator value */ 336 JS_NO_ITER_VALUE, 337 338 /** exception value thrown when closing a generator */ 339 JS_GENERATOR_CLOSING, 340 341 /** used in debug builds to catch tracing errors */ 342 JS_ARG_POISON, 343 344 /** an empty subnode in the AST serializer */ 345 JS_SERIALIZE_NO_NODE, 346 347 /** magic value passed to natives to indicate construction */ 348 JS_IS_CONSTRUCTING, 349 350 /** see class js::HashableValue */ 351 JS_HASH_KEY_EMPTY, 352 353 /** error while running Ion code */ 354 JS_ION_ERROR, 355 356 /** missing recover instruction result */ 357 JS_ION_BAILOUT, 358 359 /** optimized out slot */ 360 JS_OPTIMIZED_OUT, 361 362 /** uninitialized lexical bindings that produce ReferenceError on touch. */ 363 JS_UNINITIALIZED_LEXICAL, 364 365 /** arguments object can't be created because environment is dead. */ 366 JS_MISSING_ARGUMENTS, 367 368 /** exception value thrown when interrupting irregexp */ 369 JS_INTERRUPT_REGEXP, 370 371 /** for local use */ 372 JS_GENERIC_MAGIC, 373 374 /** 375 * When an error object is created without the error cause argument, we set 376 * the error's cause slot to this magic value. 377 */ 378 JS_ERROR_WITHOUT_CAUSE, 379 380 JS_WHY_MAGIC_COUNT 381 }; 382 383 namespace js { 384 class JS_PUBLIC_API GenericPrinter; 385 class JSONPrinter; 386 387 static inline JS::Value PoisonedObjectValue(uintptr_t poison); 388 389 } // namespace js 390 391 namespace JS { 392 393 namespace detail { 394 395 // IEEE-754 bit pattern for double-precision positive infinity. 396 constexpr int InfinitySignBit = 0; 397 constexpr uint64_t InfinityBits = 398 mozilla::InfinityBits<double, detail::InfinitySignBit>::value; 399 400 // This is a quiet NaN on IEEE-754[2008] compatible platforms, including X86, 401 // ARM, SPARC, RISC-V and modern MIPS. 402 // 403 // Note: The default sign bit for a hardware synthesized NaN differs between X86 404 // and ARM. Both values are considered compatible values on both 405 // platforms. 406 constexpr int CanonicalizedNaNSignBit = 0; 407 constexpr uint64_t CanonicalizedNaNSignificand = 0x8000000000000; 408 409 #if defined(__sparc__) 410 // Some architectures (not to name names) generate NaNs with bit patterns that 411 // are incompatible with JS::Value's bit pattern restrictions. Instead we must 412 // canonicalize all hardware values before storing in JS::Value. 413 # define JS_NONCANONICAL_HARDWARE_NAN 414 #endif 415 416 #if defined(__mips__) && !defined(__mips_nan_2008) 417 // These builds may run on hardware that has differing polarity of the signaling 418 // NaN bit. While the kernel may handle the trap for us, it is a performance 419 // issue so instead we compute the NaN to use on startup. The runtime value must 420 // still meet `ValueIsDouble` requirements which are checked on startup. 421 422 // In particular, we expect one of the following values on MIPS: 423 // - 0x7FF7FFFFFFFFFFFF Legacy 424 // - 0x7FF8000000000000 IEEE-754[2008] 425 # define JS_RUNTIME_CANONICAL_NAN 426 #endif 427 428 #if defined(JS_RUNTIME_CANONICAL_NAN) 429 extern uint64_t CanonicalizedNaNBits; 430 #else 431 constexpr uint64_t CanonicalizedNaNBits = 432 mozilla::SpecificNaNBits<double, detail::CanonicalizedNaNSignBit, 433 detail::CanonicalizedNaNSignificand>::value; 434 #endif 435 } // namespace detail 436 437 // Return a quiet NaN that is compatible with JS::Value restrictions. 438 static MOZ_ALWAYS_INLINE double GenericNaN() { 439 #if !defined(JS_RUNTIME_CANONICAL_NAN) 440 static_assert(detail::ValueIsDouble(detail::CanonicalizedNaNBits), 441 "Canonical NaN must be compatible with JS::Value"); 442 #endif 443 444 return mozilla::BitwiseCast<double>(detail::CanonicalizedNaNBits); 445 } 446 447 // Return the infinity the engine uses 448 static MOZ_ALWAYS_INLINE double Infinity() { 449 return mozilla::BitwiseCast<double>(detail::InfinityBits); 450 } 451 452 // Convert an arbitrary double to one compatible with JS::Value representation 453 // by replacing any NaN value with a canonical one. 454 static MOZ_ALWAYS_INLINE double CanonicalizeNaN(double d) { 455 if (MOZ_UNLIKELY(std::isnan(d))) { 456 return GenericNaN(); 457 } 458 return d; 459 } 460 461 /** 462 * [SMDOC] JS::Value type 463 * 464 * JS::Value is the interface for a single JavaScript Engine value. A few 465 * general notes on JS::Value: 466 * 467 * - JS::Value has setX() and isX() members for X in 468 * 469 * { Int32, Double, String, Symbol, BigInt, Boolean, Undefined, Null, 470 * Object, Magic } 471 * 472 * JS::Value also contains toX() for each of the non-singleton types. 473 * 474 * - Magic is a singleton type whose payload contains either a JSWhyMagic 475 * "reason" for the magic value or a uint32_t value. By providing JSWhyMagic 476 * values when creating and checking for magic values, it is possible to 477 * assert, at runtime, that only magic values with the expected reason flow 478 * through a particular value. For example, if cx->exception has a magic 479 * value, the reason must be JS_GENERATOR_CLOSING. 480 * 481 * - To help prevent mistakenly boxing a nullable JSObject* as an object, 482 * Value::setObject takes a JSObject&. (Conversely, Value::toObject returns a 483 * JSObject&.) A convenience member Value::setObjectOrNull is provided. 484 * 485 * - Note that JS::Value is 8 bytes on 32 and 64-bit architectures. Because most 486 * of our users are now on 64-bit platforms, code should prefer passing 487 * JS::Value by value instead of |const Value&|. 488 * 489 * Spectre mitigations 490 * =================== 491 * To mitigate Spectre attacks, we do the following: 492 * 493 * - On 64-bit platforms, when unboxing a Value, we XOR the bits with the 494 * expected type tag (instead of masking the payload bits). This guarantees 495 * that toString, toObject, toSymbol will return an invalid pointer (because 496 * some high bits will be set) when called on a Value with a different type 497 * tag. 498 * 499 * - On 32-bit platforms,when unboxing an object/string/symbol Value, we use a 500 * conditional move (not speculated) to zero the payload register if the type 501 * doesn't match. 502 */ 503 class Value { 504 private: 505 uint64_t asBits_; 506 507 public: 508 constexpr Value() : asBits_(bitsFromTagAndPayload(JSVAL_TAG_UNDEFINED, 0)) {} 509 510 private: 511 explicit constexpr Value(uint64_t asBits) : asBits_(asBits) {} 512 513 static uint64_t bitsFromDouble(double d) { 514 #if defined(JS_NONCANONICAL_HARDWARE_NAN) 515 d = CanonicalizeNaN(d); 516 #endif 517 return mozilla::BitwiseCast<uint64_t>(d); 518 } 519 520 static_assert(sizeof(JSValueType) == 1, 521 "type bits must fit in a single byte"); 522 static_assert(sizeof(JSValueTag) == 4, 523 "32-bit Value's tag_ must have size 4 to complement the " 524 "payload union's size 4"); 525 static_assert(sizeof(JSWhyMagic) <= 4, 526 "32-bit Value's JSWhyMagic payload field must not inflate " 527 "the payload beyond 4 bytes"); 528 529 public: 530 #if defined(JS_NUNBOX32) 531 using PayloadType = uint32_t; 532 #elif defined(JS_PUNBOX64) 533 using PayloadType = uint64_t; 534 #endif 535 536 static constexpr uint64_t bitsFromTagAndPayload(JSValueTag tag, 537 PayloadType payload) { 538 return (uint64_t(tag) << JSVAL_TAG_SHIFT) | payload; 539 } 540 541 static constexpr Value fromTagAndPayload(JSValueTag tag, 542 PayloadType payload) { 543 return fromRawBits(bitsFromTagAndPayload(tag, payload)); 544 } 545 546 static constexpr Value fromRawBits(uint64_t asBits) { return Value(asBits); } 547 548 static constexpr Value fromInt32(int32_t i) { 549 return fromTagAndPayload(JSVAL_TAG_INT32, uint32_t(i)); 550 } 551 552 static Value fromDouble(double d) { return fromRawBits(bitsFromDouble(d)); } 553 554 /** 555 * Returns false if creating a NumberValue containing the given type would 556 * be lossy, true otherwise. 557 */ 558 template <typename T> 559 static bool isNumberRepresentable(const T t) { 560 return T(double(t)) == t; 561 } 562 563 /*** Mutators ***/ 564 565 void setNull() { 566 asBits_ = bitsFromTagAndPayload(JSVAL_TAG_NULL, 0); 567 MOZ_ASSERT(isNull()); 568 } 569 570 void setUndefined() { 571 asBits_ = bitsFromTagAndPayload(JSVAL_TAG_UNDEFINED, 0); 572 MOZ_ASSERT(isUndefined()); 573 } 574 575 void setInt32(int32_t i) { 576 asBits_ = bitsFromTagAndPayload(JSVAL_TAG_INT32, uint32_t(i)); 577 MOZ_ASSERT(toInt32() == i); 578 } 579 580 void setDouble(double d) { 581 asBits_ = bitsFromDouble(d); 582 MOZ_ASSERT(isDouble()); 583 } 584 585 void setString(JSString* str) { 586 MOZ_ASSERT(js::gc::IsCellPointerValid(str)); 587 asBits_ = bitsFromTagAndPayload(JSVAL_TAG_STRING, PayloadType(str)); 588 MOZ_ASSERT(toString() == str); 589 } 590 591 void setSymbol(JS::Symbol* sym) { 592 MOZ_ASSERT(js::gc::IsCellPointerValid(sym)); 593 asBits_ = bitsFromTagAndPayload(JSVAL_TAG_SYMBOL, PayloadType(sym)); 594 MOZ_ASSERT(toSymbol() == sym); 595 } 596 597 void setBigInt(JS::BigInt* bi) { 598 MOZ_ASSERT(js::gc::IsCellPointerValid(bi)); 599 asBits_ = bitsFromTagAndPayload(JSVAL_TAG_BIGINT, PayloadType(bi)); 600 MOZ_ASSERT(toBigInt() == bi); 601 } 602 603 void setObject(JSObject& obj) { 604 MOZ_ASSERT(js::gc::IsCellPointerValid(&obj)); 605 setObjectNoCheck(&obj); 606 MOZ_ASSERT(&toObject() == &obj); 607 } 608 609 void changeGCThingPayload(js::gc::Cell* cell) { 610 MOZ_ASSERT(js::gc::IsCellPointerValid(cell)); 611 #ifdef DEBUG 612 assertTraceKindMatches(cell); 613 #endif 614 asBits_ = bitsFromTagAndPayload(toTag(), PayloadType(cell)); 615 MOZ_ASSERT(toGCThing() == cell); 616 } 617 618 private: 619 #ifdef DEBUG 620 void assertTraceKindMatches(js::gc::Cell* cell) const; 621 #endif 622 623 void setObjectNoCheck(JSObject* obj) { 624 asBits_ = bitsFromTagAndPayload(JSVAL_TAG_OBJECT, PayloadType(obj)); 625 } 626 627 friend inline Value js::PoisonedObjectValue(uintptr_t poison); 628 629 public: 630 void setBoolean(bool b) { 631 asBits_ = bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN, uint32_t(b)); 632 MOZ_ASSERT(toBoolean() == b); 633 } 634 635 void setMagic(JSWhyMagic why) { 636 asBits_ = bitsFromTagAndPayload(JSVAL_TAG_MAGIC, uint32_t(why)); 637 MOZ_ASSERT(whyMagic() == why); 638 } 639 640 void setMagicUint32(uint32_t payload) { 641 MOZ_ASSERT(payload >= JS_WHY_MAGIC_COUNT, 642 "This should only be used for non-standard magic values"); 643 asBits_ = bitsFromTagAndPayload(JSVAL_TAG_MAGIC, payload); 644 MOZ_ASSERT(magicUint32() == payload); 645 } 646 647 void setNumber(float f) { 648 int32_t i; 649 if (mozilla::NumberIsInt32(f, &i)) { 650 setInt32(i); 651 return; 652 } 653 654 setDouble(double(f)); 655 } 656 657 void setNumber(double d) { 658 int32_t i; 659 if (mozilla::NumberIsInt32(d, &i)) { 660 setInt32(i); 661 return; 662 } 663 664 setDouble(d); 665 } 666 667 template <typename T> 668 void setNumber(const T t) { 669 static_assert(std::is_integral<T>::value, "must be integral type"); 670 MOZ_ASSERT(isNumberRepresentable(t), "value creation would be lossy"); 671 672 if constexpr (std::numeric_limits<T>::is_signed) { 673 if constexpr (sizeof(t) <= sizeof(int32_t)) { 674 setInt32(int32_t(t)); 675 } else { 676 if (JSVAL_INT_MIN <= t && t <= JSVAL_INT_MAX) { 677 setInt32(int32_t(t)); 678 } else { 679 setDouble(double(t)); 680 } 681 } 682 } else { 683 if constexpr (sizeof(t) <= sizeof(uint16_t)) { 684 setInt32(int32_t(t)); 685 } else { 686 if (t <= JSVAL_INT_MAX) { 687 setInt32(int32_t(t)); 688 } else { 689 setDouble(double(t)); 690 } 691 } 692 } 693 } 694 695 void setObjectOrNull(JSObject* arg) { 696 if (arg) { 697 setObject(*arg); 698 } else { 699 setNull(); 700 } 701 } 702 703 void swap(Value& rhs) { 704 uint64_t tmp = rhs.asBits_; 705 rhs.asBits_ = asBits_; 706 asBits_ = tmp; 707 } 708 709 private: 710 JSValueTag toTag() const { return JSValueTag(asBits_ >> JSVAL_TAG_SHIFT); } 711 712 template <typename T, JSValueTag Tag> 713 T* unboxGCPointer() const { 714 MOZ_ASSERT((asBits_ & js::gc::CellAlignMask) == 0, 715 "GC pointer is not aligned. Is this memory corruption?"); 716 #if defined(JS_NUNBOX32) 717 uintptr_t payload = uint32_t(asBits_); 718 return reinterpret_cast<T*>(payload); 719 #elif defined(JS_PUNBOX64) 720 // Note: the 'Spectre mitigations' comment at the top of this class 721 // explains why we use XOR here. 722 constexpr uint64_t shiftedTag = uint64_t(Tag) << JSVAL_TAG_SHIFT; 723 return reinterpret_cast<T*>(uintptr_t(asBits_ ^ shiftedTag)); 724 #endif 725 } 726 727 public: 728 /*** JIT-only interfaces to interact with and create raw Values ***/ 729 #if defined(JS_NUNBOX32) 730 PayloadType toNunboxPayload() const { return uint32_t(asBits_); } 731 732 JSValueTag toNunboxTag() const { return toTag(); } 733 #elif defined(JS_PUNBOX64) 734 const void* bitsAsPunboxPointer() const { 735 return reinterpret_cast<void*>(asBits_); 736 } 737 #endif 738 739 /*** Value type queries ***/ 740 741 /* 742 * N.B. GCC, in some but not all cases, chooses to emit signed comparison 743 * of JSValueTag even though its underlying type has been forced to be 744 * uint32_t. Thus, all comparisons should explicitly cast operands to 745 * uint32_t. 746 */ 747 748 bool isUndefined() const { 749 #if defined(JS_NUNBOX32) 750 return toTag() == JSVAL_TAG_UNDEFINED; 751 #elif defined(JS_PUNBOX64) 752 return asBits_ == JSVAL_SHIFTED_TAG_UNDEFINED; 753 #endif 754 } 755 756 bool isNull() const { 757 #if defined(JS_NUNBOX32) 758 return toTag() == JSVAL_TAG_NULL; 759 #elif defined(JS_PUNBOX64) 760 return asBits_ == JSVAL_SHIFTED_TAG_NULL; 761 #endif 762 } 763 764 bool isNullOrUndefined() const { return isNull() || isUndefined(); } 765 766 bool isInt32() const { return toTag() == JSVAL_TAG_INT32; } 767 768 bool isInt32(int32_t i32) const { 769 return asBits_ == bitsFromTagAndPayload(JSVAL_TAG_INT32, uint32_t(i32)); 770 } 771 772 bool isDouble() const { return detail::ValueIsDouble(asBits_); } 773 774 bool isNaN() const { return isDouble() && std::isnan(toDouble()); } 775 776 bool isNumber() const { 777 #if defined(JS_NUNBOX32) 778 MOZ_ASSERT(toTag() != JSVAL_TAG_CLEAR); 779 return uint32_t(toTag()) <= uint32_t(detail::ValueUpperInclNumberTag); 780 #elif defined(JS_PUNBOX64) 781 return asBits_ < detail::ValueUpperExclShiftedNumberTag; 782 #endif 783 } 784 785 bool isString() const { return toTag() == JSVAL_TAG_STRING; } 786 787 bool isSymbol() const { return toTag() == JSVAL_TAG_SYMBOL; } 788 789 bool isBigInt() const { return toTag() == JSVAL_TAG_BIGINT; } 790 791 bool isObject() const { 792 #if defined(JS_NUNBOX32) 793 return toTag() == JSVAL_TAG_OBJECT; 794 #elif defined(JS_PUNBOX64) 795 MOZ_ASSERT((asBits_ >> JSVAL_TAG_SHIFT) <= JSVAL_TAG_OBJECT); 796 return asBits_ >= JSVAL_SHIFTED_TAG_OBJECT; 797 #endif 798 } 799 800 bool isPrimitive() const { 801 #if defined(JS_NUNBOX32) 802 return uint32_t(toTag()) < uint32_t(detail::ValueUpperExclPrimitiveTag); 803 #elif defined(JS_PUNBOX64) 804 return asBits_ < detail::ValueUpperExclShiftedPrimitiveTag; 805 #endif 806 } 807 808 bool isObjectOrNull() const { return isObject() || isNull(); } 809 810 bool isNumeric() const { return isNumber() || isBigInt(); } 811 812 bool isGCThing() const { 813 #if defined(JS_NUNBOX32) 814 /* gcc sometimes generates signed < without explicit casts. */ 815 return uint32_t(toTag()) >= uint32_t(detail::ValueLowerInclGCThingTag); 816 #elif defined(JS_PUNBOX64) 817 return asBits_ >= detail::ValueLowerInclShiftedGCThingTag; 818 #endif 819 } 820 821 bool isBoolean() const { return toTag() == JSVAL_TAG_BOOLEAN; } 822 823 bool isTrue() const { 824 return asBits_ == bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN, uint32_t(true)); 825 } 826 827 bool isFalse() const { 828 return asBits_ == bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN, uint32_t(false)); 829 } 830 831 bool isMagic() const { return toTag() == JSVAL_TAG_MAGIC; } 832 833 bool isMagic(JSWhyMagic why) const { 834 if (!isMagic()) { 835 return false; 836 } 837 MOZ_RELEASE_ASSERT(whyMagic() == why); 838 return true; 839 } 840 841 // Like isMagic, but without the release assertion. 842 bool isMagicNoReleaseCheck(JSWhyMagic why) const { 843 if (!isMagic()) { 844 return false; 845 } 846 MOZ_ASSERT(whyMagic() == why); 847 return true; 848 } 849 850 JS::TraceKind traceKind() const { 851 MOZ_ASSERT(isGCThing()); 852 static_assert((JSVAL_TAG_STRING & 0x03) == size_t(JS::TraceKind::String), 853 "Value type tags must correspond with JS::TraceKinds."); 854 static_assert((JSVAL_TAG_OBJECT & 0x03) == size_t(JS::TraceKind::Object), 855 "Value type tags must correspond with JS::TraceKinds."); 856 static_assert((JSVAL_TAG_BIGINT & 0x03) == size_t(JS::TraceKind::BigInt), 857 "Value type tags must correspond with JS::TraceKinds."); 858 static_assert(JSVAL_TAG_SYMBOL + 1 == JSVAL_TAG_PRIVATE_GCTHING, 859 "Symbol and PrivateGCThing tags should be adjacent to allow " 860 "checking for them with a single branch"); 861 JSValueTag tag = toTag(); 862 if (MOZ_UNLIKELY(tag == JSVAL_TAG_SYMBOL || 863 tag == JSVAL_TAG_PRIVATE_GCTHING)) { 864 return JS::GCThingTraceKind(toGCThing()); 865 } 866 return JS::TraceKind(tag & 0x03); 867 } 868 869 JSWhyMagic whyMagic() const { 870 MOZ_ASSERT(magicUint32() < JS_WHY_MAGIC_COUNT); 871 return static_cast<JSWhyMagic>(magicUint32()); 872 } 873 874 uint32_t magicUint32() const { 875 MOZ_ASSERT(isMagic()); 876 return uint32_t(asBits_); 877 } 878 879 /*** Comparison ***/ 880 881 bool operator==(const Value& rhs) const { return asBits_ == rhs.asBits_; } 882 883 bool operator!=(const Value& rhs) const { return asBits_ != rhs.asBits_; } 884 885 friend inline bool SameType(const Value& lhs, const Value& rhs); 886 887 /*** Extract the value's typed payload ***/ 888 889 int32_t toInt32() const { 890 MOZ_ASSERT(isInt32()); 891 return int32_t(asBits_); 892 } 893 894 double toDouble() const { 895 MOZ_ASSERT(isDouble()); 896 return mozilla::BitwiseCast<double>(asBits_); 897 } 898 899 double toNumber() const { 900 MOZ_ASSERT(isNumber()); 901 return isDouble() ? toDouble() : double(toInt32()); 902 } 903 904 JSString* toString() const { 905 MOZ_ASSERT(isString()); 906 return unboxGCPointer<JSString, JSVAL_TAG_STRING>(); 907 } 908 909 JS::Symbol* toSymbol() const { 910 MOZ_ASSERT(isSymbol()); 911 return unboxGCPointer<JS::Symbol, JSVAL_TAG_SYMBOL>(); 912 } 913 914 JS::BigInt* toBigInt() const { 915 MOZ_ASSERT(isBigInt()); 916 return unboxGCPointer<JS::BigInt, JSVAL_TAG_BIGINT>(); 917 } 918 919 JSObject& toObject() const { 920 MOZ_ASSERT(isObject()); 921 #if defined(JS_PUNBOX64) 922 MOZ_ASSERT((asBits_ & detail::ValueGCThingPayloadMask) != 0); 923 #endif 924 return *unboxGCPointer<JSObject, JSVAL_TAG_OBJECT>(); 925 } 926 927 JSObject* toObjectOrNull() const { 928 MOZ_ASSERT(isObjectOrNull()); 929 #if defined(JS_NUNBOX32) 930 return reinterpret_cast<JSObject*>(uintptr_t(asBits_)); 931 #elif defined(JS_PUNBOX64) 932 // Note: the 'Spectre mitigations' comment at the top of this class 933 // explains why we use XOR here and in other to* methods. 934 uint64_t ptrBits = 935 (asBits_ ^ JSVAL_SHIFTED_TAG_OBJECT) & ~detail::ValueObjectOrNullBit; 936 MOZ_ASSERT((ptrBits & 0x7) == 0); 937 return reinterpret_cast<JSObject*>(ptrBits); 938 #endif 939 } 940 941 js::gc::Cell* toGCThing() const { 942 MOZ_ASSERT(isGCThing()); 943 #if defined(JS_NUNBOX32) 944 return reinterpret_cast<js::gc::Cell*>(uintptr_t(asBits_)); 945 #elif defined(JS_PUNBOX64) 946 uint64_t ptrBits = asBits_ & detail::ValueGCThingPayloadMask; 947 MOZ_ASSERT((ptrBits & 0x7) == 0); 948 return reinterpret_cast<js::gc::Cell*>(ptrBits); 949 #endif 950 } 951 952 GCCellPtr toGCCellPtr() const { return GCCellPtr(toGCThing(), traceKind()); } 953 954 bool toBoolean() const { 955 MOZ_ASSERT(isBoolean()); 956 #if defined(JS_NUNBOX32) 957 return bool(toNunboxPayload()); 958 #elif defined(JS_PUNBOX64) 959 return bool(asBits_ & 0x1); 960 #endif 961 } 962 963 constexpr uint64_t asRawBits() const { return asBits_; } 964 965 JSValueType extractNonDoubleType() const { 966 uint32_t type = toTag() & 0xF; 967 MOZ_ASSERT(type > JSVAL_TYPE_DOUBLE); 968 return JSValueType(type); 969 } 970 971 JS::ValueType type() const { 972 if (isDouble()) { 973 return JS::ValueType::Double; 974 } 975 976 JSValueType type = extractNonDoubleType(); 977 MOZ_ASSERT(type <= JSVAL_TYPE_OBJECT); 978 return JS::ValueType(type); 979 } 980 981 /* 982 * Private API 983 * 984 * Private setters/getters allow the caller to read/write arbitrary 985 * word-size pointers or uint32s. After storing to a value with 986 * setPrivateX, it is the caller's responsibility to only read using 987 * toPrivateX. Private values are given a type which ensures they 988 * aren't marked by the GC. 989 */ 990 991 void setPrivate(void* ptr) { 992 #if defined(JS_PUNBOX64) 993 MOZ_ASSERT(detail::IsValidUserModePointer(uintptr_t(ptr))); 994 #endif 995 asBits_ = uintptr_t(ptr); 996 MOZ_ASSERT(isDouble()); 997 } 998 999 void* toPrivate() const { 1000 MOZ_ASSERT(isDouble()); 1001 #if defined(JS_PUNBOX64) 1002 MOZ_ASSERT(detail::IsValidUserModePointer(asBits_)); 1003 #endif 1004 return reinterpret_cast<void*>(uintptr_t(asBits_)); 1005 } 1006 1007 void* toPrivateUnchecked() const { 1008 return reinterpret_cast<void*>(uintptr_t(asBits_)); 1009 } 1010 1011 void setPrivateUint32(uint32_t ui) { 1012 MOZ_ASSERT(uint32_t(int32_t(ui)) == ui); 1013 setInt32(int32_t(ui)); 1014 } 1015 1016 uint32_t toPrivateUint32() const { return uint32_t(toInt32()); } 1017 1018 /* 1019 * Private GC Thing API 1020 * 1021 * Non-JSObject, JSString, and JS::Symbol cells may be put into the 64-bit 1022 * payload as private GC things. Such Values are considered isGCThing(), and 1023 * as such, automatically marked. Their traceKind() is gotten via their 1024 * cells. 1025 */ 1026 1027 void setPrivateGCThing(js::gc::Cell* cell) { 1028 MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::String, 1029 "Private GC thing Values must not be strings. Make a " 1030 "StringValue instead."); 1031 MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Symbol, 1032 "Private GC thing Values must not be symbols. Make a " 1033 "SymbolValue instead."); 1034 MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::BigInt, 1035 "Private GC thing Values must not be BigInts. Make a " 1036 "BigIntValue instead."); 1037 MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Object, 1038 "Private GC thing Values must not be objects. Make an " 1039 "ObjectValue instead."); 1040 1041 MOZ_ASSERT(js::gc::IsCellPointerValid(cell)); 1042 #if defined(JS_PUNBOX64) 1043 // VisualStudio cannot contain parenthesized C++ style cast and shift 1044 // inside decltype in template parameter: 1045 // AssertionConditionType<decltype((uintptr_t(x) >> 1))> 1046 // It throws syntax error. 1047 MOZ_ASSERT((((uintptr_t)cell) >> JSVAL_TAG_SHIFT) == 0); 1048 #endif 1049 asBits_ = 1050 bitsFromTagAndPayload(JSVAL_TAG_PRIVATE_GCTHING, PayloadType(cell)); 1051 } 1052 1053 bool isPrivateGCThing() const { return toTag() == JSVAL_TAG_PRIVATE_GCTHING; } 1054 1055 #if defined(DEBUG) || defined(JS_JITSPEW) 1056 void dump() const; 1057 void dump(js::GenericPrinter& out) const; 1058 void dump(js::JSONPrinter& json) const; 1059 1060 void dumpFields(js::JSONPrinter& json) const; 1061 void dumpStringContent(js::GenericPrinter& out) const; 1062 #endif 1063 } JS_HAZ_GC_POINTER; 1064 1065 static_assert(sizeof(Value) == 8, 1066 "Value size must leave three tag bits, be a binary power, and " 1067 "is ubiquitously depended upon everywhere"); 1068 1069 static MOZ_ALWAYS_INLINE void ExposeValueToActiveJS(const Value& v) { 1070 #ifdef DEBUG 1071 Value tmp = v; 1072 MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarrieredSlow(&tmp)); 1073 #endif 1074 if (v.isGCThing()) { 1075 js::gc::ExposeGCThingToActiveJS(v.toGCCellPtr()); 1076 } 1077 } 1078 1079 /************************************************************************/ 1080 1081 static inline MOZ_MAY_CALL_AFTER_MUST_RETURN Value NullValue() { 1082 Value v; 1083 v.setNull(); 1084 return v; 1085 } 1086 1087 static constexpr Value UndefinedValue() { return Value(); } 1088 1089 static constexpr Value Int32Value(int32_t i32) { return Value::fromInt32(i32); } 1090 1091 static inline Value DoubleValue(double dbl) { 1092 Value v; 1093 v.setDouble(dbl); 1094 return v; 1095 } 1096 1097 static inline Value CanonicalizedDoubleValue(double d) { 1098 return Value::fromDouble(CanonicalizeNaN(d)); 1099 } 1100 1101 static inline Value NaNValue() { 1102 return Value::fromRawBits(detail::CanonicalizedNaNBits); 1103 } 1104 1105 static inline Value InfinityValue() { 1106 return Value::fromRawBits(detail::InfinityBits); 1107 } 1108 1109 static inline Value Float32Value(float f) { 1110 Value v; 1111 v.setDouble(f); 1112 return v; 1113 } 1114 1115 static inline Value StringValue(JSString* str) { 1116 Value v; 1117 v.setString(str); 1118 return v; 1119 } 1120 1121 static inline Value SymbolValue(JS::Symbol* sym) { 1122 Value v; 1123 v.setSymbol(sym); 1124 return v; 1125 } 1126 1127 static inline Value BigIntValue(JS::BigInt* bi) { 1128 Value v; 1129 v.setBigInt(bi); 1130 return v; 1131 } 1132 1133 static inline Value BooleanValue(bool boo) { 1134 Value v; 1135 v.setBoolean(boo); 1136 return v; 1137 } 1138 1139 static inline Value TrueValue() { 1140 Value v; 1141 v.setBoolean(true); 1142 return v; 1143 } 1144 1145 static inline Value FalseValue() { 1146 Value v; 1147 v.setBoolean(false); 1148 return v; 1149 } 1150 1151 static inline Value ObjectValue(JSObject& obj) { 1152 Value v; 1153 v.setObject(obj); 1154 return v; 1155 } 1156 1157 static inline Value MagicValue(JSWhyMagic why) { 1158 Value v; 1159 v.setMagic(why); 1160 return v; 1161 } 1162 1163 static inline Value MagicValueUint32(uint32_t payload) { 1164 Value v; 1165 v.setMagicUint32(payload); 1166 return v; 1167 } 1168 1169 static constexpr Value NumberValue(uint32_t i) { 1170 return i <= JSVAL_INT_MAX ? Int32Value(int32_t(i)) 1171 : Value::fromDouble(double(i)); 1172 } 1173 1174 template <typename T> 1175 static inline Value NumberValue(const T t) { 1176 Value v; 1177 v.setNumber(t); 1178 return v; 1179 } 1180 1181 static inline Value ObjectOrNullValue(JSObject* obj) { 1182 Value v; 1183 v.setObjectOrNull(obj); 1184 return v; 1185 } 1186 1187 static inline Value PrivateValue(void* ptr) { 1188 Value v; 1189 v.setPrivate(ptr); 1190 return v; 1191 } 1192 1193 static inline Value PrivateValue(uintptr_t ptr) { 1194 return PrivateValue(reinterpret_cast<void*>(ptr)); 1195 } 1196 1197 static inline Value PrivateUint32Value(uint32_t ui) { 1198 Value v; 1199 v.setPrivateUint32(ui); 1200 return v; 1201 } 1202 1203 static inline Value PrivateGCThingValue(js::gc::Cell* cell) { 1204 Value v; 1205 v.setPrivateGCThing(cell); 1206 return v; 1207 } 1208 1209 inline bool SameType(const Value& lhs, const Value& rhs) { 1210 #if defined(JS_NUNBOX32) 1211 JSValueTag ltag = lhs.toTag(), rtag = rhs.toTag(); 1212 return ltag == rtag || (ltag < JSVAL_TAG_CLEAR && rtag < JSVAL_TAG_CLEAR); 1213 #elif defined(JS_PUNBOX64) 1214 return (lhs.isDouble() && rhs.isDouble()) || 1215 (((lhs.asBits_ ^ rhs.asBits_) & 0xFFFF800000000000ULL) == 0); 1216 #endif 1217 } 1218 1219 } // namespace JS 1220 1221 /************************************************************************/ 1222 1223 namespace JS { 1224 JS_PUBLIC_API void HeapValuePostWriteBarrier(Value* valuep, const Value& prev, 1225 const Value& next); 1226 JS_PUBLIC_API void HeapValueWriteBarriers(Value* valuep, const Value& prev, 1227 const Value& next); 1228 1229 template <> 1230 struct GCPolicy<JS::Value> : public GCPolicyBase<JS::Value> { 1231 static void trace(JSTracer* trc, Value* v, const char* name) { 1232 // This should only be called as part of root marking since that's the only 1233 // time we should trace unbarriered GC thing pointers. This will assert if 1234 // called at other times. 1235 TraceRoot(trc, v, name); 1236 } 1237 static bool isTenured(const Value& thing) { 1238 return !thing.isGCThing() || !IsInsideNursery(thing.toGCThing()); 1239 } 1240 static bool isValid(const Value& value) { 1241 return !value.isGCThing() || js::gc::IsCellPointerValid(value.toGCThing()); 1242 } 1243 }; 1244 1245 } // namespace JS 1246 1247 namespace js { 1248 1249 template <> 1250 struct BarrierMethods<JS::Value> { 1251 static gc::Cell* asGCThingOrNull(const JS::Value& v) { 1252 return v.isGCThing() ? v.toGCThing() : nullptr; 1253 } 1254 static void writeBarriers(JS::Value* v, const JS::Value& prev, 1255 const JS::Value& next) { 1256 JS::HeapValueWriteBarriers(v, prev, next); 1257 } 1258 static void postWriteBarrier(JS::Value* v, const JS::Value& prev, 1259 const JS::Value& next) { 1260 JS::HeapValuePostWriteBarrier(v, prev, next); 1261 } 1262 static void exposeToJS(const JS::Value& v) { JS::ExposeValueToActiveJS(v); } 1263 static void readBarrier(const JS::Value& v) { 1264 if (v.isGCThing()) { 1265 js::gc::IncrementalReadBarrier(v.toGCCellPtr()); 1266 } 1267 } 1268 }; 1269 1270 template <class Wrapper> 1271 class MutableValueOperations; 1272 1273 /** 1274 * A class designed for CRTP use in implementing the non-mutating parts of the 1275 * Value interface in Value-like classes. Wrapper must be a class inheriting 1276 * ValueOperations<Wrapper> with a visible get() method returning a const 1277 * reference to the Value abstracted by Wrapper. 1278 */ 1279 template <class Wrapper> 1280 class WrappedPtrOperations<JS::Value, Wrapper> { 1281 const JS::Value& value() const { 1282 return static_cast<const Wrapper*>(this)->get(); 1283 } 1284 1285 public: 1286 bool isUndefined() const { return value().isUndefined(); } 1287 bool isNull() const { return value().isNull(); } 1288 bool isBoolean() const { return value().isBoolean(); } 1289 bool isTrue() const { return value().isTrue(); } 1290 bool isFalse() const { return value().isFalse(); } 1291 bool isNumber() const { return value().isNumber(); } 1292 bool isInt32() const { return value().isInt32(); } 1293 bool isInt32(int32_t i32) const { return value().isInt32(i32); } 1294 bool isDouble() const { return value().isDouble(); } 1295 bool isString() const { return value().isString(); } 1296 bool isSymbol() const { return value().isSymbol(); } 1297 bool isBigInt() const { return value().isBigInt(); } 1298 bool isObject() const { return value().isObject(); } 1299 bool isMagic() const { return value().isMagic(); } 1300 bool isMagic(JSWhyMagic why) const { return value().isMagic(why); } 1301 bool isGCThing() const { return value().isGCThing(); } 1302 bool isPrivateGCThing() const { return value().isPrivateGCThing(); } 1303 bool isPrimitive() const { return value().isPrimitive(); } 1304 1305 bool isNullOrUndefined() const { return value().isNullOrUndefined(); } 1306 bool isObjectOrNull() const { return value().isObjectOrNull(); } 1307 bool isNumeric() const { return value().isNumeric(); } 1308 1309 bool toBoolean() const { return value().toBoolean(); } 1310 double toNumber() const { return value().toNumber(); } 1311 int32_t toInt32() const { return value().toInt32(); } 1312 double toDouble() const { return value().toDouble(); } 1313 JSString* toString() const { return value().toString(); } 1314 JS::Symbol* toSymbol() const { return value().toSymbol(); } 1315 JS::BigInt* toBigInt() const { return value().toBigInt(); } 1316 JSObject& toObject() const { return value().toObject(); } 1317 JSObject* toObjectOrNull() const { return value().toObjectOrNull(); } 1318 JS::GCCellPtr toGCCellPtr() const { return value().toGCCellPtr(); } 1319 gc::Cell* toGCThing() const { return value().toGCThing(); } 1320 JS::TraceKind traceKind() const { return value().traceKind(); } 1321 void* toPrivate() const { return value().toPrivate(); } 1322 uint32_t toPrivateUint32() const { return value().toPrivateUint32(); } 1323 1324 uint64_t asRawBits() const { return value().asRawBits(); } 1325 JSValueType extractNonDoubleType() const { 1326 return value().extractNonDoubleType(); 1327 } 1328 JS::ValueType type() const { return value().type(); } 1329 1330 JSWhyMagic whyMagic() const { return value().whyMagic(); } 1331 uint32_t magicUint32() const { return value().magicUint32(); } 1332 }; 1333 1334 /** 1335 * A class designed for CRTP use in implementing all the mutating parts of the 1336 * Value interface in Value-like classes. Wrapper must be a class inheriting 1337 * MutableWrappedPtrOperations<Wrapper> with visible get() methods returning 1338 * const and non-const references to the Value abstracted by Wrapper. 1339 */ 1340 template <class Wrapper> 1341 class MutableWrappedPtrOperations<JS::Value, Wrapper> 1342 : public WrappedPtrOperations<JS::Value, Wrapper> { 1343 protected: 1344 void set(const JS::Value& v) { 1345 // Call Wrapper::set to trigger any barriers. 1346 static_cast<Wrapper*>(this)->set(v); 1347 } 1348 1349 public: 1350 void setNull() { set(JS::NullValue()); } 1351 void setUndefined() { set(JS::UndefinedValue()); } 1352 void setInt32(int32_t i) { set(JS::Int32Value(i)); } 1353 void setDouble(double d) { set(JS::DoubleValue(d)); } 1354 void setNaN() { set(JS::NaNValue()); } 1355 void setInfinity() { set(JS::InfinityValue()); } 1356 void setBoolean(bool b) { set(JS::BooleanValue(b)); } 1357 void setMagic(JSWhyMagic why) { set(JS::MagicValue(why)); } 1358 template <typename T> 1359 void setNumber(T t) { 1360 set(JS::NumberValue(t)); 1361 } 1362 void setString(JSString* str) { set(JS::StringValue(str)); } 1363 void setSymbol(JS::Symbol* sym) { set(JS::SymbolValue(sym)); } 1364 void setBigInt(JS::BigInt* bi) { set(JS::BigIntValue(bi)); } 1365 void setObject(JSObject& obj) { set(JS::ObjectValue(obj)); } 1366 void setObjectOrNull(JSObject* arg) { set(JS::ObjectOrNullValue(arg)); } 1367 void setPrivate(void* ptr) { set(JS::PrivateValue(ptr)); } 1368 void setPrivateUint32(uint32_t ui) { set(JS::PrivateUint32Value(ui)); } 1369 void setPrivateGCThing(js::gc::Cell* cell) { 1370 set(JS::PrivateGCThingValue(cell)); 1371 } 1372 }; 1373 1374 /* 1375 * Augment the generic Heap<T> interface when T = Value with 1376 * type-querying, value-extracting, and mutating operations. 1377 */ 1378 template <typename Wrapper> 1379 class HeapOperations<JS::Value, Wrapper> 1380 : public MutableWrappedPtrOperations<JS::Value, Wrapper> {}; 1381 1382 [[noreturn]] MOZ_COLD MOZ_NEVER_INLINE void ReportBadValueTypeAndCrash( 1383 const JS::Value& val); 1384 1385 // If the Value is a GC pointer type, call |f| with the pointer cast to that 1386 // type and return the result wrapped in a Maybe, otherwise return None(). 1387 template <typename F> 1388 auto MapGCThingTyped(const JS::Value& val, F&& f) { 1389 switch (val.type()) { 1390 case JS::ValueType::String: { 1391 JSString* str = val.toString(); 1392 MOZ_ASSERT(gc::IsCellPointerValid(str)); 1393 return mozilla::Some(f(str)); 1394 } 1395 case JS::ValueType::Object: { 1396 JSObject* obj = &val.toObject(); 1397 MOZ_ASSERT(gc::IsCellPointerValid(obj)); 1398 return mozilla::Some(f(obj)); 1399 } 1400 case JS::ValueType::Symbol: { 1401 JS::Symbol* sym = val.toSymbol(); 1402 MOZ_ASSERT(gc::IsCellPointerValid(sym)); 1403 return mozilla::Some(f(sym)); 1404 } 1405 case JS::ValueType::BigInt: { 1406 JS::BigInt* bi = val.toBigInt(); 1407 MOZ_ASSERT(gc::IsCellPointerValid(bi)); 1408 return mozilla::Some(f(bi)); 1409 } 1410 case JS::ValueType::PrivateGCThing: { 1411 MOZ_ASSERT(gc::IsCellPointerValid(val.toGCThing())); 1412 return mozilla::Some(MapGCThingTyped(val.toGCCellPtr(), std::move(f))); 1413 } 1414 case JS::ValueType::Double: 1415 case JS::ValueType::Int32: 1416 case JS::ValueType::Boolean: 1417 case JS::ValueType::Undefined: 1418 case JS::ValueType::Null: 1419 case JS::ValueType::Magic: { 1420 MOZ_ASSERT(!val.isGCThing()); 1421 using ReturnType = decltype(f(static_cast<JSObject*>(nullptr))); 1422 return mozilla::Maybe<ReturnType>(); 1423 } 1424 } 1425 1426 ReportBadValueTypeAndCrash(val); 1427 } 1428 1429 // If the Value is a GC pointer type, call |f| with the pointer cast to that 1430 // type. Return whether this happened. 1431 template <typename F> 1432 bool ApplyGCThingTyped(const JS::Value& val, F&& f) { 1433 return MapGCThingTyped(val, 1434 [&f](auto t) { 1435 f(t); 1436 return true; 1437 }) 1438 .isSome(); 1439 } 1440 1441 static inline JS::Value PoisonedObjectValue(uintptr_t poison) { 1442 JS::Value v; 1443 v.setObjectNoCheck(reinterpret_cast<JSObject*>(poison)); 1444 return v; 1445 } 1446 1447 } // namespace js 1448 1449 #ifdef DEBUG 1450 namespace JS { 1451 1452 MOZ_ALWAYS_INLINE void AssertValueIsNotGray(const Value& value) { 1453 if (value.isGCThing()) { 1454 AssertCellIsNotGray(value.toGCThing()); 1455 } 1456 } 1457 1458 MOZ_ALWAYS_INLINE void AssertValueIsNotGray(const Heap<Value>& value) { 1459 AssertValueIsNotGray(value.unbarrieredGet()); 1460 } 1461 1462 } // namespace JS 1463 #endif 1464 1465 /************************************************************************/ 1466 1467 namespace JS { 1468 1469 extern JS_PUBLIC_DATA const HandleValue NullHandleValue; 1470 extern JS_PUBLIC_DATA const HandleValue UndefinedHandleValue; 1471 extern JS_PUBLIC_DATA const HandleValue TrueHandleValue; 1472 extern JS_PUBLIC_DATA const HandleValue FalseHandleValue; 1473 extern JS_PUBLIC_DATA const Handle<mozilla::Maybe<Value>> NothingHandleValue; 1474 1475 } // namespace JS 1476 1477 #endif /* js_Value_h */