1 | /* |
2 | * Copyright (C) 2011-2019 Apple Inc. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #pragma once |
27 | |
28 | #include "CatchScope.h" |
29 | #include "Error.h" |
30 | #include "ExceptionHelpers.h" |
31 | #include "Identifier.h" |
32 | #include "InternalFunction.h" |
33 | #include "JSBigInt.h" |
34 | #include "JSCJSValue.h" |
35 | #include "JSCellInlines.h" |
36 | #include "JSFunction.h" |
37 | #include "JSObject.h" |
38 | #include "JSProxy.h" |
39 | #include "JSStringInlines.h" |
40 | #include "MathCommon.h" |
41 | #include <wtf/Variant.h> |
42 | #include <wtf/text/StringImpl.h> |
43 | |
44 | namespace JSC { |
45 | |
46 | ALWAYS_INLINE int32_t JSValue::toInt32(JSGlobalObject* globalObject) const |
47 | { |
48 | if (isInt32()) |
49 | return asInt32(); |
50 | return JSC::toInt32(toNumber(globalObject)); |
51 | } |
52 | |
53 | inline uint32_t JSValue::toUInt32(JSGlobalObject* globalObject) const |
54 | { |
55 | // See comment on JSC::toUInt32, in JSCJSValue.h. |
56 | return toInt32(globalObject); |
57 | } |
58 | |
59 | inline uint32_t JSValue::toIndex(JSGlobalObject* globalObject, const char* errorName) const |
60 | { |
61 | VM& vm = getVM(globalObject); |
62 | auto scope = DECLARE_THROW_SCOPE(vm); |
63 | |
64 | double d = toNumber(globalObject); |
65 | RETURN_IF_EXCEPTION(scope, 0); |
66 | if (d <= -1) { |
67 | throwException(globalObject, scope, createRangeError(globalObject, makeString(errorName, " cannot be negative" ))); |
68 | return 0; |
69 | } |
70 | if (d > std::numeric_limits<unsigned>::max()) { |
71 | throwException(globalObject, scope, createRangeError(globalObject, makeString(errorName, " too large" ))); |
72 | return 0; |
73 | } |
74 | |
75 | if (isInt32()) |
76 | return asInt32(); |
77 | RELEASE_AND_RETURN(scope, JSC::toInt32(d)); |
78 | } |
79 | |
80 | inline bool JSValue::isUInt32() const |
81 | { |
82 | return isInt32() && asInt32() >= 0; |
83 | } |
84 | |
85 | inline uint32_t JSValue::asUInt32() const |
86 | { |
87 | ASSERT(isUInt32()); |
88 | return asInt32(); |
89 | } |
90 | |
91 | inline double JSValue::asNumber() const |
92 | { |
93 | ASSERT(isNumber()); |
94 | return isInt32() ? asInt32() : asDouble(); |
95 | } |
96 | |
97 | inline JSValue jsNaN() |
98 | { |
99 | return JSValue(JSValue::EncodeAsDouble, PNaN); |
100 | } |
101 | |
102 | inline JSValue::JSValue(char i) |
103 | { |
104 | *this = JSValue(static_cast<int32_t>(i)); |
105 | } |
106 | |
107 | inline JSValue::JSValue(unsigned char i) |
108 | { |
109 | *this = JSValue(static_cast<int32_t>(i)); |
110 | } |
111 | |
112 | inline JSValue::JSValue(short i) |
113 | { |
114 | *this = JSValue(static_cast<int32_t>(i)); |
115 | } |
116 | |
117 | inline JSValue::JSValue(unsigned short i) |
118 | { |
119 | *this = JSValue(static_cast<int32_t>(i)); |
120 | } |
121 | |
122 | inline JSValue::JSValue(unsigned i) |
123 | { |
124 | if (static_cast<int32_t>(i) < 0) { |
125 | *this = JSValue(EncodeAsDouble, static_cast<double>(i)); |
126 | return; |
127 | } |
128 | *this = JSValue(static_cast<int32_t>(i)); |
129 | } |
130 | |
131 | inline JSValue::JSValue(long i) |
132 | { |
133 | if (static_cast<int32_t>(i) != i) { |
134 | *this = JSValue(EncodeAsDouble, static_cast<double>(i)); |
135 | return; |
136 | } |
137 | *this = JSValue(static_cast<int32_t>(i)); |
138 | } |
139 | |
140 | inline JSValue::JSValue(unsigned long i) |
141 | { |
142 | if (static_cast<uint32_t>(i) != i) { |
143 | *this = JSValue(EncodeAsDouble, static_cast<double>(i)); |
144 | return; |
145 | } |
146 | *this = JSValue(static_cast<uint32_t>(i)); |
147 | } |
148 | |
149 | inline JSValue::JSValue(long long i) |
150 | { |
151 | if (static_cast<int32_t>(i) != i) { |
152 | *this = JSValue(EncodeAsDouble, static_cast<double>(i)); |
153 | return; |
154 | } |
155 | *this = JSValue(static_cast<int32_t>(i)); |
156 | } |
157 | |
158 | inline JSValue::JSValue(unsigned long long i) |
159 | { |
160 | if (static_cast<uint32_t>(i) != i) { |
161 | *this = JSValue(EncodeAsDouble, static_cast<double>(i)); |
162 | return; |
163 | } |
164 | *this = JSValue(static_cast<uint32_t>(i)); |
165 | } |
166 | |
167 | inline JSValue::JSValue(double d) |
168 | { |
169 | if (canBeStrictInt32(d)) { |
170 | *this = JSValue(static_cast<int32_t>(d)); |
171 | return; |
172 | } |
173 | *this = JSValue(EncodeAsDouble, d); |
174 | } |
175 | |
176 | inline EncodedJSValue JSValue::encode(JSValue value) |
177 | { |
178 | return value.u.asInt64; |
179 | } |
180 | |
181 | inline JSValue JSValue::decode(EncodedJSValue encodedJSValue) |
182 | { |
183 | JSValue v; |
184 | v.u.asInt64 = encodedJSValue; |
185 | return v; |
186 | } |
187 | |
188 | #if USE(JSVALUE32_64) |
189 | inline JSValue::JSValue() |
190 | { |
191 | u.asBits.tag = EmptyValueTag; |
192 | u.asBits.payload = 0; |
193 | } |
194 | |
195 | inline JSValue::JSValue(JSNullTag) |
196 | { |
197 | u.asBits.tag = NullTag; |
198 | u.asBits.payload = 0; |
199 | } |
200 | |
201 | inline JSValue::JSValue(JSUndefinedTag) |
202 | { |
203 | u.asBits.tag = UndefinedTag; |
204 | u.asBits.payload = 0; |
205 | } |
206 | |
207 | inline JSValue::JSValue(JSTrueTag) |
208 | { |
209 | u.asBits.tag = BooleanTag; |
210 | u.asBits.payload = 1; |
211 | } |
212 | |
213 | inline JSValue::JSValue(JSFalseTag) |
214 | { |
215 | u.asBits.tag = BooleanTag; |
216 | u.asBits.payload = 0; |
217 | } |
218 | |
219 | inline JSValue::JSValue(HashTableDeletedValueTag) |
220 | { |
221 | u.asBits.tag = DeletedValueTag; |
222 | u.asBits.payload = 0; |
223 | } |
224 | |
225 | inline JSValue::JSValue(JSCell* ptr) |
226 | { |
227 | if (ptr) |
228 | u.asBits.tag = CellTag; |
229 | else |
230 | u.asBits.tag = EmptyValueTag; |
231 | u.asBits.payload = reinterpret_cast<int32_t>(ptr); |
232 | } |
233 | |
234 | inline JSValue::JSValue(const JSCell* ptr) |
235 | { |
236 | if (ptr) |
237 | u.asBits.tag = CellTag; |
238 | else |
239 | u.asBits.tag = EmptyValueTag; |
240 | u.asBits.payload = reinterpret_cast<int32_t>(const_cast<JSCell*>(ptr)); |
241 | } |
242 | |
243 | inline JSValue::operator bool() const |
244 | { |
245 | ASSERT(tag() != DeletedValueTag); |
246 | return tag() != EmptyValueTag; |
247 | } |
248 | |
249 | inline bool JSValue::operator==(const JSValue& other) const |
250 | { |
251 | return u.asInt64 == other.u.asInt64; |
252 | } |
253 | |
254 | inline bool JSValue::operator!=(const JSValue& other) const |
255 | { |
256 | return u.asInt64 != other.u.asInt64; |
257 | } |
258 | |
259 | inline bool JSValue::isEmpty() const |
260 | { |
261 | return tag() == EmptyValueTag; |
262 | } |
263 | |
264 | inline bool JSValue::isUndefined() const |
265 | { |
266 | return tag() == UndefinedTag; |
267 | } |
268 | |
269 | inline bool JSValue::isNull() const |
270 | { |
271 | return tag() == NullTag; |
272 | } |
273 | |
274 | inline bool JSValue::isUndefinedOrNull() const |
275 | { |
276 | return isUndefined() || isNull(); |
277 | } |
278 | |
279 | inline bool JSValue::isCell() const |
280 | { |
281 | return tag() == CellTag; |
282 | } |
283 | |
284 | inline bool JSValue::isInt32() const |
285 | { |
286 | return tag() == Int32Tag; |
287 | } |
288 | |
289 | inline bool JSValue::isDouble() const |
290 | { |
291 | return tag() < LowestTag; |
292 | } |
293 | |
294 | inline bool JSValue::isTrue() const |
295 | { |
296 | return tag() == BooleanTag && payload(); |
297 | } |
298 | |
299 | inline bool JSValue::isFalse() const |
300 | { |
301 | return tag() == BooleanTag && !payload(); |
302 | } |
303 | |
304 | inline uint32_t JSValue::tag() const |
305 | { |
306 | return u.asBits.tag; |
307 | } |
308 | |
309 | inline int32_t JSValue::payload() const |
310 | { |
311 | return u.asBits.payload; |
312 | } |
313 | |
314 | inline int32_t JSValue::asInt32() const |
315 | { |
316 | ASSERT(isInt32()); |
317 | return u.asBits.payload; |
318 | } |
319 | |
320 | inline double JSValue::asDouble() const |
321 | { |
322 | ASSERT(isDouble()); |
323 | return u.asDouble; |
324 | } |
325 | |
326 | ALWAYS_INLINE JSCell* JSValue::asCell() const |
327 | { |
328 | ASSERT(isCell()); |
329 | return reinterpret_cast<JSCell*>(u.asBits.payload); |
330 | } |
331 | |
332 | ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, double d) |
333 | { |
334 | ASSERT(!isImpureNaN(d)); |
335 | u.asDouble = d; |
336 | } |
337 | |
338 | inline JSValue::JSValue(int i) |
339 | { |
340 | u.asBits.tag = Int32Tag; |
341 | u.asBits.payload = i; |
342 | } |
343 | |
344 | inline JSValue::JSValue(int32_t tag, int32_t payload) |
345 | { |
346 | u.asBits.tag = tag; |
347 | u.asBits.payload = payload; |
348 | } |
349 | |
350 | inline bool JSValue::isNumber() const |
351 | { |
352 | return isInt32() || isDouble(); |
353 | } |
354 | |
355 | inline bool JSValue::isBoolean() const |
356 | { |
357 | return tag() == BooleanTag; |
358 | } |
359 | |
360 | inline bool JSValue::asBoolean() const |
361 | { |
362 | ASSERT(isBoolean()); |
363 | return payload(); |
364 | } |
365 | |
366 | #else // !USE(JSVALUE32_64) i.e. USE(JSVALUE64) |
367 | |
368 | // 0x0 can never occur naturally because it has a tag of 00, indicating a pointer value, but a payload of 0x0, which is in the (invalid) zero page. |
369 | inline JSValue::JSValue() |
370 | { |
371 | u.asInt64 = ValueEmpty; |
372 | } |
373 | |
374 | // 0x4 can never occur naturally because it has a tag of 00, indicating a pointer value, but a payload of 0x4, which is in the (invalid) zero page. |
375 | inline JSValue::JSValue(HashTableDeletedValueTag) |
376 | { |
377 | u.asInt64 = ValueDeleted; |
378 | } |
379 | |
380 | inline JSValue::JSValue(JSCell* ptr) |
381 | { |
382 | u.asInt64 = reinterpret_cast<uintptr_t>(ptr); |
383 | } |
384 | |
385 | inline JSValue::JSValue(const JSCell* ptr) |
386 | { |
387 | u.asInt64 = reinterpret_cast<uintptr_t>(const_cast<JSCell*>(ptr)); |
388 | } |
389 | |
390 | inline JSValue::operator bool() const |
391 | { |
392 | return u.asInt64; |
393 | } |
394 | |
395 | inline bool JSValue::operator==(const JSValue& other) const |
396 | { |
397 | return u.asInt64 == other.u.asInt64; |
398 | } |
399 | |
400 | inline bool JSValue::operator!=(const JSValue& other) const |
401 | { |
402 | return u.asInt64 != other.u.asInt64; |
403 | } |
404 | |
405 | inline bool JSValue::isEmpty() const |
406 | { |
407 | return u.asInt64 == ValueEmpty; |
408 | } |
409 | |
410 | inline bool JSValue::isUndefined() const |
411 | { |
412 | return asValue() == JSValue(JSUndefined); |
413 | } |
414 | |
415 | inline bool JSValue::isNull() const |
416 | { |
417 | return asValue() == JSValue(JSNull); |
418 | } |
419 | |
420 | inline bool JSValue::isTrue() const |
421 | { |
422 | return asValue() == JSValue(JSTrue); |
423 | } |
424 | |
425 | inline bool JSValue::isFalse() const |
426 | { |
427 | return asValue() == JSValue(JSFalse); |
428 | } |
429 | |
430 | inline bool JSValue::asBoolean() const |
431 | { |
432 | ASSERT(isBoolean()); |
433 | return asValue() == JSValue(JSTrue); |
434 | } |
435 | |
436 | inline int32_t JSValue::asInt32() const |
437 | { |
438 | ASSERT(isInt32()); |
439 | return static_cast<int32_t>(u.asInt64); |
440 | } |
441 | |
442 | inline bool JSValue::isDouble() const |
443 | { |
444 | return isNumber() && !isInt32(); |
445 | } |
446 | |
447 | inline JSValue::JSValue(JSNullTag) |
448 | { |
449 | u.asInt64 = ValueNull; |
450 | } |
451 | |
452 | inline JSValue::JSValue(JSUndefinedTag) |
453 | { |
454 | u.asInt64 = ValueUndefined; |
455 | } |
456 | |
457 | inline JSValue::JSValue(JSTrueTag) |
458 | { |
459 | u.asInt64 = ValueTrue; |
460 | } |
461 | |
462 | inline JSValue::JSValue(JSFalseTag) |
463 | { |
464 | u.asInt64 = ValueFalse; |
465 | } |
466 | |
467 | inline bool JSValue::isUndefinedOrNull() const |
468 | { |
469 | // Undefined and null share the same value, bar the 'undefined' bit in the extended tag. |
470 | return (u.asInt64 & ~UndefinedTag) == ValueNull; |
471 | } |
472 | |
473 | inline bool JSValue::isBoolean() const |
474 | { |
475 | return (u.asInt64 & ~1) == ValueFalse; |
476 | } |
477 | |
478 | inline bool JSValue::isCell() const |
479 | { |
480 | return !(u.asInt64 & NotCellMask); |
481 | } |
482 | |
483 | inline bool JSValue::isInt32() const |
484 | { |
485 | return (u.asInt64 & NumberTag) == NumberTag; |
486 | } |
487 | |
488 | inline int64_t reinterpretDoubleToInt64(double value) |
489 | { |
490 | return bitwise_cast<int64_t>(value); |
491 | } |
492 | inline double reinterpretInt64ToDouble(int64_t value) |
493 | { |
494 | return bitwise_cast<double>(value); |
495 | } |
496 | |
497 | ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, double d) |
498 | { |
499 | ASSERT(!isImpureNaN(d)); |
500 | u.asInt64 = reinterpretDoubleToInt64(d) + JSValue::DoubleEncodeOffset; |
501 | } |
502 | |
503 | inline JSValue::JSValue(int i) |
504 | { |
505 | u.asInt64 = JSValue::NumberTag | static_cast<uint32_t>(i); |
506 | } |
507 | |
508 | inline double JSValue::asDouble() const |
509 | { |
510 | ASSERT(isDouble()); |
511 | return reinterpretInt64ToDouble(u.asInt64 - JSValue::DoubleEncodeOffset); |
512 | } |
513 | |
514 | inline bool JSValue::isNumber() const |
515 | { |
516 | return u.asInt64 & JSValue::NumberTag; |
517 | } |
518 | |
519 | ALWAYS_INLINE JSCell* JSValue::asCell() const |
520 | { |
521 | ASSERT(isCell()); |
522 | return u.ptr; |
523 | } |
524 | |
525 | #endif // USE(JSVALUE64) |
526 | |
527 | inline int64_t tryConvertToInt52(double number) |
528 | { |
529 | if (number != number) |
530 | return JSValue::notInt52; |
531 | #if OS(WINDOWS) && CPU(X86) |
532 | // The VS Compiler for 32-bit builds generates a floating point error when attempting to cast |
533 | // from an infinity to a 64-bit integer. We leave this routine with the floating point error |
534 | // left in a register, causing undefined behavior in later floating point operations. |
535 | // |
536 | // To avoid this issue, we check for infinity here, and return false in that case. |
537 | if (std::isinf(number)) |
538 | return JSValue::notInt52; |
539 | #endif |
540 | int64_t asInt64 = static_cast<int64_t>(number); |
541 | if (asInt64 != number) |
542 | return JSValue::notInt52; |
543 | if (!asInt64 && std::signbit(number)) |
544 | return JSValue::notInt52; |
545 | if (asInt64 >= (static_cast<int64_t>(1) << (JSValue::numberOfInt52Bits - 1))) |
546 | return JSValue::notInt52; |
547 | if (asInt64 < -(static_cast<int64_t>(1) << (JSValue::numberOfInt52Bits - 1))) |
548 | return JSValue::notInt52; |
549 | return asInt64; |
550 | } |
551 | |
552 | inline bool isInt52(double number) |
553 | { |
554 | return tryConvertToInt52(number) != JSValue::notInt52; |
555 | } |
556 | |
557 | inline bool JSValue::isAnyInt() const |
558 | { |
559 | if (isInt32()) |
560 | return true; |
561 | if (!isNumber()) |
562 | return false; |
563 | return isInt52(asDouble()); |
564 | } |
565 | |
566 | inline int64_t JSValue::asAnyInt() const |
567 | { |
568 | ASSERT(isAnyInt()); |
569 | if (isInt32()) |
570 | return asInt32(); |
571 | return static_cast<int64_t>(asDouble()); |
572 | } |
573 | |
574 | inline bool JSValue::isInt32AsAnyInt() const |
575 | { |
576 | if (!isAnyInt()) |
577 | return false; |
578 | int64_t value = asAnyInt(); |
579 | return value >= INT32_MIN && value <= INT32_MAX; |
580 | } |
581 | |
582 | inline int32_t JSValue::asInt32AsAnyInt() const |
583 | { |
584 | ASSERT(isInt32AsAnyInt()); |
585 | if (isInt32()) |
586 | return asInt32(); |
587 | return static_cast<int32_t>(asDouble()); |
588 | } |
589 | |
590 | inline bool JSValue::isUInt32AsAnyInt() const |
591 | { |
592 | if (!isAnyInt()) |
593 | return false; |
594 | int64_t value = asAnyInt(); |
595 | return value >= 0 && value <= UINT32_MAX; |
596 | } |
597 | |
598 | inline uint32_t JSValue::asUInt32AsAnyInt() const |
599 | { |
600 | ASSERT(isUInt32AsAnyInt()); |
601 | if (isUInt32()) |
602 | return asUInt32(); |
603 | return static_cast<uint32_t>(asDouble()); |
604 | } |
605 | |
606 | inline bool JSValue::isString() const |
607 | { |
608 | return isCell() && asCell()->isString(); |
609 | } |
610 | |
611 | inline bool JSValue::isBigInt() const |
612 | { |
613 | return isCell() && asCell()->isBigInt(); |
614 | } |
615 | |
616 | inline bool JSValue::isSymbol() const |
617 | { |
618 | return isCell() && asCell()->isSymbol(); |
619 | } |
620 | |
621 | inline bool JSValue::isPrimitive() const |
622 | { |
623 | return !isCell() || asCell()->isString() || asCell()->isSymbol() || asCell()->isBigInt(); |
624 | } |
625 | |
626 | inline bool JSValue::isGetterSetter() const |
627 | { |
628 | return isCell() && asCell()->isGetterSetter(); |
629 | } |
630 | |
631 | inline bool JSValue::isCustomGetterSetter() const |
632 | { |
633 | return isCell() && asCell()->isCustomGetterSetter(); |
634 | } |
635 | |
636 | inline bool JSValue::isObject() const |
637 | { |
638 | return isCell() && asCell()->isObject(); |
639 | } |
640 | |
641 | inline bool JSValue::getString(JSGlobalObject* globalObject, String& s) const |
642 | { |
643 | return isCell() && asCell()->getString(globalObject, s); |
644 | } |
645 | |
646 | inline String JSValue::getString(JSGlobalObject* globalObject) const |
647 | { |
648 | return isCell() ? asCell()->getString(globalObject) : String(); |
649 | } |
650 | |
651 | template <typename Base> String HandleConverter<Base, Unknown>::getString(JSGlobalObject* globalObject) const |
652 | { |
653 | return jsValue().getString(globalObject); |
654 | } |
655 | |
656 | inline JSObject* JSValue::getObject() const |
657 | { |
658 | return isCell() ? asCell()->getObject() : 0; |
659 | } |
660 | |
661 | ALWAYS_INLINE bool JSValue::getUInt32(uint32_t& v) const |
662 | { |
663 | if (isInt32()) { |
664 | int32_t i = asInt32(); |
665 | v = static_cast<uint32_t>(i); |
666 | return i >= 0; |
667 | } |
668 | if (isDouble()) { |
669 | double d = asDouble(); |
670 | v = static_cast<uint32_t>(d); |
671 | return v == d; |
672 | } |
673 | return false; |
674 | } |
675 | |
676 | ALWAYS_INLINE Identifier JSValue::toPropertyKey(JSGlobalObject* globalObject) const |
677 | { |
678 | VM& vm = getVM(globalObject); |
679 | auto scope = DECLARE_THROW_SCOPE(vm); |
680 | |
681 | if (isString()) |
682 | RELEASE_AND_RETURN(scope, asString(*this)->toIdentifier(globalObject)); |
683 | |
684 | JSValue primitive = toPrimitive(globalObject, PreferString); |
685 | RETURN_IF_EXCEPTION(scope, vm.propertyNames->emptyIdentifier); |
686 | if (primitive.isSymbol()) |
687 | RELEASE_AND_RETURN(scope, Identifier::fromUid(asSymbol(primitive)->privateName())); |
688 | |
689 | auto string = primitive.toString(globalObject); |
690 | RETURN_IF_EXCEPTION(scope, { }); |
691 | RELEASE_AND_RETURN(scope, string->toIdentifier(globalObject)); |
692 | } |
693 | |
694 | inline JSValue JSValue::toPrimitive(JSGlobalObject* globalObject, PreferredPrimitiveType preferredType) const |
695 | { |
696 | return isCell() ? asCell()->toPrimitive(globalObject, preferredType) : asValue(); |
697 | } |
698 | |
699 | inline PreferredPrimitiveType toPreferredPrimitiveType(JSGlobalObject* globalObject, JSValue value) |
700 | { |
701 | VM& vm = getVM(globalObject); |
702 | auto scope = DECLARE_THROW_SCOPE(vm); |
703 | |
704 | if (!value.isString()) { |
705 | throwTypeError(globalObject, scope, "Primitive hint is not a string."_s ); |
706 | return NoPreference; |
707 | } |
708 | |
709 | StringImpl* hintString = asString(value)->value(globalObject).impl(); |
710 | RETURN_IF_EXCEPTION(scope, NoPreference); |
711 | |
712 | if (WTF::equal(hintString, "default" )) |
713 | return NoPreference; |
714 | if (WTF::equal(hintString, "number" )) |
715 | return PreferNumber; |
716 | if (WTF::equal(hintString, "string" )) |
717 | return PreferString; |
718 | |
719 | throwTypeError(globalObject, scope, "Expected primitive hint to match one of 'default', 'number', 'string'."_s ); |
720 | return NoPreference; |
721 | } |
722 | |
723 | inline bool JSValue::getPrimitiveNumber(JSGlobalObject* globalObject, double& number, JSValue& value) |
724 | { |
725 | if (isInt32()) { |
726 | number = asInt32(); |
727 | value = *this; |
728 | return true; |
729 | } |
730 | if (isDouble()) { |
731 | number = asDouble(); |
732 | value = *this; |
733 | return true; |
734 | } |
735 | if (isCell()) |
736 | return asCell()->getPrimitiveNumber(globalObject, number, value); |
737 | if (isTrue()) { |
738 | number = 1.0; |
739 | value = *this; |
740 | return true; |
741 | } |
742 | if (isFalse() || isNull()) { |
743 | number = 0.0; |
744 | value = *this; |
745 | return true; |
746 | } |
747 | ASSERT(isUndefined()); |
748 | number = PNaN; |
749 | value = *this; |
750 | return true; |
751 | } |
752 | |
753 | ALWAYS_INLINE double JSValue::toNumber(JSGlobalObject* globalObject) const |
754 | { |
755 | if (isInt32()) |
756 | return asInt32(); |
757 | if (isDouble()) |
758 | return asDouble(); |
759 | return toNumberSlowCase(globalObject); |
760 | } |
761 | |
762 | ALWAYS_INLINE Variant<JSBigInt*, double> JSValue::toNumeric(JSGlobalObject* globalObject) const |
763 | { |
764 | if (isInt32()) |
765 | return asInt32(); |
766 | if (isDouble()) |
767 | return asDouble(); |
768 | if (isBigInt()) |
769 | return asBigInt(*this); |
770 | |
771 | VM& vm = getVM(globalObject); |
772 | auto scope = DECLARE_THROW_SCOPE(vm); |
773 | JSValue primValue = this->toPrimitive(globalObject, PreferNumber); |
774 | RETURN_IF_EXCEPTION(scope, 0); |
775 | if (primValue.isBigInt()) |
776 | return asBigInt(primValue); |
777 | double value = primValue.toNumber(globalObject); |
778 | RETURN_IF_EXCEPTION(scope, 0); |
779 | return value; |
780 | } |
781 | |
782 | ALWAYS_INLINE Variant<JSBigInt*, int32_t> JSValue::toBigIntOrInt32(JSGlobalObject* globalObject) const |
783 | { |
784 | if (isInt32()) |
785 | return asInt32(); |
786 | if (isDouble() && canBeInt32(asDouble())) |
787 | return static_cast<int32_t>(asDouble()); |
788 | if (isBigInt()) |
789 | return asBigInt(*this); |
790 | |
791 | VM& vm = getVM(globalObject); |
792 | auto scope = DECLARE_THROW_SCOPE(vm); |
793 | JSValue primValue = this->toPrimitive(globalObject, PreferNumber); |
794 | RETURN_IF_EXCEPTION(scope, 0); |
795 | if (primValue.isBigInt()) |
796 | return asBigInt(primValue); |
797 | int32_t value = primValue.toInt32(globalObject); |
798 | RETURN_IF_EXCEPTION(scope, 0); |
799 | return value; |
800 | } |
801 | |
802 | inline JSObject* JSValue::toObject(JSGlobalObject* globalObject) const |
803 | { |
804 | return isCell() ? asCell()->toObject(globalObject) : toObjectSlowCase(globalObject); |
805 | } |
806 | |
807 | inline bool JSValue::isFunction(VM& vm) const |
808 | { |
809 | if (!isCell()) |
810 | return false; |
811 | return asCell()->isFunction(vm); |
812 | } |
813 | |
814 | inline bool JSValue::isCallable(VM& vm, CallType& callType, CallData& callData) const |
815 | { |
816 | if (!isCell()) |
817 | return false; |
818 | return asCell()->isCallable(vm, callType, callData); |
819 | } |
820 | |
821 | inline bool JSValue::isConstructor(VM& vm) const |
822 | { |
823 | if (!isCell()) |
824 | return false; |
825 | return asCell()->isConstructor(vm); |
826 | } |
827 | |
828 | inline bool JSValue::isConstructor(VM& vm, ConstructType& constructType, ConstructData& constructData) const |
829 | { |
830 | if (!isCell()) |
831 | return false; |
832 | return asCell()->isConstructor(vm, constructType, constructData); |
833 | } |
834 | |
835 | // this method is here to be after the inline declaration of JSCell::inherits |
836 | inline bool JSValue::inherits(VM& vm, const ClassInfo* classInfo) const |
837 | { |
838 | return isCell() && asCell()->inherits(vm, classInfo); |
839 | } |
840 | |
841 | template<typename Target> |
842 | inline bool JSValue::inherits(VM& vm) const |
843 | { |
844 | return isCell() && asCell()->inherits<Target>(vm); |
845 | } |
846 | |
847 | inline const ClassInfo* JSValue::classInfoOrNull(VM& vm) const |
848 | { |
849 | return isCell() ? asCell()->classInfo(vm) : nullptr; |
850 | } |
851 | |
852 | inline JSValue JSValue::toThis(JSGlobalObject* globalObject, ECMAMode ecmaMode) const |
853 | { |
854 | return isCell() ? asCell()->methodTable(getVM(globalObject))->toThis(asCell(), globalObject, ecmaMode) : toThisSlowCase(globalObject, ecmaMode); |
855 | } |
856 | |
857 | ALWAYS_INLINE JSValue JSValue::get(JSGlobalObject* globalObject, PropertyName propertyName) const |
858 | { |
859 | PropertySlot slot(asValue(), PropertySlot::InternalMethodType::Get); |
860 | return get(globalObject, propertyName, slot); |
861 | } |
862 | |
863 | ALWAYS_INLINE JSValue JSValue::get(JSGlobalObject* globalObject, PropertyName propertyName, PropertySlot& slot) const |
864 | { |
865 | auto scope = DECLARE_THROW_SCOPE(getVM(globalObject)); |
866 | bool hasSlot = getPropertySlot(globalObject, propertyName, slot); |
867 | EXCEPTION_ASSERT(!scope.exception() || !hasSlot); |
868 | if (!hasSlot) |
869 | return jsUndefined(); |
870 | RELEASE_AND_RETURN(scope, slot.getValue(globalObject, propertyName)); |
871 | } |
872 | |
873 | template<typename CallbackWhenNoException> |
874 | ALWAYS_INLINE typename std::result_of<CallbackWhenNoException(bool, PropertySlot&)>::type JSValue::getPropertySlot(JSGlobalObject* globalObject, PropertyName propertyName, CallbackWhenNoException callback) const |
875 | { |
876 | PropertySlot slot(asValue(), PropertySlot::InternalMethodType::Get); |
877 | return getPropertySlot(globalObject, propertyName, slot, callback); |
878 | } |
879 | |
880 | template<typename CallbackWhenNoException> |
881 | ALWAYS_INLINE typename std::result_of<CallbackWhenNoException(bool, PropertySlot&)>::type JSValue::getPropertySlot(JSGlobalObject* globalObject, PropertyName propertyName, PropertySlot& slot, CallbackWhenNoException callback) const |
882 | { |
883 | auto scope = DECLARE_THROW_SCOPE(getVM(globalObject)); |
884 | bool found = getPropertySlot(globalObject, propertyName, slot); |
885 | RETURN_IF_EXCEPTION(scope, { }); |
886 | RELEASE_AND_RETURN(scope, callback(found, slot)); |
887 | } |
888 | |
889 | ALWAYS_INLINE bool JSValue::getPropertySlot(JSGlobalObject* globalObject, PropertyName propertyName, PropertySlot& slot) const |
890 | { |
891 | auto scope = DECLARE_THROW_SCOPE(getVM(globalObject)); |
892 | // If this is a primitive, we'll need to synthesize the prototype - |
893 | // and if it's a string there are special properties to check first. |
894 | JSObject* object; |
895 | if (UNLIKELY(!isObject())) { |
896 | if (isString()) { |
897 | bool hasProperty = asString(*this)->getStringPropertySlot(globalObject, propertyName, slot); |
898 | RETURN_IF_EXCEPTION(scope, false); |
899 | if (hasProperty) |
900 | return true; |
901 | } |
902 | object = synthesizePrototype(globalObject); |
903 | EXCEPTION_ASSERT(!!scope.exception() == !object); |
904 | if (UNLIKELY(!object)) |
905 | return false; |
906 | } else |
907 | object = asObject(asCell()); |
908 | |
909 | RELEASE_AND_RETURN(scope, object->getPropertySlot(globalObject, propertyName, slot)); |
910 | } |
911 | |
912 | ALWAYS_INLINE bool JSValue::getOwnPropertySlot(JSGlobalObject* globalObject, PropertyName propertyName, PropertySlot& slot) const |
913 | { |
914 | // If this is a primitive, we'll need to synthesize the prototype - |
915 | // and if it's a string there are special properties to check first. |
916 | auto scope = DECLARE_THROW_SCOPE(getVM(globalObject)); |
917 | if (UNLIKELY(!isObject())) { |
918 | if (isString()) |
919 | RELEASE_AND_RETURN(scope, asString(*this)->getStringPropertySlot(globalObject, propertyName, slot)); |
920 | |
921 | if (isUndefinedOrNull()) |
922 | throwException(globalObject, scope, createNotAnObjectError(globalObject, *this)); |
923 | return false; |
924 | } |
925 | RELEASE_AND_RETURN(scope, asObject(asCell())->getOwnPropertySlotInline(globalObject, propertyName, slot)); |
926 | } |
927 | |
928 | ALWAYS_INLINE JSValue JSValue::get(JSGlobalObject* globalObject, unsigned propertyName) const |
929 | { |
930 | PropertySlot slot(asValue(), PropertySlot::InternalMethodType::Get); |
931 | return get(globalObject, propertyName, slot); |
932 | } |
933 | |
934 | ALWAYS_INLINE JSValue JSValue::get(JSGlobalObject* globalObject, unsigned propertyName, PropertySlot& slot) const |
935 | { |
936 | auto scope = DECLARE_THROW_SCOPE(getVM(globalObject)); |
937 | // If this is a primitive, we'll need to synthesize the prototype - |
938 | // and if it's a string there are special properties to check first. |
939 | JSObject* object; |
940 | if (UNLIKELY(!isObject())) { |
941 | if (isString()) { |
942 | bool hasProperty = asString(*this)->getStringPropertySlot(globalObject, propertyName, slot); |
943 | RETURN_IF_EXCEPTION(scope, { }); |
944 | if (hasProperty) |
945 | RELEASE_AND_RETURN(scope, slot.getValue(globalObject, propertyName)); |
946 | } |
947 | object = synthesizePrototype(globalObject); |
948 | EXCEPTION_ASSERT(!!scope.exception() == !object); |
949 | if (UNLIKELY(!object)) |
950 | return JSValue(); |
951 | } else |
952 | object = asObject(asCell()); |
953 | |
954 | bool hasSlot = object->getPropertySlot(globalObject, propertyName, slot); |
955 | EXCEPTION_ASSERT(!scope.exception() || !hasSlot); |
956 | if (!hasSlot) |
957 | return jsUndefined(); |
958 | RELEASE_AND_RETURN(scope, slot.getValue(globalObject, propertyName)); |
959 | } |
960 | |
961 | ALWAYS_INLINE JSValue JSValue::get(JSGlobalObject* globalObject, uint64_t propertyName) const |
962 | { |
963 | if (LIKELY(propertyName <= std::numeric_limits<unsigned>::max())) |
964 | return get(globalObject, static_cast<unsigned>(propertyName)); |
965 | return get(globalObject, Identifier::from(getVM(globalObject), static_cast<double>(propertyName))); |
966 | } |
967 | |
968 | inline bool JSValue::put(JSGlobalObject* globalObject, PropertyName propertyName, JSValue value, PutPropertySlot& slot) |
969 | { |
970 | if (UNLIKELY(!isCell())) |
971 | return putToPrimitive(globalObject, propertyName, value, slot); |
972 | |
973 | return asCell()->methodTable(getVM(globalObject))->put(asCell(), globalObject, propertyName, value, slot); |
974 | } |
975 | |
976 | ALWAYS_INLINE bool JSValue::putInline(JSGlobalObject* globalObject, PropertyName propertyName, JSValue value, PutPropertySlot& slot) |
977 | { |
978 | if (UNLIKELY(!isCell())) |
979 | return putToPrimitive(globalObject, propertyName, value, slot); |
980 | return asCell()->putInline(globalObject, propertyName, value, slot); |
981 | } |
982 | |
983 | inline bool JSValue::putByIndex(JSGlobalObject* globalObject, unsigned propertyName, JSValue value, bool shouldThrow) |
984 | { |
985 | if (UNLIKELY(!isCell())) |
986 | return putToPrimitiveByIndex(globalObject, propertyName, value, shouldThrow); |
987 | |
988 | return asCell()->methodTable(getVM(globalObject))->putByIndex(asCell(), globalObject, propertyName, value, shouldThrow); |
989 | } |
990 | |
991 | inline Structure* JSValue::structureOrNull() const |
992 | { |
993 | if (isCell()) |
994 | return asCell()->structure(); |
995 | return nullptr; |
996 | } |
997 | |
998 | inline JSValue JSValue::structureOrUndefined() const |
999 | { |
1000 | if (isCell()) |
1001 | return JSValue(asCell()->structure()); |
1002 | return jsUndefined(); |
1003 | } |
1004 | |
1005 | // ECMA 11.9.3 |
1006 | inline bool JSValue::equal(JSGlobalObject* globalObject, JSValue v1, JSValue v2) |
1007 | { |
1008 | if (v1.isInt32() && v2.isInt32()) |
1009 | return v1 == v2; |
1010 | |
1011 | return equalSlowCase(globalObject, v1, v2); |
1012 | } |
1013 | |
1014 | ALWAYS_INLINE bool JSValue::equalSlowCaseInline(JSGlobalObject* globalObject, JSValue v1, JSValue v2) |
1015 | { |
1016 | VM& vm = getVM(globalObject); |
1017 | auto scope = DECLARE_THROW_SCOPE(vm); |
1018 | do { |
1019 | if (v1.isNumber() && v2.isNumber()) |
1020 | return v1.asNumber() == v2.asNumber(); |
1021 | |
1022 | bool s1 = v1.isString(); |
1023 | bool s2 = v2.isString(); |
1024 | if (s1 && s2) |
1025 | RELEASE_AND_RETURN(scope, asString(v1)->equal(globalObject, asString(v2))); |
1026 | |
1027 | if (v1.isBigInt() && s2) { |
1028 | String v2String = asString(v2)->value(globalObject); |
1029 | RETURN_IF_EXCEPTION(scope, false); |
1030 | JSBigInt* n = JSBigInt::stringToBigInt(globalObject, v2String); |
1031 | RETURN_IF_EXCEPTION(scope, false); |
1032 | if (!n) |
1033 | return false; |
1034 | |
1035 | v2 = JSValue(n); |
1036 | continue; |
1037 | } |
1038 | |
1039 | if (s1 && v2.isBigInt()) { |
1040 | String v1String = asString(v1)->value(globalObject); |
1041 | RETURN_IF_EXCEPTION(scope, false); |
1042 | JSBigInt* n = JSBigInt::stringToBigInt(globalObject, v1String); |
1043 | RETURN_IF_EXCEPTION(scope, false); |
1044 | if (!n) |
1045 | return false; |
1046 | |
1047 | v1 = JSValue(n); |
1048 | continue; |
1049 | } |
1050 | |
1051 | if (v1.isUndefinedOrNull()) { |
1052 | if (v2.isUndefinedOrNull()) |
1053 | return true; |
1054 | if (!v2.isCell()) |
1055 | return false; |
1056 | return v2.asCell()->structure(vm)->masqueradesAsUndefined(globalObject); |
1057 | } |
1058 | |
1059 | if (v2.isUndefinedOrNull()) { |
1060 | if (!v1.isCell()) |
1061 | return false; |
1062 | return v1.asCell()->structure(vm)->masqueradesAsUndefined(globalObject); |
1063 | } |
1064 | |
1065 | if (v1.isObject()) { |
1066 | if (v2.isObject()) |
1067 | return v1 == v2; |
1068 | JSValue p1 = v1.toPrimitive(globalObject); |
1069 | RETURN_IF_EXCEPTION(scope, false); |
1070 | v1 = p1; |
1071 | if (v1.isInt32() && v2.isInt32()) |
1072 | return v1 == v2; |
1073 | continue; |
1074 | } |
1075 | |
1076 | if (v2.isObject()) { |
1077 | JSValue p2 = v2.toPrimitive(globalObject); |
1078 | RETURN_IF_EXCEPTION(scope, false); |
1079 | v2 = p2; |
1080 | if (v1.isInt32() && v2.isInt32()) |
1081 | return v1 == v2; |
1082 | continue; |
1083 | } |
1084 | |
1085 | bool sym1 = v1.isSymbol(); |
1086 | bool sym2 = v2.isSymbol(); |
1087 | if (sym1 || sym2) { |
1088 | if (sym1 && sym2) |
1089 | return asSymbol(v1) == asSymbol(v2); |
1090 | return false; |
1091 | } |
1092 | |
1093 | if (s1 || s2) { |
1094 | double d1 = v1.toNumber(globalObject); |
1095 | RETURN_IF_EXCEPTION(scope, false); |
1096 | double d2 = v2.toNumber(globalObject); |
1097 | RETURN_IF_EXCEPTION(scope, false); |
1098 | return d1 == d2; |
1099 | } |
1100 | |
1101 | if (v1.isBoolean()) { |
1102 | if (v2.isNumber()) |
1103 | return static_cast<double>(v1.asBoolean()) == v2.asNumber(); |
1104 | else if (v2.isBigInt()) { |
1105 | v1 = JSValue(v1.toNumber(globalObject)); |
1106 | continue; |
1107 | } |
1108 | } else if (v2.isBoolean()) { |
1109 | if (v1.isNumber()) |
1110 | return v1.asNumber() == static_cast<double>(v2.asBoolean()); |
1111 | else if (v1.isBigInt()) { |
1112 | v2 = JSValue(v2.toNumber(globalObject)); |
1113 | continue; |
1114 | } |
1115 | } |
1116 | |
1117 | if (v1.isBigInt() && v2.isBigInt()) |
1118 | return JSBigInt::equals(asBigInt(v1), asBigInt(v2)); |
1119 | |
1120 | if (v1.isBigInt() && v2.isNumber()) |
1121 | return asBigInt(v1)->equalsToNumber(v2); |
1122 | |
1123 | if (v2.isBigInt() && v1.isNumber()) |
1124 | return asBigInt(v2)->equalsToNumber(v1); |
1125 | |
1126 | return v1 == v2; |
1127 | } while (true); |
1128 | } |
1129 | |
1130 | // ECMA 11.9.3 |
1131 | ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(JSGlobalObject* globalObject, JSValue v1, JSValue v2) |
1132 | { |
1133 | ASSERT(v1.isCell() && v2.isCell()); |
1134 | |
1135 | if (v1.asCell()->isString() && v2.asCell()->isString()) |
1136 | return asString(v1)->equal(globalObject, asString(v2)); |
1137 | if (v1.isBigInt() && v2.isBigInt()) |
1138 | return JSBigInt::equals(asBigInt(v1), asBigInt(v2)); |
1139 | return v1 == v2; |
1140 | } |
1141 | |
1142 | inline bool JSValue::strictEqual(JSGlobalObject* globalObject, JSValue v1, JSValue v2) |
1143 | { |
1144 | if (v1.isInt32() && v2.isInt32()) |
1145 | return v1 == v2; |
1146 | |
1147 | if (v1.isNumber() && v2.isNumber()) |
1148 | return v1.asNumber() == v2.asNumber(); |
1149 | |
1150 | if (!v1.isCell() || !v2.isCell()) |
1151 | return v1 == v2; |
1152 | |
1153 | return strictEqualSlowCaseInline(globalObject, v1, v2); |
1154 | } |
1155 | |
1156 | inline int32_t JSValue::asInt32ForArithmetic() const |
1157 | { |
1158 | if (isBoolean()) |
1159 | return asBoolean(); |
1160 | return asInt32(); |
1161 | } |
1162 | |
1163 | inline TriState JSValue::pureStrictEqual(JSValue v1, JSValue v2) |
1164 | { |
1165 | if (v1.isInt32() && v2.isInt32()) |
1166 | return triState(v1 == v2); |
1167 | |
1168 | if (v1.isNumber() && v2.isNumber()) |
1169 | return triState(v1.asNumber() == v2.asNumber()); |
1170 | |
1171 | if (!v1.isCell() || !v2.isCell()) |
1172 | return triState(v1 == v2); |
1173 | |
1174 | if (v1.asCell()->isString() && v2.asCell()->isString()) { |
1175 | const StringImpl* v1String = asString(v1)->tryGetValueImpl(); |
1176 | const StringImpl* v2String = asString(v2)->tryGetValueImpl(); |
1177 | if (!v1String || !v2String) |
1178 | return MixedTriState; |
1179 | return triState(WTF::equal(*v1String, *v2String)); |
1180 | } |
1181 | |
1182 | return triState(v1 == v2); |
1183 | } |
1184 | |
1185 | inline TriState JSValue::pureToBoolean() const |
1186 | { |
1187 | if (isInt32()) |
1188 | return asInt32() ? TrueTriState : FalseTriState; |
1189 | if (isDouble()) |
1190 | return isNotZeroAndOrdered(asDouble()) ? TrueTriState : FalseTriState; // false for NaN |
1191 | if (isCell()) |
1192 | return asCell()->pureToBoolean(); |
1193 | return isTrue() ? TrueTriState : FalseTriState; |
1194 | } |
1195 | |
1196 | ALWAYS_INLINE bool JSValue::requireObjectCoercible(JSGlobalObject* globalObject) const |
1197 | { |
1198 | VM& vm = getVM(globalObject); |
1199 | auto scope = DECLARE_THROW_SCOPE(vm); |
1200 | |
1201 | if (!isUndefinedOrNull()) |
1202 | return true; |
1203 | throwException(globalObject, scope, createNotAnObjectError(globalObject, *this)); |
1204 | return false; |
1205 | } |
1206 | |
1207 | ALWAYS_INLINE bool isThisValueAltered(const PutPropertySlot& slot, JSObject* baseObject) |
1208 | { |
1209 | JSValue thisValue = slot.thisValue(); |
1210 | if (LIKELY(thisValue == baseObject)) |
1211 | return false; |
1212 | |
1213 | if (!thisValue.isObject()) |
1214 | return true; |
1215 | JSObject* thisObject = asObject(thisValue); |
1216 | // Only PureForwardingProxyType can be seen as the same to the original target object. |
1217 | if (thisObject->type() == PureForwardingProxyType && jsCast<JSProxy*>(thisObject)->target() == baseObject) |
1218 | return false; |
1219 | return true; |
1220 | } |
1221 | |
1222 | // See section 7.2.9: https://tc39.github.io/ecma262/#sec-samevalue |
1223 | ALWAYS_INLINE bool sameValue(JSGlobalObject* globalObject, JSValue a, JSValue b) |
1224 | { |
1225 | if (!a.isNumber()) |
1226 | return JSValue::strictEqual(globalObject, a, b); |
1227 | if (!b.isNumber()) |
1228 | return false; |
1229 | double x = a.asNumber(); |
1230 | double y = b.asNumber(); |
1231 | bool xIsNaN = std::isnan(x); |
1232 | bool yIsNaN = std::isnan(y); |
1233 | if (xIsNaN || yIsNaN) |
1234 | return xIsNaN && yIsNaN; |
1235 | return bitwise_cast<uint64_t>(x) == bitwise_cast<uint64_t>(y); |
1236 | } |
1237 | |
1238 | } // namespace JSC |
1239 | |