| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "RuntimeLibraryPch.h"
- #include "errstr.h"
- #include "Library/EngineInterfaceObject.h"
- #include "Library/IntlEngineInterfaceExtensionObject.h"
- using namespace PlatformAgnostic;
- namespace Js
- {
- DEFINE_RECYCLER_TRACKER_PERF_COUNTER(JavascriptNumber);
- Var JavascriptNumber::ToVarNoCheck(double value, ScriptContext* scriptContext)
- {
- return JavascriptNumber::NewInlined(value, scriptContext);
- }
- Var JavascriptNumber::ToVarWithCheck(double value, ScriptContext* scriptContext)
- {
- #if FLOATVAR
- if (IsNan(value))
- {
- value = IsNegative(value) ? JavascriptNumber::NegativeNaN : JavascriptNumber::NaN;
- }
- #endif
- return JavascriptNumber::NewInlined(value, scriptContext);
- }
- Var JavascriptNumber::ToVarInPlace(double value, ScriptContext* scriptContext, JavascriptNumber *result)
- {
- return InPlaceNew(value, scriptContext, result);
- }
- Var JavascriptNumber::ToVarInPlace(int64 value, ScriptContext* scriptContext, JavascriptNumber *result)
- {
- if (!TaggedInt::IsOverflow(value))
- {
- return TaggedInt::ToVarUnchecked(static_cast<int>(value));
- }
- return InPlaceNew(static_cast<double>(value), scriptContext, result);
- }
- Var JavascriptNumber::ToVarMaybeInPlace(double value, ScriptContext* scriptContext, JavascriptNumber *result)
- {
- if (result)
- {
- return InPlaceNew(value, scriptContext, result);
- }
- return ToVarNoCheck(value, scriptContext);
- }
- Var JavascriptNumber::ToVarInPlace(int32 nValue, ScriptContext* scriptContext, Js::JavascriptNumber *result)
- {
- if (!TaggedInt::IsOverflow(nValue))
- {
- return TaggedInt::ToVarUnchecked(nValue);
- }
- return InPlaceNew(static_cast<double>(nValue), scriptContext, result);
- }
- Var JavascriptNumber::ToVarInPlace(uint32 nValue, ScriptContext* scriptContext, Js::JavascriptNumber *result)
- {
- if (!TaggedInt::IsOverflow(nValue))
- {
- return TaggedInt::ToVarUnchecked(nValue);
- }
- return InPlaceNew(static_cast<double>(nValue), scriptContext, result);
- }
- Var JavascriptNumber::ToVarIntCheck(double value,ScriptContext* scriptContext)
- {
- //
- // Check if a well-known value:
- // - This significantly cuts down on the below floating-point to integer conversions.
- //
- if (value == 0.0)
- {
- if(IsNegZero(value))
- {
- return scriptContext->GetLibrary()->GetNegativeZero();
- }
- return TaggedInt::ToVarUnchecked(0);
- }
- if (value == 1.0)
- {
- return TaggedInt::ToVarUnchecked(1);
- }
- //
- // Check if number can be reduced back into a TaggedInt:
- // - This avoids extra GC.
- //
- int nValue = (int) value;
- double dblCheck = (double) nValue;
- if ((dblCheck == value) && (!TaggedInt::IsOverflow(nValue)))
- {
- return TaggedInt::ToVarUnchecked(nValue);
- }
- return JavascriptNumber::NewInlined(value,scriptContext);
- }
- bool JavascriptNumber::TryGetInt32OrUInt32Value(const double value, int32 *const int32Value, bool *const isInt32)
- {
- Assert(int32Value);
- Assert(isInt32);
- if(value <= 0)
- {
- return *isInt32 = TryGetInt32Value(value, int32Value);
- }
- const uint32 i = static_cast<uint32>(value);
- if(static_cast<double>(i) != value)
- {
- return false;
- }
- *int32Value = i;
- *isInt32 = static_cast<int32>(i) >= 0;
- return true;
- }
- bool JavascriptNumber::IsInt32(const double value)
- {
- int32 i;
- return TryGetInt32Value(value, &i);
- }
- bool JavascriptNumber::IsInt32OrUInt32(const double value)
- {
- int32 i;
- bool isInt32;
- return TryGetInt32OrUInt32Value(value, &i, &isInt32);
- }
- bool JavascriptNumber::IsInt32_NoChecks(const Var number)
- {
- Assert(number);
- Assert(Is(number));
- return IsInt32(GetValue(number));
- }
- bool JavascriptNumber::IsInt32OrUInt32_NoChecks(const Var number)
- {
- Assert(number);
- Assert(Is(number));
- return IsInt32OrUInt32(GetValue(number));
- }
- int32 JavascriptNumber::GetNonzeroInt32Value_NoTaggedIntCheck(const Var object)
- {
- Assert(object);
- Assert(!TaggedInt::Is(object));
- int32 i;
- return Is_NoTaggedIntCheck(object) && TryGetInt32Value(GetValue(object), &i) ? i : 0;
- }
- int32 JavascriptNumber::DirectPowIntInt(bool* isOverflow, int32 x, int32 y)
- {
- if (y < 0)
- {
- *isOverflow = true;
- return 0;
- }
- uint32 uexp = static_cast<uint32>(y);
- int32 result = 1;
- while (true)
- {
- if ((uexp & 1) != 0)
- {
- if (Int32Math::Mul(result, x, &result))
- {
- *isOverflow = true;
- break;
- }
- }
- if ((uexp >>= 1) == 0)
- {
- *isOverflow = false;
- break;
- }
- if (Int32Math::Mul(x, x, &x))
- {
- *isOverflow = true;
- break;
- }
- }
- return *isOverflow ? 0 : result;
- }
- double JavascriptNumber::DirectPowDoubleInt(double x, int32 y)
- {
- // For exponent in [-8, 8], aggregate the product according to binary representation
- // of exponent. This acceleration may lead to significant deviation for larger exponent
- if (y >= -8 && y <= 8)
- {
- uint32 uexp = static_cast<uint32>(y >= 0 ? y : -y);
- for (double result = 1.0; ; x *= x)
- {
- if ((uexp & 1) != 0)
- {
- result *= x;
- }
- if ((uexp >>= 1) == 0)
- {
- return (y < 0 ? (1.0 / result) : result);
- }
- }
- }
- // always call pow(double, double) in C runtime which has a bug to process pow(double, int).
- return ::pow(x, static_cast<double>(y));
- }
- #if _M_IX86
- extern "C" double __cdecl __libm_sse2_pow(double, double);
- static const double d1_0 = 1.0;
- #if !ENABLE_NATIVE_CODEGEN
- double JavascriptNumber::DirectPow(double x, double y)
- {
- return ::pow(x, y);
- }
- #else
- #pragma warning(push)
- // C4740: flow in or out of inline asm code suppresses global optimization
- // It is fine to disable glot opt on this function which is mostly written in assembly
- #pragma warning(disable:4740)
- __declspec(naked)
- double JavascriptNumber::DirectPow(double x, double y)
- {
- UNREFERENCED_PARAMETER(x);
- UNREFERENCED_PARAMETER(y);
- double savedX, savedY, result;
- // This function is called directly from jitted, float-preferenced code.
- // It looks for x and y in xmm0 and xmm1 and returns the result in xmm0.
- // Check for pow(1, Infinity/NaN) and return NaN in that case;
- // then check fast path of small integer exponent, otherwise,
- // go to the fast CRT helper.
- __asm {
- // check y for 1.0
- ucomisd xmm1, d1_0
- jne pow_full
- jp pow_full
- ret
- pow_full:
- // Check y for non-finite value
- pextrw eax, xmm1, 3
- not eax
- test eax, 0x7ff0
- jne normal
- // check for |x| == 1
- movsd xmm2, xmm0
- andpd xmm2, AbsDoubleCst
- movsd xmm3, d1_0
- ucomisd xmm2, xmm3
- lahf
- test ah, 68
- jp normal
- movsd xmm0, JavascriptNumber::k_Nan
- ret
- normal:
- push ebp
- mov ebp, esp // prepare stack frame for sub function call
- sub esp, 0x40 // 4 variables, reserve 0x10 for 1
- movsd savedX, xmm0
- movsd savedY, xmm1
- }
- int intY;
- if (TryGetInt32Value(savedY, &intY) && intY >= -8 && intY <= 8)
- {
- result = DirectPowDoubleInt(savedX, intY);
- __asm {
- movsd xmm0, result
- }
- }
- else
- {
- __asm {
- movsd xmm0, savedX
- movsd xmm1, savedY
- call dword ptr[__libm_sse2_pow]
- }
- }
- __asm {
- mov esp, ebp
- pop ebp
- ret
- }
- }
- #pragma warning(pop)
- #endif
- #elif defined(_M_AMD64) || defined(_M_ARM32_OR_ARM64)
- double JavascriptNumber::DirectPow(double x, double y)
- {
- if(y == 1.0)
- {
- return x;
- }
- // For AMD64/ARM calling convention already uses SSE2/VFP registers so we don't have to use assembler.
- // We can't just use "if (0 == y)" because NaN compares
- // equal to 0 according to our compilers.
- int32 intY;
- if (0 == NumberUtilities::LuLoDbl(y) && 0 == (NumberUtilities::LuHiDbl(y) & 0x7FFFFFFF))
- {
- // pow(x, 0) = 1 even if x is NaN.
- return 1;
- }
- else if (1.0 == fabs(x) && !NumberUtilities::IsFinite(y))
- {
- // pow([+/-] 1, Infinity) = NaN according to javascript, but not for CRT pow.
- return JavascriptNumber::NaN;
- }
- else if (TryGetInt32Value(y, &intY))
- {
- // check fast path
- return DirectPowDoubleInt(x, intY);
- }
- return ::pow(x, y);
- }
- #else
- double JavascriptNumber::DirectPow(double x, double y)
- {
- UNREFERENCED_PARAMETER(x);
- UNREFERENCED_PARAMETER(y);
- AssertMsg(0, "DirectPow NYI");
- return 0;
- }
- #endif
- Var JavascriptNumber::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- //
- // Determine if called as a constructor or a function.
- //
- AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
- Var newTarget = args.GetNewTarget();
- bool isCtorSuperCall = JavascriptOperators::GetAndAssertIsConstructorSuperCall(args);
- Var result;
- if (args.Info.Count > 1)
- {
- if (TaggedInt::Is(args[1]) || JavascriptNumber::Is(args[1]))
- {
- result = args[1];
- }
- else if (VarIs<JavascriptNumberObject>(args[1]))
- {
- result = JavascriptNumber::ToVarNoCheck(VarTo<JavascriptNumberObject>(args[1])->GetValue(), scriptContext);
- }
- else
- {
- result = JavascriptNumber::ToVarNoCheck(JavascriptConversion::ToNumber(args[1], scriptContext), scriptContext);
- }
- }
- else
- {
- result = TaggedInt::ToVarUnchecked(0);
- }
- if (callInfo.Flags & CallFlags_New)
- {
- JavascriptNumberObject* obj = scriptContext->GetLibrary()->CreateNumberObject(result);
- result = obj;
- }
- return isCtorSuperCall ?
- JavascriptOperators::OrdinaryCreateFromConstructor(VarTo<RecyclableObject>(newTarget), VarTo<RecyclableObject>(result), nullptr, scriptContext) :
- result;
- }
- ///----------------------------------------------------------------------------
- /// ParseInt() returns an integer value dictated by the interpretation of the
- /// given string argument according to the given radix argument, as described
- /// in (ES6.0: S20.1.2.13).
- ///
- /// Note: This is the same as the global parseInt() function as described in
- /// (ES6.0: S18.2.5)
- ///
- /// We actually reuse GlobalObject::EntryParseInt, so no implementation here.
- ///----------------------------------------------------------------------------
- //Var JavascriptNumber::EntryParseInt(RecyclableObject* function, CallInfo callInfo, ...)
- ///----------------------------------------------------------------------------
- /// ParseFloat() returns a Number value dictated by the interpretation of the
- /// given string argument as a decimal literal, as described in
- /// (ES6.0: S20.1.2.12).
- ///
- /// Note: This is the same as the global parseFloat() function as described in
- /// (ES6.0: S18.2.4)
- ///
- /// We actually reuse GlobalObject::EntryParseFloat, so no implementation here.
- ///----------------------------------------------------------------------------
- //Var JavascriptNumber::EntryParseFloat(RecyclableObject* function, CallInfo callInfo, ...)
- ///----------------------------------------------------------------------------
- /// IsNaN() return true if the given value is a Number value and is NaN, as
- /// described in (ES6.0: S20.1.2.4).
- /// Note: This is the same as the global isNaN() function as described in
- /// (ES6.0: S18.2.3) except that it does not coerce the argument to
- /// Number, instead returns false if not already a Number.
- ///----------------------------------------------------------------------------
- Var JavascriptNumber::EntryIsNaN(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Number_Constructor_isNaN);
- if (args.Info.Count < 2 || !JavascriptOperators::IsAnyNumberValue(args[1]))
- {
- return scriptContext->GetLibrary()->GetFalse();
- }
- return JavascriptBoolean::ToVar(
- JavascriptNumber::IsNan(JavascriptConversion::ToNumber(args[1],scriptContext)),
- scriptContext);
- }
- ///----------------------------------------------------------------------------
- /// IsFinite() returns true if the given value is a Number value and is not
- /// one of NaN, +Infinity, or -Infinity, as described in (ES6.0: S20.1.2.2).
- /// Note: This is the same as the global isFinite() function as described in
- /// (ES6.0: S18.2.2) except that it does not coerce the argument to
- /// Number, instead returns false if not already a Number.
- ///----------------------------------------------------------------------------
- Var JavascriptNumber::EntryIsFinite(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Number_Constructor_isFinite);
- if (args.Info.Count < 2 || !JavascriptOperators::IsAnyNumberValue(args[1]))
- {
- return scriptContext->GetLibrary()->GetFalse();
- }
- return JavascriptBoolean::ToVar(
- NumberUtilities::IsFinite(JavascriptConversion::ToNumber(args[1],scriptContext)),
- scriptContext);
- }
- ///----------------------------------------------------------------------------
- /// IsInteger() returns true if the given value is a Number value and is an
- /// integer, as described in (ES6.0: S20.1.2.3).
- ///----------------------------------------------------------------------------
- Var JavascriptNumber::EntryIsInteger(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Number_Constructor_isInteger);
- if (args.Info.Count < 2 || !JavascriptOperators::IsAnyNumberValue(args[1]))
- {
- return scriptContext->GetLibrary()->GetFalse();
- }
- double number = JavascriptConversion::ToNumber(args[1], scriptContext);
- return JavascriptBoolean::ToVar(
- number == JavascriptConversion::ToInteger(args[1], scriptContext) &&
- NumberUtilities::IsFinite(number),
- scriptContext);
- }
- ///----------------------------------------------------------------------------
- /// IsSafeInteger() returns true if the given value is a Number value and is an
- /// integer, as described in (ES6.0: S20.1.2.5).
- ///----------------------------------------------------------------------------
- Var JavascriptNumber::EntryIsSafeInteger(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Number_Constructor_isSafeInteger);
- if (args.Info.Count < 2 || !JavascriptOperators::IsAnyNumberValue(args[1]))
- {
- return scriptContext->GetLibrary()->GetFalse();
- }
- double number = JavascriptConversion::ToNumber(args[1], scriptContext);
- return JavascriptBoolean::ToVar(
- number == JavascriptConversion::ToInteger(args[1], scriptContext) &&
- number >= Js::Math::MIN_SAFE_INTEGER && number <= Js::Math::MAX_SAFE_INTEGER,
- scriptContext);
- }
- Var JavascriptNumber::EntryToExponential(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedNumber, _u("Number.prototype.toExponential"));
- }
- AssertMsg(args.Info.Count > 0, "negative arg count");
- // spec implies ToExp is not generic. 'this' must be a number
- double value;
- if (!GetThisValue(args[0], &value))
- {
- if (JavascriptOperators::GetTypeId(args[0]) == TypeIds_HostDispatch)
- {
- Var result;
- if (VarTo<RecyclableObject>(args[0])->InvokeBuiltInOperationRemotely(EntryToExponential, args, &result))
- {
- return result;
- }
- }
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedNumber, _u("Number.prototype.toExponential"));
- }
- JavascriptString * nanF;
- if (nullptr != (nanF = ToStringNanOrInfinite(value, scriptContext)))
- return nanF;
- // If the Fraction param. is not present we have to output as many fractional digits as we can
- int fractionDigits = -1;
- if(args.Info.Count > 1)
- {
- //use the first arg as the fraction digits, ignore the rest.
- Var aFractionDigits = args[1];
- bool noRangeCheck = false;
- // shortcut for tagged int's
- if(TaggedInt::Is(aFractionDigits))
- {
- fractionDigits = TaggedInt::ToInt32(aFractionDigits);
- }
- else if(JavascriptOperators::GetTypeId(aFractionDigits) == TypeIds_Undefined)
- {
- // fraction undefined -> digits = -1, output as many fractional digits as we can
- noRangeCheck = true;
- }
- else
- {
- fractionDigits = (int)JavascriptConversion::ToInteger(aFractionDigits, scriptContext);
- }
- if(!noRangeCheck && (fractionDigits < 0 || fractionDigits >20))
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_FractionOutOfRange);
- }
- }
- return FormatDoubleToString(value, Js::NumberUtilities::FormatExponential, fractionDigits, scriptContext);
- }
- Var JavascriptNumber::EntryToFixed(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedNumber, _u("Number.prototype.toFixed"));
- }
- AssertMsg(args.Info.Count > 0, "negative arg count");
- // spec implies ToFixed is not generic. 'this' must be a number
- double value;
- if (!GetThisValue(args[0], &value))
- {
- if (JavascriptOperators::GetTypeId(args[0]) == TypeIds_HostDispatch)
- {
- Var result;
- if (VarTo<RecyclableObject>(args[0])->InvokeBuiltInOperationRemotely(EntryToFixed, args, &result))
- {
- return result;
- }
- }
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedNumber, _u("Number.prototype.toFixed"));
- }
- int fractionDigits = 0;
- bool isFractionDigitsInfinite = false;
- if(args.Info.Count > 1)
- {
- //use the first arg as the fraction digits, ignore the rest.
- Var aFractionDigits = args[1];
- // shortcut for tagged int's
- if(TaggedInt::Is(aFractionDigits))
- {
- fractionDigits = TaggedInt::ToInt32(aFractionDigits);
- }
- else if(JavascriptOperators::GetTypeId(aFractionDigits) == TypeIds_Undefined)
- {
- // fraction digits = 0
- }
- else
- {
- double fractionDigitsRaw = JavascriptConversion::ToInteger(aFractionDigits, scriptContext);
- isFractionDigitsInfinite =
- fractionDigitsRaw == JavascriptNumber::NEGATIVE_INFINITY ||
- fractionDigitsRaw == JavascriptNumber::POSITIVE_INFINITY;
- fractionDigits = (int)fractionDigitsRaw;
- }
- }
- if (fractionDigits < 0 || fractionDigits > 20)
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_FractionOutOfRange);
- }
- if(IsNan(value))
- {
- return ToStringNan(scriptContext);
- }
- if(value >= 1e21 || value <= -1e21)
- {
- return ToStringRadix10(value, scriptContext);
- }
- return FormatDoubleToString(value, NumberUtilities::FormatFixed, fractionDigits, scriptContext);
- }
- Var JavascriptNumber::EntryToPrecision(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedNumber, _u("Number.prototype.toPrecision"));
- }
- AssertMsg(args.Info.Count > 0, "negative arg count");
- // spec implies ToPrec is not generic. 'this' must be a number
- double value;
- if (!GetThisValue(args[0], &value))
- {
- if (JavascriptOperators::GetTypeId(args[0]) == TypeIds_HostDispatch)
- {
- Var result;
- if (VarTo<RecyclableObject>(args[0])->InvokeBuiltInOperationRemotely(EntryToPrecision, args, &result))
- {
- return result;
- }
- }
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedNumber, _u("Number.prototype.toPrecision"));
- }
- if(args.Info.Count < 2 || JavascriptOperators::GetTypeId(args[1]) == TypeIds_Undefined)
- {
- return JavascriptConversion::ToString(args[0], scriptContext);
- }
- int precision;
- Var aPrecision = args[1];
- if(TaggedInt::Is(aPrecision))
- {
- precision = TaggedInt::ToInt32(aPrecision);
- }
- else
- {
- precision = (int) JavascriptConversion::ToInt32(aPrecision, scriptContext);
- }
- JavascriptString * nanF;
- if (nullptr != (nanF = ToStringNanOrInfinite(value, scriptContext)))
- {
- return nanF;
- }
- if(precision < 1 || precision > 21)
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_PrecisionOutOfRange);
- }
- return FormatDoubleToString(value, NumberUtilities::FormatPrecision, precision, scriptContext);
- }
- Var JavascriptNumber::EntryToLocaleString(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedNumber, _u("Number.prototype.toLocaleString"));
- }
- return JavascriptNumber::ToLocaleStringIntl(args, callInfo, scriptContext);
- }
- JavascriptString* JavascriptNumber::ToLocaleStringIntl(Var* values, CallInfo callInfo, ScriptContext* scriptContext)
- {
- Assert(values);
- ArgumentReader args(&callInfo, values);
- return JavascriptNumber::ToLocaleStringIntl(args, callInfo, scriptContext);
- }
- JavascriptString* JavascriptNumber::ToLocaleStringIntl(ArgumentReader& args, CallInfo callInfo, ScriptContext* scriptContext)
- {
- Assert(scriptContext);
- #ifdef ENABLE_INTL_OBJECT
- if(CONFIG_FLAG(IntlBuiltIns) && scriptContext->IsIntlEnabled()){
- EngineInterfaceObject* nativeEngineInterfaceObj = scriptContext->GetLibrary()->GetEngineInterfaceObject();
- if (nativeEngineInterfaceObj)
- {
- IntlEngineInterfaceExtensionObject* intlExtensionObject = static_cast<IntlEngineInterfaceExtensionObject*>(nativeEngineInterfaceObj->GetEngineExtension(EngineInterfaceExtensionKind_Intl));
- JavascriptFunction* func = intlExtensionObject->GetNumberToLocaleString();
- if (func)
- {
- BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
- {
- return VarTo<JavascriptString>(func->CallFunction(args));
- }
- END_SAFE_REENTRANT_CALL
- }
- // Initialize Number.prototype.toLocaleString
- scriptContext->GetLibrary()->InitializeIntlForNumberPrototype();
- func = intlExtensionObject->GetNumberToLocaleString();
- if (func)
- {
- BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
- {
- return VarTo<JavascriptString>(func->CallFunction(args));
- }
- END_SAFE_REENTRANT_CALL
- }
- }
- }
- #endif
- double value;
- if (!GetThisValue(args[0], &value))
- {
- if (JavascriptOperators::GetTypeId(args[0]) == TypeIds_HostDispatch)
- {
- Var result;
- if (VarTo<RecyclableObject>(args[0])->InvokeBuiltInOperationRemotely(EntryToLocaleString, args, &result))
- {
- return VarTo<JavascriptString>(result);
- }
- }
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedNumber, _u("Number.prototype.toLocaleString"));
- }
- return JavascriptNumber::ToLocaleString(value, scriptContext);
- }
- Var JavascriptNumber::EntryToString(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedNumber, _u("Number.prototype.toString"));
- }
- // Optimize base 10 of TaggedInt numbers
- if (TaggedInt::Is(args[0]) && (args.Info.Count == 1 || (TaggedInt::Is(args[1]) && TaggedInt::ToInt32(args[1]) == 10)))
- {
- return scriptContext->GetIntegerString(args[0]);
- }
- double value;
- if (!GetThisValue(args[0], &value))
- {
- if (JavascriptOperators::GetTypeId(args[0]) == TypeIds_HostDispatch)
- {
- Var result;
- if (VarTo<RecyclableObject>(args[0])->InvokeBuiltInOperationRemotely(EntryToString, args, &result))
- {
- return result;
- }
- }
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedNumber, _u("Number.prototype.toString"));
- }
- int radix = 10;
- if(args.Info.Count > 1)
- {
- //use the first arg as the radix, ignore the rest.
- Var aRadix = args[1];
- // shortcut for tagged int's
- if(TaggedInt::Is(aRadix))
- {
- radix = TaggedInt::ToInt32(aRadix);
- }
- else if(JavascriptOperators::GetTypeId(aRadix) != TypeIds_Undefined)
- {
- radix = (int)JavascriptConversion::ToInteger(aRadix,scriptContext);
- }
- }
- if(10 == radix)
- {
- return ToStringRadix10(value, scriptContext);
- }
- if( radix < 2 || radix >36 )
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_FunctionArgument_Invalid, _u("Number.prototype.toString"));
- }
- return ToStringRadixHelper(value, radix, scriptContext);
- }
- Var JavascriptNumber::EntryValueOf(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Var value = args[0];
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedNumber, _u("Number.prototype.valueOf"));
- }
- //avoid creation of a new Number
- if (TaggedInt::Is(value) || JavascriptNumber::Is_NoTaggedIntCheck(value))
- {
- return value;
- }
- else if (VarIs<JavascriptNumberObject>(value))
- {
- JavascriptNumberObject* obj = VarTo<JavascriptNumberObject>(value);
- return CrossSite::MarshalVar(scriptContext, obj->Unwrap(), obj->GetScriptContext());
- }
- else if (Js::JavascriptOperators::GetTypeId(value) == TypeIds_Int64Number)
- {
- return value;
- }
- else if (Js::JavascriptOperators::GetTypeId(value) == TypeIds_UInt64Number)
- {
- return value;
- }
- else
- {
- if (JavascriptOperators::GetTypeId(value) == TypeIds_HostDispatch)
- {
- Var result;
- if (VarTo<RecyclableObject>(value)->InvokeBuiltInOperationRemotely(EntryValueOf, args, &result))
- {
- return result;
- }
- }
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedNumber, _u("Number.prototype.valueOf"));
- }
- }
- // The largest string representing a number is the base 2 representation of -Math.pow(2,-1073), at 1076 characters.
- static const int bufSize = 1280;
- JavascriptString* JavascriptNumber::ToString(double value, ScriptContext* scriptContext)
- {
- char16 szBuffer[bufSize];
- int cchWritten = swprintf_s(szBuffer, _countof(szBuffer), _u("%g"), value);
- return JavascriptString::NewCopyBuffer(szBuffer, cchWritten, scriptContext);
- }
- JavascriptString* JavascriptNumber::ToStringNanOrInfiniteOrZero(double value, ScriptContext* scriptContext)
- {
- JavascriptString* nanF;
- if (nullptr != (nanF = ToStringNanOrInfinite(value, scriptContext)))
- {
- return nanF;
- }
- if (IsZero(value))
- {
- return scriptContext->GetLibrary()->GetCharStringCache().GetStringForCharA('0');
- }
- return nullptr;
- }
- JavascriptString* JavascriptNumber::ToStringRadix10(double value, ScriptContext* scriptContext)
- {
- JavascriptString* string = ToStringNanOrInfiniteOrZero(value, scriptContext);
- if (string != nullptr)
- {
- return string;
- }
- string = scriptContext->GetLastNumberToStringRadix10(value);
- if (string == nullptr)
- {
- char16 szBuffer[bufSize];
- if(!Js::NumberUtilities::FNonZeroFiniteDblToStr(value, szBuffer, bufSize))
- {
- Js::JavascriptError::ThrowOutOfMemoryError(scriptContext);
- }
- string = JavascriptString::NewCopySz(szBuffer, scriptContext);
- scriptContext->SetLastNumberToStringRadix10(value, string);
- }
- return string;
- }
- JavascriptString* JavascriptNumber::ToStringRadixHelper(double value, int radix, ScriptContext* scriptContext)
- {
- Assert(radix != 10);
- Assert(radix >= 2 && radix <= 36);
- JavascriptString* string = ToStringNanOrInfiniteOrZero(value, scriptContext);
- if (string != nullptr)
- {
- return string;
- }
- char16 szBuffer[bufSize];
- if (!Js::NumberUtilities::FNonZeroFiniteDblToStr(value, radix, szBuffer, _countof(szBuffer)))
- {
- Js::JavascriptError::ThrowOutOfMemoryError(scriptContext);
- }
- return JavascriptString::NewCopySz(szBuffer, scriptContext);
- }
- BOOL JavascriptNumber::GetThisValue(Var aValue, double* pDouble)
- {
- TypeId typeId = JavascriptOperators::GetTypeId(aValue);
- if (typeId <= TypeIds_UndefinedOrNull)
- {
- return FALSE;
- }
- if (typeId == TypeIds_Integer)
- {
- *pDouble = TaggedInt::ToDouble(aValue);
- return TRUE;
- }
- else if (typeId == TypeIds_Int64Number)
- {
- *pDouble = (double)VarTo<JavascriptInt64Number>(aValue)->GetValue();
- return TRUE;
- }
- else if (typeId == TypeIds_UInt64Number)
- {
- *pDouble = (double)VarTo<JavascriptUInt64Number>(aValue)->GetValue();
- return TRUE;
- }
- else if (JavascriptNumber::Is_NoTaggedIntCheck(aValue))
- {
- *pDouble = JavascriptNumber::GetValue(aValue);
- return TRUE;
- }
- else if (typeId == TypeIds_NumberObject)
- {
- JavascriptNumberObject* obj = VarTo<JavascriptNumberObject>(aValue);
- *pDouble = obj->GetValue();
- return TRUE;
- }
- else
- {
- return FALSE;
- }
- }
- JavascriptString* JavascriptNumber::ToLocaleStringNanOrInfinite(double value, ScriptContext* scriptContext)
- {
- if (!NumberUtilities::IsFinite(value))
- {
- if (IsNan(value))
- {
- return ToStringNan(scriptContext);
- }
- BSTR bstr = nullptr;
- if (IsPosInf(value))
- {
- bstr = BstrGetResourceString(IDS_INFINITY);
- }
- else
- {
- AssertMsg(IsNegInf(value), "bad handling of infinite number");
- bstr = BstrGetResourceString(IDS_MINUSINFINITY);
- }
- if (bstr == nullptr)
- {
- Js::JavascriptError::ThrowTypeError(scriptContext, VBSERR_InternalError);
- }
- JavascriptString* str = JavascriptString::NewCopyBuffer(bstr, SysStringLen(bstr), scriptContext);
- SysFreeString(bstr);
- return str;
- }
- return nullptr;
- }
- JavascriptString* JavascriptNumber::ToLocaleString(double value, ScriptContext* scriptContext)
- {
- WCHAR szRes[bufSize];
- WCHAR * pszRes = NULL;
- WCHAR * pszToBeFreed = NULL;
- size_t count;
- if (!Js::NumberUtilities::IsFinite(value))
- {
- //
- // +- Infinity : use the localized string
- // NaN would be returned as NaN
- //
- return ToLocaleStringNanOrInfinite(value, scriptContext);
- }
- JavascriptString *result = nullptr;
- JavascriptString *dblStr = VarTo<JavascriptString>(FormatDoubleToString(value, NumberUtilities::FormatFixed, -1, scriptContext));
- const char16* szValue = dblStr->GetSz();
- const size_t szLength = dblStr->GetLength();
- pszRes = szRes;
- count = Numbers::Utility::NumberToDefaultLocaleString(szValue, szLength, pszRes, bufSize);
- if( count == 0 )
- {
- return dblStr;
- }
- else
- {
- if( count > bufSize )
- {
- pszRes = pszToBeFreed = HeapNewArray(char16, count);
- count = Numbers::Utility::NumberToDefaultLocaleString(szValue, szLength, pszRes, count);
- if ( count == 0 )
- {
- AssertMsg(false, "GetNumberFormatEx failed");
- JavascriptError::ThrowError(scriptContext, VBSERR_InternalError);
- }
- }
- if ( count != 0 )
- {
- result = JavascriptString::NewCopySz(pszRes, scriptContext);
- }
- }
- if ( pszToBeFreed )
- {
- HeapDeleteArray(count, pszToBeFreed);
- }
- return result;
- }
- Var JavascriptNumber::CloneToScriptContext(Var aValue, ScriptContext* requestContext)
- {
- return JavascriptNumber::New(JavascriptNumber::GetValue(aValue), requestContext);
- }
- #if !FLOATVAR
- Var JavascriptNumber::BoxStackNumber(Var instance, ScriptContext* scriptContext)
- {
- if (ThreadContext::IsOnStack(instance) && JavascriptNumber::Is(instance))
- {
- return BoxStackInstance(JavascriptNumber::FromVar(instance), scriptContext);
- }
- else
- {
- return instance;
- }
- }
- Var JavascriptNumber::BoxStackInstance(Var instance, ScriptContext* scriptContext)
- {
- Assert(ThreadContext::IsOnStack(instance));
- double value = JavascriptNumber::FromVar(instance)->GetValue();
- return JavascriptNumber::New(value, scriptContext);
- }
- JavascriptNumber * JavascriptNumber::NewUninitialized(Recycler * recycler)
- {
- return RecyclerNew(recycler, JavascriptNumber, VirtualTableInfoCtorValue);
- }
- #endif
- }
|