blob: fd9a9d3d328227781b4b8f923d877716eb91365d [file]
//-------------------------------------------------------------------------------------------------------
// 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
namespace Js
{
class JavascriptNumber :
#if !FLOATVAR
public RecyclableObject,
#endif
public NumberConstants
{
private:
double m_value;
#if !FLOATVAR
DEFINE_VTABLE_CTOR(JavascriptNumber, RecyclableObject);
#endif
public:
JavascriptNumber(double value, StaticType*
#if DBG
, bool oopJIT = false
#endif
);
static uint32 GetValueOffset()
{
return offsetof(JavascriptNumber, m_value);
}
static bool Is(Var aValue);
static bool Is_NoTaggedIntCheck(Var aValue);
static Var ToVarWithCheck(double value, ScriptContext* scriptContext);
static Var ToVarNoCheck(double value, ScriptContext* scriptContext);
static Var ToVarInPlace(double value, ScriptContext* scriptContext, JavascriptNumber *result);
static Var ToVarMaybeInPlace(double value, ScriptContext* scriptContext, JavascriptNumber *result);
static Var ToVarIntCheck(double value, ScriptContext* scriptContext);
static Var ToVar(int32 nValue, ScriptContext* scriptContext);
#if defined(__clang__) && defined(_M_IX86)
static Var ToVar(intptr_t nValue, ScriptContext* scriptContext);
#endif
static Var ToVarInPlace(int32 nValue, ScriptContext* scriptContext, JavascriptNumber *result);
static Var ToVarInPlace(int64 value, ScriptContext* scriptContext, JavascriptNumber *result);
static Var ToVarInPlace(uint32 nValue, ScriptContext* scriptContext, JavascriptNumber *result);
static Var ToVar(uint32 nValue, ScriptContext* scriptContext);
static Var ToVar(int64 nValue, ScriptContext* scriptContext);
static Var ToVar(uint64 nValue, ScriptContext* scriptContext);
static double GetValue(Var aValue);
static int32 DirectPowIntInt(bool*, int32, int32);
static double DirectPowDoubleInt(double, int32);
static double DirectPow(double, double);
static bool TryToVarFast(int32 nValue, Var* result);
static bool TryToVarFastWithCheck(double value, Var* result);
inline static BOOL IsNan(double value) { return NumberUtilities::IsNan(value); }
static bool IsZero(double value);
static BOOL IsNegZero(double value);
static bool IsPosInf(double value);
static bool IsNegInf(double value);
template<bool acceptNegZero = false>
static bool TryGetInt32Value(const double value, int32 *const int32Value)
{
Assert(int32Value);
const int32 i = static_cast<int32>(value);
if (static_cast<double>(i) != value || (!acceptNegZero && IsNegZero(value)))
{
return false;
}
*int32Value = i;
return true;
}
static bool TryGetInt32OrUInt32Value(const double value, int32 *const int32Value, bool *const isInt32);
static bool IsInt32(const double value);
static bool IsInt32OrUInt32(const double value);
static bool IsInt32_NoChecks(const Var number);
static bool IsInt32OrUInt32_NoChecks(const Var number);
static int32 GetNonzeroInt32Value_NoTaggedIntCheck(const Var object);
static JavascriptString* ToString(double value, ScriptContext* scriptContext);
class EntryInfo
{
public:
static FunctionInfo NewInstance;
// Number constructor built-ins:
static FunctionInfo IsNaN;
static FunctionInfo IsFinite;
static FunctionInfo IsInteger;
static FunctionInfo IsSafeInteger;
// Number prototype built-ins:
static FunctionInfo ToExponential;
static FunctionInfo ToFixed;
static FunctionInfo ToPrecision;
static FunctionInfo ToLocaleString;
static FunctionInfo ToString;
static FunctionInfo ValueOf;
};
static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...);
// Number constructor built-ins:
static Var EntryIsNaN(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryIsFinite(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryIsInteger(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryIsSafeInteger(RecyclableObject* function, CallInfo callInfo, ...);
// Number prototype built-ins:
static Var EntryToExponential(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryToFixed(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryToPrecision(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryToLocaleString(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryToString(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryValueOf(RecyclableObject* function, CallInfo callInfo, ...);
static Var New(double value, ScriptContext* scriptContext);
static Var NewWithCheck(double value, ScriptContext* scriptContext);
static Var NewInlined(double value, ScriptContext* scriptContext);
static JavascriptNumber* InPlaceNew(double value, ScriptContext* scriptContext, JavascriptNumber* result);
#if ENABLE_NATIVE_CODEGEN
#if FLOATVAR
static Var NewCodeGenInstance(double value, ScriptContext* scriptContext);
#else
static Var NewCodeGenInstance(CodeGenNumberAllocator *alloc, double value, ScriptContext* scriptContext);
#endif
#endif
inline static bool IsSpecial(double value, uint64 nSpecial) { return NumberUtilities::IsSpecial(value, nSpecial); }
inline static uint64 ToSpecial(double value) { return NumberUtilities::ToSpecial(value); }
static JavascriptString* ToStringNan(ScriptContext* scriptContext);
static JavascriptString* ToStringRadix10(double dValue, ScriptContext* scriptContext);
// when radix is 10, ToStringRadix10 should be used instead
static JavascriptString* ToStringRadixHelper(double value, int radix, ScriptContext* scriptContext);
static JavascriptString* ToLocaleString(double dValue, ScriptContext* scriptContext);
static JavascriptString* ToLocaleStringIntl(ArgumentReader& args, CallInfo callInfo, ScriptContext* scriptContext);
static JavascriptString* ToLocaleStringIntl(Var* values, CallInfo callInfo, ScriptContext* scriptContext);
static Var CloneToScriptContext(Var aValue, ScriptContext* requestContext);
#if !FLOATVAR
static JavascriptNumber* NewUninitialized(Recycler * recycler);
static Var BoxStackInstance(Var instance, ScriptContext* scriptContext);
static Var BoxStackNumber(Var instance, ScriptContext* scriptContext);
// This is needed to ensure JavascriptNumber has a VTABLE and JavascriptNumber::Is (which checks for the VTABLE value) works correctly.
// This also prevents the vtable from being folded with other classes unexpectedly.
virtual VTableValue DummyVirtualFunctionToHinderLinkerICF()
{
return VTableValue::VtableJavascriptNumber;
}
#endif
#if FLOATVAR
static Var ToVar(double value);
#else
static JavascriptNumber* FromVar(Var aValue);
#endif
private:
void SetValue(double value)
{
m_value = value;
}
double GetValue() const
{
return m_value;
}
void SetSpecial(uint64 value)
{
uint64* pnOverwrite = reinterpret_cast<uint64 *>(&this->m_value);
*pnOverwrite = value;
}
static JavascriptString* ToStringNanOrInfinite(double val, ScriptContext* scriptContext);
static JavascriptString* ToStringNanOrInfiniteOrZero(double val, ScriptContext* scriptContext);
static JavascriptString* ToLocaleStringNanOrInfinite(double val, ScriptContext* scriptContext);
static Var FormatDoubleToString( double value, Js::NumberUtilities::FormatType formatType, int fractionDigits, ScriptContext* scriptContext );
static BOOL GetThisValue(Var avalue, double* pDouble);
template <class Lib>
static typename Lib::LibStringType ToStringNan(const Lib& lib);
template <class Lib>
static typename Lib::LibStringType ToStringNanOrInfinite(double val, const Lib& lib);
};
//
// Implementations shared with diagnostics
//
#if FLOATVAR
// Since RecyclableObject and Int32 in their Var representation have top 14 bits 0
// and tagged float now has top 14 XOR'd with 1s - the value with 14 bits non zero is
// a float.
// A float can have all 14 bits 0 iff it was a NaN in the first place. Since
// we can only produce NaNs with top 13 bits set (see k_Nan) - this cannot happen.
inline bool JavascriptNumber::Is_NoTaggedIntCheck(Var aValue)
{
return ((uint64)aValue >> 50) != 0;
}
inline double JavascriptNumber::GetValue(Var aValue)
{
AssertMsg(Is(aValue), "Ensure var is actually a 'JavascriptNumber'");
double result;
(*(uint64*)(&result)) = (((uint64)aValue) ^ FloatTag_Value);
return result;
}
#endif
inline bool JavascriptNumber::IsZero(double value)
{
// succeeds for -0.0 as well
return value == 0.0;
}
inline bool JavascriptNumber::IsPosInf(double value)
{
return IsSpecial(value, k_PosInf);
}
inline bool JavascriptNumber::IsNegInf(double value)
{
return IsSpecial(value, k_NegInf);
}
inline BOOL JavascriptNumber::IsNegZero(double value)
{
return IsSpecial(value, k_NegZero);
}
template <class Lib>
inline typename Lib::LibStringType JavascriptNumber::ToStringNan(const Lib& lib)
{
return lib.CreateStringFromCppLiteral(_u("NaN"));
}
template <class Lib>
inline typename Lib::LibStringType JavascriptNumber::ToStringNanOrInfinite(double value, const Lib& lib)
{
if(!NumberUtilities::IsFinite(value))
{
if(IsNan(value))
{
return ToStringNan(lib);
}
if(IsPosInf(value))
{
return lib.CreateStringFromCppLiteral(_u("Infinity"));
}
else
{
AssertMsg(IsNegInf(value), "bad handling of infinite number");
return lib.CreateStringFromCppLiteral(_u("-Infinity"));
}
}
return nullptr;
}
}
#if defined(_M_IX86)
#ifdef DBG
#ifdef HEAP_ENUMERATION_VALIDATION
// Heap enum has an extra field m_heapEnumValidationCookie, but it fills in the alignment
// So the size is the same
CompileAssert(sizeof(Js::JavascriptNumber) == 24);
#else
CompileAssert(sizeof(Js::JavascriptNumber) == 16);
#endif
#else
CompileAssert(sizeof(Js::JavascriptNumber) == 16);
#endif
#endif