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
44namespace JSC {
45
46ALWAYS_INLINE int32_t JSValue::toInt32(JSGlobalObject* globalObject) const
47{
48 if (isInt32())
49 return asInt32();
50 return JSC::toInt32(toNumber(globalObject));
51}
52
53inline uint32_t JSValue::toUInt32(JSGlobalObject* globalObject) const
54{
55 // See comment on JSC::toUInt32, in JSCJSValue.h.
56 return toInt32(globalObject);
57}
58
59inline 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
80inline bool JSValue::isUInt32() const
81{
82 return isInt32() && asInt32() >= 0;
83}
84
85inline uint32_t JSValue::asUInt32() const
86{
87 ASSERT(isUInt32());
88 return asInt32();
89}
90
91inline double JSValue::asNumber() const
92{
93 ASSERT(isNumber());
94 return isInt32() ? asInt32() : asDouble();
95}
96
97inline JSValue jsNaN()
98{
99 return JSValue(JSValue::EncodeAsDouble, PNaN);
100}
101
102inline JSValue::JSValue(char i)
103{
104 *this = JSValue(static_cast<int32_t>(i));
105}
106
107inline JSValue::JSValue(unsigned char i)
108{
109 *this = JSValue(static_cast<int32_t>(i));
110}
111
112inline JSValue::JSValue(short i)
113{
114 *this = JSValue(static_cast<int32_t>(i));
115}
116
117inline JSValue::JSValue(unsigned short i)
118{
119 *this = JSValue(static_cast<int32_t>(i));
120}
121
122inline 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
131inline 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
140inline 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
149inline 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
158inline 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
167inline 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
176inline EncodedJSValue JSValue::encode(JSValue value)
177{
178 return value.u.asInt64;
179}
180
181inline JSValue JSValue::decode(EncodedJSValue encodedJSValue)
182{
183 JSValue v;
184 v.u.asInt64 = encodedJSValue;
185 return v;
186}
187
188#if USE(JSVALUE32_64)
189inline JSValue::JSValue()
190{
191 u.asBits.tag = EmptyValueTag;
192 u.asBits.payload = 0;
193}
194
195inline JSValue::JSValue(JSNullTag)
196{
197 u.asBits.tag = NullTag;
198 u.asBits.payload = 0;
199}
200
201inline JSValue::JSValue(JSUndefinedTag)
202{
203 u.asBits.tag = UndefinedTag;
204 u.asBits.payload = 0;
205}
206
207inline JSValue::JSValue(JSTrueTag)
208{
209 u.asBits.tag = BooleanTag;
210 u.asBits.payload = 1;
211}
212
213inline JSValue::JSValue(JSFalseTag)
214{
215 u.asBits.tag = BooleanTag;
216 u.asBits.payload = 0;
217}
218
219inline JSValue::JSValue(HashTableDeletedValueTag)
220{
221 u.asBits.tag = DeletedValueTag;
222 u.asBits.payload = 0;
223}
224
225inline 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
234inline 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
243inline JSValue::operator bool() const
244{
245 ASSERT(tag() != DeletedValueTag);
246 return tag() != EmptyValueTag;
247}
248
249inline bool JSValue::operator==(const JSValue& other) const
250{
251 return u.asInt64 == other.u.asInt64;
252}
253
254inline bool JSValue::operator!=(const JSValue& other) const
255{
256 return u.asInt64 != other.u.asInt64;
257}
258
259inline bool JSValue::isEmpty() const
260{
261 return tag() == EmptyValueTag;
262}
263
264inline bool JSValue::isUndefined() const
265{
266 return tag() == UndefinedTag;
267}
268
269inline bool JSValue::isNull() const
270{
271 return tag() == NullTag;
272}
273
274inline bool JSValue::isUndefinedOrNull() const
275{
276 return isUndefined() || isNull();
277}
278
279inline bool JSValue::isCell() const
280{
281 return tag() == CellTag;
282}
283
284inline bool JSValue::isInt32() const
285{
286 return tag() == Int32Tag;
287}
288
289inline bool JSValue::isDouble() const
290{
291 return tag() < LowestTag;
292}
293
294inline bool JSValue::isTrue() const
295{
296 return tag() == BooleanTag && payload();
297}
298
299inline bool JSValue::isFalse() const
300{
301 return tag() == BooleanTag && !payload();
302}
303
304inline uint32_t JSValue::tag() const
305{
306 return u.asBits.tag;
307}
308
309inline int32_t JSValue::payload() const
310{
311 return u.asBits.payload;
312}
313
314inline int32_t JSValue::asInt32() const
315{
316 ASSERT(isInt32());
317 return u.asBits.payload;
318}
319
320inline double JSValue::asDouble() const
321{
322 ASSERT(isDouble());
323 return u.asDouble;
324}
325
326ALWAYS_INLINE JSCell* JSValue::asCell() const
327{
328 ASSERT(isCell());
329 return reinterpret_cast<JSCell*>(u.asBits.payload);
330}
331
332ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, double d)
333{
334 ASSERT(!isImpureNaN(d));
335 u.asDouble = d;
336}
337
338inline JSValue::JSValue(int i)
339{
340 u.asBits.tag = Int32Tag;
341 u.asBits.payload = i;
342}
343
344inline JSValue::JSValue(int32_t tag, int32_t payload)
345{
346 u.asBits.tag = tag;
347 u.asBits.payload = payload;
348}
349
350inline bool JSValue::isNumber() const
351{
352 return isInt32() || isDouble();
353}
354
355inline bool JSValue::isBoolean() const
356{
357 return tag() == BooleanTag;
358}
359
360inline 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.
369inline 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.
375inline JSValue::JSValue(HashTableDeletedValueTag)
376{
377 u.asInt64 = ValueDeleted;
378}
379
380inline JSValue::JSValue(JSCell* ptr)
381{
382 u.asInt64 = reinterpret_cast<uintptr_t>(ptr);
383}
384
385inline JSValue::JSValue(const JSCell* ptr)
386{
387 u.asInt64 = reinterpret_cast<uintptr_t>(const_cast<JSCell*>(ptr));
388}
389
390inline JSValue::operator bool() const
391{
392 return u.asInt64;
393}
394
395inline bool JSValue::operator==(const JSValue& other) const
396{
397 return u.asInt64 == other.u.asInt64;
398}
399
400inline bool JSValue::operator!=(const JSValue& other) const
401{
402 return u.asInt64 != other.u.asInt64;
403}
404
405inline bool JSValue::isEmpty() const
406{
407 return u.asInt64 == ValueEmpty;
408}
409
410inline bool JSValue::isUndefined() const
411{
412 return asValue() == JSValue(JSUndefined);
413}
414
415inline bool JSValue::isNull() const
416{
417 return asValue() == JSValue(JSNull);
418}
419
420inline bool JSValue::isTrue() const
421{
422 return asValue() == JSValue(JSTrue);
423}
424
425inline bool JSValue::isFalse() const
426{
427 return asValue() == JSValue(JSFalse);
428}
429
430inline bool JSValue::asBoolean() const
431{
432 ASSERT(isBoolean());
433 return asValue() == JSValue(JSTrue);
434}
435
436inline int32_t JSValue::asInt32() const
437{
438 ASSERT(isInt32());
439 return static_cast<int32_t>(u.asInt64);
440}
441
442inline bool JSValue::isDouble() const
443{
444 return isNumber() && !isInt32();
445}
446
447inline JSValue::JSValue(JSNullTag)
448{
449 u.asInt64 = ValueNull;
450}
451
452inline JSValue::JSValue(JSUndefinedTag)
453{
454 u.asInt64 = ValueUndefined;
455}
456
457inline JSValue::JSValue(JSTrueTag)
458{
459 u.asInt64 = ValueTrue;
460}
461
462inline JSValue::JSValue(JSFalseTag)
463{
464 u.asInt64 = ValueFalse;
465}
466
467inline 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
473inline bool JSValue::isBoolean() const
474{
475 return (u.asInt64 & ~1) == ValueFalse;
476}
477
478inline bool JSValue::isCell() const
479{
480 return !(u.asInt64 & NotCellMask);
481}
482
483inline bool JSValue::isInt32() const
484{
485 return (u.asInt64 & NumberTag) == NumberTag;
486}
487
488inline int64_t reinterpretDoubleToInt64(double value)
489{
490 return bitwise_cast<int64_t>(value);
491}
492inline double reinterpretInt64ToDouble(int64_t value)
493{
494 return bitwise_cast<double>(value);
495}
496
497ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, double d)
498{
499 ASSERT(!isImpureNaN(d));
500 u.asInt64 = reinterpretDoubleToInt64(d) + JSValue::DoubleEncodeOffset;
501}
502
503inline JSValue::JSValue(int i)
504{
505 u.asInt64 = JSValue::NumberTag | static_cast<uint32_t>(i);
506}
507
508inline double JSValue::asDouble() const
509{
510 ASSERT(isDouble());
511 return reinterpretInt64ToDouble(u.asInt64 - JSValue::DoubleEncodeOffset);
512}
513
514inline bool JSValue::isNumber() const
515{
516 return u.asInt64 & JSValue::NumberTag;
517}
518
519ALWAYS_INLINE JSCell* JSValue::asCell() const
520{
521 ASSERT(isCell());
522 return u.ptr;
523}
524
525#endif // USE(JSVALUE64)
526
527inline 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
552inline bool isInt52(double number)
553{
554 return tryConvertToInt52(number) != JSValue::notInt52;
555}
556
557inline bool JSValue::isAnyInt() const
558{
559 if (isInt32())
560 return true;
561 if (!isNumber())
562 return false;
563 return isInt52(asDouble());
564}
565
566inline int64_t JSValue::asAnyInt() const
567{
568 ASSERT(isAnyInt());
569 if (isInt32())
570 return asInt32();
571 return static_cast<int64_t>(asDouble());
572}
573
574inline 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
582inline int32_t JSValue::asInt32AsAnyInt() const
583{
584 ASSERT(isInt32AsAnyInt());
585 if (isInt32())
586 return asInt32();
587 return static_cast<int32_t>(asDouble());
588}
589
590inline 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
598inline uint32_t JSValue::asUInt32AsAnyInt() const
599{
600 ASSERT(isUInt32AsAnyInt());
601 if (isUInt32())
602 return asUInt32();
603 return static_cast<uint32_t>(asDouble());
604}
605
606inline bool JSValue::isString() const
607{
608 return isCell() && asCell()->isString();
609}
610
611inline bool JSValue::isBigInt() const
612{
613 return isCell() && asCell()->isBigInt();
614}
615
616inline bool JSValue::isSymbol() const
617{
618 return isCell() && asCell()->isSymbol();
619}
620
621inline bool JSValue::isPrimitive() const
622{
623 return !isCell() || asCell()->isString() || asCell()->isSymbol() || asCell()->isBigInt();
624}
625
626inline bool JSValue::isGetterSetter() const
627{
628 return isCell() && asCell()->isGetterSetter();
629}
630
631inline bool JSValue::isCustomGetterSetter() const
632{
633 return isCell() && asCell()->isCustomGetterSetter();
634}
635
636inline bool JSValue::isObject() const
637{
638 return isCell() && asCell()->isObject();
639}
640
641inline bool JSValue::getString(JSGlobalObject* globalObject, String& s) const
642{
643 return isCell() && asCell()->getString(globalObject, s);
644}
645
646inline String JSValue::getString(JSGlobalObject* globalObject) const
647{
648 return isCell() ? asCell()->getString(globalObject) : String();
649}
650
651template <typename Base> String HandleConverter<Base, Unknown>::getString(JSGlobalObject* globalObject) const
652{
653 return jsValue().getString(globalObject);
654}
655
656inline JSObject* JSValue::getObject() const
657{
658 return isCell() ? asCell()->getObject() : 0;
659}
660
661ALWAYS_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
676ALWAYS_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
694inline JSValue JSValue::toPrimitive(JSGlobalObject* globalObject, PreferredPrimitiveType preferredType) const
695{
696 return isCell() ? asCell()->toPrimitive(globalObject, preferredType) : asValue();
697}
698
699inline 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
723inline 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
753ALWAYS_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
762ALWAYS_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
782ALWAYS_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
802inline JSObject* JSValue::toObject(JSGlobalObject* globalObject) const
803{
804 return isCell() ? asCell()->toObject(globalObject) : toObjectSlowCase(globalObject);
805}
806
807inline bool JSValue::isFunction(VM& vm) const
808{
809 if (!isCell())
810 return false;
811 return asCell()->isFunction(vm);
812}
813
814inline 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
821inline bool JSValue::isConstructor(VM& vm) const
822{
823 if (!isCell())
824 return false;
825 return asCell()->isConstructor(vm);
826}
827
828inline 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
836inline bool JSValue::inherits(VM& vm, const ClassInfo* classInfo) const
837{
838 return isCell() && asCell()->inherits(vm, classInfo);
839}
840
841template<typename Target>
842inline bool JSValue::inherits(VM& vm) const
843{
844 return isCell() && asCell()->inherits<Target>(vm);
845}
846
847inline const ClassInfo* JSValue::classInfoOrNull(VM& vm) const
848{
849 return isCell() ? asCell()->classInfo(vm) : nullptr;
850}
851
852inline 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
857ALWAYS_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
863ALWAYS_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
873template<typename CallbackWhenNoException>
874ALWAYS_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
880template<typename CallbackWhenNoException>
881ALWAYS_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
889ALWAYS_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
912ALWAYS_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
928ALWAYS_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
934ALWAYS_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
961ALWAYS_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
968inline 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
976ALWAYS_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
983inline 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
991inline Structure* JSValue::structureOrNull() const
992{
993 if (isCell())
994 return asCell()->structure();
995 return nullptr;
996}
997
998inline JSValue JSValue::structureOrUndefined() const
999{
1000 if (isCell())
1001 return JSValue(asCell()->structure());
1002 return jsUndefined();
1003}
1004
1005// ECMA 11.9.3
1006inline 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
1014ALWAYS_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
1131ALWAYS_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
1142inline 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
1156inline int32_t JSValue::asInt32ForArithmetic() const
1157{
1158 if (isBoolean())
1159 return asBoolean();
1160 return asInt32();
1161}
1162
1163inline 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
1185inline 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
1196ALWAYS_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
1207ALWAYS_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
1223ALWAYS_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