blob: 89258060065525bec157c46b321f7bad3f6ac049 [file] [log] [blame]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#pragma once
#if defined(_M_IX86) || defined(_M_X64)
#ifdef _WIN32
#include <emmintrin.h>
#endif
#endif
namespace Js {
inline BOOL JavascriptConversion::ToBoolean(Var aValue,ScriptContext* scriptContext)
{
JIT_HELPER_NOT_REENTRANT_HEADER(Conv_ToBoolean, reentrancylock, scriptContext->GetThreadContext());
if (VarIs<JavascriptBoolean>(aValue))
{
return UnsafeVarTo<JavascriptBoolean>(aValue)->GetValue();
}
else if (TaggedInt::Is(aValue))
{
return aValue != reinterpret_cast<Var>(AtomTag_IntPtr);
}
#if FLOATVAR
else if (JavascriptNumber::Is_NoTaggedIntCheck(aValue))
{
double value = JavascriptNumber::GetValue(aValue);
return (!JavascriptNumber::IsNan(value)) && (!JavascriptNumber::IsZero(value));
}
#endif
else
{
return ToBoolean_Full(aValue, scriptContext);
}
}
inline bool JavascriptConversion::ToBool(Var aValue,ScriptContext* scriptContext)
{
return !!JavascriptConversion::ToBoolean(aValue, scriptContext);
}
/// ToNumber() returns an integer value, as described in (ES3.0: S9.3).
inline double JavascriptConversion::ToNumber(Var aValue, ScriptContext* scriptContext)
{
// Optimize for TaggedInt and double before falling back to ToNumber_Full
if( TaggedInt::Is(aValue) )
{
return TaggedInt::ToDouble(aValue);
}
if( JavascriptNumber::Is_NoTaggedIntCheck(aValue) )
{
return JavascriptNumber::GetValue(aValue);
}
return ToNumber_Full(aValue, scriptContext);
}
inline double JavascriptConversion::ToInteger(Var aValue, ScriptContext* scriptContext)
{
return
TaggedInt::Is(aValue) ?
TaggedInt::ToDouble(aValue) :
ToInteger_Full(aValue, scriptContext);
}
inline int32 JavascriptConversion::ToInt32(Var aValue, ScriptContext* scriptContext)
{
return
TaggedInt::Is(aValue) ?
TaggedInt::ToInt32(aValue) :
ToInt32_Full(aValue, scriptContext);
}
inline uint32 JavascriptConversion::ToUInt32(Var aValue, ScriptContext* scriptContext)
{
JIT_HELPER_REENTRANT_HEADER(Conv_ToUInt32);
return
TaggedInt::Is(aValue) ?
TaggedInt::ToUInt32(aValue) :
ToUInt32_Full(aValue, scriptContext);
}
inline uint16 JavascriptConversion::ToUInt16(Var aValue, ScriptContext* scriptContext)
{
return
TaggedInt::Is(aValue) ?
(uint16) TaggedInt::ToUInt32(aValue) :
ToUInt16_Full(aValue, scriptContext);
}
inline int8 JavascriptConversion::ToInt8(Var aValue, ScriptContext* scriptContext)
{
return TaggedInt::Is(aValue) ?
(int8) TaggedInt::ToInt32(aValue) :
(int8) ToInt32(aValue, scriptContext);
}
inline uint8 JavascriptConversion::ToUInt8(Var aValue, ScriptContext* scriptContext)
{
return TaggedInt::Is(aValue) ?
(uint8) TaggedInt::ToInt32(aValue) :
(uint8) ToUInt32(aValue, scriptContext);
}
inline uint8 JavascriptConversion::ToUInt8Clamped(Var aValue, ScriptContext* scriptContext)
{
double dval;
if (TaggedInt::Is(aValue))
{
int32 val = Js::TaggedInt::ToInt32(aValue);
// Values larger than 0xff should be clamped to 0xff
if (val > UINT8_MAX)
{
return UINT8_MAX;
}
// Negative values should be clamped to 0
if (val < 0)
{
return 0;
}
return (uint8) val;
}
else if (JavascriptOperators::GetTypeId(aValue) == TypeIds_Number)
{
dval = JavascriptNumber::GetValue(aValue);
}
else
{
dval = JavascriptConversion::ToNumber_Full(aValue, scriptContext);
}
// This will also cover positive infinity
// Note: This is strictly greater-than check because 254.5 rounds to 254
if (dval > 254.5)
{
return UINT8_MAX;
}
// This will also cover negative infinity, and anything less than INT_MIN
if (dval < 0)
{
return 0;
}
// We now have a double value which is between 0 and 255 and just need to convert it
// to an integer following IEEE 754 rounding rules which round ties to the nearest
// even integer.
#if defined(_M_IX86) || defined(_M_X64)
if (AutoSystemInfo::Data.SSE2Available())
{
// On x86 we have a convenient CVTSD2SI intrinsic function to handle this.
__m128d t = _mm_load_sd(&dval);
return (uint8)_mm_cvtsd_si32(t);
}
else
#endif
{
// On ARM, there is not a convenient intrinsic (for VCVTRS32F64).
// Once DevDiv TFS item 656383 is complete, we should replace the below with the intrinsic.
// 1. Calculate the fractional part of the double value
// 2. Round up or down as usual if the fractional part is <> 0.5
// 3. If the fractional part == 0.5, round to nearest even integer:
// Divide by 2, add 0.5, cast to integer, multiply by 2 again.
uint8 u8 = (uint8)dval;
double frac = dval - u8;
if (frac > 0.5)
{
return (uint8)(dval + 0.5);
}
else if (frac < 0.5)
{
return u8;
}
else
{
return ((uint8)(dval / 2.0 + 0.5)) * 2;
}
}
}
inline int16 JavascriptConversion::ToInt16(Var aValue, ScriptContext* scriptContext)
{
return TaggedInt::Is(aValue) ?
(int16) TaggedInt::ToInt32(aValue) :
(int16) ToUInt32(aValue, scriptContext);
}
inline float JavascriptConversion::ToFloat(Var aValue, ScriptContext* scriptContext)
{
return (float)ToNumber(aValue, scriptContext);
}
inline bool JavascriptConversion::SameValue(Var aValue, Var bValue)
{
return SameValueCommon<false>(aValue, bValue);
}
inline bool JavascriptConversion::SameValueZero(Var aValue, Var bValue)
{
return SameValueCommon<true>(aValue, bValue);
}
template <typename T, bool allowNegOne>
inline Var JavascriptConversion::TryCanonicalizeIntHelper(T val)
{
if (TaggedInt::IsOverflow(val))
{
return nullptr;
}
if (!allowNegOne && val == -1)
{
return nullptr;
}
return TaggedInt::ToVarUnchecked((int)val);
}
template <bool allowNegOne, bool allowLossyConversion>
inline Var JavascriptConversion::TryCanonicalizeAsTaggedInt(Var value, TypeId typeId)
{
switch (typeId)
{
case TypeIds_Integer:
return (allowNegOne || value != TaggedInt::ToVarUnchecked(-1))
? value
: nullptr;
case TypeIds_Number:
{
double doubleVal = JavascriptNumber::GetValue(value);
int32 intVal = 0;
if (!JavascriptNumber::TryGetInt32Value<allowLossyConversion>(doubleVal, &intVal))
{
return nullptr;
}
return TryCanonicalizeIntHelper<int32, allowNegOne>(intVal);
}
case TypeIds_Int64Number:
{
if (!allowLossyConversion)
{
return nullptr;
}
int64 int64Val = UnsafeVarTo<JavascriptInt64Number>(value)->GetValue();
return TryCanonicalizeIntHelper<int64, allowNegOne>(int64Val);
}
case TypeIds_UInt64Number:
{
if (!allowLossyConversion)
{
return nullptr;
}
uint64 uint64Val = UnsafeVarTo<JavascriptUInt64Number>(value)->GetValue();
return TryCanonicalizeIntHelper<uint64, allowNegOne>(uint64Val);
}
default:
return nullptr;
}
}
template <bool allowNegOne, bool allowLossyConversion>
inline Var JavascriptConversion::TryCanonicalizeAsTaggedInt(Var value)
{
TypeId typeId = JavascriptOperators::GetTypeId(value);
return TryCanonicalizeAsTaggedInt<allowNegOne, allowLossyConversion>(value, typeId);
}
// Lossy conversion means values are StrictEqual equivalent,
// but we cannot reconstruct the original value after canonicalization
// (e.g. -0 or an Int64Number object)
template <bool allowLossyConversion>
inline Var JavascriptConversion::TryCanonicalizeAsSimpleVar(Var value)
{
TypeId typeId = JavascriptOperators::GetTypeId(value);
switch (typeId)
{
case TypeIds_Integer:
case TypeIds_Number:
case TypeIds_Int64Number:
case TypeIds_UInt64Number:
{
Var taggedInt = TryCanonicalizeAsTaggedInt<true, allowLossyConversion>(value, typeId);
if (taggedInt)
{
return taggedInt;
}
#if FLOATVAR
if (typeId == TypeIds_Number)
{
double numberValue = JavascriptNumber::GetValue(value);
// NaN and -NaN do not canonicalize to the same value, but they are equal, so only allow +NaN
return JavascriptNumber::IsNan(numberValue)
? JavascriptNumber::IsNegative(numberValue)
? nullptr
: JavascriptNumber::ToVar(JavascriptNumber::NaN)
: value;
}
#else
return nullptr;
#endif
}
case TypeIds_String:
return nullptr;
default:
return value;
}
}
} // namespace Js