summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/runtime/JSCJSValueInlines.h
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/runtime/JSCJSValueInlines.h
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/JavaScriptCore/runtime/JSCJSValueInlines.h')
-rw-r--r--Source/JavaScriptCore/runtime/JSCJSValueInlines.h380
1 files changed, 312 insertions, 68 deletions
diff --git a/Source/JavaScriptCore/runtime/JSCJSValueInlines.h b/Source/JavaScriptCore/runtime/JSCJSValueInlines.h
index b34adf809..5bcb995b4 100644
--- a/Source/JavaScriptCore/runtime/JSCJSValueInlines.h
+++ b/Source/JavaScriptCore/runtime/JSCJSValueInlines.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -23,13 +23,19 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef JSValueInlines_h
-#define JSValueInlines_h
+#pragma once
+#include "Error.h"
+#include "ExceptionHelpers.h"
+#include "Identifier.h"
#include "InternalFunction.h"
#include "JSCJSValue.h"
#include "JSCellInlines.h"
#include "JSFunction.h"
+#include "JSObject.h"
+#include "JSStringInlines.h"
+#include "MathCommon.h"
+#include <wtf/text/StringImpl.h>
namespace JSC {
@@ -42,10 +48,31 @@ ALWAYS_INLINE int32_t JSValue::toInt32(ExecState* exec) const
inline uint32_t JSValue::toUInt32(ExecState* exec) const
{
- // See comment on JSC::toUInt32, above.
+ // See comment on JSC::toUInt32, in JSCJSValue.h.
return toInt32(exec);
}
+inline uint32_t JSValue::toIndex(ExecState* exec, const char* errorName) const
+{
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ double d = toNumber(exec);
+
+ if (d <= -1) {
+ throwException(exec, scope, createRangeError(exec, makeString(errorName, " cannot be negative")));
+ return 0;
+ }
+ if (d > std::numeric_limits<unsigned>::max()) {
+ throwException(exec, scope, createRangeError(exec, makeString(errorName, " too large")));
+ return 0;
+ }
+
+ if (isInt32())
+ return asInt32();
+ return JSC::toInt32(d);
+}
+
inline bool JSValue::isUInt32() const
{
return isInt32() && asInt32() >= 0;
@@ -65,7 +92,7 @@ inline double JSValue::asNumber() const
inline JSValue jsNaN()
{
- return JSValue(QNaN);
+ return JSValue(JSValue::EncodeAsDouble, PNaN);
}
inline JSValue::JSValue(char i)
@@ -135,6 +162,7 @@ inline JSValue::JSValue(unsigned long long i)
inline JSValue::JSValue(double d)
{
+ // Note: while this behavior is undefined for NaN and inf, the subsequent statement will catch these cases.
const int32_t asInt32 = static_cast<int32_t>(d);
if (asInt32 != d || (!asInt32 && std::signbit(d))) { // true for -0.0
*this = JSValue(EncodeAsDouble, d);
@@ -210,10 +238,10 @@ inline JSValue::JSValue(const JSCell* ptr)
u.asBits.payload = reinterpret_cast<int32_t>(const_cast<JSCell*>(ptr));
}
-inline JSValue::operator UnspecifiedBoolType*() const
+inline JSValue::operator bool() const
{
ASSERT(tag() != DeletedValueTag);
- return tag() != EmptyValueTag ? reinterpret_cast<UnspecifiedBoolType*>(1) : 0;
+ return tag() != EmptyValueTag;
}
inline bool JSValue::operator==(const JSValue& other) const
@@ -301,6 +329,7 @@ ALWAYS_INLINE JSCell* JSValue::asCell() const
ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, double d)
{
+ ASSERT(!isImpureNaN(d));
u.asDouble = d;
}
@@ -310,7 +339,7 @@ inline JSValue::JSValue(int i)
u.asBits.payload = i;
}
-#if ENABLE(LLINT_C_LOOP)
+#if !ENABLE(JIT)
inline JSValue::JSValue(int32_t tag, int32_t payload)
{
u.asBits.tag = tag;
@@ -325,7 +354,7 @@ inline bool JSValue::isNumber() const
inline bool JSValue::isBoolean() const
{
- return isTrue() || isFalse();
+ return tag() == BooleanTag;
}
inline bool JSValue::asBoolean() const
@@ -358,9 +387,9 @@ inline JSValue::JSValue(const JSCell* ptr)
u.asInt64 = reinterpret_cast<uintptr_t>(const_cast<JSCell*>(ptr));
}
-inline JSValue::operator UnspecifiedBoolType*() const
+inline JSValue::operator bool() const
{
- return u.asInt64 ? reinterpret_cast<UnspecifiedBoolType*>(1) : 0;
+ return u.asInt64;
}
inline bool JSValue::operator==(const JSValue& other) const
@@ -467,6 +496,7 @@ inline double reinterpretInt64ToDouble(int64_t value)
ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, double d)
{
+ ASSERT(!isImpureNaN(d));
u.asInt64 = reinterpretDoubleToInt64(d) + DoubleEncodeOffset;
}
@@ -494,30 +524,48 @@ ALWAYS_INLINE JSCell* JSValue::asCell() const
#endif // USE(JSVALUE64)
-inline bool JSValue::isMachineInt() const
+inline int64_t tryConvertToInt52(double number)
{
- if (isInt32())
- return true;
- if (!isNumber())
- return false;
- double number = asDouble();
if (number != number)
- return false;
+ 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 false;
+ 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;
- if (asInt64 >= (static_cast<int64_t>(1) << (numberOfInt52Bits - 1)))
- return false;
- if (asInt64 < -(static_cast<int64_t>(1) << (numberOfInt52Bits - 1)))
- return false;
- return true;
+ return isInt52(asDouble());
}
-inline int64_t JSValue::asMachineInt() const
+inline int64_t JSValue::asAnyInt() const
{
- ASSERT(isMachineInt());
+ ASSERT(isAnyInt());
if (isInt32())
return asInt32();
return static_cast<int64_t>(asDouble());
@@ -528,9 +576,14 @@ inline bool JSValue::isString() const
return isCell() && asCell()->isString();
}
+inline bool JSValue::isSymbol() const
+{
+ return isCell() && asCell()->isSymbol();
+}
+
inline bool JSValue::isPrimitive() const
{
- return !isCell() || asCell()->isString();
+ return !isCell() || asCell()->isString() || asCell()->isSymbol();
}
inline bool JSValue::isGetterSetter() const
@@ -538,6 +591,11 @@ 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();
@@ -578,11 +636,51 @@ ALWAYS_INLINE bool JSValue::getUInt32(uint32_t& v) const
return false;
}
+ALWAYS_INLINE Identifier JSValue::toPropertyKey(ExecState* exec) const
+{
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ if (isString())
+ return asString(*this)->toIdentifier(exec);
+
+ JSValue primitive = toPrimitive(exec, PreferString);
+ RETURN_IF_EXCEPTION(scope, vm.propertyNames->emptyIdentifier);
+ if (primitive.isSymbol())
+ return Identifier::fromUid(asSymbol(primitive)->privateName());
+ scope.release();
+ return primitive.toString(exec)->toIdentifier(exec);
+}
+
inline JSValue JSValue::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const
{
return isCell() ? asCell()->toPrimitive(exec, preferredType) : asValue();
}
+inline PreferredPrimitiveType toPreferredPrimitiveType(ExecState* exec, JSValue value)
+{
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ if (!value.isString()) {
+ throwTypeError(exec, scope, ASCIILiteral("Primitive hint is not a string."));
+ return NoPreference;
+ }
+
+ StringImpl* hintString = asString(value)->value(exec).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(exec, scope, ASCIILiteral("Expected primitive hint to match one of 'default', 'number', 'string'."));
+ return NoPreference;
+}
+
inline bool JSValue::getPrimitiveNumber(ExecState* exec, double& number, JSValue& value)
{
if (isInt32()) {
@@ -608,7 +706,7 @@ inline bool JSValue::getPrimitiveNumber(ExecState* exec, double& number, JSValue
return true;
}
ASSERT(isUndefined());
- number = QNaN;
+ number = PNaN;
value = *this;
return true;
}
@@ -634,58 +732,123 @@ inline JSObject* JSValue::toObject(ExecState* exec, JSGlobalObject* globalObject
inline bool JSValue::isFunction() const
{
- return isCell() && (asCell()->inherits(JSFunction::info()) || asCell()->inherits(InternalFunction::info()));
+ if (!isCell())
+ return false;
+ JSCell* cell = asCell();
+ CallData ignored;
+ return cell->methodTable()->getCallData(cell, ignored) != CallType::None;
+}
+
+inline bool JSValue::isFunction(CallType& callType, CallData& callData) const
+{
+ return isCallable(callType, callData);
+}
+
+inline bool JSValue::isCallable(CallType& callType, CallData& callData) const
+{
+ if (!isCell())
+ return false;
+ JSCell* cell = asCell();
+ callType = cell->methodTable()->getCallData(cell, callData);
+ return callType != CallType::None;
+}
+
+inline bool JSValue::isConstructor() const
+{
+ if (!isCell())
+ return false;
+ JSCell* cell = asCell();
+ ConstructData ignored;
+ return cell->methodTable()->getConstructData(cell, ignored) != ConstructType::None;
+}
+
+inline bool JSValue::isConstructor(ConstructType& constructType, ConstructData& constructData) const
+{
+ if (!isCell())
+ return false;
+ JSCell* cell = asCell();
+ constructType = cell->methodTable()->getConstructData(cell, constructData);
+ return constructType != ConstructType::None;
}
// this method is here to be after the inline declaration of JSCell::inherits
-inline bool JSValue::inherits(const ClassInfo* classInfo) const
+inline bool JSValue::inherits(VM& vm, const ClassInfo* classInfo) const
+{
+ return isCell() && asCell()->inherits(vm, classInfo);
+}
+
+inline const ClassInfo* JSValue::classInfoOrNull(VM& vm) const
{
- return isCell() && asCell()->inherits(classInfo);
+ return isCell() ? asCell()->classInfo(vm) : nullptr;
}
inline JSValue JSValue::toThis(ExecState* exec, ECMAMode ecmaMode) const
{
- return isCell() ? asCell()->methodTable()->toThis(asCell(), exec, ecmaMode) : toThisSlowCase(exec, ecmaMode);
+ return isCell() ? asCell()->methodTable(exec->vm())->toThis(asCell(), exec, ecmaMode) : toThisSlowCase(exec, ecmaMode);
}
-inline JSValue JSValue::get(ExecState* exec, PropertyName propertyName) const
+ALWAYS_INLINE JSValue JSValue::get(ExecState* exec, PropertyName propertyName) const
{
- PropertySlot slot(asValue());
+ PropertySlot slot(asValue(), PropertySlot::InternalMethodType::Get);
return get(exec, propertyName, slot);
}
-inline JSValue JSValue::get(ExecState* exec, PropertyName propertyName, PropertySlot& slot) const
+ALWAYS_INLINE JSValue JSValue::get(ExecState* exec, PropertyName propertyName, PropertySlot& slot) const
+{
+ return getPropertySlot(exec, propertyName, slot) ?
+ slot.getValue(exec, propertyName) : jsUndefined();
+}
+
+template<typename CallbackWhenNoException>
+ALWAYS_INLINE typename std::result_of<CallbackWhenNoException(bool, PropertySlot&)>::type JSValue::getPropertySlot(ExecState* exec, PropertyName propertyName, CallbackWhenNoException callback) const
+{
+ PropertySlot slot(asValue(), PropertySlot::InternalMethodType::Get);
+ return getPropertySlot(exec, propertyName, slot, callback);
+}
+
+template<typename CallbackWhenNoException>
+ALWAYS_INLINE typename std::result_of<CallbackWhenNoException(bool, PropertySlot&)>::type JSValue::getPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot, CallbackWhenNoException callback) const
+{
+ auto scope = DECLARE_THROW_SCOPE(exec->vm());
+ bool found = getPropertySlot(exec, propertyName, slot);
+ RETURN_IF_EXCEPTION(scope, { });
+ return callback(found, slot);
+}
+
+ALWAYS_INLINE bool JSValue::getPropertySlot(ExecState* exec, 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.
JSObject* object;
if (UNLIKELY(!isObject())) {
- if (isCell() && asString(*this)->getStringPropertySlot(exec, propertyName, slot))
- return slot.getValue(exec, propertyName);
+ if (isString() && asString(*this)->getStringPropertySlot(exec, propertyName, slot))
+ return true;
object = synthesizePrototype(exec);
+ if (UNLIKELY(!object))
+ return false;
} else
object = asObject(asCell());
- if (object->getPropertySlot(exec, propertyName, slot))
- return slot.getValue(exec, propertyName);
- return jsUndefined();
+ return object->getPropertySlot(exec, propertyName, slot);
}
-inline JSValue JSValue::get(ExecState* exec, unsigned propertyName) const
+ALWAYS_INLINE JSValue JSValue::get(ExecState* exec, unsigned propertyName) const
{
- PropertySlot slot(asValue());
+ PropertySlot slot(asValue(), PropertySlot::InternalMethodType::Get);
return get(exec, propertyName, slot);
}
-inline JSValue JSValue::get(ExecState* exec, unsigned propertyName, PropertySlot& slot) const
+ALWAYS_INLINE JSValue JSValue::get(ExecState* exec, unsigned 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.
JSObject* object;
if (UNLIKELY(!isObject())) {
- if (isCell() && asString(*this)->getStringPropertySlot(exec, propertyName, slot))
+ if (isString() && asString(*this)->getStringPropertySlot(exec, propertyName, slot))
return slot.getValue(exec, propertyName);
object = synthesizePrototype(exec);
+ if (UNLIKELY(!object))
+ return JSValue();
} else
object = asObject(asCell());
@@ -694,22 +857,50 @@ inline JSValue JSValue::get(ExecState* exec, unsigned propertyName, PropertySlot
return jsUndefined();
}
-inline void JSValue::put(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
+ALWAYS_INLINE JSValue JSValue::get(ExecState* exec, uint64_t propertyName) const
{
- if (UNLIKELY(!isCell())) {
- putToPrimitive(exec, propertyName, value, slot);
- return;
- }
- asCell()->methodTable()->put(asCell(), exec, propertyName, value, slot);
+ if (LIKELY(propertyName <= std::numeric_limits<unsigned>::max()))
+ return get(exec, static_cast<unsigned>(propertyName));
+ return get(exec, Identifier::from(exec, static_cast<double>(propertyName)));
}
-inline void JSValue::putByIndex(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
+inline bool JSValue::put(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
{
- if (UNLIKELY(!isCell())) {
- putToPrimitiveByIndex(exec, propertyName, value, shouldThrow);
- return;
- }
- asCell()->methodTable()->putByIndex(asCell(), exec, propertyName, value, shouldThrow);
+ if (UNLIKELY(!isCell()))
+ return putToPrimitive(exec, propertyName, value, slot);
+
+ return asCell()->methodTable(exec->vm())->put(asCell(), exec, propertyName, value, slot);
+}
+
+ALWAYS_INLINE bool JSValue::putInline(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
+{
+ if (UNLIKELY(!isCell()))
+ return putToPrimitive(exec, propertyName, value, slot);
+
+ JSCell* cell = asCell();
+ auto putMethod = cell->methodTable(exec->vm())->put;
+ if (LIKELY(putMethod == JSObject::put))
+ return JSObject::putInline(cell, exec, propertyName, value, slot);
+
+ PutPropertySlot otherSlot = slot;
+ bool result = putMethod(cell, exec, propertyName, value, otherSlot);
+ slot = otherSlot;
+ return result;
+}
+
+inline bool JSValue::putByIndex(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
+{
+ if (UNLIKELY(!isCell()))
+ return putToPrimitiveByIndex(exec, propertyName, value, shouldThrow);
+
+ return asCell()->methodTable(exec->vm())->putByIndex(asCell(), exec, propertyName, value, shouldThrow);
+}
+
+inline Structure* JSValue::structureOrNull() const
+{
+ if (isCell())
+ return asCell()->structure();
+ return nullptr;
}
inline JSValue JSValue::structureOrUndefined() const
@@ -730,6 +921,8 @@ inline bool JSValue::equal(ExecState* exec, JSValue v1, JSValue v2)
ALWAYS_INLINE bool JSValue::equalSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2)
{
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
do {
if (v1.isNumber() && v2.isNumber())
return v1.asNumber() == v2.asNumber();
@@ -737,28 +930,27 @@ ALWAYS_INLINE bool JSValue::equalSlowCaseInline(ExecState* exec, JSValue v1, JSV
bool s1 = v1.isString();
bool s2 = v2.isString();
if (s1 && s2)
- return asString(v1)->value(exec) == asString(v2)->value(exec);
+ return asString(v1)->equal(exec, asString(v2));
if (v1.isUndefinedOrNull()) {
if (v2.isUndefinedOrNull())
return true;
if (!v2.isCell())
return false;
- return v2.asCell()->structure()->masqueradesAsUndefined(exec->lexicalGlobalObject());
+ return v2.asCell()->structure(vm)->masqueradesAsUndefined(exec->lexicalGlobalObject());
}
if (v2.isUndefinedOrNull()) {
if (!v1.isCell())
return false;
- return v1.asCell()->structure()->masqueradesAsUndefined(exec->lexicalGlobalObject());
+ return v1.asCell()->structure(vm)->masqueradesAsUndefined(exec->lexicalGlobalObject());
}
if (v1.isObject()) {
if (v2.isObject())
return v1 == v2;
JSValue p1 = v1.toPrimitive(exec);
- if (exec->hadException())
- return false;
+ RETURN_IF_EXCEPTION(scope, false);
v1 = p1;
if (v1.isInt32() && v2.isInt32())
return v1 == v2;
@@ -767,14 +959,21 @@ ALWAYS_INLINE bool JSValue::equalSlowCaseInline(ExecState* exec, JSValue v1, JSV
if (v2.isObject()) {
JSValue p2 = v2.toPrimitive(exec);
- if (exec->hadException())
- return false;
+ 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 asSymbol(v1) == asSymbol(v2);
+ return false;
+ }
+
if (s1 || s2) {
double d1 = v1.toNumber(exec);
double d2 = v2.toNumber(exec);
@@ -799,8 +998,7 @@ ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(ExecState* exec, JSValue v
ASSERT(v1.isCell() && v2.isCell());
if (v1.asCell()->isString() && v2.asCell()->isString())
- return asString(v1)->value(exec) == asString(v2)->value(exec);
-
+ return asString(v1)->equal(exec, asString(v2));
return v1 == v2;
}
@@ -818,6 +1016,13 @@ inline bool JSValue::strictEqual(ExecState* exec, JSValue v1, JSValue v2)
return strictEqualSlowCaseInline(exec, 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())
@@ -834,7 +1039,7 @@ inline TriState JSValue::pureStrictEqual(JSValue v1, JSValue v2)
const StringImpl* v2String = asString(v2)->tryGetValueImpl();
if (!v1String || !v2String)
return MixedTriState;
- return triState(WTF::equal(v1String, v2String));
+ return triState(WTF::equal(*v1String, *v2String));
}
return triState(v1 == v2);
@@ -851,7 +1056,46 @@ inline TriState JSValue::pureToBoolean() const
return isTrue() ? TrueTriState : FalseTriState;
}
-} // namespace JSC
+ALWAYS_INLINE bool JSValue::requireObjectCoercible(ExecState* exec) const
+{
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
-#endif // JSValueInlines_h
+ if (!isUndefinedOrNull())
+ return true;
+ throwException(exec, scope, createNotAnObjectError(exec, *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(ExecState* exec, JSValue a, JSValue b)
+{
+ if (!a.isNumber())
+ return JSValue::strictEqual(exec, 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