14514f5e3Sopenharmony_ci/* 24514f5e3Sopenharmony_ci * Copyright (c) 2021-2024 Huawei Device Co., Ltd. 34514f5e3Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 44514f5e3Sopenharmony_ci * you may not use this file except in compliance with the License. 54514f5e3Sopenharmony_ci * You may obtain a copy of the License at 64514f5e3Sopenharmony_ci * 74514f5e3Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 84514f5e3Sopenharmony_ci * 94514f5e3Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software 104514f5e3Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 114514f5e3Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 124514f5e3Sopenharmony_ci * See the License for the specific language governing permissions and 134514f5e3Sopenharmony_ci * limitations under the License. 144514f5e3Sopenharmony_ci */ 154514f5e3Sopenharmony_ci 164514f5e3Sopenharmony_ci#include "ecmascript/builtins/builtins_number.h" 174514f5e3Sopenharmony_ci 184514f5e3Sopenharmony_ci#include "ecmascript/js_function.h" 194514f5e3Sopenharmony_ci#include "ecmascript/js_primitive_ref.h" 204514f5e3Sopenharmony_ci#ifdef ARK_SUPPORT_INTL 214514f5e3Sopenharmony_ci#include "ecmascript/js_number_format.h" 224514f5e3Sopenharmony_ci#else 234514f5e3Sopenharmony_ci#ifndef ARK_NOT_SUPPORT_INTL_GLOBAL 244514f5e3Sopenharmony_ci#include "ecmascript/intl/global_intl_helper.h" 254514f5e3Sopenharmony_ci#endif 264514f5e3Sopenharmony_ci#endif 274514f5e3Sopenharmony_ci 284514f5e3Sopenharmony_cinamespace panda::ecmascript::builtins { 294514f5e3Sopenharmony_ciusing NumberHelper = base::NumberHelper; 304514f5e3Sopenharmony_ci 314514f5e3Sopenharmony_ciJSTaggedValue BuiltinsNumber::NumberConstructor(EcmaRuntimeCallInfo *argv) 324514f5e3Sopenharmony_ci{ 334514f5e3Sopenharmony_ci ASSERT(argv); 344514f5e3Sopenharmony_ci BUILTINS_API_TRACE(argv->GetThread(), Number, Constructor); 354514f5e3Sopenharmony_ci JSThread *thread = argv->GetThread(); 364514f5e3Sopenharmony_ci [[maybe_unused]] EcmaHandleScope handleScope(thread); 374514f5e3Sopenharmony_ci JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv); 384514f5e3Sopenharmony_ci 394514f5e3Sopenharmony_ci // 1. If value is present, then a , b , c. 404514f5e3Sopenharmony_ci // 2. Else Let n be +0. 414514f5e3Sopenharmony_ci JSTaggedNumber numberValue(0); 424514f5e3Sopenharmony_ci if (argv->GetArgsNumber() > 0) { 434514f5e3Sopenharmony_ci JSHandle<JSTaggedValue> value = GetCallArg(argv, 0); 444514f5e3Sopenharmony_ci // a. Let prim be ? ToNumeric(value). 454514f5e3Sopenharmony_ci if (!value->IsNumber()) { 464514f5e3Sopenharmony_ci JSHandle<JSTaggedValue> numericVal = JSTaggedValue::ToNumeric(thread, value); 474514f5e3Sopenharmony_ci RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 484514f5e3Sopenharmony_ci // b. If Type(prim) is BigInt, let n be (ℝ(prim)). 494514f5e3Sopenharmony_ci if (numericVal->IsBigInt()) { 504514f5e3Sopenharmony_ci JSHandle<BigInt> bigNumericVal(numericVal); 514514f5e3Sopenharmony_ci numberValue = BigInt::BigIntToNumber(bigNumericVal); 524514f5e3Sopenharmony_ci } else { 534514f5e3Sopenharmony_ci // c. Otherwise, let n be prim. 544514f5e3Sopenharmony_ci numberValue = JSTaggedNumber(numericVal.GetTaggedValue()); 554514f5e3Sopenharmony_ci } 564514f5e3Sopenharmony_ci } else { 574514f5e3Sopenharmony_ci numberValue = JSTaggedNumber(value.GetTaggedValue()); 584514f5e3Sopenharmony_ci } 594514f5e3Sopenharmony_ci } 604514f5e3Sopenharmony_ci // 3. If NewTarget is undefined, return n. 614514f5e3Sopenharmony_ci if (newTarget->IsUndefined()) { 624514f5e3Sopenharmony_ci return numberValue; 634514f5e3Sopenharmony_ci } 644514f5e3Sopenharmony_ci // 4. Let O be OrdinaryCreateFromConstructor(NewTarget, "%NumberPrototype%", «[[NumberData]]» ). 654514f5e3Sopenharmony_ci JSHandle<JSTaggedValue> constructor = GetConstructor(argv); 664514f5e3Sopenharmony_ci JSHandle<JSObject> result = 674514f5e3Sopenharmony_ci thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle<JSFunction>::Cast(constructor), newTarget); 684514f5e3Sopenharmony_ci RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 694514f5e3Sopenharmony_ci // 5. Set O.[[NumberData]] to n. 704514f5e3Sopenharmony_ci JSPrimitiveRef::Cast(*result)->SetValue(thread, numberValue); 714514f5e3Sopenharmony_ci // 6. Return O. 724514f5e3Sopenharmony_ci return result.GetTaggedValue(); 734514f5e3Sopenharmony_ci} 744514f5e3Sopenharmony_ci 754514f5e3Sopenharmony_ci// 20.1.2.2 764514f5e3Sopenharmony_ciJSTaggedValue BuiltinsNumber::IsFinite(EcmaRuntimeCallInfo *argv) 774514f5e3Sopenharmony_ci{ 784514f5e3Sopenharmony_ci ASSERT(argv); 794514f5e3Sopenharmony_ci BUILTINS_API_TRACE(argv->GetThread(), Number, IsFinite); 804514f5e3Sopenharmony_ci JSTaggedValue msg = GetCallArg(argv, 0).GetTaggedValue(); 814514f5e3Sopenharmony_ci // 1. If Type(number) is not Number, return false 824514f5e3Sopenharmony_ci // 2. If number is NaN, +infinite, or -infinite, return false 834514f5e3Sopenharmony_ci if (NumberHelper::IsFinite(msg)) { 844514f5e3Sopenharmony_ci return GetTaggedBoolean(true); 854514f5e3Sopenharmony_ci } 864514f5e3Sopenharmony_ci return GetTaggedBoolean(false); 874514f5e3Sopenharmony_ci} 884514f5e3Sopenharmony_ci 894514f5e3Sopenharmony_ci// 20.1.2.3 904514f5e3Sopenharmony_ciJSTaggedValue BuiltinsNumber::IsInteger(EcmaRuntimeCallInfo *argv) 914514f5e3Sopenharmony_ci{ 924514f5e3Sopenharmony_ci ASSERT(argv); 934514f5e3Sopenharmony_ci BUILTINS_API_TRACE(argv->GetThread(), Number, IsInteger); 944514f5e3Sopenharmony_ci JSThread *thread = argv->GetThread(); 954514f5e3Sopenharmony_ci JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0); 964514f5e3Sopenharmony_ci bool result = false; 974514f5e3Sopenharmony_ci // 1. If Type(number) is not Number, return false. 984514f5e3Sopenharmony_ci // 2. If number is NaN, +infinite, or -infinite, return false 994514f5e3Sopenharmony_ci if (NumberHelper::IsFinite(msg.GetTaggedValue())) { 1004514f5e3Sopenharmony_ci [[maybe_unused]] EcmaHandleScope handleScope(thread); 1014514f5e3Sopenharmony_ci double value = JSTaggedNumber(msg.GetTaggedValue()).GetNumber(); 1024514f5e3Sopenharmony_ci // 3. Let integer be ToInteger(number). 1034514f5e3Sopenharmony_ci JSTaggedNumber number = JSTaggedValue::ToInteger(thread, msg); 1044514f5e3Sopenharmony_ci RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 1054514f5e3Sopenharmony_ci // 4. If integer is not equal to number, return false. 1064514f5e3Sopenharmony_ci // 5. Otherwise, return true. 1074514f5e3Sopenharmony_ci result = (value == number.GetNumber()); 1084514f5e3Sopenharmony_ci } 1094514f5e3Sopenharmony_ci return GetTaggedBoolean(result); 1104514f5e3Sopenharmony_ci} 1114514f5e3Sopenharmony_ci 1124514f5e3Sopenharmony_ci// 20.1.2.4 1134514f5e3Sopenharmony_ciJSTaggedValue BuiltinsNumber::IsNaN(EcmaRuntimeCallInfo *argv) 1144514f5e3Sopenharmony_ci{ 1154514f5e3Sopenharmony_ci ASSERT(argv); 1164514f5e3Sopenharmony_ci BUILTINS_API_TRACE(argv->GetThread(), Number, IsNaN); 1174514f5e3Sopenharmony_ci JSTaggedValue msg = GetCallArg(argv, 0).GetTaggedValue(); 1184514f5e3Sopenharmony_ci // 1. If Type(number) is not Number, return false. 1194514f5e3Sopenharmony_ci // 2. If number is NaN, return true. 1204514f5e3Sopenharmony_ci if (NumberHelper::IsNaN(msg)) { 1214514f5e3Sopenharmony_ci return GetTaggedBoolean(true); 1224514f5e3Sopenharmony_ci } 1234514f5e3Sopenharmony_ci // 3. Otherwise, return false. 1244514f5e3Sopenharmony_ci return GetTaggedBoolean(false); 1254514f5e3Sopenharmony_ci} 1264514f5e3Sopenharmony_ci 1274514f5e3Sopenharmony_ci// 20.1.2.5 1284514f5e3Sopenharmony_ciJSTaggedValue BuiltinsNumber::IsSafeInteger(EcmaRuntimeCallInfo *argv) 1294514f5e3Sopenharmony_ci{ 1304514f5e3Sopenharmony_ci ASSERT(argv); 1314514f5e3Sopenharmony_ci BUILTINS_API_TRACE(argv->GetThread(), Number, IsSafeInteger); 1324514f5e3Sopenharmony_ci JSThread *thread = argv->GetThread(); 1334514f5e3Sopenharmony_ci JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0); 1344514f5e3Sopenharmony_ci bool result = false; 1354514f5e3Sopenharmony_ci // 1. If Type(number) is not Number, return false. 1364514f5e3Sopenharmony_ci // 2. If number is NaN, +infinite, or -infinite, return false 1374514f5e3Sopenharmony_ci if (NumberHelper::IsFinite(msg.GetTaggedValue())) { 1384514f5e3Sopenharmony_ci [[maybe_unused]] EcmaHandleScope handleScope(thread); 1394514f5e3Sopenharmony_ci double value = JSTaggedNumber(msg.GetTaggedValue()).GetNumber(); 1404514f5e3Sopenharmony_ci // 3. Let integer be ToInteger(number). 1414514f5e3Sopenharmony_ci JSTaggedNumber number = JSTaggedValue::ToInteger(thread, msg); 1424514f5e3Sopenharmony_ci RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 1434514f5e3Sopenharmony_ci // 4. If integer is not equal to number, return false. 1444514f5e3Sopenharmony_ci // 5. If abs(integer) ≤ 253−1, return true. 1454514f5e3Sopenharmony_ci result = (value == number.GetNumber()) && std::abs(value) <= base::MAX_SAFE_INTEGER; 1464514f5e3Sopenharmony_ci } 1474514f5e3Sopenharmony_ci return GetTaggedBoolean(result); 1484514f5e3Sopenharmony_ci} 1494514f5e3Sopenharmony_ci 1504514f5e3Sopenharmony_ci// 18.2.4 1514514f5e3Sopenharmony_ci// 20.1.2.12 1524514f5e3Sopenharmony_ciJSTaggedValue BuiltinsNumber::ParseFloat(EcmaRuntimeCallInfo *argv) 1534514f5e3Sopenharmony_ci{ 1544514f5e3Sopenharmony_ci ASSERT(argv); 1554514f5e3Sopenharmony_ci BUILTINS_API_TRACE(argv->GetThread(), Number, ParseFloat); 1564514f5e3Sopenharmony_ci JSThread *thread = argv->GetThread(); 1574514f5e3Sopenharmony_ci JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0); 1584514f5e3Sopenharmony_ci if (msg->IsUndefined()) { 1594514f5e3Sopenharmony_ci return GetTaggedDouble(base::NAN_VALUE); 1604514f5e3Sopenharmony_ci } 1614514f5e3Sopenharmony_ci [[maybe_unused]] EcmaHandleScope handleScope(thread); 1624514f5e3Sopenharmony_ci // 1. Let inputString be ToString(string). 1634514f5e3Sopenharmony_ci JSHandle<EcmaString> numberString = JSTaggedValue::ToString(thread, msg); 1644514f5e3Sopenharmony_ci // 2. ReturnIfAbrupt(inputString). 1654514f5e3Sopenharmony_ci RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 1664514f5e3Sopenharmony_ci CVector<uint8_t> buf; 1674514f5e3Sopenharmony_ci Span<const uint8_t> str = EcmaStringAccessor(numberString).ToUtf8Span(buf); 1684514f5e3Sopenharmony_ci // 4. If neither trimmedString nor any prefix of trimmedString satisfies the syntax of a StrDecimalLiteral 1694514f5e3Sopenharmony_ci // (see 7.1.3.1), return NaN. 1704514f5e3Sopenharmony_ci if (NumberHelper::IsEmptyString(str.begin(), str.end())) { 1714514f5e3Sopenharmony_ci return BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); 1724514f5e3Sopenharmony_ci } 1734514f5e3Sopenharmony_ci double result = NumberHelper::StringToDouble(str.begin(), str.end(), 0, base::IGNORE_TRAILING); 1744514f5e3Sopenharmony_ci return GetTaggedDouble(result); 1754514f5e3Sopenharmony_ci} 1764514f5e3Sopenharmony_ci 1774514f5e3Sopenharmony_ci// 18.2.5 1784514f5e3Sopenharmony_ci// 20.1.2.13 1794514f5e3Sopenharmony_ciJSTaggedValue BuiltinsNumber::ParseInt(EcmaRuntimeCallInfo *argv) 1804514f5e3Sopenharmony_ci{ 1814514f5e3Sopenharmony_ci ASSERT(argv); 1824514f5e3Sopenharmony_ci BUILTINS_API_TRACE(argv->GetThread(), Number, ParseInt); 1834514f5e3Sopenharmony_ci JSThread *thread = argv->GetThread(); 1844514f5e3Sopenharmony_ci [[maybe_unused]] EcmaHandleScope handleScope(thread); 1854514f5e3Sopenharmony_ci JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0); 1864514f5e3Sopenharmony_ci JSHandle<JSTaggedValue> arg2 = GetCallArg(argv, 1); 1874514f5e3Sopenharmony_ci int32_t radix = 0; 1884514f5e3Sopenharmony_ci 1894514f5e3Sopenharmony_ci // 1. Let inputString be ToString(string). 1904514f5e3Sopenharmony_ci JSHandle<EcmaString> numberString = JSTaggedValue::ToString(thread, msg); 1914514f5e3Sopenharmony_ci RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 1924514f5e3Sopenharmony_ci if (!arg2->IsUndefined()) { 1934514f5e3Sopenharmony_ci // 7. Let R = ToInt32(radix). 1944514f5e3Sopenharmony_ci radix = JSTaggedValue::ToInt32(thread, arg2); 1954514f5e3Sopenharmony_ci RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 1964514f5e3Sopenharmony_ci } 1974514f5e3Sopenharmony_ci 1984514f5e3Sopenharmony_ci return NumberHelper::StringToNumber(*numberString, radix); 1994514f5e3Sopenharmony_ci} 2004514f5e3Sopenharmony_ci 2014514f5e3Sopenharmony_ci// prototype 2024514f5e3Sopenharmony_ci// 20.1.3.2 2034514f5e3Sopenharmony_ciJSTaggedValue BuiltinsNumber::ToExponential(EcmaRuntimeCallInfo *argv) 2044514f5e3Sopenharmony_ci{ 2054514f5e3Sopenharmony_ci ASSERT(argv); 2064514f5e3Sopenharmony_ci JSThread *thread = argv->GetThread(); 2074514f5e3Sopenharmony_ci BUILTINS_API_TRACE(thread, Number, ToExponential); 2084514f5e3Sopenharmony_ci [[maybe_unused]] EcmaHandleScope handleScope(thread); 2094514f5e3Sopenharmony_ci // 1. Let x be ? thisNumberValue(this value). 2104514f5e3Sopenharmony_ci JSTaggedNumber value = ThisNumberValue(thread, argv); 2114514f5e3Sopenharmony_ci // 2. ReturnIfAbrupt(x). 2124514f5e3Sopenharmony_ci RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 2134514f5e3Sopenharmony_ci 2144514f5e3Sopenharmony_ci // 3. Let f be ToInteger(fractionDigits). 2154514f5e3Sopenharmony_ci JSHandle<JSTaggedValue> digits = GetCallArg(argv, 0); 2164514f5e3Sopenharmony_ci JSTaggedNumber digitInt = JSTaggedValue::ToInteger(thread, digits); 2174514f5e3Sopenharmony_ci // 5. ReturnIfAbrupt(f). 2184514f5e3Sopenharmony_ci RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 2194514f5e3Sopenharmony_ci 2204514f5e3Sopenharmony_ci double values = value.GetNumber(); 2214514f5e3Sopenharmony_ci // 6. If x is NaN, return the String "NaN". 2224514f5e3Sopenharmony_ci if (std::isnan(values)) { 2234514f5e3Sopenharmony_ci return GetTaggedString(thread, "NaN"); 2244514f5e3Sopenharmony_ci } 2254514f5e3Sopenharmony_ci // 8. If x < 0, then 2264514f5e3Sopenharmony_ci // a. Let s be "-". 2274514f5e3Sopenharmony_ci // b. Let x = –x. 2284514f5e3Sopenharmony_ci // 9. If x = +infinity, then 2294514f5e3Sopenharmony_ci // a. Return the concatenation of the Strings s and "Infinity". 2304514f5e3Sopenharmony_ci if (!std::isfinite(values)) { 2314514f5e3Sopenharmony_ci if (values < 0) { 2324514f5e3Sopenharmony_ci return GetTaggedString(thread, "-Infinity"); 2334514f5e3Sopenharmony_ci } 2344514f5e3Sopenharmony_ci return GetTaggedString(thread, "Infinity"); 2354514f5e3Sopenharmony_ci } 2364514f5e3Sopenharmony_ci 2374514f5e3Sopenharmony_ci // 4. Assert: f is 0, when fractionDigits is undefined. 2384514f5e3Sopenharmony_ci // 10. If f < 0 or f > 20, throw a RangeError exception 2394514f5e3Sopenharmony_ci double fraction = digitInt.GetNumber(); 2404514f5e3Sopenharmony_ci if (digits->IsUndefined()) { 2414514f5e3Sopenharmony_ci fraction = 0; 2424514f5e3Sopenharmony_ci } else { 2434514f5e3Sopenharmony_ci if (fraction < base::MIN_FRACTION || fraction > base::MAX_FRACTION) { 2444514f5e3Sopenharmony_ci THROW_RANGE_ERROR_AND_RETURN(thread, "fraction must be 0 to 100", JSTaggedValue::Exception()); 2454514f5e3Sopenharmony_ci } 2464514f5e3Sopenharmony_ci fraction++; 2474514f5e3Sopenharmony_ci } 2484514f5e3Sopenharmony_ci return NumberHelper::DoubleToExponential(thread, values, static_cast<int>(fraction)); 2494514f5e3Sopenharmony_ci} 2504514f5e3Sopenharmony_ci 2514514f5e3Sopenharmony_ci// 20.1.3.3 2524514f5e3Sopenharmony_ciJSTaggedValue BuiltinsNumber::ToFixed(EcmaRuntimeCallInfo *argv) 2534514f5e3Sopenharmony_ci{ 2544514f5e3Sopenharmony_ci ASSERT(argv); 2554514f5e3Sopenharmony_ci JSThread *thread = argv->GetThread(); 2564514f5e3Sopenharmony_ci BUILTINS_API_TRACE(thread, Number, ToFixed); 2574514f5e3Sopenharmony_ci [[maybe_unused]] EcmaHandleScope handleScope(thread); 2584514f5e3Sopenharmony_ci // 1. Let x be ? thisNumberValue(this value). 2594514f5e3Sopenharmony_ci JSTaggedNumber value = ThisNumberValue(thread, argv); 2604514f5e3Sopenharmony_ci // 2. ReturnIfAbrupt(x). 2614514f5e3Sopenharmony_ci RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 2624514f5e3Sopenharmony_ci // 3. Let f be ToInteger(fractionDigits). (If fractionDigits is undefined, this step produces the value 0). 2634514f5e3Sopenharmony_ci JSHandle<JSTaggedValue> digitArgv = GetCallArg(argv, 0); 2644514f5e3Sopenharmony_ci JSTaggedNumber digitInt = JSTaggedValue::ToInteger(thread, digitArgv); 2654514f5e3Sopenharmony_ci RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 2664514f5e3Sopenharmony_ci if (digitArgv->IsUndefined()) { 2674514f5e3Sopenharmony_ci digitInt = JSTaggedNumber(0); 2684514f5e3Sopenharmony_ci } 2694514f5e3Sopenharmony_ci // 4. ReturnIfAbrupt(f). 2704514f5e3Sopenharmony_ci RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 2714514f5e3Sopenharmony_ci double digit = digitInt.GetNumber(); 2724514f5e3Sopenharmony_ci if (digit < base::MIN_FRACTION || digit > base::MAX_FRACTION) { 2734514f5e3Sopenharmony_ci THROW_RANGE_ERROR_AND_RETURN(thread, "fraction must be 0 to 100", JSTaggedValue::Exception()); 2744514f5e3Sopenharmony_ci } 2754514f5e3Sopenharmony_ci 2764514f5e3Sopenharmony_ci // 6. If x is NaN, return the String "NaN". 2774514f5e3Sopenharmony_ci double valueNumber = value.GetNumber(); 2784514f5e3Sopenharmony_ci if (std::isnan(valueNumber)) { 2794514f5e3Sopenharmony_ci const GlobalEnvConstants *globalConst = thread->GlobalConstants(); 2804514f5e3Sopenharmony_ci return globalConst->GetNanCapitalString(); 2814514f5e3Sopenharmony_ci } 2824514f5e3Sopenharmony_ci if (!std::isfinite(valueNumber)) { 2834514f5e3Sopenharmony_ci if (valueNumber < 0) { 2844514f5e3Sopenharmony_ci return GetTaggedString(thread, "-Infinity"); 2854514f5e3Sopenharmony_ci } 2864514f5e3Sopenharmony_ci return GetTaggedString(thread, "Infinity"); 2874514f5e3Sopenharmony_ci } 2884514f5e3Sopenharmony_ci // 9. If x 1021, then 2894514f5e3Sopenharmony_ci // a. Let m = ToString(x). 2904514f5e3Sopenharmony_ci const double FIRST_NO_FIXED = 1e21; 2914514f5e3Sopenharmony_ci if (std::abs(valueNumber) >= FIRST_NO_FIXED) { 2924514f5e3Sopenharmony_ci return value.ToString(thread).GetTaggedValue(); 2934514f5e3Sopenharmony_ci } 2944514f5e3Sopenharmony_ci return NumberHelper::DoubleToFixedString(thread, valueNumber, static_cast<int>(digit)); 2954514f5e3Sopenharmony_ci} 2964514f5e3Sopenharmony_ci 2974514f5e3Sopenharmony_ci// 20.1.3.4 2984514f5e3Sopenharmony_ciJSTaggedValue BuiltinsNumber::ToLocaleString(EcmaRuntimeCallInfo *argv) 2994514f5e3Sopenharmony_ci{ 3004514f5e3Sopenharmony_ci ASSERT(argv); 3014514f5e3Sopenharmony_ci JSThread *thread = argv->GetThread(); 3024514f5e3Sopenharmony_ci BUILTINS_API_TRACE(thread, Number, ToLocaleString); 3034514f5e3Sopenharmony_ci [[maybe_unused]] EcmaHandleScope handleScope(thread); 3044514f5e3Sopenharmony_ci // 1. Let x be ? thisNumberValue(this value). 3054514f5e3Sopenharmony_ci [[maybe_unused]] JSHandle<JSTaggedValue> x(thread, ThisNumberValue(thread, argv)); 3064514f5e3Sopenharmony_ci RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 3074514f5e3Sopenharmony_ci 3084514f5e3Sopenharmony_ci JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0); 3094514f5e3Sopenharmony_ci JSHandle<JSTaggedValue> options = GetCallArg(argv, 1); 3104514f5e3Sopenharmony_ci [[maybe_unused]] bool cacheable = (locales->IsUndefined() || locales->IsString()) && options->IsUndefined(); 3114514f5e3Sopenharmony_ci#ifdef ARK_SUPPORT_INTL 3124514f5e3Sopenharmony_ci if (cacheable) { 3134514f5e3Sopenharmony_ci auto numberFormatter = JSNumberFormat::GetCachedIcuNumberFormatter(thread, locales); 3144514f5e3Sopenharmony_ci if (numberFormatter != nullptr) { 3154514f5e3Sopenharmony_ci JSHandle<JSTaggedValue> result = JSNumberFormat::FormatNumeric(thread, numberFormatter, x.GetTaggedValue()); 3164514f5e3Sopenharmony_ci RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 3174514f5e3Sopenharmony_ci return result.GetTaggedValue(); 3184514f5e3Sopenharmony_ci } 3194514f5e3Sopenharmony_ci } 3204514f5e3Sopenharmony_ci // 2. Let numberFormat be ? Construct(%NumberFormat%, « locales, options »). 3214514f5e3Sopenharmony_ci EcmaVM *ecmaVm = thread->GetEcmaVM(); 3224514f5e3Sopenharmony_ci JSHandle<JSFunction> ctor(ecmaVm->GetGlobalEnv()->GetNumberFormatFunction()); 3234514f5e3Sopenharmony_ci ObjectFactory *factory = ecmaVm->GetFactory(); 3244514f5e3Sopenharmony_ci JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(ctor); 3254514f5e3Sopenharmony_ci JSHandle<JSNumberFormat> numberFormat = JSHandle<JSNumberFormat>::Cast(obj); 3264514f5e3Sopenharmony_ci JSNumberFormat::InitializeNumberFormat(thread, numberFormat, locales, options, cacheable); 3274514f5e3Sopenharmony_ci RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 3284514f5e3Sopenharmony_ci if (cacheable) { 3294514f5e3Sopenharmony_ci auto numberFormatter = JSNumberFormat::GetCachedIcuNumberFormatter(thread, locales); 3304514f5e3Sopenharmony_ci ASSERT(numberFormatter != nullptr); 3314514f5e3Sopenharmony_ci JSHandle<JSTaggedValue> result = JSNumberFormat::FormatNumeric(thread, numberFormatter, x.GetTaggedValue()); 3324514f5e3Sopenharmony_ci RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 3334514f5e3Sopenharmony_ci return result.GetTaggedValue(); 3344514f5e3Sopenharmony_ci } 3354514f5e3Sopenharmony_ci 3364514f5e3Sopenharmony_ci // Return ? FormatNumeric(numberFormat, x). 3374514f5e3Sopenharmony_ci JSHandle<JSTaggedValue> result = JSNumberFormat::FormatNumeric(thread, numberFormat, x.GetTaggedValue()); 3384514f5e3Sopenharmony_ci RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 3394514f5e3Sopenharmony_ci return result.GetTaggedValue(); 3404514f5e3Sopenharmony_ci#else 3414514f5e3Sopenharmony_ci#ifdef ARK_NOT_SUPPORT_INTL_GLOBAL 3424514f5e3Sopenharmony_ci ARK_SUPPORT_INTL_RETURN_JSVALUE(thread, "LocaleCompare"); 3434514f5e3Sopenharmony_ci#else 3444514f5e3Sopenharmony_ci intl::GlobalIntlHelper gh(thread, intl::GlobalFormatterType::NumberFormatter); 3454514f5e3Sopenharmony_ci auto numberFormatter = gh.GetGlobalObject<intl::GlobalNumberFormat>(thread, 3464514f5e3Sopenharmony_ci locales, options, intl::GlobalFormatterType::NumberFormatter, cacheable); 3474514f5e3Sopenharmony_ci if (numberFormatter == nullptr) { 3484514f5e3Sopenharmony_ci LOG_ECMA(ERROR) << "BuiltinsNumber:numberFormatter is nullptr"; 3494514f5e3Sopenharmony_ci } 3504514f5e3Sopenharmony_ci ASSERT(numberFormatter != nullptr); 3514514f5e3Sopenharmony_ci std::string result = numberFormatter->Format(x->GetDouble()); 3524514f5e3Sopenharmony_ci EcmaVM *ecmaVm = thread->GetEcmaVM(); 3534514f5e3Sopenharmony_ci ObjectFactory *factory = ecmaVm->GetFactory(); 3544514f5e3Sopenharmony_ci JSHandle returnValue = factory->NewFromStdString(result); 3554514f5e3Sopenharmony_ci RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 3564514f5e3Sopenharmony_ci return returnValue.GetTaggedValue(); 3574514f5e3Sopenharmony_ci#endif 3584514f5e3Sopenharmony_ci#endif 3594514f5e3Sopenharmony_ci} 3604514f5e3Sopenharmony_ci 3614514f5e3Sopenharmony_ci// 20.1.3.5 3624514f5e3Sopenharmony_ciJSTaggedValue BuiltinsNumber::ToPrecision(EcmaRuntimeCallInfo *argv) 3634514f5e3Sopenharmony_ci{ 3644514f5e3Sopenharmony_ci ASSERT(argv); 3654514f5e3Sopenharmony_ci JSThread *thread = argv->GetThread(); 3664514f5e3Sopenharmony_ci BUILTINS_API_TRACE(thread, Number, ToPrecision); 3674514f5e3Sopenharmony_ci [[maybe_unused]] EcmaHandleScope handleScope(thread); 3684514f5e3Sopenharmony_ci // 1. Let x be ? thisNumberValue(this value). 3694514f5e3Sopenharmony_ci JSTaggedNumber value = ThisNumberValue(thread, argv); 3704514f5e3Sopenharmony_ci // 2. ReturnIfAbrupt(x). 3714514f5e3Sopenharmony_ci RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 3724514f5e3Sopenharmony_ci 3734514f5e3Sopenharmony_ci // 3. If precision is undefined, return ToString(x). 3744514f5e3Sopenharmony_ci JSHandle<JSTaggedValue> digitArgv = GetCallArg(argv, 0); 3754514f5e3Sopenharmony_ci if (digitArgv->IsUndefined()) { 3764514f5e3Sopenharmony_ci return value.ToString(thread).GetTaggedValue(); 3774514f5e3Sopenharmony_ci } 3784514f5e3Sopenharmony_ci // 4. Let p be ToInteger(precision). 3794514f5e3Sopenharmony_ci JSTaggedNumber digitInt = JSTaggedValue::ToInteger(thread, digitArgv); 3804514f5e3Sopenharmony_ci // 5. ReturnIfAbrupt(p). 3814514f5e3Sopenharmony_ci RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 3824514f5e3Sopenharmony_ci 3834514f5e3Sopenharmony_ci // 6. If x is NaN, return the String "NaN". 3844514f5e3Sopenharmony_ci double valueNumber = value.GetNumber(); 3854514f5e3Sopenharmony_ci if (std::isnan(valueNumber)) { 3864514f5e3Sopenharmony_ci return GetTaggedString(thread, "NaN"); 3874514f5e3Sopenharmony_ci } 3884514f5e3Sopenharmony_ci // 9. If x = +infinity, then 3894514f5e3Sopenharmony_ci // a. Return the String that is the concatenation of s and "Infinity". 3904514f5e3Sopenharmony_ci if (!std::isfinite(valueNumber)) { 3914514f5e3Sopenharmony_ci if (valueNumber < 0) { 3924514f5e3Sopenharmony_ci return GetTaggedString(thread, "-Infinity"); 3934514f5e3Sopenharmony_ci } 3944514f5e3Sopenharmony_ci return GetTaggedString(thread, "Infinity"); 3954514f5e3Sopenharmony_ci } 3964514f5e3Sopenharmony_ci 3974514f5e3Sopenharmony_ci // If p < 1 or p > 21, throw a RangeError exception 3984514f5e3Sopenharmony_ci double digit = digitInt.GetNumber(); 3994514f5e3Sopenharmony_ci if (digit < base::MIN_FRACTION + 1 || digit > base::MAX_FRACTION) { 4004514f5e3Sopenharmony_ci THROW_RANGE_ERROR_AND_RETURN(thread, "fraction must be 1 to 100", JSTaggedValue::Exception()); 4014514f5e3Sopenharmony_ci } 4024514f5e3Sopenharmony_ci return NumberHelper::DoubleToPrecisionString(thread, valueNumber, static_cast<int>(digit)); 4034514f5e3Sopenharmony_ci} 4044514f5e3Sopenharmony_ci 4054514f5e3Sopenharmony_ci// 20.1.3.6 4064514f5e3Sopenharmony_ciJSTaggedValue BuiltinsNumber::ToString(EcmaRuntimeCallInfo *argv) 4074514f5e3Sopenharmony_ci{ 4084514f5e3Sopenharmony_ci ASSERT(argv); 4094514f5e3Sopenharmony_ci JSThread *thread = argv->GetThread(); 4104514f5e3Sopenharmony_ci BUILTINS_API_TRACE(thread, Number, ToString); 4114514f5e3Sopenharmony_ci // NOTE: There is no heap alloc in fast path, so delay the scope. 4124514f5e3Sopenharmony_ci 4134514f5e3Sopenharmony_ci // 1. Let x be ? thisNumberValue(this value). 4144514f5e3Sopenharmony_ci JSTaggedNumber value = ThisNumberValue(thread, argv); 4154514f5e3Sopenharmony_ci // 2. ReturnIfAbrupt(x). 4164514f5e3Sopenharmony_ci RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 4174514f5e3Sopenharmony_ci 4184514f5e3Sopenharmony_ci // 3. If radix is not present, let radixNumber be 10. 4194514f5e3Sopenharmony_ci // 4. Else if radix is undefined, let radixNumber be 10. 4204514f5e3Sopenharmony_ci double radix = base::DECIMAL; 4214514f5e3Sopenharmony_ci JSHandle<JSTaggedValue> radixValue = GetCallArg(argv, 0); 4224514f5e3Sopenharmony_ci // 5. Else let radixNumber be ToInteger(radix). 4234514f5e3Sopenharmony_ci if (radixValue->IsInt()) { 4244514f5e3Sopenharmony_ci radix = radixValue->GetInt(); 4254514f5e3Sopenharmony_ci } else if (!radixValue->IsUndefined()) { 4264514f5e3Sopenharmony_ci JSTaggedNumber radixNumber = JSTaggedValue::ToInteger(thread, radixValue); 4274514f5e3Sopenharmony_ci // 6. ReturnIfAbrupt(x). 4284514f5e3Sopenharmony_ci RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 4294514f5e3Sopenharmony_ci radix = radixNumber.GetNumber(); 4304514f5e3Sopenharmony_ci } 4314514f5e3Sopenharmony_ci 4324514f5e3Sopenharmony_ci // 7. If radixNumber < 2 or radixNumber > 36, throw a RangeError exception. 4334514f5e3Sopenharmony_ci if (radix < base::MIN_RADIX || radix > base::MAX_RADIX) { 4344514f5e3Sopenharmony_ci THROW_RANGE_ERROR_AND_RETURN(thread, "radix must be 2 to 36", JSTaggedValue::Exception()); 4354514f5e3Sopenharmony_ci } 4364514f5e3Sopenharmony_ci // 8. If radixNumber = 10, return ToString(x). 4374514f5e3Sopenharmony_ci if (radix == base::DECIMAL) { 4384514f5e3Sopenharmony_ci JSHandle<NumberToStringResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetNumberToStringResultCache()); 4394514f5e3Sopenharmony_ci int entry = cacheTable->GetNumberHash(value); 4404514f5e3Sopenharmony_ci JSTaggedValue cacheResult = cacheTable->FindCachedResult(entry, value); 4414514f5e3Sopenharmony_ci if (cacheResult != JSTaggedValue::Undefined()) { 4424514f5e3Sopenharmony_ci return cacheResult; 4434514f5e3Sopenharmony_ci } 4444514f5e3Sopenharmony_ci // NOTE: There is no heap alloc before here, so delay the scope to here. 4454514f5e3Sopenharmony_ci [[maybe_unused]] EcmaHandleScope handleScope(thread); 4464514f5e3Sopenharmony_ci JSHandle<EcmaString> resultJSHandle = value.ToString(thread); 4474514f5e3Sopenharmony_ci cacheTable->SetCachedResult(thread, entry, value, resultJSHandle); 4484514f5e3Sopenharmony_ci return resultJSHandle.GetTaggedValue(); 4494514f5e3Sopenharmony_ci } 4504514f5e3Sopenharmony_ci // NOTE: There is no heap alloc before here, so delay the scope to here. 4514514f5e3Sopenharmony_ci [[maybe_unused]] EcmaHandleScope handleScope(thread); 4524514f5e3Sopenharmony_ci if (value.IsInt()) { 4534514f5e3Sopenharmony_ci return NumberHelper::Int32ToString(thread, value.GetInt(), radix); 4544514f5e3Sopenharmony_ci } 4554514f5e3Sopenharmony_ci 4564514f5e3Sopenharmony_ci double valueNumber = value.GetNumber(); 4574514f5e3Sopenharmony_ci // If x is NaN, return the String "NaN". 4584514f5e3Sopenharmony_ci if (std::isnan(valueNumber)) { 4594514f5e3Sopenharmony_ci return GetTaggedString(thread, "NaN"); 4604514f5e3Sopenharmony_ci } 4614514f5e3Sopenharmony_ci // If x = +infinity, then 4624514f5e3Sopenharmony_ci // Return the String that is the concatenation of s and "Infinity". 4634514f5e3Sopenharmony_ci if (!std::isfinite(valueNumber)) { 4644514f5e3Sopenharmony_ci if (valueNumber < 0) { 4654514f5e3Sopenharmony_ci return GetTaggedString(thread, "-Infinity"); 4664514f5e3Sopenharmony_ci } 4674514f5e3Sopenharmony_ci return GetTaggedString(thread, "Infinity"); 4684514f5e3Sopenharmony_ci } 4694514f5e3Sopenharmony_ci return NumberHelper::DoubleToString(thread, valueNumber, static_cast<int>(radix)); 4704514f5e3Sopenharmony_ci} 4714514f5e3Sopenharmony_ci 4724514f5e3Sopenharmony_ci// 20.1.3.7 4734514f5e3Sopenharmony_ciJSTaggedValue BuiltinsNumber::ValueOf(EcmaRuntimeCallInfo *argv) 4744514f5e3Sopenharmony_ci{ 4754514f5e3Sopenharmony_ci ASSERT(argv); 4764514f5e3Sopenharmony_ci JSThread *thread = argv->GetThread(); 4774514f5e3Sopenharmony_ci BUILTINS_API_TRACE(thread, Number, ValueOf); 4784514f5e3Sopenharmony_ci // 1. Let x be ? thisNumberValue(this value). 4794514f5e3Sopenharmony_ci JSTaggedValue x = ThisNumberValue(thread, argv); 4804514f5e3Sopenharmony_ci 4814514f5e3Sopenharmony_ci RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 4824514f5e3Sopenharmony_ci return x; 4834514f5e3Sopenharmony_ci} 4844514f5e3Sopenharmony_ci 4854514f5e3Sopenharmony_ciJSTaggedNumber BuiltinsNumber::ThisNumberValue(JSThread *thread, EcmaRuntimeCallInfo *argv) 4864514f5e3Sopenharmony_ci{ 4874514f5e3Sopenharmony_ci BUILTINS_API_TRACE(thread, Number, ThisNumberValue); 4884514f5e3Sopenharmony_ci JSHandle<JSTaggedValue> value = GetThis(argv); 4894514f5e3Sopenharmony_ci if (value->IsNumber()) { 4904514f5e3Sopenharmony_ci return JSTaggedNumber(value.GetTaggedValue()); 4914514f5e3Sopenharmony_ci } 4924514f5e3Sopenharmony_ci if (value->IsJSPrimitiveRef()) { 4934514f5e3Sopenharmony_ci JSTaggedValue primitive = JSPrimitiveRef::Cast(value->GetTaggedObject())->GetValue(); 4944514f5e3Sopenharmony_ci if (primitive.IsNumber()) { 4954514f5e3Sopenharmony_ci return JSTaggedNumber(primitive); 4964514f5e3Sopenharmony_ci } 4974514f5e3Sopenharmony_ci } 4984514f5e3Sopenharmony_ci [[maybe_unused]] EcmaHandleScope handleScope(thread); 4994514f5e3Sopenharmony_ci THROW_TYPE_ERROR_AND_RETURN(thread, "not number type", JSTaggedNumber::Exception()); 5004514f5e3Sopenharmony_ci} 5014514f5e3Sopenharmony_ci 5024514f5e3Sopenharmony_ciJSTaggedValue NumberToStringResultCache::CreateCacheTable(const JSThread *thread) 5034514f5e3Sopenharmony_ci{ 5044514f5e3Sopenharmony_ci int length = INITIAL_CACHE_NUMBER * ENTRY_SIZE; 5054514f5e3Sopenharmony_ci auto table = static_cast<NumberToStringResultCache*>( 5064514f5e3Sopenharmony_ci *thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length, JSTaggedValue::Undefined())); 5074514f5e3Sopenharmony_ci return JSTaggedValue(table); 5084514f5e3Sopenharmony_ci} 5094514f5e3Sopenharmony_ci 5104514f5e3Sopenharmony_ciJSTaggedValue NumberToStringResultCache::FindCachedResult(int entry, JSTaggedValue &target) 5114514f5e3Sopenharmony_ci{ 5124514f5e3Sopenharmony_ci uint32_t index = static_cast<uint32_t>(entry * ENTRY_SIZE); 5134514f5e3Sopenharmony_ci JSTaggedValue entryNumber = Get(index + NUMBER_INDEX); 5144514f5e3Sopenharmony_ci if (entryNumber == target) { 5154514f5e3Sopenharmony_ci return Get(index + RESULT_INDEX); 5164514f5e3Sopenharmony_ci } 5174514f5e3Sopenharmony_ci return JSTaggedValue::Undefined(); 5184514f5e3Sopenharmony_ci} 5194514f5e3Sopenharmony_ci 5204514f5e3Sopenharmony_civoid NumberToStringResultCache::SetCachedResult(const JSThread *thread, int entry, JSTaggedValue &number, 5214514f5e3Sopenharmony_ci JSHandle<EcmaString> &result) 5224514f5e3Sopenharmony_ci{ 5234514f5e3Sopenharmony_ci uint32_t index = static_cast<uint32_t>(entry * ENTRY_SIZE); 5244514f5e3Sopenharmony_ci Set(thread, index + NUMBER_INDEX, number); 5254514f5e3Sopenharmony_ci Set(thread, index + RESULT_INDEX, result.GetTaggedValue()); 5264514f5e3Sopenharmony_ci} 5274514f5e3Sopenharmony_ci} // namespace panda::ecmascript::builtins 528