| //------------------------------------------------------------------------------------------------------- |
| // 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 "Language/JavascriptMathOperators.h" |
| #include "Math/CrtSSE2Math.h" |
| |
| #if defined(_M_IX86) || defined(_M_X64) |
| #pragma intrinsic(_mm_round_sd) |
| #endif |
| |
| 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(_M_X64_OR_ARM64) |
| __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) |
| 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) |
| 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) |
| 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; |
| #ifdef _M_IX86 |
| 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 input = args[1]; |
| |
| if (TaggedInt::Is(input)) |
| { |
| return input; |
| } |
| |
| double x = JavascriptConversion::ToNumber(args[1], scriptContext); |
| #if defined(_M_ARM32_OR_ARM64) |
| if (Js::JavascriptNumber::IsNan(x)) |
| { |
| return scriptContext->GetLibrary()->GetNaN(); |
| } |
| #endif |
| |
| #if defined(_M_IX86) || defined(_M_X64) |
| if (AutoSystemInfo::Data.SSE4_1Available()) |
| { |
| __m128d input, output; |
| input = _mm_load_sd(&x); |
| #pragma prefast(suppress:6001, "Signature of _mm_ceil_sd intrinsic confuses prefast, output in the parameter list is not used, it is the dst of the intrinsic") |
| output = _mm_ceil_sd(output, 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) |
| 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) |
| 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) |
| { |
| #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 |
| { |
| #pragma prefast(suppress:6001, "Signature of _mm_floor_sd intrinsic confuses prefast, output in the parameter list is not used, it is the dst of the intrinsic") |
| output = _mm_floor_sd(output, 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 |
| { |
| 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) |
| 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(); |
| |
| 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 if (args.Info.Count == 3) |
| { |
| if (TaggedInt::Is(args[1]) && TaggedInt::Is(args[2])) |
| { |
| return TaggedInt::ToVarUnchecked(max(TaggedInt::ToInt32(args[1]), TaggedInt::ToInt32(args[2]))); |
| } |
| } |
| |
| double current = JavascriptConversion::ToNumber(args[1], scriptContext); |
| if(JavascriptNumber::IsNan(current)) |
| { |
| return scriptContext->GetLibrary()->GetNaN(); |
| } |
| |
| for (uint idxArg = 2; idxArg < args.Info.Count; idxArg++) |
| { |
| double compare = JavascriptConversion::ToNumber(args[idxArg], scriptContext); |
| if(JavascriptNumber::IsNan(compare)) |
| { |
| return scriptContext->GetLibrary()->GetNaN(); |
| } |
| if((JavascriptNumber::IsNegZero(current) && compare == 0) || |
| current < compare ) |
| { |
| current = compare; |
| } |
| } |
| |
| 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(); |
| |
| 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 if (args.Info.Count == 3) |
| { |
| if (TaggedInt::Is(args[1]) && TaggedInt::Is(args[2])) |
| { |
| return TaggedInt::ToVarUnchecked(min(TaggedInt::ToInt32(args[1]), TaggedInt::ToInt32(args[2]))); |
| } |
| } |
| |
| double current = JavascriptConversion::ToNumber(args[1], scriptContext); |
| if(JavascriptNumber::IsNan(current)) |
| { |
| return scriptContext->GetLibrary()->GetNaN(); |
| } |
| |
| for (uint idxArg = 2; idxArg < args.Info.Count; idxArg++) |
| { |
| double compare = JavascriptConversion::ToNumber(args[idxArg], scriptContext); |
| if(JavascriptNumber::IsNan(compare)) |
| { |
| return scriptContext->GetLibrary()->GetNaN(); |
| } |
| if((JavascriptNumber::IsNegZero(compare) && current == 0) || |
| current > compare ) |
| { |
| current = compare; |
| } |
| } |
| |
| 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) |
| |
| // 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 |
| { |
| 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) |
| 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) |
| 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(Log10Count); |
| |
| 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) |
| 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(Log2Count); |
| |
| 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) |
| { |
| #ifdef WHEN_UCRT_IS_LINKED_IN_BUILD |
| 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(Log1pCount); |
| |
| if (args.Info.Count >= 2) |
| { |
| double x = JavascriptConversion::ToNumber(args[1], scriptContext); |
| |
| #ifdef WHEN_UCRT_IS_LINKED_IN_BUILD |
| 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(Expm1Count); |
| |
| if (args.Info.Count >= 2) |
| { |
| double x = JavascriptConversion::ToNumber(args[1], scriptContext); |
| |
| #ifdef WHEN_UCRT_IS_LINKED_IN_BUILD |
| 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(CoshCount); |
| |
| 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(SinhCount); |
| |
| 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(TanhCount); |
| |
| 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(AcoshCount); |
| |
| if (args.Info.Count >= 2) |
| { |
| double x = JavascriptConversion::ToNumber(args[1], scriptContext); |
| |
| #ifdef WHEN_UCRT_IS_LINKED_IN_BUILD |
| 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(AsinhCount); |
| |
| if (args.Info.Count >= 2) |
| { |
| double x = JavascriptConversion::ToNumber(args[1], scriptContext); |
| |
| #ifdef WHEN_UCRT_IS_LINKED_IN_BUILD |
| 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(AtanhCount); |
| |
| if (args.Info.Count >= 2) |
| { |
| double x = JavascriptConversion::ToNumber(args[1], scriptContext); |
| |
| #ifdef WHEN_UCRT_IS_LINKED_IN_BUILD |
| 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(HypotCount); |
| |
| // ES6 20.2.2.18 Math.hypot(value1, value2, ...values) |
| // If no arguments are passed, the result is +0. |
| // If any argument is +∞, the result is +∞. |
| // If any argument is -∞, the result is +∞. |
| // If no argument is +∞ or -∞, 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 +∞ or -∞ |
| 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(TruncCount); |
| |
| if (args.Info.Count >= 2) |
| { |
| double x = JavascriptConversion::ToNumber(args[1], scriptContext); |
| |
| #ifdef WHEN_UCRT_IS_LINKED_IN_BUILD |
| 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(SignCount); |
| |
| 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(CbrtCount); |
| |
| if (args.Info.Count >= 2) |
| { |
| double x = JavascriptConversion::ToNumber(args[1], scriptContext); |
| |
| #ifdef WHEN_UCRT_IS_LINKED_IN_BUILD |
| 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(ImulCount); |
| |
| 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(Clz32Count); |
| |
| 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(FroundCount); |
| |
| 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 |