| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Copyright (c) ChakraCore Project Contributors. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "RuntimeLibraryPch.h"
- #include "Language/JavascriptMathOperators.h"
- #include "Math/CrtSSE2Math.h"
- #include <math.h>
- const LPCWSTR UCrtC99MathApis::LibraryName = _u("api-ms-win-crt-math-l1-1-0.dll");
- void UCrtC99MathApis::Ensure()
- {
- if (m_isInit)
- {
- return;
- }
- DelayLoadLibrary::EnsureFromSystemDirOnly();
- if (IsAvailable())
- {
- m_pfnlog2 = (PFNMathFn)GetFunction("log2");
- m_pfnlog1p = (PFNMathFn)GetFunction("log1p");
- m_pfnexpm1 = (PFNMathFn)GetFunction("expm1");
- m_pfnacosh = (PFNMathFn)GetFunction("acosh");
- m_pfnasinh = (PFNMathFn)GetFunction("asinh");
- m_pfnatanh = (PFNMathFn)GetFunction("atanh");
- m_pfntrunc = (PFNMathFn)GetFunction("trunc");
- m_pfncbrt = (PFNMathFn)GetFunction("cbrt");
- if (m_pfnlog2 == nullptr ||
- m_pfnlog1p == nullptr ||
- m_pfnexpm1 == nullptr ||
- m_pfnacosh == nullptr ||
- m_pfnasinh == nullptr ||
- m_pfnatanh == nullptr ||
- m_pfntrunc == nullptr ||
- m_pfncbrt == nullptr)
- {
- // If any of the APIs fail to load then presume the entire module is bogus and free it
- FreeLibrary(m_hModule);
- m_hModule = nullptr;
- }
- }
- }
- namespace Js
- {
- const double Math::PI = 3.1415926535897931;
- const double Math::E = 2.7182818284590451;
- const double Math::LN10 = 2.3025850929940459;
- const double Math::LN2 = 0.69314718055994529;
- const double Math::LOG2E = 1.4426950408889634;
- const double Math::LOG10E = 0.43429448190325182;
- const double Math::SQRT1_2 = 0.70710678118654757;
- const double Math::SQRT2 = 1.4142135623730951;
- const double Math::EPSILON = 2.2204460492503130808472633361816e-16;
- const double Math::MAX_SAFE_INTEGER = 9007199254740991.0;
- const double Math::MIN_SAFE_INTEGER = -9007199254740991.0;
- ///----------------------------------------------------------------------------
- /// Abs() returns the absolute value of the given number, as described in
- /// (ES5.0: S15.8.2.1).
- ///----------------------------------------------------------------------------
- Var Math::Abs(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 >= 2)
- {
- Var arg = args[1];
- if (TaggedInt::Is(arg))
- {
- #if defined(TARGET_64)
- __int64 result = ::_abs64(TaggedInt::ToInt32(arg));
- #else
- __int32 result = ::abs(TaggedInt::ToInt32(arg));
- #endif
- return JavascriptNumber::ToVar(result, scriptContext);
- }
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- double result = Math::Abs(x);
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- double Math::Abs(double x)
- {
- // ::fabs if linked from UCRT changes FPU ctrl word for NaN input
- if (NumberUtilities::IsNan(x))
- {
- // canonicalize to 0xFFF8000..., so we can tag correctly on x64.
- return NumberConstants::NaN;
- }
- return ::fabs(x);
- }
- ///----------------------------------------------------------------------------
- /// Acos() returns the arc-cosine of the given angle in radians, as described
- /// in (ES5.0: S15.8.2.2).
- ///----------------------------------------------------------------------------
- Var Math::Acos(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));
- if (args.Info.Count >= 2)
- {
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- double result = Math::Acos(x);
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- double Math::Acos(double x)
- {
- double result;
- #if defined(_M_IX86) && defined(_WIN32)
- // This is for perf, not for functionality
- // If non Win32 CRT implementation already support SSE2,
- // then we get most of the perf already.
- if (AutoSystemInfo::Data.SSE2Available())
- {
- _asm {
- movsd xmm0, x
- call dword ptr [__libm_sse2_acos]
- movsd result, xmm0
- }
- }
- else
- #endif
- {
- result = ::acos(x);
- }
- return result;
- }
- ///----------------------------------------------------------------------------
- /// Asin() returns the arc-sine of the given angle in radians, as described in
- /// (ES5.0: S15.8.2.3).
- ///----------------------------------------------------------------------------
- Var Math::Asin(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));
- if (args.Info.Count >= 2)
- {
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- double result = Math::Asin(x);
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- double Math::Asin(double x)
- {
- double result;
- #if defined(_M_IX86) && defined(_WIN32)
- // This is for perf, not for functionality
- // If non Win32 CRT implementation already support SSE2,
- // then we get most of the perf already.
- if (AutoSystemInfo::Data.SSE2Available())
- {
- _asm {
- movsd xmm0, x
- call dword ptr [__libm_sse2_asin]
- movsd result, xmm0
- }
- }
- else
- #endif
- {
- result = ::asin(x);
- }
- return result;
- }
- ///----------------------------------------------------------------------------
- /// Atan() returns the arc-tangent of the given angle in radians, as described
- /// in (ES5.0: S15.8.2.4).
- ///----------------------------------------------------------------------------
- Var Math::Atan(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));
- if (args.Info.Count >= 2)
- {
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- double result = Math::Atan(x);
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- double Math::Atan(double x)
- {
- double result;
- #if defined(_M_IX86) && defined(_WIN32)
- // This is for perf, not for functionality
- // If non Win32 CRT implementation already support SSE2,
- // then we get most of the perf already.
- if (AutoSystemInfo::Data.SSE2Available())
- {
- _asm {
- movsd xmm0, x
- call dword ptr [__libm_sse2_atan]
- movsd result, xmm0
- }
- }
- else
- #endif
- {
- result = ::atan(x);
- }
- return result;
- }
- ///----------------------------------------------------------------------------
- /// Atan2() returns the arc-tangent of the angle described by the given x and y
- /// lengths, as described in (ES5.0: S15.8.2.5).
- ///----------------------------------------------------------------------------
- Var Math::Atan2(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));
- if (args.Info.Count >= 3)
- {
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- double y = JavascriptConversion::ToNumber(args[2], scriptContext);
- double result = Math::Atan2(x,y);
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- double Math::Atan2( double x, double y )
- {
- double result;
- #if defined(_M_IX86) && defined(_WIN32)
- // This is for perf, not for functionality
- // If non Win32 CRT implementation already support SSE2,
- // then we get most of the perf already.
- if (AutoSystemInfo::Data.SSE2Available())
- {
- _asm
- {
- movsd xmm0, x
- movsd xmm1, y
- call dword ptr[__libm_sse2_atan2]
- movsd result, xmm0
- }
- }
- else
- #endif
- {
- result = ::atan2(x, y);
- }
- return result;
- }
- ///----------------------------------------------------------------------------
- /// Ceil() returns the smallest (closest to -Inf) number value that is not less
- /// than x and is equal to an integer, as described in (ES5.0: S15.8.2.6).
- ///----------------------------------------------------------------------------
- #pragma warning(push)
- #pragma warning(disable:4700) // uninitialized local variable 'output' used, for call to _mm_ceil_sd
- Var Math::Ceil(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));
- if (args.Info.Count >= 2)
- {
- Var inputArg = args[1];
- if (TaggedInt::Is(inputArg))
- {
- return inputArg;
- }
- double x = JavascriptConversion::ToNumber(inputArg, scriptContext);
- #if defined(_M_ARM32_OR_ARM64)
- if (Js::JavascriptNumber::IsNan(x))
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- #endif
- #if (defined(_M_IX86) || defined(_M_X64)) \
- && (__SSE4_1__ || _WIN32) // _mm_ceil_sd needs this
- if (AutoSystemInfo::Data.SSE4_1Available())
- {
- __m128d input, output;
- input = _mm_load_sd(&x);
- output = _mm_ceil_sd(input, input);
- int intResult = _mm_cvtsd_si32(output);
- if (TaggedInt::IsOverflow(intResult) || intResult == 0 || intResult == 0x80000000)
- {
- double dblResult;
- _mm_store_sd(&dblResult, output);
- if (intResult == 0 && !JavascriptNumber::IsNegZero(dblResult))
- {
- return JavascriptNumber::ToVar(0, scriptContext);
- }
- Assert(dblResult == ::ceil(x) || Js::JavascriptNumber::IsNan(dblResult));
- return JavascriptNumber::ToVarNoCheck(dblResult, scriptContext);
- }
- else
- {
- return JavascriptNumber::ToVar(intResult, scriptContext);
- }
- }
- else
- #endif
- {
- double result = ::ceil(x);
- intptr_t intResult = (intptr_t)result;
- if (TaggedInt::IsOverflow(intResult) || JavascriptNumber::IsNegZero(result))
- {
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- return JavascriptNumber::ToVar(intResult, scriptContext);
- }
- }
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- #pragma warning(pop)
- ///----------------------------------------------------------------------------
- /// Cos() returns the cosine of the given angle in radians, as described in
- /// (ES5.0: S15.8.2.7).
- ///----------------------------------------------------------------------------
- Var Math::Cos(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));
- if (args.Info.Count >= 2)
- {
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- double result = Math::Cos(x);
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- double Math::Cos(double x)
- {
- double result;
- #if defined(_M_IX86) && defined(_WIN32)
- // This is for perf, not for functionality
- // If non Win32 CRT implementation already support SSE2,
- // then we get most of the perf already.
- if (AutoSystemInfo::Data.SSE2Available())
- {
- _asm {
- movsd xmm0, x
- call dword ptr [__libm_sse2_cos]
- movsd result, xmm0
- }
- }
- else
- #endif
- {
- result = ::cos(x);
- }
- return result;
- }
- ///----------------------------------------------------------------------------
- /// Exp() returns e^x, as described in (ES5.0: S15.8.2.8).
- ///----------------------------------------------------------------------------
- Var Math::Exp(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));
- if (args.Info.Count >= 2)
- {
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- double result = Math::Exp(x);
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- double Math::Exp(double x)
- {
- double result;
- #if defined(_M_IX86) && defined(_WIN32)
- // This is for perf, not for functionality
- // If non Win32 CRT implementation already support SSE2,
- // then we get most of the perf already.
- if (AutoSystemInfo::Data.SSE2Available())
- {
- _asm {
- movsd xmm0, x
- call dword ptr [__libm_sse2_exp]
- movsd result, xmm0
- }
- }
- else
- #endif
- {
- result = ::exp(x);
- }
- return result;
- }
- ///----------------------------------------------------------------------------
- /// Floor() returns the greatest (closest to +Inf) number value that is not
- /// greater than x and is equal to an integer, as described in
- /// (ES5.0: S15.8.2.9).
- ///----------------------------------------------------------------------------
- Var Math::Floor(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));
- if (args.Info.Count >= 2)
- {
- Var input = args[1];
- if (TaggedInt::Is(input))
- {
- return input;
- }
- double x = JavascriptConversion::ToNumber(input, scriptContext);
- #if defined(_M_ARM32_OR_ARM64)
- if (Js::JavascriptNumber::IsNan(x))
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- #endif
- return Math::FloorDouble(x, scriptContext);
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- #pragma warning(push)
- #pragma warning(disable:4700) // // uninitialized local variable 'output' used, for call to _mm_floor_sd
- Var inline Math::FloorDouble(double d, ScriptContext *scriptContext)
- {
- // xplat-todo: use intrinsics here on linux
- #ifdef _MSC_VER
- #if defined(_M_IX86) || defined(_M_X64)
- if (AutoSystemInfo::Data.SSE4_1Available())
- {
- __m128d input, output;
- int intResult;
- input = _mm_load_sd(&d);
- if (d >= 0.0)
- {
- output = input;
- }
- else
- {
- output = _mm_floor_sd(input, input);
- }
- intResult = _mm_cvttsd_si32(output);
- if (TaggedInt::IsOverflow(intResult) || intResult == 0x80000000 || JavascriptNumber::IsNegZero(d))
- {
- double dblResult;
- if (d >= 0.0)
- {
- output = _mm_floor_sd(output, input);
- }
- _mm_store_sd(&dblResult, output);
- Assert(dblResult == ::floor(d) || Js::JavascriptNumber::IsNan(dblResult));
- return JavascriptNumber::ToVarNoCheck(dblResult, scriptContext);
- }
- else
- {
- Assert(intResult == (int)::floor(d));
- return JavascriptNumber::ToVar(intResult, scriptContext);
- }
- }
- else
- #endif
- #endif
- {
- intptr_t intResult;
- if (d >= 0.0)
- {
- intResult = (intptr_t)d;
- }
- else
- {
- d = ::floor(d);
- intResult = (intptr_t)d;
- }
- if (TaggedInt::IsOverflow(intResult) || JavascriptNumber::IsNegZero(d))
- {
- return JavascriptNumber::ToVarNoCheck(::floor(d), scriptContext);
- }
- else
- {
- return JavascriptNumber::ToVar(intResult, scriptContext);
- }
- }
- }
- #pragma warning(pop)
- ///----------------------------------------------------------------------------
- /// Log() returns the natural logarithm of x, as described in
- /// (ES5.0: S15.8.2.10).
- ///----------------------------------------------------------------------------
- Var Math::Log(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));
- if (args.Info.Count >= 2)
- {
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- double result = Math::Log(x);
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- double Math::Log(double x)
- {
- double result;
- #if defined(_M_IX86) && defined(_WIN32)
- // This is for perf, not for functionality
- // If non Win32 CRT implementation already support SSE2,
- // then we get most of the perf already.
- if (AutoSystemInfo::Data.SSE2Available())
- {
- _asm {
- movsd xmm0, x
- call dword ptr [__libm_sse2_log]
- movsd result, xmm0
- }
- }
- else
- #endif
- {
- result = ::log(x);
- }
- return result;
- }
- ///----------------------------------------------------------------------------
- /// Max() returns the maximum of a series of numbers, as described in
- /// (ES5.0: S15.8.2.11):
- /// - Arg:0 = "this"
- /// - Arg:1-n = Values to compare
- ///----------------------------------------------------------------------------
- Var Math::Max(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();
- bool hasOnlyIntegerArgs = false;
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count <= 1)
- {
- return scriptContext->GetLibrary()->GetNegativeInfinite();
- }
- else if (args.Info.Count == 2)
- {
- double result = JavascriptConversion::ToNumber(args[1], scriptContext);
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- hasOnlyIntegerArgs = TaggedInt::OnlyContainsTaggedInt(args);
- if (hasOnlyIntegerArgs && args.Info.Count == 3)
- {
- return TaggedInt::ToVarUnchecked(max(TaggedInt::ToInt32(args[1]), TaggedInt::ToInt32(args[2])));
- }
- }
- if (hasOnlyIntegerArgs)
- {
- int32 current = TaggedInt::ToInt32(args[1]);
- for (uint idxArg = 2; idxArg < args.Info.Count; idxArg++)
- {
- int32 compare = TaggedInt::ToInt32(args[idxArg]);
- if (current < compare)
- {
- current = compare;
- }
- }
- return TaggedInt::ToVarUnchecked(current);
- }
- else
- {
- double current = JavascriptConversion::ToNumber(args[1], scriptContext);
- bool returnNaN = false;
- if (JavascriptNumber::IsNan(current))
- {
- returnNaN = true;
- }
- for (uint idxArg = 2; idxArg < args.Info.Count; idxArg++)
- {
- double compare = JavascriptConversion::ToNumber(args[idxArg], scriptContext);
- if (JavascriptNumber::IsNan(compare) || returnNaN) // Call ToNumber for all args
- {
- returnNaN = true;
- }
- // In C++, -0.0f == 0.0f; however, in ES, -0.0f < 0.0f. Thus, use additional library
- // call to test this comparison.
- else if ((compare == 0 && JavascriptNumber::IsNegZero(current)) ||
- current < compare)
- {
- current = compare;
- }
- }
- if (returnNaN)
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- return JavascriptNumber::ToVarNoCheck(current, scriptContext);
- }
- }
- ///----------------------------------------------------------------------------
- /// Min() returns the minimum of a series of numbers, as described in
- /// (ES5.0: S15.8.2.12):
- /// - Arg:0 = "this"
- /// - Arg:1-n = Values to compare
- ///----------------------------------------------------------------------------
- Var Math::Min(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();
- bool hasOnlyIntegerArgs = false;
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count <= 1)
- {
- return scriptContext->GetLibrary()->GetPositiveInfinite();
- }
- else if (args.Info.Count == 2)
- {
- double result = JavascriptConversion::ToNumber(args[1], scriptContext);
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- hasOnlyIntegerArgs = TaggedInt::OnlyContainsTaggedInt(args);
- if (hasOnlyIntegerArgs && args.Info.Count == 3)
- {
- return TaggedInt::ToVarUnchecked(min(TaggedInt::ToInt32(args[1]), TaggedInt::ToInt32(args[2])));
- }
- }
- if (hasOnlyIntegerArgs)
- {
- int32 current = TaggedInt::ToInt32(args[1]);
- for (uint idxArg = 2; idxArg < args.Info.Count; idxArg++)
- {
- int32 compare = TaggedInt::ToInt32(args[idxArg]);
- if (current > compare)
- {
- current = compare;
- }
- }
- return TaggedInt::ToVarUnchecked(current);
- }
- else
- {
- double current = JavascriptConversion::ToNumber(args[1], scriptContext);
- bool returnNaN = false;
- if (JavascriptNumber::IsNan(current))
- {
- returnNaN = true;
- }
- for (uint idxArg = 2; idxArg < args.Info.Count; idxArg++)
- {
- double compare = JavascriptConversion::ToNumber(args[idxArg], scriptContext);
- if (JavascriptNumber::IsNan(compare) || returnNaN) // Call ToNumber for all args
- {
- returnNaN = true;
- }
- // In C++, -0.0f == 0.0f; however, in ES, -0.0f < 0.0f. Thus, use additional library
- // call to test this comparison.
- else if ((current == 0 && JavascriptNumber::IsNegZero(compare)) ||
- current > compare)
- {
- current = compare;
- }
- }
- if (returnNaN)
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- return JavascriptNumber::ToVarNoCheck(current, scriptContext);
- }
- }
- ///----------------------------------------------------------------------------
- /// Pow() returns x ^ y, as described in (ES5.0: S15.8.2.13).
- ///----------------------------------------------------------------------------
- Var Math::Pow(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));
- if (args.Info.Count >= 3)
- {
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- double y = JavascriptConversion::ToNumber(args[2], scriptContext);
- double result = Math::Pow( x, y );
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- double Math::Pow( double x, double y )
- {
- double result = 0;
- #if defined(_M_IX86) && defined(_WIN32) // TODO: xplat support
- // We can't just use "if (0 == y)" because NaN compares
- // equal to 0 according to our compilers.
- if( 0 == NumberUtilities::LuLoDbl( y ) && 0 == ( NumberUtilities::LuHiDbl( y ) & 0x7FFFFFFF ) )
- {
- // Result is 1 even if x is NaN.
- result = 1;
- }
- else if( 1.0 == Math::Abs( x ) && !NumberUtilities::IsFinite( y ) )
- {
- result = JavascriptNumber::NaN;
- }
- else
- {
- int32 intY;
- // range [-8, 8] is from JavascriptNumber::DirectPowDoubleInt
- if (JavascriptNumber::TryGetInt32Value(y, &intY) && intY >= -8 && intY <= 8)
- {
- result = JavascriptNumber::DirectPowDoubleInt(x, intY);
- }
- else if( AutoSystemInfo::Data.SSE2Available() )
- {
- _asm {
- movsd xmm0, x
- movsd xmm1, y
- call dword ptr[__libm_sse2_pow]
- movsd result, xmm0
- }
- }
- else
- {
- result = ::pow( x, y );
- }
- }
- #else
- result = JavascriptNumber::DirectPow( x, y );
- #endif
- return result;
- }
- ///----------------------------------------------------------------------------
- /// Random() returns a random number 0 <= n < 1.0, as described in
- /// (ES5.0: S15.8.2.14).
- ///----------------------------------------------------------------------------
- Var Math::Random(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- AssertMsg(callInfo.Count > 0, "Should always have implicit 'this'");
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- double res = JavascriptMath::Random(scriptContext);
- return JavascriptNumber::ToVarNoCheck(res, scriptContext);
- }
- ///----------------------------------------------------------------------------
- /// Round() returns the number value that is closest to x and is equal to an
- /// integer, as described in (ES5.0: S15.8.2.15).
- ///----------------------------------------------------------------------------
- Var Math::Round(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));
- if (args.Info.Count >= 2)
- {
- Var input = args[1];
- if (TaggedInt::Is(input))
- {
- return input;
- }
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- if(JavascriptNumber::IsNan(x))
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- // for doubles, if x >= 2^52 or <= -2^52, x must be an integer, and adding 0.5 will overflow the
- // integer to the next one. Therefore, we directly return x.
- if (x == 0.0 || !NumberUtilities::IsFinite(x) || x >= 4503599627370496.0 || x <= -4503599627370496.0)
- {
- // 0.0 catches the -0 case...
- return JavascriptNumber::ToVarNoCheck(x, scriptContext);
- }
- if (x > 0 && x < 0.5) {
- return JavascriptNumber::ToVarNoCheck((double)Js::JavascriptNumber::k_Zero, scriptContext);
- }
- if(x < 0 && x >= -0.5)
- {
- return scriptContext->GetLibrary()->GetNegativeZero();
- }
- return Math::FloorDouble(x+0.5, scriptContext);
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- ///----------------------------------------------------------------------------
- /// Sin() returns the sine of the given angle in radians, as described in
- /// (ES5.0: S15.8.2.16).
- ///----------------------------------------------------------------------------
- Var Math::Sin(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));
- if (args.Info.Count >= 2)
- {
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- double result = Math::Sin(x);
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- double Math::Sin(double x)
- {
- double result;
- #if defined(_M_IX86) && defined(_WIN32)
- // This is for perf, not for functionality
- // If non Win32 CRT implementation already support SSE2,
- // then we get most of the perf already.
- if (AutoSystemInfo::Data.SSE2Available())
- {
- _asm {
- movsd xmm0, x
- call dword ptr [__libm_sse2_sin]
- movsd result, xmm0
- }
- }
- else
- #endif
- {
- result = ::sin(x);
- }
- return result;
- }
- ///----------------------------------------------------------------------------
- /// Sqrt() returns the square-root of the given number, as described in
- /// (ES5.0: S15.8.2.17).
- ///----------------------------------------------------------------------------
- Var Math::Sqrt(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));
- if (args.Info.Count >= 2)
- {
- Var arg = args[1];
- double x = JavascriptConversion::ToNumber(arg, scriptContext);
- double result = ::sqrt(x);
- if (TaggedInt::Is(arg))
- {
- return JavascriptNumber::ToVarIntCheck(result, scriptContext);
- }
- else
- {
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- ///----------------------------------------------------------------------------
- /// Tan() returns the tangent of the given angle, in radians, as described in
- /// (ES5.0: S15.8.2.18).
- ///----------------------------------------------------------------------------
- Var Math::Tan(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));
- if (args.Info.Count >= 2)
- {
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- double result = Math::Tan( x );
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- double Math::Tan( double x )
- {
- double result = 0;
- #if defined(_M_IX86) && defined(_WIN32)
- // This is for perf, not for functionality
- // If non Win32 CRT implementation already support SSE2,
- // then we get most of the perf already.
- if( AutoSystemInfo::Data.SSE2Available() )
- {
- _asm {
- movsd xmm0, x
- call dword ptr[__libm_sse2_tan]
- movsd result, xmm0
- }
- }
- else
- #endif
- {
- result = ::tan( x );
- }
- return result;
- }
- ///----------------------------------------------------------------------------
- /// Log10() returns the base 10 logarithm of the given number, as described in
- /// (ES6.0: S20.2.2.21).
- ///----------------------------------------------------------------------------
- Var Math::Log10(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(Math_Constructor_log10);
- if (args.Info.Count >= 2)
- {
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- double result = Math::Log10(x);
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- double Math::Log10(double x)
- {
- double result;
- #if defined(_M_IX86) && defined(_WIN32)
- // This is for perf, not for functionality
- // If non Win32 CRT implementation already support SSE2,
- // then we get most of the perf already.
- if (AutoSystemInfo::Data.SSE2Available())
- {
- _asm {
- movsd xmm0, x
- call dword ptr [__libm_sse2_log10]
- movsd result, xmm0
- }
- }
- else
- #endif
- {
- result = ::log10(x);
- }
- return result;
- }
- ///----------------------------------------------------------------------------
- /// Log2() returns the base 2 logarithm of the given number, as described in
- /// (ES6.0: S20.2.2.22).
- ///----------------------------------------------------------------------------
- Var Math::Log2(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(Math_Constructor_log2);
- if (args.Info.Count >= 2)
- {
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- double result = Math::Log2(x, scriptContext);
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- double Math::Log2(double x, ScriptContext* scriptContext)
- {
- #if defined(WHEN_UCRT_IS_LINKED_IN_BUILD) || !defined(_WIN32)
- return ::log2( x );
- #else
- // TODO: THE FALLBACK IS NOT ACCURATE; Universal CRT is available on Threshold so we should never fallback but ideally we would link at build time to these APIs instead of loading them at runtime
- UCrtC99MathApis* ucrtC99MathApis = scriptContext->GetThreadContext()->GetUCrtC99MathApis();
- return ucrtC99MathApis->IsAvailable() ?
- ucrtC99MathApis->log2(x) :
- Math::Log( x ) / Math::LN2;
- #endif
- }
- ///----------------------------------------------------------------------------
- /// Log1p() returns the natural logarithm of one plus the given number, as
- /// described in (ES6.0: S20.2.2.20).
- ///----------------------------------------------------------------------------
- Var Math::Log1p(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(Math_Constructor_log1p);
- if (args.Info.Count >= 2)
- {
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- #if defined(WHEN_UCRT_IS_LINKED_IN_BUILD) || !defined(_WIN32)
- double result = ::log1p(x);
- #else
- // TODO: THE FALLBACK IS NOT ACCURATE; Universal CRT is available on Threshold so we should never fallback but ideally we would link at build time to these APIs instead of loading them at runtime
- UCrtC99MathApis* ucrtC99MathApis = scriptContext->GetThreadContext()->GetUCrtC99MathApis();
- double result = ucrtC99MathApis->IsAvailable() ?
- ucrtC99MathApis->log1p(x):
- (JavascriptNumber::IsNegZero(x)) ? x : Math::Log(1.0 + x);
- #endif
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- ///----------------------------------------------------------------------------
- /// Expm1() returns the result of subtracting one from the exponential
- /// function of the given number, as described in (ES6.0: S20.2.2.14).
- ///----------------------------------------------------------------------------
- Var Math::Expm1(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(Math_Constructor_expm1);
- if (args.Info.Count >= 2)
- {
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- #if defined(WHEN_UCRT_IS_LINKED_IN_BUILD) || !defined(_WIN32)
- double result = ::expm1(x);
- #else
- // TODO: THE FALLBACK IS NOT ACCURATE; Universal CRT is available on Threshold so we should never fallback but ideally we would link at build time to these APIs instead of loading them at runtime
- UCrtC99MathApis* ucrtC99MathApis = scriptContext->GetThreadContext()->GetUCrtC99MathApis();
- double result = ucrtC99MathApis->IsAvailable() ?
- ucrtC99MathApis->expm1(x) :
- (JavascriptNumber::IsNegZero(x)) ? x : Math::Exp(x) - 1.0;
- #endif
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- ///----------------------------------------------------------------------------
- /// Cosh() returns the hyperbolic cosine of the given number, as described in
- /// (ES6.0: S20.2.2.12).
- ///----------------------------------------------------------------------------
- Var Math::Cosh(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(Math_Constructor_cosh);
- if (args.Info.Count >= 2)
- {
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- double result = ::cosh(x);
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- ///----------------------------------------------------------------------------
- /// Sinh() returns the hyperbolic sine of the given number, as described in
- /// (ES6.0: S20.2.2.30).
- ///----------------------------------------------------------------------------
- Var Math::Sinh(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(Math_Constructor_sinh);
- if (args.Info.Count >= 2)
- {
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- double result = ::sinh(x);
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- ///----------------------------------------------------------------------------
- /// Tanh() returns the hyperbolic tangent of the given number, as described in
- /// (ES6.0: S20.2.2.33).
- ///----------------------------------------------------------------------------
- Var Math::Tanh(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(Math_Constructor_tanh);
- if (args.Info.Count >= 2)
- {
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- double result = ::tanh(x);
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- ///----------------------------------------------------------------------------
- /// Acosh() returns the inverse hyperbolic cosine of the given number, as
- /// described in (ES6.0: S20.2.2.3).
- ///----------------------------------------------------------------------------
- Var Math::Acosh(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(Math_Constructor_acosh);
- if (args.Info.Count >= 2)
- {
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- #if defined(WHEN_UCRT_IS_LINKED_IN_BUILD) || !defined(_WIN32)
- double result = ::acosh(x);
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- #else
- // TODO: THE FALLBACK IS NOT ACCURATE; Universal CRT is available on Threshold so we should never fallback but ideally we would link at build time to these APIs instead of loading them at runtime
- UCrtC99MathApis* ucrtC99MathApis = scriptContext->GetThreadContext()->GetUCrtC99MathApis();
- if (ucrtC99MathApis->IsAvailable())
- {
- return JavascriptNumber::ToVarNoCheck(ucrtC99MathApis->acosh(x), scriptContext);
- }
- else if (x >= 1.0)
- {
- // Can be smarter about large values of x, e.g. as x -> Infinity, sqrt(x^2 - 1) -> x
- // Therefore for large x, log(x+x) is sufficient, but how to decide what a large x is?
- // Also ln(x+x) = ln 2x = ln 2 + ln x
- double result = (x == 1.0) ? 0.0 : Math::Log(x + ::sqrt(x*x - 1.0));
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- #endif
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- ///----------------------------------------------------------------------------
- /// Asinh() returns the inverse hyperbolic sine of the given number, as
- /// described in (ES6.0: S20.2.2.5).
- ///----------------------------------------------------------------------------
- Var Math::Asinh(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(Math_Constructor_asinh);
- if (args.Info.Count >= 2)
- {
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- #if defined(WHEN_UCRT_IS_LINKED_IN_BUILD) || !defined(_WIN32)
- double result = ::asinh(x);
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- #else
- // TODO: THE FALLBACK IS NOT ACCURATE; Universal CRT is available on Threshold so we should never fallback but ideally we would link at build time to these APIs instead of loading them at runtime
- UCrtC99MathApis* ucrtC99MathApis = scriptContext->GetThreadContext()->GetUCrtC99MathApis();
- if (ucrtC99MathApis->IsAvailable())
- {
- return JavascriptNumber::ToVarNoCheck(ucrtC99MathApis->asinh(x), scriptContext);
- }
- else
- {
- double result = JavascriptNumber::IsNegZero(x) ? x :
- JavascriptNumber::IsPosInf(x) ? JavascriptNumber::POSITIVE_INFINITY :
- JavascriptNumber::IsNegInf(x) ? JavascriptNumber::NEGATIVE_INFINITY :
- Math::Log(x + ::sqrt(x*x + 1.0));
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- #endif
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- ///----------------------------------------------------------------------------
- /// Atanh() returns the inverse hyperbolic tangent of the given number, as
- /// described in (ES6.0: S20.2.2.7).
- ///----------------------------------------------------------------------------
- Var Math::Atanh(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(Math_Constructor_atanh);
- if (args.Info.Count >= 2)
- {
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- #if defined(WHEN_UCRT_IS_LINKED_IN_BUILD) || !defined(_WIN32)
- double result = ::atanh(x);
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- #else
- // TODO: THE FALLBACK IS NOT ACCURATE; Universal CRT is available on Threshold so we should never fallback but ideally we would link at build time to these APIs instead of loading them at runtime
- UCrtC99MathApis* ucrtC99MathApis = scriptContext->GetThreadContext()->GetUCrtC99MathApis();
- if (ucrtC99MathApis->IsAvailable())
- {
- return JavascriptNumber::ToVarNoCheck(ucrtC99MathApis->atanh(x), scriptContext);
- }
- else if (Math::Abs(x) < 1.0)
- {
- double result = (JavascriptNumber::IsNegZero(x)) ? x : Math::Log((1.0 + x) / (1.0 - x)) / 2.0;
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- if (x == -1.0)
- {
- return scriptContext->GetLibrary()->GetNegativeInfinite();
- }
- else if (x == 1.0)
- {
- return scriptContext->GetLibrary()->GetPositiveInfinite();
- }
- return scriptContext->GetLibrary()->GetNaN();
- }
- #endif
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- ///----------------------------------------------------------------------------
- /// Hypot() returns the square root of the sum of squares of the two or three
- /// given numbers, as described in (ES6.0: S20.2.2.17).
- ///----------------------------------------------------------------------------
- Var Math::Hypot(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(Math_Constructor_hypot);
- // ES6 20.2.2.18 Math.hypot(value1, value2, ...values)
- // If no arguments are passed, the result is +0.
- // If any argument is +Infinity, the result is +Infinity.
- // If any argument is -Infinity, the result is +Infinity.
- // If no argument is +Infinity or -Infinity, and any argument is NaN, the result is NaN.
- // If all arguments are either +0 or -0, the result is +0.
- double result = JavascriptNumber::k_Zero; // If there are no arguments return value is positive zero.
- if (args.Info.Count == 2)
- {
- // Special case for one argument
- double x1 = JavascriptConversion::ToNumber(args[1], scriptContext);
- if (JavascriptNumber::IsPosInf(x1) || JavascriptNumber::IsNegInf(x1))
- {
- result = JavascriptNumber::POSITIVE_INFINITY;
- }
- else
- {
- result = Math::Abs(x1);
- }
- }
- else if (args.Info.Count == 3)
- {
- // CRT hypot call
- double x1 = JavascriptConversion::ToNumber(args[1], scriptContext);
- double x2 = JavascriptConversion::ToNumber(args[2], scriptContext);
- if (JavascriptNumber::IsPosInf(x1) || JavascriptNumber::IsNegInf(x1) ||
- JavascriptNumber::IsPosInf(x2) || JavascriptNumber::IsNegInf(x2))
- {
- result = JavascriptNumber::POSITIVE_INFINITY;
- }
- else if (JavascriptNumber::IsNan(x1) || JavascriptNumber::IsNan(x2))
- {
- result = JavascriptNumber::NaN;
- }
- else
- {
- result = ::hypot(x1, x2);
- }
- }
- else if (args.Info.Count > 3)
- {
- // Uncommon case of more than 2 arguments for hypot
- result = Math::HypotHelper(args, scriptContext);
- }
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- double Math::HypotHelper(Arguments args, ScriptContext *scriptContext)
- {
- // CRT does not have a multiple version of hypot, so we implement it here ourselves.
- bool foundNaN = false;
- double scale = 0;
- double sum = 0;
- //Ignore first argument which is this pointer
- for (uint counter = 1; counter < args.Info.Count; counter++)
- {
- double doubleVal = JavascriptConversion::ToNumber(args[counter], scriptContext);
- if (JavascriptNumber::IsPosInf(doubleVal) || JavascriptNumber::IsNegInf(doubleVal))
- {
- return JavascriptNumber::POSITIVE_INFINITY;
- }
- if (!foundNaN)
- {
- if (JavascriptNumber::IsNan(doubleVal))
- {
- //Even though we found NaN, we still need to validate none of the other arguments are +Infinity or -Infinity
- foundNaN = true;
- }
- else
- {
- doubleVal = Math::Abs(doubleVal);
- if (scale < doubleVal)
- {
- sum = sum * (scale / doubleVal) * (scale / doubleVal) + 1; /* scale/scale === 1*/
- //change the scale to new max value
- scale = doubleVal;
- }
- else if (scale != 0)
- {
- sum += (doubleVal / scale) * (doubleVal / scale);
- }
- }
- }
- }
- if (foundNaN)
- {
- return JavascriptNumber::NaN;
- }
- return scale * ::sqrt(sum);
- }
- ///----------------------------------------------------------------------------
- /// Trunc() returns the integral part of the given number, removing any
- /// fractional digits, as described in (ES6.0: S20.2.2.34).
- ///----------------------------------------------------------------------------
- Var Math::Trunc(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(Math_Constructor_trunc);
- if (args.Info.Count >= 2)
- {
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- #if defined(WHEN_UCRT_IS_LINKED_IN_BUILD) || !defined(_WIN32)
- double result = ::trunc(x);
- #else
- // TODO: THE FALLBACK IS NOT ACCURATE; Universal CRT is available on Threshold so we should never fallback but ideally we would link at build time to these APIs instead of loading them at runtime
- UCrtC99MathApis* ucrtC99MathApis = scriptContext->GetThreadContext()->GetUCrtC99MathApis();
- double result = ucrtC99MathApis->IsAvailable() ?
- ucrtC99MathApis->trunc(x) :
- (x < 0.0) ? ::ceil(x) : ::floor(x);
- #endif
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- ///----------------------------------------------------------------------------
- /// Sign() returns the sign of the given number, indicating whether it is
- /// positive, negative, or zero, as described in (ES6.0: S20.2.2.28).
- ///----------------------------------------------------------------------------
- Var Math::Sign(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(Math_Constructor_sign);
- if (args.Info.Count >= 2)
- {
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- if (JavascriptNumber::IsNan(x))
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- else if (JavascriptNumber::IsNegZero(x))
- {
- return scriptContext->GetLibrary()->GetNegativeZero();
- }
- else
- {
- return TaggedInt::ToVarUnchecked(x == 0.0 ? 0 : x < 0.0 ? -1 : 1);
- }
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- ///----------------------------------------------------------------------------
- /// Cbrt() returns the cube root of the given number, as described in
- /// (ES6.0: S20.2.2.9).
- ///----------------------------------------------------------------------------
- Var Math::Cbrt(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(Math_Constructor_cbrt);
- if (args.Info.Count >= 2)
- {
- double x = JavascriptConversion::ToNumber(args[1], scriptContext);
- #if defined(WHEN_UCRT_IS_LINKED_IN_BUILD) || !defined(_WIN32)
- double result = ::cbrt(x);
- #else
- // TODO: THE FALLBACK IS NOT ACCURATE; Universal CRT is available on Threshold so we should never fallback but ideally we would link at build time to these APIs instead of loading them at runtime
- UCrtC99MathApis* ucrtC99MathApis = scriptContext->GetThreadContext()->GetUCrtC99MathApis();
- if (ucrtC99MathApis->IsAvailable())
- {
- return JavascriptNumber::ToVarNoCheck(ucrtC99MathApis->cbrt(x), scriptContext);
- }
- bool isNeg = x < 0.0;
- if (isNeg)
- {
- x = -x;
- }
- double result = (x == 0.0) ? x : Math::Exp(Math::Log(x) / 3.0);
- if (isNeg)
- {
- result = -result;
- }
- #endif
- return JavascriptNumber::ToVarNoCheck(result, scriptContext);
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- ///----------------------------------------------------------------------------
- /// Imul() returns the 32-bit integer multiplication of two given numbers, as
- /// described in (ES6.0: S20.2.2.18).
- ///----------------------------------------------------------------------------
- Var Math::Imul(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(Math_Constructor_imul);
- if (args.Info.Count >= 3)
- {
- int32 x = JavascriptConversion::ToInt32(args[1], scriptContext);
- int32 y = JavascriptConversion::ToInt32(args[2], scriptContext);
- int64 int64Result = (int64)x * (int64)y;
- int32 result = (int32)int64Result;
- return JavascriptNumber::ToVar(result, scriptContext);
- }
- else
- {
- // The arguments, if left unspecified default to undefined, and ToUint32(undefined) produces +0.
- // Therefore we return +0, not NaN here like the other Math functions.
- return TaggedInt::ToVarUnchecked(0);
- }
- }
- ///----------------------------------------------------------------------------
- /// Clz32() returns the leading number of zero bits of ToUint32(x), as
- /// described in (ES6.0: S20.2.2.11).
- ///----------------------------------------------------------------------------
- Var Math::Clz32(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Var value = args.Info.Count > 1 ? args[1] : scriptContext->GetLibrary()->GetUndefined();
- Assert(!(callInfo.Flags & CallFlags_New));
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Math_Constructor_clz32);
- uint32 uint32value = JavascriptConversion::ToUInt32(value, scriptContext);
- DWORD index;
- if (!_BitScanReverse(&index, uint32value))
- {
- return TaggedInt::ToVarUnchecked(32);
- }
- return TaggedInt::ToVarUnchecked(31 - index);
- }
- Var Math::Fround(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(Math_Constructor_fround);
- if (args.Info.Count >= 2)
- {
- float x = (float) JavascriptConversion::ToNumber(args[1], scriptContext);
- return JavascriptNumber::ToVarNoCheck((double) x, scriptContext);
- }
- else
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- }
- } // namespace Js
|