|  | /* | 
|  | * Copyright (C) 2011-2021 Apple Inc. All rights reserved. | 
|  | * | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions | 
|  | * are met: | 
|  | * 1. Redistributions of source code must retain the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer. | 
|  | * 2. Redistributions in binary form must reproduce the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer in the | 
|  | *    documentation and/or other materials provided with the distribution. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | 
|  | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 
|  | * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR | 
|  | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | 
|  | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 
|  | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 
|  | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | 
|  | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
|  | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | */ | 
|  |  | 
|  | #pragma once | 
|  |  | 
|  | #include "CatchScope.h" | 
|  | #include "Error.h" | 
|  | #include "ExceptionHelpers.h" | 
|  | #include "Identifier.h" | 
|  | #include "InternalFunction.h" | 
|  | #include "JSBigInt.h" | 
|  | #include "JSCJSValue.h" | 
|  | #include "JSCellInlines.h" | 
|  | #include "JSFunction.h" | 
|  | #include "JSObject.h" | 
|  | #include "JSProxy.h" | 
|  | #include "JSStringInlines.h" | 
|  | #include "MathCommon.h" | 
|  | #include <wtf/text/StringImpl.h> | 
|  |  | 
|  | namespace JSC { | 
|  |  | 
|  | ALWAYS_INLINE int32_t JSValue::toInt32(JSGlobalObject* globalObject) const | 
|  | { | 
|  | if (isInt32()) | 
|  | return asInt32(); | 
|  |  | 
|  | double d = toNumber(globalObject); | 
|  | return JSC::toInt32(d); | 
|  | } | 
|  |  | 
|  | inline uint32_t JSValue::toUInt32(JSGlobalObject* globalObject) const | 
|  | { | 
|  | // The only difference between toInt32 and toUint32 is that toUint32 reinterprets resulted int32_t value as uint32_t. | 
|  | // https://tc39.es/ecma262/#sec-touint32 | 
|  | return toInt32(globalObject); | 
|  | } | 
|  |  | 
|  | inline uint32_t JSValue::toIndex(JSGlobalObject* globalObject, const char* errorName) const | 
|  | { | 
|  | VM& vm = getVM(globalObject); | 
|  | auto scope = DECLARE_THROW_SCOPE(vm); | 
|  |  | 
|  | double d = toNumber(globalObject); | 
|  | RETURN_IF_EXCEPTION(scope, 0); | 
|  | if (d <= -1) { | 
|  | throwException(globalObject, scope, createRangeError(globalObject, makeString(errorName, " cannot be negative"))); | 
|  | return 0; | 
|  | } | 
|  | if (d > std::numeric_limits<unsigned>::max()) { | 
|  | throwException(globalObject, scope, createRangeError(globalObject, makeString(errorName, " too large"))); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (isInt32()) | 
|  | return asInt32(); | 
|  | RELEASE_AND_RETURN(scope, JSC::toInt32(d)); | 
|  | } | 
|  |  | 
|  | // https://tc39.es/ecma262/#sec-tointegerorinfinity | 
|  | inline double JSValue::toIntegerOrInfinity(JSGlobalObject* globalObject) const | 
|  | { | 
|  | if (isInt32()) | 
|  | return asInt32(); | 
|  | double d = toNumber(globalObject); | 
|  | if (std::isnan(d) || !d) | 
|  | return 0.0; | 
|  | return trunc(d); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isUInt32() const | 
|  | { | 
|  | return isInt32() && asInt32() >= 0; | 
|  | } | 
|  |  | 
|  | inline uint32_t JSValue::asUInt32() const | 
|  | { | 
|  | ASSERT(isUInt32()); | 
|  | return asInt32(); | 
|  | } | 
|  |  | 
|  | inline double JSValue::asNumber() const | 
|  | { | 
|  | ASSERT(isNumber()); | 
|  | return isInt32() ? asInt32() : asDouble(); | 
|  | } | 
|  |  | 
|  | inline Optional<uint32_t> JSValue::tryGetAsUint32Index() | 
|  | { | 
|  | if (isUInt32()) { | 
|  | ASSERT(isIndex(asUInt32())); | 
|  | return asUInt32(); | 
|  | } | 
|  | if (isNumber()) { | 
|  | double number = asNumber(); | 
|  | uint32_t asUint = static_cast<uint32_t>(number); | 
|  | if (static_cast<double>(asUint) == number && isIndex(asUint)) | 
|  | return asUint; | 
|  | } | 
|  | return WTF::nullopt; | 
|  | } | 
|  |  | 
|  | inline Optional<int32_t> JSValue::tryGetAsInt32() | 
|  | { | 
|  | if (isInt32()) | 
|  | return asInt32(); | 
|  | if (isNumber()) { | 
|  | double number = asNumber(); | 
|  | int32_t asInt = static_cast<int32_t>(number); | 
|  | if (static_cast<double>(asInt) == number) | 
|  | return asInt; | 
|  | } | 
|  | return WTF::nullopt; | 
|  | } | 
|  |  | 
|  | inline JSValue jsNaN() | 
|  | { | 
|  | return JSValue(JSValue::EncodeAsDouble, PNaN); | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(char i) | 
|  | { | 
|  | *this = JSValue(static_cast<int32_t>(i)); | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(unsigned char i) | 
|  | { | 
|  | *this = JSValue(static_cast<int32_t>(i)); | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(short i) | 
|  | { | 
|  | *this = JSValue(static_cast<int32_t>(i)); | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(unsigned short i) | 
|  | { | 
|  | *this = JSValue(static_cast<int32_t>(i)); | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(unsigned i) | 
|  | { | 
|  | if (static_cast<int32_t>(i) < 0) { | 
|  | *this = JSValue(EncodeAsDouble, static_cast<double>(i)); | 
|  | return; | 
|  | } | 
|  | *this = JSValue(static_cast<int32_t>(i)); | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(long i) | 
|  | { | 
|  | if (static_cast<int32_t>(i) != i) { | 
|  | *this = JSValue(EncodeAsDouble, static_cast<double>(i)); | 
|  | return; | 
|  | } | 
|  | *this = JSValue(static_cast<int32_t>(i)); | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(unsigned long i) | 
|  | { | 
|  | if (static_cast<uint32_t>(i) != i) { | 
|  | *this = JSValue(EncodeAsDouble, static_cast<double>(i)); | 
|  | return; | 
|  | } | 
|  | *this = JSValue(static_cast<uint32_t>(i)); | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(long long i) | 
|  | { | 
|  | if (static_cast<int32_t>(i) != i) { | 
|  | *this = JSValue(EncodeAsDouble, static_cast<double>(i)); | 
|  | return; | 
|  | } | 
|  | *this = JSValue(static_cast<int32_t>(i)); | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(unsigned long long i) | 
|  | { | 
|  | if (static_cast<uint32_t>(i) != i) { | 
|  | *this = JSValue(EncodeAsDouble, static_cast<double>(i)); | 
|  | return; | 
|  | } | 
|  | *this = JSValue(static_cast<uint32_t>(i)); | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(double d) | 
|  | { | 
|  | if (canBeStrictInt32(d)) { | 
|  | *this = JSValue(static_cast<int32_t>(d)); | 
|  | return; | 
|  | } | 
|  | *this = JSValue(EncodeAsDouble, d); | 
|  | } | 
|  |  | 
|  | inline EncodedJSValue JSValue::encode(JSValue value) | 
|  | { | 
|  | return value.u.asInt64; | 
|  | } | 
|  |  | 
|  | inline JSValue JSValue::decode(EncodedJSValue encodedJSValue) | 
|  | { | 
|  | JSValue v; | 
|  | v.u.asInt64 = encodedJSValue; | 
|  | return v; | 
|  | } | 
|  |  | 
|  | #if USE(JSVALUE32_64) | 
|  | inline JSValue::JSValue() | 
|  | { | 
|  | u.asBits.tag = EmptyValueTag; | 
|  | u.asBits.payload = 0; | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(JSNullTag) | 
|  | { | 
|  | u.asBits.tag = NullTag; | 
|  | u.asBits.payload = 0; | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(JSUndefinedTag) | 
|  | { | 
|  | u.asBits.tag = UndefinedTag; | 
|  | u.asBits.payload = 0; | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(JSTrueTag) | 
|  | { | 
|  | u.asBits.tag = BooleanTag; | 
|  | u.asBits.payload = 1; | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(JSFalseTag) | 
|  | { | 
|  | u.asBits.tag = BooleanTag; | 
|  | u.asBits.payload = 0; | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(HashTableDeletedValueTag) | 
|  | { | 
|  | u.asBits.tag = DeletedValueTag; | 
|  | u.asBits.payload = 0; | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(JSCell* ptr) | 
|  | { | 
|  | if (ptr) | 
|  | u.asBits.tag = CellTag; | 
|  | else | 
|  | u.asBits.tag = EmptyValueTag; | 
|  | u.asBits.payload = reinterpret_cast<int32_t>(ptr); | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(const JSCell* ptr) | 
|  | { | 
|  | if (ptr) | 
|  | u.asBits.tag = CellTag; | 
|  | else | 
|  | u.asBits.tag = EmptyValueTag; | 
|  | u.asBits.payload = reinterpret_cast<int32_t>(const_cast<JSCell*>(ptr)); | 
|  | } | 
|  |  | 
|  | inline JSValue::operator bool() const | 
|  | { | 
|  | ASSERT(tag() != DeletedValueTag); | 
|  | return tag() != EmptyValueTag; | 
|  | } | 
|  |  | 
|  | inline bool JSValue::operator==(const JSValue& other) const | 
|  | { | 
|  | return u.asInt64 == other.u.asInt64; | 
|  | } | 
|  |  | 
|  | inline bool JSValue::operator!=(const JSValue& other) const | 
|  | { | 
|  | return u.asInt64 != other.u.asInt64; | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isEmpty() const | 
|  | { | 
|  | return tag() == EmptyValueTag; | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isUndefined() const | 
|  | { | 
|  | return tag() == UndefinedTag; | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isNull() const | 
|  | { | 
|  | return tag() == NullTag; | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isUndefinedOrNull() const | 
|  | { | 
|  | return isUndefined() || isNull(); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isCell() const | 
|  | { | 
|  | return tag() == CellTag; | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isInt32() const | 
|  | { | 
|  | return tag() == Int32Tag; | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isDouble() const | 
|  | { | 
|  | return tag() < LowestTag; | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isTrue() const | 
|  | { | 
|  | return tag() == BooleanTag && payload(); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isFalse() const | 
|  | { | 
|  | return tag() == BooleanTag && !payload(); | 
|  | } | 
|  |  | 
|  | inline uint32_t JSValue::tag() const | 
|  | { | 
|  | return u.asBits.tag; | 
|  | } | 
|  |  | 
|  | inline int32_t JSValue::payload() const | 
|  | { | 
|  | return u.asBits.payload; | 
|  | } | 
|  |  | 
|  | inline int32_t JSValue::asInt32() const | 
|  | { | 
|  | ASSERT(isInt32()); | 
|  | return u.asBits.payload; | 
|  | } | 
|  |  | 
|  | inline double JSValue::asDouble() const | 
|  | { | 
|  | ASSERT(isDouble()); | 
|  | return u.asDouble; | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE JSCell* JSValue::asCell() const | 
|  | { | 
|  | ASSERT(isCell()); | 
|  | return reinterpret_cast<JSCell*>(u.asBits.payload); | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE JSBigInt* JSValue::asHeapBigInt() const | 
|  | { | 
|  | ASSERT(isHeapBigInt()); | 
|  | return reinterpret_cast<JSBigInt*>(u.asBits.payload); | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, double d) | 
|  | { | 
|  | ASSERT(!isImpureNaN(d)); | 
|  | u.asDouble = d; | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(int i) | 
|  | { | 
|  | u.asBits.tag = Int32Tag; | 
|  | u.asBits.payload = i; | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(int32_t tag, int32_t payload) | 
|  | { | 
|  | u.asBits.tag = tag; | 
|  | u.asBits.payload = payload; | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isNumber() const | 
|  | { | 
|  | return isInt32() || isDouble(); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isBoolean() const | 
|  | { | 
|  | return tag() == BooleanTag; | 
|  | } | 
|  |  | 
|  | inline bool JSValue::asBoolean() const | 
|  | { | 
|  | ASSERT(isBoolean()); | 
|  | return payload(); | 
|  | } | 
|  |  | 
|  | #else // !USE(JSVALUE32_64) i.e. USE(JSVALUE64) | 
|  |  | 
|  | // 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. | 
|  | inline JSValue::JSValue() | 
|  | { | 
|  | u.asInt64 = ValueEmpty; | 
|  | } | 
|  |  | 
|  | // 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. | 
|  | inline JSValue::JSValue(HashTableDeletedValueTag) | 
|  | { | 
|  | u.asInt64 = ValueDeleted; | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(JSCell* ptr) | 
|  | { | 
|  | u.asInt64 = reinterpret_cast<uintptr_t>(ptr); | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(const JSCell* ptr) | 
|  | { | 
|  | u.asInt64 = reinterpret_cast<uintptr_t>(const_cast<JSCell*>(ptr)); | 
|  | } | 
|  |  | 
|  | inline JSValue::operator bool() const | 
|  | { | 
|  | return u.asInt64; | 
|  | } | 
|  |  | 
|  | inline bool JSValue::operator==(const JSValue& other) const | 
|  | { | 
|  | return u.asInt64 == other.u.asInt64; | 
|  | } | 
|  |  | 
|  | inline bool JSValue::operator!=(const JSValue& other) const | 
|  | { | 
|  | return u.asInt64 != other.u.asInt64; | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isEmpty() const | 
|  | { | 
|  | return u.asInt64 == ValueEmpty; | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isUndefined() const | 
|  | { | 
|  | return asValue() == JSValue(JSUndefined); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isNull() const | 
|  | { | 
|  | return asValue() == JSValue(JSNull); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isTrue() const | 
|  | { | 
|  | return asValue() == JSValue(JSTrue); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isFalse() const | 
|  | { | 
|  | return asValue() == JSValue(JSFalse); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::asBoolean() const | 
|  | { | 
|  | ASSERT(isBoolean()); | 
|  | return asValue() == JSValue(JSTrue); | 
|  | } | 
|  |  | 
|  | inline int32_t JSValue::asInt32() const | 
|  | { | 
|  | ASSERT(isInt32()); | 
|  | return static_cast<int32_t>(u.asInt64); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isDouble() const | 
|  | { | 
|  | return isNumber() && !isInt32(); | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(JSNullTag) | 
|  | { | 
|  | u.asInt64 = ValueNull; | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(JSUndefinedTag) | 
|  | { | 
|  | u.asInt64 = ValueUndefined; | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(JSTrueTag) | 
|  | { | 
|  | u.asInt64 = ValueTrue; | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(JSFalseTag) | 
|  | { | 
|  | u.asInt64 = ValueFalse; | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isUndefinedOrNull() const | 
|  | { | 
|  | // Undefined and null share the same value, bar the 'undefined' bit in the extended tag. | 
|  | return (u.asInt64 & ~UndefinedTag) == ValueNull; | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isBoolean() const | 
|  | { | 
|  | return (u.asInt64 & ~1) == ValueFalse; | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isCell() const | 
|  | { | 
|  | return !(u.asInt64 & NotCellMask); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isInt32() const | 
|  | { | 
|  | return (u.asInt64 & NumberTag) == NumberTag; | 
|  | } | 
|  |  | 
|  | inline int64_t reinterpretDoubleToInt64(double value) | 
|  | { | 
|  | return bitwise_cast<int64_t>(value); | 
|  | } | 
|  | inline double reinterpretInt64ToDouble(int64_t value) | 
|  | { | 
|  | return bitwise_cast<double>(value); | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, double d) | 
|  | { | 
|  | ASSERT(!isImpureNaN(d)); | 
|  | u.asInt64 = reinterpretDoubleToInt64(d) + JSValue::DoubleEncodeOffset; | 
|  | } | 
|  |  | 
|  | inline JSValue::JSValue(int i) | 
|  | { | 
|  | u.asInt64 = JSValue::NumberTag | static_cast<uint32_t>(i); | 
|  | } | 
|  |  | 
|  | inline double JSValue::asDouble() const | 
|  | { | 
|  | ASSERT(isDouble()); | 
|  | return reinterpretInt64ToDouble(u.asInt64 - JSValue::DoubleEncodeOffset); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isNumber() const | 
|  | { | 
|  | return u.asInt64 & JSValue::NumberTag; | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE JSCell* JSValue::asCell() const | 
|  | { | 
|  | ASSERT(isCell()); | 
|  | return u.ptr; | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE JSBigInt* JSValue::asHeapBigInt() const | 
|  | { | 
|  | ASSERT(isHeapBigInt()); | 
|  | return static_cast<JSBigInt*>(u.ptr); | 
|  | } | 
|  |  | 
|  | #endif // USE(JSVALUE64) | 
|  |  | 
|  | #if USE(BIGINT32) | 
|  | inline JSValue::JSValue(EncodeAsBigInt32Tag, int32_t value) | 
|  | { | 
|  | uint64_t shiftedValue = static_cast<uint64_t>(static_cast<uint32_t>(value)) << 16; | 
|  | ASSERT(!(shiftedValue & NumberTag)); | 
|  | u.asInt64 = shiftedValue | BigInt32Tag; | 
|  | } | 
|  | #endif // USE(BIGINT32) | 
|  |  | 
|  | inline int64_t tryConvertToInt52(double number) | 
|  | { | 
|  | if (number != number) | 
|  | return JSValue::notInt52; | 
|  | #if OS(WINDOWS) && CPU(X86) | 
|  | // The VS Compiler for 32-bit builds generates a floating point error when attempting to cast | 
|  | // from an infinity to a 64-bit integer. We leave this routine with the floating point error | 
|  | // left in a register, causing undefined behavior in later floating point operations. | 
|  | // | 
|  | // To avoid this issue, we check for infinity here, and return false in that case. | 
|  | if (std::isinf(number)) | 
|  | return JSValue::notInt52; | 
|  | #endif | 
|  | int64_t asInt64 = static_cast<int64_t>(number); | 
|  | if (asInt64 != number) | 
|  | return JSValue::notInt52; | 
|  | if (!asInt64 && std::signbit(number)) | 
|  | return JSValue::notInt52; | 
|  | if (asInt64 >= (static_cast<int64_t>(1) << (JSValue::numberOfInt52Bits - 1))) | 
|  | return JSValue::notInt52; | 
|  | if (asInt64 < -(static_cast<int64_t>(1) << (JSValue::numberOfInt52Bits - 1))) | 
|  | return JSValue::notInt52; | 
|  | return asInt64; | 
|  | } | 
|  |  | 
|  | inline bool isInt52(double number) | 
|  | { | 
|  | return tryConvertToInt52(number) != JSValue::notInt52; | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isAnyInt() const | 
|  | { | 
|  | if (isInt32()) | 
|  | return true; | 
|  | if (!isNumber()) | 
|  | return false; | 
|  | return isInt52(asDouble()); | 
|  | } | 
|  |  | 
|  | inline int64_t JSValue::asAnyInt() const | 
|  | { | 
|  | ASSERT(isAnyInt()); | 
|  | if (isInt32()) | 
|  | return asInt32(); | 
|  | return static_cast<int64_t>(asDouble()); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isInt32AsAnyInt() const | 
|  | { | 
|  | if (!isAnyInt()) | 
|  | return false; | 
|  | int64_t value = asAnyInt(); | 
|  | return value >= INT32_MIN && value <= INT32_MAX; | 
|  | } | 
|  |  | 
|  | inline int32_t JSValue::asInt32AsAnyInt() const | 
|  | { | 
|  | ASSERT(isInt32AsAnyInt()); | 
|  | if (isInt32()) | 
|  | return asInt32(); | 
|  | return static_cast<int32_t>(asDouble()); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isUInt32AsAnyInt() const | 
|  | { | 
|  | if (!isAnyInt()) | 
|  | return false; | 
|  | int64_t value = asAnyInt(); | 
|  | return value >= 0 && value <= UINT32_MAX; | 
|  | } | 
|  |  | 
|  | inline uint32_t JSValue::asUInt32AsAnyInt() const | 
|  | { | 
|  | ASSERT(isUInt32AsAnyInt()); | 
|  | if (isUInt32()) | 
|  | return asUInt32(); | 
|  | return static_cast<uint32_t>(asDouble()); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isString() const | 
|  | { | 
|  | return isCell() && asCell()->isString(); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isBigInt() const | 
|  | { | 
|  | return isBigInt32() || isHeapBigInt(); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isHeapBigInt() const | 
|  | { | 
|  | return isCell() && asCell()->isHeapBigInt(); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isBigInt32() const | 
|  | { | 
|  | #if USE(BIGINT32) | 
|  | return (u.asInt64 & BigInt32Mask) == BigInt32Tag; | 
|  | #else | 
|  | return false; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | #if USE(BIGINT32) | 
|  | inline int32_t JSValue::bigInt32AsInt32() const | 
|  | { | 
|  | ASSERT(isBigInt32()); | 
|  | return static_cast<int32_t>(u.asInt64 >> 16); | 
|  | } | 
|  | #endif // USE(BIGINT32) | 
|  |  | 
|  | inline bool JSValue::isSymbol() const | 
|  | { | 
|  | return isCell() && asCell()->isSymbol(); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isPrimitive() const | 
|  | { | 
|  | return !isCell() || asCell()->isString() || asCell()->isSymbol() || asCell()->isHeapBigInt(); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isGetterSetter() const | 
|  | { | 
|  | return isCell() && asCell()->isGetterSetter(); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isCustomGetterSetter() const | 
|  | { | 
|  | return isCell() && asCell()->isCustomGetterSetter(); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isObject() const | 
|  | { | 
|  | return isCell() && asCell()->isObject(); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::getString(JSGlobalObject* globalObject, String& s) const | 
|  | { | 
|  | return isCell() && asCell()->getString(globalObject, s); | 
|  | } | 
|  |  | 
|  | inline String JSValue::getString(JSGlobalObject* globalObject) const | 
|  | { | 
|  | return isCell() ? asCell()->getString(globalObject) : String(); | 
|  | } | 
|  |  | 
|  | template <typename Base> String HandleConverter<Base, Unknown>::getString(JSGlobalObject* globalObject) const | 
|  | { | 
|  | return jsValue().getString(globalObject); | 
|  | } | 
|  |  | 
|  | inline JSObject* JSValue::getObject() const | 
|  | { | 
|  | return isCell() ? asCell()->getObject() : nullptr; | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE bool JSValue::getUInt32(uint32_t& v) const | 
|  | { | 
|  | if (isInt32()) { | 
|  | int32_t i = asInt32(); | 
|  | v = static_cast<uint32_t>(i); | 
|  | return i >= 0; | 
|  | } | 
|  | if (isDouble()) { | 
|  | double d = asDouble(); | 
|  | v = static_cast<uint32_t>(d); | 
|  | return v == d; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE Identifier JSValue::toPropertyKey(JSGlobalObject* globalObject) const | 
|  | { | 
|  | VM& vm = getVM(globalObject); | 
|  | auto scope = DECLARE_THROW_SCOPE(vm); | 
|  |  | 
|  | if (isString()) | 
|  | RELEASE_AND_RETURN(scope, asString(*this)->toIdentifier(globalObject)); | 
|  |  | 
|  | JSValue primitive = toPrimitive(globalObject, PreferString); | 
|  | RETURN_IF_EXCEPTION(scope, vm.propertyNames->emptyIdentifier); | 
|  | if (primitive.isSymbol()) | 
|  | RELEASE_AND_RETURN(scope, Identifier::fromUid(asSymbol(primitive)->privateName())); | 
|  |  | 
|  | auto string = primitive.toString(globalObject); | 
|  | RETURN_IF_EXCEPTION(scope, { }); | 
|  | RELEASE_AND_RETURN(scope, string->toIdentifier(globalObject)); | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE JSValue JSValue::toPropertyKeyValue(JSGlobalObject* globalObject) const | 
|  | { | 
|  | VM& vm = getVM(globalObject); | 
|  | auto scope = DECLARE_THROW_SCOPE(vm); | 
|  |  | 
|  | if (isString() || isSymbol()) | 
|  | return *this; | 
|  |  | 
|  | JSValue primitive = toPrimitive(globalObject, PreferString); | 
|  | RETURN_IF_EXCEPTION(scope, JSValue()); | 
|  | if (primitive.isSymbol()) | 
|  | return primitive; | 
|  |  | 
|  | RELEASE_AND_RETURN(scope, primitive.toString(globalObject)); | 
|  | } | 
|  |  | 
|  | inline JSValue JSValue::toPrimitive(JSGlobalObject* globalObject, PreferredPrimitiveType preferredType) const | 
|  | { | 
|  | return isCell() ? asCell()->toPrimitive(globalObject, preferredType) : asValue(); | 
|  | } | 
|  |  | 
|  | inline PreferredPrimitiveType toPreferredPrimitiveType(JSGlobalObject* globalObject, JSValue value) | 
|  | { | 
|  | VM& vm = getVM(globalObject); | 
|  | auto scope = DECLARE_THROW_SCOPE(vm); | 
|  |  | 
|  | if (!value.isString()) { | 
|  | throwTypeError(globalObject, scope, "Primitive hint is not a string."_s); | 
|  | return NoPreference; | 
|  | } | 
|  |  | 
|  | StringImpl* hintString = asString(value)->value(globalObject).impl(); | 
|  | RETURN_IF_EXCEPTION(scope, NoPreference); | 
|  |  | 
|  | if (WTF::equal(hintString, "default")) | 
|  | return NoPreference; | 
|  | if (WTF::equal(hintString, "number")) | 
|  | return PreferNumber; | 
|  | if (WTF::equal(hintString, "string")) | 
|  | return PreferString; | 
|  |  | 
|  | throwTypeError(globalObject, scope, "Expected primitive hint to match one of 'default', 'number', 'string'."_s); | 
|  | return NoPreference; | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE double JSValue::toNumber(JSGlobalObject* globalObject) const | 
|  | { | 
|  | if (isInt32()) | 
|  | return asInt32(); | 
|  | if (isDouble()) | 
|  | return asDouble(); | 
|  | return toNumberSlowCase(globalObject); | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE JSValue JSValue::toNumeric(JSGlobalObject* globalObject) const | 
|  | { | 
|  | VM& vm = globalObject->vm(); | 
|  | auto scope = DECLARE_THROW_SCOPE(vm); | 
|  |  | 
|  | if (isInt32() || isDouble() || isBigInt()) | 
|  | return *this; | 
|  |  | 
|  | JSValue primValue = this->toPrimitive(globalObject, PreferNumber); | 
|  | RETURN_IF_EXCEPTION(scope, { }); | 
|  |  | 
|  | if (primValue.isDouble() || primValue.isBigInt()) | 
|  | return primValue; | 
|  |  | 
|  | double value = primValue.toNumber(globalObject); | 
|  | RETURN_IF_EXCEPTION(scope, { }); | 
|  |  | 
|  | return jsNumber(value); | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE Optional<uint32_t> JSValue::toUInt32AfterToNumeric(JSGlobalObject* globalObject) const | 
|  | { | 
|  | VM& vm = getVM(globalObject); | 
|  | auto scope = DECLARE_THROW_SCOPE(vm); | 
|  | JSValue result = toBigIntOrInt32(globalObject); | 
|  | RETURN_IF_EXCEPTION(scope, { }); | 
|  | if (LIKELY(result.isInt32())) | 
|  | return static_cast<uint32_t>(result.asInt32()); | 
|  | return WTF::nullopt; | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE JSValue JSValue::toBigIntOrInt32(JSGlobalObject* globalObject) const | 
|  | { | 
|  | VM& vm = getVM(globalObject); | 
|  | auto scope = DECLARE_THROW_SCOPE(vm); | 
|  |  | 
|  | if (isInt32() || isBigInt()) | 
|  | return *this; | 
|  | if (isDouble() && canBeInt32(asDouble())) | 
|  | return jsNumber(static_cast<int32_t>(asDouble())); | 
|  |  | 
|  | JSValue primValue = this->toPrimitive(globalObject, PreferNumber); | 
|  | RETURN_IF_EXCEPTION(scope, { }); | 
|  |  | 
|  | if (primValue.isInt32() || primValue.isBigInt()) | 
|  | return primValue; | 
|  |  | 
|  | int32_t value = primValue.toInt32(globalObject); | 
|  | RETURN_IF_EXCEPTION(scope, { }); | 
|  | return jsNumber(value); | 
|  | } | 
|  |  | 
|  | inline JSObject* JSValue::toObject(JSGlobalObject* globalObject) const | 
|  | { | 
|  | return isCell() ? asCell()->toObject(globalObject) : toObjectSlowCase(globalObject); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isCallable(VM& vm) const | 
|  | { | 
|  | return isCell() && asCell()->isCallable(vm); | 
|  | } | 
|  |  | 
|  | template<Concurrency concurrency> | 
|  | inline TriState JSValue::isCallableWithConcurrency(VM& vm) const | 
|  | { | 
|  | if (!isCell()) | 
|  | return TriState::False; | 
|  | return asCell()->isCallableWithConcurrency<concurrency>(vm); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::isConstructor(VM& vm) const | 
|  | { | 
|  | return isCell() && asCell()->isConstructor(vm); | 
|  | } | 
|  |  | 
|  | template<Concurrency concurrency> | 
|  | inline TriState JSValue::isConstructorWithConcurrency(VM& vm) const | 
|  | { | 
|  | if (!isCell()) | 
|  | return TriState::False; | 
|  | return asCell()->isConstructorWithConcurrency<concurrency>(vm); | 
|  | } | 
|  |  | 
|  | // this method is here to be after the inline declaration of JSCell::inherits | 
|  | inline bool JSValue::inherits(VM& vm, const ClassInfo* classInfo) const | 
|  | { | 
|  | return isCell() && asCell()->inherits(vm, classInfo); | 
|  | } | 
|  |  | 
|  | template<typename Target> | 
|  | inline bool JSValue::inherits(VM& vm) const | 
|  | { | 
|  | return isCell() && asCell()->inherits<Target>(vm); | 
|  | } | 
|  |  | 
|  | inline const ClassInfo* JSValue::classInfoOrNull(VM& vm) const | 
|  | { | 
|  | return isCell() ? asCell()->classInfo(vm) : nullptr; | 
|  | } | 
|  |  | 
|  | inline JSValue JSValue::toThis(JSGlobalObject* globalObject, ECMAMode ecmaMode) const | 
|  | { | 
|  | return isCell() ? asCell()->methodTable(getVM(globalObject))->toThis(asCell(), globalObject, ecmaMode) : toThisSlowCase(globalObject, ecmaMode); | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE JSValue JSValue::get(JSGlobalObject* globalObject, PropertyName propertyName) const | 
|  | { | 
|  | PropertySlot slot(asValue(), PropertySlot::InternalMethodType::Get); | 
|  | return get(globalObject, propertyName, slot); | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE JSValue JSValue::get(JSGlobalObject* globalObject, PropertyName propertyName, PropertySlot& slot) const | 
|  | { | 
|  | auto scope = DECLARE_THROW_SCOPE(getVM(globalObject)); | 
|  | bool hasSlot = getPropertySlot(globalObject, propertyName, slot); | 
|  | EXCEPTION_ASSERT(!scope.exception() || !hasSlot); | 
|  | if (!hasSlot) | 
|  | return jsUndefined(); | 
|  | RELEASE_AND_RETURN(scope, slot.getValue(globalObject, propertyName)); | 
|  | } | 
|  |  | 
|  | template<typename CallbackWhenNoException> | 
|  | ALWAYS_INLINE typename std::result_of<CallbackWhenNoException(bool, PropertySlot&)>::type JSValue::getPropertySlot(JSGlobalObject* globalObject, PropertyName propertyName, CallbackWhenNoException callback) const | 
|  | { | 
|  | PropertySlot slot(asValue(), PropertySlot::InternalMethodType::Get); | 
|  | return getPropertySlot(globalObject, propertyName, slot, callback); | 
|  | } | 
|  |  | 
|  | template<typename CallbackWhenNoException> | 
|  | ALWAYS_INLINE typename std::result_of<CallbackWhenNoException(bool, PropertySlot&)>::type JSValue::getPropertySlot(JSGlobalObject* globalObject, PropertyName propertyName, PropertySlot& slot, CallbackWhenNoException callback) const | 
|  | { | 
|  | auto scope = DECLARE_THROW_SCOPE(getVM(globalObject)); | 
|  | bool found = getPropertySlot(globalObject, propertyName, slot); | 
|  | RETURN_IF_EXCEPTION(scope, { }); | 
|  | RELEASE_AND_RETURN(scope, callback(found, slot)); | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE bool JSValue::getPropertySlot(JSGlobalObject* globalObject, PropertyName propertyName, PropertySlot& slot) const | 
|  | { | 
|  | auto scope = DECLARE_THROW_SCOPE(getVM(globalObject)); | 
|  | // If this is a primitive, we'll need to synthesize the prototype - | 
|  | // and if it's a string there are special properties to check first. | 
|  | JSObject* object; | 
|  | if (UNLIKELY(!isObject())) { | 
|  | if (isString()) { | 
|  | bool hasProperty = asString(*this)->getStringPropertySlot(globalObject, propertyName, slot); | 
|  | RETURN_IF_EXCEPTION(scope, false); | 
|  | if (hasProperty) | 
|  | return true; | 
|  | } | 
|  | object = synthesizePrototype(globalObject); | 
|  | EXCEPTION_ASSERT(!!scope.exception() == !object); | 
|  | if (UNLIKELY(!object)) | 
|  | return false; | 
|  | } else | 
|  | object = asObject(asCell()); | 
|  |  | 
|  | RELEASE_AND_RETURN(scope, object->getPropertySlot(globalObject, propertyName, slot)); | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE bool JSValue::getOwnPropertySlot(JSGlobalObject* globalObject, PropertyName propertyName, PropertySlot& slot) const | 
|  | { | 
|  | // If this is a primitive, we'll need to synthesize the prototype - | 
|  | // and if it's a string there are special properties to check first. | 
|  | auto scope = DECLARE_THROW_SCOPE(getVM(globalObject)); | 
|  | if (UNLIKELY(!isObject())) { | 
|  | if (isString()) | 
|  | RELEASE_AND_RETURN(scope, asString(*this)->getStringPropertySlot(globalObject, propertyName, slot)); | 
|  |  | 
|  | if (isUndefinedOrNull()) | 
|  | throwException(globalObject, scope, createNotAnObjectError(globalObject, *this)); | 
|  | return false; | 
|  | } | 
|  | RELEASE_AND_RETURN(scope, asObject(asCell())->getOwnPropertySlotInline(globalObject, propertyName, slot)); | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE JSValue JSValue::get(JSGlobalObject* globalObject, unsigned propertyName) const | 
|  | { | 
|  | PropertySlot slot(asValue(), PropertySlot::InternalMethodType::Get); | 
|  | return get(globalObject, propertyName, slot); | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE JSValue JSValue::get(JSGlobalObject* globalObject, unsigned propertyName, PropertySlot& slot) const | 
|  | { | 
|  | auto scope = DECLARE_THROW_SCOPE(getVM(globalObject)); | 
|  | // If this is a primitive, we'll need to synthesize the prototype - | 
|  | // and if it's a string there are special properties to check first. | 
|  | JSObject* object; | 
|  | if (UNLIKELY(!isObject())) { | 
|  | if (isString()) { | 
|  | bool hasProperty = asString(*this)->getStringPropertySlot(globalObject, propertyName, slot); | 
|  | RETURN_IF_EXCEPTION(scope, { }); | 
|  | if (hasProperty) | 
|  | RELEASE_AND_RETURN(scope, slot.getValue(globalObject, propertyName)); | 
|  | } | 
|  | object = synthesizePrototype(globalObject); | 
|  | EXCEPTION_ASSERT(!!scope.exception() == !object); | 
|  | if (UNLIKELY(!object)) | 
|  | return JSValue(); | 
|  | } else | 
|  | object = asObject(asCell()); | 
|  |  | 
|  | bool hasSlot = object->getPropertySlot(globalObject, propertyName, slot); | 
|  | EXCEPTION_ASSERT(!scope.exception() || !hasSlot); | 
|  | if (!hasSlot) | 
|  | return jsUndefined(); | 
|  | RELEASE_AND_RETURN(scope, slot.getValue(globalObject, propertyName)); | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE JSValue JSValue::get(JSGlobalObject* globalObject, uint64_t propertyName) const | 
|  | { | 
|  | if (LIKELY(propertyName <= std::numeric_limits<unsigned>::max())) | 
|  | return get(globalObject, static_cast<unsigned>(propertyName)); | 
|  | return get(globalObject, Identifier::from(getVM(globalObject), static_cast<double>(propertyName))); | 
|  | } | 
|  |  | 
|  | template<typename T, typename PropertyNameType> | 
|  | ALWAYS_INLINE T JSValue::getAs(JSGlobalObject* globalObject, PropertyNameType propertyName) const | 
|  | { | 
|  | JSValue value = get(globalObject, propertyName); | 
|  | #if ASSERT_ENABLED || ENABLE(SECURITY_ASSERTIONS) | 
|  | VM& vm = getVM(globalObject); | 
|  | if (vm.exceptionForInspection()) | 
|  | return nullptr; | 
|  | #endif | 
|  | return jsCast<T>(value); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::put(JSGlobalObject* globalObject, PropertyName propertyName, JSValue value, PutPropertySlot& slot) | 
|  | { | 
|  | if (UNLIKELY(!isCell())) | 
|  | return putToPrimitive(globalObject, propertyName, value, slot); | 
|  |  | 
|  | return asCell()->methodTable(getVM(globalObject))->put(asCell(), globalObject, propertyName, value, slot); | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE bool JSValue::putInline(JSGlobalObject* globalObject, PropertyName propertyName, JSValue value, PutPropertySlot& slot) | 
|  | { | 
|  | if (UNLIKELY(!isCell())) | 
|  | return putToPrimitive(globalObject, propertyName, value, slot); | 
|  | return asCell()->putInline(globalObject, propertyName, value, slot); | 
|  | } | 
|  |  | 
|  | inline bool JSValue::putByIndex(JSGlobalObject* globalObject, unsigned propertyName, JSValue value, bool shouldThrow) | 
|  | { | 
|  | if (UNLIKELY(!isCell())) | 
|  | return putToPrimitiveByIndex(globalObject, propertyName, value, shouldThrow); | 
|  |  | 
|  | return asCell()->methodTable(getVM(globalObject))->putByIndex(asCell(), globalObject, propertyName, value, shouldThrow); | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE JSValue JSValue::getPrototype(JSGlobalObject* globalObject) const | 
|  | { | 
|  | VM& vm = getVM(globalObject); | 
|  | if (isObject()) | 
|  | return asObject(asCell())->getPrototype(vm, globalObject); | 
|  | return synthesizePrototype(globalObject); | 
|  | } | 
|  |  | 
|  | inline Structure* JSValue::structureOrNull(VM& vm) const | 
|  | { | 
|  | if (isCell()) | 
|  | return asCell()->structure(vm); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | // ECMA 11.9.3 | 
|  | inline bool JSValue::equal(JSGlobalObject* globalObject, JSValue v1, JSValue v2) | 
|  | { | 
|  | if (v1.isInt32() && v2.isInt32()) | 
|  | return v1 == v2; | 
|  |  | 
|  | return equalSlowCase(globalObject, v1, v2); | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE bool JSValue::equalSlowCaseInline(JSGlobalObject* globalObject, JSValue v1, JSValue v2) | 
|  | { | 
|  | VM& vm = getVM(globalObject); | 
|  | auto scope = DECLARE_THROW_SCOPE(vm); | 
|  | do { | 
|  | if (v1.isNumber()) { | 
|  | if (v2.isNumber()) | 
|  | return v1.asNumber() == v2.asNumber(); | 
|  | // Guaranteeing that if we have a number it is v2 makes some of the cases below simpler. | 
|  | std::swap(v1, v2); | 
|  | } | 
|  |  | 
|  | // This deals with Booleans, BigInt32, Objects, and is a shortcut for a few more types. | 
|  | // It has to come here and not before, because it is NOT true that NaN == NaN | 
|  | if (v1 == v2) | 
|  | return true; | 
|  |  | 
|  | if (v1.isUndefinedOrNull()) { | 
|  | if (v2.isUndefinedOrNull()) | 
|  | return true; | 
|  | if (!v2.isCell()) | 
|  | return false; | 
|  | return v2.asCell()->structure(vm)->masqueradesAsUndefined(globalObject); | 
|  | } | 
|  |  | 
|  | if (v2.isUndefinedOrNull()) { | 
|  | if (!v1.isCell()) | 
|  | return false; | 
|  | return v1.asCell()->structure(vm)->masqueradesAsUndefined(globalObject); | 
|  | } | 
|  |  | 
|  | if (v1.isObject()) { | 
|  | if (v2.isObject()) | 
|  | return false; // v1 == v2 is already dealt with previously | 
|  | JSValue p1 = v1.toPrimitive(globalObject); | 
|  | RETURN_IF_EXCEPTION(scope, false); | 
|  | v1 = p1; | 
|  | if (v1.isInt32() && v2.isInt32()) | 
|  | return v1 == v2; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (v2.isObject()) { | 
|  | JSValue p2 = v2.toPrimitive(globalObject); | 
|  | RETURN_IF_EXCEPTION(scope, false); | 
|  | v2 = p2; | 
|  | if (v1.isInt32() && v2.isInt32()) | 
|  | return v1 == v2; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | bool sym1 = v1.isSymbol(); | 
|  | bool sym2 = v2.isSymbol(); | 
|  | if (sym1 || sym2) { | 
|  | if (sym1 && sym2) | 
|  | return false; // v1 == v2 is already dealt with previously | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool s1 = v1.isString(); | 
|  | bool s2 = v2.isString(); | 
|  | if (s1) { | 
|  | if (s2) | 
|  | RELEASE_AND_RETURN(scope, asString(v1)->equal(globalObject, asString(v2))); | 
|  | std::swap(v1, v2); | 
|  | // We are guaranteed to enter the next case, so losing the invariant of only v2 being a number is fine | 
|  | } | 
|  | if (s1 || s2) { | 
|  | // We are guaranteed that the string is v2 (thanks to the swap above) | 
|  | if (v1.isBigInt()) { | 
|  | String v2String = asString(v2)->value(globalObject); | 
|  | RETURN_IF_EXCEPTION(scope, false); | 
|  | v2 = JSBigInt::stringToBigInt(globalObject, v2String); | 
|  | RETURN_IF_EXCEPTION(scope, false); | 
|  | if (!v2) | 
|  | return false; | 
|  | if (v1 == v2) | 
|  | return true; // For BigInt32 | 
|  | // We fallthrough to the generic code for comparing BigInts (which is only missing the BigInt32/BigInt32 case, hence the check above) | 
|  | } else { | 
|  | ASSERT(v1.isNumber() || v1.isBoolean()); | 
|  | double d1 = v1.toNumber(globalObject); | 
|  | RETURN_IF_EXCEPTION(scope, false); | 
|  | double d2 = v2.toNumber(globalObject); | 
|  | RETURN_IF_EXCEPTION(scope, false); | 
|  | return d1 == d2; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (v1.isBoolean()) { | 
|  | if (v2.isNumber()) | 
|  | return static_cast<double>(v1.asBoolean()) == v2.asNumber(); | 
|  | v1 = JSValue(v1.toNumber(globalObject)); | 
|  | RETURN_IF_EXCEPTION(scope, false); | 
|  | // We fallthrough to the BigInt/Number comparison below | 
|  | // We just need one more swap to repair the rule that only v2 is allowed to be a number in these comparisons | 
|  | std::swap(v1, v2); | 
|  | } else if (v2.isBoolean()) { | 
|  | v2 = JSValue(v2.toNumber(globalObject)); | 
|  | RETURN_IF_EXCEPTION(scope, false); | 
|  | // We fallthrough to the BigInt/Number comparison below | 
|  | } | 
|  |  | 
|  | #if USE(BIGINT32) | 
|  | if (v1.isBigInt32()) { | 
|  | if (v2.isInt32()) | 
|  | return v1.bigInt32AsInt32() == v2.asInt32(); | 
|  | if (v2.isDouble()) | 
|  | return static_cast<double>(v1.bigInt32AsInt32()) == v2.asDouble(); | 
|  | std::swap(v1, v2); | 
|  | } | 
|  | #endif // USE(BIGINT32) | 
|  |  | 
|  | if (v1.isHeapBigInt()) { | 
|  | if (v2.isHeapBigInt()) | 
|  | return JSBigInt::equals(v1.asHeapBigInt(), v2.asHeapBigInt()); | 
|  | #if USE(BIGINT32) | 
|  | if (v2.isBigInt32()) | 
|  | return v1.asHeapBigInt()->equalsToInt32(v2.bigInt32AsInt32()); | 
|  | #endif // USE(BIGINT32) | 
|  | if (v2.isNumber()) | 
|  | return v1.asHeapBigInt()->equalsToNumber(v2); | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } while (true); | 
|  | } | 
|  |  | 
|  | // ECMA 11.9.3 | 
|  | ALWAYS_INLINE bool JSValue::strictEqualForCells(JSGlobalObject* globalObject, JSCell* v1, JSCell* v2) | 
|  | { | 
|  | if (v1->isString() && v2->isString()) | 
|  | return asString(v1)->equal(globalObject, asString(v2)); | 
|  | if (v1->isHeapBigInt() && v2->isHeapBigInt()) | 
|  | return JSBigInt::equals(static_cast<JSBigInt*>(v1), static_cast<JSBigInt*>(v2)); | 
|  | return v1 == v2; | 
|  | } | 
|  |  | 
|  | inline bool JSValue::strictEqual(JSGlobalObject* globalObject, JSValue v1, JSValue v2) | 
|  | { | 
|  | if (v1.isInt32() && v2.isInt32()) | 
|  | return v1 == v2; | 
|  |  | 
|  | if (v1.isNumber() && v2.isNumber()) | 
|  | return v1.asNumber() == v2.asNumber(); | 
|  |  | 
|  | #if USE(BIGINT32) | 
|  | if (v1.isHeapBigInt() && v2.isBigInt32()) | 
|  | return v1.asHeapBigInt()->equalsToInt32(v2.bigInt32AsInt32()); | 
|  | if (v1.isBigInt32() && v2.isHeapBigInt()) | 
|  | return v2.asHeapBigInt()->equalsToInt32(v1.bigInt32AsInt32()); | 
|  | #endif | 
|  |  | 
|  | if (v1.isCell() && v2.isCell()) | 
|  | return strictEqualForCells(globalObject, v1.asCell(), v2.asCell()); | 
|  |  | 
|  | return v1 == v2; | 
|  | } | 
|  |  | 
|  | inline int32_t JSValue::asInt32ForArithmetic() const | 
|  | { | 
|  | if (isBoolean()) | 
|  | return asBoolean(); | 
|  | return asInt32(); | 
|  | } | 
|  |  | 
|  | inline TriState JSValue::pureStrictEqual(JSValue v1, JSValue v2) | 
|  | { | 
|  | if (v1.isInt32() && v2.isInt32()) | 
|  | return triState(v1 == v2); | 
|  |  | 
|  | if (v1.isNumber() && v2.isNumber()) | 
|  | return triState(v1.asNumber() == v2.asNumber()); | 
|  |  | 
|  | #if USE(BIGINT32) | 
|  | if (v1.isHeapBigInt() && v2.isBigInt32()) | 
|  | return triState(v1.asHeapBigInt()->equalsToInt32(v2.bigInt32AsInt32())); | 
|  | if (v1.isBigInt32() && v2.isHeapBigInt()) | 
|  | return triState(v2.asHeapBigInt()->equalsToInt32(v1.bigInt32AsInt32())); | 
|  | #endif | 
|  |  | 
|  | if (v1.isCell() && v2.isCell()) { | 
|  | if (v1.asCell()->isString() && v2.asCell()->isString()) { | 
|  | const StringImpl* v1String = asString(v1)->tryGetValueImpl(); | 
|  | const StringImpl* v2String = asString(v2)->tryGetValueImpl(); | 
|  | if (!v1String || !v2String) | 
|  | return TriState::Indeterminate; | 
|  | return triState(WTF::equal(*v1String, *v2String)); | 
|  | } | 
|  | if (v1.asCell()->isHeapBigInt() && v2.asCell()->isHeapBigInt()) | 
|  | return triState(JSBigInt::equals(v1.asHeapBigInt(), v2.asHeapBigInt())); | 
|  | } | 
|  |  | 
|  | return triState(v1 == v2); | 
|  | } | 
|  |  | 
|  | inline TriState JSValue::pureToBoolean() const | 
|  | { | 
|  | if (isInt32()) | 
|  | return asInt32() ? TriState::True : TriState::False; | 
|  | if (isDouble()) | 
|  | return isNotZeroAndOrdered(asDouble()) ? TriState::True : TriState::False; // false for NaN | 
|  | if (isCell()) | 
|  | return asCell()->pureToBoolean(); | 
|  | #if USE(BIGINT32) | 
|  | if (isBigInt32()) | 
|  | return bigInt32AsInt32() ? TriState::True : TriState::False; | 
|  | #endif | 
|  | return isTrue() ? TriState::True : TriState::False; | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE bool JSValue::requireObjectCoercible(JSGlobalObject* globalObject) const | 
|  | { | 
|  | VM& vm = getVM(globalObject); | 
|  | auto scope = DECLARE_THROW_SCOPE(vm); | 
|  |  | 
|  | if (!isUndefinedOrNull()) | 
|  | return true; | 
|  | throwException(globalObject, scope, createNotAnObjectError(globalObject, *this)); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | ALWAYS_INLINE bool isThisValueAltered(const PutPropertySlot& slot, JSObject* baseObject) | 
|  | { | 
|  | JSValue thisValue = slot.thisValue(); | 
|  | if (LIKELY(thisValue == baseObject)) | 
|  | return false; | 
|  |  | 
|  | if (!thisValue.isObject()) | 
|  | return true; | 
|  | JSObject* thisObject = asObject(thisValue); | 
|  | // Only PureForwardingProxyType can be seen as the same to the original target object. | 
|  | if (thisObject->type() == PureForwardingProxyType && jsCast<JSProxy*>(thisObject)->target() == baseObject) | 
|  | return false; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // See section 7.2.9: https://tc39.github.io/ecma262/#sec-samevalue | 
|  | ALWAYS_INLINE bool sameValue(JSGlobalObject* globalObject, JSValue a, JSValue b) | 
|  | { | 
|  | if (!a.isNumber()) | 
|  | return JSValue::strictEqual(globalObject, a, b); | 
|  | if (!b.isNumber()) | 
|  | return false; | 
|  | double x = a.asNumber(); | 
|  | double y = b.asNumber(); | 
|  | bool xIsNaN = std::isnan(x); | 
|  | bool yIsNaN = std::isnan(y); | 
|  | if (xIsNaN || yIsNaN) | 
|  | return xIsNaN && yIsNaN; | 
|  | return bitwise_cast<uint64_t>(x) == bitwise_cast<uint64_t>(y); | 
|  | } | 
|  |  | 
|  | } // namespace JSC |